Skip to content

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

Description

@ferponse

Summary

The ADK middleware never emits REASONING_ENCRYPTED_VALUE, even with Gemini thinking enabled, because it only reads thought_signature from thought-text parts. Gemini attaches the signature to function_call parts (and to the final answer part), so REASONING_ENCRYPTED_VALUE is never produced and the encrypted reasoning is silently dropped.

Environment

  • ag-ui-adk 0.7.0 (bug also present on main)
  • google-adk 2.3.0, google-genai (current)
  • Gemini 2.5 Flash via Vertex AI
  • Agent: LlmAgent(planner=BuiltInPlanner(thinking_config=ThinkingConfig(include_thoughts=True)))

Root cause

In integrations/adk-middleware/python/src/ag_ui_adk/event_translator.py, the thought signature is captured only inside the if is_thought: branch (~L544–548) and later emitted at ~L790–800 with subtype="message":

if is_thought:
    thought_parts.append(part.text)
    sig = getattr(part, 'thought_signature', None)   # ← only thought-TEXT parts
    thought_signatures.append(sig)

But in practice Gemini does not attach the signature to thought-text parts — it attaches it to function_call parts. So thought_signatures ends up all None and nothing is emitted.

Evidence

Probe over runner.run_async(...) (SSE streaming, one turn with 2 tool calls), inspecting every part:

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 in the SSE stream across 3 separate runs: 0.

Expected behaviour

The AG-UI ReasoningEncryptedValueEvent already defines subtype: "tool-call" for exactly this case (a signature bound to a tool call). The middleware should emit:

ReasoningEncryptedValueEvent(
    subtype="tool-call",
    entity_id=<toolCallId>,
    encrypted_value=<base64(thought_signature)>,
)

whenever a function_call part carries a thought_signature, mirroring the existing subtype="message" emission for thought-text parts.

Proposed fix

In the function-call translation path, read part.thought_signature from function_call parts and emit ReasoningEncryptedValueEvent(subtype="tool-call", entity_id=tool_call_id, ...). Happy to send a PR (branch + focused test).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions