Редактировать giga_client.py

This commit is contained in:
Markov Andrey
2026-06-30 21:26:00 +00:00
parent 3e2f48fc1b
commit 6db740f761

View File

@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Простой клиент для GigaChat API. Простой клиент для GigaChat API с поддержкой повторных попыток и circuit breaker.
Только вызовы chat и upload_file, без бизнес-логики. Использует tenacity для retry и pybreaker для circuit breaker.
Используется AIService и другими модулями.
""" """
import asyncio import asyncio
@@ -11,9 +10,14 @@ from typing import List, Dict, Optional
from gigachat import GigaChat from gigachat import GigaChat
from gigachat.models import Chat, Messages, MessagesRole 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__) logger = logging.getLogger(__name__)
# Circuit breaker: при 3 ошибках подряд открывается на 60 секунд
breaker = pybreaker.CircuitBreaker(fail_max=3, reset_timeout=60)
class GigaClient: class GigaClient:
def __init__(self, api_key: str, model: str, temperature: float, timeout: int, verify_ssl: bool = False): 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") logger.debug("GigaChat client initialized")
return self._client 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( async def chat(
self, self,
history: List[Dict[str, str]], history: List[Dict[str, str]],
@@ -45,7 +55,7 @@ class GigaClient:
temperature: Optional[float] = None, temperature: Optional[float] = None,
) -> str: ) -> str:
""" """
Отправляет запрос к GigaChat и возвращает ответ. Отправляет запрос к GigaChat и возвращает ответ с повторными попытками.
Аргументы: Аргументы:
history: список предыдущих сообщений (каждое с ключами role и content). history: список предыдущих сообщений (каждое с ключами role и content).
@@ -65,7 +75,6 @@ class GigaClient:
messages.append(Messages(role=MessagesRole.USER, content=query)) messages.append(Messages(role=MessagesRole.USER, content=query))
attachments = [file_id] if file_id else None attachments = [file_id] if file_id else None
actual_temp = temperature if temperature is not None else self.temperature actual_temp = temperature if temperature is not None else self.temperature
try: try:
@@ -81,9 +90,12 @@ class GigaClient:
) )
) )
return response.choices[0].message.content return response.choices[0].message.content
except pybreaker.CircuitBreakerError:
logger.error("Circuit breaker открыт, GigaChat временно недоступен")
return "Извините, сервис ИИ временно недоступен. Попробуйте позже."
except Exception as e: except Exception as e:
logger.error(f"Ошибка при вызове GigaChat: {e}", exc_info=True) logger.error(f"Ошибка при вызове GigaChat: {e}", exc_info=True)
return "Извините, произошла ошибка при обращении к сервису ИИ." raise
async def upload_file(self, file_path: str) -> Optional[str]: async def upload_file(self, file_path: str) -> Optional[str]:
""" """
@@ -92,16 +104,13 @@ class GigaClient:
""" """
try: try:
with open(file_path, "rb") as f: with open(file_path, "rb") as f:
# Загружаем файл синхронно в потоке
uploaded = await asyncio.to_thread(self._get_client().upload_file, f, "general") uploaded = await asyncio.to_thread(self._get_client().upload_file, f, "general")
# В разных версиях SDK поле может называться id_, id или file_id
if hasattr(uploaded, "id_"): if hasattr(uploaded, "id_"):
return uploaded.id_ return uploaded.id_
if hasattr(uploaded, "id"): if hasattr(uploaded, "id"):
return uploaded.id return uploaded.id
if hasattr(uploaded, "file_id"): if hasattr(uploaded, "file_id"):
return uploaded.file_id return uploaded.file_id
# fallback: пробуем преобразовать в словарь
data = uploaded.dict() if hasattr(uploaded, "dict") else {} data = uploaded.dict() if hasattr(uploaded, "dict") else {}
return data.get("id_") or data.get("id") or data.get("file_id") return data.get("id_") or data.get("id") or data.get("file_id")
except Exception as e: except Exception as e: