Kazeia-central/kazeia_central/core/provider/models.py

158 lines
4.3 KiB
Python

"""Modèles pydantic des endpoints du provider `com.kazeia.provider`.
Source de vérité : KazeiaTelemetryProvider.kt (kazeia-android) — vérifié contre le
code. Les valeurs d'`adb content query` arrivent en chaînes ; les validators
pydantic les coercent. Les booléens du provider sont des entiers 0/1.
"""
from __future__ import annotations
from pydantic import BaseModel, field_validator
def _as_bool(v):
if isinstance(v, bool):
return v
return str(v).strip() in ("1", "true", "True")
class _Base(BaseModel):
model_config = {"extra": "ignore"}
class StateRow(_Base):
# Tous optionnels : sur une flotte hétérogène (§8 handshake), une tablette
# plus ancienne peut omettre une colonne. Mieux vaut une cellule vide qu'un
# endpoint /state mort (extra="ignore" tolère déjà les colonnes en PLUS ;
# ceci tolère les colonnes en MOINS).
pid: int | None = None
uptime_seconds: int | None = None
pss_mb: int | None = None
ion_mb: int | None = None
# Colonnes présentes dans le code mais non documentées au §4.1 du CLAUDE.md
# (relevées par le dev) :
anon_pages_mb: int | None = None
mem_available_mb: int | None = None
class TurnRow(_Base):
timestamp: int
input_kind: str | None = None
status: str | None = None
total_ms: int | None = None
stt_ms: int | None = None
llm_ttft_ms: int | None = None
llm_gen_ms: int | None = None
llm_tokens: int | None = None
time_to_first_audio_ms: int | None = None
audio_playback_ms: int | None = None
class CrashRow(_Base):
timestamp: int
component: str | None = None
message: str | None = None
class ModelRow(_Base):
id: str
display_name: str | None = None
pte_path: str | None = None
tokenizer_path: str | None = None
role: str | None = None
size_mb: int | None = None
max_seq_len: int | None = None
file_exists: bool = False
notes: str | None = None
_b = field_validator("file_exists", mode="before")(_as_bool)
class VoiceRow(_Base):
id: str
name: str | None = None
state: str | None = None
wav_exists: bool = False
prefix_exists: bool = False
suffix_exists: bool = False
wav_size_bytes: int | None = None
wav_duration_seconds: float | None = None
wav_path: str | None = None
created_at: int | None = None
_b = field_validator("wav_exists", "prefix_exists", "suffix_exists", mode="before")(_as_bool)
class RagStatus(_Base):
enabled: bool = False
ready: bool = False
model: str | None = None
dim: int | None = None
doc_count: int | None = None
chunk_count: int | None = None
pending: int | None = None
_b = field_validator("enabled", "ready", mode="before")(_as_bool)
class RagDocRow(_Base):
source: str
char_count: int | None = None
chunk_count: int | None = None
updated_at: int | None = None
class RagHit(_Base):
source: str
score: float
text: str | None = None
class UpdateStatus(_Base):
phase: str | None = None
app_update_available: bool = False
app_version_name: str | None = None
app_size: int | None = None
content_count: int | None = None
content_size: int | None = None
progress_done: int | None = None
progress_total: int | None = None
label: str | None = None
error: str | None = None
last_check: int | None = None
_b = field_validator("app_update_available", mode="before")(_as_bool)
class DistConfig(_Base):
webdav_base: str | None = None
webdav_user: str | None = None
has_password: bool = False
is_customized: bool = False
_b = field_validator("has_password", "is_customized", mode="before")(_as_bool)
class SessionRow(_Base):
profile_id: str
session_id: str
started_at: int | None = None
ended_at: int | None = None
turn_count: int | None = None
class ProfileRow(_Base):
id: str
display_name: str | None = None
avatar_color: int | None = None
voice_id: str | None = None
has_pin: bool = False
created_at: int | None = None
last_used_at: int | None = None
is_default: bool = False
turn_count: int | None = None
# system_prompt_override / notes : présents en colonne mais multilignes →
# à lire via `profile_dump_json` (RPC futur), pas via query.
_b = field_validator("has_pin", "is_default", mode="before")(_as_bool)