mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-24 18:50:15 +00:00
880350312a
* fix(streaming): fallback to model_metadata for context_length when compressor missing (#1318 follow-up)
PR #1318 (shipped in v0.50.246 via PR #1341 + commit a5c10d5) persisted
context_length on the session so the context-ring indicator survives
page reloads. But the writer only fired when agent.context_compressor
was present and reported a non-zero value. Fresh agents, interrupted
streams, or compressors without the attribute would still leave
s.context_length=0 — and the indicator would still show 0% on reload.
This follow-up adds a fallback that calls
agent.model_metadata.get_model_context_length(model, base_url) when the
compressor didn't populate the value. The function returns a sensible
static context window for any known model (with a 256K default for
unknown models). Wrapped in a broad try/except because older
hermes-agent builds may not expose the helper.
Sourced from PR #1344 (@jasonjcwu) — extracted into this focused
follow-up after #1344 was closed as superseded by #1341.
Adds 6 structural tests covering: import + call presence, falsy-gate,
agent.model/base_url passing, exception swallowing, save() ordering,
result assignment.
Closes the data-flow gap in #1318 for the compressor-missing case.
* test: relax pr1341 block-size assertion to accommodate the new fallback
---------
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
105 lines
4.5 KiB
Python
105 lines
4.5 KiB
Python
"""Regression test for #1318 fallback (#1344 follow-up).
|
|
|
|
PR #1318 / #1341 / a5c10d5 (in v0.50.246) persisted context_length to the
|
|
session when agent.context_compressor was present. But for fresh agents or
|
|
interrupted streams, context_compressor may be absent or report 0 — leaving
|
|
the context-ring indicator showing 0% even with the writer in place.
|
|
|
|
This follow-up adds a fallback to agent.model_metadata.get_model_context_length()
|
|
that resolves the model's static context window when the compressor didn't.
|
|
|
|
Sourced from @jasonjcwu's PR #1344, extracted into a focused follow-up.
|
|
|
|
Tests:
|
|
1. Writer block contains the fallback after the compressor block
|
|
2. Fallback gates on s.context_length being 0/falsy
|
|
3. Fallback uses agent.model + agent.base_url
|
|
4. Fallback exception is silently swallowed (older agent builds)
|
|
5. Fallback runs before s.save() so the value is persisted
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
STREAMING = Path(__file__).resolve().parent.parent / "api" / "streaming.py"
|
|
|
|
|
|
def _persistence_block():
|
|
"""Return the source range covering the post-merge per-turn save block."""
|
|
src = STREAMING.read_text(encoding="utf-8")
|
|
start = src.find("if _reasoning_text and s.messages:")
|
|
assert start != -1, "Reasoning trace marker not found in streaming.py"
|
|
end = src.find("\n s.save()", start)
|
|
assert end != -1, "s.save() not found after the reasoning trace marker"
|
|
# Include the s.save() line so we can verify ordering
|
|
end = src.find("\n", end + 1)
|
|
return src[start:end]
|
|
|
|
|
|
def test_fallback_uses_model_metadata():
|
|
"""Block must import and call get_model_context_length on missing compressor data."""
|
|
block = _persistence_block()
|
|
assert "from agent.model_metadata import get_model_context_length" in block, (
|
|
"Fallback must import get_model_context_length from agent.model_metadata"
|
|
)
|
|
assert "get_model_context_length(" in block, (
|
|
"Fallback must call get_model_context_length()"
|
|
)
|
|
|
|
|
|
def test_fallback_gates_on_falsy_context_length():
|
|
"""Fallback runs only when the compressor didn't populate s.context_length.
|
|
|
|
The gate must check s.context_length (not _cc_for_save) — if the compressor
|
|
set context_length but it was 0, we still want the fallback to fire.
|
|
"""
|
|
block = _persistence_block()
|
|
# The conditional must reference s.context_length (or getattr(s, 'context_length', 0))
|
|
assert (
|
|
"if not getattr(s, 'context_length'" in block
|
|
or "if not s.context_length" in block
|
|
), "Fallback must gate on s.context_length being falsy"
|
|
|
|
|
|
def test_fallback_passes_model_and_base_url():
|
|
"""Fallback must source the model and base_url from the agent itself."""
|
|
block = _persistence_block()
|
|
# Must reference both agent.model and agent.base_url in the call
|
|
assert "agent, 'model'" in block, "Fallback must read agent.model"
|
|
assert "agent, 'base_url'" in block, "Fallback must read agent.base_url"
|
|
|
|
|
|
def test_fallback_exception_is_swallowed():
|
|
"""If get_model_context_length raises (older agent build, network error,
|
|
bad provider config), the fallback must not break s.save()."""
|
|
block = _persistence_block()
|
|
# Must wrap the import + call in try/except
|
|
fallback_section = block[block.find("Fallback"):]
|
|
assert "try:" in fallback_section, "Fallback must use try/except"
|
|
# except Exception: pass-style — old agent builds may not have this helper at all
|
|
assert "except Exception:" in fallback_section, (
|
|
"Fallback must catch broad Exception (older agent builds may not have the helper)"
|
|
)
|
|
|
|
|
|
def test_fallback_runs_before_save():
|
|
"""The fallback must mutate s.context_length BEFORE s.save() so the value lands on disk."""
|
|
block = _persistence_block()
|
|
fallback_idx = block.find("get_model_context_length")
|
|
save_idx = block.rfind("s.save()")
|
|
assert fallback_idx != -1 and save_idx != -1
|
|
assert fallback_idx < save_idx, (
|
|
"Fallback must run BEFORE s.save() — otherwise the resolved context_length "
|
|
"is not persisted to the session JSON."
|
|
)
|
|
|
|
|
|
def test_fallback_assigns_context_length_when_resolved():
|
|
"""The fallback must assign s.context_length when get_model_context_length returns a non-zero value."""
|
|
block = _persistence_block()
|
|
fallback_section = block[block.find("Fallback"):]
|
|
# Must have an `if _resolved_cl:` guard followed by `s.context_length = _resolved_cl`
|
|
assert "_resolved_cl" in fallback_section, "Fallback must capture the result"
|
|
assert "s.context_length = _resolved_cl" in fallback_section, (
|
|
"Fallback must assign the resolved value to s.context_length"
|
|
)
|