Skip to content

Request file_search_call.results and surface the FileSearch tool-call output in the Responses provider - #731

Open
PratikDhanave wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:file-search-call-results
Open

Request file_search_call.results and surface the FileSearch tool-call output in the Responses provider#731
PratikDhanave wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:file-search-call-results

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

What

The Responses provider maps *hostedtool.FileSearch to a responses.FileSearchToolParam, but it never asked the API to include the retrieved chunks and never handled the resulting output item:

  • responsesBuildCompletionParams only ever appended ResponseIncludableReasoningEncryptedContent to params.Include — never file_search_call.results.
  • responsesProcessResponse (and the streaming ResponseOutputItemDoneEvent switch) had no case for responses.ResponseFileSearchToolCall.

Net effect: the file_search_call item (queries + retrieved chunks) was dropped; only file_citation annotations on the assistant message surfaced via populateAnnotations.

This change:

  1. In the *hostedtool.FileSearch branch, guard-appends responses.ResponseIncludableFileSearchCallResults to params.Include (same not-duplicated guard used for the reasoning-encrypted-content include), so the API returns the retrieved chunks.
  2. Adds a responses.ResponseFileSearchToolCall case in both the non-streaming and streaming output switches that surfaces each result as a TextContent carrying a CitationAnnotation (file ID, filename, snippet), with the raw call (queries + status) kept on RawRepresentation.

Why

This aligns the Go Responses provider with how the .NET and Python Agent Framework SDKs surface FileSearch tool output: the retrieved file chunks and their citations become message content rather than being discarded. Without requesting file_search_call.results the API omits the chunk text entirely, so both parts are needed to close the gap.

Testing

go build ./..., go vet ./provider/openaiprovider/... and go test ./provider/openaiprovider/... all pass. Added to responses_test.go:

  • TestResponsesFileSearchTool_NonStreaming — asserts the request include contains file_search_call.results and that the retrieved file ID / filename / text are surfaced through RunText().Collect().
  • TestResponsesFileSearchTool_Streaming — same, through the streaming path.
  • TestResponsesFileSearchTool_DoesNotDuplicateInclude — a caller that already set the include via ResponsesNewParams does not get a duplicated entry.

All three fail before the change (missing include in the request body; results dropped) and pass after.

Copilot AI review requested due to automatic review settings July 24, 2026 03:42
@PratikDhanave
PratikDhanave requested a review from a team as a code owner July 24, 2026 03:42

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 updates the OpenAI Responses provider to properly request and surface file_search_call.results, ensuring FileSearch tool-call retrieved chunks are no longer silently discarded and can be consumed as message content (with citations), in line with other SDKs’ behavior.

Changes:

  • Adds file_search_call.results to params.Include when the hosted FileSearch tool is configured, avoiding duplicate include entries.
  • Handles responses.ResponseFileSearchToolCall output items in both non-streaming and streaming processing paths.
  • Adds unit tests covering non-streaming, streaming, and “does not duplicate include” scenarios for FileSearch.

Reviewed changes

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

File Description
provider/openaiprovider/responses.go Requests FileSearch results in include and surfaces file_search_call output items as TextContent with CitationAnnotation.
provider/openaiprovider/responses_test.go Adds coverage to ensure file_search_call.results is requested and FileSearch results appear in collected/streamed message output without duplicate includes.

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

Comment thread provider/openaiprovider/responses.go Outdated
Comment on lines +1182 to +1186
// fileSearchToolCallContents surfaces a file_search_call output item as message
// content. Each retrieved chunk becomes a TextContent carrying a CitationAnnotation
// (file ID, filename and snippet), so the queries/results are no longer dropped and
// only the file_citation annotations survive. The raw item (queries + status) is kept
// on RawRepresentation, matching the .NET/Python surfacing of FileSearch results.

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.

Good catch — fixed in ee9c5c4: reworded the doc comment so the dropped queries/results are described as the previous behavior this helper fixes, not the current one.

