Skip to content

fix(ui): stop rendering the model's thought signature in the answer - #2355

Open
garlicKim21 wants to merge 5 commits into
kagent-dev:mainfrom
garlicKim21:fix/ui-datapart-thought-signature
Open

fix(ui): stop rendering the model's thought signature in the answer#2355
garlicKim21 wants to merge 5 commits into
kagent-dev:mainfrom
garlicKim21:fix/ui-datapart-thought-signature

Conversation

@garlicKim21

Copy link
Copy Markdown

Summary

Gemini attaches an encrypted thoughtSignature to the parts it returns. It is
protocol state — meant to be round-tripped into the next request — not content.

It reaches the UI as an unlabeled data part next to the text part, so it matches
neither the function_call nor the function_response branch in
handleA2ATaskArtifactUpdate and falls through to the JSON.stringify fallback,
which appends it to the visible answer. There is no separator because the text
and the stringified data are concatenated:

...verify the actual cluster resources before answering.{"thoughtSignature":"EjQKMgER...<truncated>"}

aggregatePartsToText has the same fallback for plain A2A messages.

Data parts carrying only model-internal keys are now skipped in both places,
right next to the existing skip for the empty lastChunk sentinel. Unlabeled
data parts that are real content still render exactly as before.

What the stream actually contains

Captured from an agent's A2A message/stream (Gemini 3 model, Go ADK runtime):

result.artifact.parts[0]  kind=text  "...Cilium is on v1.19.5."
result.artifact.parts[1]  kind=data  data={thoughtSignature}  metadata=null

The signature arrives structured and is stored structured — the persisted event
rows carry it as its own field and never inside message text. So nothing is lost
by not printing it; this is a rendering-only defect, and reasoning continuity is
not affected.

Environment

kagent controller/UI 0.9.12, kmcp controller 0.3.0, kagent tools 0.2.1.
Declarative agent, native Gemini ModelConfig (provider: Gemini), Go ADK runtime.

Related but not duplicates: #1631 preserves thought_signature across
OpenAI-compatible tool-call round trips (encoded into the tool-call id), and
#2032 covers those ids overflowing the chat column. Both concern the tool-call
id; this is the assistant message body, and the field here is camelCase
thoughtSignature (the Gemini REST Part spelling).

Testing

ui/src/lib/__tests__/messageHandlers.test.ts gains two cases: the signature
part is dropped, and an unlabeled data part that is genuine content still
renders. Full UI suite: 346 passed.

Independent of #2351 and #2352 — different file, no overlap.

Gemini attaches an encrypted `thoughtSignature` to the parts it returns. It is
protocol state, meant to be round-tripped into the next request, not content.
It arrives as an unlabeled data part alongside the text part, so it matches
neither the function_call nor the function_response branch in the artifact
handler and falls through to the JSON.stringify fallback, which appends it to
the visible answer:

    ...verify the actual cluster resources before answering.{"thoughtSignature":"EjQKMgER..."}

There is no separator because the text and the stringified data are
concatenated. The same fallback exists in aggregatePartsToText for plain A2A
messages.

Data parts that carry only model-internal keys are now skipped in both places,
next to the existing skip for the empty lastChunk sentinel. Unlabeled data
parts that are real content still render as before.

The signature itself is unaffected: it reaches the UI as a structured field and
is stored as one, so nothing is lost by not printing it.

Signed-off-by: Golden Garlic <148346166+garlicKim21@users.noreply.github.com>
@github-actions github-actions Bot added the bug Something isn't working label Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants