Skip to content

fix: image search for genai search tool - #5647

Merged
akshaydeo merged 1 commit into
devfrom
07-29-fix_image_search_for_genai_search_tool
Jul 29, 2026
Merged

fix: image search for genai search tool#5647
akshaydeo merged 1 commit into
devfrom
07-29-fix_image_search_for_genai_search_tool

Conversation

@TejasGhatte

@TejasGhatte TejasGhatte commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Extends the Gemini provider's Google Search grounding support to cover image search: searchTypes (web + image), image grounding chunks, image search queries, and search localization via latLng. Also fixes a bug where multi-source grounding supports were fanned out into one support per cited chunk instead of being regrouped by segment.

Changes

  • Added SearchTypes, WebSearch, and ImageSearch types to types.go, with camelCase/snake_case UnmarshalJSON support. GoogleSearch now carries a SearchTypes field.
  • Added GroundingChunkImage to represent image-search grounding chunks (with sourceUri, imageUri, title, domain). GroundingChunk now includes an Image field alongside Web.
  • Added ImageSearchQueries to GroundingMetadata to keep image queries separate from web queries.
  • Introduced groundingChunkSource helper that normalizes both web and image chunks into a ResponsesWebSearchToolCallActionSearchSource, replacing scattered inline chunk-to-source conversions throughout responses.go.
  • search_content_types on ResponsesToolWebSearch now maps "text"WebSearch and "image"ImageSearch when converting to/from Gemini's searchTypes.
  • Search localization (latLng from toolConfig.retrievalConfig) is now round-tripped through ResponsesToolWebSearchUserLocation.Latitude/Longitude so it survives the Gemini → Bifrost Responses → Gemini hop.
  • ImageQueries added to ResponsesWebSearchToolCallAction and ImageURL/Domain added to ResponsesWebSearchToolCallActionSearchSource to carry image-specific grounding data through the neutral schema.
  • Fixed buildGroundingMetadataFromWebSearch: annotations were previously emitting one GroundingSupport per (segment, chunk) pair. They are now regrouped by segment key so multi-source supports correctly list all cited chunk indices rather than duplicating the segment.
  • emitWebSearchFromGroundingMetadata and emitAnnotationsFromGroundingSupports updated to handle image chunks via groundingChunkSource and to propagate ImageQueries.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./core/providers/gemini/...

New tests cover:

  • TestGeminiGoogleSearchToolRoundTrip — verifies searchTypes, excludeDomains, timeRangeFilter, and latLng survive a full Gemini → Bifrost Responses → Gemini round trip.
  • TestGeminiGoogleSearchToolRoundTripSnakeCase — same for snake_case input; asserts unselected search types are not synthesized.
  • TestGeminiImageGroundingRoundTrip — verifies image chunks keep their asset URL and domain, and that imageSearchQueries stay separate from webSearchQueries.
  • TestGeminiGroundedRoundTripMergesMultiSourceSupports — non-streaming path: four supports over four chunks, three citing two sources, must come back as four supports (not seven).
  • TestGeminiGroundedStreamRoundTripMergesMultiSourceSupports — streaming counterpart of the above.
  • TestToBifrostCountTokensResponseCachedContent — locks cached-token accounting so cache modalities are never folded into the breakdown a second time.

Breaking changes

  • Yes
  • No

Related issues

Security considerations

No auth, secrets, or PII implications. latLng coordinates flow through the existing tool config path and are not persisted.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e47d2da-9d70-4526-ba39-dd7330d934a9

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9cd7b and c13f703.

📒 Files selected for processing (5)
  • core/providers/gemini/payload_ordering_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/types.go
  • core/providers/gemini/websearchstreamstate_test.go
  • core/schemas/responses.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for Gemini image search alongside web search.
    • Preserved web-search location preferences and selected search types, including text and image results.
    • Improved grounding metadata with image queries, image sources, titles, domains, and URLs.
    • Enhanced citations by combining multi-source references and maintaining accurate text spans.
  • Bug Fixes

    • Corrected cached-token accounting and prevented cached content from inflating modality totals.

Walkthrough

Gemini search conversions now preserve localization coordinates, text/image search types, image grounding metadata, merged multi-source citations, and streaming parity. Responses schemas carry the added fields, with round-trip tests covering these paths. A separate test validates cached-content token accounting.

