gateway/gwserver/connector_aichat_openai.py
2025-03-20 00:21:51 +01:00

146 lines
No EOL
5.4 KiB
Python

import os
import json
import logging
import httpx
import base64
import mimetypes
from typing import Dict, Any, List, Optional
from fastapi import HTTPException
import configload as configload
# Logger konfigurieren
logger = logging.getLogger(__name__)
# Konfigurationsdaten laden
def load_config_data():
config = configload.load_config()
return {
"api_key": config.get('Connector_AiOpenai', 'API_KEY'),
"api_url": config.get('Connector_AiOpenai', 'API_URL', fallback="https://api.openai.com/v1/chat/completions"),
"model_name": config.get('Connector_AiOpenai', 'MODEL_NAME', fallback="gpt-4o"),
"temperature": float(config.get('Connector_AiOpenai', 'TEMPERATURE', fallback="0.2")),
"max_tokens": int(config.get('Connector_AiOpenai', 'MAX_TOKENS', fallback="2000"))
}
class ChatService:
"""
Connector für die Kommunikation mit der OpenAI API.
"""
def __init__(self):
# Konfiguration laden
self.config = load_config_data()
self.api_key = self.config["api_key"]
self.api_url = self.config["api_url"]
self.model_name = self.config["model_name"]
# HttpClient für API-Aufrufe
self.http_client = httpx.AsyncClient(
timeout=120.0, # Längeres Timeout für komplexe Anfragen
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
)
logger.info(f"OpenAI Connector initialisiert mit Modell: {self.model_name}")
async def call_api(self, messages: List[Dict[str, Any]], temperature: float = None, max_tokens: int = None) -> Dict[str, Any]:
"""
Ruft die OpenAI API mit den gegebenen Nachrichten auf.
Args:
messages: Liste von Nachrichten im OpenAI-Format (role, content)
temperature: Temperatur für die Antwortgenerierung (0.0-1.0)
max_tokens: Maximale Anzahl der Token in der Antwort
Returns:
Die Antwort der OpenAI API
Raises:
HTTPException: Bei Fehlern in der API-Kommunikation
"""
try:
# Verwende Parameter aus der Konfiguration, falls keine überschrieben wurden
if temperature is None:
temperature = self.config.get("temperature", 0.2)
if max_tokens is None:
max_tokens = self.config.get("max_tokens", 2000)
payload = {
"model": self.model_name,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens
}
response = await self.http_client.post(
self.api_url,
json=payload
)
if response.status_code != 200:
logger.error(f"OpenAI API-Fehler: {response.status_code} - {response.text}")
raise HTTPException(status_code=500, detail="Fehler bei der Kommunikation mit OpenAI API")
return response.json()
except Exception as e:
logger.error(f"Fehler beim Aufruf der OpenAI API: {str(e)}")
raise HTTPException(status_code=500, detail=f"Fehler beim Aufruf der OpenAI API: {str(e)}")
def prepare_file_message_content(self, prompt_text: str, file_paths: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Bereitet eine Nachricht mit Dateien für OpenAI API vor.
Args:
prompt_text: Der Text-Prompt
file_paths: Liste von Dateipfaden mit Metadaten (Dict mit id, name, type, path)
Returns:
Eine für OpenAI-API formatierte content-Liste
"""
message_content = [
{
"type": "text",
"text": prompt_text
}
]
# Füge Dateien als Base64-Anhänge hinzu
for file_info in file_paths:
file_path = file_info.get("path", "")
if file_path and os.path.exists(file_path):
try:
# Datei als Base64 codieren
with open(file_path, "rb") as f:
file_data = f.read()
base64_data = base64.b64encode(file_data).decode('utf-8')
# MIME-Typ bestimmen
mime_type, _ = mimetypes.guess_type(file_path)
if not mime_type:
mime_type = "application/octet-stream"
# Füge die Datei als Anhang hinzu
message_content.append({
"type": "file",
"source": {
"type": "base64",
"media_type": mime_type,
"data": base64_data
}
})
logger.info(f"Datei {file_info.get('name', 'Unbekannt')} als Anhang hinzugefügt")
except Exception as e:
logger.error(f"Fehler beim Hinzufügen der Datei {file_info.get('name', 'Unbekannt')}: {str(e)}")
return message_content
async def close(self):
"""Schließt den HTTP-Client beim Beenden der Anwendung"""
await self.http_client.aclose()