Skip to content

feat: support structured output in JSON Schema over A2A DataPart - #2890

Merged
EItanya merged 13 commits into
mainfrom
jetc/feat/structured-output
Sep 22, 2026
Merged

EItanya merged 13 commits into
mainfrom
jetc/feat/structured-output

Conversation

@supreme-gg-gg

@supreme-gg-gg supreme-gg-gg commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This feature is only supported in Kagent's own harness, not yet in Claude / Codex

  • Add inline and ConfigMap-backed output schemas to ADK agent templates.
  • Validate schemas against a portable provider-compatible JSON Schema profile.
  • Support structured output with tools across OpenAI, Anthropic, Bedrock, and Gemini through ADK.
  • Validate final responses and publish them as JSON A2A artifacts with schema metadata.
  • Add unit and E2E coverage for tool calls and partial streaming responses.
  • Document the structured-output design and provider behavior.
  • Relevant UI changes to configure output mode, display structured output, etc

Testing

  • Unit and E2E tests
  • Manual testing on OpenAI (Chat completion + Responses), Gemini (API), Claude (Bedrock)
  • Tested with both tool using and structured output

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 21, 2026
@blacksmith-sh

This comment has been minimized.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 22, 2026
@supreme-gg-gg

Copy link
Copy Markdown
Contributor Author

First image: UI for using tool call with structured output

Screenshot 2026-09-21 at 10 12 44 PM

Second image: AgentTemplate details page showing the agent's structured output schema

Screenshot 2026-09-21 at 10 13 15 PM

Third image: Gemini models might (depending on version) use an additional tool to set structured output. This is done internally by ADK and is a known workaround for some older model that does not support tool calling with structured output.

Screenshot 2026-09-21 at 10 39 38 PM

@supreme-gg-gg
supreme-gg-gg marked this pull request as ready for review September 22, 2026 02:48
Comment thread go/adk/pkg/a2a/consts.go Outdated
A2ADataPartMetadataIsLongRunningKey = "is_long_running"
A2ADataPartMetadataTypeFunctionCall = "function_call"
A2ADataPartMetadataTypeFunctionResponse = "function_response"
OutputSchemaSHA256MetadataKey = "kagent.dev/output-schema-sha256"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to have a KagentMetadata fn which prepended this key, do we still have that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function uses the kagent_ prefix for ADK function metadata these are public A2A result metadata like kagent.dev/timeline-position

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should still have functions to make sure they use the correct prefix. Also maybe the prefix should be kagent.dev/a2a?

@supreme-gg-gg supreme-gg-gg Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I'll add both. I also think it's worth doing a follow-up to review the metadata before we do a stable release so we can still centralize and change them (e.g. we will want to change the timeline position above as well, and some legacy ADK metadata are no longer necessary, and probably renaming the adk_ and kagent_ ones)

@supreme-gg-gg supreme-gg-gg Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a follow up for A2A metadata in #2902 if you want to take a quick look first, not fully ready for review yet

Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread go/adk/pkg/models/openai_adk.go Outdated
Comment thread go/adk/pkg/models/openai_responses.go Outdated
Comment thread go/adk/pkg/a2a/executor.go
Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread ui/src/api/chat/a2aGrpcChatClient.ts
Comment thread go/core/internal/translator/outputschema.go

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This automated review is a first pass. A manual review will follow once the blockers are addressed.


Two things block merge, both in transformStructuredOutput. The finish-reason guard added this round rejects an empty reason, which is what ADK's set_model_response path produces — the Gemini-with-tools case in the third screenshot. Separately, a HITL approval request still reaches the transform, where it has no text to validate and fails the task.

