Files
ledgerdock/backend/app/schemas/settings.py

259 lines
8.4 KiB
Python

"""Pydantic schemas for application-level runtime settings."""
from pydantic import BaseModel, Field
class ProviderSettingsResponse(BaseModel):
"""Represents a persisted model provider with non-secret connection metadata."""
id: str
label: str
provider_type: str = "openai_compatible"
base_url: str
timeout_seconds: int
api_key_set: bool
api_key_masked: str = ""
class ProviderSettingsUpdateRequest(BaseModel):
"""Represents a model provider create-or-update request."""
id: str
label: str
provider_type: str = "openai_compatible"
base_url: str
timeout_seconds: int = Field(default=45, ge=5, le=180)
api_key: str | None = None
clear_api_key: bool = False
class OcrTaskSettingsResponse(BaseModel):
"""Represents OCR task runtime settings and prompt configuration."""
enabled: bool
provider_id: str
model: str
prompt: str
class OcrTaskSettingsUpdateRequest(BaseModel):
"""Represents OCR task settings updates."""
enabled: bool | None = None
provider_id: str | None = None
model: str | None = None
prompt: str | None = None
class SummaryTaskSettingsResponse(BaseModel):
"""Represents summarization task runtime settings."""
enabled: bool
provider_id: str
model: str
prompt: str
max_input_tokens: int
class SummaryTaskSettingsUpdateRequest(BaseModel):
"""Represents summarization task settings updates."""
enabled: bool | None = None
provider_id: str | None = None
model: str | None = None
prompt: str | None = None
max_input_tokens: int | None = Field(default=None, ge=512, le=64000)
class RoutingTaskSettingsResponse(BaseModel):
"""Represents routing task runtime settings for path and tag classification."""
enabled: bool
provider_id: str
model: str
prompt: str
neighbor_count: int
neighbor_min_similarity: float
auto_apply_confidence_threshold: float
auto_apply_neighbor_similarity_threshold: float
neighbor_path_override_enabled: bool
neighbor_path_override_min_similarity: float
neighbor_path_override_min_gap: float
neighbor_path_override_max_confidence: float
class RoutingTaskSettingsUpdateRequest(BaseModel):
"""Represents routing task settings updates."""
enabled: bool | None = None
provider_id: str | None = None
model: str | None = None
prompt: str | None = None
neighbor_count: int | None = Field(default=None, ge=1, le=40)
neighbor_min_similarity: float | None = Field(default=None, ge=0.0, le=1.0)
auto_apply_confidence_threshold: float | None = Field(default=None, ge=0.0, le=1.0)
auto_apply_neighbor_similarity_threshold: float | None = Field(default=None, ge=0.0, le=1.0)
neighbor_path_override_enabled: bool | None = None
neighbor_path_override_min_similarity: float | None = Field(default=None, ge=0.0, le=1.0)
neighbor_path_override_min_gap: float | None = Field(default=None, ge=0.0, le=1.0)
neighbor_path_override_max_confidence: float | None = Field(default=None, ge=0.0, le=1.0)
class UploadDefaultsResponse(BaseModel):
"""Represents default upload destination and default tags."""
logical_path: str
tags: list[str] = Field(default_factory=list)
class UploadDefaultsUpdateRequest(BaseModel):
"""Represents updates for default upload destination and default tags."""
logical_path: str | None = None
tags: list[str] | None = None
class DisplaySettingsResponse(BaseModel):
"""Represents document-list display preferences."""
cards_per_page: int = Field(default=12, ge=1, le=200)
log_typing_animation_enabled: bool = True
class DisplaySettingsUpdateRequest(BaseModel):
"""Represents updates for document-list display preferences."""
cards_per_page: int | None = Field(default=None, ge=1, le=200)
log_typing_animation_enabled: bool | None = None
class ProcessingLogRetentionSettingsResponse(BaseModel):
"""Represents retention limits used when pruning processing pipeline logs."""
keep_document_sessions: int = Field(default=2, ge=0, le=20)
keep_unbound_entries: int = Field(default=80, ge=0, le=400)
class ProcessingLogRetentionSettingsUpdateRequest(BaseModel):
"""Represents partial updates for processing log retention limits."""
keep_document_sessions: int | None = Field(default=None, ge=0, le=20)
keep_unbound_entries: int | None = Field(default=None, ge=0, le=400)
class PredefinedPathEntryResponse(BaseModel):
"""Represents one predefined logical path with global discoverability scope."""
value: str
global_shared: bool
class PredefinedPathEntryUpdateRequest(BaseModel):
"""Represents one predefined logical path create-or-update request."""
value: str
global_shared: bool = False
class PredefinedTagEntryResponse(BaseModel):
"""Represents one predefined tag with global discoverability scope."""
value: str
global_shared: bool
class PredefinedTagEntryUpdateRequest(BaseModel):
"""Represents one predefined tag create-or-update request."""
value: str
global_shared: bool = False
class HandwritingStyleSettingsResponse(BaseModel):
"""Represents handwriting-style clustering settings used by Typesense image embeddings."""
enabled: bool
embed_model: str
neighbor_limit: int
match_min_similarity: float
bootstrap_match_min_similarity: float
bootstrap_sample_size: int
image_max_side: int
class HandwritingStyleSettingsUpdateRequest(BaseModel):
"""Represents updates for handwriting-style clustering and match thresholds."""
enabled: bool | None = None
embed_model: str | None = None
neighbor_limit: int | None = Field(default=None, ge=1, le=32)
match_min_similarity: float | None = Field(default=None, ge=0.0, le=1.0)
bootstrap_match_min_similarity: float | None = Field(default=None, ge=0.0, le=1.0)
bootstrap_sample_size: int | None = Field(default=None, ge=1, le=30)
image_max_side: int | None = Field(default=None, ge=256, le=4096)
class TaskSettingsResponse(BaseModel):
"""Represents all task-level model bindings and prompt settings."""
ocr_handwriting: OcrTaskSettingsResponse
summary_generation: SummaryTaskSettingsResponse
routing_classification: RoutingTaskSettingsResponse
class TaskSettingsUpdateRequest(BaseModel):
"""Represents partial updates for task-level settings."""
ocr_handwriting: OcrTaskSettingsUpdateRequest | None = None
summary_generation: SummaryTaskSettingsUpdateRequest | None = None
routing_classification: RoutingTaskSettingsUpdateRequest | None = None
class AppSettingsResponse(BaseModel):
"""Represents all application settings exposed by the API."""
upload_defaults: UploadDefaultsResponse
display: DisplaySettingsResponse
processing_log_retention: ProcessingLogRetentionSettingsResponse
handwriting_style_clustering: HandwritingStyleSettingsResponse
predefined_paths: list[PredefinedPathEntryResponse] = Field(default_factory=list)
predefined_tags: list[PredefinedTagEntryResponse] = Field(default_factory=list)
providers: list[ProviderSettingsResponse]
tasks: TaskSettingsResponse
class AppSettingsUpdateRequest(BaseModel):
"""Represents full settings update input for providers and task bindings."""
upload_defaults: UploadDefaultsUpdateRequest | None = None
display: DisplaySettingsUpdateRequest | None = None
processing_log_retention: ProcessingLogRetentionSettingsUpdateRequest | None = None
handwriting_style_clustering: HandwritingStyleSettingsUpdateRequest | None = None
predefined_paths: list[PredefinedPathEntryUpdateRequest] | None = None
predefined_tags: list[PredefinedTagEntryUpdateRequest] | None = None
providers: list[ProviderSettingsUpdateRequest] | None = None
tasks: TaskSettingsUpdateRequest | None = None
class HandwritingSettingsResponse(BaseModel):
"""Represents legacy handwriting response shape kept for backward compatibility."""
provider: str = "openai_compatible"
enabled: bool
openai_base_url: str
openai_model: str
openai_timeout_seconds: int
openai_api_key_set: bool
openai_api_key_masked: str = ""
class HandwritingSettingsUpdateRequest(BaseModel):
"""Represents legacy handwriting update shape kept for backward compatibility."""
enabled: bool | None = None
openai_base_url: str | None = None
openai_model: str | None = None
openai_timeout_seconds: int | None = Field(default=None, ge=5, le=180)
openai_api_key: str | None = None
clear_openai_api_key: bool = False