Changes

Gemini search and grounding

Layer / File(s) Summary
Search and grounding contracts
core/providers/gemini/types.go, core/schemas/responses.go
Gemini and Responses schemas add search-type, image-grounding, image-query, domain, and location-coordinate fields.
Search tool and localization conversion
core/providers/gemini/responses.go, core/providers/gemini/websearchstreamstate_test.go
Search types and retrieval coordinates convert between Gemini and Responses web-search tools, including camelCase and snake_case inputs.
Grounding and citation conversion
core/providers/gemini/responses.go, core/providers/gemini/websearchstreamstate_test.go
Web and image grounding sources, queries, citations, and annotations are normalized, while multi-source supports are merged by segment in non-streaming and streaming paths.

Cached token accounting

Layer / File(s) Summary
Cached token conversion validation
core/providers/gemini/payload_ordering_test.go
Tests verify cached modalities are not double-counted and cached read totals are summed correctly.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Gemini
  participant BifrostResponses
  participant StreamState
  Gemini->>BifrostResponses: convert search tools and grounding metadata
  BifrostResponses->>StreamState: buffer grounding annotations
  StreamState->>BifrostResponses: emit merged citations and image sources
Loading

Possibly related PRs

Suggested reviewers: akshaydeo, pratham-mishra04, sammaji

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main change: adding image search support to the Gemini search tool.
Description check ✅ Passed The PR description follows the template closely and includes summary, changes, testing, breaking changes, and security notes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-29-fix_image_search_for_genai_search_tool

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Comment @coderabbitai help to get the list of available commands.

@TejasGhatte
TejasGhatte marked this pull request as ready for review July 29, 2026 08:04

TejasGhatte commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

@CLAassistant

CLAassistant commented Jul 29, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
core/providers/gemini/responses.go (1)

3772-3805: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

urlToIndexMap collision risk between web and image chunks sharing the same page URL.

urlToIndexMap is keyed by source.URL alone (line 3804, pre-existing). The new image branch (3786-3801) writes GroundingChunkImage.SourceURI = source.URL — the same URL namespace as web chunks. If a web citation and an image citation attribute to the same page URL, the later chunk overwrites the earlier map entry, and any annotation citing that URL gets silently misattributed to the wrong chunk (and possibly the wrong chunk type) later in the function.

♻️ Suggested fix: key by URL+kind instead of URL alone
-		groundingChunks = append(groundingChunks, chunk)
-		urlToIndexMap[source.URL] = int32(len(groundingChunks) - 1)
+		groundingChunks = append(groundingChunks, chunk)
+		key := source.URL
+		if chunk.Image != nil {
+			key += "|image"
+		}
+		urlToIndexMap[key] = int32(len(groundingChunks) - 1)

(and apply the matching key construction where urlToIndexMap is looked up for annotations, based on whether the annotation source was an image source)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/gemini/responses.go` around lines 3772 - 3805, Update the
grounding-chunk mapping around urlToIndexMap to use a composite key containing
the source URL and chunk kind, distinguishing web sources from image sources.
Apply the identical key construction when annotation lookups occur, using
whether the annotation references an image source, so web and image chunks
sharing a page URL resolve to their respective chunk indices.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/providers/gemini/responses.go`:
- Around line 2490-2515: Update groundingChunkSource to return the existing
zero-value source and false immediately when chunk is nil, before accessing
chunk.Web or chunk.Image. Keep the current URL, title, image URL, domain, and
unsupported-chunk handling unchanged for non-nil inputs; the callers should
continue receiving the same boolean-based skip behavior.

---

Nitpick comments:
In `@core/providers/gemini/responses.go`:
- Around line 3772-3805: Update the grounding-chunk mapping around urlToIndexMap
to use a composite key containing the source URL and chunk kind, distinguishing
web sources from image sources. Apply the identical key construction when
annotation lookups occur, using whether the annotation references an image
source, so web and image chunks sharing a page URL resolve to their respective
chunk indices.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 68661c67-2bd0-4da0-a96d-34f8f724c8c7

📥 Commits

Reviewing files that changed from the base of the PR and between af8a7ee and ce522ef.

📒 Files selected for processing (5)
  • core/providers/gemini/payload_ordering_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/types.go
  • core/providers/gemini/websearchstreamstate_test.go
  • core/schemas/responses.go

