feat(reasoning): add max and ultra effort levels (#62650)

This commit is contained in:
Teknium 2026-07-12 00:26:49 -07:00 committed by GitHub
parent 62a76bd3d5
commit 7550c594ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 175 additions and 65 deletions

View file

@ -65,6 +65,7 @@ THINKING_BUDGET = {"xhigh": 32000, "high": 16000, "medium": 8000, "low": 4000}
# maps to low on every model. See:
# https://platform.claude.com/docs/en/about-claude/models/migration-guide
ADAPTIVE_EFFORT_MAP = {
"ultra": "max",
"max": "max",
"xhigh": "xhigh",
"high": "high",

View file

@ -18,6 +18,20 @@ from agent.transports.base import ProviderTransport
from agent.transports.types import NormalizedResponse, ToolCall, Usage
def _reasoning_config_for_model(model: str, reasoning_config: dict | None) -> dict | None:
"""Return the model's wire-compatible reasoning config."""
if not isinstance(reasoning_config, dict):
return reasoning_config
if (
"gpt-5.6" in (model or "").lower()
and str(reasoning_config.get("effort") or "").strip().lower() == "ultra"
):
normalized = dict(reasoning_config)
normalized["effort"] = "max"
return normalized
return reasoning_config
def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) -> dict | None:
"""Translate Hermes/OpenRouter-style reasoning config to Gemini thinkingConfig."""
if reasoning_config is None or not isinstance(reasoning_config, dict):
@ -52,7 +66,7 @@ def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) ->
if normalized_model.startswith("gemini-2.5-"):
return thinking_config
if effort not in {"minimal", "low", "medium", "high", "xhigh"}:
if effort not in {"minimal", "low", "medium", "high", "xhigh", "max", "ultra"}:
effort = "medium"
# Gemini 3 Flash documents low/medium/high thinking levels; Gemini 3 Pro
@ -62,13 +76,13 @@ def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) ->
if "flash" in normalized_model:
if effort in {"minimal", "low"}:
thinking_config["thinkingLevel"] = "low"
elif effort in {"high", "xhigh"}:
elif effort in {"high", "xhigh", "max", "ultra"}:
thinking_config["thinkingLevel"] = "high"
else:
thinking_config["thinkingLevel"] = "medium"
elif "pro" in normalized_model:
thinking_config["thinkingLevel"] = (
"high" if effort in {"high", "xhigh"} else "low"
"high" if effort in {"high", "xhigh", "max", "ultra"} else "low"
)
return thinking_config
@ -364,7 +378,7 @@ class ChatCompletionsTransport(ProviderTransport):
is_nvidia_nim = params.get("is_nvidia_nim", False)
is_kimi = params.get("is_kimi", False)
is_tokenhub = params.get("is_tokenhub", False)
reasoning_config = params.get("reasoning_config")
reasoning_config = _reasoning_config_for_model(model, params.get("reasoning_config"))
if ephemeral is not None and max_tokens_fn:
api_kwargs.update(max_tokens_fn(ephemeral))
@ -563,7 +577,7 @@ class ChatCompletionsTransport(ProviderTransport):
api_kwargs["max_tokens"] = anthropic_max
# Provider-specific api_kwargs extras (reasoning_effort, metadata, etc.)
reasoning_config = params.get("reasoning_config")
reasoning_config = _reasoning_config_for_model(model, params.get("reasoning_config"))
extra_body_from_profile, top_level_from_profile = (
profile.build_api_kwargs_extras(
reasoning_config=reasoning_config,

View file

@ -164,6 +164,12 @@ class ResponsesApiTransport(ProviderTransport):
reasoning_effort = reasoning_config["effort"]
_effort_clamp = {"minimal": "low"}
if "gpt-5.6" in (model or "").lower():
# Ultra is the Codex product tier; the Responses API wire value is max.
_effort_clamp["ultra"] = "max"
if params.get("is_xai_responses", False):
# xAI Responses tops out at high; keep generic stronger values usable.
_effort_clamp.update({"xhigh": "high", "max": "high", "ultra": "high"})
reasoning_effort = _effort_clamp.get(reasoning_effort, reasoning_effort)
response_tools = _responses_tools(tools)

View file

@ -237,7 +237,7 @@ export const ENUM_OPTIONS: Record<string, string[]> = {
'approvals.mode': ['manual', 'smart', 'off'],
'code_execution.mode': ['project', 'strict'],
'context.engine': ['compressor', 'default', 'custom'],
'delegation.reasoning_effort': ['', 'minimal', 'low', 'medium', 'high', 'xhigh'],
'delegation.reasoning_effort': ['', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'],
'memory.provider': ['', 'builtin', 'hindsight', 'honcho'],
// Terminal execution backends — kept in sync with the dispatch ladder in
// tools/terminal_tool.py::_create_environment (local/docker/singularity/

View file

@ -82,7 +82,7 @@ export function ModelSettingsSkeleton() {
// Hermes' reasoning levels (VALID_REASONING_EFFORTS); `none` = thinking off.
// Empty config = Hermes default (medium), shown as Medium.
const EFFORT_VALUES = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const
const EFFORT_VALUES = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'] as const
// agent.service_tier stores "fast"/"priority"/"on" for fast; anything else is
// normal (mirrors tui_gateway _load_service_tier).
@ -93,8 +93,8 @@ const isFastTier = (tier: unknown): boolean =>
.toLowerCase()
)
// Reuse the composer's effort labels (`xhigh` shows as "Max", else 1:1).
const effortLabelKey = (v: string) => (v === 'xhigh' ? 'max' : v) as 'high' | 'low' | 'max' | 'medium' | 'minimal'
// Reuse the composer's effort labels.
const effortLabelKey = (v: string) => v as 'high' | 'low' | 'max' | 'medium' | 'minimal' | 'ultra' | 'xhigh'
// A provider row is "ready" to pick a model from when it reports models. The
// backend now surfaces the full `hermes model` universe (every canonical

View file

@ -24,7 +24,9 @@ const EFFORT_OPTIONS = [
{ value: 'low', labelKey: 'low' },
{ value: 'medium', labelKey: 'medium' },
{ value: 'high', labelKey: 'high' },
{ value: 'xhigh', labelKey: 'max' }
{ value: 'xhigh', labelKey: 'xhigh' },
{ value: 'max', labelKey: 'max' },
{ value: 'ultra', labelKey: 'ultra' }
] as const
/** How "fast" is achieved for a given model two different mechanisms:

View file

@ -2039,7 +2039,9 @@ export const en: Translations = {
low: 'Low',
medium: 'Medium',
high: 'High',
xhigh: 'Extra High',
max: 'Max',
ultra: 'Ultra',
updateFailed: 'Model option update failed',
fastFailed: 'Fast mode update failed'
},

View file

@ -1984,7 +1984,9 @@ export const ja = defineLocale({
low: '低',
medium: '中',
high: '高',
xhigh: '特高',
max: '最大',
ultra: 'ウルトラ',
updateFailed: 'モデルオプションの更新に失敗しました',
fastFailed: '高速モードの更新に失敗しました'
},

View file

@ -1671,7 +1671,9 @@ export interface Translations {
low: string
medium: string
high: string
xhigh: string
max: string
ultra: string
updateFailed: string
fastFailed: string
}

View file

@ -1919,7 +1919,9 @@ export const zhHant = defineLocale({
low: '低',
medium: '中',
high: '高',
xhigh: '極高',
max: '最高',
ultra: '超高',
updateFailed: '模型選項更新失敗',
fastFailed: '快速模式更新失敗'
},

View file

@ -2203,7 +2203,9 @@ export const zh: Translations = {
low: '低',
medium: '中',
high: '高',
xhigh: '极高',
max: '最高',
ultra: '超高',
updateFailed: '模型选项更新失败',
fastFailed: '快速模式更新失败'
},

View file

@ -22,7 +22,9 @@ describe('model-status-label', () => {
it('maps reasoning effort to compact labels', () => {
expect(reasoningEffortLabel('high')).toBe('High')
expect(reasoningEffortLabel('xhigh')).toBe('Max')
expect(reasoningEffortLabel('xhigh')).toBe('XHigh')
expect(reasoningEffortLabel('max')).toBe('Max')
expect(reasoningEffortLabel('ultra')).toBe('Ultra')
expect(reasoningEffortLabel('')).toBe('')
})

View file

@ -6,7 +6,9 @@ const REASONING_LABELS: Record<string, string> = {
low: 'Low',
medium: 'Med',
high: 'High',
xhigh: 'Max'
xhigh: 'XHigh',
max: 'Max',
ultra: 'Ultra'
}
export function reasoningEffortLabel(effort: string): string {

View file

@ -1192,7 +1192,7 @@ def main(
providers_order (str): Comma-separated list of OpenRouter providers to try in order (e.g. "anthropic,openai,google")
provider_sort (str): Sort providers by "price", "throughput", or "latency" (OpenRouter only)
max_tokens (int): Maximum tokens for model responses (optional, uses model default if not set)
reasoning_effort (str): OpenRouter reasoning effort level: "none", "minimal", "low", "medium", "high", "xhigh" (default: "medium")
reasoning_effort (str): Reasoning effort: "none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra" (default: "medium")
reasoning_disabled (bool): Completely disable reasoning/thinking tokens (default: False)
prefill_messages_file (str): Path to JSON file containing prefill messages (list of {role, content} dicts)
max_samples (int): Only process the first N samples from the dataset (optional, processes all if not set)
@ -1261,7 +1261,7 @@ def main(
print("🧠 Reasoning: DISABLED (effort=none)")
elif reasoning_effort:
# Use specified effort level
valid_efforts = ["none", "minimal", "low", "medium", "high", "xhigh"]
valid_efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]
if reasoning_effort not in valid_efforts:
print(f"❌ Error: --reasoning_effort must be one of: {', '.join(valid_efforts)}")
return

View file

@ -4792,7 +4792,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
"""Load reasoning effort from config.yaml.
Reads agent.reasoning_effort from config.yaml. Valid: "none",
"minimal", "low", "medium", "high", "xhigh". Returns None to use
"minimal", "low", "medium", "high", "xhigh", "max", "ultra". Returns None to use
default (medium).
"""
from hermes_constants import parse_reasoning_effort

View file

@ -2733,7 +2733,7 @@ class GatewaySlashCommandsMixin:
return t("gateway.reasoning.reset_done")
if effort == "none":
parsed = {"enabled": False}
elif effort in {"minimal", "low", "medium", "high", "xhigh"}:
elif effort in {"minimal", "low", "medium", "high", "xhigh", "max", "ultra"}:
parsed = {"enabled": True, "effort": effort}
else:
return t(

View file

@ -2471,7 +2471,7 @@ class CLICommandsMixin:
Usage:
/reasoning Show current effort level and display state
/reasoning <level> Set reasoning effort (none, minimal, low, medium, high, xhigh)
/reasoning <level> Set effort (none, minimal, low, medium, high, xhigh, max, ultra)
/reasoning show|on Show model thinking/reasoning in output
/reasoning hide|off Hide model thinking/reasoning from output
/reasoning full Show complete thinking (no 10-line clamp)
@ -2493,7 +2493,7 @@ class CLICommandsMixin:
full_state = "full" if getattr(self, "reasoning_full", False) else "clamped to 10 lines"
_cprint(f" {_ACCENT}Reasoning effort: {level}{_RST}")
_cprint(f" {_ACCENT}Reasoning display: {display_state} ({full_state}){_RST}")
_cprint(f" {_DIM}Usage: /reasoning <none|minimal|low|medium|high|xhigh|show|hide|full|clamp>{_RST}")
_cprint(f" {_DIM}Usage: /reasoning <none|minimal|low|medium|high|xhigh|max|ultra|show|hide|full|clamp>{_RST}")
return
arg = parts[1].strip().lower()
@ -2534,7 +2534,7 @@ class CLICommandsMixin:
parsed = _parse_reasoning_config(arg)
if parsed is None:
_cprint(f" {_DIM}(._.) Unknown argument: {arg}{_RST}")
_cprint(f" {_DIM}Valid levels: none, minimal, low, medium, high, xhigh{_RST}")
_cprint(f" {_DIM}Valid levels: none, minimal, low, medium, high, xhigh, max, ultra{_RST}")
_cprint(f" {_DIM}Display: show, hide{_RST}")
return

View file

@ -154,7 +154,7 @@ COMMAND_REGISTRY: list[CommandDef] = [
"Configuration"),
CommandDef("reasoning", "Manage reasoning effort and display", "Configuration",
args_hint="[level|show|hide|full|clamp]",
subcommands=("none", "minimal", "low", "medium", "high", "xhigh", "show", "hide", "on", "off", "full", "clamp")),
subcommands=("none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra", "show", "hide", "on", "off", "full", "clamp")),
CommandDef("fast", "Toggle fast mode — OpenAI Priority Processing / Anthropic Fast Mode (Normal/Fast)", "Configuration",
args_hint="[normal|fast|status]",
subcommands=("normal", "fast", "status", "on", "off")),

View file

@ -2240,8 +2240,8 @@ DEFAULT_CONFIG = {
# (API, tools, iteration budget), never a delegation
# stopwatch. Set a positive number of seconds
# (floor 30s) to enforce a hard cap.
"reasoning_effort": "", # reasoning effort for subagents: "xhigh", "high", "medium",
# "low", "minimal", "none" (empty = inherit parent's level)
"reasoning_effort": "", # subagent effort: "ultra", "max", "xhigh", "high",
# "medium", "low", "minimal", "none" (empty = inherit)
"max_concurrent_children": 3, # unified concurrency cap: max parallel children per batch
# AND max concurrent background (background=true)
# delegation units. New async dispatches beyond the cap

View file

@ -3925,7 +3925,7 @@ def _prompt_reasoning_effort_selection(efforts, current_effort=""):
str(effort).strip().lower() for effort in efforts if str(effort).strip()
)
)
canonical_order = ("minimal", "low", "medium", "high", "xhigh")
canonical_order = ("minimal", "low", "medium", "high", "xhigh", "max", "ultra")
ordered = [effort for effort in canonical_order if effort in deduped]
ordered.extend(effort for effort in deduped if effort not in canonical_order)
if not ordered:

View file

@ -696,7 +696,7 @@ _SCHEMA_OVERRIDES: Dict[str, Dict[str, Any]] = {
"delegation.reasoning_effort": {
"type": "select",
"description": "Reasoning effort for delegated subagents",
"options": ["", "low", "medium", "high"],
"options": ["", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"],
},
"updates.non_interactive_local_changes": {
"type": "select",

View file

@ -791,13 +791,16 @@ def apply_subprocess_home_env(env: dict[str, str]) -> None:
env["HOME"] = home
VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh", "max")
VALID_REASONING_EFFORTS = (
"minimal", "low", "medium", "high", "xhigh", "max", "ultra",
)
def parse_reasoning_effort(effort) -> dict | None:
"""Parse a reasoning effort level into a config dict.
Valid levels: "none", "minimal", "low", "medium", "high", "xhigh", "max".
Valid levels: "none", "minimal", "low", "medium", "high", "xhigh", "max",
"ultra".
Returns None when the input is empty or unrecognized (caller uses default).
Returns {"enabled": False} for "none" (aliases: "false", "disabled", and
YAML boolean False users write ``reasoning_effort: false``/``off``/``no``

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View file

@ -35,8 +35,8 @@ class CopilotProfile(ProviderProfile):
supported_efforts = github_model_reasoning_efforts(model)
if supported_efforts and reasoning_config:
effort = reasoning_config.get("effort", "medium")
# Normalize non-standard effort levels to the nearest supported
if effort == "xhigh":
# Normalize stronger generic levels to the nearest supported.
if effort in {"xhigh", "max", "ultra"}:
effort = "high"
if effort in supported_efforts:
extra_body["reasoning"] = {"effort": effort}

View file

@ -69,12 +69,12 @@ class DeepSeekProfile(ProviderProfile):
if not enabled:
return extra_body, top_level
# Effort mapping. Pass low/medium/high through; xhigh/max → max.
# Effort mapping. Pass low/medium/high through; stronger levels → max.
# When no effort is set we omit reasoning_effort so DeepSeek applies
# its server default (currently high).
if isinstance(reasoning_config, dict):
effort = (reasoning_config.get("effort") or "").strip().lower()
if effort in {"xhigh", "max"}:
if effort in {"xhigh", "max", "ultra"}:
top_level["reasoning_effort"] = "max"
elif effort in {"low", "medium", "high"}:
top_level["reasoning_effort"] = effort

View file

@ -51,7 +51,7 @@ class OllamaCloudProfile(ProviderProfile):
return {}, {}
if effort == "none":
return {}, {} # explicit none → suppress thinking
if effort in ("xhigh", "max"):
if effort in ("xhigh", "max", "ultra"):
top_level["reasoning_effort"] = "max"
elif effort in ("low", "medium", "high"):
top_level["reasoning_effort"] = effort

View file

@ -73,7 +73,7 @@ class OpenCodeGoProfile(ProviderProfile):
effort = (reasoning_config.get("effort") or "").strip().lower()
if not effort or effort == "none":
return extra_body, top_level
top_level["reasoning_effort"] = "max" if effort in {"xhigh", "max"} else "high"
top_level["reasoning_effort"] = "max" if effort in {"xhigh", "max", "ultra"} else "high"
return extra_body, top_level
if _is_kimi_k2_model(model):
@ -90,7 +90,7 @@ class OpenCodeGoProfile(ProviderProfile):
return extra_body, top_level
effort = (reasoning_config.get("effort") or "").strip().lower()
if effort in {"xhigh", "max"}:
if effort in {"xhigh", "max", "ultra"}:
top_level["reasoning_effort"] = "high"
elif effort in {"low", "medium", "high"}:
top_level["reasoning_effort"] = effort
@ -114,7 +114,7 @@ class OpenCodeGoProfile(ProviderProfile):
if isinstance(reasoning_config, dict):
effort = (reasoning_config.get("effort") or "").strip().lower()
if effort in {"xhigh", "max"}:
if effort in {"xhigh", "max", "ultra"}:
top_level["reasoning_effort"] = "max"
elif effort in {"low", "medium", "high"}:
top_level["reasoning_effort"] = effort

View file

@ -62,7 +62,7 @@ def _is_glm_5_2(model: str | None) -> bool:
def _glm_5_2_reasoning_effort(reasoning_config: dict | None) -> str | None:
"""Map Hermes reasoning effort onto GLM-5.2's native ``high``/``max``.
GLM-5.2 only supports two enabled effort levels. ``xhigh``/``max``
GLM-5.2 only supports two enabled effort levels. ``xhigh``/``max``/``ultra``
request the top tier; everything else that is enabled requests ``high``
(its minimum thinking level). When reasoning is explicitly disabled, or
no effort preference is supplied, the server default is left untouched.
@ -76,7 +76,7 @@ def _glm_5_2_reasoning_effort(reasoning_config: dict | None) -> str | None:
if not effort or effort == "none":
return None
if effort in {"xhigh", "max"}:
if effort in {"xhigh", "max", "ultra"}:
return "max"
# low / medium / minimal / high all clamp to GLM-5.2's minimum: high.
return "high"

View file

@ -4085,7 +4085,7 @@ class DiscordAdapter(BasePlatformAdapter):
await self._run_simple_slash(interaction, f"/model {name}".strip())
@tree.command(name="reasoning", description="Show or change reasoning effort")
@discord.app_commands.describe(effort="Reasoning effort: none, minimal, low, medium, high, or xhigh.")
@discord.app_commands.describe(effort="Effort: none, minimal, low, medium, high, xhigh, max, or ultra.")
async def slash_reasoning(interaction: discord.Interaction, effort: str = ""):
await self._run_simple_slash(interaction, f"/reasoning {effort}".strip())

View file

@ -294,7 +294,7 @@ The registry of record is `hermes_cli/commands.py` — every consumer
/config Show config (CLI)
/model [name] Show or change model
/personality [name] Set personality
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|show|hide)
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|max|ultra|show|hide)
/verbose Cycle: off → new → all → verbose
/voice [on|off|tts] Voice mode
/yolo Toggle approval bypass

View file

@ -1448,6 +1448,16 @@ class TestBuildAnthropicKwargs:
assert kwargs["thinking"] == {"type": "adaptive", "display": "summarized"}
assert kwargs["output_config"] == {"effort": "xhigh"}
def test_reasoning_config_clamps_generic_ultra_to_anthropic_max(self):
kwargs = build_anthropic_kwargs(
model="claude-opus-4.8",
messages=[{"role": "user", "content": "think harder"}],
tools=None,
max_tokens=4096,
reasoning_config={"enabled": True, "effort": "ultra"},
)
assert kwargs["output_config"] == {"effort": "max"}
def test_reasoning_config_maps_max_effort_for_4_7_models(self):
kwargs = build_anthropic_kwargs(
model="claude-opus-4-7",

View file

@ -21,6 +21,23 @@ class TestChatCompletionsBasic:
def test_registered(self, transport):
assert transport is not None
@pytest.mark.parametrize("provider", ["nous", "openrouter"])
def test_gpt56_ultra_uses_max_wire_effort(self, transport, provider):
from providers import get_provider_profile
profile = get_provider_profile(provider)
kw = transport.build_kwargs(
model="openai/gpt-5.6-sol",
messages=[{"role": "user", "content": "Hi"}],
tools=[],
reasoning_config={"enabled": True, "effort": "ultra"},
supports_reasoning=True,
provider_profile=profile,
provider_name=provider,
base_url=profile.base_url,
)
assert kw["extra_body"]["reasoning"] == {"enabled": True, "effort": "max"}
def test_convert_tools_identity(self, transport):
tools = [{"type": "function", "function": {"name": "test", "parameters": {}}}]
assert transport.convert_tools(tools) is tools

View file

@ -75,6 +75,16 @@ class TestCodexBuildKwargs:
)
assert kw.get("reasoning", {}).get("effort") == "high"
@pytest.mark.parametrize("effort, wire_effort", [("max", "max"), ("ultra", "max")])
def test_extended_reasoning_efforts_use_api_wire_value(self, transport, effort, wire_effort):
kw = transport.build_kwargs(
model="gpt-5.6-sol",
messages=[{"role": "user", "content": "Hi"}],
tools=[],
reasoning_config={"enabled": True, "effort": effort},
)
assert kw.get("reasoning", {}).get("effort") == wire_effort
def test_reasoning_disabled(self, transport):
messages = [{"role": "user", "content": "Hi"}]
kw = transport.build_kwargs(
@ -401,6 +411,17 @@ class TestCodexBuildKwargs:
# full history.
assert "reasoning.encrypted_content" in kw.get("include", [])
@pytest.mark.parametrize("effort", ["xhigh", "max", "ultra"])
def test_xai_stronger_generic_efforts_clamp_to_high(self, transport, effort):
kw = transport.build_kwargs(
model="grok-4.3",
messages=[{"role": "user", "content": "Hi"}],
tools=[],
is_xai_responses=True,
reasoning_config={"enabled": True, "effort": effort},
)
assert kw.get("reasoning") == {"effort": "high"}
def test_xai_injects_native_web_search_when_client_web_search_present(self, transport):
"""xAI path swaps a client-side ``web_search`` function for xAI's
native server-side ``web_search`` built-in so grok server-side search

View file

@ -30,7 +30,7 @@ class TestParseReasoningConfig(unittest.TestCase):
self.assertEqual(result, {"enabled": False})
def test_valid_levels(self):
for level in ("low", "medium", "high", "xhigh", "minimal"):
for level in ("low", "medium", "high", "xhigh", "max", "ultra", "minimal"):
result = self._parse(level)
self.assertIsNotNone(result)
self.assertTrue(result.get("enabled"))
@ -41,7 +41,6 @@ class TestParseReasoningConfig(unittest.TestCase):
self.assertIsNone(self._parse(" "))
def test_unknown_returns_none(self):
self.assertIsNone(self._parse("ultra"))
self.assertIsNone(self._parse("turbo"))
def test_case_insensitive(self):

View file

@ -148,6 +148,29 @@ class TestReasoningCommand:
assert runner._reasoning_config == {"enabled": True, "effort": "high"}
assert "session only" in result
@pytest.mark.asyncio
@pytest.mark.parametrize("effort", ["max", "ultra"])
async def test_handle_reasoning_command_accepts_extended_efforts(
self, tmp_path, monkeypatch, effort
):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text(
"agent:\n reasoning_effort: medium\n", encoding="utf-8"
)
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
runner = _make_runner()
event = _make_event(f"/reasoning {effort}")
session_key = runner._session_key_for_source(event.source)
await runner._handle_reasoning_command(event)
assert runner._session_reasoning_overrides[session_key] == {
"enabled": True,
"effort": effort,
}
@pytest.mark.asyncio
async def test_reasoning_global_clears_existing_session_override(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"

View file

@ -75,13 +75,15 @@ class TestCommandRegistry:
def test_reasoning_subcommands_are_in_logical_order(self):
reasoning = next(cmd for cmd in COMMAND_REGISTRY if cmd.name == "reasoning")
assert reasoning.subcommands[:6] == (
assert reasoning.subcommands[:8] == (
"none",
"minimal",
"low",
"medium",
"high",
"xhigh",
"max",
"ultra",
)
def test_cli_only_and_gateway_only_are_mutually_exclusive(self):

View file

@ -106,9 +106,9 @@ class TestOllamaCloudReasoningEffort:
def test_unknown_effort_forwarded(self, ollama_cloud_profile):
_, top_level = ollama_cloud_profile.build_api_kwargs_extras(
reasoning_config={"enabled": True, "effort": "ultra"},
reasoning_config={"enabled": True, "effort": "future-tier"},
)
assert top_level == {"reasoning_effort": "ultra"}
assert top_level == {"reasoning_effort": "future-tier"}
class TestOllamaCloudFullKwargsIntegration:

View file

@ -314,12 +314,12 @@ class TestOpenRouterProfile:
Covers the full real config range produced by
``hermes_constants.parse_reasoning_effort``
``VALID_REASONING_EFFORTS = (minimal, low, medium, high, xhigh)``.
``VALID_REASONING_EFFORTS`` (including max and ultra).
"""
p = get_provider_profile("openrouter")
model = "anthropic/claude-fable-5"
assert self._is_mandatory(model) # fixture really is mandatory
for effort in ("minimal", "low", "medium", "high", "xhigh"):
for effort in ("minimal", "low", "medium", "high", "xhigh", "max", "ultra"):
eb, tl = p.build_api_kwargs_extras(
reasoning_config={"enabled": True, "effort": effort},
supports_reasoning=True,
@ -342,14 +342,12 @@ class TestOpenRouterProfile:
def test_mandatory_anthropic_verbosity_is_value_agnostic_passthrough(self):
"""The mapping passes the effort value through verbatim — it must NOT
clamp or whitelist. ``xhigh`` is a real config value; ``max`` is not
producible by ``parse_reasoning_effort`` today but OpenRouter accepts it
for Claude (live-proven in #43432), so a forward value must survive
clamp or whitelist. Extended values must survive
rather than be silently dropped. The OpenAI SDK type only literals
``low|medium|high`` but it's a TypedDict (no runtime validation), so the
extended scale reaches the wire untouched."""
p = get_provider_profile("openrouter")
for effort in ("xhigh", "max"):
for effort in ("xhigh", "max", "ultra"):
_, tl = p.build_api_kwargs_extras(
reasoning_config={"enabled": True, "effort": effort},
supports_reasoning=True,

View file

@ -483,10 +483,10 @@ class TestParseReasoningEffort:
"""Guard against silently dropping a documented level.
The docstring promises "minimal", "low", "medium", "high", "xhigh",
"max". If someone removes one from VALID_REASONING_EFFORTS without
"max", "ultra". If someone removes one from VALID_REASONING_EFFORTS without
updating the docstring, this test will fail and force the call out.
"""
documented = {"minimal", "low", "medium", "high", "xhigh", "max"}
documented = {"minimal", "low", "medium", "high", "xhigh", "max", "ultra"}
assert documented.issubset(set(VALID_REASONING_EFFORTS))

View file

@ -14,7 +14,7 @@ describe("normalizeEffort", () => {
});
it("passes through every valid effort level", () => {
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh"]) {
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]) {
expect(normalizeEffort(level)).toBe(level);
}
});
@ -26,7 +26,6 @@ describe("normalizeEffort", () => {
it("falls back to medium for unknown values", () => {
expect(normalizeEffort("turbo")).toBe("medium");
expect(normalizeEffort("max")).toBe("medium"); // 'max' is a label, not a value
expect(normalizeEffort(42)).toBe("medium");
});
});
@ -41,7 +40,7 @@ describe("EFFORT_OPTIONS", () => {
it("covers the real reasoning levels plus thinking-off", () => {
// Invariant against hermes_constants.VALID_REASONING_EFFORTS + 'none'.
const values = new Set(EFFORT_OPTIONS.map((o) => o.value));
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh"]) {
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]) {
expect(values.has(level)).toBe(true);
}
});

View file

@ -20,7 +20,9 @@ export const EFFORT_OPTIONS: ReadonlyArray<EffortOption> = [
{ value: "low", label: "Low" },
{ value: "medium", label: "Medium" },
{ value: "high", label: "High" },
{ value: "xhigh", label: "Max" },
{ value: "xhigh", label: "Extra High" },
{ value: "max", label: "Max" },
{ value: "ultra", label: "Ultra" },
];
export const VALID_EFFORTS: ReadonlySet<string> = new Set(

View file

@ -1276,7 +1276,7 @@ Control how much "thinking" the model does before responding:
```yaml
agent:
reasoning_effort: "" # empty = medium (default). Options: none, minimal, low, medium, high, xhigh (max)
reasoning_effort: "" # empty = medium. Options: none, minimal, low, medium, high, xhigh, max, ultra
```
When unset (default), reasoning effort defaults to "medium" — a balanced level that works well for most tasks. Setting a value overrides it — higher reasoning effort gives better results on complex tasks at the cost of more tokens and latency.
@ -1285,10 +1285,9 @@ When unset (default), reasoning effort defaults to "medium" — a balanced level
These models use *adaptive* thinking and don't accept the usual `reasoning.effort`
field — OpenRouter ignores it for them. Hermes transparently routes your
`reasoning_effort` to OpenRouter's `verbosity` parameter instead (which maps to
Anthropic's `output_config.effort`), so the same `low`/`medium`/`high`/`xhigh`
knob keeps working — no extra configuration needed. `none` (or unset) leaves the
model on its own adaptive default. (`max` is accepted on the wire but is not a
selectable `reasoning_effort` value; `xhigh` is the configurable ceiling.) The
Anthropic's `output_config.effort`), so the same effort knob keeps working with
the levels supported by the selected model. `none` (or unset) leaves the model
on its own adaptive default. The
native Anthropic provider already controls effort directly and is unaffected.
:::

View file

@ -83,7 +83,7 @@ Entries can optionally include:
| Parameter | Description |
|-----------|-------------|
| `--reasoning_effort` | Effort level: `none`, `minimal`, `low`, `medium`, `high`, `xhigh` |
| `--reasoning_effort` | Reasoning effort: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `ultra` |
| `--reasoning_disabled` | Completely disable reasoning/thinking tokens |
### Advanced Options

View file

@ -286,7 +286,7 @@ The registry of record is `hermes_cli/commands.py` — every consumer
/config Show config (CLI)
/model [name] Show or change model
/personality [name] Set personality
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|show|hide)
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|max|ultra|show|hide)
/verbose Cycle: off → new → all → verbose
/voice [on|off|tts] Voice mode
/yolo Toggle approval bypass

View file

@ -1044,7 +1044,7 @@ auxiliary:
```yaml
agent:
reasoning_effort: "" # 空 = 中等(默认)。选项none、minimal、low、medium、high、xhigh(最大)
reasoning_effort: "" # 空 = 中等。选项none、minimal、low、medium、high、xhigh、max、ultra
```
未设置时(默认),推理努力程度默认为"medium" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。

View file

@ -83,7 +83,7 @@ python batch_runner.py --list_distributions
| 参数 | 说明 |
|-----------|-------------|
| `--reasoning_effort` | 推理力度:`none``minimal``low``medium``high``xhigh` |
| `--reasoning_effort` | 推理力度:`none``minimal``low``medium``high``xhigh``max``ultra` |
| `--reasoning_disabled` | 完全禁用推理/思考 token |
### 高级选项

View file

@ -275,7 +275,7 @@ hermes uninstall Uninstall Hermes
/config Show config (CLI)
/model [name] Show or change model
/personality [name] Set personality
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|show|hide)
/reasoning [level] Set reasoning (none|minimal|low|medium|high|xhigh|max|ultra|show|hide)
/verbose Cycle: off → new → all → verbose
/voice [on|off|tts] Voice mode
/yolo Toggle approval bypass