mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(errors): log when the empty-stub guard and malformed-body classification fire
Add distinct, greppable log lines at both fix sites so the condition is observable in the field instead of only inferable from the absence of the old 'Cannot compress further' spiral: - chat_completion_helpers: warn when an empty partial-stream stub is replaced with the placeholder (0 chars recovered, no tool call). - error_classifier: warn when a malformed-body 400 is classified as format_error rather than context overflow, with num_messages/approx_tokens. Extend the litellm-shape classifier test to assert the warning is emitted.
This commit is contained in:
parent
207a6c969d
commit
4587d77e0e
3 changed files with 28 additions and 5 deletions
|
|
@ -3959,6 +3959,14 @@ def interruptible_streaming_api_call(agent, api_kwargs: dict, *, on_first_delta=
|
|||
# and the continuation prompt still reads as an interrupted turn.
|
||||
if not _partial_text:
|
||||
_partial_text = "[response interrupted]"
|
||||
logger.warning(
|
||||
"Empty partial-stream stub (0 chars recovered, no tool "
|
||||
"call) — substituting placeholder content so the assistant "
|
||||
"message is not persisted empty (would otherwise 400 every "
|
||||
"later request with 'messages must have non-empty content' "
|
||||
"/ INVALID_REQUEST_BODY). error=%s",
|
||||
result["error"],
|
||||
)
|
||||
_stub_msg = SimpleNamespace(
|
||||
role="assistant", content=_partial_text, tool_calls=None,
|
||||
reasoning_content=None,
|
||||
|
|
|
|||
|
|
@ -1326,6 +1326,14 @@ def _classify_400(
|
|||
any(p in error_msg for p in _INVALID_MESSAGE_BODY_PATTERNS)
|
||||
or error_code_lower == "invalid_request_body"
|
||||
):
|
||||
logger.warning(
|
||||
"Malformed message array 400 (invalid request body) classified as "
|
||||
"format_error, NOT context overflow — failing fast + falling back "
|
||||
"instead of entering the compression loop. This usually means an "
|
||||
"empty-content assistant stub is in the transcript; num_messages=%s "
|
||||
"approx_tokens=%s. error=%.200s",
|
||||
num_messages, approx_tokens, error_msg,
|
||||
)
|
||||
return result_fn(
|
||||
FailoverReason.format_error,
|
||||
retryable=False,
|
||||
|
|
|
|||
|
|
@ -1326,7 +1326,7 @@ class TestClassifyApiError:
|
|||
assert result.retryable is False
|
||||
assert result.should_compress is not True
|
||||
|
||||
def test_400_litellm_invalid_request_body_shape(self):
|
||||
def test_400_litellm_invalid_request_body_shape(self, caplog):
|
||||
"""litellm/Bedrock proxy shape (errorMessage/errorCode) → format_error.
|
||||
|
||||
The proxy in front of Anthropic surfaces the empty-content rejection
|
||||
|
|
@ -1335,8 +1335,10 @@ class TestClassifyApiError:
|
|||
are not the standard error.message / message, so err_body_msg used to
|
||||
come back empty → is_generic=True → mis-routed into compression on a
|
||||
large session. Both the message pattern and the errorCode must be
|
||||
recognized.
|
||||
recognized, and a distinct warning must be logged so the condition is
|
||||
observable in the field.
|
||||
"""
|
||||
import logging
|
||||
proxy_msg = ("The provided request body is invalid: claude "
|
||||
"messages.208: all messages must have non-empty content "
|
||||
"except for the optional final assistant message")
|
||||
|
|
@ -1350,12 +1352,17 @@ class TestClassifyApiError:
|
|||
"errorArgs": {"reason": "claude messages.208: ..."},
|
||||
},
|
||||
)
|
||||
result = classify_api_error(
|
||||
e, approx_tokens=66000, context_length=200000, num_messages=219,
|
||||
)
|
||||
with caplog.at_level(logging.WARNING, logger="agent.error_classifier"):
|
||||
result = classify_api_error(
|
||||
e, approx_tokens=66000, context_length=200000, num_messages=219,
|
||||
)
|
||||
assert result.reason == FailoverReason.format_error
|
||||
assert result.retryable is False
|
||||
assert result.should_compress is not True
|
||||
assert any(
|
||||
"Malformed message array 400" in r.getMessage()
|
||||
for r in caplog.records
|
||||
), "Expected a distinct warning identifying the malformed-body 400"
|
||||
|
||||
def test_400_real_context_overflow_still_compresses(self):
|
||||
"""Guard: the new empty-content guard must NOT swallow real overflows.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue