wrolpi/modules/ai/schema.py
Roland 2f27ce49a7
ai: ai.yaml config, model catalog, and the GGUF downloader
ai.yaml is the source of truth (opt-in: enabled=False by default) with
active model, idle-unload minutes, context override, and tool-call
bound.  The model catalog follows the map pattern: a bundled constant
keeps the Manage tab working offline, the CDN manifest (ai/models.json
+ detached GPG signature) wins when reachable.  GGUF downloads clone
the map-extract flow — the per-file meta4 is GPG-verified once and the
same bytes handed to aria2c, so the verified hash is the enforced one;
unverified downloads are refused.  Models land in <media>/ai/models/
(gated by the existing media_mounted download rules).  Manage endpoints
(catalog with RAM-tier recommendation, settings) live under
/api/ai/manage/ which the future agent loop excludes from its tool
catalog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-13 20:10:15 -06:00

76 lines
1.7 KiB
Python

import dataclasses
from typing import Optional
@dataclasses.dataclass
class AISearchRequest:
search_str: Optional[str] = None
limit: int = 5
offset: int = 0
mimetypes: Optional[list] = None
tag_names: Optional[list] = None
@dataclasses.dataclass
class AIVideoSearchRequest:
search_str: Optional[str] = None
limit: int = 5
offset: int = 0
channel_id: Optional[int] = None
tag_names: Optional[list] = None
@dataclasses.dataclass
class AIArchiveSearchRequest:
search_str: Optional[str] = None
limit: int = 5
offset: int = 0
domain: Optional[str] = None
tag_names: Optional[list] = None
@dataclasses.dataclass
class AIDocSearchRequest:
search_str: Optional[str] = None
author: Optional[str] = None
subject: Optional[str] = None
language: Optional[str] = None
mimetype: Optional[str] = None
limit: int = 5
offset: int = 0
tag_names: Optional[list] = None
@dataclasses.dataclass
class AIZimSearchRequest:
search_str: Optional[str] = None
zim_id: Optional[int] = None
limit: int = 5
offset: int = 0
@dataclasses.dataclass
class AIHelpSearchRequest:
search_str: Optional[str] = None
limit: int = 5
@dataclasses.dataclass
class AIManageSettingsRequest:
enabled: Optional[bool] = None
active_model: Optional[str] = None
idle_unload_minutes: Optional[int] = None
context_size: Optional[int] = None
@dataclasses.dataclass
class AISearchResponse:
results: list = dataclasses.field(default_factory=list)
total: int = 0
@dataclasses.dataclass
class AIPagedTextResponse:
content: str = ''
next_offset: Optional[int] = None
total_chars: int = 0