Skip to content

Surface chat system_fingerprint and logprobs as response metadata - #1104

Open
PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:fix/openai-chat-response-metadata
Open

PratikDhanave (PratikDhanave) wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:fix/openai-chat-response-metadata

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

The Chat path built its ResponseUpdate with ResponseID/FinishReason/usage but never set AdditionalProperties, so two pieces of response metadata were dropped:

  • system_fingerprint (on the completion / chunk), and
  • per-choice logprobs (returned when a caller requests them via Logprobs/TopLogprobs).

The Python client carries both as chat response metadata (_get_metadata_from_chat_responsesystem_fingerprint, _get_metadata_from_chat_choicelogprobs). The Responses path in this provider already populates AdditionalProperties, so this was also a chat-vs-responses asymmetry — the existing chat fixtures even contain system_fingerprint/logprobs values that nothing surfaced.

Change

  • Populate AdditionalProperties with SystemFingerprint (when non-empty) and Logprobs (when the choice carries content/refusal logprobs) on both the non-streaming and streaming paths. Keys follow the PascalCase convention already used by the Responses path helper (responsesPopulateAdditionalProperties).

Tests

  • TestChatResponseMetadataSurfaced_NonStreaming and _Streaming: assert SystemFingerprint (and, non-streaming, Logprobs) reach the message AdditionalProperties. Both fail before the change (no message carries any), pass after.

The Chat path set ResponseID, FinishReason, and usage but never populated
AdditionalProperties, dropping the response system_fingerprint and the
per-choice logprobs that a caller requesting logprobs would expect back. The
Responses path already populates AdditionalProperties, so this was also a
chat-vs-responses asymmetry.

Populate AdditionalProperties with SystemFingerprint (when present) and
Logprobs (when the choice carries any) on both the non-streaming and
streaming paths, matching the Python client which surfaces both as chat
response metadata.
Copilot AI lite review requested due to automatic review settings September 18, 2026 12:28
@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:large At most 300 changed lines across at most 10 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 may be overwritten across chunks, and this path lacks coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Surfaces Chat response system_fingerprint and logprobs metadata through AdditionalProperties.

Changes:

  • Propagates metadata for streaming and non-streaming responses.
  • Adds tests for metadata exposure.
File summaries
File Summary
provider/openaiprovider/chat.go Populates Chat response metadata.
provider/openaiprovider/chat_test.go Tests metadata propagation.
Review details

Suppressed comments (2)

provider/openaiprovider/chat.go:227

  • The new streaming Logprobs branch is not exercised: the added streaming test only sends/asserts SystemFingerprint, while all existing streaming fixtures use logprobs: null. Add a streamed chunk containing content or refusal logprobs and assert that the yielded update (and collected message) preserves the Logprobs metadata, so this path cannot regress unnoticed.
				if logprobs := chunk.Choices[0].Logprobs; len(logprobs.Content) > 0 || len(logprobs.Refusal) > 0 {
					additionalProperties = map[string]any{"Logprobs": logprobs}
				}

provider/openaiprovider/chat_test.go:457

  • This streaming test only sends system_fingerprint; it never supplies a non-empty chunk logprobs value, so it would pass if the new streaming Logprobs handling were removed or broken. Add a chunk with content/refusal logprobs and assert that the streamed update (and, if collection is supported, the collected response) preserves them.
func TestChatResponseMetadataSurfaced_Streaming(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		_, _ = io.WriteString(w, "data: {\"id\":\"chatcmpl-md\",\"object\":\"chat.completion.chunk\",\"created\":1727888631,\"model\":\"gpt-4o-mini\",\"system_fingerprint\":\"fp_test\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"ok\"},\"finish_reason\":\"stop\"}]}\n\n")
  • 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 thread provider/openaiprovider/chat.go Outdated
Comment on lines +225 to +226
if logprobs := chunk.Choices[0].Logprobs; len(logprobs.Content) > 0 || len(logprobs.Refusal) > 0 {
additionalProperties = map[string]any{"Logprobs": 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.

Streaming logprobs arrive incrementally (each chunk carries only its own
tokens), and Response.Update merges AdditionalProperties with maps.Copy, so
attaching per-chunk logprobs kept only the final chunk's tokens after
Collect(). Accumulate content/refusal token logprobs across chunks and attach
the running total, so the collected message holds the complete per-choice
list.
@github-actions

Copy link
Copy Markdown
Contributor

Scope: user-visible behavior

Changed Go contract: No new exported types/functions/fields. chatClient.run (both non-streaming and streaming paths in provider/openaiprovider/chat.go) now populates the pre-existing agent.ResponseUpdate.AdditionalProperties field with SystemFingerprint (when non-empty) and Logprobs (when the choice/chunk carries content or refusal logprobs). Previously these fields were silently dropped on the Chat Completions path.

Upstream evidence reviewed:

  • Python: _get_metadata_from_chat_response (surfaces system_fingerprint) and _get_metadata_from_chat_choice (surfaces logprobs) in the OpenAI chat client, both populating response/message additional properties — matching the fields and behavior added here.
  • Go (in-repo, same provider): provider/openaiprovider/responses.go responsesPopulateAdditionalProperties helper already populates AdditionalProperties on the Responses path using the same PascalCase key convention (SystemFingerprint, etc.), which this PR now mirrors on the Chat path for parity between the two paths.

Result: aligned. This closes a pre-existing chat-vs-responses asymmetry and chat-vs-Python metadata gap; no new exported Go API surface is introduced (the AdditionalProperties map[string]any field on agent.ResponseUpdate already existed), so no public-api-change label is needed. Streaming logprobs accumulation across chunks is a reasonable Go-specific implementation detail (Python's chat client is non-streaming for this metadata) and does not diverge from upstream semantics — the final accumulated value matches the non-streaming shape. Tests added (TestChatResponseMetadataSurfaced_NonStreaming/_Streaming, TestChatStreamingLogprobsAccumulated) verify the fix.

Generated by Go API Consistency Review Agent · copilot · auto · 32.3 AIC · ⌖ 9.22 AIC · ⊞ 9.2K ·

@github-actions github-actions Bot added the kind:dependencies Changes dependencies or manifests label Sep 21, 2026
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