160 lines
5.2 KiB
Markdown
160 lines
5.2 KiB
Markdown
# 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
|
|
```mermaid
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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 |