mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(agent): flatten multi-part user_message in codex intermediate-ack detector
Vision requests routed through the OpenAI-compat API server forward the
raw multi-part content list ([{type:"text"}, {type:"image_url"}, ...])
straight through as user_message. The codex intermediate-ack detector
flattened it with (user_message or "").strip(), so a truthy list survived
and .strip() raised AttributeError — killing any Codex-routed vision turn
that took the require_workspace path.
Route through the existing _summarize_user_message_for_log helper (which
already backs the logging/banner previews on main), and widen the param
type hint from str to Any to match how the function is actually called.
The two logging-preview sites the original PR also touched were fixed
independently on main by the conversation-loop refactor.
Co-authored-by: Hermes Agent <agent@nousresearch.com>
This commit is contained in:
parent
cd9f5cc671
commit
b8ebe32866
3 changed files with 33 additions and 2 deletions
|
|
@ -2206,7 +2206,7 @@ def sanitize_api_messages(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]
|
|||
|
||||
def looks_like_codex_intermediate_ack(
|
||||
agent,
|
||||
user_message: str,
|
||||
user_message: Any,
|
||||
assistant_content: str,
|
||||
messages: List[Dict[str, Any]],
|
||||
require_workspace: bool = True,
|
||||
|
|
@ -2286,7 +2286,14 @@ def looks_like_codex_intermediate_ack(
|
|||
if not require_workspace:
|
||||
return True
|
||||
|
||||
user_text = (user_message or "").strip().lower()
|
||||
# ``user_message`` is typed ``str`` but can arrive as an OpenAI-style
|
||||
# multi-part content list (``[{type:"text",...}, {type:"image_url",...}]``)
|
||||
# for vision requests routed through the OpenAI-compat API server. A
|
||||
# truthy list survives ``(user_message or "")`` and then ``.strip()``
|
||||
# raises ``AttributeError`` — flatten to text first.
|
||||
from agent.codex_responses_adapter import _summarize_user_message_for_log
|
||||
|
||||
user_text = _summarize_user_message_for_log(user_message).strip().lower()
|
||||
user_targets_workspace = (
|
||||
any(marker in user_text for marker in workspace_markers)
|
||||
or "~/" in user_text
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ AUTHOR_MAP = {
|
|||
"tenoryang@outlook.com": "MarioYounger", # PR #9028 salvage (bash/sh heredoc approval, NFKC homograph fold, execute_code CREDS/BEARER/APIKEY env filter)
|
||||
"peet.wannasarnmetha@gmail.com": "peetwan", # PR #51841 salvage (loopback ws-ping tuning + token-frame coalescing + loop heartbeat; #48445/#50005)
|
||||
"297292863+Zyxxx-xxxyZ@users.noreply.github.com": "Zyxxx-xxxyZ", # PR #54287 salvage (route frontend-polled inline RPCs to _LONG_HANDLERS; #48445/#50005)
|
||||
"kevenyanisme@gmail.com": "DataAdvisory", # PR #9562 salvage (flatten multi-part user_message in codex intermediate-ack detector so vision turns don't crash)
|
||||
"telos@apex-z.com": "telos-oc", # PR #14353 salvage (propagate custom_providers key_env into ProviderDef.api_key_env_vars; named + bare-custom self-heal paths)
|
||||
"256073454+Kolektori@users.noreply.github.com": "Kolektori", # PR #6436 salvage (require approval for host-bound Docker commands; container guard fast-path)
|
||||
"41764686+LIC99@users.noreply.github.com": "LIC99", # PR #4682 salvage (warn + default to manual on unknown approvals.mode; #4261)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,29 @@ def test_codex_only_path_requires_workspace():
|
|||
)
|
||||
|
||||
|
||||
def test_multipart_user_message_does_not_crash_on_workspace_path():
|
||||
"""#9562: vision requests forward ``user_message`` as a multi-part list.
|
||||
|
||||
The OpenAI-compat API server passes the raw ``content`` field straight
|
||||
through for vision turns, so ``user_message`` reaches the detector as
|
||||
``[{type:"text",...}, {type:"image_url",...}]``. The ``require_workspace``
|
||||
path flattened it with ``(user_message or "").strip()`` — a truthy list
|
||||
survived and ``.strip()`` raised ``AttributeError``, killing the turn.
|
||||
The text part still has to drive workspace detection.
|
||||
"""
|
||||
a = _agent("auto", "codex_responses")
|
||||
multipart = [
|
||||
{"type": "text", "text": CODE_USER},
|
||||
{"type": "image_url", "image_url": {"url": "data:image/png;base64,AAAA"}},
|
||||
]
|
||||
msgs = [{"role": "user", "content": multipart}]
|
||||
# No crash, and the text part ("review the codebase in /app") still
|
||||
# satisfies the workspace requirement so the ack fires.
|
||||
assert looks_like_codex_intermediate_ack(
|
||||
a, multipart, CODE_ACK, msgs, require_workspace=True
|
||||
)
|
||||
|
||||
|
||||
def test_all_path_drops_workspace_requirement():
|
||||
"""The #27881 fix: opted-in turns catch non-codebase intent acks."""
|
||||
a = _agent(True, "chat_completions")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue