Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ docs/让*
tests/run_*.sh
tests/launch_*.py
*.launch.log
.codegraph/
uv.lock
# Superpowers smoke runs: raw agent output + local paths, share sanitized excerpts instead
smoke_results/
84 changes: 84 additions & 0 deletions skillopt/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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 hermes_backend as _hermes
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
Expand Down Expand Up @@ -67,6 +68,10 @@ def set_backend(name: str | None) -> str:
set_optimizer_backend("openai_chat")
set_target_backend("minimax_chat")
return "minimax_chat"
if normalized in {"hermes", "hermes_chat"}:
set_optimizer_backend("hermes_chat")
set_target_backend("hermes_chat")
return "hermes_chat"
if normalized in {"openai_compatible", "openai_compatible_chat", "openai-compatible", "compat"}:
set_optimizer_backend("openai_compatible")
set_target_backend("openai_compatible")
Expand All @@ -90,6 +95,8 @@ def get_backend_name() -> str:
return "qwen_chat"
if optimizer == "openai_chat" and target == "minimax_chat":
return "minimax_chat"
if optimizer == "hermes_chat" and target == "hermes_chat":
return "hermes_chat"
if optimizer == "openai_chat" and target == "cursor_exec":
return "cursor_exec"
if optimizer == "openai_compatible" and target == "openai_compatible":
Expand Down Expand Up @@ -125,6 +132,15 @@ def chat_optimizer(
reasoning_effort=reasoning_effort,
timeout=timeout,
)
if get_optimizer_backend() == "hermes_chat":
return _hermes.chat_optimizer(
system=system,
user=user,
max_completion_tokens=max_completion_tokens,
retries=retries,
stage=stage,
timeout=timeout,
)
if get_optimizer_backend() == "minimax_chat":
return _minimax.chat_optimizer(
system=system,
Expand Down Expand Up @@ -202,6 +218,15 @@ def chat_target(
stage=stage,
reasoning_effort=reasoning_effort,
)
if get_target_backend() == "hermes_chat":
return _hermes.chat_target(
system=system,
user=user,
max_completion_tokens=max_completion_tokens,
retries=retries,
stage=stage,
timeout=timeout,
)
if get_target_backend() == "openai_compatible":
return _openai_compat.chat_target(
system=system,
Expand Down Expand Up @@ -263,6 +288,17 @@ def chat_optimizer_messages(
return_message=return_message,
timeout=timeout,
)
if get_optimizer_backend() == "hermes_chat":
return _hermes.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,
)
if get_optimizer_backend() == "minimax_chat":
return _minimax.chat_target_messages(
messages=messages,
Expand Down Expand Up @@ -357,6 +393,17 @@ def chat_target_messages(
tool_choice=tool_choice,
return_message=return_message,
)
if get_target_backend() == "hermes_chat":
return _hermes.chat_target_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,
)
if get_target_backend() == "openai_compatible":
return _openai_compat.chat_target_messages(
messages=messages,
Expand Down Expand Up @@ -400,6 +447,18 @@ def chat_messages_with_deployment(
return_message: bool = False,
timeout: int | None = None,
) -> tuple[Any, dict]:
if get_optimizer_backend() == "hermes_chat" or get_target_backend() == "hermes_chat":
return _hermes.chat_messages_with_deployment(
deployment=deployment,
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_messages_with_deployment(
deployment=deployment,
messages=messages,
Expand All @@ -424,6 +483,16 @@ def chat_with_deployment(
reasoning_effort: str | None = None,
timeout: int | None = None,
) -> tuple[str, dict]:
if get_optimizer_backend() == "hermes_chat" or get_target_backend() == "hermes_chat":
return _hermes.chat_with_deployment(
deployment=deployment,
system=system,
user=user,
max_completion_tokens=max_completion_tokens,
retries=retries,
stage=stage,
timeout=timeout,
)
return _openai.chat_with_deployment(
deployment=deployment,
system=system,
Expand Down Expand Up @@ -471,6 +540,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"]
hermes_summary = _hermes.get_token_summary()
for stage, values in hermes_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"]
openai_compat_summary = _openai_compat.get_token_summary()
for stage, values in openai_compat_summary.items():
if stage == "_total":
Expand Down Expand Up @@ -515,6 +595,7 @@ def reset_token_tracker() -> None:
_claude.reset_token_tracker()
_qwen.reset_token_tracker()
_minimax.reset_token_tracker()
_hermes.reset_token_tracker()
_openai_compat.reset_token_tracker()
_codex.reset_token_tracker()

Expand Down Expand Up @@ -666,6 +747,7 @@ def set_reasoning_effort(effort: str | None) -> None:
_claude.set_reasoning_effort(effort)
_qwen.set_reasoning_effort(effort)
_minimax.set_reasoning_effort(effort)
_hermes.set_reasoning_effort(effort)
_openai_compat.set_reasoning_effort(effort)
_codex.set_reasoning_effort(effort)

Expand All @@ -675,6 +757,7 @@ def set_target_deployment(deployment: str) -> None:
_claude.set_target_deployment(deployment)
_qwen.set_target_deployment(deployment)
_minimax.set_target_deployment(deployment)
_hermes.set_target_deployment(deployment)
_openai_compat.set_target_deployment(deployment)
_codex.set_target_deployment(deployment)

Expand All @@ -683,5 +766,6 @@ def set_optimizer_deployment(deployment: str) -> None:
_openai.set_optimizer_deployment(deployment)
_claude.set_optimizer_deployment(deployment)
_qwen.set_optimizer_deployment(deployment)
_hermes.set_optimizer_deployment(deployment)
_openai_compat.set_optimizer_deployment(deployment)
_codex.set_optimizer_deployment(deployment)
11 changes: 6 additions & 5 deletions skillopt/model/backend_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ def set_optimizer_backend(backend: str) -> None:
"claude_chat",
"qwen_chat",
"minimax_chat",
"hermes_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', "
"'openai_compatible', and 'codex_exec'."
"'hermes_chat', 'openai_compatible', and 'codex_exec'."
)
os.environ["OPTIMIZER_BACKEND"] = OPTIMIZER_BACKEND

Expand All @@ -74,11 +75,11 @@ def get_optimizer_backend() -> str:
def set_target_backend(backend: str) -> None:
global TARGET_BACKEND
TARGET_BACKEND = normalize_backend_name(backend or "openai_chat")
if TARGET_BACKEND not in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "openai_compatible", "codex_exec", "claude_code_exec", "cursor_exec"}:
if TARGET_BACKEND not in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "openai_compatible", "codex_exec", "claude_code_exec", "cursor_exec", "hermes_chat"}:
raise ValueError(
f"Unsupported target backend: {TARGET_BACKEND!r}. "
"Supported values are 'openai_chat', 'claude_chat', 'qwen_chat', 'minimax_chat', "
"'openai_compatible', 'codex_exec', 'claude_code_exec', and 'cursor_exec'."
"'openai_compatible', 'codex_exec', 'claude_code_exec', 'cursor_exec', and 'hermes_chat'."
)
os.environ["TARGET_BACKEND"] = TARGET_BACKEND

Expand All @@ -90,20 +91,20 @@ def get_target_backend() -> str:
def is_target_exec_backend() -> bool:
return TARGET_BACKEND in {"codex_exec", "claude_code_exec", "cursor_exec"}


def is_optimizer_chat_backend() -> bool:
return OPTIMIZER_BACKEND in {
"openai_chat",
"claude_chat",
"qwen_chat",
"minimax_chat",
"hermes_chat",
"openai_compatible",
"codex_exec",
}


def is_target_chat_backend() -> bool:
return TARGET_BACKEND in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "openai_compatible"}
return TARGET_BACKEND in {"openai_chat", "claude_chat", "qwen_chat", "minimax_chat", "hermes_chat", "openai_compatible"}


def configure_codex_exec(
Expand Down
3 changes: 3 additions & 0 deletions skillopt/model/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"cursor_exec": "composer-2.5",
"qwen_chat": "Qwen/Qwen3.5-4B",
"minimax_chat": "MiniMax-M2.7",
"hermes_chat": "hermes",
"openai_compatible": "gpt-4o-mini",
}

Expand All @@ -49,6 +50,8 @@
"qwen_chat": "qwen_chat",
"minimax": "minimax_chat",
"minimax_chat": "minimax_chat",
"hermes": "hermes_chat",
"hermes_chat": "hermes_chat",
"openai_compatible": "openai_compatible",
"openai_compatible_chat": "openai_compatible",
"openai-compatible": "openai_compatible",
Expand Down
Loading