Add new file

This commit is contained in:
Markov Andrey
2026-06-30 13:56:36 +00:00
parent 3d5c8259f5
commit 64155908f3

228
rag/config_models.py Normal file
View File

@@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
"""
Модели данных для конфигурации на основе Pydantic.
Используются для загрузки и валидации настроек из YAML-файлов (rag.conf и bot.conf).
Обеспечивают строгую типизацию и автоматическую проверку обязательных полей.
"""
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field, field_validator
from pathlib import Path
# ============================================================
# Базовые модели для секций конфигурации
# ============================================================
class DatabaseConfig(BaseModel):
"""Настройки подключения к PostgreSQL."""
host: str = Field(..., description="Хост базы данных")
port: int = Field(5432, description="Порт PostgreSQL")
user: str = Field(..., description="Имя пользователя")
password_env: str = Field(..., description="Имя переменной окружения с паролем")
database: str = Field(..., description="Имя базы данных")
ssl: bool = Field(False, description="Использовать SSL")
ssl_mode: Optional[str] = Field("prefer", description="Режим SSL (require, prefer, disable)")
@field_validator('port')
def validate_port(cls, v):
if not 1 <= v <= 65535:
raise ValueError('port must be between 1 and 65535')
return v
class QdrantConfig(BaseModel):
"""Настройки векторной базы Qdrant."""
host: str = Field(..., description="Хост Qdrant")
port: int = Field(6333, description="HTTP порт Qdrant")
grpc_port: int = Field(6334, description="gRPC порт Qdrant")
collection: str = Field(..., description="Имя коллекции")
vector_size: int = Field(1024, description="Размерность dense-векторов")
distance: str = Field("Cosine", description="Метрика расстояния (Cosine или Euclid)")
class GigaChatConfig(BaseModel):
"""Настройки GigaChat API."""
credentials_env: str = Field(..., description="Имя переменной окружения с API-ключом")
model_embedding: str = Field("Embeddings", description="Модель для эмбеддингов")
model_generation: str = Field("GigaChat-Max", description="Модель для генерации")
timeout: int = Field(600, description="Таймаут запросов в секундах")
temperature: float = Field(0.1, description="Температура генерации по умолчанию")
class EmbeddingConfig(BaseModel):
"""Настройки кэширования эмбеддингов."""
cache_size: int = Field(4096, description="Размер кэша (количество элементов)")
verify_ssl_certs: bool = Field(False, description="Проверять SSL-сертификаты")
model: str = Field("Embeddings", description="Модель эмбеддингов")
timeout: int = Field(30, description="Таймаут запроса эмбеддингов")
class ChunkingConfig(BaseModel):
"""Настройки разбиения документов на чанки."""
enabled: bool = Field(True, description="Включить чанкинг")
chunk_size_tokens: int = Field(200, description="Размер чанка в токенах")
overlap_tokens: int = Field(50, description="Перекрытие между чанками в токенах")
strategy: str = Field("recursive_split_by_sentences", description="Стратегия разбиения")
approx_chunk_chars: int = Field(500, description="Приблизительный размер чанка в символах (fallback)")
approx_overlap_chars: int = Field(100, description="Приблизительное перекрытие в символах (fallback)")
class RAGConfig(BaseModel):
"""Параметры RAG-пайплайна."""
default_top_k: int = Field(30, description="Количество фрагментов по умолчанию")
metrics_top_k: int = Field(30, description="Количество фрагментов для извлечения метрик")
contradiction_top_k: int = Field(10, description="Количество фрагментов для проверки противоречий")
max_context_tokens: int = Field(3000, description="Максимальное количество токенов контекста")
reserved_for_answer_tokens: int = Field(1000, description="Резерв токенов для ответа")
reserved_for_overhead_tokens: int = Field(200, description="Резерв для служебных нужд")
max_model_tokens: int = Field(8192, description="Максимальный размер контекстного окна модели")
rerank_min_length: int = Field(5000, description="Минимальная длина контекста для переранжирования (символы)")
class HTTPServerConfig(BaseModel):
"""Настройки HTTP-сервера."""
host: str = Field("0.0.0.0", description="Хост для привязки")
port: int = Field(8080, description="Порт для привязки")
class HTTPConfig(BaseModel):
"""Таймауты HTTP-запросов."""
download_timeout: int = Field(30, description="Таймаут скачивания")
upload_timeout: int = Field(60, description="Таймаут загрузки")
class SummarizationConfig(BaseModel):
"""Настройки иерархического резюмирования."""
enable_hierarchical_summarization: bool = Field(True, description="Включить сжатие")
max_tokens_for_document: int = Field(8000, description="Порог для сжатия документа (токены)")
target_tokens_after_summary: int = Field(3000, description="Целевой размер после сжатия")
chunk_size_tokens: int = Field(500, description="Размер блока для резюмирования")
max_depth: int = Field(2, description="Максимальная глубина рекурсии")
class PathsConfig(BaseModel):
"""Пути к каталогам."""
data_dir: str = Field("data", description="Каталог для постоянных данных")
temp_dir: str = Field("temp", description="Каталог для временных файлов")
prompts_dir: str = Field("prompts", description="Каталог с промптами")
upload_dir: str = Field("upload", description="Каталог для загрузок")
log_dir: str = Field("logs", description="Каталог для логов")
log_filename: str = Field("{name}.log", description="Шаблон имени лог-файла")
class FeaturesConfig(BaseModel):
"""Включение/отключение функций."""
file_processing: bool = Field(True, description="Обработка файлов")
surgical_replace: bool = Field(True, description="Хирургическая замена")
voice_recognition: bool = Field(True, description="Распознавание голоса")
vision: bool = Field(True, description="OCR")
archive_support: bool = Field(True, description="Поддержка архивов")
ocr: bool = Field(True, description="Распознавание текста на изображениях")
allow_public_knowledge: bool = Field(False, description="Разрешить использование общих знаний")
max_file_size_mb: int = Field(100, description="Максимальный размер файла в МБ")
max_archive_files: int = Field(100, description="Максимальное количество файлов в архиве")
enable_intent_classification: bool = Field(True, description="Включить классификацию намерений")
enable_self_critique: bool = Field(True, description="Включить самокритику")
surgical_keywords: List[str] = Field(["замени", "заменить"], description="Ключевые слова для хирургической замены")
mention_keyword: Optional[str] = Field(None, description="Ключевое слово для упоминания бота в группе")
class CleanupConfig(BaseModel):
"""Настройки очистки старых файлов."""
temp_days: int = Field(3, description="Срок хранения временных файлов (дни)")
upload_days: int = Field(7, description="Срок хранения загруженных файлов (дни)")
interval_seconds: int = Field(3600, description="Интервал очистки (секунды)")
user_data_days: int = Field(30, description="Срок хранения пользовательских данных (дни)")
class FilePermissionsConfig(BaseModel):
"""Права доступа к создаваемым файлам."""
mode: str = Field("0644", description="Права в восьмеричном формате (строка)")
class AIConfig(BaseModel):
"""Настройки AI (температура, таймауты)."""
model: Optional[str] = Field(None, description="Модель генерации (если не указана, берётся из GigaChatConfig)")
temperature: float = Field(0.1, description="Температура генерации")
timeout: int = Field(600, description="Таймаут генерации")
prompts: Dict[str, str] = Field(..., description="Имена файлов промптов (ключ -> имя файла)")
class PromptTemperatures(BaseModel):
"""Температуры для отдельных функций."""
temperature: float = Field(0.1, description="Температура")
class PromptsConfig(BaseModel):
"""Секция с температурами для разных функций."""
intent: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
expand: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
metrics: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
summary: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
summary_max_chars: int = Field(8000, description="Максимальная длина текста для суммаризации")
consistency: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
consistency_max_fragments: int = Field(5, description="Максимальное количество фрагментов для проверки противоречий")
critique: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
rerank: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
rerank_min_length: int = Field(5000, description="Минимальная длина контекста для переранжирования")
generate_document: PromptTemperatures = Field(default_factory=lambda: PromptTemperatures(temperature=0.1))
# ============================================================
# Главная модель конфигурации приложения
# ============================================================
class AppConfig(BaseModel):
"""
Полная конфигурация приложения.
Объединяет все секции.
"""
name: str = Field(..., description="Имя профиля бота")
jid: str = Field(..., description="JID бота (user@domain)")
password_env: str = Field(..., description="Имя переменной окружения с паролем XMPP")
resource: str = Field("bot", description="Ресурс XMPP")
status_message: str = Field(..., description="Сообщение статуса присутствия")
xmpp_server: Optional[str] = Field(None, description="XMPP сервер (если не указан, берётся из JID)")
xmpp_port: int = Field(5222, description="Порт XMPP")
xmpp_use_ssl: bool = Field(False, description="Использовать SSL для XMPP")
admin_jids: List[str] = Field([], description="Список JID администраторов")
log_level: str = Field("INFO", description="Уровень логирования")
rag_server_url: str = Field("http://localhost:8080", description="URL RAG-сервера")
# Секции
database: DatabaseConfig
qdrant: QdrantConfig
gigachat: GigaChatConfig
embedding: EmbeddingConfig
chunking: ChunkingConfig
rag: RAGConfig
http_server: HTTPServerConfig
http: HTTPConfig
summarization: SummarizationConfig
paths: PathsConfig
features: FeaturesConfig
cleanup: CleanupConfig
file_permissions: FilePermissionsConfig
ai: AIConfig
prompts: PromptsConfig
# Дополнительные вычисляемые поля (заполняются при загрузке)
xmpp_password: Optional[str] = Field(None, description="Пароль XMPP (загружается из окружения)")
gigachat_api_key: Optional[str] = Field(None, description="API-ключ GigaChat (из окружения)")
db_password: Optional[str] = Field(None, description="Пароль PostgreSQL (из окружения)")
salute_speech_auth: Optional[str] = Field(None, description="Авторизация для SaluteSpeech (из окружения)")
# Пути (преобразуются в Path при загрузке)
profile_dir: Optional[Path] = Field(None, description="Корень профиля")
data_dir_path: Optional[Path] = Field(None)
temp_dir_path: Optional[Path] = Field(None)
prompts_dir_path: Optional[Path] = Field(None)
upload_dir_path: Optional[Path] = Field(None)
log_dir_path: Optional[Path] = Field(None)
log_file_path: Optional[Path] = Field(None)
# Промпты (загружаются из файлов)
prompts_content: Optional[Dict[str, str]] = Field(default_factory=dict, description="Содержимое промптов")
class Config:
extra = "forbid" # Запрещаем неизвестные поля