feat(gateway): add provider_model, context_full, and reasoning runtime footer fields - #47600
feat(gateway): add provider_model, context_full, and reasoning runtime footer fields#47600Kyzcreig wants to merge 2 commits into
Conversation
8b6dedb to
2073f39
Compare
93f82c1 to
ddfb92d
Compare
WHAT: gateway/run.py now resolves _resolved_provider = getattr(_agent,
"provider", None) and includes "provider" in both _run_agent result
dicts (success path + the early/failure return). gateway/runtime_footer.py
guards _split_provider_model so a model string already carrying a
"provider/" prefix wins over a redundant passed provider (no triple
"a/b/model"). Adds the triple-collapse test case.
WHY: the runtime footer reads agent_result.get("provider"), but the
result dict only ever set "model" (bare, prefix already stripped) and
never "provider" — so provider_model silently degraded to a bare model
name on every live gateway turn (observed in Discord:
"claude-opus-4-8 · …" with no "claude-app/" prefix). Unit tests passed
provider explicitly, so the green suite never caught the missing dict key
— a classic green-in-tests / dead-on-the-gateway gap.
EFFECT: live footer now renders "claude-app/claude-opus-4-8 · …".
Confirmed live on both Apollo and Aegis gateways. 50/50 footer tests green.
This is the same change shipped to fork PR NousResearch#47600 (commit ddfb92dbb,
on a fresh-origin/main base); committing it to live fork/main so the
running gateway's behavior is reproducible and reset-proof rather than a
floating uncommitted working-tree edit.
No new config surface, no env vars, presentation-layer only.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the provider/model and context-footprint expansion. The feature remains useful on current main, but a salvage needs to align it with the current gateway configuration and session state.
Problems
hermes_cli/config.py:1922-1925still defines the default fields asmodel,context_pct, andcwd;hermes_cli/cli_commands_mixin.py:2390uses the same fallback. The diff changes neither, so the claimed new default is inconsistent.- The new reasoning lookup reads config only, but
/reasoning <level>is session-scoped by default (gateway/slash_commands.py:2643-2645,2754) and effective resolution checks_session_reasoning_overridesfirst (gateway/run.py:4835-4852). The footer can therefore report a stale/global value rather than the active session setting. - The changed gateway
/footerpreview fallback omitsreasoning, despite the PR declaring it part of the default set. website/docs/user-guide/configuration.md:1474-1485andwebsite/docs/reference/slash-commands.md:80still document only the original fields; this diff contains no documentation update.
Suggested changes
- Make config defaults, CLI/gateway fallbacks, preview, and docs agree.
- Thread the effective session reasoning configuration into the footer and add a session-override regression test. Open #63812 already targets that resolution path.
Automated hermes-sweeper review.
| @@ -140,10 +224,16 @@ def build_footer_line( | |||
| cfg = resolve_footer_config(user_config, platform_key) | |||
| if not cfg.get("enabled"): | |||
| return "" | |||
| # Reasoning effort comes from config (agent.reasoning_effort); caller may | |||
| # override with a live value if it ever has one. | |||
There was a problem hiding this comment.
This reads only persisted config, but /reasoning <level> is session-scoped by default and stored in _session_reasoning_overrides (gateway/run.py:4835-4852). Thread the active session's resolved reasoning configuration through the footer call so this displays the value actually selected for the turn.
| @@ -2478,7 +2478,7 @@ async def _handle_footer_command(self, event: MessageEvent) -> str: | |||
| model=_resolve_gateway_model(user_config) or None, | |||
There was a problem hiding this comment.
The new fallback omits reasoning, while _DEFAULT_FIELDS in this PR includes it. Use the same shared default contract so /footer on preview does not disagree with the rendered default footer.
…e footer fields Add three optional fields to the opt-in /footer runtime footer: - provider_model: provider/model (the model's own provider/ prefix is authoritative; a separately-supplied provider is ignored to avoid a triple prefix) - context_full: humanized used/window (pct), e.g. 50.2k/1M (5%) - reasoning: model reasoning-effort level as r:<level>, sourced from agent.reasoning_effort Also thread the resolved provider through the _run_agent result dict so the footer can render provider_model (the result previously carried model but not provider, so provider_model degraded to a bare model name). New default field set [provider_model, context_full, reasoning, cwd]; model and context_pct remain selectable. Fields skip silently when data is missing. No new config surface or env vars.
…ion reasoning
Follow-up to the sweeper review on this PR. Four changes:
1. Keep _DEFAULT_FIELDS as ("model", "context_pct", "cwd"). The three new
fields (provider_model, context_full, reasoning) are opt-in via
display.runtime_footer.fields. This resolves the review's inconsistency
finding at its root: hermes_cli/config.py, cli_commands_mixin.py, and the
/footer preview fallback all already say model/context_pct/cwd, and now
agree with the module default — nothing to re-sync. It also means every
existing footer renders byte-identically.
2. Resolve `reasoning` through the shared chokepoint
hermes_constants.resolve_reasoning_config instead of reading
agent.reasoning_effort raw, so per-model overrides
(agent.reasoning_overrides) and the YAML-boolean "disabled" spelling are
honored exactly as the agent honors them.
3. Thread the session-effective reasoning config into the footer:
gateway/run.py now passes _resolve_session_reasoning_config(...) as
build_footer_line(reasoning_config=...), so a session-scoped
`/reasoning <level>` is reported rather than a stale global value.
4. Document the full field table in
website/docs/user-guide/configuration.md and update the /footer row in
website/docs/reference/slash-commands.md.
Tests: byte-stability tests pinning default-config output to the exact
pre-change strings, plus session-override and per-model-override regression
tests. All three behaviors RED-proved by mutation. 61 passed in
tests/gateway/test_runtime_footer.py; 92 passed across the footer/reasoning
blast radius.
ddfb92d to
74346e6
Compare
|
Superseded by #80661, which re-ports this feature onto current This branch had drifted ~2.9k commits behind and hard-conflicted with two upstream changes (the |
What
Adds three new optional fields to the opt-in gateway runtime footer (
/footer), and makes them the default field set:provider_model— rendersprovider/model(e.g.claude-bridge-f3/claude-opus-4-8). When the provider is unset but the model string carries aprovider/modelprefix, it's split so the footer reads cleanly. Bare model with no provider renders just the model (no leading slash).context_full— rendersused/window (pct)(e.g.50.2k/1M (5%)). The used count is the used and window both humanized (50.2k/1M (5%)).reasoning— renders the model's reasoning-effort level asr:<level>(e.g.r:xhigh), sourced fromagent.reasoning_effort(the config key/reasoning <level>writes to). Skipped silently when unset.New default field set:
[provider_model, context_full, reasoning, cwd](was[model, context_pct, cwd]). The originalmodelandcontext_pctfields are unchanged and still selectable.Example footer:
Why
The footer previously showed only the bare model name and a context percentage. Surfacing the provider (useful behind bridges/proxies/failover where the same model is served by different providers), the absolute token occupancy alongside the percentage, and the active reasoning-effort level gives a far more useful at-a-glance runtime readout — at zero cost when disabled (footer is off by default).
How
All in the existing footer machinery — no new config surface, no new core tool, no new env vars:
gateway/runtime_footer.py— addedprovider_model,context_full, andreasoningfield branches toformat_runtime_footer; aproviderand areasoningkwarg threaded throughbuild_footer_line;_split_provider_model,_humanize_tok, and_reasoning_from_confighelpers.format_runtime_footerstays pure (explicit args);build_footer_lineresolvesreasoningfromagent.reasoning_effortin the passeduser_configwhen the caller doesn't supply it. Fields skip silently when their data is missing (no?%/empty-slot artifacts).gateway/run.py— passprovider=agent_result.get("provider")intobuild_footer_line(already present inagent_result). Reasoning needs no call-site change — it's config-sourced.gateway/slash_commands.py— keep the/footertoggle preview's fallback field list in sync with the new default.Config
All behavioral settings stay in
config.yaml— no new env vars.Tests
tests/gateway/test_runtime_footer.py: added per-field tests forprovider_model(explicit / prefix-split / bare),context_full(full / no-window / no-data),reasoning(render / empty-skip / config-resolution / config-absent),_split_provider_model,_humanize_tok, and end-to-end tests reproducing the exact target footer strings. Updated the default-field assertions to the new default set. 49 passed against the upstream base.