74 lines
2.1 KiB
Python
74 lines
2.1 KiB
Python
"""Generic OpenAI-compatible provider (OpenAI, Together, etc.)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
|
|
import requests
|
|
|
|
from .base import ModelInfo, ProviderResponse
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# OpenAI-compat specific env vars
|
|
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
|
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1")
|
|
OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
|
|
|
|
MODELS = {
|
|
"gpt-4o-mini": ModelInfo(
|
|
id="gpt-4o-mini",
|
|
vision=True,
|
|
cost_per_input_token=0.00000015,
|
|
cost_per_output_token=0.0000006,
|
|
notes="Cheap, fast, decent vision",
|
|
),
|
|
"gpt-4o": ModelInfo(
|
|
id="gpt-4o",
|
|
vision=True,
|
|
cost_per_input_token=0.0000025,
|
|
cost_per_output_token=0.00001,
|
|
notes="Best OpenAI vision model",
|
|
),
|
|
}
|
|
|
|
|
|
class OpenAICompatProvider:
|
|
name = "openai"
|
|
models = MODELS
|
|
|
|
def __init__(self):
|
|
self.api_key = OPENAI_API_KEY
|
|
self.base_url = OPENAI_BASE_URL
|
|
self.model = OPENAI_MODEL
|
|
self.endpoint = f"{self.base_url.rstrip('/')}/chat/completions"
|
|
self.headers = {
|
|
"Authorization": f"Bearer {self.api_key}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
def call(self, image_b64: str, prompt: str) -> ProviderResponse:
|
|
payload = {
|
|
"model": self.model,
|
|
"messages": [{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": prompt},
|
|
{"type": "image_url", "image_url": {
|
|
"url": f"data:image/jpeg;base64,{image_b64}",
|
|
}},
|
|
],
|
|
}],
|
|
"max_tokens": 150,
|
|
}
|
|
|
|
resp = requests.post(self.endpoint, headers=self.headers, json=payload, timeout=30)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
answer = data["choices"][0]["message"]["content"].strip()
|
|
total_tokens = data.get("usage", {}).get("total_tokens", 0)
|
|
|
|
return ProviderResponse(answer=answer, total_tokens=total_tokens)
|