Comment thread core/providers/gemini/responses.go
@TejasGhatte
TejasGhatte force-pushed the 07-28-fix_count_tokens_request_building branch from af8a7ee to c012275 Compare July 29, 2026 09:02
@TejasGhatte
TejasGhatte force-pushed the 07-29-fix_image_search_for_genai_search_tool branch from ce522ef to 4b9cd7b Compare July 29, 2026 09:02

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/providers/gemini/responses.go (1)

3775-3808: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

URL-keyed chunk map can misattribute citations between web and image chunks sharing the same page URL.

urlToIndexMap is map[string]int32, so when a web-type source and an image-type source share the same URL (the image's containing page can legitimately equal a separate web citation's URL), the second insert silently overwrites the first. Later, annotation lookup only ever resolves to the last chunk stored for that URL:

var chunkIndices []int32
if annotation.URL != nil {
    if chunkIdx, exists := urlToIndexMap[*annotation.URL]; exists {
        chunkIndices = []int32{chunkIdx}
    }
}

This can attribute a plain web citation to the image chunk (or vice versa), showing incorrect source metadata (e.g. ImageURL/Domain) for that citation. Before image grounding was added, all chunks were Web-type so a URL collision was a harmless duplicate; now it can cross type boundaries.

🛡️ Proposed fix
-	urlToIndexMap := make(map[string]int32) // Map URL to chunk index for annotation processing
+	urlToIndexMap := make(map[string][]int32) // Map URL to all matching chunk indices for annotation processing
@@
 		groundingChunks = append(groundingChunks, chunk)
-		urlToIndexMap[source.URL] = int32(len(groundingChunks) - 1)
+		urlToIndexMap[source.URL] = append(urlToIndexMap[source.URL], int32(len(groundingChunks)-1))
@@
 		var chunkIndices []int32
 		if annotation.URL != nil {
-			if chunkIdx, exists := urlToIndexMap[*annotation.URL]; exists {
-				chunkIndices = []int32{chunkIdx}
-			}
+			if idxs, exists := urlToIndexMap[*annotation.URL]; exists {
+				chunkIndices = idxs
+			}
 		}

Also applies to: 3838-3844

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/gemini/responses.go` around lines 3775 - 3808, Update the
grounding chunk index bookkeeping in the source loop and the annotation URL
lookup so duplicate page URLs do not overwrite one another across web and image
chunks. Preserve every matching chunk index, then resolve annotations using the
appropriate source type or URL rather than always selecting the last entry; keep
citation attribution tied to the correct web or image metadata.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@core/providers/gemini/responses.go`:
- Around line 3775-3808: Update the grounding chunk index bookkeeping in the
source loop and the annotation URL lookup so duplicate page URLs do not
overwrite one another across web and image chunks. Preserve every matching chunk
index, then resolve annotations using the appropriate source type or URL rather
than always selecting the last entry; keep citation attribution tied to the
correct web or image metadata.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1678cee6-fef2-4980-8552-25c6f3e68ce2

📥 Commits

Reviewing files that changed from the base of the PR and between ce522ef and 4b9cd7b.

📒 Files selected for processing (5)
  • core/providers/gemini/payload_ordering_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/types.go
  • core/providers/gemini/websearchstreamstate_test.go
  • core/schemas/responses.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • core/providers/gemini/payload_ordering_test.go
  • core/schemas/responses.go
  • core/providers/gemini/types.go
  • core/providers/gemini/websearchstreamstate_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 29, 2026

akshaydeo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Jul 29, 11:07 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 29, 11:10 AM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 29, 11:11 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 07-28-fix_count_tokens_request_building to graphite-base/5647 July 29, 2026 11:08
@akshaydeo
akshaydeo changed the base branch from graphite-base/5647 to dev July 29, 2026 11:09
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review July 29, 2026 11:09

The base branch was changed.

@akshaydeo
akshaydeo force-pushed the 07-29-fix_image_search_for_genai_search_tool branch from 4b9cd7b to c13f703 Compare July 29, 2026 11:10
@akshaydeo
akshaydeo merged commit 402206f into dev Jul 29, 2026
12 of 14 checks passed
@akshaydeo
akshaydeo deleted the 07-29-fix_image_search_for_genai_search_tool branch July 29, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants