# -*- coding: utf-8 -*- """ Единый API-интерфейс для работы с RAG-оркестратором. Используется ботами для вызова RAG-логики без необходимости знать внутреннее устройство оркестратора. Этот класс является прослойкой (facade) между ботами и RAGOrchestrator. В будущем он может быть легко заменён на HTTP-клиент, если оркестратор будет вынесен в отдельный сервис. Сейчас этот файл может использоваться для локальной работы, но в новой архитектуре мы используем RAGClient (HTTP), поэтому данный класс является опциональным. """ import logging from typing import Optional, Dict, List, Any from .rag_orchestrator import RAGOrchestrator from .services.kb_service import KBService from .services.giga_client import GigaClient from .services.file_service import FileService from .services.postgres_service import PostgresService from .config_models import AppConfig logger = logging.getLogger(__name__) class RAGAPI: """ Единый API-интерфейс для RAG-функциональности. Создаёт и содержит экземпляр RAGOrchestrator. Предоставляет методы для выполнения запросов и индексации документов. """ def __init__( self, db: PostgresService, qdrant, embedding, kb: KBService, giga: GigaClient, files: FileService, config: AppConfig, default_prompts: Optional[Dict[str, str]] = None ): """ Инициализация API. Аргументы: db: сервис PostgreSQL (PostgresService) qdrant: сервис Qdrant (QdrantService) embedding: сервис эмбеддингов (EmbeddingService) kb: сервис базы знаний (KBService) giga: клиент GigaChat (GigaClient) files: сервис файлов (FileService) config: объект конфигурации (AppConfig) default_prompts: словарь промптов по умолчанию. Используется, если в запросе не переданы свои промпты. """ self.config = config self.default_prompts = default_prompts or {} # Создаём оркестратор с переданными сервисами self.orchestrator = RAGOrchestrator( db=db, qdrant=qdrant, embedding=embedding, kb=kb, giga=giga, files=files, config=config, default_prompts=self.default_prompts ) logger.info("RAGAPI инициализирован") async def query( self, query: str, user_jid: str, room_jid: Optional[str], prompts: Optional[Dict[str, str]] = None, intent_override: Optional[str] = None, last_file_path: Optional[str] = None, last_file_text: Optional[str] = None, ) -> Dict[str, Any]: """ Выполняет RAG-запрос через оркестратор. ИСТОРИЯ НЕ ПЕРЕДАЁТСЯ – оркестратор получает её из БД. """ try: if prompts is None: prompts = self.default_prompts.copy() result = await self.orchestrator.process_query( query=query, user_jid=user_jid, room_jid=room_jid, prompts=prompts, intent_override=intent_override, last_file_path=last_file_path, last_file_text=last_file_text ) logger.debug(f"RAGAPI.query успешно выполнен для {user_jid}, intent={result.get('intent')}") return result except Exception as e: logger.error(f"Ошибка в RAGAPI.query: {e}", exc_info=True) return { "answer": f"⚠️ Произошла ошибка при обработке запроса: {str(e)}", "intent": "ERROR", "context": "", "sources": [], "confidence": None, "error": str(e) } async def index_document( self, file_name: str, file_text: str, user_jid: str, room_jid: Optional[str], is_global: bool = False, title: Optional[str] = None, metadata: Optional[Dict] = None, file_hash: Optional[str] = None, update_if_exists: bool = True ) -> Dict[str, Any]: """Индексирует документ через оркестратор.""" try: result = await self.orchestrator.index_document( file_name=file_name, file_text=file_text, user_jid=user_jid, room_jid=room_jid, is_global=is_global, title=title, metadata=metadata, file_hash=file_hash, update_if_exists=update_if_exists ) logger.info(f"Документ {file_name} проиндексирован, doc_id={result.get('doc_id')}") return result except Exception as e: logger.error(f"Ошибка в RAGAPI.index_document: {e}", exc_info=True) return {"doc_id": None, "chunk_count": 0, "error": str(e)} def close(self): """Закрывает ресурсы (если необходимо).""" logger.debug("RAGAPI закрывается")