-добавлены нетворки в докер композ -исправлен рутинг (баг пайчарма) -запросы к ии агентам не проходят из-за ссл сертификата (пробовали отключить, но пока не выходит, нужно доделать)
160 lines
6.7 KiB
Python
160 lines
6.7 KiB
Python
import os
|
||
|
||
import aiohttp
|
||
import base64
|
||
import uuid
|
||
import time
|
||
from urllib.parse import urlencode
|
||
from typing import Optional, List, Dict, Any
|
||
|
||
from dotenv import load_dotenv
|
||
|
||
from app.core.config import settings
|
||
|
||
load_dotenv()
|
||
|
||
class GigaChatService:
|
||
def __init__(self):
|
||
self.access_token: Optional[str] = None
|
||
self.token_expires_at: Optional[float] = None
|
||
|
||
async def _get_token(self) -> str:
|
||
"""Получить OAuth токен"""
|
||
# Проверяем, не истек ли токен (оставляем запас 60 секунд)
|
||
if self.access_token and self.token_expires_at:
|
||
if time.time() < (self.token_expires_at - 60):
|
||
return self.access_token
|
||
|
||
# Проверяем наличие credentials
|
||
client_id = os.getenv("GIGACHAT_CLIENT_ID")
|
||
client_secret = os.getenv("GIGACHAT_CLIENT_SECRET")
|
||
|
||
if not client_id or not client_secret:
|
||
raise Exception(
|
||
"GigaChat credentials not configured. "
|
||
"Please set GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET in .env file"
|
||
)
|
||
|
||
# Формируем credentials и кодируем в Base64 с явным указанием UTF-8
|
||
credentials = f"{client_id}:{client_secret}".strip().encode('utf-8') # Обрезаем лишние символы
|
||
encoded_credentials = base64.b64encode(credentials).decode('utf-8')
|
||
|
||
headers = {
|
||
"Authorization": f"Basic {encoded_credentials}",
|
||
"Content-Type": "application/x-www-form-urlencoded",
|
||
"Accept": "application/json",
|
||
"RqUID": str(uuid.uuid4())
|
||
}
|
||
|
||
# Правильно кодируем данные формы (как в рабочем примере)
|
||
form_data = {
|
||
"grant_type": "client_credentials",
|
||
"scope": "GIGACHAT_API_PERS"
|
||
}
|
||
|
||
# Отключаем проверку SSL (только для разработки!)
|
||
# Используем ssl=False для полного отключения проверки сертификата
|
||
connector = aiohttp.TCPConnector(ssl=False)
|
||
async with aiohttp.ClientSession(connector=connector) as session:
|
||
async with session.post(
|
||
os.getenv("GIGACHAT_BASE_URL"),
|
||
headers=headers,
|
||
data=form_data
|
||
) as response:
|
||
if response.status != 200:
|
||
# Получаем детали ошибки из ответа
|
||
try:
|
||
error_body = await response.text()
|
||
# Пытаемся распарсить как JSON, если не получается - возвращаем текст
|
||
try:
|
||
error_json = await response.json()
|
||
error_detail = error_json.get("error_description") or error_json.get("error") or str(error_json)
|
||
except:
|
||
error_detail = error_body
|
||
except:
|
||
error_detail = "No error details available"
|
||
|
||
raise Exception(
|
||
f"Failed to get token: HTTP {response.status}. "
|
||
f"Error details: {error_detail}. "
|
||
f"Check your GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET_2 in .env file"
|
||
)
|
||
|
||
result = await response.json()
|
||
self.access_token = result.get("access_token")
|
||
if not self.access_token:
|
||
raise Exception(f"Token not found in response: {result}")
|
||
|
||
# Обрабатываем время истечения токена (может быть expires_at или expires_in)
|
||
expires_at = result.get("expires_at")
|
||
expires_in = result.get("expires_in")
|
||
|
||
if expires_at:
|
||
# expires_at может быть timestamp или количество секунд
|
||
if expires_at > 1000000000: # Это timestamp
|
||
self.token_expires_at = expires_at
|
||
else: # Это количество секунд
|
||
self.token_expires_at = time.time() + expires_at
|
||
elif expires_in:
|
||
# expires_in - это всегда количество секунд до истечения
|
||
self.token_expires_at = time.time() + expires_in
|
||
else:
|
||
# По умолчанию 30 минут (1800 секунд)
|
||
self.token_expires_at = time.time() + 1800
|
||
|
||
return self.access_token
|
||
|
||
async def chat(
|
||
self,
|
||
message: str,
|
||
context: Optional[List[Dict[str, Any]]] = None,
|
||
model: str = None
|
||
) -> Dict[str, Any]:
|
||
"""Отправить сообщение в GigaChat"""
|
||
token = await self._get_token()
|
||
model = model or settings.GIGACHAT_MODEL_CHAT or "GigaChat"
|
||
|
||
messages = context or []
|
||
messages.append({"role": "user", "content": message})
|
||
|
||
headers = {
|
||
"Authorization": f"Bearer {token}",
|
||
"Content-Type": "application/json"
|
||
}
|
||
|
||
payload = {
|
||
"model": model,
|
||
"messages": messages,
|
||
"temperature": 0.7,
|
||
"max_tokens": 2000
|
||
}
|
||
|
||
# Отключаем проверку SSL (только для разработки!)
|
||
# Используем ssl=False для полного отключения проверки сертификата
|
||
connector = aiohttp.TCPConnector(ssl=False)
|
||
async with aiohttp.ClientSession(connector=connector) as session:
|
||
async with session.post(
|
||
f"{settings.GIGACHAT_BASE_URL}/chat/completions",
|
||
headers=headers,
|
||
json=payload
|
||
) as response:
|
||
if response.status != 200:
|
||
error_text = await response.text()
|
||
raise Exception(f"GigaChat API error: {response.status} - {error_text}")
|
||
|
||
result = await response.json()
|
||
return result
|
||
|
||
async def generate_text(
|
||
self,
|
||
prompt: str,
|
||
model: str = None
|
||
) -> str:
|
||
"""Генерация текста по промпту"""
|
||
result = await self.chat(prompt, model=model)
|
||
return result.get("choices", [{}])[0].get("message", {}).get("content", "")
|
||
|
||
|
||
gigachat_service = GigaChatService()
|
||
|