CV/my-app/docs/openrouter-refactoring-plan.md

5.2 KiB

Plan refaktoryzacji integracji OpenRouter

Cel

Refaktoryzacja kodu w resume_analysis.py w celu eliminacji wszystkich zależności od OpenAI API i wykorzystania wyłącznie OpenRouter API, z poprawą obecnej implementacji połączenia z OpenRouter.

Diagram przepływu zmian

graph TD
    A[Obecna implementacja] --> B[Faza 1: Usunięcie zależności OpenAI]
    B --> C[Faza 2: Refaktoryzacja klienta OpenRouter]
    C --> D[Faza 3: Optymalizacja obsługi odpowiedzi]
    D --> E[Faza 4: Testy i walidacja]

    subgraph "Faza 1: Usunięcie zależności OpenAI"
        B1[Usuń importy OpenAI]
        B2[Usuń zmienne konfiguracyjne OpenAI]
        B3[Usuń logikę wyboru klienta]
    end

    subgraph "Faza 2: Refaktoryzacja klienta OpenRouter"
        C1[Stwórz dedykowaną klasę OpenRouterClient]
        C2[Implementuj prawidłową konfigurację nagłówków]
        C3[Dodaj obsługę różnych modeli]
    end

    subgraph "Faza 3: Optymalizacja obsługi odpowiedzi"
        D1[Ujednolicenie formatu odpowiedzi]
        D2[Implementacja lepszej obsługi błędów]
        D3[Dodanie walidacji odpowiedzi]
    end

    subgraph "Faza 4: Testy i walidacja"
        E1[Testy jednostkowe]
        E2[Testy integracyjne]
        E3[Dokumentacja zmian]
    end

Szczegółowa implementacja

1. Dedykowana klasa OpenRouterClient

class OpenRouterClient:
    def __init__(self, api_key: str, model_name: str):
        self.api_key = api_key
        self.model_name = model_name
        self.base_url = "https://openrouter.ai/api/v1"
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "HTTP-Referer": "https://github.com/OpenRouterTeam/openrouter-examples",
            "X-Title": "CV Analysis Tool"
        })

    def create_chat_completion(self, messages: list, max_tokens: int = None):
        endpoint = f"{self.base_url}/chat/completions"
        payload = {
            "model": self.model_name,
            "messages": messages,
            "max_tokens": max_tokens
        }
        
        response = self.session.post(endpoint, json=payload)
        response.raise_for_status()
        return response.json()

    def get_available_models(self):
        endpoint = f"{self.base_url}/models"
        response = self.session.get(endpoint)
        response.raise_for_status()
        return response.json()

2. Konfiguracja i inicjalizacja

def initialize_openrouter_client():
    if not OPENROUTER_API_KEY:
        raise ValueError("OPENROUTER_API_KEY is required")
    
    client = OpenRouterClient(
        api_key=OPENROUTER_API_KEY,
        model_name=OPENROUTER_MODEL_NAME
    )
    
    # Verify connection and model availability
    try:
        models = client.get_available_models()
        if not any(model["id"] == OPENROUTER_MODEL_NAME for model in models):
            raise ValueError(f"Model {OPENROUTER_MODEL_NAME} not available")
        logger.debug(f"Successfully connected to OpenRouter. Available models: {models}")
        return client
    except Exception as e:
        logger.error(f"Failed to initialize OpenRouter client: {e}")
        raise

3. Obsługa odpowiedzi

class OpenRouterResponse:
    def __init__(self, raw_response: dict):
        self.raw_response = raw_response
        self.choices = self._parse_choices()
        self.usage = self._parse_usage()
        self.model = raw_response.get("model")

    def _parse_choices(self):
        choices = self.raw_response.get("choices", [])
        return [
            {
                "message": choice.get("message", {}),
                "finish_reason": choice.get("finish_reason"),
                "index": choice.get("index")
            }
            for choice in choices
        ]

    def _parse_usage(self):
        usage = self.raw_response.get("usage", {})
        return {
            "prompt_tokens": usage.get("prompt_tokens", 0),
            "completion_tokens": usage.get("completion_tokens", 0),
            "total_tokens": usage.get("total_tokens", 0)
        }

4. Obsługa błędów

class OpenRouterError(Exception):
    def __init__(self, message: str, status_code: int = None, response: dict = None):
        super().__init__(message)
        self.status_code = status_code
        self.response = response

def handle_openrouter_error(error: Exception) -> OpenRouterError:
    if isinstance(error, requests.exceptions.RequestException):
        if error.response is not None:
            try:
                error_data = error.response.json()
                message = error_data.get("error", {}).get("message", str(error))
                return OpenRouterError(
                    message=message,
                    status_code=error.response.status_code,
                    response=error_data
                )
            except ValueError:
                pass
    return OpenRouterError(str(error))

Kolejne kroki

  1. Implementacja powyższych klas i funkcji
  2. Usunięcie wszystkich zależności OpenAI
  3. Aktualizacja istniejącego kodu do korzystania z nowego klienta
  4. Dodanie testów jednostkowych i integracyjnych
  5. Aktualizacja dokumentacji