Spaces:
Sleeping
Sleeping
| from datetime import datetime | |
| from pydantic import BaseModel, EmailStr, Field, field_validator | |
| from typing import Any, Optional | |
| import re | |
| class UserCreate(BaseModel): | |
| email: EmailStr | |
| name: str = Field(..., min_length=1, max_length=100) | |
| password: str = Field(..., min_length=8) | |
| def validate_name(cls, v): | |
| if not v.strip(): | |
| raise ValueError('Name cannot be empty or just whitespace') | |
| return v.strip() | |
| def validate_password(cls, v): | |
| if not validate_password_strength(v): | |
| raise ValueError( | |
| 'Password must be at least 8 characters long and contain ' | |
| 'at least one uppercase letter, one lowercase letter, ' | |
| 'one digit' | |
| ) | |
| return v | |
| class Token(BaseModel): | |
| access_token: str | |
| token_type: str = "bearer" | |
| expires_in: int | |
| email: EmailStr | |
| name: str | |
| class LogoutResponse(BaseModel): | |
| message: str | |
| class MessageResponse(BaseModel): | |
| message: str | |
| class EmailVerificationRequest(BaseModel): | |
| email: EmailStr | |
| class UserPublic(BaseModel): | |
| id: str | |
| email: EmailStr | |
| name: str | |
| created_at: datetime | |
| model_config = { | |
| "json_encoders": { | |
| datetime: lambda v: v.isoformat() | |
| } | |
| } | |
| def validate_password_strength(password: str) -> bool: | |
| """Validate password meets security requirements""" | |
| if len(password) < 8: | |
| return False | |
| if not re.search(r"[A-Z]", password): | |
| return False | |
| if not re.search(r"[a-z]", password): | |
| return False | |
| if not re.search(r"[0-9]", password): | |
| return False | |
| return True | |
| class UserQuery(BaseModel): | |
| query:str | |
| thread_id: Optional[str] = None | |
| create_new_thread: Optional[bool] = False | |
| class SourceItem(BaseModel): | |
| document_id: str | |
| filename: str | |
| chunk_index: Optional[int] = None | |
| score: Optional[float] = None | |
| class ChatResponse(BaseModel): | |
| result: str | |
| thread_id: str | |
| user_id: str | |
| is_new_thread: bool | |
| sources: list[SourceItem] = Field(default_factory=list) | |
| class ThreadItem(BaseModel): | |
| thread_id: str | |
| title: str | |
| created_at: Optional[datetime] = None | |
| class ThreadsResponse(BaseModel): | |
| threads: list[ThreadItem] | |
| total: int | |
| class MessageContent(BaseModel): | |
| query: Optional[str] = None | |
| messages: list[Any] = Field(default_factory=list) | |
| def normalize_messages(cls, v): | |
| """Always coerce a bare string into a single-element list.""" | |
| if isinstance(v, str): | |
| return [v] | |
| if v is None: | |
| return [] | |
| return v | |
| class ThreadMessagesResponse(BaseModel): | |
| thread_id: str | |
| messages: MessageContent | |
| class DocumentItem(BaseModel): | |
| document_id: str | |
| filename: str | |
| content_type: str | |
| status: str | |
| chunk_count: int = 0 | |
| created_at: datetime | |
| updated_at: datetime | |
| error_message: Optional[str] = None | |
| class DocumentListResponse(BaseModel): | |
| documents: list[DocumentItem] | |
| total: int | |
| class DocumentUploadResponse(BaseModel): | |
| message: str | |
| document: DocumentItem | |
| class DocumentDeleteResponse(BaseModel): | |
| message: str | |
| document_id: str | |