⚠️ What I checked
  • ✅ AfterEventCallback fires only for a non-nil A2A event, and its error becomes a failed task — adk@v2.3.0 server/adka2a/v2/executor.go:366-375.
  • ✅ The CEL rule making outputSchema and outputSchemaFrom mutually exclusive is on AgentTemplateSpec.
  • ✅ outputSchemaFrom is added to the provenance ConfigMap set, so editing the ConfigMap recompiles the revision.
  • ✅ Cycle detection uses pointer identity after $ref resolution, so A → B → A is caught.
  • ✅ The canonical form re-marshals through map[string]any, so the digest is stable across key order.
  • ⚠️ IsFinalResponse() short-circuits to true on LongRunningToolIDs and SkipSummarization — adk@v2.3.0 session/session.go:225.
  • ⚠️ ADK builds the set_model_response final event with no finish reason — adk@v2.3.0 internal/llminternal/outputschema_processor.go:67-76.

🤖 written by Claude

Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread go/adk/pkg/a2a/executor.go Outdated
Comment thread go/adk/pkg/models/openai_adk.go Outdated
Comment thread go/adk/pkg/outputschema/convert.go
Comment thread go/api/v1alpha3/harness_types.go
Comment thread go/core/internal/translator/outputschema.go
Comment thread docs/architecture/structured-output.md Outdated
Comment thread ui/src/components/chat/StructuredOutputCard.tsx

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UI changes here LGTM - thanks for the screenshots! I also kicked off an automated review.

And I added #2894 as a follow-up issue since the chat message part renderers might be a good place for extension points.

@supreme-gg-gg

Copy link
Copy Markdown
Contributor Author

Thanks for the review @Charlesthebird and I've also verified now structured output works with HITL:

Screen.Recording.2026-09-22.at.3.05.55.PM.mov

func NewKAgentExecutor(cfg KAgentExecutorConfig) *KAgentExecutor {
// NewKAgentExecutor creates a KAgentExecutor from config. It returns an error if
// the configured output schema cannot be prepared for runtime validation.
func NewKAgentExecutor(cfg KAgentExecutorConfig) (*KAgentExecutor, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2896 also rewrites this callback, conflicts on merge

@supreme-gg-gg supreme-gg-gg Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @mesutoezdil can you avoid leaving this type of comments since they're not too constructive. Merge conflicts are natural and authors are expected to rebase on top, this simply adds more noise during reviews. Unless the changes across PRs are going in opposite directions, in which case it might be worthwhile to bring the author's attention to it and drive some discussion, otherwise this type of comment don't add any value, as in the case here and in a few other PRs that you left similar comments before.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point, noted 👍🏽 thx for feedback

@supreme-gg-gg
supreme-gg-gg added this pull request to stack #2903 September 22, 2026 22:35
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
…ntent config and adk validation

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
@supreme-gg-gg
supreme-gg-gg force-pushed the jetc/feat/structured-output branch from 1dbd99c to c1fb364 Compare September 22, 2026 22:37
@EItanya
EItanya added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit 4ee8b69 Sep 22, 2026
27 checks passed
@EItanya
EItanya deleted the jetc/feat/structured-output branch September 22, 2026 23:23
fiunchinho added a commit to giantswarm/kagent-upstream that referenced this pull request Sep 23, 2026
…-fail-task-on-max-tokens

NewKAgentExecutor now returns an error too; the output-limit test checks it.

Signed-off-by: Jose Armesto <github@armesto.net>
alexdarbyshire pushed a commit to alexdarbyshire/kagent that referenced this pull request Oct 5, 2026
…ent-dev#2890)

## Summary

> This feature is only supported in Kagent's own harness, not yet in
Claude / Codex

- Add inline and ConfigMap-backed output schemas to ADK agent templates.
- Validate schemas against a portable provider-compatible JSON Schema
profile.
- Support structured output with tools across OpenAI, Anthropic,
Bedrock, and Gemini through ADK.
- Validate final responses and publish them as JSON A2A artifacts with
schema metadata.
- Add unit and E2E coverage for tool calls and partial streaming
responses.
- Document the structured-output design and provider behavior.
- Relevant UI changes to configure output mode, display structured
output, etc

## Testing

- Unit and E2E tests
- Manual testing on OpenAI (Chat completion + Responses), Gemini (API),
Claude (Bedrock)
- Tested with both tool using and structured output

---------

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants