"""OpenAI Adapter - OpenAI/DeepSeek/GLM/MiniMax compatible API adapter""" import json import logging from typing import Dict, List, Any, AsyncGenerator from .base import ProviderAdapter from ..llm_response import ParsedDelta logger = logging.getLogger(__name__) class OpenAIAdapter(ProviderAdapter): """OpenAI-compatible API adapter""" def __init__(self): pass @property def provider_type(self) -> str: return "openai" def build_request(self, model: str, messages: List[Dict], tools=None, **kwargs) -> tuple: api_key = kwargs.get("api_key", "") headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} body = {"model": model, "messages": messages, "stream": kwargs.get("stream", True)} if "temperature" in kwargs: body["temperature"] = kwargs["temperature"] if "max_tokens" in kwargs: body["max_tokens"] = kwargs["max_tokens"] if tools: body["tools"] = tools body["tool_choice"] = "auto" return body, headers def reset(self): pass async def parse_stream_chunk(self, raw_chunk: str) -> AsyncGenerator[ParsedDelta, None]: """Parse OpenAI/MiniMax format. Returns raw content for accumulation.""" if not raw_chunk or not raw_chunk.strip(): return chunk_str = raw_chunk.strip() if chunk_str.startswith("data: "): chunk_str = chunk_str[6:] elif chunk_str.startswith("data:"): chunk_str = chunk_str[5:] if chunk_str.strip() == "[DONE]": yield ParsedDelta(is_complete=True) return try: chunk = json.loads(chunk_str) except json.JSONDecodeError: return choices = chunk.get("choices", []) if not choices: return delta = choices[0].get("delta", {}) finish_reason = choices[0].get("finish_reason") content = delta.get("content", "") if not content: if finish_reason is not None: yield ParsedDelta(is_complete=True) return yield ParsedDelta(content=content) def parse_response(self, data: Dict) -> Dict: """Parse non-streaming response.""" choices = data.get("choices", []) if not choices: return {"content": "", "tool_calls": [], "usage": {}} message = choices[0].get("message", {}) content = message.get("content", "") tool_calls = message.get("tool_calls", []) usage = data.get("usage", {}) return {"content": content, "tool_calls": tool_calls, "usage": usage} def supports_tools(self) -> bool: return True