Files
2026-07-24 22:36:04 +03:00

62 lines
2.3 KiB
Python

import asyncio
import json
import logging
import time
from collections.abc import Sequence
from openai import APIConnectionError, APIStatusError, AsyncOpenAI, RateLimitError
from app.llm.prompts import SYSTEM_PROMPT
logger = logging.getLogger(__name__)
class LLMUnavailable(RuntimeError):
pass
class DeepSeekClient:
def __init__(
self, api_key: str, base_url: str, model: str, timeout: float = 45, retries: int = 3
):
self.client = AsyncOpenAI(
api_key=api_key, base_url=base_url, timeout=timeout, max_retries=0
)
self.model, self.retries = model, retries
async def complete(
self, messages: Sequence[dict[str, str]], *, temperature: float = 0.1
) -> str:
started = time.monotonic()
payload = [{"role": "system", "content": SYSTEM_PROMPT}, *messages]
for attempt in range(self.retries):
try:
response = await self.client.chat.completions.create(
model=self.model, messages=list(payload), temperature=temperature
)
content = response.choices[0].message.content or ""
logger.info(
"llm_complete duration_ms=%d tokens=%s",
int((time.monotonic() - started) * 1000),
getattr(response.usage, "total_tokens", "unknown"),
)
return content
except (RateLimitError, APIConnectionError, APIStatusError) as error:
if (
isinstance(error, APIStatusError)
and error.status_code < 500
and error.status_code != 429
):
break
if attempt + 1 < self.retries:
await asyncio.sleep(2**attempt)
logger.warning("llm_unavailable duration_ms=%d", int((time.monotonic() - started) * 1000))
raise LLMUnavailable("Модель временно недоступна. Попробуйте ещё раз позже.")
async def json(self, messages: Sequence[dict[str, str]]) -> dict[str, object]:
content = await self.complete(messages, temperature=0)
try:
return json.loads(content.removeprefix("```json").removesuffix("```").strip())
except json.JSONDecodeError:
return {}