Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion raven/agent/loop/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,12 @@ async def _run_agent_loop(
usage_sink["cost_usd"] = usage_snapshot.estimated_cost_usd
usage_sink["context_max"] = context_max
usage_sink["context_used"] = context_used
usage_sink["context_percent"] = round(100 * context_used / context_max) if context_max else 0
# context_max is a real provider window when resolve_context_window()
# succeeds, but otherwise a guessed default (self.context_window_tokens)
# that can be smaller than the model's true window. Clamp so the gauge
# never reports past full: an unresolved model must not render as
# "over budget" while the request is still well within its real limit.
usage_sink["context_percent"] = min(100, round(100 * context_used / context_max)) if context_max else 0

# Context-window overflow recovery: the structured classifier flags
# should_compress (a smaller window won't help, but eliding the bulk
Expand Down
28 changes: 28 additions & 0 deletions tests/test_agent_loop_usage_sink.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,31 @@ def client_factory(*args, **kwargs):

assert sink["context_max"] == 163840
assert sink["context_used"] == 1500


@pytest.mark.asyncio
async def test_usage_sink_context_percent_clamps_when_window_is_a_guess(workspace):
"""Usage past the configured fallback window must not render past 100%.

``stub`` never resolves via resolve_context_window(), so context_max here is
the configured (guessed) default. Real usage can legitimately exceed that
guess while staying under the model's true, unresolved window, so the
reported percent must clamp rather than imply an impossible over-budget bar.
"""
provider = UsageProvider("stub", prompt_tokens=6000, completion_tokens=2000)
agent = _make_agent(workspace, provider, model="stub", window=1000)
sink: dict = {}

await agent._process_message(
TurnRequest(
origin=Origin.USER,
source=Source(channel="test", chat_id="c1", sender_id="user", chat_type=ChatType.DM),
text="hi",
),
session_key="s1",
usage_sink=sink,
)

assert sink["context_max"] == 1000
assert sink["context_used"] == 8000
assert sink["context_percent"] == 100