feat: add Gemini embedding provider for codebase indexing (#5228)

Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com>
This commit is contained in:
SannidhyaSah 2025-07-03 09:21:42 +05:30 committed by GitHub
parent 19cd001f8d
commit 87aa688f33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 372 additions and 11 deletions

View file

@ -7,7 +7,7 @@ import { z } from "zod"
export const codebaseIndexConfigSchema = z.object({
codebaseIndexEnabled: z.boolean().optional(),
codebaseIndexQdrantUrl: z.string().optional(),
codebaseIndexEmbedderProvider: z.enum(["openai", "ollama", "openai-compatible"]).optional(),
codebaseIndexEmbedderProvider: z.enum(["openai", "ollama", "openai-compatible", "gemini"]).optional(),
codebaseIndexEmbedderBaseUrl: z.string().optional(),
codebaseIndexEmbedderModelId: z.string().optional(),
codebaseIndexSearchMinScore: z.number().min(0).max(1).optional(),
@ -23,6 +23,7 @@ export const codebaseIndexModelsSchema = z.object({
openai: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
ollama: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
"openai-compatible": z.record(z.string(), z.object({ dimension: z.number() })).optional(),
gemini: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
})
export type CodebaseIndexModels = z.infer<typeof codebaseIndexModelsSchema>
@ -37,6 +38,7 @@ export const codebaseIndexProviderSchema = z.object({
codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(),
codebaseIndexOpenAiCompatibleApiKey: z.string().optional(),
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
codebaseIndexGeminiApiKey: z.string().optional(),
})
export type CodebaseIndexProvider = z.infer<typeof codebaseIndexProviderSchema>

View file

@ -147,6 +147,7 @@ export const SECRET_STATE_KEYS = [
"codeIndexOpenAiKey",
"codeIndexQdrantApiKey",
"codebaseIndexOpenAiCompatibleApiKey",
"codebaseIndexGeminiApiKey",
] as const satisfies readonly (keyof ProviderSettings)[]
export type SecretState = Pick<ProviderSettings, (typeof SECRET_STATE_KEYS)[number]>

View file

@ -1049,6 +1049,46 @@ describe("CodeIndexConfigManager", () => {
expect(configManager.isFeatureConfigured).toBe(false)
})
it("should validate Gemini configuration correctly", async () => {
mockContextProxy.getGlobalState.mockImplementation((key: string) => {
if (key === "codebaseIndexConfig") {
return {
codebaseIndexEnabled: true,
codebaseIndexQdrantUrl: "http://qdrant.local",
codebaseIndexEmbedderProvider: "gemini",
}
}
return undefined
})
mockContextProxy.getSecret.mockImplementation((key: string) => {
if (key === "codebaseIndexGeminiApiKey") return "test-gemini-key"
return undefined
})
await configManager.loadConfiguration()
expect(configManager.isFeatureConfigured).toBe(true)
})
it("should return false when Gemini API key is missing", async () => {
mockContextProxy.getGlobalState.mockImplementation((key: string) => {
if (key === "codebaseIndexConfig") {
return {
codebaseIndexEnabled: true,
codebaseIndexQdrantUrl: "http://qdrant.local",
codebaseIndexEmbedderProvider: "gemini",
}
}
return undefined
})
mockContextProxy.getSecret.mockImplementation((key: string) => {
if (key === "codebaseIndexGeminiApiKey") return ""
return undefined
})
await configManager.loadConfiguration()
expect(configManager.isFeatureConfigured).toBe(false)
})
it("should return false when required values are missing", async () => {
mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,

View file

@ -3,12 +3,14 @@ import { CodeIndexServiceFactory } from "../service-factory"
import { OpenAiEmbedder } from "../embedders/openai"
import { CodeIndexOllamaEmbedder } from "../embedders/ollama"
import { OpenAICompatibleEmbedder } from "../embedders/openai-compatible"
import { GeminiEmbedder } from "../embedders/gemini"
import { QdrantVectorStore } from "../vector-store/qdrant-client"
// Mock the embedders and vector store
vitest.mock("../embedders/openai")
vitest.mock("../embedders/ollama")
vitest.mock("../embedders/openai-compatible")
vitest.mock("../embedders/gemini")
vitest.mock("../vector-store/qdrant-client")
// Mock the embedding models module
@ -20,6 +22,7 @@ vitest.mock("../../../shared/embeddingModels", () => ({
const MockedOpenAiEmbedder = OpenAiEmbedder as MockedClass<typeof OpenAiEmbedder>
const MockedCodeIndexOllamaEmbedder = CodeIndexOllamaEmbedder as MockedClass<typeof CodeIndexOllamaEmbedder>
const MockedOpenAICompatibleEmbedder = OpenAICompatibleEmbedder as MockedClass<typeof OpenAICompatibleEmbedder>
const MockedGeminiEmbedder = GeminiEmbedder as MockedClass<typeof GeminiEmbedder>
const MockedQdrantVectorStore = QdrantVectorStore as MockedClass<typeof QdrantVectorStore>
// Import the mocked functions
@ -259,6 +262,49 @@ describe("CodeIndexServiceFactory", () => {
)
})
it("should create GeminiEmbedder when using Gemini provider", () => {
// Arrange
const testConfig = {
embedderProvider: "gemini",
geminiOptions: {
apiKey: "test-gemini-api-key",
},
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act
factory.createEmbedder()
// Assert
expect(MockedGeminiEmbedder).toHaveBeenCalledWith("test-gemini-api-key")
})
it("should throw error when Gemini API key is missing", () => {
// Arrange
const testConfig = {
embedderProvider: "gemini",
geminiOptions: {
apiKey: undefined,
},
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Gemini configuration missing for embedder creation")
})
it("should throw error when Gemini options are missing", () => {
// Arrange
const testConfig = {
embedderProvider: "gemini",
geminiOptions: undefined,
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act & Assert
expect(() => factory.createEmbedder()).toThrow("Gemini configuration missing for embedder creation")
})
it("should throw error for invalid embedder provider", () => {
// Arrange
const testConfig = {
@ -454,6 +500,30 @@ describe("CodeIndexServiceFactory", () => {
)
})
it("should use fixed dimension 768 for Gemini provider", () => {
// Arrange
const testConfig = {
embedderProvider: "gemini",
modelId: "text-embedding-004", // This is ignored by Gemini
qdrantUrl: "http://localhost:6333",
qdrantApiKey: "test-key",
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
// Act
factory.createVectorStore()
// Assert
// getModelDimension should not be called for Gemini
expect(mockGetModelDimension).not.toHaveBeenCalled()
expect(MockedQdrantVectorStore).toHaveBeenCalledWith(
"/test/workspace",
"http://localhost:6333",
768, // Fixed dimension for Gemini
"test-key",
)
})
it("should use default model when config.modelId is undefined", () => {
// Arrange
const testConfig = {

View file

@ -16,6 +16,7 @@ export class CodeIndexConfigManager {
private openAiOptions?: ApiHandlerOptions
private ollamaOptions?: ApiHandlerOptions
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
private geminiOptions?: { apiKey: string }
private qdrantUrl?: string = "http://localhost:6333"
private qdrantApiKey?: string
private searchMinScore?: number
@ -56,6 +57,7 @@ export class CodeIndexConfigManager {
const openAiCompatibleModelDimension = this.contextProxy?.getGlobalState(
"codebaseIndexOpenAiCompatibleModelDimension",
) as number | undefined
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
// Update instance variables with configuration
this.isEnabled = codebaseIndexEnabled || false
@ -69,6 +71,8 @@ export class CodeIndexConfigManager {
this.embedderProvider = "ollama"
} else if (codebaseIndexEmbedderProvider === "openai-compatible") {
this.embedderProvider = "openai-compatible"
} else if (codebaseIndexEmbedderProvider === "gemini") {
this.embedderProvider = "gemini"
} else {
this.embedderProvider = "openai"
}
@ -87,6 +91,8 @@ export class CodeIndexConfigManager {
modelDimension: openAiCompatibleModelDimension,
}
: undefined
this.geminiOptions = geminiApiKey ? { apiKey: geminiApiKey } : undefined
}
/**
@ -102,6 +108,7 @@ export class CodeIndexConfigManager {
openAiOptions?: ApiHandlerOptions
ollamaOptions?: ApiHandlerOptions
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
geminiOptions?: { apiKey: string }
qdrantUrl?: string
qdrantApiKey?: string
searchMinScore?: number
@ -119,6 +126,7 @@ export class CodeIndexConfigManager {
openAiCompatibleBaseUrl: this.openAiCompatibleOptions?.baseUrl ?? "",
openAiCompatibleApiKey: this.openAiCompatibleOptions?.apiKey ?? "",
openAiCompatibleModelDimension: this.openAiCompatibleOptions?.modelDimension,
geminiApiKey: this.geminiOptions?.apiKey ?? "",
qdrantUrl: this.qdrantUrl ?? "",
qdrantApiKey: this.qdrantApiKey ?? "",
}
@ -138,6 +146,7 @@ export class CodeIndexConfigManager {
openAiOptions: this.openAiOptions,
ollamaOptions: this.ollamaOptions,
openAiCompatibleOptions: this.openAiCompatibleOptions,
geminiOptions: this.geminiOptions,
qdrantUrl: this.qdrantUrl,
qdrantApiKey: this.qdrantApiKey,
searchMinScore: this.currentSearchMinScore,
@ -166,6 +175,10 @@ export class CodeIndexConfigManager {
const apiKey = this.openAiCompatibleOptions?.apiKey
const qdrantUrl = this.qdrantUrl
return !!(baseUrl && apiKey && qdrantUrl)
} else if (this.embedderProvider === "gemini") {
const apiKey = this.geminiOptions?.apiKey
const qdrantUrl = this.qdrantUrl
return !!(apiKey && qdrantUrl)
}
return false // Should not happen if embedderProvider is always set correctly
}
@ -186,6 +199,7 @@ export class CodeIndexConfigManager {
const prevOpenAiCompatibleBaseUrl = prev?.openAiCompatibleBaseUrl ?? ""
const prevOpenAiCompatibleApiKey = prev?.openAiCompatibleApiKey ?? ""
const prevOpenAiCompatibleModelDimension = prev?.openAiCompatibleModelDimension
const prevGeminiApiKey = prev?.geminiApiKey ?? ""
const prevQdrantUrl = prev?.qdrantUrl ?? ""
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
@ -243,6 +257,13 @@ export class CodeIndexConfigManager {
}
}
if (this.embedderProvider === "gemini") {
const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
if (prevGeminiApiKey !== currentGeminiApiKey) {
return true
}
}
// Qdrant configuration changes
const currentQdrantUrl = this.qdrantUrl ?? ""
const currentQdrantApiKey = this.qdrantApiKey ?? ""
@ -293,6 +314,7 @@ export class CodeIndexConfigManager {
openAiOptions: this.openAiOptions,
ollamaOptions: this.ollamaOptions,
openAiCompatibleOptions: this.openAiCompatibleOptions,
geminiOptions: this.geminiOptions,
qdrantUrl: this.qdrantUrl,
qdrantApiKey: this.qdrantApiKey,
searchMinScore: this.currentSearchMinScore,

View file

@ -23,3 +23,6 @@ export const PARSING_CONCURRENCY = 10
export const MAX_BATCH_TOKENS = 100000
export const MAX_ITEM_TOKENS = 8191
export const BATCH_PROCESSING_CONCURRENCY = 10
/**Gemini Embedder */
export const GEMINI_MAX_ITEM_TOKENS = 2048

View file

@ -0,0 +1,58 @@
import { vitest, describe, it, expect, beforeEach } from "vitest"
import type { MockedClass } from "vitest"
import { GeminiEmbedder } from "../gemini"
import { OpenAICompatibleEmbedder } from "../openai-compatible"
// Mock the OpenAICompatibleEmbedder
vitest.mock("../openai-compatible")
const MockedOpenAICompatibleEmbedder = OpenAICompatibleEmbedder as MockedClass<typeof OpenAICompatibleEmbedder>
describe("GeminiEmbedder", () => {
let embedder: GeminiEmbedder
beforeEach(() => {
vitest.clearAllMocks()
})
describe("constructor", () => {
it("should create an instance with correct fixed values passed to OpenAICompatibleEmbedder", () => {
// Arrange
const apiKey = "test-gemini-api-key"
// Act
embedder = new GeminiEmbedder(apiKey)
// Assert
expect(MockedOpenAICompatibleEmbedder).toHaveBeenCalledWith(
"https://generativelanguage.googleapis.com/v1beta/openai/",
apiKey,
"text-embedding-004",
2048,
)
})
it("should throw error when API key is not provided", () => {
// Act & Assert
expect(() => new GeminiEmbedder("")).toThrow("API key is required for Gemini embedder")
expect(() => new GeminiEmbedder(null as any)).toThrow("API key is required for Gemini embedder")
expect(() => new GeminiEmbedder(undefined as any)).toThrow("API key is required for Gemini embedder")
})
})
describe("embedderInfo", () => {
it("should return correct embedder info with dimension 768", () => {
// Arrange
embedder = new GeminiEmbedder("test-api-key")
// Act
const info = embedder.embedderInfo
// Assert
expect(info).toEqual({
name: "gemini",
})
expect(GeminiEmbedder.dimension).toBe(768)
})
})
})

View file

@ -0,0 +1,64 @@
import { OpenAICompatibleEmbedder } from "./openai-compatible"
import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder"
import { GEMINI_MAX_ITEM_TOKENS } from "../constants"
/**
* Gemini embedder implementation that wraps the OpenAI Compatible embedder
* with fixed configuration for Google's Gemini embedding API.
*
* Fixed values:
* - Base URL: https://generativelanguage.googleapis.com/v1beta/openai/
* - Model: text-embedding-004
* - Dimension: 768
*/
export class GeminiEmbedder implements IEmbedder {
private readonly openAICompatibleEmbedder: OpenAICompatibleEmbedder
private static readonly GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
private static readonly GEMINI_MODEL = "text-embedding-004"
private static readonly GEMINI_DIMENSION = 768
/**
* Creates a new Gemini embedder
* @param apiKey The Gemini API key for authentication
*/
constructor(apiKey: string) {
if (!apiKey) {
throw new Error("API key is required for Gemini embedder")
}
// Create an OpenAI Compatible embedder with Gemini's fixed configuration
this.openAICompatibleEmbedder = new OpenAICompatibleEmbedder(
GeminiEmbedder.GEMINI_BASE_URL,
apiKey,
GeminiEmbedder.GEMINI_MODEL,
GEMINI_MAX_ITEM_TOKENS,
)
}
/**
* Creates embeddings for the given texts using Gemini's embedding API
* @param texts Array of text strings to embed
* @param model Optional model identifier (ignored - always uses text-embedding-004)
* @returns Promise resolving to embedding response
*/
async createEmbeddings(texts: string[], model?: string): Promise<EmbeddingResponse> {
// Always use the fixed Gemini model, ignoring any passed model parameter
return this.openAICompatibleEmbedder.createEmbeddings(texts, GeminiEmbedder.GEMINI_MODEL)
}
/**
* Returns information about this embedder
*/
get embedderInfo(): EmbedderInfo {
return {
name: "gemini",
}
}
/**
* Gets the fixed dimension for Gemini embeddings
*/
static get dimension(): number {
return GeminiEmbedder.GEMINI_DIMENSION
}
}

View file

@ -29,14 +29,16 @@ interface OpenAIEmbeddingResponse {
export class OpenAICompatibleEmbedder implements IEmbedder {
private embeddingsClient: OpenAI
private readonly defaultModelId: string
private readonly maxItemTokens: number
/**
* Creates a new OpenAI Compatible embedder
* @param baseUrl The base URL for the OpenAI-compatible API endpoint
* @param apiKey The API key for authentication
* @param modelId Optional model identifier (defaults to "text-embedding-3-small")
* @param maxItemTokens Optional maximum tokens per item (defaults to MAX_ITEM_TOKENS)
*/
constructor(baseUrl: string, apiKey: string, modelId?: string) {
constructor(baseUrl: string, apiKey: string, modelId?: string, maxItemTokens?: number) {
if (!baseUrl) {
throw new Error("Base URL is required for OpenAI Compatible embedder")
}
@ -49,6 +51,7 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
apiKey: apiKey,
})
this.defaultModelId = modelId || getDefaultModelId("openai-compatible")
this.maxItemTokens = maxItemTokens || MAX_ITEM_TOKENS
}
/**
@ -98,12 +101,12 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
const text = remainingTexts[i]
const itemTokens = Math.ceil(text.length / 4)
if (itemTokens > MAX_ITEM_TOKENS) {
if (itemTokens > this.maxItemTokens) {
console.warn(
t("embeddings:textExceedsTokenLimit", {
index: i,
itemTokens,
maxTokens: MAX_ITEM_TOKENS,
maxTokens: this.maxItemTokens,
}),
)
processedIndices.push(i)

View file

@ -12,6 +12,7 @@ export interface CodeIndexConfig {
openAiOptions?: ApiHandlerOptions
ollamaOptions?: ApiHandlerOptions
openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
geminiOptions?: { apiKey: string }
qdrantUrl?: string
qdrantApiKey?: string
searchMinScore?: number
@ -30,6 +31,7 @@ export type PreviousConfigSnapshot = {
openAiCompatibleBaseUrl?: string
openAiCompatibleApiKey?: string
openAiCompatibleModelDimension?: number
geminiApiKey?: string
qdrantUrl?: string
qdrantApiKey?: string
}

View file

@ -21,7 +21,7 @@ export interface EmbeddingResponse {
}
}
export type AvailableEmbedders = "openai" | "ollama" | "openai-compatible"
export type AvailableEmbedders = "openai" | "ollama" | "openai-compatible" | "gemini"
export interface EmbedderInfo {
name: AvailableEmbedders

View file

@ -70,7 +70,7 @@ export interface ICodeIndexManager {
}
export type IndexingState = "Standby" | "Indexing" | "Indexed" | "Error"
export type EmbedderProvider = "openai" | "ollama" | "openai-compatible"
export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini"
export interface IndexProgressUpdate {
systemStatus: IndexingState

View file

@ -2,6 +2,7 @@ import * as vscode from "vscode"
import { OpenAiEmbedder } from "./embedders/openai"
import { CodeIndexOllamaEmbedder } from "./embedders/ollama"
import { OpenAICompatibleEmbedder } from "./embedders/openai-compatible"
import { GeminiEmbedder } from "./embedders/gemini"
import { EmbedderProvider, getDefaultModelId, getModelDimension } from "../../shared/embeddingModels"
import { QdrantVectorStore } from "./vector-store/qdrant-client"
import { codeParser, DirectoryScanner, FileWatcher } from "./processors"
@ -53,6 +54,11 @@ export class CodeIndexServiceFactory {
config.openAiCompatibleOptions.apiKey,
config.modelId,
)
} else if (provider === "gemini") {
if (!config.geminiOptions?.apiKey) {
throw new Error("Gemini configuration missing for embedder creation")
}
return new GeminiEmbedder(config.geminiOptions.apiKey)
}
throw new Error(`Invalid embedder type configured: ${config.embedderProvider}`)
@ -78,6 +84,9 @@ export class CodeIndexServiceFactory {
// Fallback if not provided or invalid in openAiCompatibleOptions
vectorSize = getModelDimension(provider, modelId)
}
} else if (provider === "gemini") {
// Gemini's text-embedding-004 has a fixed dimension of 768
vectorSize = 768
} else {
vectorSize = getModelDimension(provider, modelId)
}

View file

@ -2,7 +2,7 @@
* Defines profiles for different embedding models, including their dimensions.
*/
export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" // Add other providers as needed
export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" // Add other providers as needed
export interface EmbeddingModelProfile {
dimension: number
@ -46,6 +46,9 @@ export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = {
queryPrefix: "Represent this query for searching relevant code: ",
},
},
gemini: {
"text-embedding-004": { dimension: 768 },
},
}
/**
@ -129,6 +132,10 @@ export function getDefaultModelId(provider: EmbedderProvider): string {
// Return a placeholder or throw an error, depending on desired behavior
return "unknown-default" // Placeholder specific model ID
}
case "gemini":
return "text-embedding-004"
default:
// Fallback for unknown providers
console.warn(`Unknown provider for default model ID: ${provider}. Falling back to OpenAI default.`)

View file

@ -71,9 +71,9 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
// Safely calculate available models for current provider
const currentProvider = codebaseIndexConfig?.codebaseIndexEmbedderProvider
const modelsForProvider =
currentProvider === "openai" || currentProvider === "ollama" || currentProvider === "openai-compatible"
? codebaseIndexModels?.[currentProvider] || codebaseIndexModels?.openai
: codebaseIndexModels?.openai
currentProvider === "openai" || currentProvider === "openai-compatible"
? (codebaseIndexModels?.openai ?? codebaseIndexModels?.["openai-compatible"])
: codebaseIndexModels?.[currentProvider as keyof typeof codebaseIndexModels]
const availableModelIds = Object.keys(modelsForProvider || {})
useEffect(() => {
@ -154,6 +154,10 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
.positive("Dimension must be a positive number")
.optional(),
}),
gemini: baseSchema.extend({
codebaseIndexEmbedderProvider: z.literal("gemini"),
codebaseIndexGeminiApiKey: z.string().min(1, "Gemini API key is required"),
}),
}
try {
@ -162,7 +166,9 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
? providerSchemas.openai
: config.codebaseIndexEmbedderProvider === "ollama"
? providerSchemas.ollama
: providerSchemas["openai-compatible"]
: config.codebaseIndexEmbedderProvider === "gemini"
? providerSchemas.gemini
: providerSchemas["openai-compatible"]
schema.parse({
...config,
@ -170,6 +176,7 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
codebaseIndexOpenAiCompatibleBaseUrl: apiConfig.codebaseIndexOpenAiCompatibleBaseUrl,
codebaseIndexOpenAiCompatibleApiKey: apiConfig.codebaseIndexOpenAiCompatibleApiKey,
codebaseIndexOpenAiCompatibleModelDimension: apiConfig.codebaseIndexOpenAiCompatibleModelDimension,
codebaseIndexGeminiApiKey: apiConfig.codebaseIndexGeminiApiKey,
})
return true
} catch {
@ -284,6 +291,7 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
<SelectItem value="openai-compatible">
{t("settings:codeIndex.openaiCompatibleProvider")}
</SelectItem>
<SelectItem value="gemini">{t("settings:codeIndex.geminiProvider")}</SelectItem>
</SelectContent>
</Select>
</div>
@ -428,6 +436,24 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
</div>
)}
{codebaseIndexConfig?.codebaseIndexEmbedderProvider === "gemini" && (
<div className="flex flex-col gap-3">
<div className="flex items-center gap-4 font-bold">
<div>{t("settings:codeIndex.geminiApiKeyLabel")}</div>
</div>
<div>
<VSCodeTextField
type="password"
value={apiConfiguration.codebaseIndexGeminiApiKey || ""}
onInput={(e: any) =>
setApiConfigurationField("codebaseIndexGeminiApiKey", e.target.value)
}
placeholder={t("settings:codeIndex.geminiApiKeyPlaceholder")}
style={{ width: "100%" }}></VSCodeTextField>
</div>
</div>
)}
<div className="flex flex-col gap-3">
<div className="flex items-center gap-4 font-bold">
<div>{t("settings:codeIndex.qdrantUrlLabel")}</div>

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Seleccionar proveïdor",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Clau API:",
"geminiApiKeyPlaceholder": "Introduïu la vostra clau d'API de Gemini",
"openaiCompatibleProvider": "Compatible amb OpenAI",
"openaiCompatibleBaseUrlLabel": "URL base:",
"openaiCompatibleApiKeyLabel": "Clau API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Anbieter auswählen",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API-Schlüssel:",
"geminiApiKeyPlaceholder": "Geben Sie Ihren Gemini-API-Schlüssel ein",
"openaiCompatibleProvider": "OpenAI-kompatibel",
"openaiCompatibleBaseUrlLabel": "Basis-URL:",
"openaiCompatibleApiKeyLabel": "API-Schlüssel:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Select provider",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API Key:",
"geminiApiKeyPlaceholder": "Enter your Gemini API key",
"openaiCompatibleProvider": "OpenAI Compatible",
"openaiKeyLabel": "OpenAI Key:",
"openaiCompatibleBaseUrlLabel": "Base URL:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Seleccionar proveedor",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Clave API:",
"geminiApiKeyPlaceholder": "Introduce tu clave de API de Gemini",
"openaiCompatibleProvider": "Compatible con OpenAI",
"openaiCompatibleBaseUrlLabel": "URL base:",
"openaiCompatibleApiKeyLabel": "Clave API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Sélectionner un fournisseur",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Clé API :",
"geminiApiKeyPlaceholder": "Entrez votre clé API Gemini",
"openaiCompatibleProvider": "Compatible OpenAI",
"openaiCompatibleBaseUrlLabel": "URL de base :",
"openaiCompatibleApiKeyLabel": "Clé API :",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "प्रदाता चुनें",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API कुंजी:",
"geminiApiKeyPlaceholder": "अपना जेमिनी एपीआई कुंजी दर्ज करें",
"openaiCompatibleProvider": "OpenAI संगत",
"openaiCompatibleBaseUrlLabel": "आधार URL:",
"openaiCompatibleApiKeyLabel": "API कुंजी:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Pilih provider",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API Key:",
"geminiApiKeyPlaceholder": "Masukkan kunci API Gemini Anda",
"openaiCompatibleProvider": "OpenAI Compatible",
"openaiKeyLabel": "OpenAI Key:",
"openaiCompatibleBaseUrlLabel": "Base URL:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Seleziona fornitore",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Chiave API:",
"geminiApiKeyPlaceholder": "Inserisci la tua chiave API Gemini",
"openaiCompatibleProvider": "Compatibile con OpenAI",
"openaiCompatibleBaseUrlLabel": "URL di base:",
"openaiCompatibleApiKeyLabel": "Chiave API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "プロバイダーを選択",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "APIキー",
"geminiApiKeyPlaceholder": "Gemini APIキーを入力してください",
"openaiCompatibleProvider": "OpenAI互換",
"openaiCompatibleBaseUrlLabel": "ベースURL",
"openaiCompatibleApiKeyLabel": "APIキー",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "제공자 선택",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API 키:",
"geminiApiKeyPlaceholder": "Gemini API 키를 입력하세요",
"openaiCompatibleProvider": "OpenAI 호환",
"openaiCompatibleBaseUrlLabel": "기본 URL:",
"openaiCompatibleApiKeyLabel": "API 키:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Selecteer provider",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API-sleutel:",
"geminiApiKeyPlaceholder": "Voer uw Gemini API-sleutel in",
"openaiCompatibleProvider": "OpenAI-compatibel",
"openaiCompatibleBaseUrlLabel": "Basis-URL:",
"openaiCompatibleApiKeyLabel": "API-sleutel:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Wybierz dostawcę",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Klucz API:",
"geminiApiKeyPlaceholder": "Wprowadź swój klucz API Gemini",
"openaiCompatibleProvider": "Kompatybilny z OpenAI",
"openaiCompatibleBaseUrlLabel": "Bazowy URL:",
"openaiCompatibleApiKeyLabel": "Klucz API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Selecionar provedor",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Chave de API:",
"geminiApiKeyPlaceholder": "Digite sua chave de API do Gemini",
"openaiCompatibleProvider": "Compatível com OpenAI",
"openaiCompatibleBaseUrlLabel": "URL Base:",
"openaiCompatibleApiKeyLabel": "Chave de API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Выберите провайдера",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Ключ API:",
"geminiApiKeyPlaceholder": "Введите свой API-ключ Gemini",
"openaiCompatibleProvider": "OpenAI-совместимый",
"openaiCompatibleBaseUrlLabel": "Базовый URL:",
"openaiCompatibleApiKeyLabel": "Ключ API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Sağlayıcı seç",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API Anahtarı:",
"geminiApiKeyPlaceholder": "Gemini API anahtarınızı girin",
"openaiCompatibleProvider": "OpenAI Uyumlu",
"openaiCompatibleBaseUrlLabel": "Temel URL:",
"openaiCompatibleApiKeyLabel": "API Anahtarı:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "Chọn nhà cung cấp",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "Khóa API:",
"geminiApiKeyPlaceholder": "Nhập khóa API Gemini của bạn",
"openaiCompatibleProvider": "Tương thích OpenAI",
"openaiCompatibleBaseUrlLabel": "URL cơ sở:",
"openaiCompatibleApiKeyLabel": "Khóa API:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "选择提供商",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API 密钥:",
"geminiApiKeyPlaceholder": "输入您的Gemini API密钥",
"openaiCompatibleProvider": "OpenAI 兼容",
"openaiCompatibleBaseUrlLabel": "基础 URL",
"openaiCompatibleApiKeyLabel": "API 密钥:",

View file

@ -44,6 +44,9 @@
"selectProviderPlaceholder": "選擇提供者",
"openaiProvider": "OpenAI",
"ollamaProvider": "Ollama",
"geminiProvider": "Gemini",
"geminiApiKeyLabel": "API 金鑰:",
"geminiApiKeyPlaceholder": "輸入您的Gemini API金鑰",
"openaiCompatibleProvider": "OpenAI 相容",
"openaiCompatibleBaseUrlLabel": "基礎 URL",
"openaiCompatibleApiKeyLabel": "API 金鑰:",