Files
fckbot/rag/config_models.py
Markov Andrey 64155908f3 Add new file
2026-06-30 13:56:36 +00:00

228 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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" # Запрещаем неизвестные поля