mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
fix(agent): exempt parseable vLLM/LM Studio output-cap errors from compression-disabled guard
Salvage of #63862. is_output_cap_error() returns False for vLLM/LM Studio error messages that contain 'prompt contains ... input tokens' (treated as input-overflow signal). But parse_available_output_tokens_from_error() CAN extract a valid available_tokens from those same messages. The compression-disabled guard only checked is_output_cap_error(), so vLLM/LM Studio users with compression off still got a terminal failure instead of the max-tokens retry. Fix: also exempt when parse_available_output_tokens_from_error() returns a value — that function determines whether the retry path can actually handle the error, so it's the right predicate for the exemption. Added test: verify vLLM-format error with compression_disabled=False still triggers the max-tokens retry path. Co-authored-by: dmabry <dmabry@users.noreply.github.com>
This commit is contained in:
parent
af4006000f
commit
2ccfdb2db4
3 changed files with 55 additions and 1 deletions
|
|
@ -3071,7 +3071,10 @@ def run_conversation(
|
|||
FailoverReason.payload_too_large,
|
||||
FailoverReason.context_overflow,
|
||||
}
|
||||
_is_output_cap_error = is_output_cap_error(error_msg)
|
||||
_is_output_cap_error = (
|
||||
is_output_cap_error(error_msg)
|
||||
or parse_available_output_tokens_from_error(error_msg) is not None
|
||||
)
|
||||
if (
|
||||
classified.reason in _overflow_reasons
|
||||
and not getattr(agent, "compression_enabled", True)
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ AUTHOR_MAP = {
|
|||
"259353979+testingbuddies24@users.noreply.github.com": "testingbuddies24", # PR #43192 salvage (strip orphan think-tag close tags in progressive gateway stream so a bare </think> whose open was dropped upstream can't leak to the user)
|
||||
"shx_929@163.com": "Lazymonter", # PR #42914 salvage (retry launchd bootstrap after bootout on EIO for install/start instead of degrading to detached)
|
||||
"96322396+WXBR@users.noreply.github.com": "WXBR", # PR #46183 salvage (persist recovered final_response at the finalize_turn chokepoint so recovery-path breaks don't drop the delivered assistant row)
|
||||
"dmabry@sparky.fabe-gray.ts.net": "dmabry", # PR #63862 salvage (output-cap retry: use provider available_tokens + request estimate; exempt parseable vLLM/LM Studio errors from compression-disabled guard)
|
||||
"sahil.rakhaiya117814@marwadiuniversity.ac.in": "SahilRakhaiya05", # PR #44073 salvage (fail-closed gateway/external-surface hardening: own-policy defaults, open-policy startup guard, profile-aware multiplex authz, API-server auth, execute_code per-session RPC token)
|
||||
"5848605+itenev@users.noreply.github.com": "itenev", # PR #22753 salvage (asyncify model-context resolution in gateway message path so blocking requests.get can't starve Discord heartbeats)
|
||||
"arthur.zhang@ingenico.com": "arthurzhang", # PR #34718 salvage (redact Slack App-Level xapp- tokens in agent/redact.py + gateway/run.py)
|
||||
|
|
|
|||
|
|
@ -5580,6 +5580,56 @@ class TestRunConversation:
|
|||
assert agent.context_compressor.context_length == 200_000
|
||||
mock_compress.assert_not_called()
|
||||
|
||||
def test_output_cap_retry_with_compression_disabled_vllm_format(self, agent):
|
||||
"""vLLM/LM Studio error messages contain 'prompt contains ... input
|
||||
tokens' which is_output_cap_error() treats as an input-overflow signal
|
||||
(returns False). But parse_available_output_tokens_from_error() CAN
|
||||
extract a valid available_tokens from them. The compression-disabled
|
||||
guard must exempt these too — otherwise users on vLLM/LM Studio with
|
||||
compression off get a terminal failure instead of a max-tokens retry.
|
||||
"""
|
||||
self._setup_agent(agent)
|
||||
agent.api_mode = "chat_completions"
|
||||
agent.provider = "openrouter"
|
||||
agent.model = "some/model"
|
||||
agent.max_tokens = 65_536
|
||||
agent.compression_enabled = False
|
||||
agent.context_compressor.context_length = 131_072
|
||||
agent.context_compressor.should_compress = MagicMock(return_value=False)
|
||||
|
||||
# vLLM-format error (from tests/test_output_cap_parsing.py)
|
||||
error_msg = (
|
||||
"This model's maximum context length is 131072 tokens. "
|
||||
"However, you requested 1024 output tokens and your prompt "
|
||||
"contains at least 65537 input tokens, for a total of at least "
|
||||
"66561 tokens."
|
||||
)
|
||||
exc = Exception(error_msg)
|
||||
exc.status_code = 400
|
||||
exc.code = 400
|
||||
|
||||
ok_resp = _mock_response(content="done", finish_reason="stop")
|
||||
agent.client.chat.completions.create.side_effect = [exc, ok_resp]
|
||||
|
||||
mock_compress = MagicMock()
|
||||
with (
|
||||
patch.object(agent, "_persist_session"),
|
||||
patch.object(agent, "_save_trajectory"),
|
||||
patch.object(agent, "_cleanup_task_resources"),
|
||||
patch.object(agent.context_compressor, "update_model"),
|
||||
patch.object(agent, "_compress_context", mock_compress),
|
||||
):
|
||||
result = agent.run_conversation("hello")
|
||||
|
||||
assert len(agent.client.chat.completions.create.call_args_list) == 2
|
||||
second_call = agent.client.chat.completions.create.call_args_list[1].kwargs
|
||||
assert result["completed"] is True
|
||||
assert result.get("compaction_disabled") is None
|
||||
# parse_available_output_tokens_from_error returns 65535 for this message
|
||||
assert second_call["max_tokens"] <= 65471 # 65535 - 64
|
||||
assert agent.context_compressor.context_length == 131_072
|
||||
mock_compress.assert_not_called()
|
||||
|
||||
|
||||
class TestHookPayloadSanitizesSimpleNamespace:
|
||||
"""Regression: ``_hook_jsonable`` referenced ``SimpleNamespace`` without
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue