llm-ready-data / app /models /schemas.py
xce009's picture
add temp db warning to vector store responses
8a20097
Raw
History Blame Contribute Delete
23.2 kB
from __future__ import annotations
from enum import Enum
from typing import Any, Dict, List, Literal, Optional
from pydantic import BaseModel, Field, field_validator, model_validator
class RouteConfig(BaseModel):
name: str = Field(..., min_length=1, description="Route name")
utterances: List[str] = Field(..., min_length=1, description="Example phrases for this route")
models: List[str] = Field(default_factory=list, description="Model identifiers assigned to this route")
class SemanticRouterRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=50000, description="User query to route")
routes: List[RouteConfig] = Field(..., min_length=1, description="Route definitions")
threshold: float = Field(default=0.3, ge=0.0, le=1.0, description="Minimum similarity score to match")
class SemanticRouterResponse(BaseModel):
success: bool
time_ms: float
name: Optional[str] = None
models: List[str] = []
error: Optional[str] = None
confidence: Optional[float] = None
margin: Optional[float] = None
threshold: Optional[float] = None
matched_utterance: Optional[str] = None
class TokenCountRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=1000000, description="Text to count tokens for")
encoding: str = Field(default="o200k_base", description="TikToken encoding name")
class TokenCountResponse(BaseModel):
success: bool
time_ms: float
token_count: int = 0
char_count: int = 0
encoding: str = "o200k_base"
error: Optional[str] = None
class ConversionMetadata(BaseModel):
source: str
char_count: int
word_count: int
line_count: int
file_size_bytes: int
mime_type: str
content_hash: str
token_estimate: int
class ConversionResponse(BaseModel):
success: bool
time_ms: float
content: str
return_json: bool = False
json_content: Optional[Any] = None
metadata: Optional[ConversionMetadata] = None
error_message: Optional[str] = None
class UrlRequest(BaseModel):
url: str
return_json: bool = False
mappings: Optional[Dict[str, Dict[str, Any]]] = None
model_config = {"populate_by_name": True}
@field_validator("url")
@classmethod
def validate_scheme(cls, v: str) -> str:
if not v.startswith(("http://", "https://")):
raise ValueError("Only http/https URLs are supported.")
return v
class BatchUrlRequest(BaseModel):
urls: List[str]
return_json: bool = False
mappings: Optional[Dict[str, Dict[str, Any]]] = None
@field_validator("urls")
@classmethod
def validate_urls(cls, v: List[str]) -> List[str]:
for url in v:
if not url.startswith(("http://", "https://")):
raise ValueError(f"Invalid URL scheme: {url}")
if len(v) > 20:
raise ValueError("Maximum 20 URLs per batch request.")
return v
class BatchFileResult(BaseModel):
filename: str
success: bool
time_ms: float
content: Optional[str] = None
json_content: Optional[Any] = None
error: Optional[str] = None
metadata: Optional[ConversionMetadata] = None
class BatchResponse(BaseModel):
total: int
succeeded: int
failed: int
total_time_ms: float
results: List[BatchFileResult]
class HealthResponse(BaseModel):
success: bool
status: str
version: str
uptime_seconds: float
timestamp: str
class InfoResponse(BaseModel):
success: bool
app: str
version: str
python_version: str
platform: str
uptime_seconds: float
max_upload_mb: int
supported_extensions: int
timestamp: str
class SupportedFormatsResponse(BaseModel):
success: bool
total_count: int
all_extensions: List[str]
by_category: Dict[str, List[str]]
class SpacyLabelsResponse(BaseModel):
success: bool
spacy_labels: Dict[str, str]
source_types: Dict[str, str]
example_mappings: Dict[str, Dict[str, Any]]
class SSLConfig(BaseModel):
enabled: bool = False
ca_cert: Optional[str] = None
cert: Optional[str] = None
key: Optional[str] = None
class DatabaseConnection(BaseModel):
host: str
port: Optional[int] = None
database: str
username: str
password: str = Field(repr=False)
selected_schema: str = Field(default="public", description="PostgreSQL schema (ignored for MySQL/MongoDB)")
ssl: SSLConfig = SSLConfig()
@field_validator("host")
@classmethod
def host_not_empty(cls, v: str) -> str:
stripped = v.strip()
if not stripped:
raise ValueError("host must not be empty")
return stripped
@field_validator("port")
@classmethod
def validate_port(cls, v: Optional[int]) -> Optional[int]:
if v is not None and (v < 1 or v > 65535):
raise ValueError("port must be between 1 and 65535")
return v
@field_validator("database")
@classmethod
def database_not_empty(cls, v: str) -> str:
stripped = v.strip()
if not stripped:
raise ValueError("database must not be empty")
return stripped
def safe_repr(self) -> str:
return (
f"host={self.host}, port={self.port}, "
f"database={self.database}, username={self.username}"
)
class DatabaseQueryRequest(BaseModel):
db_type: Literal["mysql", "postgresql", "mongodb"]
connection: DatabaseConnection
query: List[Any]
use_transaction: bool = True
query_timeout_seconds: float = Field(default=10.0, ge=1.0, le=300.0)
connection_timeout_seconds: float = Field(default=10.0, ge=1.0, le=60.0)
max_rows: int = Field(default=10000, ge=1, le=1_000_000)
@model_validator(mode="after")
def validate_query(self) -> "DatabaseQueryRequest":
if not self.query:
raise ValueError("query must not be empty")
if self.db_type in ("mysql", "postgresql"):
for i, q in enumerate(self.query):
if not isinstance(q, str):
raise ValueError(f"query[{i}] must be a string for {self.db_type}")
if not q.strip():
raise ValueError(f"query[{i}] must not be empty")
elif self.db_type == "mongodb":
for i, q in enumerate(self.query):
if not isinstance(q, dict):
raise ValueError(f"query[{i}] must be an object with 'collection' and 'pipeline'")
if "collection" not in q or "pipeline" not in q:
raise ValueError(f"query[{i}] must have 'collection' and 'pipeline' fields")
if not isinstance(q["pipeline"], list):
raise ValueError(f"query[{i}].pipeline must be an array")
return self
def to_connection_config(self) -> Dict[str, Any]:
return {
"db_type": self.db_type,
"host": self.connection.host,
"port": self.connection.port or self._default_port(),
"database": self.connection.database,
"username": self.connection.username,
"password": self.connection.password,
"selected_schema": self.connection.selected_schema,
"ssl_enabled": self.connection.ssl.enabled,
"ssl_ca_cert": self.connection.ssl.ca_cert,
"ssl_cert": self.connection.ssl.cert,
"ssl_key": self.connection.ssl.key,
"query_timeout_seconds": self.query_timeout_seconds,
"connection_timeout_seconds": self.connection_timeout_seconds,
"max_rows": self.max_rows,
}
def _default_port(self) -> int:
return {"mysql": 3306, "postgresql": 5432, "mongodb": 27017}[self.db_type]
class StatementResultSchema(BaseModel):
success: bool
rows: int = 0
data: List[Dict[str, Any]] = []
error: Optional[str] = None
error_code: Optional[str] = None
class DatabaseQueryError(BaseModel):
message: str
code: Optional[str] = None
class DatabaseQueryResponse(BaseModel):
success: bool
execution_time_ms: float
results: Optional[List[StatementResultSchema]] = None
error: Optional[DatabaseQueryError] = None
class TableValidationResult(BaseModel):
name: str
exists: bool
error: Optional[str] = None
class DatabaseValidateRequest(BaseModel):
db_type: Literal["mysql", "postgresql", "mongodb"]
connection: DatabaseConnection
selected_schema: str = Field(default="public", description="PostgreSQL schema (ignored for MySQL/MongoDB)")
table_or_collection_names: List[str] = Field(default_factory=list, description="Optional list of tables/collections to check for existence")
def to_connection_config(self) -> Dict[str, Any]:
return {
"db_type": self.db_type,
"host": self.connection.host,
"port": self.connection.port or {"mysql": 3306, "postgresql": 5432, "mongodb": 27017}[self.db_type],
"database": self.connection.database,
"username": self.connection.username,
"password": self.connection.password,
"selected_schema": self.selected_schema,
"ssl_enabled": self.connection.ssl.enabled,
"ssl_ca_cert": self.connection.ssl.ca_cert,
"ssl_cert": self.connection.ssl.cert,
"ssl_key": self.connection.ssl.key,
}
class DatabaseValidateResponse(BaseModel):
success: bool
time_ms: float
message: str
connection_details: str
error_message: Optional[str] = None
tables: List[TableValidationResult] = []
class EmbeddingItem(BaseModel):
success: bool
time_ms: float
embeddings: List[float] = Field(default_factory=list)
dimension: int = 0
error_message: Optional[str] = None
class EmbeddingRequest(BaseModel):
content: List[str] = Field(..., min_length=1, max_length=10, description="Array of text strings to embed (max 10)")
dimension: int = Field(default=384, ge=384, le=384, description="Target embedding dimension (384 only)")
@field_validator("content")
@classmethod
def validate_content_length(cls, v: List[str]) -> List[str]:
if len(v) > 10:
raise ValueError("Maximum 10 text items allowed per request.")
if len(v) < 1:
raise ValueError("At least 1 text item is required.")
return v
class EmbeddingResponse(BaseModel):
success: bool
time_ms: float
success_count: int
failed_count: int
error_message: Optional[str] = None
results: List[EmbeddingItem]
# class VisionUrlRequest(BaseModel): # DISABLED (OOM mitigation)
# urls: List[str] = Field(..., min_length=1, max_length=5, description="Array of image URLs to embed (max 5)")
#
# @field_validator("urls")
# @classmethod
# def validate_urls(cls, v: List[str]) -> List[str]:
# for url in v:
# if not url.startswith(("http://", "https://")):
# raise ValueError(f"Invalid URL scheme: {url}")
# return v
class CodeItem(BaseModel):
language: Literal["python", "javascript"] # "java" DISABLED (OOM mitigation)
code: str = Field(..., min_length=1, max_length=65536, description="Source code to execute")
class CodeExecutionRequest(BaseModel):
items: List[CodeItem] = Field(..., min_length=1, max_length=5, description="Code execution items (max 5)")
class CodeExecutionItemResult(BaseModel):
success: bool
output: str = ""
error: Optional[str] = None
exit_code: Optional[int] = None
execution_time_ms: Optional[float] = None
language: str
timed_out: bool = False
class CodeExecutionResponse(BaseModel):
success: bool
time_ms: float
success_count: int
failed_count: int
error_message: Optional[str] = None
results: List[CodeExecutionItemResult]
# ---------------------------------------------------------------------------
# Web Search (SearXNG)
# ---------------------------------------------------------------------------
class WebSearchResult(BaseModel):
title: str
url: str
content: str = ""
engine: str = ""
category: str = ""
published_date: Optional[str] = None
class WebSearchSuggestion(BaseModel):
suggestion: str
class WebSearchInfobox(BaseModel):
title: str = ""
content: str = ""
infobox: str = ""
engine: str = ""
urls: List[Dict[str, Any]] = []
class WebSearchRequest(BaseModel):
q: str = Field(..., min_length=1, max_length=500, description="Search query")
categories: str = Field(default="general", description="Comma-separated categories: general,images,videos,news,music,it,science,map,files,social media")
language: str = Field(default="en", description="Language code (en, fr, de, hi, bn, etc.)")
pageno: int = Field(default=1, ge=1, le=100, description="Page number")
time_range: Optional[str] = Field(default=None, description="Time range: day, week, month, year")
safesearch: int = Field(default=0, ge=0, le=2, description="Safe search level: 0=off, 1=moderate, 2=strict")
engines: Optional[str] = Field(default=None, description="Comma-separated engine names to restrict to")
max_results: int = Field(default=10, ge=1, le=50, description="Max results to return")
class WebSearchResponse(BaseModel):
success: bool
time_ms: float
query: str
number_of_results: int
results: List[WebSearchResult]
suggestions: List[str] = []
infoboxes: List[WebSearchInfobox] = []
error: Optional[str] = None
class WebSearchConfigResponse(BaseModel):
success: bool
time_ms: float
instance_name: Optional[str] = None
version: Optional[str] = None
engines: List[Dict[str, Any]] = []
categories: List[str] = []
plugins: List[str] = []
error: Optional[str] = None
# ---------------------------------------------------------------------------
# Web Scraping (Scrapling)
# ---------------------------------------------------------------------------
class FetcherType(str, Enum):
http = "http"
dynamic = "dynamic"
stealth = "stealth"
class SelectorType(str, Enum):
css = "css"
xpath = "xpath"
class ExtractionRule(BaseModel):
field_name: str = Field(..., description="Key name in the output JSON.")
selector: str = Field(..., description="CSS or XPath selector (e.g. '.price::text').")
selector_type: SelectorType = Field(SelectorType.css)
extract_all: bool = Field(False, description="Get a list of all matches.")
auto_save: bool = Field(False, description="Commit element's DOM fingerprint to local SQLite.")
auto_match: bool = Field(False, description="Use deterministic healing if layout breaks.")
class ScrapeRequest(BaseModel):
url: str = Field(..., description="Target URL to scrape.")
fetcher_type: FetcherType = Field(FetcherType.http)
rules: List[ExtractionRule] = Field(..., min_length=1, max_length=50)
proxy: Optional[str] = Field(None, description="Proxy URL for this request.")
network_idle: bool = Field(False, description="Wait for network idle (Dynamic/Stealth only).")
class ScrapeResponse(BaseModel):
success: bool
time_ms: float
url: str
data: Dict[str, Any]
fetcher: str
error: Optional[str] = None
class ScrapeHealthResponse(BaseModel):
success: bool
framework: str
version: str
fetchers_available: List[str]
error: Optional[str] = None
class WebSearchAutocompleteRequest(BaseModel):
q: str = Field(..., min_length=1, max_length=200, description="Search query prefix for autocomplete")
class WebSearchAutocompleteResponse(BaseModel):
success: bool
time_ms: float
query: str
suggestions: List[str] = []
error: Optional[str] = None
class WebSearchStatsResponse(BaseModel):
success: bool
time_ms: float
stats: Optional[Dict[str, Any]] = None
error: Optional[str] = None
class WebSearchEngineDescriptionsResponse(BaseModel):
success: bool
time_ms: float
engines: Optional[Dict[str, Any]] = None
error: Optional[str] = None
class TokenGenerateRequest(BaseModel):
subject: str = Field(..., min_length=1, max_length=256, description="Token subject (user ID, client ID, etc.)")
role: Optional[str] = Field(None, max_length=64, description="User role for RBAC")
permissions: Optional[List[str]] = Field(None, description="List of permission strings")
issuer: Optional[str] = Field(None, max_length=128, description="Token issuer (overrides default)")
audience: Optional[str] = Field(None, max_length=128, description="Token audience")
expiry_minutes: Optional[int] = Field(None, ge=1, le=525600, description="Token lifetime in minutes")
not_before_minutes: int = Field(default=0, ge=0, le=525600, description="Delay token validity by N minutes")
extra_claims: Optional[Dict[str, Any]] = Field(None, description="Additional custom claims")
secret: Optional[str] = Field(None, description="JWT signing secret (auto-generated if not provided)")
algorithm: Optional[str] = Field(None, pattern="^(HS256|HS384|HS512)$", description="JWT signing algorithm (defaults to HS256)")
class TokenGenerateResponse(BaseModel):
success: bool
time_ms: float
token: Optional[str] = None
claims: Optional[Dict[str, Any]] = None
expires_at: Optional[str] = None
valid_for: Optional[str] = None
secret: Optional[str] = None
algorithm: Optional[str] = None
error: Optional[str] = None
class TokenValidateRequest(BaseModel):
token: str = Field(..., min_length=1, description="JWT token string to validate")
audience: Optional[str] = Field(None, description="Expected audience to verify against")
secret: Optional[str] = Field(None, description="Signing secret used during generation (uses server default if not provided)")
algorithm: Optional[str] = Field(None, pattern="^(HS256|HS384|HS512)$", description="Algorithm used during generation (uses server default if not provided)")
class TokenValidateResponse(BaseModel):
success: bool
time_ms: float
valid: bool
claims: Optional[Dict[str, Any]] = None
error: Optional[str] = None
class SqlValidationRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=100000, description="SQL query string to validate")
dialect: Optional[str] = Field(None, description="SQL dialect (mysql, postgres, bigquery, snowflake, sqlite, etc.)")
class SqlValidationResponse(BaseModel):
success: bool
time_ms: float
valid: bool
query_type: str
dialect: Optional[str] = None
is_read_only: bool
errors: List[str] = []
warnings: List[str] = []
tables: List[str] = []
columns: List[str] = []
# ---------------------------------------------------------------------------
# Vector Store (RAG) - Powered by Zvec
# ---------------------------------------------------------------------------
class VectorStoreCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=256, description="Human-readable name")
description: Optional[str] = Field(None, max_length=1024)
metadata: Dict[str, Any] = Field(default_factory=dict)
class VectorStoreResponse(BaseModel):
success: bool
vector_store_id: str
app_id: str
name: str
description: Optional[str] = None
embedding_dimension: int
document_count: int
created_at: str
metadata: Dict[str, Any] = {}
warning: Optional[Dict[str, str]] = None
class VectorStoreListResponse(BaseModel):
success: bool
total: int
stores: List[VectorStoreResponse]
class DocumentIngestRequest(BaseModel):
doc_id: str = Field(..., min_length=1, max_length=256)
text: str = Field(..., min_length=1)
source: Optional[str] = Field(None, max_length=512)
metadata: Dict[str, Any] = Field(default_factory=dict)
chunk_size: Optional[int] = Field(None, ge=64, le=4096)
chunk_overlap: Optional[int] = Field(None, ge=0, le=512)
class DocumentIngestResponse(BaseModel):
success: bool
vector_store_id: str
doc_id: str
chunks_ingested: int
time_ms: float
error: Optional[str] = None
class DocumentIngestUrlRequest(BaseModel):
url: str = Field(..., min_length=1, description="URL of the PDF file to ingest")
doc_id: str = Field(..., min_length=1, max_length=256)
chunk_size: int = Field(512, ge=64, le=4096)
chunk_overlap: int = Field(64, ge=0, le=512)
class SearchRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=5000, description="Natural language query")
top_k: int = Field(default=10, ge=1, le=100, description="Max results to return")
filter: Optional[str] = Field(None, description="Filter expression (e.g. 'category = \"tech\"')")
min_score: Optional[float] = Field(None, ge=0.0, le=1.0, description="Minimum similarity score threshold")
include_vectors: bool = Field(default=False, description="Include vector embeddings in results")
include_metadata: bool = Field(default=False, description="Include source metadata in results")
class SearchResultItem(BaseModel):
rank: int
doc_id: str
chunk_index: int
text: str
score: float
source: Optional[str] = None
metadata: Dict[str, Any] = {}
vector: Optional[List[float]] = None
class SearchResponse(BaseModel):
success: bool
vector_store_id: str
query: str
results: List[SearchResultItem]
total_results: int
time_ms: float
error: Optional[str] = None
class DeleteRequest(BaseModel):
ids: Optional[List[str]] = Field(None)
filter: Optional[str] = Field(None)
class DeleteResponse(BaseModel):
success: bool
vector_store_id: str
deleted_count: int
time_ms: float
error: Optional[str] = None
# ---------------------------------------------------------------------------
# Webhook / Socket
# ---------------------------------------------------------------------------
class ChannelCreateRequest(BaseModel):
channel_id: Optional[str] = Field(None, min_length=1, max_length=64, description="Optional custom channel ID")
secret: Optional[str] = Field(None, min_length=1, description="HMAC secret for webhook verification")
buffer_size: Optional[int] = Field(None, ge=0, le=10000, description="Replay buffer size (0 = no replay)")
class ChannelCreateResponse(BaseModel):
channel_id: str
webhook_url: str
ws_url: str
secret: Optional[str] = None
buffer_size: int
class ChannelInfoResponse(BaseModel):
channel_id: str
subscribers: int
messages: int
buffered: int
buffer_size: int
has_secret: bool
created_at: float
last_activity: float
class ChannelListItem(BaseModel):
channel_id: str
subscribers: int
messages: int
buffered: int
created_at: float
last_activity: float
class ChannelListResponse(BaseModel):
channels: List[ChannelListItem]
class ChannelDeleteResponse(BaseModel):
deleted: str
class WebhookResponse(BaseModel):
status: str
channel: str
subscribers_notified: int
message_id: int
class WebhookSocketStatsResponse(BaseModel):
channels: int
total_messages: int
total_subscribers: int
channels_detail: Dict[str, Dict[str, Any]]