Skip to content

feat: add openai to gcp embedding translations for gemini embedding 2 - #2114

Merged
aabchoo merged 14 commits into
envoyproxy:mainfrom
hustxiayang:gemini-embedding-2
Jun 8, 2026
Merged

feat: add openai to gcp embedding translations for gemini embedding 2#2114
aabchoo merged 14 commits into
envoyproxy:mainfrom
hustxiayang:gemini-embedding-2

Conversation

@hustxiayang

@hustxiayang hustxiayang commented May 7, 2026

Copy link
Copy Markdown
Contributor

Description

1. Extend OpenAI embedding API to support multimodal embeddings

Gemini Embedding 2 is a multimodal embedding model, but the OpenAI embedding API only supports strings or token arrays as input. To support multimodal inputs (images, audio, video, PDF), we extended the /v1/embeddings endpoint with a messages field, following the https://github.com/vllm-project/vllm/blob/2a16ece2d342c0c154a4949ad317b521f8c04ec4/vllm/entrypoints/pooling/embed/protocol.py#L113

2. Gemini Embedding 2 embedContent endpoint support

Gemini Embedding 2 models (gemini-embedding-2-*) use the embedContent endpoint: https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings, unlike older models (text-embedding-004, gemini-embedding-001) which use the predict endpoint. The translator auto-detects the endpoint based on model name.

EmbeddingInputItem is only supported with the predict endpoint. The predict endpoint supports per-instance task_type and title:

{
  "instances": [
    {"content": "query text", "task_type": "RETRIEVAL_QUERY"},
    {"content": "doc text", "task_type": "RETRIEVAL_DOCUMENT", "title": "My Doc"}
  ]
}

The embedContent endpoint does not support per-item task types — it accepts a single content with one global taskType via embedContentConfig. Accepting EmbeddingInputItem for embedContent would be misleading (users would expect per-item behavior), so we reject it with a clear error. Users should set task_type at the request level instead.

task_type limitation for gemini-embedding-2. Per the doc: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings, gemini-embedding-2 does not support the task_type field — the task must be included as a prompt instruction instead (e.g. "task: search result | query: how to reset password"). The API silently ignores taskType in embedContentConfig. We still send it if the user provides it, in case future embedContent models support it.

embedContentConfig vs deprecated top-level fields. The REST API reference documents taskType, outputDimensionality, autoTruncate as deprecated top-level fields. The genai SDK v1.54+: https://github.com/googleapis/go-genai/blob/v1.54.0/models.go#L727 sends them via embedContentConfig instead.

Batch processing limitation

The Vertex AI REST API does not support batch embedding for embedContent models. Experimentally verified:

  1. batchEmbedContents — 404 on all Vertex AI endpoints. This endpoint only exists on the Gemini API (generativelanguage.googleapis.com), not Vertex AI (aiplatform.googleapis.com).
  2. embedContent with content (singular) — works, returns one embedding per request.
  3. embedContent with contents (plural) — rejected.
  4. embedContent with multiple parts — returns one aggregated embedding, not separate embeddings per part. parts are designed for combining modalities, not for batching.

A possible workaround is fan-out (splitting batch inputs into multiple embedContent calls), but this is out of scope for the gateway. We expect Vertex AI to add batchEmbedContents support in the future.

There should be little impact on performance: the inference engine is a batch processing engine regardless. Whether it receives a single request or batch requests, the engine batches internally. The only difference is whether the requests arrive in one HTTP call or multiple.

Signed-off-by: yxia216 <yxia216@bloomberg.net>
@hustxiayang
hustxiayang requested a review from a team as a code owner May 7, 2026 14:55
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label May 7, 2026
@hustxiayang
hustxiayang marked this pull request as draft May 7, 2026 15:00
@codecov-commenter

codecov-commenter commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.31088% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.50%. Comparing base (a7f9155) to head (6d9c8a8).

Files with missing lines Patch % Lines
...ternal/translator/openai_gcpvertexai_embeddings.go 85.00% 14 Missing and 7 partials ⚠️
...rnal/tracing/openinference/openai/request_attrs.go 68.00% 7 Missing and 1 partial ⚠️
internal/apischema/openai/openai.go 70.83% 4 Missing and 3 partials ⚠️
...nternal/translator/openai_awsbedrock_embeddings.go 50.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2114      +/-   ##
==========================================
- Coverage   84.52%   84.50%   -0.03%     
==========================================
  Files         144      144              
  Lines       20901    21055     +154     
==========================================
+ Hits        17667    17792     +125     
- Misses       2168     2188      +20     
- Partials     1066     1075       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hustxiayang
hustxiayang force-pushed the gemini-embedding-2 branch 2 times, most recently from 7e1585a to 0d8b2f5 Compare May 9, 2026 03:13
Signed-off-by: yxia216 <yxia216@bloomberg.net>
@hustxiayang
hustxiayang force-pushed the gemini-embedding-2 branch from 0d8b2f5 to a5929ab Compare May 9, 2026 03:47
@hustxiayang

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces support for multimodal embeddings and the newer Gemini embedContent endpoint. It refactors the OpenAI EmbeddingRequest into a discriminated union to accommodate both classic text-only inputs and chat-style message inputs. The GCP Vertex AI translator is updated to automatically select between predict and embedContent endpoints based on the model name. Feedback focuses on ensuring OpenAI API compliance for batch processing in the embedContent path and improving the handling of the AutoTruncate vendor field by using a pointer to distinguish between explicit false values and omitted fields.

