feat(state): persist context_length for dashboard cap fallback - #4
Merged
Conversation
Downstream dashboards (notably MeshBoard's context chip) compute the context-window cap via a static model-metadata table OR a live HTTP probe of the model's backend. For unrecognized models on unreachable backends, neither answers — the chip degrades to "ctx N / ?" with no bar, costing the operator the most useful piece of context-window context. Hermes already knows the answer authoritatively via ``self.context_compressor.context_length``. Persist it once per session so the dashboard has a guaranteed cap. Changes: - SCHEMA_SQL: new ``context_length INTEGER DEFAULT 0`` column on the ``sessions`` table. Declarative reconciliation picks it up. - New ``set_context_length(session_id, length)`` method — simple SET with the standard INSERT-OR-IGNORE row guard. - run_agent.py:_ensure_db_session: after create_session succeeds, snapshot the model's context_length. Constant for the session lifetime; written once. Best-effort try/except — display hint, not load-bearing. Tests (+4 new): - write succeeds - zero clears (caller signals 'unknown') - lazy setter INSERT-OR-IGNORE pattern - declarative reconciliation adds the column on existing DBs Test run: 230 passed (was 226 pre-this-PR; +4 new).
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
First entries
run_agent.py:7496: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
run_agent.py:13782: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
run_agent.py:13785: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown, Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
✅ Fixed issues (3):
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
First entries
run_agent.py:7480: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
run_agent.py:13769: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
run_agent.py:13766: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
Unchanged: 4421 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
OmarB97
pushed a commit
that referenced
this pull request
Jun 11, 2026
…eSessionPage (NousResearch#43487) When auto-compression rotates the session tip (old #4 → new #5), the incoming page carries the new tip but the previous list still holds the old one. The old tip's id differs from the new tip's id, so the existing id-only dedup in mergeSessionPage() preserves both as separate sidebar rows. Add lineage-level dedup: build a set of incoming lineage keys (`_lineage_root_id ?? id`) and filter survivors whose lineage key matches any incoming row. This mirrors the existing sessionPinId() logic used for pin stability. Fixes NousResearch#43483
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Persist
context_lengthto state.db on session init. Downstreamdashboards (notably MeshBoard's context chip) can render a
definitive context-window cap even when their static
model-metadata tables and live HTTP probes can't answer for an
unrecognized backend.
Motivation
The chip computes its cap via:
context_window_hint(model_name))/slots//api/show/
/v1/modelsendpointsFor unrecognized models behind unreachable backends, neither answers
and the chip degrades to
ctx N / ?with no bar. Hermes knows theanswer authoritatively via
self.context_compressor.context_length— it just wasn't surfaced.
Changes
hermes_state.py:context_length INTEGER DEFAULT 0column on thesessionstable. Declarative reconciliation picks it up.set_context_length(session_id, length)method — simple SETwith INSERT-OR-IGNORE row guard.
run_agent.py:_ensure_db_session: aftercreate_sessionsucceeds, snapshotthe model's
context_length. Constant for the session lifetime;written once. Best-effort try/except.
Tests
4 new tests in
tests/test_hermes_state.py:Test run
Compatibility
Hermes startup. No migration script.
create_sessionsignature unchanged. The setter is called asa follow-up in
_ensure_db_session.existing context_window resolution chain. No coordination
required — when present, the column wins; when missing
(older Hermes), the chip falls back to its existing chain.