Files

185 lines
5.8 KiB
Python

"""Загрузка llm.config.yaml и переопределений из переменных окружения."""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
from typing import Any
import yaml
from dotenv import load_dotenv
PROJECT_ROOT = Path(__file__).resolve().parents[2]
CONFIG_PATH = PROJECT_ROOT / "llm.config.yaml"
# .env в корне репозитория (секреты не коммитить)
load_dotenv(PROJECT_ROOT / ".env")
def _deep_get(data: dict[str, Any], *keys: str, default: Any = None) -> Any:
node: Any = data
for key in keys:
if not isinstance(node, dict):
return default
node = node.get(key)
if node is None:
return default
return node
@lru_cache(maxsize=1)
def load_config() -> dict[str, Any]:
if not CONFIG_PATH.exists():
return {}
with CONFIG_PATH.open(encoding="utf-8") as fh:
return yaml.safe_load(fh) or {}
class Settings:
"""Настройки интеграции LLM и временного локального RAG."""
@property
def project_root(self) -> Path:
return PROJECT_ROOT
@property
def llm_enabled(self) -> bool:
env = os.getenv("VKUS_LLM_ENABLED")
if env is not None:
return env.lower() in ("1", "true", "yes")
return bool(_deep_get(load_config(), "provider", "enabled", default=False))
@property
def llm_base_url(self) -> str:
# Полный URL из .env (OpenRouter и др.) — нормализуем до base
api_url = os.getenv("VKUS_LLM_API_URL")
if api_url:
url = api_url.rstrip("/")
if url.endswith("/v1/chat/completions"):
return url[: -len("/v1/chat/completions")]
return url
return os.getenv(
"VKUS_LLM_BASE_URL",
_deep_get(load_config(), "api", "base_url", default="https://apiai.sibur.local"),
)
@property
def llm_chat_url(self) -> str:
"""Полный URL для chat/completions."""
api_url = os.getenv("VKUS_LLM_API_URL")
if api_url:
return api_url.rstrip("/")
return f"{self.llm_base_url.rstrip('/')}/v1/chat/completions"
@property
def llm_model(self) -> str:
return os.getenv(
"VKUS_LLM_MODEL",
_deep_get(load_config(), "llm", "model", default="gpt-oss-120b"),
)
@property
def llm_api_key(self) -> str | None:
key_name = _deep_get(load_config(), "auth", "api_key_env", default="VKUS_LLM_API_KEY")
return os.getenv(key_name or "VKUS_LLM_API_KEY") or None
@property
def llm_jwt_token(self) -> str | None:
key_name = _deep_get(load_config(), "auth", "jwt_token_env", default="VKUS_LLM_JWT_TOKEN")
return os.getenv(key_name or "VKUS_LLM_JWT_TOKEN") or None
@property
def llm_timeout(self) -> float:
env = os.getenv("VKUS_LLM_TIMEOUT_SEC")
if env:
return float(env)
return float(_deep_get(load_config(), "api", "timeout_seconds", default=60))
@property
def llm_verify_ssl(self) -> bool:
return bool(_deep_get(load_config(), "api", "verify_ssl", default=True))
@property
def llm_temperature(self) -> float:
env = os.getenv("VKUS_LLM_TEMPERATURE")
if env:
return float(env)
return float(_deep_get(load_config(), "llm", "parameters", "temperature", default=0.3))
@property
def llm_max_tokens(self) -> int:
env = os.getenv("VKUS_LLM_MAX_TOKENS")
if env:
return int(env)
return int(_deep_get(load_config(), "llm", "parameters", "max_tokens", default=2048))
@property
def system_prompt_path(self) -> Path:
rel = _deep_get(
load_config(),
"llm",
"system_prompt",
"file",
default="Start data/Системный промпт - правила общения с пользователем.md",
)
return PROJECT_ROOT / str(rel)
@property
def system_prompt_fallback(self) -> str:
return str(
_deep_get(
load_config(),
"llm",
"system_prompt",
"fallback",
default="Ты — корпоративный ИТ-ассистент портала ВКУС.",
)
)
@property
def confidence_threshold(self) -> int:
return int(_deep_get(load_config(), "assistant", "confidence_threshold", default=70))
# --- Временный локальный RAG (внутри backend) ---
@property
def rag_mode(self) -> str:
"""local — временный RAG в backend; external — целевая платформа ИИ (будущее)."""
return str(_deep_get(load_config(), "rag", "mode", default="local"))
@property
def rag_local_enabled(self) -> bool:
env = os.getenv("VKUS_RAG_LOCAL_ENABLED")
if env is not None:
return env.lower() in ("1", "true", "yes")
return bool(_deep_get(load_config(), "rag", "local", "enabled", default=True))
@property
def rag_documents_path(self) -> Path:
rel = _deep_get(load_config(), "rag", "local", "documents_path", default="Manuals")
return PROJECT_ROOT / str(rel)
@property
def rag_top_k(self) -> int:
return int(_deep_get(load_config(), "rag", "local", "top_k", default=5))
@property
def rag_min_score(self) -> float:
return float(_deep_get(load_config(), "rag", "local", "min_score", default=0.12))
@property
def rag_chunk_size(self) -> int:
return int(_deep_get(load_config(), "rag", "local", "chunk_size", default=450))
@property
def use_pattern_kb(self) -> bool:
return bool(_deep_get(load_config(), "fallback", "use_local_knowledge_base", default=True))
@property
def use_template_on_llm_failure(self) -> bool:
return bool(_deep_get(load_config(), "fallback", "use_template_on_llm_failure", default=True))
settings = Settings()