149 lines
5.0 KiB
Python
149 lines
5.0 KiB
Python
"""Minimal provider HTTP adapters for Groq, DeepSeek, and OpenAI-compatible endpoints using httpx."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
import httpx
|
|
|
|
|
|
def _load_env_file() -> None:
|
|
"""Loads environment variables from .env if present."""
|
|
for parent in [Path.cwd(), Path(__file__).resolve().parent.parent.parent.parent]:
|
|
env_file = parent / ".env"
|
|
if env_file.is_file():
|
|
try:
|
|
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if line and not line.startswith("#") and "=" in line:
|
|
key, val = line.split("=", 1)
|
|
key = key.strip()
|
|
val = val.strip().strip("'\"")
|
|
if key and key not in os.environ:
|
|
os.environ[key] = val
|
|
except Exception:
|
|
pass
|
|
break
|
|
|
|
|
|
_load_env_file()
|
|
|
|
|
|
class ProviderAdapter:
|
|
def __init__(self, provider_name: str, base_url: Optional[str] = None):
|
|
self.provider_name = provider_name
|
|
self.base_url = base_url
|
|
|
|
async def execute_call(
|
|
self,
|
|
model: str,
|
|
messages: List[Dict[str, str]],
|
|
temperature: float = 0.0,
|
|
timeout_seconds: int = 30,
|
|
response_format: Optional[Dict[str, Any]] = None,
|
|
) -> Dict[str, Any]:
|
|
raise NotImplementedError
|
|
|
|
|
|
class GroqAdapter(ProviderAdapter):
|
|
def __init__(self, base_url: str = "https://api.groq.com/openai/v1"):
|
|
super().__init__("groq", base_url)
|
|
self.api_key = os.environ.get("GROQ_API_KEY", "")
|
|
|
|
async def execute_call(
|
|
self,
|
|
model: str,
|
|
messages: List[Dict[str, str]],
|
|
temperature: float = 0.0,
|
|
timeout_seconds: int = 30,
|
|
response_format: Optional[Dict[str, Any]] = None,
|
|
) -> Dict[str, Any]:
|
|
headers = {
|
|
"Authorization": f"Bearer {self.api_key or os.environ.get('GROQ_API_KEY', '')}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload: Dict[str, Any] = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": temperature,
|
|
}
|
|
if response_format:
|
|
payload["response_format"] = response_format
|
|
|
|
async with httpx.AsyncClient(timeout=float(timeout_seconds)) as client:
|
|
resp = await client.post(
|
|
f"{self.base_url}/chat/completions", headers=headers, json=payload
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
class DeepSeekAdapter(ProviderAdapter):
|
|
def __init__(self, base_url: str = "https://api.deepseek.com/v1"):
|
|
super().__init__("deepseek", base_url)
|
|
self.api_key = os.environ.get("DEEPSEEK_API_KEY", "")
|
|
|
|
async def execute_call(
|
|
self,
|
|
model: str,
|
|
messages: List[Dict[str, str]],
|
|
temperature: float = 0.0,
|
|
timeout_seconds: int = 30,
|
|
response_format: Optional[Dict[str, Any]] = None,
|
|
) -> Dict[str, Any]:
|
|
headers = {
|
|
"Authorization": f"Bearer {self.api_key or os.environ.get('DEEPSEEK_API_KEY', '')}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload: Dict[str, Any] = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": temperature,
|
|
}
|
|
if response_format:
|
|
payload["response_format"] = response_format
|
|
|
|
async with httpx.AsyncClient(timeout=float(timeout_seconds)) as client:
|
|
resp = await client.post(
|
|
f"{self.base_url}/chat/completions", headers=headers, json=payload
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
class OpenAIAdapter(ProviderAdapter):
|
|
def __init__(self, base_url: Optional[str] = None):
|
|
url = base_url or os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1")
|
|
super().__init__("openai", url)
|
|
self.api_key = os.environ.get("OPENAI_API_KEY", "")
|
|
|
|
async def execute_call(
|
|
self,
|
|
model: str,
|
|
messages: List[Dict[str, str]],
|
|
temperature: float = 0.0,
|
|
timeout_seconds: int = 30,
|
|
response_format: Optional[Dict[str, Any]] = None,
|
|
) -> Dict[str, Any]:
|
|
key = self.api_key or os.environ.get("OPENAI_API_KEY", "")
|
|
headers = {
|
|
"Authorization": f"Bearer {key}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload: Dict[str, Any] = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": temperature,
|
|
}
|
|
if response_format:
|
|
payload["response_format"] = response_format
|
|
|
|
async with httpx.AsyncClient(timeout=float(timeout_seconds)) as client:
|
|
resp = await client.post(
|
|
f"{self.base_url}/chat/completions", headers=headers, json=payload
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|