Загрузка данных


class LLMClient:
    """Клиент для отправки текста на OpenAI-совместимый LLM-сервер (vLLM и т.п.)."""

    def __init__(self, base_url: str, model_name: str, api_key: str = "none"):
        self.base_url = base_url.rstrip("/")
        self.server_url = self.base_url + "/v1/chat/completions"
        self.models_url = self.base_url + "/v1/models"
        self.model_name = model_name
        self.headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {api_key}",
        }

    def get_models(self) -> list:
        """Получает список доступных моделей с сервера."""
        try:
            response = requests.get(self.models_url, headers=self.headers, timeout=30)
            response.raise_for_status()
            data = response.json()
            if "data" in data:
                return [model["id"] for model in data["data"]]
            return []
        except requests.exceptions.RequestException as e:
            logger.error("Ошибка при получении списка моделей: %s", e)
            return []
        except (KeyError, TypeError, ValueError) as e:
            logger.error("Ошибка парсинга списка моделей: %s", e)
            return []

    def send_prompt(
        self,
        system_role: str,
        user_text: str,
        temperature: float = 0.2,
        response_format: Optional[dict] = None,
    ) -> str:
        payload = {
            "model": self.model_name,
            "messages": [
                {"role": "system", "content": system_role},
                {"role": "user", "content": user_text},
            ],
            "temperature": temperature,
        }
        if response_format is not None:
            payload["response_format"] = response_format

        last_error = "Неизвестная ошибка"
        for attempt in range(1, MAX_RETRIES + 1):
            try:
                response = requests.post(
                    self.server_url,
                    headers=self.headers,
                    json=payload,
                    timeout=REQUEST_TIMEOUT_SEC,
                )
                response.raise_for_status()
                return response.json()["choices"][0]["message"]["content"]
            except requests.exceptions.RequestException as e:
                last_error = f"Ошибка соединения с LLM-сервером: {e}"
            except (KeyError, IndexError):
                last_error = f"Ошибка парсинга ответа. Сервер вернул: {response.text}"

            logger.warning("Попытка %s/%s не удалась: %s", attempt, MAX_RETRIES, last_error)
            if attempt < MAX_RETRIES:
                time.sleep(RETRY_DELAY_SEC * attempt)

        return f"__LLM_ERROR__ {last_error}"


client = LLMClient(base_url=BASE_URL, model_name=MODEL_NAME, api_key=API_KEY)