mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 00:11:39 +08:00
Compare commits
1 Commits
fix/plugin
...
hermes/her
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a26669826 |
38
run_agent.py
38
run_agent.py
@@ -7391,20 +7391,30 @@ class AIAgent:
|
|||||||
response_invalid = True
|
response_invalid = True
|
||||||
error_details.append("response.output is not a list")
|
error_details.append("response.output is not a list")
|
||||||
elif not output_items:
|
elif not output_items:
|
||||||
# If we reach here, _run_codex_stream's backfill
|
# Stream backfill may have failed, but
|
||||||
# from output_item.done events and text-delta
|
# _normalize_codex_response can still recover
|
||||||
# synthesis both failed to populate output.
|
# from response.output_text. Only mark invalid
|
||||||
_resp_status = getattr(response, "status", None)
|
# when that fallback is also absent.
|
||||||
_resp_incomplete = getattr(response, "incomplete_details", None)
|
_out_text = getattr(response, "output_text", None)
|
||||||
logging.warning(
|
_out_text_stripped = _out_text.strip() if isinstance(_out_text, str) else ""
|
||||||
"Codex response.output is empty after stream backfill "
|
if _out_text_stripped:
|
||||||
"(status=%s, incomplete_details=%s, model=%s). %s",
|
logger.debug(
|
||||||
_resp_status, _resp_incomplete,
|
"Codex response.output is empty but output_text is present "
|
||||||
getattr(response, "model", None),
|
"(%d chars); deferring to normalization.",
|
||||||
f"api_mode={self.api_mode} provider={self.provider}",
|
len(_out_text_stripped),
|
||||||
)
|
)
|
||||||
response_invalid = True
|
else:
|
||||||
error_details.append("response.output is empty")
|
_resp_status = getattr(response, "status", None)
|
||||||
|
_resp_incomplete = getattr(response, "incomplete_details", None)
|
||||||
|
logger.warning(
|
||||||
|
"Codex response.output is empty after stream backfill "
|
||||||
|
"(status=%s, incomplete_details=%s, model=%s). %s",
|
||||||
|
_resp_status, _resp_incomplete,
|
||||||
|
getattr(response, "model", None),
|
||||||
|
f"api_mode={self.api_mode} provider={self.provider}",
|
||||||
|
)
|
||||||
|
response_invalid = True
|
||||||
|
error_details.append("response.output is empty")
|
||||||
elif self.api_mode == "anthropic_messages":
|
elif self.api_mode == "anthropic_messages":
|
||||||
content_blocks = getattr(response, "content", None) if response is not None else None
|
content_blocks = getattr(response, "content", None) if response is not None else None
|
||||||
if response is None:
|
if response is None:
|
||||||
|
|||||||
@@ -386,6 +386,56 @@ def test_run_conversation_codex_plain_text(monkeypatch):
|
|||||||
assert result["messages"][-1]["content"] == "OK"
|
assert result["messages"][-1]["content"] == "OK"
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_conversation_codex_empty_output_with_output_text(monkeypatch):
|
||||||
|
"""Regression: empty response.output + valid output_text should succeed,
|
||||||
|
not trigger retry/fallback. The validation stage must defer to
|
||||||
|
_normalize_codex_response which synthesizes output from output_text."""
|
||||||
|
agent = _build_agent(monkeypatch)
|
||||||
|
|
||||||
|
def _empty_output_response(api_kwargs):
|
||||||
|
return SimpleNamespace(
|
||||||
|
output=[],
|
||||||
|
output_text="Hello from Codex",
|
||||||
|
usage=SimpleNamespace(input_tokens=5, output_tokens=3, total_tokens=8),
|
||||||
|
status="completed",
|
||||||
|
model="gpt-5-codex",
|
||||||
|
)
|
||||||
|
|
||||||
|
monkeypatch.setattr(agent, "_interruptible_api_call", _empty_output_response)
|
||||||
|
|
||||||
|
result = agent.run_conversation("Say hello")
|
||||||
|
|
||||||
|
assert result["completed"] is True
|
||||||
|
assert result["final_response"] == "Hello from Codex"
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_conversation_codex_empty_output_no_output_text_retries(monkeypatch):
|
||||||
|
"""When both output and output_text are empty, validation should
|
||||||
|
correctly mark the response as invalid and trigger retry."""
|
||||||
|
agent = _build_agent(monkeypatch)
|
||||||
|
calls = {"api": 0}
|
||||||
|
|
||||||
|
def _fake_api_call(api_kwargs):
|
||||||
|
calls["api"] += 1
|
||||||
|
if calls["api"] == 1:
|
||||||
|
return SimpleNamespace(
|
||||||
|
output=[],
|
||||||
|
output_text=None,
|
||||||
|
usage=SimpleNamespace(input_tokens=5, output_tokens=3, total_tokens=8),
|
||||||
|
status="completed",
|
||||||
|
model="gpt-5-codex",
|
||||||
|
)
|
||||||
|
return _codex_message_response("Recovered")
|
||||||
|
|
||||||
|
monkeypatch.setattr(agent, "_interruptible_api_call", _fake_api_call)
|
||||||
|
|
||||||
|
result = agent.run_conversation("Say hello")
|
||||||
|
|
||||||
|
assert calls["api"] >= 2
|
||||||
|
assert result["completed"] is True
|
||||||
|
assert result["final_response"] == "Recovered"
|
||||||
|
|
||||||
|
|
||||||
def test_run_conversation_codex_refreshes_after_401_and_retries(monkeypatch):
|
def test_run_conversation_codex_refreshes_after_401_and_retries(monkeypatch):
|
||||||
agent = _build_agent(monkeypatch)
|
agent = _build_agent(monkeypatch)
|
||||||
calls = {"api": 0, "refresh": 0}
|
calls = {"api": 0, "refresh": 0}
|
||||||
|
|||||||
Reference in New Issue
Block a user