refactor(openai): drop SDK dependency and switch to native HTTP client

This commit is contained in:
zhayujie
2026-04-27 20:21:54 +08:00
parent 5c65196e44
commit 8752f0cc60
8 changed files with 920 additions and 330 deletions

View File

@@ -8,11 +8,11 @@ This includes: OpenAI, LinkAI, Azure OpenAI, and many third-party providers.
"""
import json
import openai
import requests
from typing import Optional
from common.log import logger
from agent.protocol.message_utils import drop_orphaned_tool_results_openai
from models.openai.openai_http_client import OpenAIHTTPClient, OpenAIHTTPError
class OpenAICompatibleBot:
@@ -135,49 +135,87 @@ class OpenAICompatibleBot:
"status_code": 500
}
def _get_http_client(self) -> OpenAIHTTPClient:
"""Build an HTTP client honoring the global proxy config.
Subclasses can override this for custom auth headers (e.g. Azure's
``api-key`` header) by returning a pre-configured client.
"""
from config import conf
proxy = conf().get("proxy") or None
return OpenAIHTTPClient(proxy=proxy)
def _handle_sync_response(self, request_params, api_key, api_base):
"""Handle synchronous OpenAI API response"""
"""Handle synchronous chat-completion via HTTP."""
params = dict(request_params)
params.pop("stream", None)
# Translate legacy SDK timeout kwarg to our HTTP client kwarg.
timeout = params.pop("request_timeout", None) or params.pop("timeout", None)
try:
# Build kwargs with explicit API configuration
kwargs = dict(request_params)
if api_key:
kwargs["api_key"] = api_key
if api_base:
kwargs["api_base"] = api_base
response = openai.ChatCompletion.create(**kwargs)
return response
client = self._get_http_client()
return client.chat_completions(
api_key=api_key,
api_base=api_base,
timeout=timeout,
stream=False,
**params,
)
except OpenAIHTTPError as e:
logger.error(
f"[{self.__class__.__name__}] sync response error: "
f"HTTP {e.status_code}: {e.message}"
)
return {
"error": True,
"message": e.message,
"status_code": e.status_code or 500,
}
except Exception as e:
logger.error(f"[{self.__class__.__name__}] sync response error: {e}")
return {
"error": True,
"message": str(e),
"status_code": 500
"status_code": 500,
}
def _handle_stream_response(self, request_params, api_key, api_base):
"""Handle streaming OpenAI API response"""
"""Handle streaming chat-completion via HTTP (SSE).
Yields dict chunks in OpenAI's standard streaming shape:
{"choices": [{"delta": {...}, "finish_reason": ...}], ...}
On error, yields a single ``{"error": ..., "status_code": ...}`` chunk
— the same contract :mod:`agent.protocol.agent_stream` already handles.
"""
params = dict(request_params)
params.pop("stream", None)
timeout = params.pop("request_timeout", None) or params.pop("timeout", None)
try:
# Build kwargs with explicit API configuration
kwargs = dict(request_params)
if api_key:
kwargs["api_key"] = api_key
if api_base:
kwargs["api_base"] = api_base
stream = openai.ChatCompletion.create(**kwargs)
# Stream chunks to caller
client = self._get_http_client()
stream = client.chat_completions(
api_key=api_key,
api_base=api_base,
timeout=timeout,
stream=True,
**params,
)
for chunk in stream:
yield chunk
except OpenAIHTTPError as e:
logger.error(
f"[{self.__class__.__name__}] stream response error: "
f"HTTP {e.status_code}: {e.message}"
)
yield {
"error": True,
"message": e.message,
"status_code": e.status_code or 500,
}
except Exception as e:
logger.error(f"[{self.__class__.__name__}] stream response error: {e}")
yield {
"error": True,
"message": str(e),
"status_code": 500
"status_code": 500,
}
def _convert_tools_to_openai_format(self, tools):