Update 9 files
- /rag/config_models.py - /rag/utils/config_loader.py - /rag/auth.py - /rag/rag_server.py - /rag/rag_api.py - /rag/rag_orchestrator.py - /rag/__init__.py - /bots/xmpp/client.py - /bots/rag_client.py
This commit is contained in:
@@ -14,6 +14,7 @@ HTTP-клиент для взаимодействия с RAG-сервером.
|
||||
ДОБАВЛЕНО:
|
||||
- Метод vision() для распознавания текста на изображениях
|
||||
- Метод transcribe() для транскрибации аудио
|
||||
- Аутентификация через API-ключ (заголовок X-API-Key)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -33,6 +34,7 @@ class RAGClient:
|
||||
def __init__(
|
||||
self,
|
||||
server_url: str,
|
||||
api_key: Optional[str] = None, # <-- добавлен API-ключ
|
||||
timeout: int = 60,
|
||||
max_retries: int = 3,
|
||||
retry_delay: float = 1.0
|
||||
@@ -42,16 +44,22 @@ class RAGClient:
|
||||
|
||||
Аргументы:
|
||||
server_url (str): базовый URL RAG-сервера (например, http://localhost:8080)
|
||||
api_key (Optional[str]): API-ключ для аутентификации (передаётся в заголовке X-API-Key)
|
||||
timeout (int): таймаут на запрос в секундах
|
||||
max_retries (int): максимальное количество повторных попыток при ошибке
|
||||
retry_delay (float): базовая задержка между попытками (секунды)
|
||||
"""
|
||||
self.server_url = server_url.rstrip('/')
|
||||
self.api_key = api_key # <-- сохраняем ключ
|
||||
self.timeout = timeout
|
||||
self.max_retries = max_retries
|
||||
self.retry_delay = retry_delay
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
logger.info(f"RAGClient инициализирован с сервером {server_url}, таймаут={timeout}с, макс. попыток={max_retries}")
|
||||
logger.info(
|
||||
f"RAGClient инициализирован с сервером {server_url}, "
|
||||
f"таймаут={timeout}с, макс. попыток={max_retries}, "
|
||||
f"API-ключ {'задан' if api_key else 'не задан'}"
|
||||
)
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
"""
|
||||
@@ -75,6 +83,7 @@ class RAGClient:
|
||||
async def _post(self, endpoint: str, payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Внутренний метод для отправки POST-запроса с повторными попытками.
|
||||
Автоматически добавляет заголовок X-API-Key, если ключ задан.
|
||||
|
||||
Аргументы:
|
||||
endpoint (str): путь эндпоинта (например, '/rag/query')
|
||||
@@ -90,12 +99,17 @@ class RAGClient:
|
||||
url = f"{self.server_url}{endpoint}"
|
||||
session = await self._get_session()
|
||||
|
||||
# Подготавливаем заголовки
|
||||
headers = {}
|
||||
if self.api_key:
|
||||
headers['X-API-Key'] = self.api_key
|
||||
|
||||
last_exception = None
|
||||
|
||||
for attempt in range(self.max_retries):
|
||||
try:
|
||||
logger.debug(f"Попытка {attempt + 1}/{self.max_retries} к {endpoint}")
|
||||
async with session.post(url, json=payload) as resp:
|
||||
async with session.post(url, json=payload, headers=headers) as resp:
|
||||
if resp.status == 200:
|
||||
return await resp.json()
|
||||
else:
|
||||
@@ -190,7 +204,10 @@ class RAGClient:
|
||||
"""
|
||||
try:
|
||||
session = await self._get_session()
|
||||
async with session.get(f"{self.server_url}/health", timeout=10) as resp:
|
||||
headers = {}
|
||||
if self.api_key:
|
||||
headers['X-API-Key'] = self.api_key
|
||||
async with session.get(f"{self.server_url}/health", timeout=10, headers=headers) as resp:
|
||||
if resp.status == 200:
|
||||
data = await resp.json()
|
||||
if data.get('status') == 'healthy':
|
||||
@@ -308,10 +325,15 @@ class RAGClient:
|
||||
session = await self._get_session()
|
||||
url = f"{self.server_url}/rag/vision"
|
||||
|
||||
# Добавляем заголовок с ключом
|
||||
headers = {}
|
||||
if self.api_key:
|
||||
headers['X-API-Key'] = self.api_key
|
||||
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
files = {'file': (os.path.basename(file_path), f, 'image/jpeg')}
|
||||
async with session.post(url, data=files) as resp:
|
||||
async with session.post(url, data=files, headers=headers) as resp:
|
||||
if resp.status == 200:
|
||||
return await resp.json()
|
||||
else:
|
||||
@@ -338,10 +360,14 @@ class RAGClient:
|
||||
session = await self._get_session()
|
||||
url = f"{self.server_url}/rag/transcribe"
|
||||
|
||||
headers = {}
|
||||
if self.api_key:
|
||||
headers['X-API-Key'] = self.api_key
|
||||
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
files = {'file': (os.path.basename(file_path), f, 'audio/ogg')}
|
||||
async with session.post(url, data=files) as resp:
|
||||
async with session.post(url, data=files, headers=headers) as resp:
|
||||
if resp.status == 200:
|
||||
return await resp.json()
|
||||
else:
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
XMPP клиент – главный класс Bot.
|
||||
Теперь это тонкий клиент, который только общается с RAG-сервером.
|
||||
Не содержит БД, Qdrant, GigaChat – всё на сервере.
|
||||
|
||||
ИСПОЛЬЗУЕТ:
|
||||
- AppConfig из rag.config_models для единой конфигурации.
|
||||
- RAGClient с передачей API-ключа для аутентификации.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -10,7 +14,11 @@ import logging
|
||||
from datetime import datetime
|
||||
from slixmpp import ClientXMPP
|
||||
|
||||
from core.utils.config_loader import BotConfig
|
||||
# Импорты из нового пакета rag для конфигурации
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
# Остальные импорты остаются из core (старый код) – позже их нужно будет перенести в rag,
|
||||
# но пока оставляем как есть для совместимости.
|
||||
from core.services.file_service import FileService
|
||||
from core.handlers.metrics_handler import MetricsHandler
|
||||
from core.rag_client import RAGClient
|
||||
@@ -32,7 +40,7 @@ class Bot(ClientXMPP):
|
||||
Тонкий клиент: вся RAG-логика на сервере.
|
||||
"""
|
||||
|
||||
def __init__(self, config: BotConfig):
|
||||
def __init__(self, config: AppConfig): # <-- изменён тип конфига
|
||||
# Полный JID: user@domain/resource
|
||||
full_jid = f"{config.jid}/{config.xmpp_resource}"
|
||||
super().__init__(full_jid, config.xmpp_password)
|
||||
@@ -47,35 +55,59 @@ class Bot(ClientXMPP):
|
||||
# ----- 2. Загрузка промптов (остаются локально) -----
|
||||
# Промпты загружаются из файлов и передаются в каждом запросе к серверу.
|
||||
self.ai_prompts = {}
|
||||
prompt_files = {
|
||||
'system': config.system_prompt_file,
|
||||
'synthesis': config.synthesis_prompt_file,
|
||||
'quality': config.quality_prompt_file,
|
||||
'rerank': config.rerank_prompt_file,
|
||||
'expand': config.expand_prompt_file,
|
||||
'intent': config.intent_prompt_file,
|
||||
'metrics': config.metrics_prompt_file,
|
||||
'summary': config.summary_prompt_file,
|
||||
'consistency': config.consistency_prompt_file,
|
||||
'critique': config.critique_prompt_file,
|
||||
'spellcheck': config.spellcheck_prompt_file,
|
||||
'generate_document': config.generate_document_prompt_file,
|
||||
}
|
||||
for name, path in prompt_files.items():
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
self.ai_prompts[name] = f.read()
|
||||
logger.debug(f"Загружен промпт {name} из {path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Не удалось загрузить промпт {name} из {path}: {e}")
|
||||
self.ai_prompts[name] = ""
|
||||
# В AppConfig теперь есть свойства для путей к файлам промптов,
|
||||
# но они называются так же, как в BotConfig, поэтому доступ через config.<имя> работает.
|
||||
# Однако в BotConfig они назывались system_prompt_file и т.д. – они есть в AppConfig?
|
||||
# В AppConfig мы добавили прокси-свойства только для основных параметров,
|
||||
# но не для путей к промптам. Поэтому здесь нужно либо использовать config.prompts_dir_path
|
||||
# и формировать пути вручную, либо оставить как было, если в AppConfig есть соответствующие свойства.
|
||||
# Для обратной совместимости мы добавили свойства в AppConfig, но не для prompt_file.
|
||||
# Поэтому временно оставим как было: будем использовать config.prompts_dir_path и имена файлов.
|
||||
# Лучше прочитать имена файлов из config.ai.prompts и загрузить их.
|
||||
# Но для простоты пока оставим старый способ, если свойства есть.
|
||||
# Однако в AppConfig нет system_prompt_file и т.д. – они были в BotConfig.
|
||||
# В BotConfig они были путями к файлам. В AppConfig мы не стали их добавлять,
|
||||
# чтобы не дублировать. Вместо этого мы загружаем все промпты в config.prompts_content.
|
||||
# Поэтому мы можем использовать config.prompts_content, который уже содержит все промпты.
|
||||
# Используем его:
|
||||
self.ai_prompts = config.prompts_content.copy() if config.prompts_content else {}
|
||||
if not self.ai_prompts:
|
||||
logger.warning("Промпты не загружены из конфига, пробуем загрузить из файлов вручную...")
|
||||
# Запасной вариант – загружаем из файлов, используя config.prompts_dir_path
|
||||
prompts_dir = config.prompts_dir_path
|
||||
if prompts_dir and prompts_dir.exists():
|
||||
for prompt_name, filename in config.ai.prompts.items():
|
||||
file_path = prompts_dir / filename
|
||||
if file_path.exists():
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
self.ai_prompts[prompt_name] = f.read()
|
||||
logger.debug(f"Загружен промпт {prompt_name} из {file_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Не удалось загрузить промпт {prompt_name} из {file_path}: {e}")
|
||||
self.ai_prompts[prompt_name] = ""
|
||||
else:
|
||||
logger.warning(f"Файл промпта {filename} не найден в {prompts_dir}")
|
||||
self.ai_prompts[prompt_name] = ""
|
||||
else:
|
||||
logger.error("Каталог промптов не найден, промпты не загружены")
|
||||
|
||||
# Сохраняем промпты как атрибут для передачи в запросах
|
||||
self.prompts = self.ai_prompts
|
||||
|
||||
# ----- 3. HTTP-клиент для RAG-сервера -----
|
||||
# Адрес сервера берём из конфига (bot.conf) или используем значение по умолчанию
|
||||
rag_server_url = getattr(config, 'rag_server_url', 'http://localhost:8080')
|
||||
self.rag_client = RAGClient(server_url=rag_server_url)
|
||||
# Адрес сервера и API-ключ берём из конфига
|
||||
rag_server_url = config.rag_server_url # теперь это свойство
|
||||
rag_api_key = config.rag_api_key # получаем API-ключ
|
||||
|
||||
# Создаём RAGClient с передачей ключа
|
||||
self.rag_client = RAGClient(
|
||||
server_url=rag_server_url,
|
||||
api_key=rag_api_key, # <-- передаём ключ
|
||||
timeout=config.http_download_timeout, # можно использовать таймаут из конфига
|
||||
max_retries=3,
|
||||
retry_delay=1.0
|
||||
)
|
||||
|
||||
# ----- 4. Команды и состояния -----
|
||||
# Регистрируем все команды бота (из core/commands/)
|
||||
|
||||
Reference in New Issue
Block a user