Skip to content

Commit 768e553

Browse files
committed
feat: add Nebius AI as codebase indexing provider
- Add NebiusEmbedder class with rate limiting (600k TPM, 10k RPM) - Support Qwen/Qwen3-Embedding-8B model with 4096 dimensions - Implement proper rate limiting for tokens and requests per minute - Add comprehensive test coverage for the new embedder - Update types, config manager, and service factory to support Nebius - Cost-effective option at /bin/sh.01 per 1M tokens vs /bin/sh.13-0.18 for others Addresses #8589
1 parent bdc91b2 commit 768e553

File tree

12 files changed

+574
-4
lines changed

12 files changed

+574
-4
lines changed

packages/types/src/codebase-index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const codebaseIndexConfigSchema = z.object({
2222
codebaseIndexEnabled: z.boolean().optional(),
2323
codebaseIndexQdrantUrl: z.string().optional(),
2424
codebaseIndexEmbedderProvider: z
25-
.enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway"])
25+
.enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway", "nebius"])
2626
.optional(),
2727
codebaseIndexEmbedderBaseUrl: z.string().optional(),
2828
codebaseIndexEmbedderModelId: z.string().optional(),
@@ -51,6 +51,7 @@ export const codebaseIndexModelsSchema = z.object({
5151
gemini: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
5252
mistral: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
5353
"vercel-ai-gateway": z.record(z.string(), z.object({ dimension: z.number() })).optional(),
54+
nebius: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
5455
})
5556

5657
export type CodebaseIndexModels = z.infer<typeof codebaseIndexModelsSchema>
@@ -68,6 +69,7 @@ export const codebaseIndexProviderSchema = z.object({
6869
codebaseIndexGeminiApiKey: z.string().optional(),
6970
codebaseIndexMistralApiKey: z.string().optional(),
7071
codebaseIndexVercelAiGatewayApiKey: z.string().optional(),
72+
codebaseIndexNebiusApiKey: z.string().optional(),
7173
})
7274

7375
export type CodebaseIndexProvider = z.infer<typeof codebaseIndexProviderSchema>

packages/types/src/global-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export const SECRET_STATE_KEYS = [
199199
"codebaseIndexGeminiApiKey",
200200
"codebaseIndexMistralApiKey",
201201
"codebaseIndexVercelAiGatewayApiKey",
202+
"codebaseIndexNebiusApiKey",
202203
"huggingFaceApiKey",
203204
"sambaNovaApiKey",
204205
"zaiApiKey",

src/core/webview/webviewMessageHandler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,12 @@ export const webviewMessageHandler = async (
24932493
settings.codebaseIndexVercelAiGatewayApiKey,
24942494
)
24952495
}
2496+
if (settings.codebaseIndexNebiusApiKey !== undefined) {
2497+
await provider.contextProxy.storeSecret(
2498+
"codebaseIndexNebiusApiKey",
2499+
settings.codebaseIndexNebiusApiKey,
2500+
)
2501+
}
24962502

24972503
// Send success response first - settings are saved regardless of validation
24982504
await provider.postMessageToWebview({
@@ -2630,6 +2636,7 @@ export const webviewMessageHandler = async (
26302636
const hasVercelAiGatewayApiKey = !!(await provider.context.secrets.get(
26312637
"codebaseIndexVercelAiGatewayApiKey",
26322638
))
2639+
const hasNebiusApiKey = !!(await provider.context.secrets.get("codebaseIndexNebiusApiKey"))
26332640

26342641
provider.postMessageToWebview({
26352642
type: "codeIndexSecretStatus",
@@ -2640,6 +2647,7 @@ export const webviewMessageHandler = async (
26402647
hasGeminiApiKey,
26412648
hasMistralApiKey,
26422649
hasVercelAiGatewayApiKey,
2650+
hasNebiusApiKey,
26432651
},
26442652
})
26452653
break

src/services/code-index/config-manager.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class CodeIndexConfigManager {
2020
private geminiOptions?: { apiKey: string }
2121
private mistralOptions?: { apiKey: string }
2222
private vercelAiGatewayOptions?: { apiKey: string }
23+
private nebiusOptions?: { apiKey: string }
2324
private qdrantUrl?: string = "http://localhost:6333"
2425
private qdrantApiKey?: string
2526
private searchMinScore?: number
@@ -71,6 +72,7 @@ export class CodeIndexConfigManager {
7172
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
7273
const mistralApiKey = this.contextProxy?.getSecret("codebaseIndexMistralApiKey") ?? ""
7374
const vercelAiGatewayApiKey = this.contextProxy?.getSecret("codebaseIndexVercelAiGatewayApiKey") ?? ""
75+
const nebiusApiKey = this.contextProxy?.getSecret("codebaseIndexNebiusApiKey") ?? ""
7476

7577
// Update instance variables with configuration
7678
this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
@@ -108,6 +110,8 @@ export class CodeIndexConfigManager {
108110
this.embedderProvider = "mistral"
109111
} else if (codebaseIndexEmbedderProvider === "vercel-ai-gateway") {
110112
this.embedderProvider = "vercel-ai-gateway"
113+
} else if (codebaseIndexEmbedderProvider === "nebius") {
114+
this.embedderProvider = "nebius"
111115
} else {
112116
this.embedderProvider = "openai"
113117
}
@@ -129,6 +133,7 @@ export class CodeIndexConfigManager {
129133
this.geminiOptions = geminiApiKey ? { apiKey: geminiApiKey } : undefined
130134
this.mistralOptions = mistralApiKey ? { apiKey: mistralApiKey } : undefined
131135
this.vercelAiGatewayOptions = vercelAiGatewayApiKey ? { apiKey: vercelAiGatewayApiKey } : undefined
136+
this.nebiusOptions = nebiusApiKey ? { apiKey: nebiusApiKey } : undefined
132137
}
133138

134139
/**
@@ -147,6 +152,7 @@ export class CodeIndexConfigManager {
147152
geminiOptions?: { apiKey: string }
148153
mistralOptions?: { apiKey: string }
149154
vercelAiGatewayOptions?: { apiKey: string }
155+
nebiusOptions?: { apiKey: string }
150156
qdrantUrl?: string
151157
qdrantApiKey?: string
152158
searchMinScore?: number
@@ -167,6 +173,7 @@ export class CodeIndexConfigManager {
167173
geminiApiKey: this.geminiOptions?.apiKey ?? "",
168174
mistralApiKey: this.mistralOptions?.apiKey ?? "",
169175
vercelAiGatewayApiKey: this.vercelAiGatewayOptions?.apiKey ?? "",
176+
nebiusApiKey: this.nebiusOptions?.apiKey ?? "",
170177
qdrantUrl: this.qdrantUrl ?? "",
171178
qdrantApiKey: this.qdrantApiKey ?? "",
172179
}
@@ -192,6 +199,7 @@ export class CodeIndexConfigManager {
192199
geminiOptions: this.geminiOptions,
193200
mistralOptions: this.mistralOptions,
194201
vercelAiGatewayOptions: this.vercelAiGatewayOptions,
202+
nebiusOptions: this.nebiusOptions,
195203
qdrantUrl: this.qdrantUrl,
196204
qdrantApiKey: this.qdrantApiKey,
197205
searchMinScore: this.currentSearchMinScore,
@@ -234,6 +242,11 @@ export class CodeIndexConfigManager {
234242
const qdrantUrl = this.qdrantUrl
235243
const isConfigured = !!(apiKey && qdrantUrl)
236244
return isConfigured
245+
} else if (this.embedderProvider === "nebius") {
246+
const apiKey = this.nebiusOptions?.apiKey
247+
const qdrantUrl = this.qdrantUrl
248+
const isConfigured = !!(apiKey && qdrantUrl)
249+
return isConfigured
237250
}
238251
return false // Should not happen if embedderProvider is always set correctly
239252
}
@@ -269,6 +282,7 @@ export class CodeIndexConfigManager {
269282
const prevGeminiApiKey = prev?.geminiApiKey ?? ""
270283
const prevMistralApiKey = prev?.mistralApiKey ?? ""
271284
const prevVercelAiGatewayApiKey = prev?.vercelAiGatewayApiKey ?? ""
285+
const prevNebiusApiKey = prev?.nebiusApiKey ?? ""
272286
const prevQdrantUrl = prev?.qdrantUrl ?? ""
273287
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
274288

@@ -307,6 +321,7 @@ export class CodeIndexConfigManager {
307321
const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
308322
const currentMistralApiKey = this.mistralOptions?.apiKey ?? ""
309323
const currentVercelAiGatewayApiKey = this.vercelAiGatewayOptions?.apiKey ?? ""
324+
const currentNebiusApiKey = this.nebiusOptions?.apiKey ?? ""
310325
const currentQdrantUrl = this.qdrantUrl ?? ""
311326
const currentQdrantApiKey = this.qdrantApiKey ?? ""
312327

@@ -337,6 +352,10 @@ export class CodeIndexConfigManager {
337352
return true
338353
}
339354

355+
if (prevNebiusApiKey !== currentNebiusApiKey) {
356+
return true
357+
}
358+
340359
// Check for model dimension changes (generic for all providers)
341360
if (prevModelDimension !== currentModelDimension) {
342361
return true
@@ -395,6 +414,7 @@ export class CodeIndexConfigManager {
395414
geminiOptions: this.geminiOptions,
396415
mistralOptions: this.mistralOptions,
397416
vercelAiGatewayOptions: this.vercelAiGatewayOptions,
417+
nebiusOptions: this.nebiusOptions,
398418
qdrantUrl: this.qdrantUrl,
399419
qdrantApiKey: this.qdrantApiKey,
400420
searchMinScore: this.currentSearchMinScore,

0 commit comments

Comments
 (0)