support-bot-saas/backend/app/models/schemas.py
ed0ss 41af532e7b
Some checks are pending
CI / lint-backend (push) Waiting to run
CI / lint-frontend (push) Waiting to run
CI / build-backend (push) Blocked by required conditions
CI / build-frontend (push) Blocked by required conditions
fix: масштабные исправления и улучшения бэкенда и фронтенда
Бэкенд:
- TicketInput: ticket_id принимает int | str, приводится к str (было 500 на числовых ID)
- LLM: добавлен импорт logger, обработка 429 и не-200 ответов от OpenRouter
- Смена модели на poolside/laguna-m.1:free (обход лимитов OpenRouter)
- Добавлен GET /api/collections/{id} — получение коллекции по ID
- Дедикат заявок: hash() заменён на детерминированный hashlib.md5
  для ID точек в Qdrant (раньше после рестарта пода создавались дубликаты)
- Добавлена get_existing_ticket_ids() — проверка дубликатов до эмбеддинга,
  пропуск уже загруженных заявок
- Мультитенантная изоляция: tenant_id + collection_id во всех фильтрах Qdrant
- Эмбеддинги: заменён несовместимый openai SDK на прямой httpx
- Реренкинг: исправлен доступ к ScoredPoint.payload через .get()
- Чанкинг: расширены роли (support/operator/agent + client/user/customer)
- bcrypt: понижен до 4.0.1 (5.x несовместим с passlib)

Фронтенд:
- Регистрация: добавлен выбор тарифа (free/starter/pro)
- CollectionView: ticket_count загружается с бэкенда при монтировании
  (раньше жил только в React-состоянии — сбрасывался после обновления страницы)
- uploadFile: используется getToken() вместо устаревшей переменной token,
  в ошибке показывается реальный HTTP-статус
- Nginx: увеличен client_max_body_size до 50M для загрузки файлов
- k8s: добавлены манифесты namespace, postgres, redis, qdrant, backend,
  frontend, ingress, traefik
2026-06-20 19:51:58 +03:00

79 lines
1.5 KiB
Python

import uuid
from datetime import datetime
from pydantic import BaseModel, EmailStr, field_validator
class RegisterRequest(BaseModel):
email: EmailStr
password: str
plan: str = "free"
@field_validator("plan")
@classmethod
def validate_plan(cls, v: str) -> str:
if v not in ("free", "starter", "pro"):
raise ValueError("Invalid plan. Choose: free, starter, pro")
return v
class LoginRequest(BaseModel):
email: EmailStr
password: str
class TokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
class UserResponse(BaseModel):
id: uuid.UUID
email: str
api_key: str
plan: str
created_at: datetime
model_config = {"from_attributes": True}
class CollectionCreate(BaseModel):
name: str
class CollectionResponse(BaseModel):
id: uuid.UUID
user_id: uuid.UUID
name: str
ticket_count: int
created_at: datetime
model_config = {"from_attributes": True}
class TicketMessage(BaseModel):
role: str
text: str
class TicketInput(BaseModel):
ticket_id: int | str
category: str = ""
description: str = ""
client: str = ""
messages: list[TicketMessage] = []
@field_validator("ticket_id", mode="before")
@classmethod
def coerce_ticket_id(cls, v: int | str) -> str:
return str(v)
class TicketsUpload(BaseModel):
tickets: list[TicketInput]
class TicketsUploadResponse(BaseModel):
processed: int
skipped: int
collection_ticket_count: int