Skip to content

fix(adk-middleware): emit REASONING_ENCRYPTED_VALUE for function_call thought signatures#2073

Open
ferponse wants to merge 6 commits into
ag-ui-protocol:mainfrom
ferponse:fix/adk-tool-call-thought-signatures
Open

fix(adk-middleware): emit REASONING_ENCRYPTED_VALUE for function_call thought signatures#2073
ferponse wants to merge 6 commits into
ag-ui-protocol:mainfrom
ferponse:fix/adk-tool-call-thought-signatures

Conversation

@ferponse

@ferponse ferponse commented Jul 1, 2026

Copy link
Copy Markdown

What & why

Fixes #2072.

The ADK middleware never emits REASONING_ENCRYPTED_VALUE, even with Gemini thinking enabled, because it only reads thought_signature from thought-text parts. In practice Gemini attaches the signature to the function_call part (the encrypted chain-of-thought that led to the tool call), so the encrypted reasoning was silently dropped.

Verified against Gemini 2.5 Flash on Vertex AI (ag-ui-adk 0.7.0 / google-adk 2.3.0), inspecting every part of a run with two tool calls:

part kind=text          thought=True  thought_signature=None
part kind=function_call thought=None  thought_signature=446 bytes   ← signature HERE
part kind=function_call thought=None  thought_signature=336 bytes
part kind=text          thought=None  thought_signature=385 bytes

REASONING_ENCRYPTED_VALUE count across 3 runs before this change: 0.

Change

  • Add EventTranslator._translate_function_call_signatures(): reads thought_signature from function_call parts and emits ReasoningEncryptedValueEvent(subtype="tool-call", entity_id=<toolCallId>, encrypted_value=base64(sig)), mirroring the existing subtype="message" path for thought-text parts.
  • Called from the non-partial function-call branch in translate(), covering both LRO and non-LRO calls in the event.
  • Deduplicated per tool call id (_emitted_signature_tool_call_ids) so partial/non-partial replays of the same call don't emit it twice.
  • Guards: only bytes-like signatures and string tool-call ids are emitted (robust against non-signature parts).

The AG-UI spec already defines ReasoningEncryptedValueEvent with subtype: "tool-call" for exactly this case.

Tests

  • Two focused tests added to test_event_translator_comprehensive.py:
    • a function_call part with a thought_signature → one REASONING_ENCRYPTED_VALUE with subtype="tool-call", correct entity_id and base64 payload;
    • a function_call part without a signature → none emitted.
  • Full middleware suite (integrations/adk-middleware/python): 920 passed, 0 failed.

… thought signatures

Gemini attaches thought_signature (the encrypted chain-of-thought that led to
a tool call) to the function_call part rather than the thought-text part. The
reasoning path in _translate_reasoning_content only reads signatures from
thought-text parts, so REASONING_ENCRYPTED_VALUE was never emitted for tool
calls and the encrypted reasoning was silently dropped.

Add _translate_function_call_signatures() to read thought_signature from
function_call parts and emit ReasoningEncryptedValueEvent(subtype="tool-call",
entity_id=<toolCallId>), mirroring the existing subtype="message" emission for
thought-text parts. Deduplicated per tool call id so partial/non-partial replays
don't emit it twice.

Adds two focused tests covering the tool-call signature and no-signature cases.

Fixes ag-ui-protocol#2072
@ferponse
ferponse requested a review from a team as a code owner July 1, 2026 17:16
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the CopilotKit Team on Vercel.

A member of the Team first needs to authorize it.

@ferponse

Copy link
Copy Markdown
Author

@contextablemark could you sanity-check this fix — emitting REASONING_ENCRYPTED_VALUE for thought signatures that live on function_call parts (issue #2072)? Want to confirm it matches your understanding.

Without this, the set grew unbounded across sequential runs sharing one
translator, and after reset() a reused tool-call id could have its
REASONING_ENCRYPTED_VALUE emission wrongly suppressed.

Addresses review comment from contextablemark on PR ag-ui-protocol#2073.

Co-authored-by: Atenea Agent <svc_atenea_gitlab@ofidona.net>
@ferponse

Copy link
Copy Markdown
Author

Thanks @contextablemark! Added self._emitted_signature_tool_call_ids.clear() to reset() right after _predictive_state_tool_call_ids.clear(), as suggested. Pushed in ff892cb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[adk-middleware] REASONING_ENCRYPTED_VALUE never emitted: thought signatures are on function_call parts, not thought parts

2 participants