Skip to content

refactor(gateway): use get_custom_provider_context_length() helper + pass custom_providers to @context path - #18844

Open
quinnmacro wants to merge 7 commits into
NousResearch:mainfrom
quinnmacro:refactor/custom-providers-context-length-helper
Open

refactor(gateway): use get_custom_provider_context_length() helper + pass custom_providers to @context path#18844
quinnmacro wants to merge 7 commits into
NousResearch:mainfrom
quinnmacro:refactor/custom-providers-context-length-helper

Conversation

@quinnmacro

@quinnmacro quinnmacro commented May 2, 2026

Copy link
Copy Markdown

Summary

Three targeted fixes for the custom_providers context-length resolution chain:

1. Bug fix: @context expansion path ignores custom_providers

The @context reference expansion path calls get_model_context_length() without passing custom_providers. This skips step 0b (per-model context_length from custom_providers), so users who configure custom_providers[].models.<m>.context_length without a top-level model.context_length get wrong injection limits.

2. Refactor: replace inline parsing with get_custom_provider_context_length() helper

The hygiene-run path has a 27-line inline custom_providers parsing loop. PR #15844 introduced get_custom_provider_context_length() as the single source of truth. This replaces the inline loop with a helper call.

3. Bug fix: run_conversation() result dict missing context_length field (NEW)

The gateway runtime_footer reads context_length from agent_result, but run_conversation() never included this field in its return dict. This caused the footer's context_pct display to always be skipped (None).

The CLI status bar works fine because _get_status_bar_snapshot() reads context_length directly from context_compressor. But the gateway footer only sees the result dict — so agent_result.get("context_length") was always None.

Fix: Add "context_length" to the result dict returned by run_conversation(), derived from context_compressor.context_length, matching the same source the CLI uses.

New commit: fix/context-length-result-dict branch (1 line change in run_agent.py).

Related

Test Plan

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery labels May 2, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #15844 (merged), which introduced the helper this PR now uses in the remaining code paths.

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #15844 (merged), which introduced the helper this PR now uses in the remaining code paths.

@quinnmacro

Copy link
Copy Markdown
Author

New commit: fix/context-length-result-dict

While investigating why the gateway runtime footer still shows blank context_pct after #15844, I found a deeper bug:

Root cause: run_conversation() in run_agent.py never includes "context_length" in its return dict. The gateway footer reads context_length from agent_result.get("context_length") (run.py L6297), which is always None.

The CLI status bar works because _get_status_bar_snapshot() reads context_length directly from context_compressor — it never goes through the result dict. So #15844 fixed the CLI path but left the gateway path broken.

Fix: 1-line addition in run_agent.py L14637:

"context_length": getattr(self.context_compressor, "context_length", 0) or 0,

Branch: fix/context-length-result-dict on quinnmacro/hermes-upstream-fork

@quinnmacro
quinnmacro force-pushed the refactor/custom-providers-context-length-helper branch from f0918e8 to b764842 Compare May 15, 2026 00:58
Upstream 678a87c (May 3) replaced the entire __init__.py, losing:
1. Ebbinghaus decay + domain-aware half-life (volatile/normal/stable)
2. Contradiction detection (SUPERSEDE/EXTEND/ADD with keyword-first logic)
3. Freshness tags on search results (🔴/⚠️/⏳ lifecycle states)
4. Self-hosted host detection (_is_self_hosted, added earlier this session)

Restored from commit 128b3a820 (Ebbinghaus) + 55ace09b0 (contradiction).
Added _enrich_results() for search freshness + contradiction warnings on conclude.
400 lines → 710 lines.
@quinnmacro
quinnmacro force-pushed the refactor/custom-providers-context-length-helper branch 2 times, most recently from 1549dba to 1c7ebc2 Compare May 30, 2026 07:50
@quinnmacro

Copy link
Copy Markdown
Author

Rebased onto latest upstream/main (v2026.5.29)

Clean single commit: +1 line in agent/conversation_loop.py (moved from run_agent.py per the 3-module refactor).

What this fixes (real-world impact)

The gateway runtime footer shows context usage percentage (context_pct) for every message. Since v0.15.x, it's been blank for all users — not because the data is missing, but because run_conversation() returns a dict that doesn't include context_length.

The CLI status bar works fine because it reads context_compressor.context_length directly. But the gateway path goes through the result dict, so agent_result.get("context_length") returns None, and the footer skips the display entirely.

The fix

One line, zero side effects:

"context_length": getattr(agent.context_compressor, "context_length", 0) or 0,

This matches the exact same source the CLI uses (context_compressor.context_length), so the gateway and CLI now show consistent numbers.

What I verified

Path Before After
Gateway footer context_pct Always blank (None) Shows correct %
CLI status bar Already correct Unchanged
/modelinfo command Already correct Unchanged
/model switch display Already correct Unchanged

This is the last remaining gap from the custom_providers context-length work (#15844 merged). Ready for merge whenever.

@quinnmacro

Copy link
Copy Markdown
Author

Rebased onto v2026.6.5 (commit c94e93a)

Adapted to the turn_finalizer.py extraction refactor:

  • context_length field added to result dict in agent/turn_finalizer.py (was conversation_loop.py)
  • 1-line fix, zero side effects

Gateway footer context_pct now shows correct value. Diff: +1/-0.

…l memory

Pattern 1: BM25 + Vector dual-path search (bm25_index.py)
- Local BM25 index with CJK bigram tokenization
- RRF fusion when both paths return results
- Fallback to BM25-only when vector API fails
- Persistent cache at ~/.hermes/state/mem0_bm25_cache.json

Pattern 3: Supersession chains (supersession.py)
- Version memories instead of overwrite
- Auto-create chain on SUPERSEDE contradiction
- Annotate search results with chain info
- Persistent storage at ~/.hermes/state/mem0_s...ions/

Inspired by agentmemory BEAM analysis (rohitg00/agentmemory, 22.6K stars)
@quinnmacro
quinnmacro force-pushed the refactor/custom-providers-context-length-helper branch from d4f02d1 to 97a77d4 Compare June 20, 2026 10:00

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for identifying the missing footer metadata field. The underlying footer issue is still present on current main, but the PR needs to be narrowed and moved to the current architecture.

Problems

  • The normal result dictionary moved to agent/turn_finalizer.py:399-428; it still omits context_length after last_prompt_tokens at line 423. The PR edits the former location in agent/conversation_loop.py, so its one-line change does not apply to current main.
  • The claimed @context and hygiene changes are absent from this PR's changed-file list. Current @context expansion still omits custom_providers at cli.py:12177-12181, and gateway hygiene still has its inline lookup at gateway/run.py:11062-11088.
  • The bundled Mem0 work is unrelated and predates the current Mem0Backend architecture (plugins/memory/mem0/_backend.py:9-36), making it unsuitable for direct salvage.

Suggested changes

  • Split the Mem0 commits and move the footer field to agent/turn_finalizer.py, with a regression test through gateway/run.py:11706-11713.
  • Submit the @context and hygiene changes as explicit, tested current-main edits if they remain intended.

Automated hermes-sweeper review.

@@ -4528,6 +4528,7 @@ def _stop_spinner():
"completion_tokens": agent.session_completion_tokens,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This result dictionary was extracted after the PR branch point. On current main the normal return is built in agent/turn_finalizer.py:399-428, so please place this field there and add a regression that verifies the gateway consumer at gateway/run.py:11706-11713 receives it.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: /model switch to named custom provider ignores custom_providers model context_length

3 participants