feat: add submitted collection, /similar and /submitted endpoints (Stage 4)
Made-with: Cursor
This commit is contained in:
189
app/schemas.py
189
app/schemas.py
@@ -2,36 +2,66 @@
|
||||
Pydantic схемы для API Embedding сервиса.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Запросы
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ScoreRequest(BaseModel):
|
||||
"""Запрос на расчет скора."""
|
||||
|
||||
text: str = Field(..., min_length=1, description="Текст поста для оценки")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"text": "Это пример текста поста для оценки скоринга"
|
||||
}
|
||||
}
|
||||
"json_schema_extra": {"example": {"text": "Это пример текста поста для оценки скоринга"}}
|
||||
}
|
||||
|
||||
|
||||
class ExampleRequest(BaseModel):
|
||||
"""Запрос на добавление примера."""
|
||||
|
||||
text: str = Field(..., min_length=1, description="Текст примера")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {"example": {"text": "Это пример опубликованного/отклоненного поста"}}
|
||||
}
|
||||
|
||||
|
||||
class SimilarRequest(BaseModel):
|
||||
"""Запрос на поиск похожих постов."""
|
||||
|
||||
text: str = Field(..., min_length=1, description="Текст для поиска похожих")
|
||||
threshold: float = Field(
|
||||
default=0.9, ge=0.0, le=1.0, description="Минимальный порог similarity"
|
||||
)
|
||||
hours: int = Field(default=24, ge=1, le=168, description="Количество часов для фильтрации")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"text": "Это пример опубликованного/отклоненного поста"
|
||||
"text": "Текст поста для поиска похожих",
|
||||
"threshold": 0.9,
|
||||
"hours": 24,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SubmittedRequest(BaseModel):
|
||||
"""Запрос на добавление submitted-поста."""
|
||||
|
||||
text: str = Field(..., min_length=1, description="Текст поста")
|
||||
post_id: int | None = None
|
||||
rag_score: float | None = None
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"text": "Текст submitted-поста",
|
||||
"post_id": 12345,
|
||||
"rag_score": 0.85,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,8 +71,10 @@ class ExampleRequest(BaseModel):
|
||||
# Ответы
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class ScoreMetadata(BaseModel):
|
||||
"""Метаданные результата скоринга."""
|
||||
|
||||
positive_examples: int = Field(..., description="Количество положительных примеров")
|
||||
negative_examples: int = Field(..., description="Количество отрицательных примеров")
|
||||
model: str = Field(..., description="Название модели")
|
||||
@@ -51,11 +83,14 @@ class ScoreMetadata(BaseModel):
|
||||
|
||||
class ScoreResponse(BaseModel):
|
||||
"""Ответ с результатом скоринга."""
|
||||
|
||||
rag_score: float = Field(..., ge=0.0, le=1.0, description="Основной скор (neg/pos формула)")
|
||||
rag_confidence: float = Field(..., ge=0.0, le=1.0, description="Уверенность в оценке")
|
||||
rag_score_pos_only: float = Field(..., ge=0.0, le=1.0, description="Скор только по положительным примерам")
|
||||
rag_score_pos_only: float = Field(
|
||||
..., ge=0.0, le=1.0, description="Скор только по положительным примерам"
|
||||
)
|
||||
meta: ScoreMetadata = Field(..., description="Метаданные")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
@@ -66,8 +101,8 @@ class ScoreResponse(BaseModel):
|
||||
"positive_examples": 500,
|
||||
"negative_examples": 350,
|
||||
"model": "sentence-transformers/all-MiniLM-L12-v2",
|
||||
"timestamp": 1706270000
|
||||
}
|
||||
"timestamp": 1706270000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,18 +110,71 @@ class ScoreResponse(BaseModel):
|
||||
|
||||
class ExampleResponse(BaseModel):
|
||||
"""Ответ на добавление примера."""
|
||||
|
||||
success: bool = Field(..., description="Успешность добавления")
|
||||
message: str = Field(..., description="Сообщение о результате")
|
||||
positive_count: int = Field(..., description="Текущее количество положительных примеров")
|
||||
negative_count: int = Field(..., description="Текущее количество отрицательных примеров")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"success": True,
|
||||
"message": "Положительный пример добавлен",
|
||||
"positive_count": 501,
|
||||
"negative_count": 350
|
||||
"negative_count": 350,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SimilarPostItem(BaseModel):
|
||||
"""Элемент похожего поста."""
|
||||
|
||||
similarity: float = Field(..., description="Косинусное сходство")
|
||||
created_at: int = Field(..., description="Unix timestamp создания")
|
||||
post_id: int | None = None
|
||||
text: str = Field(..., description="Текст поста")
|
||||
rag_score: float | None = None
|
||||
|
||||
|
||||
class SimilarResponse(BaseModel):
|
||||
"""Ответ с похожими постами."""
|
||||
|
||||
similar_count: int = Field(..., description="Количество найденных похожих постов")
|
||||
similar_posts: list[SimilarPostItem] = Field(..., description="Список похожих постов")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"similar_count": 2,
|
||||
"similar_posts": [
|
||||
{
|
||||
"similarity": 0.95,
|
||||
"created_at": 1706270000,
|
||||
"post_id": 123,
|
||||
"text": "Похожий пост",
|
||||
"rag_score": 0.85,
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SubmittedResponse(BaseModel):
|
||||
"""Ответ на добавление submitted-поста."""
|
||||
|
||||
success: bool = Field(..., description="Успешность добавления")
|
||||
message: str = Field(..., description="Сообщение о результате")
|
||||
submitted_count: int = Field(..., description="Текущее количество submitted-постов")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"success": True,
|
||||
"message": "Submitted-пост добавлен",
|
||||
"submitted_count": 42,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,20 +182,24 @@ class ExampleResponse(BaseModel):
|
||||
|
||||
class VectorStoreStats(BaseModel):
|
||||
"""Статистика хранилища векторов."""
|
||||
|
||||
positive_count: int = Field(..., description="Количество положительных примеров")
|
||||
negative_count: int = Field(..., description="Количество отрицательных примеров")
|
||||
total_count: int = Field(..., description="Общее количество примеров")
|
||||
submitted_count: int = Field(default=0, description="Количество submitted-постов")
|
||||
vector_dim: int = Field(..., description="Размерность векторов")
|
||||
max_examples: int = Field(..., description="Максимальное количество примеров")
|
||||
max_submitted: int = Field(default=5000, description="Максимальное количество submitted-постов")
|
||||
|
||||
|
||||
class StatsResponse(BaseModel):
|
||||
"""Ответ со статистикой сервиса."""
|
||||
|
||||
model_name: str = Field(..., description="Название модели")
|
||||
model_loaded: bool = Field(..., description="Загружена ли модель")
|
||||
device: Optional[str] = Field(None, description="Устройство (cpu/cuda)")
|
||||
device: str | None = Field(None, description="Устройство (cpu/cuda)")
|
||||
vector_store: VectorStoreStats = Field(..., description="Статистика хранилища векторов")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
@@ -119,8 +211,8 @@ class StatsResponse(BaseModel):
|
||||
"negative_count": 350,
|
||||
"total_count": 850,
|
||||
"vector_dim": 384,
|
||||
"max_examples": 10000
|
||||
}
|
||||
"max_examples": 10000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,16 +220,17 @@ class StatsResponse(BaseModel):
|
||||
|
||||
class WarmupResponse(BaseModel):
|
||||
"""Ответ на прогрев модели."""
|
||||
|
||||
success: bool = Field(..., description="Успешность загрузки")
|
||||
model_loaded: bool = Field(..., description="Загружена ли модель")
|
||||
message: str = Field(..., description="Сообщение о результате")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"success": True,
|
||||
"model_loaded": True,
|
||||
"message": "Модель успешно загружена"
|
||||
"message": "Модель успешно загружена",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,14 +238,15 @@ class WarmupResponse(BaseModel):
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Ответ с ошибкой."""
|
||||
|
||||
detail: str = Field(..., description="Описание ошибки")
|
||||
error_type: str = Field(..., description="Тип ошибки")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"detail": "Недостаточно примеров для расчета скора",
|
||||
"error_type": "InsufficientExamplesError"
|
||||
"error_type": "InsufficientExamplesError",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,23 +254,21 @@ class ErrorResponse(BaseModel):
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
"""Ответ проверки здоровья сервиса."""
|
||||
|
||||
status: str = Field(..., description="Статус сервиса")
|
||||
model_loaded: bool = Field(..., description="Загружена ли модель")
|
||||
version: str = Field(..., description="Версия сервиса")
|
||||
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"status": "healthy",
|
||||
"model_loaded": True,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
"example": {"status": "healthy", "model_loaded": True, "version": "0.1.0"}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ScoringParamsResponse(BaseModel):
|
||||
"""Ответ с текущими параметрами формулы расчета score."""
|
||||
|
||||
score_multiplier: float = Field(
|
||||
...,
|
||||
description=(
|
||||
@@ -185,7 +277,7 @@ class ScoringParamsResponse(BaseModel):
|
||||
"где diff = avg_pos - avg_neg (разница средних сходств топ-k примеров). "
|
||||
"Чем больше значение, тем сильнее влияние разницы между положительными и отрицательными примерами на итоговый score. "
|
||||
"Рекомендуемое значение: 5.0"
|
||||
)
|
||||
),
|
||||
)
|
||||
k: int = Field(
|
||||
...,
|
||||
@@ -195,22 +287,16 @@ class ScoringParamsResponse(BaseModel):
|
||||
"и вычисляет среднее косинусное сходство. "
|
||||
"Меньшее значение k делает алгоритм более чувствительным к различиям, но может быть менее стабильным. "
|
||||
"Рекомендуемое значение: 3"
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"score_multiplier": 5.0,
|
||||
"k": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model_config = {"json_schema_extra": {"example": {"score_multiplier": 5.0, "k": 3}}}
|
||||
|
||||
|
||||
class UpdateScoringParamsRequest(BaseModel):
|
||||
"""Запрос на обновление параметров формулы расчета score."""
|
||||
score_multiplier: Optional[float] = Field(
|
||||
|
||||
score_multiplier: float | None = Field(
|
||||
None,
|
||||
gt=0,
|
||||
description=(
|
||||
@@ -219,9 +305,9 @@ class UpdateScoringParamsRequest(BaseModel):
|
||||
"где diff = avg_pos - avg_neg (разница средних сходств топ-k примеров). "
|
||||
"Чем больше значение, тем сильнее влияние разницы между положительными и отрицательными примерами на итоговый score. "
|
||||
"Должен быть > 0. Рекомендуемое значение: 5.0"
|
||||
)
|
||||
),
|
||||
)
|
||||
k: Optional[int] = Field(
|
||||
k: int | None = Field(
|
||||
None,
|
||||
ge=1,
|
||||
description=(
|
||||
@@ -230,14 +316,7 @@ class UpdateScoringParamsRequest(BaseModel):
|
||||
"и вычисляет среднее косинусное сходство. "
|
||||
"Меньшее значение k делает алгоритм более чувствительным к различиям, но может быть менее стабильным. "
|
||||
"Должно быть >= 1. Рекомендуемое значение: 3"
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"score_multiplier": 5.0,
|
||||
"k": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model_config = {"json_schema_extra": {"example": {"score_multiplier": 5.0, "k": 3}}}
|
||||
|
||||
Reference in New Issue
Block a user