Comment on lines +1187 to +1204
func fileSearchToolCallContents(item responses.ResponseFileSearchToolCall) []message.Content {
contents := make([]message.Content, 0, len(item.Results))
for _, res := range item.Results {
textContent := &message.TextContent{
ContentHeader: message.ContentHeader{RawRepresentation: item},
Text: res.Text,
}
textContent.Annotations = append(textContent.Annotations, &message.CitationAnnotation{
FileID: res.FileID,
Title: res.Filename,
Snippet: res.Text,
ToolName: "file_search",
RawRepresentation: res,
})
contents = append(contents, textContent)
}
return contents
}

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.

Good catch — fixed in ee9c5c4: a zero-result file_search_call now emits a single empty annotated TextContent (ToolName file_search) carrying the raw item on RawRepresentation, so the call is surfaced and, being annotated, is not coalesced away. Added TestResponsesFileSearchTool_NoResultsStillSurfaced to lock this.

@github-actions github-actions Bot added the public-api-change Pull Request changes public APIs label Jul 24, 2026
Attaching a *hostedtool.FileSearch tool never requested file_search_call.results,
and responsesProcessResponse had no case for the file_search_call output item, so
the queries and retrieved chunks were silently dropped and only file_citation
annotations survived.

Request responses.ResponseIncludableFileSearchCallResults (guarded like the
reasoning-encrypted-content include so it is not duplicated) and map the
ResponseFileSearchToolCall output item to TextContent carrying a CitationAnnotation
(file ID, filename and snippet) in both the non-streaming and streaming paths,
matching how the .NET/Python SDKs surface FileSearch results.
@PratikDhanave
PratikDhanave force-pushed the file-search-call-results branch from ee9c5c4 to 4219007 Compare July 24, 2026 09:30
@github-actions github-actions Bot added parity-approved Go API consistency review found no parity issues and removed public-api-change Pull Request changes public APIs labels Jul 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Parity Review — Cross-SDK Consistency ✅

Summary

This PR fixes a silent data-loss bug in provider/openaiprovider/responses.go: when *hostedtool.FileSearch is attached, the Responses provider now (1) adds file_search_call.results to params.Include so the API returns retrieved chunks, and (2) handles the ResponseFileSearchToolCall output item in both the non-streaming and streaming paths, surfacing each chunk as a TextContent with a CitationAnnotation.

Upstream parity

Python (python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py, python/packages/openai/agent_framework_openai/_chat_client.py): Both files handle file_search_call output items, and the OpenAI chat client requests file_search_call.results in its include list. The Go change closes the same gap that Python already had closed.

Behavior alignment: Python surfaces file_search_call as an informational_only function-call content entry carrying the queries as JSON args. Go surfaces each retrieved chunk as an annotated TextContent (consistent with how Go represents file_citation annotations elsewhere). The representation is idiomatic to each SDK; the semantic intent — request the chunks and surface the call rather than discard it — is aligned across both.

No .NET equivalent found: The .NET Microsoft.Agents.AI.OpenAI and Microsoft.Agents.AI.Hosting.OpenAI layers do not appear to have a direct FileSearchToolCall response handler in the inspected paths, but this is consistent with the Python lead; any .NET gap is pre-existing and out of scope for this PR.

Public API surface

All changes are to unexported functions (responsesBuildCompletionParams, responsesProcessResponse, responsesProcessStreamingUpdate, and the new fileSearchToolCallContents helper). No exported Go identifiers were added, changed, or removed. The public-api-change label has been removed as it was applied in error.

Verdict

The PR aligns the Go Responses provider with upstream Python behavior and contains no inconsistencies or divergences that warrant a parity concern. Labelled parity-approved.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Go API Consistency Review Agent · 81.8 AIC · ⌖ 5.69 AIC · ⊞ 5.9K ·

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.

2 participants