mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-16 14:32:34 +00:00
fix(codex): drop oversized message ids on Responses input replay
Codex assigns assistant message items server-side ids that can run 400+ chars (base64 encrypted blobs), but the Responses API caps input[].id at 64 chars and rejects the whole request with a non-retryable HTTP 400. Once a session captures one of these long ids, every subsequent turn replays it and 400s forever, since the history persists it in codex_message_items. Add a 64-char length guard at both replay sites — the history-to- input converter and the final preflight gate — so oversized ids are dropped while short ids (msg_...) are kept for prefix-cache hits. Mirrors the existing pattern for reasoning items, which already strip their id before replay because store=False means the API can't resolve ids server-side anyway. Fixes #27038 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
dabae386ef
commit
0b2907f586
2 changed files with 137 additions and 2 deletions
|
|
@ -288,6 +288,13 @@ _RESPONSES_BUILTIN_TOOL_TYPES = {
|
|||
|
||||
_RESPONSE_MESSAGE_STATUSES = {"completed", "incomplete", "in_progress"}
|
||||
|
||||
# The Responses API rejects input[].id longer than this with a non-retryable
|
||||
# HTTP 400 ("string too long"). Codex-issued assistant message ids are
|
||||
# server-assigned base64 blobs that can run 400+ chars, while Hermes-minted
|
||||
# ids (msg_...) stay well under this cap and are worth keeping for
|
||||
# prefix-cache hits. Drop only the oversized ones on replay.
|
||||
_MAX_RESPONSES_ITEM_ID_LENGTH = 64
|
||||
|
||||
|
||||
def _normalize_responses_message_status(value: Any, *, default: str = "completed") -> str:
|
||||
"""Normalize a Responses assistant message status for replay.
|
||||
|
|
@ -464,7 +471,9 @@ def _chat_messages_to_responses_input(
|
|||
}
|
||||
item_id = raw_item.get("id")
|
||||
if isinstance(item_id, str) and item_id.strip():
|
||||
replay_item["id"] = item_id.strip()
|
||||
stripped_id = item_id.strip()
|
||||
if len(stripped_id) <= _MAX_RESPONSES_ITEM_ID_LENGTH:
|
||||
replay_item["id"] = stripped_id
|
||||
phase = raw_item.get("phase")
|
||||
if isinstance(phase, str) and phase.strip():
|
||||
replay_item["phase"] = phase.strip()
|
||||
|
|
@ -718,7 +727,9 @@ def _preflight_codex_input_items(raw_items: Any) -> List[Dict[str, Any]]:
|
|||
}
|
||||
item_id = item.get("id")
|
||||
if isinstance(item_id, str) and item_id.strip():
|
||||
normalized_item["id"] = item_id.strip()
|
||||
stripped_id = item_id.strip()
|
||||
if len(stripped_id) <= _MAX_RESPONSES_ITEM_ID_LENGTH:
|
||||
normalized_item["id"] = stripped_id
|
||||
phase = item.get("phase")
|
||||
if isinstance(phase, str) and phase.strip():
|
||||
normalized_item["phase"] = phase.strip()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue