From 608757a73f1e2a71a909c0565d668db296e613ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BA=91=E9=BE=99?= <76432572+nankingjing@users.noreply.github.com> Date: Thu, 9 Jul 2026 23:28:41 +0800 Subject: [PATCH] feat(model): route optimizer/target calls to the openai_compatible backend --- skillopt/model/__init__.py | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/skillopt/model/__init__.py b/skillopt/model/__init__.py index d332997..5795800 100644 --- a/skillopt/model/__init__.py +++ b/skillopt/model/__init__.py @@ -7,6 +7,7 @@ from typing import Any from skillopt.model import azure_openai as _openai from skillopt.model import claude_backend as _claude from skillopt.model import minimax_backend as _minimax +from skillopt.model import openai_compatible_backend as _openai_compat from skillopt.model import qwen_backend as _qwen from skillopt.model.backend_config import ( # noqa: F401 configure_claude_code_exec, @@ -55,6 +56,10 @@ def set_backend(name: str | None) -> str: set_optimizer_backend("openai_chat") set_target_backend("minimax_chat") return "minimax_chat" + if normalized in {"openai_compatible", "openai_compatible_chat", "openai-compatible", "compat"}: + set_optimizer_backend("openai_compatible") + set_target_backend("openai_compatible") + return "openai_compatible" raise ValueError(f"Unsupported legacy backend: {name!r}") @@ -74,6 +79,8 @@ def get_backend_name() -> str: return "qwen_chat" if optimizer == "openai_chat" and target == "minimax_chat": return "minimax_chat" + if optimizer == "openai_compatible" and target == "openai_compatible": + return "openai_compatible" return f"{optimizer}+{target}" @@ -105,8 +112,13 @@ def chat_optimizer( reasoning_effort=reasoning_effort, timeout=timeout, ) +<<<<<<< HEAD if get_optimizer_backend() == "minimax_chat": return _minimax.chat_optimizer( +======= + if get_optimizer_backend() == "openai_compatible": + return _openai_compat.chat_optimizer( +>>>>>>> 3a5fa59 (feat(model): route optimizer/target calls to the openai_compatible backend) system=system, user=user, max_completion_tokens=max_completion_tokens, @@ -163,6 +175,16 @@ def chat_target( stage=stage, reasoning_effort=reasoning_effort, ) + if get_target_backend() == "openai_compatible": + return _openai_compat.chat_target( + system=system, + user=user, + max_completion_tokens=max_completion_tokens, + retries=retries, + stage=stage, + reasoning_effort=reasoning_effort, + timeout=timeout, + ) if not is_target_chat_backend(): raise NotImplementedError( "chat_target is only supported with target_backend=openai_chat, claude_chat, qwen_chat, or minimax_chat. " @@ -214,8 +236,13 @@ def chat_optimizer_messages( return_message=return_message, timeout=timeout, ) +<<<<<<< HEAD if get_optimizer_backend() == "minimax_chat": return _minimax.chat_target_messages( +======= + if get_optimizer_backend() == "openai_compatible": + return _openai_compat.chat_optimizer_messages( +>>>>>>> 3a5fa59 (feat(model): route optimizer/target calls to the openai_compatible backend) messages=messages, max_completion_tokens=max_completion_tokens, retries=retries, @@ -285,6 +312,18 @@ def chat_target_messages( tool_choice=tool_choice, return_message=return_message, ) + if get_target_backend() == "openai_compatible": + return _openai_compat.chat_target_messages( + messages=messages, + max_completion_tokens=max_completion_tokens, + retries=retries, + stage=stage, + reasoning_effort=reasoning_effort, + tools=tools, + tool_choice=tool_choice, + return_message=return_message, + timeout=timeout, + ) if not is_target_chat_backend(): raise NotImplementedError( "chat_target_messages is only supported with target_backend=openai_chat, claude_chat, qwen_chat, or minimax_chat. " @@ -387,6 +426,17 @@ def get_token_summary() -> dict: summary[stage]["prompt_tokens"] += values["prompt_tokens"] summary[stage]["completion_tokens"] += values["completion_tokens"] summary[stage]["total_tokens"] += values["total_tokens"] + openai_compat_summary = _openai_compat.get_token_summary() + for stage, values in openai_compat_summary.items(): + if stage == "_total": + continue + if stage not in summary: + summary[stage] = values + continue + summary[stage]["calls"] += values["calls"] + summary[stage]["prompt_tokens"] += values["prompt_tokens"] + summary[stage]["completion_tokens"] += values["completion_tokens"] + summary[stage]["total_tokens"] += values["total_tokens"] total = { "calls": 0, "prompt_tokens": 0, @@ -409,6 +459,7 @@ def reset_token_tracker() -> None: _claude.reset_token_tracker() _qwen.reset_token_tracker() _minimax.reset_token_tracker() + _openai_compat.reset_token_tracker() def configure_azure_openai( @@ -522,11 +573,43 @@ def configure_minimax_chat( ) +def configure_openai_compatible( + *, + base_url: str | None = None, + api_key: str | None = None, + model: str | None = None, + temperature: float | str | None = None, + timeout_seconds: float | str | None = None, + max_tokens: int | str | None = None, + optimizer_base_url: str | None = None, + optimizer_api_key: str | None = None, + optimizer_model: str | None = None, + target_base_url: str | None = None, + target_api_key: str | None = None, + target_model: str | None = None, +) -> None: + _openai_compat.configure_openai_compatible( + base_url=base_url, + api_key=api_key, + model=model, + temperature=temperature, + timeout_seconds=timeout_seconds, + max_tokens=max_tokens, + optimizer_base_url=optimizer_base_url, + optimizer_api_key=optimizer_api_key, + optimizer_model=optimizer_model, + target_base_url=target_base_url, + target_api_key=target_api_key, + target_model=target_model, + ) + + def set_reasoning_effort(effort: str | None) -> None: _openai.set_reasoning_effort(effort) _claude.set_reasoning_effort(effort) _qwen.set_reasoning_effort(effort) _minimax.set_reasoning_effort(effort) + _openai_compat.set_reasoning_effort(effort) def set_target_deployment(deployment: str) -> None: @@ -534,9 +617,11 @@ def set_target_deployment(deployment: str) -> None: _claude.set_target_deployment(deployment) _qwen.set_target_deployment(deployment) _minimax.set_target_deployment(deployment) + _openai_compat.set_target_deployment(deployment) def set_optimizer_deployment(deployment: str) -> None: _openai.set_optimizer_deployment(deployment) _claude.set_optimizer_deployment(deployment) _qwen.set_optimizer_deployment(deployment) + _openai_compat.set_optimizer_deployment(deployment)