Skip to content

Surface Responses output_text logprobs on the text content - #1105

Open
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:fix/openai-responses-output-text-logprobs
Open

PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:fix/openai-responses-output-text-logprobs

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

The Responses path built a TextContent from an output_text part (non-streaming responsesProcessResponse, and the streaming output_item.done handler) but never read output_text.logprobs. So a caller that requested logprobs via raw params (Include: ["message.output_text.logprobs"], TopLogprobs) never received them.

The Python client preserves them on the text content's additional_properties["logprobs"] on both parse paths.

Change

  • Non-streaming: populate the TextContent AdditionalProperties with the output_text logprobs when present.
  • Streaming: the output_item.done handler previously emitted a text content only when annotations were present (the text itself already arrived via deltas); it now also emits when logprobs are present and carries them, mirroring the annotations handling. Key uses the PascalCase convention already used elsewhere in the provider.

Test

  • TestResponsesOutputTextLogprobsSurfaced_NonStreaming: a canned response whose output_text carries a logprobs array now surfaces Logprobs on the TextContent AdditionalProperties. Fails before the change (nil), passes after.

The Responses path built a TextContent from an output_text part but never
read its logprobs, so a caller requesting logprobs (Include:
message.output_text.logprobs) never received them. The Python client stores
them on the text content's additional_properties.

Populate the TextContent's AdditionalProperties with the output_text
logprobs when present, on both the non-streaming path and the streaming
output_item.done path (which now emits an annotations/logprobs-only content
the same way it already does for annotations).
Copilot AI lite review requested due to automatic review settings September 18, 2026 15:41
@github-actions github-actions Bot added area:provider Changes files in the provider area area:provider/openai Changes files in the provider / openai area size:medium At most 100 changed lines across at most 5 files labels Sep 18, 2026

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.

🟡 Changes recommended

Streaming logprobs can be lost during response collection, and the new streaming path lacks regression coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates Responses API handling to expose output_text logprobs for non-streaming and streaming responses.

Changes:

  • Propagates logprobs to TextContent.AdditionalProperties.
  • Emits streaming metadata for logprobs.
  • Adds non-streaming regression coverage.
File summaries
File Summary
provider/openaiprovider/responses.go Propagates output text logprobs.
provider/openaiprovider/responses_test.go Tests non-streaming logprobs exposure.
Review details

Suppressed comments (1)

provider/openaiprovider/responses.go:1602

  • When logprobs are present without annotations, this creates an empty TextContent immediately after the delta text. ResponseStream.Collect() then coalesces adjacent text contents and copies AdditionalProperties only from the first item (message/content.go:1175-1179), so the returned collected response loses these logprobs. Preserve the metadata through coalescing (for example, merge the content metadata) or otherwise make the logprobs update survive Collect(); add a streaming regression test for the logprobs-only case.
						populateAnnotations(outputText.Annotations, annotatedContent)
						if len(outputText.Logprobs) > 0 {
							annotatedContent.AdditionalProperties = map[string]any{"Logprobs": outputText.Logprobs}
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1590 to +1602
if c, ok := c.AsAny().(responses.ResponseOutputText); ok && (len(c.Annotations) > 0 || len(c.Logprobs) > 0) {
hasMetadata = true
break
}
}

if hasAnnotations {
if hasMetadata {
annotatedContent := &message.TextContent{}
for _, c := range item.Content {
if outputText, ok := c.AsAny().(responses.ResponseOutputText); ok {
populateAnnotations(outputText.Annotations, annotatedContent)
if len(outputText.Logprobs) > 0 {
annotatedContent.AdditionalProperties = map[string]any{"Logprobs": outputText.Logprobs}
@github-actions github-actions Bot added kind:code Changes production behavior or code kind:tests Changes tests, fixtures, or test infrastructure labels Sep 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Scope: user-visible behavior (bug fix); no public API surface changed

Changed Go contract: provider/openaiprovider/responses.goresponsesProcessResponse (non-streaming) and responsesProcessStreamingUpdate's output_item.done handling now populate message.TextContent.AdditionalProperties["Logprobs"] with the output_text.logprobs payload when present. No exported types, fields, functions, or method signatures were added, removed, or changed — AdditionalProperties map[string]any already existed on TextContent; this PR only starts populating it with an additional key.

Upstream evidence reviewed:

  • python/packages/openai/agent_framework_openai/_chat_client.py lines ~3310-3319 (output_text_properties helper reading output.logprobs and returning {"logprobs": serialized}) and lines ~2039-2041 (round-tripping content.additional_properties["logprobs"] back into the request payload) — confirms the Python client stores raw OpenAI output_text.logprobs on the text content's additional_properties under the key "logprobs", exactly the behavior this PR ports to Go.
  • python/packages/openai/agent_framework_openai/_chat_client.py line 233 ('message.output_text.logprobs' in the Include doc) confirms the opt-in mechanism (Include: ["message.output_text.logprobs"]) matches the Go PR's premise.
  • No equivalent found in dotnet/src/Microsoft.Agents.AI.OpenAI/ (no logprobs handling in the .NET OpenAI client wrapper), so .NET parity could not be evaluated for this specific surface.

Result: aligned. The Go change reproduces the Python client's existing behavior (surface provider logprobs into the content's extension-properties bag) rather than introducing new Go-only behavior; there's no default/opt-in mismatch since surfacing only occurs when the caller already requested logprobs via provider-specific Include/TopLogprobs raw params, matching Python's opt-in gate. The only cross-repo difference is the map key casing (Go: "Logprobs" PascalCase vs Python: "logprobs" snake/lowercase); this is consistent with this Go provider's existing convention for similar provider-extension keys (e.g. "ContainerId", "EndUserId", "SafetyIdentifier", though "reasoningItemId" is an existing outlier), and provider-internal metadata-bag key casing is not considered a public-API parity concern. No inline findings raised.

Generated by Go API Consistency Review Agent · copilot · auto · 34.6 AIC · ⌖ 7.9 AIC · ⊞ 9.2K ·

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

Labels

area:provider/openai Changes files in the provider / openai area area:provider Changes files in the provider area kind:code Changes production behavior or code kind:tests Changes tests, fixtures, or test infrastructure size:medium At most 100 changed lines across at most 5 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants