From 6db740f761205a0074fcb87e04ac2f4eb2eeb092 Mon Sep 17 00:00:00 2001 From: Markov Andrey Date: Tue, 30 Jun 2026 21:26:00 +0000 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20giga=5Fclient.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rag/services/giga_client.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/rag/services/giga_client.py b/rag/services/giga_client.py index 62dd05b..31afca6 100644 --- a/rag/services/giga_client.py +++ b/rag/services/giga_client.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- """ -Простой клиент для GigaChat API. -Только вызовы chat и upload_file, без бизнес-логики. -Используется AIService и другими модулями. +Простой клиент для GigaChat API с поддержкой повторных попыток и circuit breaker. +Использует tenacity для retry и pybreaker для circuit breaker. """ import asyncio @@ -11,9 +10,14 @@ from typing import List, Dict, Optional from gigachat import GigaChat from gigachat.models import Chat, Messages, MessagesRole +from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type +import pybreaker logger = logging.getLogger(__name__) +# Circuit breaker: при 3 ошибках подряд открывается на 60 секунд +breaker = pybreaker.CircuitBreaker(fail_max=3, reset_timeout=60) + class GigaClient: def __init__(self, api_key: str, model: str, temperature: float, timeout: int, verify_ssl: bool = False): @@ -36,6 +40,12 @@ class GigaClient: logger.debug("GigaChat client initialized") return self._client + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=1, max=8), + retry=retry_if_exception_type((TimeoutError, ConnectionError)) + ) + @breaker async def chat( self, history: List[Dict[str, str]], @@ -45,7 +55,7 @@ class GigaClient: temperature: Optional[float] = None, ) -> str: """ - Отправляет запрос к GigaChat и возвращает ответ. + Отправляет запрос к GigaChat и возвращает ответ с повторными попытками. Аргументы: history: список предыдущих сообщений (каждое с ключами role и content). @@ -65,7 +75,6 @@ class GigaClient: messages.append(Messages(role=MessagesRole.USER, content=query)) attachments = [file_id] if file_id else None - actual_temp = temperature if temperature is not None else self.temperature try: @@ -81,9 +90,12 @@ class GigaClient: ) ) return response.choices[0].message.content + except pybreaker.CircuitBreakerError: + logger.error("Circuit breaker открыт, GigaChat временно недоступен") + return "Извините, сервис ИИ временно недоступен. Попробуйте позже." except Exception as e: logger.error(f"Ошибка при вызове GigaChat: {e}", exc_info=True) - return "Извините, произошла ошибка при обращении к сервису ИИ." + raise async def upload_file(self, file_path: str) -> Optional[str]: """ @@ -92,16 +104,13 @@ class GigaClient: """ try: with open(file_path, "rb") as f: - # Загружаем файл синхронно в потоке uploaded = await asyncio.to_thread(self._get_client().upload_file, f, "general") - # В разных версиях SDK поле может называться id_, id или file_id if hasattr(uploaded, "id_"): return uploaded.id_ if hasattr(uploaded, "id"): return uploaded.id if hasattr(uploaded, "file_id"): return uploaded.file_id - # fallback: пробуем преобразовать в словарь data = uploaded.dict() if hasattr(uploaded, "dict") else {} return data.get("id_") or data.get("id") or data.get("file_id") except Exception as e: