| | """ |
| | utils/api_handler.py - API-Kommunikationsmodul für den Dr. Franz Psychochatbot |
| | |
| | Dieses Modul verwaltet die Kommunikation mit der HuggingFace Inference API: |
| | - Senden von Anfragen |
| | - Verarbeiten von Antworten |
| | - Fehlerbehandlung und Wiederholungslogik |
| | """ |
| |
|
| | import requests |
| | import time |
| | import random |
| | import json |
| | from typing import Dict, Any, Optional, List, Union |
| |
|
| | |
| | import config |
| |
|
| | class ApiHandler: |
| | """Klasse zur Verwaltung der API-Kommunikation""" |
| | |
| | def __init__(self, api_token: str = config.API_TOKEN, model_id: str = config.MODEL_ID): |
| | """ |
| | Initialisiert den API-Handler |
| | |
| | Args: |
| | api_token: Der API-Token für HuggingFace |
| | model_id: Die ID des zu verwendenden Modells |
| | """ |
| | self.api_token = api_token |
| | self.model_id = model_id |
| | self.api_url = f"{config.API_BASE_URL}{model_id}" |
| | self.headers = {"Authorization": f"Bearer {api_token}"} |
| | self.max_retries = 3 |
| | self.retry_delay = 2 |
| | |
| | def generate_text(self, prompt: str, params: Optional[Dict[str, Any]] = None) -> str: |
| | """ |
| | Generiert Text über die HuggingFace Inference API |
| | |
| | Args: |
| | prompt: Der Prompt für die Textgenerierung |
| | params: Optional, Parameter für die Textgenerierung |
| | |
| | Returns: |
| | Der generierte Text |
| | """ |
| | |
| | if params is None: |
| | params = { |
| | "max_new_tokens": config.MAX_NEW_TOKENS, |
| | "do_sample": True, |
| | "temperature": config.TEMPERATURE, |
| | "top_p": config.TOP_P, |
| | "top_k": config.TOP_K, |
| | "repetition_penalty": config.REPETITION_PENALTY |
| | } |
| | |
| | payload = { |
| | "inputs": prompt, |
| | "parameters": params |
| | } |
| | |
| | return self._send_api_request(payload) |
| | |
| | def _send_api_request(self, payload: Dict[str, Any]) -> str: |
| | """ |
| | Sendet eine Anfrage an die API mit Wiederholungslogik |
| | |
| | Args: |
| | payload: Die Anfragedaten |
| | |
| | Returns: |
| | Die generierte Antwort |
| | """ |
| | for attempt in range(self.max_retries): |
| | try: |
| | response = requests.post( |
| | self.api_url, |
| | headers=self.headers, |
| | json=payload, |
| | timeout=config.API_TIMEOUT |
| | ) |
| | |
| | |
| | if response.status_code == 200: |
| | return self._process_successful_response(response.json()) |
| | |
| | |
| | if response.status_code == 401: |
| | if config.DEBUG_MODE: |
| | print("API-Fehler: Ungültiger API-Token") |
| | return self._get_fallback_response("authentication_error") |
| | |
| | if response.status_code == 503 or response.status_code == 429: |
| | |
| | if config.DEBUG_MODE: |
| | print(f"API-Fehler: Server überlastet (Versuch {attempt+1}/{self.max_retries})") |
| | time.sleep(self.retry_delay * (attempt + 1)) |
| | continue |
| | |
| | |
| | if config.DEBUG_MODE: |
| | print(f"API-Fehler: Statuscode {response.status_code}") |
| | return self._get_fallback_response("general_error") |
| | |
| | except requests.exceptions.Timeout: |
| | if config.DEBUG_MODE: |
| | print(f"API-Timeout (Versuch {attempt+1}/{self.max_retries})") |
| | if attempt < self.max_retries - 1: |
| | time.sleep(self.retry_delay * (attempt + 1)) |
| | else: |
| | return self._get_fallback_response("timeout") |
| | |
| | except Exception as e: |
| | if config.DEBUG_MODE: |
| | print(f"API-Anfragefehler: {e}") |
| | return self._get_fallback_response("general_error") |
| | |
| | |
| | return self._get_fallback_response("max_retries") |
| | |
| | def _process_successful_response(self, response_data: Union[List[Dict[str, Any]], Dict[str, Any]]) -> str: |
| | """ |
| | Verarbeitet eine erfolgreiche API-Antwort |
| | |
| | Args: |
| | response_data: Die Antwortdaten von der API |
| | |
| | Returns: |
| | Der extrahierte generierte Text |
| | """ |
| | try: |
| | if isinstance(response_data, list) and len(response_data) > 0 and "generated_text" in response_data[0]: |
| | full_response = response_data[0]["generated_text"] |
| | |
| | if "Dr. Franz:" in full_response: |
| | reply = full_response.split("Dr. Franz:")[-1].strip() |
| | return reply |
| | else: |
| | return full_response |
| | elif isinstance(response_data, dict) and "generated_text" in response_data: |
| | return response_data["generated_text"] |
| | else: |
| | if config.DEBUG_MODE: |
| | print(f"Unerwartetes API-Antwortformat: {response_data}") |
| | return self._get_fallback_response("unexpected_format") |
| | except Exception as e: |
| | if config.DEBUG_MODE: |
| | print(f"Fehler bei der Verarbeitung der API-Antwort: {e}") |
| | return self._get_fallback_response("processing_error") |
| | |
| | def _get_fallback_response(self, error_type: str) -> str: |
| | """ |
| | Gibt eine Fallback-Antwort basierend auf dem Fehlertyp zurück |
| | |
| | Args: |
| | error_type: Der Typ des aufgetretenen Fehlers |
| | |
| | Returns: |
| | Eine passende Fallback-Antwort |
| | """ |
| | |
| | error_responses = { |
| | "authentication_error": [ |
| | "Es scheint ein Problem mit meiner Verbindung zu geben. Erzählen Sie mir mehr über Ihre Gedanken.", |
| | "Ich muss kurz nachdenken. Was meinen Sie genau mit Ihrer letzten Aussage?" |
| | ], |
| | "timeout": [ |
| | "Ihre Aussage ist komplex und bedarf tieferer Analyse. Könnten Sie Ihren Gedanken weiter ausführen?", |
| | "Interessant, dass Sie das so formulieren. Ich brauche einen Moment, um das zu verarbeiten." |
| | ], |
| | "max_retries": [ |
| | "Ihre Gedankengänge sind bemerkenswert verschlungen. Lassen Sie uns einen Schritt zurückgehen.", |
| | "Ich spüre Widerstand in unserer Kommunikation. Vielleicht sollten wir einen anderen Ansatz versuchen." |
| | ] |
| | } |
| | |
| | |
| | if error_type in error_responses: |
| | return random.choice(error_responses[error_type]) |
| | else: |
| | return random.choice(config.FALLBACK_RESPONSES) |
| |
|