Skip to content

Surface Responses output_text logprobs on the text content - #1105

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

PratikDhanave (PratikDhanave) wants to merge 2 commits 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

This comment has been minimized.

Add a streaming test asserting the output_item.done path surfaces
output_text logprobs on the emitted text content, not just the non-streaming
path.
@github-actions github-actions Bot added size:large At most 300 changed lines across at most 10 files kind:dependencies Changes dependencies or manifests and removed size:medium At most 100 changed lines across at most 5 files labels Sep 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Scope: user-visible behavior (bug fix), internal-only implementation
Changed Go contract: No new exported types/functions/fields. responsesProcessResponse and the streaming output_item.done handler in provider/openaiprovider/responses.go now populate the existing message.TextContent.AdditionalProperties["Logprobs"] map entry when the Responses API output_text.logprobs field is present. Previously this data was silently dropped on both the non-streaming and streaming parse paths.
Upstream evidence reviewed:

  • python/packages/openai/agent_framework_openai/_chat_client.py_get_metadata_from_response (~L3933) sets additional_properties={"logprobs": ...} for non-streaming output, and output_text_properties() (~L3311) plus its callers in _parse_chunk_from_openai (response.content_part.added / response.output_text.delta, ~L3376/~L3400) do the same for streaming — confirming the Python client preserves output_text.logprobs on the text content's additional_properties, matching the intent of this Go fix.
  • No equivalent Logprobs-specific handling found in dotnet/src/Microsoft.Agents.AI.OpenAI (the .NET OpenAI provider); the only .NET Logprobs references are in Microsoft.Agents.AI.Hosting.OpenAI (server-side hosting/serialization, not the client parse path), so no direct .NET parity signal either way.
    Result: aligned. This restores parity with the Python client's existing additional_properties["logprobs"] behavior; it is a bug fix (previously-silent data loss) rather than a new capability, so no Go-only feature is being introduced ahead of upstream. The key casing ("Logprobs" PascalCase) follows the existing convention in this file (e.g. "EndUserId", "SafetyIdentifier", "ContainerId"), consistent with how other provider-specific extras are surfaced. No exported Go API surface changed (only internal population of an existing map[string]any field), and the two added tests pass. No public-api-change label needed.

Generated by Go API Consistency Review Agent · copilot · auto · 67.4 AIC · ⌖ 7 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:dependencies Changes dependencies or manifests kind:tests Changes tests, fixtures, or test infrastructure size:large At most 300 changed lines across at most 10 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants