-
Notifications
You must be signed in to change notification settings - Fork 2.5k
fix: exclude encoding_format parameter for Google Gemini embeddings API #7762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -38,6 +38,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly apiKey: string | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly isFullUrl: boolean | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly maxItemTokens: number | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly isGeminiEndpoint: boolean | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Global rate limiting state shared across all instances | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static globalRateLimitState = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -73,6 +74,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.defaultModelId = modelId || getDefaultModelId("openai-compatible") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Cache the URL type check for performance | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.isFullUrl = this.isFullEndpointUrl(baseUrl) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.isGeminiEndpoint = this.isGeminiUrl(baseUrl) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.maxItemTokens = maxItemTokens || MAX_ITEM_TOKENS | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -182,6 +184,15 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return patterns.some((pattern) => pattern.test(url)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Determines if the provided URL is a Google Gemini endpoint | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param url The URL to check | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns true if it's a Gemini endpoint | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private isGeminiUrl(url: string): boolean { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return url.includes("generativelanguage.googleapis.com") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Incomplete URL substring sanitization High
'
generativelanguage.googleapis.com Error loading related location Loading
Copilot AutofixAI 2 months ago To robustly detect if the provided URL belongs to the Google Gemini API, we should parse the URL and inspect the hostname (not the full string) for an exact match or subdomain match. This avoids the scenario where the substring appears elsewhere in the URL (path, query, etc).
The required change is only within the method
Suggested changeset
1
src/services/code-index/embedders/openai-compatible.ts
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
Positive FeedbackNegative Feedback
Refresh and try again.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is using
Suggested change
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Makes a direct HTTP request to the embeddings endpoint | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Used when the user provides a full endpoint URL (e.g., Azure OpenAI with query parameters) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -204,11 +215,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "api-key": this.apiKey, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${this.apiKey}`, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: batchTexts, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: model, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encoding_format: "base64", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.isGeminiEndpoint | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: batchTexts, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: model, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Gemini doesn't support encoding_format parameter | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: batchTexts, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: model, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encoding_format: "base64", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response || !response.ok) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -263,14 +282,23 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = await this.makeDirectEmbeddingRequest(this.baseUrl, batchTexts, model) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use OpenAI SDK for base URLs | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = (await this.embeddingsClient.embeddings.create({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const embeddingParams: any = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: batchTexts, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: model, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // OpenAI package (as of v4.78.1) has a parsing issue that truncates embedding dimensions to 256 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // when processing numeric arrays, which breaks compatibility with models using larger dimensions. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // By requesting base64 encoding, we bypass the package's parser and handle decoding ourselves. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encoding_format: "base64", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })) as OpenAIEmbeddingResponse | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only add encoding_format for non-Gemini endpoints | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // OpenAI package (as of v4.78.1) has a parsing issue that truncates embedding dimensions to 256 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // when processing numeric arrays, which breaks compatibility with models using larger dimensions. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // By requesting base64 encoding, we bypass the package's parser and handle decoding ourselves. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // However, Gemini doesn't support this parameter, so we exclude it for Gemini endpoints. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.isGeminiEndpoint) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we add more provider-specific quirks, would it make sense to extract this provider detection logic into a separate utility? Something like a |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| embeddingParams.encoding_format = "base64" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = (await this.embeddingsClient.embeddings.create( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| embeddingParams, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) as OpenAIEmbeddingResponse | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Convert base64 embeddings to float32 arrays | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -365,11 +393,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = await this.makeDirectEmbeddingRequest(this.baseUrl, testTexts, modelToUse) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Test using OpenAI SDK for base URLs | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = (await this.embeddingsClient.embeddings.create({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const embeddingParams: any = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: testTexts, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: modelToUse, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encoding_format: "base64", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })) as OpenAIEmbeddingResponse | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only add encoding_format for non-Gemini endpoints | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.isGeminiEndpoint) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| embeddingParams.encoding_format = "base64" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = (await this.embeddingsClient.embeddings.create( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| embeddingParams, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) as OpenAIEmbeddingResponse | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if we got a valid response | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work documenting why we exclude
encoding_formatfor Gemini! Could we also add a class-level comment or constructor documentation mentioning that this class handles both standard OpenAI-compatible endpoints and Google Gemini endpoints with their specific requirements? This would help future maintainers understand the dual purpose of this class.