"""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): pid: int uptime_seconds: int pss_mb: int ion_mb: int # 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 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)