Skip to content

feat(state): persist context_length for dashboard cap fallback - #4

Merged
OmarB97 merged 1 commit into
mainfrom
feat/state-db-context-length
May 26, 2026
Merged

feat(state): persist context_length for dashboard cap fallback#4
OmarB97 merged 1 commit into
mainfrom
feat/state-db-context-length

Conversation

@OmarB97

@OmarB97 OmarB97 commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

Persist context_length to state.db on session init. Downstream
dashboards (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:

  1. A static model-metadata table (context_window_hint(model_name))
  2. A live HTTP probe of the loaded backend's /slots / /api/show
    / /v1/models endpoints

For unrecognized models behind unreachable backends, neither answers
and the chip degrades to ctx N / ? with no bar. Hermes knows the
answer authoritatively via self.context_compressor.context_length
— it just wasn't surfaced.

Changes

hermes_state.py:

  • 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 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.

Tests

4 new tests in tests/test_hermes_state.py:

  • write succeeds
  • zero clears (caller signals 'unknown')
  • INSERT-OR-IGNORE creates row if missing
  • declarative reconciliation adds the column on existing DBs

Test run

$ python3 -m pytest tests/test_hermes_state.py
============================= 230 passed in 7.67s ==============================

Compatibility

  • Existing DBs: declarative reconciliation adds the column on next
    Hermes startup. No migration script.
  • create_session signature unchanged. The setter is called as
    a follow-up in _ensure_db_session.
  • Dashboard: a MeshBoard PR consumes the new column via the
    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.

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).
@github-actions

Copy link
Copy Markdown

🔎 Lint report: feat/state-db-context-length vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8386 on HEAD, 8386 on base (➖ 0)

🆕 New issues (3):

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
OmarB97 merged this pull request into main May 26, 2026
12 of 14 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant