""" API request and response models for multi-tenant LightRAG. """ from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any, Literal from datetime import datetime from enum import Enum # Tenant Management Models class TenantConfigRequest(BaseModel): """Request model for tenant configuration.""" llm_model: Optional[str] = None embedding_model: Optional[str] = None rerank_model: Optional[str] = None chunk_size: Optional[int] = None chunk_overlap: Optional[int] = None top_k: Optional[int] = None cosine_threshold: Optional[float] = None class CreateTenantRequest(BaseModel): """Request to create a new tenant.""" tenant_name: str = Field(..., min_length=1, max_length=255) description: Optional[str] = None config: Optional[TenantConfigRequest] = None class UpdateTenantRequest(BaseModel): """Request to update a tenant.""" tenant_name: Optional[str] = None description: Optional[str] = None config: Optional[TenantConfigRequest] = None class TenantResponse(BaseModel): """Response model for tenant information.""" tenant_id: str tenant_name: str description: Optional[str] = None is_active: bool created_at: str updated_at: str kb_count: int = 0 class CreateKBRequest(BaseModel): """Request to create a knowledge base.""" kb_name: str = Field(..., min_length=1, max_length=255) description: Optional[str] = None class KBResponse(BaseModel): """Response model for knowledge base information.""" kb_id: str kb_name: str description: Optional[str] = None tenant_id: str is_active: bool document_count: int = 0 entity_count: int = 0 relationship_count: int = 0 chunk_count: int = 0 storage_used_mb: float = 0.0 created_at: str updated_at: str class PaginatedKBResponse(BaseModel): """Paginated response for knowledge bases.""" items: List[KBResponse] total: int skip: int limit: int # Membership Management Models class UserRole(str, Enum): """User roles for tenant access control.""" OWNER = "owner" ADMIN = "admin" EDITOR = "editor" VIEWER = "viewer" class TenantMembership(BaseModel): """Tenant membership information.""" id: str user_id: str tenant_id: str role: UserRole created_at: str created_by: str updated_at: str class AddMemberRequest(BaseModel): """Request to add a user to a tenant.""" user_id: str = Field(..., min_length=1, max_length=255) role: UserRole = UserRole.VIEWER class UpdateMemberRoleRequest(BaseModel): """Request to update a member's role.""" role: UserRole class MemberResponse(BaseModel): """Response model for tenant member.""" user_id: str role: UserRole created_at: str created_by: str class PaginatedMembersResponse(BaseModel): """Paginated response for tenant members.""" items: List[MemberResponse] total: int skip: int limit: int # Document Models class DocumentAddRequest(BaseModel): """Request to add a document (file path is passed as form data).""" metadata: Optional[str] = None # JSON string class DocumentStatusResponse(BaseModel): """Response for document processing status.""" doc_id: str status: str # ready | processing | error chunks_processed: int = 0 entities_extracted: int = 0 relationships_extracted: int = 0 error_message: Optional[str] = None class DocumentResponse(BaseModel): """Response model for document information.""" doc_id: str kb_id: str tenant_id: str doc_name: str file_type: Optional[str] = None file_size: Optional[int] = None chunk_count: int = 0 created_at: str updated_at: str # Query Models class QueryRequest(BaseModel): """Request to query a knowledge base.""" query: str = Field(..., min_length=3, max_length=2000) mode: Literal["local", "global", "hybrid", "naive", "mix", "bypass"] = "mix" top_k: Optional[int] = Field(None, ge=1, le=100) include_references: bool = True stream: bool = False class QueryReference(BaseModel): """Reference to a source document/chunk.""" doc_id: str doc_name: str chunk_id: Optional[str] = None content: str score: Optional[float] = None class QueryResponse(BaseModel): """Response model for query results.""" response: str references: Optional[List[QueryReference]] = None metadata: Dict[str, Any] = {} class QueryDataResponse(BaseModel): """Response model for query with full context data.""" response: str references: Optional[List[QueryReference]] = None entities: Optional[List[Dict[str, Any]]] = None relationships: Optional[List[Dict[str, Any]]] = None chunks: Optional[List[Dict[str, Any]]] = None metadata: Dict[str, Any] = {} # API Key Models class CreateAPIKeyRequest(BaseModel): """Request to create an API key.""" key_name: str = Field(..., min_length=1, max_length=255) kb_id: Optional[str] = None # None = all KBs permissions: Optional[List[str]] = None # Default: ['query', 'document:read'] expires_at: Optional[str] = None class APIKeyResponse(BaseModel): """Response when creating an API key (includes the key itself).""" key_id: str key: str # Only returned once on creation key_name: str created_at: str message: str = "Save this key securely - it won't be shown again" class APIKeyMetadata(BaseModel): """Metadata for an API key (without the key itself).""" key_id: str key_name: str created_at: str last_used_at: Optional[str] = None permissions: List[str] is_active: bool = True # Error Response Models class ErrorResponse(BaseModel): """Standard error response.""" status: str = "error" code: str # e.g., "ACCESS_DENIED", "NOT_FOUND", "INVALID_REQUEST" message: str request_id: Optional[str] = None class ValidationErrorResponse(BaseModel): """Response for validation errors.""" status: str = "error" code: str = "VALIDATION_ERROR" message: str details: List[Dict[str, Any]] # List of field-level errors request_id: Optional[str] = None