fix(codex): support exec optimizer backend

This commit is contained in:
TheGameVIX
2026-07-08 13:49:55 -04:00
parent 57333f3406
commit 0b94f890a0
11 changed files with 437 additions and 29 deletions
+43 -3
View File
@@ -6,6 +6,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 codex_backend as _codex
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
@@ -41,10 +42,14 @@ def set_backend(name: str | None) -> str:
set_target_backend("claude_chat")
return "claude_chat"
if normalized == "codex":
set_optimizer_backend("openai_chat")
set_optimizer_backend("codex_exec")
set_target_backend("codex_exec")
return "codex"
if normalized in {"codex_exec", "claude_code_exec"}:
if normalized == "codex_exec":
set_optimizer_backend("codex_exec")
set_target_backend("codex_exec")
return normalized
if normalized == "claude_code_exec":
set_optimizer_backend("openai_chat")
set_target_backend(normalized)
return normalized
@@ -73,7 +78,7 @@ def get_backend_name() -> str:
return "qwen_chat"
if optimizer == "openai_chat" and target == "openai_chat":
return "azure_openai"
if optimizer == "openai_chat" and target == "codex_exec":
if optimizer == "codex_exec" and target == "codex_exec":
return "codex"
if optimizer == "openai_chat" and target == "qwen_chat":
return "qwen_chat"
@@ -132,6 +137,15 @@ def chat_optimizer(
reasoning_effort=reasoning_effort,
timeout=timeout,
)
if get_optimizer_backend() == "codex_exec":
return _codex.chat_optimizer(
system=system,
user=user,
max_completion_tokens=max_completion_tokens,
retries=retries,
stage=stage,
timeout=timeout,
)
return _openai.chat_optimizer(
system=system,
user=user,
@@ -265,6 +279,17 @@ def chat_optimizer_messages(
return_message=return_message,
timeout=timeout,
)
if get_optimizer_backend() == "codex_exec":
return _codex.chat_optimizer_messages(
messages=messages,
max_completion_tokens=max_completion_tokens,
retries=retries,
stage=stage,
tools=tools,
tool_choice=tool_choice,
return_message=return_message,
timeout=timeout,
)
return _openai.chat_optimizer_messages(
messages=messages,
max_completion_tokens=max_completion_tokens,
@@ -449,6 +474,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"]
codex_summary = _codex.get_token_summary()
for stage, values in codex_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,
@@ -472,6 +508,7 @@ def reset_token_tracker() -> None:
_qwen.reset_token_tracker()
_minimax.reset_token_tracker()
_openai_compat.reset_token_tracker()
_codex.reset_token_tracker()
def configure_azure_openai(
@@ -622,6 +659,7 @@ def set_reasoning_effort(effort: str | None) -> None:
_qwen.set_reasoning_effort(effort)
_minimax.set_reasoning_effort(effort)
_openai_compat.set_reasoning_effort(effort)
_codex.set_reasoning_effort(effort)
def set_target_deployment(deployment: str) -> None:
@@ -630,6 +668,7 @@ def set_target_deployment(deployment: str) -> None:
_qwen.set_target_deployment(deployment)
_minimax.set_target_deployment(deployment)
_openai_compat.set_target_deployment(deployment)
_codex.set_target_deployment(deployment)
def set_optimizer_deployment(deployment: str) -> None:
@@ -637,3 +676,4 @@ def set_optimizer_deployment(deployment: str) -> None:
_claude.set_optimizer_deployment(deployment)
_qwen.set_optimizer_deployment(deployment)
_openai_compat.set_optimizer_deployment(deployment)
_codex.set_optimizer_deployment(deployment)
+18 -3
View File
@@ -49,10 +49,18 @@ CLAUDE_CODE_EXEC_MAX_THINKING_TOKENS = max(
def set_optimizer_backend(backend: str) -> None:
global OPTIMIZER_BACKEND
OPTIMIZER_BACKEND = normalize_backend_name(backend or "openai_chat")
if OPTIMIZER_BACKEND not in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "openai_compatible"}:
if OPTIMIZER_BACKEND not in {
"openai_chat",
"claude_chat",
"qwen_chat",
"minimax_chat",
"openai_compatible",
"codex_exec",
}:
raise ValueError(
f"Unsupported optimizer backend: {OPTIMIZER_BACKEND!r}. "
"Supported values are 'openai_chat', 'claude_chat', 'qwen_chat', 'minimax_chat', and 'openai_compatible'."
"Supported values are 'openai_chat', 'claude_chat', 'qwen_chat', 'minimax_chat', "
"'openai_compatible', and 'codex_exec'."
)
os.environ["OPTIMIZER_BACKEND"] = OPTIMIZER_BACKEND
@@ -81,7 +89,14 @@ def is_target_exec_backend() -> bool:
def is_optimizer_chat_backend() -> bool:
return OPTIMIZER_BACKEND in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "openai_compatible"}
return OPTIMIZER_BACKEND in {
"openai_chat",
"claude_chat",
"qwen_chat",
"minimax_chat",
"openai_compatible",
"codex_exec",
}
def is_target_chat_backend() -> bool:
+12 -7
View File
@@ -18,6 +18,7 @@ from skillopt.model.common import (
CompatToolFunction,
tracker,
)
from skillopt.model.backend_config import get_codex_exec_config
CODEX_BIN = os.environ.get("CODEX_CLI_BIN", "codex")
@@ -286,20 +287,21 @@ def _run_codex_exec(
timeout: int | None,
) -> tuple[str, dict[str, int]]:
with tempfile.TemporaryDirectory(prefix="skillopt_codex_") as temp_dir:
config = get_codex_exec_config()
output_path = os.path.join(temp_dir, "last_message.txt")
image_paths = _materialize_attachments(attachments, temp_dir)
profile = str(config.get("profile") or os.environ.get("CODEX_PROFILE", "")).strip()
reasoning_effort = str(REASONING_EFFORT or config.get("reasoning_effort") or "").strip()
command = [
CODEX_BIN,
str(config.get("path") or CODEX_BIN),
"exec",
"--json",
"--ephemeral",
"--profile",
CODEX_PROFILE,
"-c",
"approval_policy=\"never\"",
f"approval_policy={json.dumps(str(config.get('approval_policy') or 'never'))}",
"--sandbox",
CODEX_SANDBOX_MODE,
str(config.get("sandbox") or CODEX_SANDBOX_MODE),
"--skip-git-repo-check",
"--cd",
_default_working_directory(),
@@ -309,8 +311,11 @@ def _run_codex_exec(
output_path,
]
if REASONING_EFFORT:
command.extend(["-c", f"model_reasoning_effort={json.dumps(REASONING_EFFORT)}"])
if profile:
command.extend(["--profile", profile])
if reasoning_effort and reasoning_effort != "none":
command.extend(["-c", f"model_reasoning_effort={json.dumps(reasoning_effort)}"])
schema_path = None
if output_schema is not None: