Merge pull request 'Update environment variables, Docker configuration, and dependencies; refactor token management and chat agent logic. Added FastAPI server setup and improved message handling in GigaChat client.' (#3) from aiFixes into master
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
47
.dockerignore
Normal file
47
.dockerignore
Normal file
@@ -0,0 +1,47 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
.venv
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
*.cover
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Project specific
|
||||
.env
|
||||
*.log
|
||||
*.db
|
||||
*.sqlite
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Docker
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
4
.env
4
.env
@@ -1,5 +1,5 @@
|
||||
GIGACHAT_CLIENT_ID=019966f4-1c5c-7382-9006-b84419fbe5d1
|
||||
GIGACHAT_CLIENT_SECRET="MDE5OTY2ZjQtMWM1Yy03MzgyLTkwMDYtYjg0NDE5ZmJlNWQxOmRjNTk2ZmFlLWMzY2UtNDRmNC05NDk3LWE2YWIxMDI5ZmE1OA=="
|
||||
GIGACHAT_CLIENT_ID=019966f0-5781-76e6-a84f-ec7de158188a
|
||||
GIGACHAT_CLIENT_SECRET=MDE5OTY2ZjAtNTc4MS03NmU2LWE4NGYtZWM3ZGUxNTgxODhhOjI3MDMxZjIxLWY3NWYtNGI4NS05MzM1LTI4ZDYyOWM3MmM0MA==
|
||||
GIGACHAT_AUTH_URL=https://ngw.devices.sberbank.ru:9443/api/v2/oauth
|
||||
GIGACHAT_BASE_URL=https://gigachat.devices.sberbank.ru/api/v1
|
||||
|
||||
|
||||
@@ -40,13 +40,13 @@ COPY models/ ./models/
|
||||
COPY services/ ./services/
|
||||
COPY prompts/ ./prompts/
|
||||
COPY scripts/ ./scripts/
|
||||
COPY app.py ./
|
||||
|
||||
# Установка переменных окружения
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONPATH=/app
|
||||
|
||||
# По умолчанию запускаем Python REPL для интерактивного использования
|
||||
# В production это будет использоваться как библиотека, импортируемая в backend
|
||||
CMD ["python"]
|
||||
# Запуск FastAPI сервера
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class ChatAgent:
|
||||
user_id: UUID,
|
||||
message: str,
|
||||
conversation_id: Optional[str] = None,
|
||||
model: str = "GigaChat-2-Lite",
|
||||
model: str = "GigaChat-2",
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Отправить сообщение и получить ответ.
|
||||
@@ -39,22 +39,25 @@ class ChatAgent:
|
||||
context_messages = []
|
||||
if conversation_id:
|
||||
cached_context = await self.cache.get_context(str(conversation_id))
|
||||
# Фильтруем системные сообщения из кэша - они не должны там храниться
|
||||
context_messages = [
|
||||
GigaChatMessage(role=msg["role"], content=msg["content"])
|
||||
for msg in cached_context
|
||||
if msg["role"] != "system"
|
||||
]
|
||||
|
||||
# Добавляем системный промпт в начало
|
||||
# Системное сообщение ВСЕГДА должно быть первым
|
||||
system_message = GigaChatMessage(role="system", content=EARTH_PERSONA)
|
||||
if not context_messages or context_messages[0].role != "system":
|
||||
context_messages.insert(0, system_message)
|
||||
# Убеждаемся, что системное сообщение первое (удаляем все системные сообщения и добавляем одно в начало)
|
||||
context_messages = [msg for msg in context_messages if msg.role != "system"]
|
||||
context_messages.insert(0, system_message)
|
||||
|
||||
# Добавляем текущее сообщение пользователя
|
||||
context_messages.append(GigaChatMessage(role="user", content=message))
|
||||
|
||||
# Отправляем запрос
|
||||
# Отправляем запрос (не передаем message отдельно, т.к. оно уже в context_messages)
|
||||
response = await self.gigachat.chat_with_response(
|
||||
message=message,
|
||||
message="", # Пустое, т.к. сообщение уже добавлено в context_messages
|
||||
context=context_messages,
|
||||
model=model,
|
||||
temperature=0.7,
|
||||
@@ -76,7 +79,7 @@ class ChatAgent:
|
||||
user_id: UUID,
|
||||
message: str,
|
||||
context: Optional[List[dict]] = None,
|
||||
model: str = "GigaChat-2-Lite",
|
||||
model: str = "GigaChat-2",
|
||||
) -> tuple[str, int]:
|
||||
"""
|
||||
Отправить сообщение с явным контекстом.
|
||||
@@ -100,8 +103,9 @@ class ChatAgent:
|
||||
|
||||
context_messages.append(GigaChatMessage(role="user", content=message))
|
||||
|
||||
# Отправляем запрос (не передаем message отдельно, т.к. оно уже в context_messages)
|
||||
response = await self.gigachat.chat_with_response(
|
||||
message=message,
|
||||
message="", # Пустое, т.к. сообщение уже добавлено в context_messages
|
||||
context=context_messages,
|
||||
model=model,
|
||||
temperature=0.7,
|
||||
|
||||
@@ -23,7 +23,8 @@ class GigaChatClient:
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
"""Получить HTTP сессию (lazy initialization)."""
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession()
|
||||
connector = aiohttp.TCPConnector(ssl=False)
|
||||
self._session = aiohttp.ClientSession(connector=connector)
|
||||
return self._session
|
||||
|
||||
async def chat(
|
||||
@@ -73,7 +74,7 @@ class GigaChatClient:
|
||||
|
||||
Args:
|
||||
message: Текст сообщения
|
||||
context: История сообщений
|
||||
context: История сообщений (уже должна содержать системное сообщение первым)
|
||||
model: Модель GigaChat
|
||||
temperature: Температура генерации
|
||||
max_tokens: Максимальное количество токенов
|
||||
@@ -81,8 +82,22 @@ class GigaChatClient:
|
||||
Returns:
|
||||
Полный ответ от API
|
||||
"""
|
||||
messages = context or []
|
||||
messages.append(GigaChatMessage(role="user", content=message))
|
||||
# Создаем копию списка, чтобы не изменять оригинал
|
||||
messages = list(context) if context else []
|
||||
|
||||
# Убеждаемся, что системное сообщение первое
|
||||
system_messages = [msg for msg in messages if msg.role == "system"]
|
||||
non_system_messages = [msg for msg in messages if msg.role != "system"]
|
||||
|
||||
# Если есть системные сообщения, берем первое, иначе оставляем список пустым
|
||||
if system_messages:
|
||||
messages = [system_messages[0]] + non_system_messages
|
||||
else:
|
||||
messages = non_system_messages
|
||||
|
||||
# Добавляем текущее сообщение пользователя только если его еще нет в конце
|
||||
if not messages or messages[-1].role != "user" or messages[-1].content != message:
|
||||
messages.append(GigaChatMessage(role="user", content=message))
|
||||
|
||||
request = GigaChatRequest(
|
||||
model=model,
|
||||
|
||||
143
app.py
Normal file
143
app.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""FastAPI сервер для AI-агентов."""
|
||||
import os
|
||||
from typing import List, Optional, Dict, Any
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from fastapi import FastAPI, HTTPException, APIRouter
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from agents.gigachat_client import GigaChatClient
|
||||
from agents.chat_agent import ChatAgent
|
||||
from services.token_manager import TokenManager
|
||||
from services.cache_service import CacheService
|
||||
from models.gigachat_types import GigaChatMessage
|
||||
|
||||
app = FastAPI(title="New Planet AI Agents API", version="1.0.0")
|
||||
|
||||
# CORS middleware для работы с backend
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # В production указать конкретные домены
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Роутер для API эндпоинтов
|
||||
api_router = APIRouter(prefix="/api/v1", tags=["ai"])
|
||||
|
||||
# Инициализация сервисов
|
||||
token_manager = TokenManager()
|
||||
gigachat_client = GigaChatClient(token_manager)
|
||||
cache_service = CacheService() # Использует REDIS_URL из окружения
|
||||
chat_agent = ChatAgent(gigachat_client, cache_service)
|
||||
|
||||
|
||||
# Модели запросов/ответов
|
||||
class ChatRequest(BaseModel):
|
||||
"""Запрос на отправку сообщения в чат."""
|
||||
message: str = Field(..., min_length=1, max_length=2000)
|
||||
conversation_id: Optional[str] = None
|
||||
context: Optional[List[Dict[str, Any]]] = None
|
||||
model: Optional[str] = "GigaChat-2"
|
||||
user_id: Optional[UUID] = None
|
||||
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
"""Ответ от ИИ-агента."""
|
||||
response: str
|
||||
conversation_id: Optional[str] = None
|
||||
tokens_used: Optional[int] = None
|
||||
model: Optional[str] = None
|
||||
|
||||
|
||||
class GenerateTextRequest(BaseModel):
|
||||
"""Запрос на генерацию текста."""
|
||||
prompt: str = Field(..., min_length=1)
|
||||
model: Optional[str] = "GigaChat-2-Pro"
|
||||
|
||||
|
||||
class GenerateTextResponse(BaseModel):
|
||||
"""Ответ с сгенерированным текстом."""
|
||||
text: str
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Проверка здоровья сервиса."""
|
||||
return {"status": "ok", "service": "ai-agents"}
|
||||
|
||||
|
||||
@api_router.post("/chat", response_model=ChatResponse)
|
||||
async def chat(request: ChatRequest):
|
||||
"""
|
||||
Отправить сообщение в чат через ChatAgent.
|
||||
|
||||
Поддерживает два режима:
|
||||
1. С conversation_id - использует ChatAgent с контекстом из Redis
|
||||
2. С явным context - использует chat_with_context
|
||||
"""
|
||||
try:
|
||||
user_id = request.user_id or uuid4()
|
||||
|
||||
if request.context:
|
||||
# Используем явный контекст
|
||||
response_text, tokens_used = await chat_agent.chat_with_context(
|
||||
user_id=user_id,
|
||||
message=request.message,
|
||||
context=request.context,
|
||||
model=request.model or "GigaChat-2",
|
||||
)
|
||||
else:
|
||||
# Используем ChatAgent с conversation_id
|
||||
response_text, tokens_used = await chat_agent.chat(
|
||||
user_id=user_id,
|
||||
message=request.message,
|
||||
conversation_id=request.conversation_id,
|
||||
model=request.model or "GigaChat-2",
|
||||
)
|
||||
|
||||
return ChatResponse(
|
||||
response=response_text,
|
||||
conversation_id=request.conversation_id,
|
||||
tokens_used=tokens_used,
|
||||
model=request.model or "GigaChat-2",
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Chat error: {str(e)}")
|
||||
|
||||
|
||||
@api_router.post("/generate_text", response_model=GenerateTextResponse)
|
||||
async def generate_text(request: GenerateTextRequest):
|
||||
"""
|
||||
Генерация текста по промпту через GigaChat.
|
||||
"""
|
||||
try:
|
||||
response_text = await gigachat_client.chat(
|
||||
message=request.prompt,
|
||||
model=request.model or "GigaChat-2-Pro",
|
||||
temperature=0.7,
|
||||
max_tokens=2000,
|
||||
)
|
||||
|
||||
return GenerateTextResponse(text=response_text)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Generate text error: {str(e)}")
|
||||
|
||||
|
||||
# Подключение роутера к приложению
|
||||
app.include_router(api_router)
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown():
|
||||
"""Закрытие соединений при остановке."""
|
||||
await gigachat_client.close()
|
||||
await cache_service.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
|
||||
ai-agents:
|
||||
ports:
|
||||
- "8001:8000"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: new-planet-ai-agents
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- REDIS_URL=redis://newplanet-redis:6379/0
|
||||
- GIGACHAT_CLIENT_ID=${GIGACHAT_CLIENT_ID}
|
||||
- GIGACHAT_CLIENT_SECRET=${GIGACHAT_CLIENT_SECRET}
|
||||
- PYTHONUNBUFFERED=1
|
||||
@@ -20,13 +21,12 @@ services:
|
||||
- ./services:/app/services
|
||||
- ./prompts:/app/prompts
|
||||
- ./scripts:/app/scripts
|
||||
- ./app.py:/app/app.py
|
||||
networks:
|
||||
- new-planet-network
|
||||
# По умолчанию запускается Python REPL для интерактивного использования
|
||||
# Можно переопределить через docker-compose run или command
|
||||
stdin_open: true
|
||||
tty: true
|
||||
# Запуск FastAPI сервера
|
||||
command: uvicorn app:app --host 0.0.0.0 --port 8000
|
||||
|
||||
networks:
|
||||
new-planet-network:
|
||||
external: true
|
||||
external: true
|
||||
|
||||
@@ -41,9 +41,9 @@ class GigaChatUsage(BaseModel):
|
||||
class GigaChatResponse(BaseModel):
|
||||
"""Ответ от GigaChat API."""
|
||||
|
||||
id: str
|
||||
object: str
|
||||
created: int
|
||||
id: Optional[str] = None
|
||||
object: Optional[str] = None
|
||||
created: Optional[int] = None
|
||||
model: str
|
||||
choices: List[GigaChatChoice]
|
||||
usage: GigaChatUsage
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Pydantic модели для расписаний."""
|
||||
from datetime import date
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date as date_type
|
||||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
@@ -24,7 +26,7 @@ class Schedule(BaseModel):
|
||||
|
||||
id: Optional[UUID] = None
|
||||
title: str = Field(..., description="Название расписания")
|
||||
date: date = Field(..., description="Дата расписания")
|
||||
date: date_type = Field(..., description="Дата расписания")
|
||||
tasks: List[Task] = Field(default_factory=list, description="Список заданий")
|
||||
user_id: Optional[UUID] = None
|
||||
created_at: Optional[str] = None
|
||||
@@ -35,6 +37,6 @@ class ScheduleGenerateRequest(BaseModel):
|
||||
|
||||
child_age: int = Field(..., ge=1, le=18, description="Возраст ребенка")
|
||||
preferences: List[str] = Field(default_factory=list, description="Предпочтения ребенка")
|
||||
date: date = Field(..., description="Дата расписания")
|
||||
date: date_type = Field(..., description="Дата расписания")
|
||||
existing_tasks: Optional[List[str]] = Field(None, description="Существующие задания для учета")
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ numpy
|
||||
aiohttp
|
||||
redis
|
||||
Pillow
|
||||
fastapi
|
||||
uvicorn[standard]
|
||||
|
||||
# Dev
|
||||
pytest
|
||||
|
||||
@@ -12,7 +12,8 @@ class CacheService:
|
||||
"""Сервис для работы с Redis кэшем."""
|
||||
|
||||
def __init__(self, redis_url: Optional[str] = None):
|
||||
self.redis_url = redis_url or "redis://localhost:6379/0"
|
||||
import os
|
||||
self.redis_url = redis_url or os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
||||
self._client: Optional[redis.Redis] = None
|
||||
|
||||
async def _get_client(self) -> redis.Redis:
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
"""Управление токенами GigaChat."""
|
||||
import base64
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import aiohttp
|
||||
from aiohttp import FormData
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
@@ -17,10 +21,13 @@ class TokenManager:
|
||||
client_id: Optional[str] = None,
|
||||
client_secret: Optional[str] = None,
|
||||
auth_url: Optional[str] = None,
|
||||
credentials: Optional[str] = None,
|
||||
):
|
||||
self.client_id = client_id or os.getenv("GIGACHAT_CLIENT_ID")
|
||||
self.client_secret = client_secret or os.getenv("GIGACHAT_CLIENT_SECRET")
|
||||
self.auth_url = auth_url or os.getenv(
|
||||
# Приоритет: переданные параметры > переменные окружения > .env файл
|
||||
self.credentials = credentials or os.environ.get("GIGACHAT_CREDENTIALS") or os.getenv("GIGACHAT_CREDENTIALS")
|
||||
self.client_id = client_id or os.environ.get("GIGACHAT_CLIENT_ID") or os.getenv("GIGACHAT_CLIENT_ID")
|
||||
self.client_secret = client_secret or os.environ.get("GIGACHAT_CLIENT_SECRET") or os.getenv("GIGACHAT_CLIENT_SECRET")
|
||||
self.auth_url = auth_url or os.environ.get("GIGACHAT_AUTH_URL") or os.getenv(
|
||||
"GIGACHAT_AUTH_URL", "https://ngw.devices.sberbank.ru:9443/api/v2/oauth"
|
||||
)
|
||||
self._access_token: Optional[str] = None
|
||||
@@ -39,24 +46,101 @@ class TokenManager:
|
||||
if not force_refresh and self._access_token and time.time() < self._expires_at:
|
||||
return self._access_token
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
auth = aiohttp.BasicAuth(self.client_id, self.client_secret)
|
||||
async with session.post(
|
||||
self.auth_url,
|
||||
auth=auth,
|
||||
data={"scope": "GIGACHAT_API_PERS"},
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(f"Failed to get token: {response.status} - {error_text}")
|
||||
# Определяем, какой вариант используется: готовый ключ или client_id/client_secret
|
||||
if self.credentials:
|
||||
# Используем готовый ключ авторизации (уже закодированный в Base64)
|
||||
# Убираем префикс "Basic " если он есть
|
||||
credentials_key = self.credentials.strip().replace('\n', '').replace('\r', '')
|
||||
if credentials_key.startswith('Basic '):
|
||||
credentials_key = credentials_key[6:]
|
||||
|
||||
connector = aiohttp.TCPConnector(ssl=False)
|
||||
async with aiohttp.ClientSession(connector=connector) as session:
|
||||
headers = {
|
||||
"Authorization": f"Basic {credentials_key}",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
"RqUID": str(uuid.uuid4())
|
||||
}
|
||||
|
||||
form_data = {
|
||||
"scope": "GIGACHAT_API_PERS"
|
||||
}
|
||||
|
||||
async with session.post(
|
||||
self.auth_url,
|
||||
headers=headers,
|
||||
data=form_data,
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(f"Failed to get token: {response.status} - {error_text}")
|
||||
|
||||
data = await response.json()
|
||||
self._access_token = data["access_token"]
|
||||
# Токен обычно действителен 30 минут, обновляем за 5 минут до истечения
|
||||
expires_in = data.get("expires_in", 1800)
|
||||
self._expires_at = time.time() + expires_in - 300
|
||||
data = await response.json()
|
||||
self._access_token = data["access_token"]
|
||||
expires_in = data.get("expires_in", 1800)
|
||||
self._expires_at = time.time() + expires_in - 300
|
||||
|
||||
return self._access_token
|
||||
return self._access_token
|
||||
elif self.client_id and self.client_secret:
|
||||
# Очищаем от пробелов и переносов
|
||||
client_secret = self.client_secret.strip().replace('\n', '').replace('\r', '')
|
||||
|
||||
# Проверяем, является ли client_secret уже закодированным ключом Base64
|
||||
# Если secret начинается с букв/цифр и длиннее 50 символов, это уже ключ авторизации
|
||||
is_already_encoded = len(client_secret) > 50 and all(c.isalnum() or c in '+/=' for c in client_secret)
|
||||
|
||||
if is_already_encoded:
|
||||
# Это уже готовый ключ авторизации в Base64
|
||||
encoded_credentials = client_secret
|
||||
print(f"DEBUG: Using pre-encoded authorization key (length: {len(encoded_credentials)})")
|
||||
else:
|
||||
# Это настоящий client_id и client_secret, нужно закодировать
|
||||
client_id = self.client_id.strip().replace('\n', '').replace('\r', '')
|
||||
|
||||
if not client_id or not client_secret:
|
||||
raise Exception("GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET cannot be empty after cleaning")
|
||||
|
||||
credentials_string = f"{client_id}:{client_secret}"
|
||||
encoded_credentials = base64.b64encode(credentials_string.encode('utf-8')).decode('utf-8')
|
||||
print(f"DEBUG: Encoded client_id:client_secret (length: {len(encoded_credentials)})")
|
||||
|
||||
connector = aiohttp.TCPConnector(ssl=False)
|
||||
async with aiohttp.ClientSession(connector=connector) as session:
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
"RqUID": str(uuid.uuid4()),
|
||||
"Authorization": f"Basic {encoded_credentials}"
|
||||
}
|
||||
|
||||
payload = {"scope": "GIGACHAT_API_PERS"}
|
||||
|
||||
print(f"DEBUG: Authorization header starts with: Basic {encoded_credentials[:10]}...")
|
||||
|
||||
async with session.post(
|
||||
self.auth_url,
|
||||
headers=headers,
|
||||
data=payload,
|
||||
) as response:
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
raise Exception(
|
||||
f"Failed to get token: {response.status} - {error_text}. "
|
||||
f"URL: {self.auth_url}"
|
||||
)
|
||||
|
||||
data = await response.json()
|
||||
self._access_token = data["access_token"]
|
||||
expires_in = data.get("expires_in", 1800)
|
||||
self._expires_at = time.time() + expires_in - 300
|
||||
|
||||
return self._access_token
|
||||
else:
|
||||
raise Exception(
|
||||
"Either GIGACHAT_CREDENTIALS (ready authorization key) or "
|
||||
"GIGACHAT_CLIENT_ID and GIGACHAT_CLIENT_SECRET must be set"
|
||||
)
|
||||
|
||||
def is_token_valid(self) -> bool:
|
||||
"""Проверить, действителен ли текущий токен."""
|
||||
|
||||
Reference in New Issue
Block a user