Comment thread internal/translator/openai_gcpvertexai_embeddings.go
Comment thread internal/apischema/openai/openai.go Outdated
Comment thread internal/translator/openai_gcpvertexai_embeddings.go Outdated
Signed-off-by: yxia216 <yxia216@bloomberg.net>
@hustxiayang
hustxiayang marked this pull request as ready for review May 9, 2026 04:20
@hustxiayang

Copy link
Copy Markdown
Contributor Author

@yuzisun @nacx @aabchoo Do you mind to review it? Thanks!

Comment thread internal/apischema/gcp/gcp.go Outdated
Co-authored-by: Aaron Choo <achoo30@bloomberg.net>
Signed-off-by: hustxiayang <yangxiast@gmail.com>
Comment thread internal/tracing/openinference/openai/request_attrs.go Outdated
@hustxiayang

Copy link
Copy Markdown
Contributor Author

@nacx @aabchoo Please let me know if there are further concerns on this pr

Comment thread internal/translator/openai_gcpvertexai_embeddings.go
Comment thread internal/translator/openai_gcpvertexai_embeddings.go

@aabchoo aabchoo 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.

Thank you for the changes!

@aabchoo
aabchoo enabled auto-merge (squash) June 8, 2026 16:34
@hustxiayang

Copy link
Copy Markdown
Contributor Author

/retest

2 similar comments
@hustxiayang

Copy link
Copy Markdown
Contributor Author

/retest

@hustxiayang

Copy link
Copy Markdown
Contributor Author

/retest

@aabchoo
aabchoo merged commit f081c16 into envoyproxy:main Jun 8, 2026
30 checks passed
saixso pushed a commit to saixso/ai-gateway that referenced this pull request Jun 21, 2026
…envoyproxy#2114)

**Description**

### 1. Extend OpenAI embedding API to support multimodal embeddings

Gemini Embedding 2 is a multimodal embedding model, but the OpenAI
embedding API only supports strings or token arrays as input. To support
multimodal inputs (images, audio, video, PDF), we extended the
`/v1/embeddings` endpoint with a `messages` field, following the
https://github.com/vllm-project/vllm/blob/2a16ece2d342c0c154a4949ad317b521f8c04ec4/vllm/entrypoints/pooling/embed/protocol.py#L113

### 2. Gemini Embedding 2 `embedContent` endpoint support

Gemini Embedding 2 models (`gemini-embedding-2-*`) use the
`embedContent` endpoint:
https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings,
unlike older models (`text-embedding-004`, `gemini-embedding-001`) which
use the `predict` endpoint. The translator auto-detects the endpoint
based on model name.

**`EmbeddingInputItem` is only supported with the predict endpoint.**
The predict endpoint supports per-instance `task_type` and `title`:

```json
{
  "instances": [
    {"content": "query text", "task_type": "RETRIEVAL_QUERY"},
    {"content": "doc text", "task_type": "RETRIEVAL_DOCUMENT", "title": "My Doc"}
  ]
}
```

The `embedContent` endpoint does not support per-item task types — it
accepts a single `content` with one global `taskType` via
`embedContentConfig`. Accepting `EmbeddingInputItem` for `embedContent`
would be misleading (users would expect per-item behavior), so we reject
it with a clear error. Users should set `task_type` at the request level
instead.

**`task_type` limitation for `gemini-embedding-2`.** Per the doc:
https://docs.cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings,
`gemini-embedding-2` does not support the `task_type` field — the task
must be included as a prompt instruction instead (e.g. `"task: search
result | query: how to reset password"`). The API silently ignores
`taskType` in `embedContentConfig`. We still send it if the user
provides it, in case future `embedContent` models support it.

**`embedContentConfig` vs deprecated top-level fields.** The REST API
reference documents `taskType`, `outputDimensionality`, `autoTruncate`
as deprecated top-level fields. The genai SDK v1.54+:
https://github.com/googleapis/go-genai/blob/v1.54.0/models.go#L727 sends
them via `embedContentConfig` instead.

### Batch processing limitation

The Vertex AI REST API does not support batch embedding for
`embedContent` models. Experimentally verified:

1. **`batchEmbedContents`** — 404 on all Vertex AI endpoints. This
endpoint only exists on the Gemini API
(`generativelanguage.googleapis.com`), not Vertex AI
(`aiplatform.googleapis.com`).
2. **`embedContent` with `content` (singular)** — works, returns one
embedding per request.
3. **`embedContent` with `contents` (plural)** — rejected.
4. **`embedContent` with multiple `parts`** — returns one aggregated
embedding, not separate embeddings per part. `parts` are designed for
combining modalities, not for batching.

A possible workaround is fan-out (splitting batch inputs into multiple
`embedContent` calls), but this is out of scope for the gateway. We
expect Vertex AI to add `batchEmbedContents` support in the future.

There should be little impact on performance: the inference engine is a
batch processing engine regardless. Whether it receives a single request
or batch requests, the engine batches internally. The only difference is
whether the requests arrive in one HTTP call or multiple.

---------

Signed-off-by: yxia216 <yxia216@bloomberg.net>
Signed-off-by: hustxiayang <yangxiast@gmail.com>
Co-authored-by: Dan Sun <dsun20@bloomberg.net>
Co-authored-by: Ignasi Barrera <ignasi@tetrate.io>
Co-authored-by: Aaron Choo <achoo30@bloomberg.net>
Signed-off-by: saixso <sai.soundararajan@spoton.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants