Files
PTSRaiAgent/app/config.py
T
ElebobaandClaude Opus 5.5 ade93d959b
CI / ruff · pytest (pull_request) Successful in 2m15s
feat(bot): GigaChat function-calling provider for the chat agent; never 500 on provider errors
OpenRouter returns 403 «Access denied by security policy» from the RF server, and parsing that string error
crashed the request (500 instead of the fallback answer). Error parsing is robust now and any agent exception
degrades to the extractive answer. GigaChat tool provider (GigaChat-2-Pro by default) converts the agent's
OpenAI-style history to function_call / role=function; auto mode prefers GigaChat when credentials are set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:46:21 +03:00

58 lines
2.5 KiB
Python

"""Typed application settings. The only place that reads environment variables."""
from functools import lru_cache
from pathlib import Path
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
ROOT_DIR = Path(__file__).resolve().parent.parent
class Settings(BaseSettings):
# Empty values in .env (e.g. `TG_BOT_TOKEN=`) fall back to defaults instead of failing validation.
model_config = SettingsConfigDict(env_file=".env", extra="ignore", env_ignore_empty=True)
# Two separate databases (ADR-003): identity holds direct identifiers, clinical holds pseudonymised data.
identity_db_url: str = "postgresql+asyncpg://identity_app:identity_app@localhost:5432/identity"
clinical_db_url: str = "postgresql+asyncpg://clinical_app:clinical_app@localhost:5432/clinical"
# HMAC key linking identity and clinical records (hex, 32+ bytes).
pseudo_key: str = Field(default="dev-only-insecure-key-change-me-0000000000000000", min_length=32)
# LLM: GigaChat only (152-FZ: no foreign APIs). `mock` is the offline default (ADR-004).
llm_provider: Literal["mock", "gigachat"] = "mock"
gigachat_credentials: str = ""
gigachat_scope: str = "GIGACHAT_API_PERS"
gigachat_model: str = "GigaChat"
gigachat_verify_ssl: bool = False
# Chat agent (tool-using LLM over RAG + patient record). `auto` = OpenRouter if a key is set, else off
# (extractive RAG / instruction answers stay). OpenRouter is a TEMPORARY prototype provider on synthetic
# data only — prod must use GigaChat (152-FZ, foreign services are forbidden for real PII).
chat_agent: Literal["auto", "off", "mock", "openrouter", "gigachat"] = "auto"
openrouter_api_key: str = ""
openrouter_model: str = "google/gemini-2.5-flash-lite"
openrouter_base_url: str = "https://openrouter.ai/api/v1"
gigachat_agent_model: str = "GigaChat-2-Pro" # function calling quality matters more than price here
chat_agent_timeout_s: float = 25.0
chat_agent_max_steps: int = 5
# Telegram is used only for the prototype on synthetic data; prod channel is MAX / Gosuslugi.
tg_bot_token: str = ""
doctor_notify_chat_id: int | None = None
# Virtual clock for the demo (ADR-005).
demo_clock: bool = True
tz: str = "Europe/Moscow"
api_base_url: str = "http://localhost:8000"
config_dir: Path = ROOT_DIR / "config"
data_dir: Path = ROOT_DIR / "data"
@lru_cache
def get_settings() -> Settings:
return Settings()