Spaces:
Runtime error
Runtime error
| from typing import Any, Dict, List, Literal, Optional | |
| from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator | |
| # Notebook models | |
| class NotebookCreate(BaseModel): | |
| name: str = Field(..., description="Name of the notebook") | |
| description: str = Field(default="", description="Description of the notebook") | |
| class NotebookUpdate(BaseModel): | |
| name: Optional[str] = Field(None, description="Name of the notebook") | |
| description: Optional[str] = Field(None, description="Description of the notebook") | |
| archived: Optional[bool] = Field( | |
| None, description="Whether the notebook is archived" | |
| ) | |
| class NotebookResponse(BaseModel): | |
| id: str | |
| name: str | |
| description: str | |
| archived: bool | |
| created: str | |
| updated: str | |
| source_count: int | |
| note_count: int | |
| # Search models | |
| class SearchRequest(BaseModel): | |
| query: str = Field(..., description="Search query") | |
| type: Literal["text", "vector"] = Field("text", description="Search type") | |
| limit: int = Field(100, description="Maximum number of results", le=1000) | |
| search_sources: bool = Field(True, description="Include sources in search") | |
| search_notes: bool = Field(True, description="Include notes in search") | |
| minimum_score: float = Field( | |
| 0.2, description="Minimum score for vector search", ge=0, le=1 | |
| ) | |
| class SearchResponse(BaseModel): | |
| results: List[Dict[str, Any]] = Field(..., description="Search results") | |
| total_count: int = Field(..., description="Total number of results") | |
| search_type: str = Field(..., description="Type of search performed") | |
| class AskRequest(BaseModel): | |
| question: str = Field(..., description="Question to ask the knowledge base") | |
| strategy_model: str = Field(..., description="Model ID for query strategy") | |
| answer_model: str = Field(..., description="Model ID for individual answers") | |
| final_answer_model: str = Field(..., description="Model ID for final answer") | |
| class AskResponse(BaseModel): | |
| answer: str = Field(..., description="Final answer from the knowledge base") | |
| question: str = Field(..., description="Original question") | |
| # Models API models | |
| class ModelCreate(BaseModel): | |
| name: str = Field(..., description="Model name (e.g., gpt-5-mini, claude, gemini)") | |
| provider: str = Field( | |
| ..., description="Provider name (e.g., openai, anthropic, gemini)" | |
| ) | |
| type: str = Field( | |
| ..., | |
| description="Model type (language, embedding, text_to_speech, speech_to_text)", | |
| ) | |
| credential: Optional[str] = Field( | |
| None, description="Credential ID to link this model to" | |
| ) | |
| class ModelResponse(BaseModel): | |
| id: str | |
| name: str | |
| provider: str | |
| type: str | |
| credential: Optional[str] = None | |
| created: str | |
| updated: str | |
| class DefaultModelsResponse(BaseModel): | |
| default_chat_model: Optional[str] = None | |
| default_transformation_model: Optional[str] = None | |
| large_context_model: Optional[str] = None | |
| default_text_to_speech_model: Optional[str] = None | |
| default_speech_to_text_model: Optional[str] = None | |
| default_embedding_model: Optional[str] = None | |
| default_tools_model: Optional[str] = None | |
| class ProviderAvailabilityResponse(BaseModel): | |
| available: List[str] = Field(..., description="List of available providers") | |
| unavailable: List[str] = Field(..., description="List of unavailable providers") | |
| supported_types: Dict[str, List[str]] = Field( | |
| ..., description="Provider to supported model types mapping" | |
| ) | |
| # Transformations API models | |
| class TransformationCreate(BaseModel): | |
| name: str = Field(..., description="Transformation name") | |
| title: str = Field(..., description="Display title for the transformation") | |
| description: str = Field( | |
| ..., description="Description of what this transformation does" | |
| ) | |
| prompt: str = Field(..., description="The transformation prompt") | |
| apply_default: bool = Field( | |
| False, description="Whether to apply this transformation by default" | |
| ) | |
| class TransformationUpdate(BaseModel): | |
| name: Optional[str] = Field(None, description="Transformation name") | |
| title: Optional[str] = Field( | |
| None, description="Display title for the transformation" | |
| ) | |
| description: Optional[str] = Field( | |
| None, description="Description of what this transformation does" | |
| ) | |
| prompt: Optional[str] = Field(None, description="The transformation prompt") | |
| apply_default: Optional[bool] = Field( | |
| None, description="Whether to apply this transformation by default" | |
| ) | |
| class TransformationResponse(BaseModel): | |
| id: str | |
| name: str | |
| title: str | |
| description: str | |
| prompt: str | |
| apply_default: bool | |
| created: str | |
| updated: str | |
| class TransformationExecuteRequest(BaseModel): | |
| model_config = ConfigDict(protected_namespaces=()) | |
| transformation_id: str = Field( | |
| ..., description="ID of the transformation to execute" | |
| ) | |
| input_text: str = Field(..., description="Text to transform") | |
| model_id: str = Field(..., description="Model ID to use for the transformation") | |
| class TransformationExecuteResponse(BaseModel): | |
| model_config = ConfigDict(protected_namespaces=()) | |
| output: str = Field(..., description="Transformed text") | |
| transformation_id: str = Field(..., description="ID of the transformation used") | |
| model_id: str = Field(..., description="Model ID used") | |
| # Default Prompt API models | |
| class DefaultPromptResponse(BaseModel): | |
| transformation_instructions: str = Field( | |
| ..., description="Default transformation instructions" | |
| ) | |
| class DefaultPromptUpdate(BaseModel): | |
| transformation_instructions: str = Field( | |
| ..., description="Default transformation instructions" | |
| ) | |
| # Notes API models | |
| class NoteCreate(BaseModel): | |
| title: Optional[str] = Field(None, description="Note title") | |
| content: str = Field(..., description="Note content") | |
| note_type: Optional[str] = Field("human", description="Type of note (human, ai)") | |
| notebook_id: Optional[str] = Field( | |
| None, description="Notebook ID to add the note to" | |
| ) | |
| class NoteUpdate(BaseModel): | |
| title: Optional[str] = Field(None, description="Note title") | |
| content: Optional[str] = Field(None, description="Note content") | |
| note_type: Optional[str] = Field(None, description="Type of note (human, ai)") | |
| class NoteResponse(BaseModel): | |
| id: str | |
| title: Optional[str] | |
| content: Optional[str] | |
| note_type: Optional[str] | |
| created: str | |
| updated: str | |
| command_id: Optional[str] = None | |
| # Embedding API models | |
| class EmbedRequest(BaseModel): | |
| item_id: str = Field(..., description="ID of the item to embed") | |
| item_type: str = Field(..., description="Type of item (source, note)") | |
| async_processing: bool = Field( | |
| False, description="Process asynchronously in background" | |
| ) | |
| class EmbedResponse(BaseModel): | |
| success: bool = Field(..., description="Whether embedding was successful") | |
| message: str = Field(..., description="Result message") | |
| item_id: str = Field(..., description="ID of the item that was embedded") | |
| item_type: str = Field(..., description="Type of item that was embedded") | |
| command_id: Optional[str] = Field( | |
| None, description="Command ID for async processing" | |
| ) | |
| # Rebuild request/response models | |
| class RebuildRequest(BaseModel): | |
| mode: Literal["existing", "all"] = Field( | |
| ..., | |
| description="Rebuild mode: 'existing' only re-embeds items with embeddings, 'all' embeds everything", | |
| ) | |
| include_sources: bool = Field(True, description="Include sources in rebuild") | |
| include_notes: bool = Field(True, description="Include notes in rebuild") | |
| include_insights: bool = Field(True, description="Include insights in rebuild") | |
| class RebuildResponse(BaseModel): | |
| command_id: str = Field(..., description="Command ID to track progress") | |
| total_items: int = Field(..., description="Estimated number of items to process") | |
| message: str = Field(..., description="Status message") | |
| class RebuildProgress(BaseModel): | |
| processed: int = Field(..., description="Number of items processed") | |
| total: int = Field(..., description="Total items to process") | |
| percentage: float = Field(..., description="Progress percentage") | |
| class RebuildStats(BaseModel): | |
| sources: int = Field(0, description="Sources processed") | |
| notes: int = Field(0, description="Notes processed") | |
| insights: int = Field(0, description="Insights processed") | |
| failed: int = Field(0, description="Failed items") | |
| class RebuildStatusResponse(BaseModel): | |
| command_id: str = Field(..., description="Command ID") | |
| status: str = Field(..., description="Status: queued, running, completed, failed") | |
| progress: Optional[RebuildProgress] = None | |
| stats: Optional[RebuildStats] = None | |
| started_at: Optional[str] = None | |
| completed_at: Optional[str] = None | |
| error_message: Optional[str] = None | |
| # Settings API models | |
| class SettingsResponse(BaseModel): | |
| default_content_processing_engine_doc: Optional[str] = None | |
| default_content_processing_engine_url: Optional[str] = None | |
| default_embedding_option: Optional[str] = None | |
| auto_delete_files: Optional[str] = None | |
| youtube_preferred_languages: Optional[List[str]] = None | |
| class SettingsUpdate(BaseModel): | |
| default_content_processing_engine_doc: Optional[str] = None | |
| default_content_processing_engine_url: Optional[str] = None | |
| default_embedding_option: Optional[str] = None | |
| auto_delete_files: Optional[str] = None | |
| youtube_preferred_languages: Optional[List[str]] = None | |
| # Sources API models | |
| class AssetModel(BaseModel): | |
| file_path: Optional[str] = None | |
| url: Optional[str] = None | |
| class SourceCreate(BaseModel): | |
| # Backward compatibility: support old single notebook_id | |
| notebook_id: Optional[str] = Field( | |
| None, description="Notebook ID to add the source to (deprecated, use notebooks)" | |
| ) | |
| # New multi-notebook support | |
| notebooks: Optional[List[str]] = Field( | |
| None, description="List of notebook IDs to add the source to" | |
| ) | |
| # Required fields | |
| type: str = Field(..., description="Source type: link, upload, or text") | |
| url: Optional[str] = Field(None, description="URL for link type") | |
| file_path: Optional[str] = Field(None, description="File path for upload type") | |
| content: Optional[str] = Field(None, description="Text content for text type") | |
| title: Optional[str] = Field(None, description="Source title") | |
| transformations: Optional[List[str]] = Field( | |
| default_factory=list, description="Transformation IDs to apply" | |
| ) | |
| embed: bool = Field(False, description="Whether to embed content for vector search") | |
| delete_source: bool = Field( | |
| False, description="Whether to delete uploaded file after processing" | |
| ) | |
| # New async processing support | |
| async_processing: bool = Field( | |
| False, description="Whether to process source asynchronously" | |
| ) | |
| def validate_notebook_fields(self): | |
| # Ensure only one of notebook_id or notebooks is provided | |
| if self.notebook_id is not None and self.notebooks is not None: | |
| raise ValueError( | |
| "Cannot specify both 'notebook_id' and 'notebooks'. Use 'notebooks' for multi-notebook support." | |
| ) | |
| # Convert single notebook_id to notebooks array for internal processing | |
| if self.notebook_id is not None: | |
| self.notebooks = [self.notebook_id] | |
| # Keep notebook_id for backward compatibility in response | |
| # Set empty array if no notebooks specified (allow sources without notebooks) | |
| if self.notebooks is None: | |
| self.notebooks = [] | |
| return self | |
| class SourceUpdate(BaseModel): | |
| title: Optional[str] = Field(None, description="Source title") | |
| topics: Optional[List[str]] = Field(None, description="Source topics") | |
| class SourceResponse(BaseModel): | |
| id: str | |
| title: Optional[str] | |
| topics: Optional[List[str]] | |
| asset: Optional[AssetModel] | |
| full_text: Optional[str] | |
| embedded: bool | |
| embedded_chunks: int | |
| file_available: Optional[bool] = None | |
| created: str | |
| updated: str | |
| # New fields for async processing | |
| command_id: Optional[str] = None | |
| status: Optional[str] = None | |
| processing_info: Optional[Dict] = None | |
| # Notebook associations | |
| notebooks: Optional[List[str]] = None | |
| class SourceListResponse(BaseModel): | |
| id: str | |
| title: Optional[str] | |
| topics: Optional[List[str]] | |
| asset: Optional[AssetModel] | |
| embedded: bool # Boolean flag indicating if source has embeddings | |
| embedded_chunks: int # Number of embedded chunks | |
| insights_count: int | |
| created: str | |
| updated: str | |
| file_available: Optional[bool] = None | |
| # Status fields for async processing | |
| command_id: Optional[str] = None | |
| status: Optional[str] = None | |
| processing_info: Optional[Dict[str, Any]] = None | |
| # Context API models | |
| class ContextConfig(BaseModel): | |
| sources: Dict[str, str] = Field( | |
| default_factory=dict, description="Source inclusion config {source_id: level}" | |
| ) | |
| notes: Dict[str, str] = Field( | |
| default_factory=dict, description="Note inclusion config {note_id: level}" | |
| ) | |
| class ContextRequest(BaseModel): | |
| notebook_id: str = Field(..., description="Notebook ID to get context for") | |
| context_config: Optional[ContextConfig] = Field( | |
| None, description="Context configuration" | |
| ) | |
| class ContextResponse(BaseModel): | |
| notebook_id: str | |
| sources: List[Dict[str, Any]] = Field(..., description="Source context data") | |
| notes: List[Dict[str, Any]] = Field(..., description="Note context data") | |
| total_tokens: Optional[int] = Field(None, description="Estimated token count") | |
| # Insights API models | |
| class SourceInsightResponse(BaseModel): | |
| id: str | |
| source_id: str | |
| insight_type: str | |
| content: str | |
| created: str | |
| updated: str | |
| class InsightCreationResponse(BaseModel): | |
| """Response for async insight creation.""" | |
| status: Literal["pending"] = "pending" | |
| message: str = "Insight generation started" | |
| source_id: str | |
| transformation_id: str | |
| command_id: Optional[str] = None | |
| class SaveAsNoteRequest(BaseModel): | |
| notebook_id: Optional[str] = Field(None, description="Notebook ID to add note to") | |
| class CreateSourceInsightRequest(BaseModel): | |
| model_config = ConfigDict(protected_namespaces=()) | |
| transformation_id: str = Field(..., description="ID of transformation to apply") | |
| model_id: Optional[str] = Field( | |
| None, description="Model ID (uses default if not provided)" | |
| ) | |
| # Source status response | |
| class SourceStatusResponse(BaseModel): | |
| status: Optional[str] = Field(None, description="Processing status") | |
| message: str = Field(..., description="Descriptive message about the status") | |
| processing_info: Optional[Dict[str, Any]] = Field( | |
| None, description="Detailed processing information" | |
| ) | |
| command_id: Optional[str] = Field(None, description="Command ID if available") | |
| # Error response | |
| class ErrorResponse(BaseModel): | |
| error: str | |
| message: str | |
| # API Key Configuration models | |
| class SetApiKeyRequest(BaseModel): | |
| """Request to set an API key for a provider.""" | |
| api_key: Optional[str] = Field(None, description="API key for the provider") | |
| base_url: Optional[str] = Field( | |
| None, description="Base URL for URL-based providers (Ollama, OpenAI-compatible)" | |
| ) | |
| endpoint: Optional[str] = Field( | |
| None, description="Endpoint URL for Azure OpenAI" | |
| ) | |
| api_version: Optional[str] = Field( | |
| None, description="API version for Azure OpenAI" | |
| ) | |
| endpoint_llm: Optional[str] = Field( | |
| None, description="Service-specific endpoint for LLM (Azure)" | |
| ) | |
| endpoint_embedding: Optional[str] = Field( | |
| None, description="Service-specific endpoint for embedding (Azure)" | |
| ) | |
| endpoint_stt: Optional[str] = Field( | |
| None, description="Service-specific endpoint for STT (Azure)" | |
| ) | |
| endpoint_tts: Optional[str] = Field( | |
| None, description="Service-specific endpoint for TTS (Azure)" | |
| ) | |
| service_type: Optional[Literal["llm", "embedding", "stt", "tts"]] = Field( | |
| None, | |
| description="Service type for OpenAI-compatible providers (llm, embedding, stt, tts)", | |
| ) | |
| # Vertex AI specific fields | |
| vertex_project: Optional[str] = Field( | |
| None, description="Google Cloud Project ID for Vertex AI" | |
| ) | |
| vertex_location: Optional[str] = Field( | |
| None, description="Google Cloud Region for Vertex AI (e.g., us-central1)" | |
| ) | |
| vertex_credentials_path: Optional[str] = Field( | |
| None, description="Path to Google Cloud service account JSON file" | |
| ) | |
| def validate_not_empty_string(cls, v: Optional[str]) -> Optional[str]: | |
| """Reject empty strings - convert to None or raise error.""" | |
| if v is not None: | |
| stripped = v.strip() | |
| if not stripped: | |
| return None # Treat empty/whitespace-only as None | |
| return stripped | |
| return v | |
| class ApiKeyStatusResponse(BaseModel): | |
| """Response showing which providers are configured and their source.""" | |
| configured: Dict[str, bool] = Field( | |
| ..., description="Map of provider name to whether it is configured" | |
| ) | |
| source: Dict[str, Literal["database", "environment", "none"]] = Field( | |
| ..., | |
| description="Map of provider name to configuration source (database, environment, or none)", | |
| ) | |
| encryption_configured: bool = Field( | |
| ..., | |
| description="Whether OPEN_NOTEBOOK_ENCRYPTION_KEY is set (required to store keys in database)", | |
| ) | |
| class TestConnectionResponse(BaseModel): | |
| """Response from testing a provider connection.""" | |
| provider: str = Field(..., description="Provider name that was tested") | |
| success: bool = Field(..., description="Whether connection test succeeded") | |
| message: str = Field(..., description="Result message with details") | |
| class MigrateFromEnvRequest(BaseModel): | |
| """Request to migrate API keys from environment variables to database.""" | |
| force: bool = Field( | |
| False, description="Force overwrite existing database configurations" | |
| ) | |
| class MigrationResult(BaseModel): | |
| """Response from migrating API keys from environment to database.""" | |
| message: str = Field(..., description="Summary message") | |
| migrated: List[str] = Field( | |
| default_factory=list, description="Providers successfully migrated" | |
| ) | |
| skipped: List[str] = Field( | |
| default_factory=list, description="Providers skipped (already in DB)" | |
| ) | |
| errors: List[str] = Field( | |
| default_factory=list, description="Migration errors by provider" | |
| ) | |
| # Notebook delete cascade models | |
| # Credential models | |
| class CreateCredentialRequest(BaseModel): | |
| """Request to create a new credential.""" | |
| name: str = Field(..., description="Credential name") | |
| provider: str = Field(..., description="Provider name (openai, anthropic, etc.)") | |
| modalities: List[str] = Field( | |
| default_factory=list, | |
| description="Supported modalities (language, embedding, text_to_speech, speech_to_text)", | |
| ) | |
| api_key: Optional[str] = Field(None, description="API key (stored encrypted)") | |
| base_url: Optional[str] = Field(None, description="Base URL") | |
| endpoint: Optional[str] = Field(None, description="Endpoint URL (Azure)") | |
| api_version: Optional[str] = Field(None, description="API version (Azure)") | |
| endpoint_llm: Optional[str] = Field(None, description="LLM endpoint") | |
| endpoint_embedding: Optional[str] = Field(None, description="Embedding endpoint") | |
| endpoint_stt: Optional[str] = Field(None, description="STT endpoint") | |
| endpoint_tts: Optional[str] = Field(None, description="TTS endpoint") | |
| project: Optional[str] = Field(None, description="Project ID (Vertex)") | |
| location: Optional[str] = Field(None, description="Location (Vertex)") | |
| credentials_path: Optional[str] = Field( | |
| None, description="Credentials file path (Vertex)" | |
| ) | |
| class UpdateCredentialRequest(BaseModel): | |
| """Request to update an existing credential.""" | |
| name: Optional[str] = Field(None, description="Credential name") | |
| modalities: Optional[List[str]] = Field(None, description="Supported modalities") | |
| api_key: Optional[str] = Field(None, description="API key (stored encrypted)") | |
| base_url: Optional[str] = Field(None, description="Base URL") | |
| endpoint: Optional[str] = Field(None, description="Endpoint URL") | |
| api_version: Optional[str] = Field(None, description="API version") | |
| endpoint_llm: Optional[str] = Field(None, description="LLM endpoint") | |
| endpoint_embedding: Optional[str] = Field(None, description="Embedding endpoint") | |
| endpoint_stt: Optional[str] = Field(None, description="STT endpoint") | |
| endpoint_tts: Optional[str] = Field(None, description="TTS endpoint") | |
| project: Optional[str] = Field(None, description="Project ID") | |
| location: Optional[str] = Field(None, description="Location") | |
| credentials_path: Optional[str] = Field(None, description="Credentials path") | |
| class CredentialResponse(BaseModel): | |
| """Response for a credential (never includes api_key).""" | |
| id: str | |
| name: str | |
| provider: str | |
| modalities: List[str] | |
| base_url: Optional[str] = None | |
| endpoint: Optional[str] = None | |
| api_version: Optional[str] = None | |
| endpoint_llm: Optional[str] = None | |
| endpoint_embedding: Optional[str] = None | |
| endpoint_stt: Optional[str] = None | |
| endpoint_tts: Optional[str] = None | |
| project: Optional[str] = None | |
| location: Optional[str] = None | |
| credentials_path: Optional[str] = None | |
| has_api_key: bool = False | |
| created: str | |
| updated: str | |
| model_count: int = 0 | |
| decryption_error: Optional[str] = None | |
| class CredentialDeleteResponse(BaseModel): | |
| """Response for credential deletion.""" | |
| message: str | |
| deleted_models: int = 0 | |
| class DiscoveredModelResponse(BaseModel): | |
| """A model discovered from a provider.""" | |
| name: str | |
| provider: str | |
| model_type: Optional[str] = None | |
| description: Optional[str] = None | |
| class DiscoverModelsResponse(BaseModel): | |
| """Response from model discovery.""" | |
| credential_id: str | |
| provider: str | |
| discovered: List[DiscoveredModelResponse] | |
| class RegisterModelData(BaseModel): | |
| """A model to register with user-specified type.""" | |
| name: str | |
| provider: str | |
| model_type: str # Required: user specifies the type | |
| class RegisterModelsRequest(BaseModel): | |
| """Request to register discovered models.""" | |
| models: List[RegisterModelData] | |
| class RegisterModelsResponse(BaseModel): | |
| """Response from model registration.""" | |
| created: int | |
| existing: int | |
| class NotebookDeletePreview(BaseModel): | |
| notebook_id: str = Field(..., description="ID of the notebook") | |
| notebook_name: str = Field(..., description="Name of the notebook") | |
| note_count: int = Field(..., description="Number of notes that will be deleted") | |
| exclusive_source_count: int = Field( | |
| ..., description="Number of sources only in this notebook" | |
| ) | |
| shared_source_count: int = Field( | |
| ..., description="Number of sources shared with other notebooks" | |
| ) | |
| class NotebookDeleteResponse(BaseModel): | |
| message: str = Field(..., description="Success message") | |
| deleted_notes: int = Field(..., description="Number of notes deleted") | |
| deleted_sources: int = Field(..., description="Number of exclusive sources deleted") | |
| unlinked_sources: int = Field( | |
| ..., description="Number of sources unlinked from notebook" | |
| ) | |