mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
feat(openrouter): wire Pareto Code router with min_coding_score knob (#22838)
Pick openrouter/pareto-code as your model and OpenRouter auto-routes each
request to the cheapest model meeting your coding-quality bar (ranked by
Artificial Analysis). The new openrouter.min_coding_score config key (0.0-1.0,
default 0.65) tunes the floor.
- hermes_cli/models.py: add openrouter/pareto-code to OPENROUTER_MODELS so
it shows up in the picker with a description
- hermes_cli/config.py: add openrouter.min_coding_score (default 0.65 — lands
on a mid-tier coder on the current Pareto frontier)
- plugins/model-providers/openrouter: emit extra_body.plugins =
[{id: pareto-router, min_coding_score: X}] when model is openrouter/pareto-code
AND the score is a valid float in [0.0, 1.0]
- agent/transports/chat_completions.py: same emission on the legacy flag
path (when no provider profile is loaded)
- run_agent.py: openrouter_min_coding_score kwarg + storage; plumbed into
both build_kwargs() invocations and the context-summary extra_body path
- cli.py: read openrouter.min_coding_score once at init, validate float in
[0,1], pass to AIAgent constructions (CLI + background-task paths)
- cron/scheduler.py, batch_runner.py, tools/delegate_tool.py,
tui_gateway/server.py: propagate the kwarg (mirrors providers_order
plumbing — subagents inherit, cron/batch read from config)
- tests: profile-level + transport-level coverage of the model gating,
unset/empty/out-of-range handling, and the legacy flag path
- docs: new 'OpenRouter Pareto Code Router' section in providers.md
Verified end-to-end against api.openrouter.ai: at score=0.65 we land on a
mid-tier coder, at omission we get the strongest. Score is silently dropped
on any model other than openrouter/pareto-code, so it's safe to leave set.
This commit is contained in:
parent
b349ae1e4c
commit
c7f0aab949
14 changed files with 222 additions and 0 deletions
28
run_agent.py
28
run_agent.py
|
|
@ -1075,6 +1075,7 @@ class AIAgent:
|
|||
provider_sort: str = None,
|
||||
provider_require_parameters: bool = False,
|
||||
provider_data_collection: str = None,
|
||||
openrouter_min_coding_score: Optional[float] = None,
|
||||
session_id: str = None,
|
||||
tool_progress_callback: callable = None,
|
||||
tool_start_callback: callable = None,
|
||||
|
|
@ -1137,6 +1138,9 @@ class AIAgent:
|
|||
providers_ignored (List[str]): OpenRouter providers to ignore (optional)
|
||||
providers_order (List[str]): OpenRouter providers to try in order (optional)
|
||||
provider_sort (str): Sort providers by price/throughput/latency (optional)
|
||||
openrouter_min_coding_score (float): Coding-score floor (0.0-1.0) for the
|
||||
openrouter/pareto-code router. Only applied when model == "openrouter/pareto-code".
|
||||
None or empty = let OpenRouter pick the strongest available coder.
|
||||
session_id (str): Pre-generated session ID for logging (optional, auto-generated if not provided)
|
||||
tool_progress_callback (callable): Callback function(tool_name, args_preview) for progress notifications
|
||||
clarify_callback (callable): Callback function(question, choices) -> str for interactive user questions.
|
||||
|
|
@ -1356,6 +1360,7 @@ class AIAgent:
|
|||
self.provider_sort = provider_sort
|
||||
self.provider_require_parameters = provider_require_parameters
|
||||
self.provider_data_collection = provider_data_collection
|
||||
self.openrouter_min_coding_score = openrouter_min_coding_score
|
||||
|
||||
# Store toolset filtering options
|
||||
self.enabled_toolsets = enabled_toolsets
|
||||
|
|
@ -9029,6 +9034,7 @@ class AIAgent:
|
|||
ollama_num_ctx=self._ollama_num_ctx,
|
||||
# Context forwarded to profile hooks:
|
||||
provider_preferences=_prefs or None,
|
||||
openrouter_min_coding_score=self.openrouter_min_coding_score,
|
||||
anthropic_max_output=_ant_max,
|
||||
supports_reasoning=self._supports_reasoning_extra_body(),
|
||||
qwen_session_metadata=_qwen_meta,
|
||||
|
|
@ -9068,6 +9074,7 @@ class AIAgent:
|
|||
is_custom_provider=self.provider == "custom",
|
||||
ollama_num_ctx=self._ollama_num_ctx,
|
||||
provider_preferences=_prefs or None,
|
||||
openrouter_min_coding_score=self.openrouter_min_coding_score,
|
||||
qwen_prepare_fn=self._qwen_prepare_chat_messages if _is_qwen else None,
|
||||
qwen_prepare_inplace_fn=self._qwen_prepare_chat_messages_inplace if _is_qwen else None,
|
||||
qwen_session_metadata=_qwen_meta,
|
||||
|
|
@ -10974,6 +10981,27 @@ class AIAgent:
|
|||
):
|
||||
summary_extra_body["provider"] = provider_preferences
|
||||
|
||||
# Pareto Code router plugin — model-gated. Same shape as
|
||||
# the main-loop emission so summary calls on
|
||||
# openrouter/pareto-code respect the user's coding-score floor.
|
||||
if (
|
||||
self.model == "openrouter/pareto-code"
|
||||
and (
|
||||
(self.provider or "").strip().lower() == "openrouter"
|
||||
or self._is_openrouter_url()
|
||||
)
|
||||
and self.openrouter_min_coding_score is not None
|
||||
and self.openrouter_min_coding_score != ""
|
||||
):
|
||||
try:
|
||||
_ps = float(self.openrouter_min_coding_score)
|
||||
except (TypeError, ValueError):
|
||||
_ps = None
|
||||
if _ps is not None and 0.0 <= _ps <= 1.0:
|
||||
summary_extra_body["plugins"] = [
|
||||
{"id": "pareto-router", "min_coding_score": _ps}
|
||||
]
|
||||
|
||||
if summary_extra_body:
|
||||
summary_kwargs["extra_body"] = summary_extra_body
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue