"""Configuration de l'assistant IA (optionnel, desactive par defaut). La cle API n'est jamais stockee ici : seuls les parametres non sensibles (activation, fournisseur, URL, modele, timeout, temperature) le sont, via app.app_settings. La cle passe exclusivement par CredentialStore (app.credentials), jamais dans settings.json/sessions.json/logs.""" from __future__ import annotations from dataclasses import dataclass from app.ai.provider import AIProvider, OpenAICompatibleProvider from app.app_settings import get_setting, set_setting from app.credentials import CredentialStore AI_API_KEY_CREDENTIAL = "ai:api_key" DEFAULT_BASE_URL = "https://api.openai.com/v1" DEFAULT_PROVIDER_NAME = "OpenAI" DEFAULT_MODEL = "" DEFAULT_TIMEOUT = 30.0 DEFAULT_TEMPERATURE = 0.3 @dataclass class AISettings: enabled: bool = False provider_name: str = DEFAULT_PROVIDER_NAME base_url: str = DEFAULT_BASE_URL model: str = DEFAULT_MODEL timeout: float = DEFAULT_TIMEOUT temperature: float = DEFAULT_TEMPERATURE def load_ai_settings() -> AISettings: try: timeout = float(get_setting("ai_timeout", DEFAULT_TIMEOUT)) except (TypeError, ValueError): timeout = DEFAULT_TIMEOUT try: temperature = float(get_setting("ai_temperature", DEFAULT_TEMPERATURE)) except (TypeError, ValueError): temperature = DEFAULT_TEMPERATURE return AISettings( enabled=bool(get_setting("ai_enabled", False)), provider_name=get_setting("ai_provider_name", DEFAULT_PROVIDER_NAME) or DEFAULT_PROVIDER_NAME, base_url=get_setting("ai_base_url", DEFAULT_BASE_URL) or DEFAULT_BASE_URL, model=get_setting("ai_model", DEFAULT_MODEL) or DEFAULT_MODEL, timeout=timeout, temperature=temperature, ) def save_ai_settings(settings: AISettings) -> None: set_setting("ai_enabled", settings.enabled) set_setting("ai_provider_name", settings.provider_name) set_setting("ai_base_url", settings.base_url) set_setting("ai_model", settings.model) set_setting("ai_timeout", settings.timeout) set_setting("ai_temperature", settings.temperature) def load_api_key(credential_store: CredentialStore) -> str: return credential_store.get_secret(AI_API_KEY_CREDENTIAL) or "" def save_api_key(credential_store: CredentialStore, api_key: str) -> bool: if api_key: return credential_store.set_secret(AI_API_KEY_CREDENTIAL, api_key) credential_store.delete_secret(AI_API_KEY_CREDENTIAL) return True def build_provider_from_settings(credential_store: CredentialStore) -> AIProvider | None: """None si l'assistant est desactive : point d'appel unique pour tous les endroits de l'UI qui ont besoin d'un fournisseur pret a l'emploi.""" settings = load_ai_settings() if not settings.enabled: return None return OpenAICompatibleProvider( base_url=settings.base_url, api_key=load_api_key(credential_store), model=settings.model, timeout=settings.timeout, temperature=settings.temperature, )