vector-auditor / src /models /schemas.py
AkshayM
Add max_tokens parameter to NexAGI endpoint
f89f0c8
Raw
History Blame Contribute Delete
9.55 kB
"""Pydantic request/response schemas β€” matches frontend BACKEND_API.md spec."""
from __future__ import annotations
from datetime import datetime
from enum import Enum
from typing import Annotated, Optional
from pydantic import BaseModel, BeforeValidator, EmailStr, Field
def _dt_to_str(v):
if v is None:
return None
if isinstance(v, datetime):
return v.isoformat()
return str(v)
DatetimeOrStr = Annotated[Optional[str], BeforeValidator(_dt_to_str)]
# ── Enums ──────────────────────────────────────────────────────────────────
class Mode(str, Enum):
white_box = "white_box"
black_box = "black_box"
class Role(str, Enum):
user = "user"
admin = "admin"
# ── Auth ───────────────────────────────────────────────────────────────────
class RegisterRequest(BaseModel):
email: EmailStr
password: str = Field(min_length=8, max_length=200)
username: Optional[str] = Field(default=None, max_length=128)
first_name: Optional[str] = Field(default=None, max_length=120)
last_name: Optional[str] = Field(default=None, max_length=120)
class LoginRequest(BaseModel):
email: EmailStr
password: str
class LoginResponse(BaseModel):
access_token: str
user_id: str
email: str = ""
username: Optional[str] = None
display_name: Optional[str] = None
roles: list[str] = []
class RefreshResponse(BaseModel):
access_token: str
class MFALoginRequest(BaseModel):
code: str
class MFASetupResponse(BaseModel):
secret: str
uri: str
qr_code_url: str
class MFAVerifyRequest(BaseModel):
code: str
class OAuthConfigResponse(BaseModel):
github_client_id: str
class GitHubOAuthRequest(BaseModel):
code: str
# ── User ───────────────────────────────────────────────────────────────────
class UserResponse(BaseModel):
id: str
email: Optional[str] = None
username: Optional[str] = None
display_name: Optional[str] = None
roles: list[str] = []
mfa_enabled: bool = False
created_at: DatetimeOrStr = None
# ── Health ─────────────────────────────────────────────────────────────────
class HealthResponse(BaseModel):
status: str # "ok" | "degraded" | "down"
version: str
timestamp: str
checks: dict[str, str] = {}
class ReadyResponse(BaseModel):
ready: bool
reason: Optional[str] = None
# ── Query ──────────────────────────────────────────────────────────────────
class MessageHistory(BaseModel):
role: str
content: str
class Citation(BaseModel):
quote: str
source: str
location: str
page: Optional[int] = None
document_id: Optional[str] = None
bboxes: Optional[list[list[float]]] = None
page_width: Optional[float] = None
page_height: Optional[float] = None
class QueryRequest(BaseModel):
question: str = Field(min_length=1, max_length=4000)
document_ids: Optional[list[str]] = None
conversation_history: Optional[list[MessageHistory]] = None
mode: Mode = Mode.white_box
max_citations: Optional[int] = Field(default=None, ge=1, le=100)
model: Optional[str] = Field(default=None, description="LLM profile key (mercury, gpt5-mini, gpt5, claude-haiku, claude-opus, openrouter)")
class QueryResponse(BaseModel):
answer: str
citations: list[Citation] = []
reasoning_path: list[str] = []
tokens_used: int = 0
cost_usd: float = 0.0
query_id: str
timestamp: str
verification: Optional[str] = None
mode: Optional[Mode] = None
# ── Analysis ───────────────────────────────────────────────────────────────
class AnalyzeRequest(BaseModel):
question: Optional[str] = None
document_ids: Optional[list[str]] = None
max_citations: Optional[int] = Field(default=None, ge=1, le=100)
model: Optional[str] = Field(default=None, description="LLM profile key")
class CrossDocComparison(BaseModel):
common_themes: list[str] = []
differences: list[str] = []
complementary_insights: list[str] = []
class DocumentAnalysis(BaseModel):
summary: str
key_findings: list[str] = []
methodology: str = ""
research_gaps: list[str] = []
contradictions: list[str] = []
open_questions: list[str] = []
limitations: str = ""
confidence: str = "moderate" # "high" | "moderate" | "low"
citations: list[Citation] = []
documents_analyzed: list[str] = []
cross_document_comparison: Optional[CrossDocComparison] = None
per_document_summary: Optional[dict[str, str]] = None
# ── Documents ──────────────────────────────────────────────────────────────
class DocumentResponse(BaseModel):
id: str
document_id: Optional[str] = None
filename: str
status: str # "processing" | "ready" | "failed" | "duplicate" | "skipped" | "stuck"
has_pii: bool = False
privacy: bool = False
sha256: str = ""
cloudinary_url: Optional[str] = None
uploaded_by: str
created_at: DatetimeOrStr = None
class UploadedDocument(BaseModel):
upload_id: str
document_id: str
filename: str
status: str
class UploadResponse(BaseModel):
uploaded_documents: list[UploadedDocument]
class UploadStatusResponse(BaseModel):
id: str
filename: str
stage: str # "uploading" | "extracting" | "chunking" | "embedding" | "indexing" | "completed" | "failed" | "duplicate" | "skipped" | "stuck"
progress: int = 0
error: Optional[str] = None
document_id: Optional[str] = None
user_id: str
created_at: DatetimeOrStr = None
updated_at: DatetimeOrStr = None
# ── Sessions ───────────────────────────────────────────────────────────────
class CreateSessionRequest(BaseModel):
title: Optional[str] = "New chat"
id: Optional[str] = None
class UpdateSessionRequest(BaseModel):
title: str = Field(min_length=1, max_length=255)
class AddMessageRequest(BaseModel):
role: str # "user" | "assistant"
content: str
citations: Optional[list[Citation]] = None
reasoning_path: Optional[list[str]] = None
tokens_used: Optional[int] = None
cost_usd: Optional[float] = None
query_id: Optional[str] = None
verification: Optional[str] = None
class SessionResponse(BaseModel):
id: str
title: Optional[str] = None
user_id: str
created_at: DatetimeOrStr = None
updated_at: DatetimeOrStr = None
class MessageResponse(BaseModel):
id: str
session_id: str
role: str
content: str
citations: Optional[list[Citation]] = None
reasoning_path: Optional[list[str]] = None
tokens_used: Optional[int] = None
cost_usd: Optional[float] = None
query_id: Optional[str] = None
feedback: Optional[str] = None
verification: Optional[str] = None
created_at: DatetimeOrStr = None
class SessionDetailResponse(SessionResponse):
messages: list[MessageResponse] = []
class SessionsListResponse(BaseModel):
sessions: list[SessionResponse]
class MessagesListResponse(BaseModel):
messages: list[MessageResponse]
# ── Feedback ───────────────────────────────────────────────────────────────
class FeedbackRequest(BaseModel):
query_id: str # string, not int
thumbs_up: bool
comment: Optional[str] = Field(default=None, max_length=2000)
# ── DLQ ────────────────────────────────────────────────────────────────────
class DLQItem(BaseModel):
id: str
task: str
payload: str
error: str
failed_at: Optional[str] = None
class DLQResponse(BaseModel):
dead_letter_queue: list[DLQItem] = []
# ── NexAGI ────────────────────────────────────────────────────────────────
class NexAGIMessage(BaseModel):
role: str
content: str | None = None
reasoning_details: Optional[list[dict]] = None
class NexAGIRequest(BaseModel):
messages: list[NexAGIMessage]
model: str = "nex-agi/nex-n2-pro:free"
reasoning: bool = True
max_tokens: Optional[int] = Field(default=None, ge=100, le=128000)
class NexAGIResponse(BaseModel):
content: str | None = None
reasoning_details: Optional[list[dict]] = None
model: str = ""
usage: Optional[dict] = None