Skip to content

Forward provider updates in the structured-output middleware so the response is not empty - #607

Merged
George Adams (gdams) merged 1 commit into
microsoft:mainfrom
PratikDhanaveFork:forward-structured-output-updates
Jul 23, 2026
Merged

George Adams (gdams) merged 1 commit into
microsoft:mainfrom
PratikDhanaveFork:forward-structured-output-updates

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

What

The structured-output middleware's happy-path loop accumulates provider updates to assemble the JSON payload for unmarshalling, but it never yields any update downstream. The only success exit yields nothing.

Because this is a provider middleware and agent.invoke builds the response by consuming the provider-middleware chain (historyResponse.Update(update) per yielded update), emitting zero updates leaves historyResponse empty. The consequence: the collected Response has empty text, and the history provider persists an empty assistant message even though the structured value was deserialized correctly.

Fix

After appending the update text to the accumulation buffer and updating the message key, forward the update downstream and stop if the consumer stops:

data = append(data, update.String()...)
current.update(update)
if !yield(update, nil) {
    return
}

This preserves the existing per-message reset logic and the final unmarshal, while surfacing the assistant response so agent.invoke populates and persists historyResponse.

Parity

This matches .NET AgentRunResponse<T>, which keeps Messages/Text alongside the deserialized Result rather than discarding the raw assistant response once the value is parsed.

Tests

  • Extended TestAgent_StructuredOutput_SuccessfulUnmarshal to assert the returned Response.String() equals the streamed JSON payload (previously discarded).
  • Added TestAgent_StructuredOutput_StoresAssistantMessages, which runs with an explicit history provider and a session and asserts the stored assistant messages are non-empty and contain the JSON payload.

Both tests fail before the change and pass after. go build ./..., go vet ./agent/..., and go test ./agent/... are green.

Copilot AI review requested due to automatic review settings July 23, 2026 05:34
@PratikDhanave
PratikDhanave (PratikDhanave) requested a review from a team as a code owner July 23, 2026 05:34

Copilot AI left a comment

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.

Pull request overview

This PR fixes the structured-output provider middleware so it forwards streamed ResponseUpdates downstream while still accumulating text for final structured unmarshalling, preventing collected responses and history persistence from ending up empty on the happy path.

Changes:

  • Forward provider updates from the structured-output middleware (and respect downstream stop/backpressure) while continuing to accumulate JSON text for unmarshalling.
  • Extend the existing structured-output success test to assert the collected Response.String() contains the streamed JSON payload.
  • Add a history-provider test ensuring assistant messages are actually persisted (non-empty) when structured output is enabled.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
agent/structuredoutput.go Forwards updates in the structured-output middleware so downstream consumers (including history accumulation) receive assistant output.
agent/structuredoutput_test.go Adds/extends tests to validate returned response text and persisted assistant messages are non-empty and match the streamed JSON payload.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The structured-output middleware accumulated provider updates to build the
JSON payload but never yielded any update downstream, so agent.invoke saw
zero updates and left historyResponse empty. As a result the collected
Response had no text and no assistant message was persisted by the history
provider, even though unmarshalling succeeded.

Forward each non-nil update after accumulating it, stopping if the consumer
stops. This surfaces the assistant response alongside the deserialized value,
matching .NET AgentRunResponse<T> which keeps Messages/Text next to the
parsed Result.
@PratikDhanave
PratikDhanave (PratikDhanave) force-pushed the forward-structured-output-updates branch from 6bfdb0e to 4a5106e Compare July 23, 2026 08:52
@github-actions

Copy link
Copy Markdown
Contributor

Parity Review — PR #607

Result: ✅ No parity issues found.

This PR fixes a bug in agent/structuredoutput.go where the structuredOutputMiddleware.Run method was silently consuming all provider streaming updates without forwarding them to callers.

Change Summary

The three-line addition inserts yield(update, nil) inside the update accumulation loop:

if !yield(update, nil) {
    return
}

This ensures that:

  1. Response text is surfaced — callers can read resp.String() and get the raw JSON payload alongside the deserialized value, matching .NET's AgentRunResponse<T> which keeps both Messages/Text and the parsed Result accessible.
  2. History persistence works correctlyHistoryProvider callbacks receive ResponseMessages because the middleware now feeds updates into the response accumulator that the history layer reads.

Upstream Alignment

The change moves Go toward parity with the .NET ChatClientAgent / AgentRunResponse<T> pattern (see dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs), where streaming chunks and the final structured result are both available to the caller. Before this fix, the Go structured output middleware was silently dropping all streaming updates, which was a behavioral divergence.

No new exported APIs were added; no defaults or option shapes changed. The modification is a narrow internal correctness fix.

Generated by Go API Consistency Review Agent · 95.5 AIC · ⌖ 11.9 AIC · ⊞ 27.7K ·

@github-actions github-actions Bot added the parity-approved Go API consistency review found no parity issues label Jul 23, 2026
@gdams
George Adams (gdams) added this pull request to the merge queue Jul 23, 2026
Merged via the queue into microsoft:main with commit a095fc2 Jul 23, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parity-approved Go API consistency review found no parity issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants