mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-24 16:54:43 +00:00
fix(agent): preserve Gemini thought_signature in MoA aggregator mode
When MoA mode is active with a Gemini model as the aggregator, agent.model holds the virtual preset name (e.g. "closed"), not the actual aggregator model name. The _sanitize_tool_calls_for_strict_api call uses agent.model to decide whether to keep extra_content (thought_signature) on tool_calls — since "closed" doesn't contain "gemini", the thought_signature is stripped and the Gemini aggregator rejects the next request with HTTP 400 (INVALID_ARGUMENT): "Function call is missing a thought_signature in functionCall parts." Fix: resolve the actual aggregator model name from moa_config (conversation_loop) or last_aggregator_slot (chat_completion_helpers) and pass it to _sanitize_tool_calls_for_strict_api so the _model_consumes_thought_signature check sees the real Gemini model. Closes #65092
This commit is contained in:
parent
d0d116be2e
commit
f65d105cbb
2 changed files with 21 additions and 2 deletions
|
|
@ -1946,7 +1946,17 @@ def handle_max_iterations(agent, messages: list, api_call_count: int) -> str:
|
|||
for internal_key in [k for k in api_msg if isinstance(k, str) and k.startswith("_")]:
|
||||
api_msg.pop(internal_key, None)
|
||||
if _needs_sanitize:
|
||||
agent._sanitize_tool_calls_for_strict_api(api_msg, model=agent.model)
|
||||
# In MoA mode, agent.model is the virtual preset name,
|
||||
# not the actual aggregator model. Resolve the real
|
||||
# aggregator model so Gemini preserves thought_signature.
|
||||
_sanitize_model = agent.model
|
||||
if agent.provider == "moa":
|
||||
_moa_client = getattr(agent, "client", None)
|
||||
if _moa_client is not None:
|
||||
_agg_slot = getattr(_moa_client, "last_aggregator_slot", None)
|
||||
if _agg_slot and _agg_slot.get("model"):
|
||||
_sanitize_model = _agg_slot["model"]
|
||||
agent._sanitize_tool_calls_for_strict_api(api_msg, model=_sanitize_model)
|
||||
api_messages.append(api_msg)
|
||||
|
||||
effective_system = agent._cached_system_prompt or ""
|
||||
|
|
|
|||
|
|
@ -1103,7 +1103,16 @@ def run_conversation(
|
|||
# Uses new dicts so the internal messages list retains the fields
|
||||
# for Codex Responses compatibility.
|
||||
if agent._should_sanitize_tool_calls():
|
||||
agent._sanitize_tool_calls_for_strict_api(api_msg, model=agent.model)
|
||||
# In MoA mode, agent.model is the virtual preset name
|
||||
# (e.g. "closed"), not the actual aggregator model. Use
|
||||
# the resolved aggregator model so Gemini aggregators
|
||||
# correctly preserve thought_signature (extra_content).
|
||||
_sanitize_model = agent.model
|
||||
if agent.provider == "moa" and moa_config:
|
||||
_agg = moa_config.get("aggregator") or {}
|
||||
if _agg.get("model"):
|
||||
_sanitize_model = _agg["model"]
|
||||
agent._sanitize_tool_calls_for_strict_api(api_msg, model=_sanitize_model)
|
||||
# Keep 'reasoning_details' - OpenRouter uses this for multi-turn reasoning context
|
||||
# The signature field helps maintain reasoning continuity
|
||||
api_messages.append(api_msg)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue