Files
octopus/myproject/integrations/services/ai_services/openrouter_service.py

201 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Dict, Any, Tuple, Optional
from ..base import BaseIntegrationService
from .config import get_openrouter_config
import logging
import sys
import locale
# Патч для исправления проблемы с кодировкой в httpx на Windows
# Устанавливаем кодировку по умолчанию для Python
if sys.platform == 'win32':
try:
import httpx._models
original_normalize_header_value = httpx._models._normalize_header_value
def patched_normalize_header_value(value, encoding):
"""Патч для использования UTF-8 вместо ASCII для заголовков"""
# Если значение уже bytes, возвращаем его как есть
if isinstance(value, bytes):
return value
# Всегда используем UTF-8 вместо ASCII
encoding = encoding or 'utf-8'
if encoding.lower() == 'ascii':
encoding = 'utf-8'
return value.encode(encoding)
httpx._models._normalize_header_value = patched_normalize_header_value
logging.getLogger(__name__).info("Applied patch for httpx header encoding on Windows")
except Exception as e:
logging.getLogger(__name__).warning(f"Failed to apply httpx patch: {e}")
from openai import OpenAI
logger = logging.getLogger(__name__)
class OpenRouterIntegrationService(BaseIntegrationService):
"""
Сервис интеграции с OpenRouter.ai
"""
def __init__(self, config):
super().__init__(config)
logger.info(f"=== Инициализация OpenRouterIntegrationService ===")
try:
self.cfg = get_openrouter_config(config)
logger.info(f"Конфигурация успешно получена")
logger.info(f"API URL: {self.cfg.api_url}")
logger.info(f"Model name: {self.cfg.model_name}")
logger.info(f"Temperature: {self.cfg.temperature}")
logger.info(f"API key (первые 10 символов): {self.cfg.api_key[:10] if self.cfg.api_key else 'None'}...")
except Exception as e:
logger.error(f"Ошибка при получении конфигурации: {str(e)}", exc_info=True)
raise
# Импортируем клиент OpenAI (OpenRouter совместим с OpenAI API)
try:
self.client = OpenAI(
api_key=self.cfg.api_key,
base_url=self.cfg.api_url,
)
logger.info(f"OpenAI клиент для OpenRouter успешно создан")
except ImportError:
logger.error("Библиотека openai не установлена")
raise ImportError(
"Необходимо установить библиотеку openai: pip install openai"
)
except Exception as e:
logger.error(f"Ошибка при создании клиента: {str(e)}", exc_info=True)
raise
def test_connection(self) -> Tuple[bool, str]:
"""
Проверить соединение с API OpenRouter
"""
logger.info(f"=== Начало тестирования соединения с OpenRouter ===")
try:
logger.info(f"Отправка запроса к API OpenRouter...")
logger.info(f"Model: {self.cfg.model_name}, Temperature: {self.cfg.temperature}")
messages = [{"role": "user", "content": "ping"}]
response = self.client.chat.completions.create(
model=self.cfg.model_name,
messages=messages,
temperature=self.cfg.temperature,
max_tokens=10
)
logger.info(f"Получен ответ от API OpenRouter")
if response.choices and len(response.choices) > 0:
logger.info(f"Успешное подключение к OpenRouter")
return True, "Connection to OpenRouter successful"
else:
logger.error(f"Некорректный ответ от API OpenRouter: нет choices")
return False, "Invalid response from OpenRouter API"
except Exception as e:
logger.error(f"Ошибка подключения к OpenRouter: {str(e)}", exc_info=True)
return False, f"Connection error: {str(e)}"
def sync(self) -> Tuple[bool, str]:
"""
Основная операция синхронизации (для OpenRouter не требуется)
"""
return True, "Синхронизация OpenRouter не требуется"
def generate_text(self,
prompt: str,
system_prompt: Optional[str] = None,
max_tokens: Optional[int] = None) -> Tuple[bool, str, Optional[Dict]]:
"""
Генерация текста с помощью OpenRouter
Args:
prompt: Входной текст для генерации
system_prompt: Системный промпт (опционально)
max_tokens: Максимальное количество токенов в ответе
Returns:
tuple: (success: bool, message: str, response_data: dict or None)
"""
if not self.is_available():
return False, "Интеграция OpenRouter не активна", None
try:
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": prompt})
response = self.client.chat.completions.create(
model=self.cfg.model_name,
messages=messages,
temperature=self.cfg.temperature,
max_tokens=max_tokens or self.cfg.max_tokens
)
generated_text = response.choices[0].message.content
usage_info = getattr(response, 'usage', {})
return True, "Текст успешно сгенерирован", {
'generated_text': generated_text,
'usage': usage_info,
'model': self.cfg.model_name
}
except Exception as e:
logger.error(f"Ошибка генерации текста с помощью OpenRouter: {str(e)}")
return False, f"Ошибка генерации: {str(e)}", None
def generate_code(self,
prompt: str,
language: Optional[str] = None,
max_tokens: Optional[int] = None) -> Tuple[bool, str, Optional[Dict]]:
"""
Генерация кода с помощью OpenRouter
Args:
prompt: Описание задачи для генерации кода
language: Язык программирования (опционально)
max_tokens: Максимальное количество токенов в ответе
Returns:
tuple: (success: bool, message: str, response_data: dict or None)
"""
if not self.is_available():
return False, "Интеграция OpenRouter не активна", None
try:
system_prompt = "You are a helpful AI coding assistant. Generate clean, efficient, and well-documented code."
if language:
system_prompt += f" The code should be in {language}."
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
response = self.client.chat.completions.create(
model=self.cfg.model_name,
messages=messages,
temperature=self.cfg.temperature,
max_tokens=max_tokens or self.cfg.max_tokens
)
generated_code = response.choices[0].message.content
usage_info = getattr(response, 'usage', {})
return True, "Код успешно сгенерирован", {
'generated_code': generated_code,
'usage': usage_info,
'model': self.cfg.model_name
}
except Exception as e:
logger.error(f"Ошибка генерации кода с помощью OpenRouter: {str(e)}")
return False, f"Ошибка генерации кода: {str(e)}", None