Enhancement: Adds OpenRouter Base URL for OpenRouter embedder provider

- Adds UI with standard text input within CodeIndexPopover
- Modifies interface accordingly
- Adds UI locales for english (other languages to come)
- Updates the config-manager with boiler plate
- Update openrouter embedder code to use baseURL if available, otherwise use default.
This commit is contained in:
Toray Altas 2025-12-25 18:50:32 -05:00
parent a510223a09
commit bc0b5a49f6
5 changed files with 61 additions and 5 deletions

View file

@ -21,7 +21,7 @@ export class CodeIndexConfigManager {
private mistralOptions?: { apiKey: string }
private vercelAiGatewayOptions?: { apiKey: string }
private bedrockOptions?: { region: string; profile?: string }
private openRouterOptions?: { apiKey: string; specificProvider?: string }
private openRouterOptions?: { apiKey: string; specificProvider?: string; openRouterBaseUrl?: string }
private qdrantUrl?: string = "http://localhost:6333"
private qdrantApiKey?: string
private searchMinScore?: number
@ -142,7 +142,11 @@ export class CodeIndexConfigManager {
this.mistralOptions = mistralApiKey ? { apiKey: mistralApiKey } : undefined
this.vercelAiGatewayOptions = vercelAiGatewayApiKey ? { apiKey: vercelAiGatewayApiKey } : undefined
this.openRouterOptions = openRouterApiKey
? { apiKey: openRouterApiKey, specificProvider: openRouterSpecificProvider || undefined }
? {
apiKey: openRouterApiKey,
openRouterBaseUrl: codebaseIndexEmbedderBaseUrl,
specificProvider: openRouterSpecificProvider || undefined,
}
: undefined
// Set bedrockOptions if region is provided (profile is optional)
this.bedrockOptions = bedrockRegion
@ -167,7 +171,7 @@ export class CodeIndexConfigManager {
mistralOptions?: { apiKey: string }
vercelAiGatewayOptions?: { apiKey: string }
bedrockOptions?: { region: string; profile?: string }
openRouterOptions?: { apiKey: string }
openRouterOptions?: { apiKey: string; specificProvider?: string; openRouterBaseUrl?: string }
qdrantUrl?: string
qdrantApiKey?: string
searchMinScore?: number
@ -191,6 +195,7 @@ export class CodeIndexConfigManager {
bedrockRegion: this.bedrockOptions?.region ?? "",
bedrockProfile: this.bedrockOptions?.profile ?? "",
openRouterApiKey: this.openRouterOptions?.apiKey ?? "",
openRouterBaseUrl: this.openRouterOptions?.openRouterBaseUrl ?? "",
openRouterSpecificProvider: this.openRouterOptions?.specificProvider ?? "",
qdrantUrl: this.qdrantUrl ?? "",
qdrantApiKey: this.qdrantApiKey ?? "",
@ -269,6 +274,7 @@ export class CodeIndexConfigManager {
return isConfigured
} else if (this.embedderProvider === "openrouter") {
const apiKey = this.openRouterOptions?.apiKey
const openRouterBaseUrl = this.openRouterOptions?.openRouterBaseUrl
const qdrantUrl = this.qdrantUrl
const isConfigured = !!(apiKey && qdrantUrl)
return isConfigured
@ -310,6 +316,7 @@ export class CodeIndexConfigManager {
const prevBedrockRegion = prev?.bedrockRegion ?? ""
const prevBedrockProfile = prev?.bedrockProfile ?? ""
const prevOpenRouterApiKey = prev?.openRouterApiKey ?? ""
const prevOpenRouterBaseUrl = prev?.openRouterBaseUrl ?? ""
const prevOpenRouterSpecificProvider = prev?.openRouterSpecificProvider ?? ""
const prevQdrantUrl = prev?.qdrantUrl ?? ""
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
@ -353,6 +360,7 @@ export class CodeIndexConfigManager {
const currentBedrockProfile = this.bedrockOptions?.profile ?? ""
const currentOpenRouterApiKey = this.openRouterOptions?.apiKey ?? ""
const currentOpenRouterSpecificProvider = this.openRouterOptions?.specificProvider ?? ""
const currentOpenRouterBaseUrl = this.openRouterOptions?.openRouterBaseUrl ?? ""
const currentQdrantUrl = this.qdrantUrl ?? ""
const currentQdrantApiKey = this.qdrantApiKey ?? ""
@ -396,6 +404,10 @@ export class CodeIndexConfigManager {
return true
}
if (prevOpenRouterBaseUrl !== currentOpenRouterBaseUrl) {
return true
}
// Check for model dimension changes (generic for all providers)
if (prevModelDimension !== currentModelDimension) {
return true

View file

@ -58,13 +58,22 @@ export class OpenRouterEmbedder implements IEmbedder {
* @param apiKey The API key for authentication
* @param modelId Optional model identifier (defaults to "openai/text-embedding-3-large")
* @param maxItemTokens Optional maximum tokens per item (defaults to MAX_ITEM_TOKENS)
* @param openRouterBaseUrl Optional custom OpenRouter base URL
* @param specificProvider Optional specific provider to route requests to
*/
constructor(apiKey: string, modelId?: string, maxItemTokens?: number, specificProvider?: string) {
constructor(
apiKey: string,
modelId?: string,
maxItemTokens?: number,
openRouterBaseUrl?: string,
specificProvider?: string,
) {
if (!apiKey) {
throw new Error(t("embeddings:validation.apiKeyRequired"))
}
let baseUrl = openRouterBaseUrl || "https://openrouter.ai/api/v1"
this.apiKey = apiKey
// Only set specificProvider if it's not the default value
this.specificProvider =

View file

@ -16,7 +16,7 @@ export interface CodeIndexConfig {
mistralOptions?: { apiKey: string }
vercelAiGatewayOptions?: { apiKey: string }
bedrockOptions?: { region: string; profile?: string }
openRouterOptions?: { apiKey: string; specificProvider?: string }
openRouterOptions?: { apiKey: string; specificProvider?: string; openRouterBaseUrl?: string }
qdrantUrl?: string
qdrantApiKey?: string
searchMinScore?: number
@ -42,6 +42,7 @@ export type PreviousConfigSnapshot = {
bedrockRegion?: string
bedrockProfile?: string
openRouterApiKey?: string
openRouterBaseUrl?: string
openRouterSpecificProvider?: string
qdrantUrl?: string
qdrantApiKey?: string

View file

@ -53,6 +53,7 @@ import {
// Default URLs for providers
const DEFAULT_QDRANT_URL = "http://localhost:6333"
const DEFAULT_OLLAMA_URL = "http://localhost:11434"
const DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1"
interface CodeIndexPopoverProps {
children: React.ReactNode
@ -1321,6 +1322,37 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
{currentSettings.codebaseIndexEmbedderProvider === "openrouter" && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openRouterBaseUrlLabel")}
</label>
<VSCodeTextField
value={currentSettings.codebaseIndexEmbedderBaseUrl || ""}
onInput={(e: any) =>
updateSetting("codebaseIndexEmbedderBaseUrl", e.target.value)
}
onBlur={(e: any) => {
// Set default OpenRouter URL if field is empty
if (!e.target.value.trim()) {
e.target.value = DEFAULT_OPENROUTER_URL
updateSetting(
"codebaseIndexEmbedderBaseUrl",
DEFAULT_OPENROUTER_URL,
)
}
}}
placeholder={t("settings:codeIndex.openRouterUrlPlaceholder")}
className={cn("w-full", {
"border-red-500": formErrors.codebaseIndexEmbedderBaseUrl,
})}
/>
{formErrors.codebaseIndexEmbedderBaseUrl && (
<p className="text-xs text-vscode-errorForeground mt-1 mb-0">
{formErrors.codebaseIndexEmbedderBaseUrl}
</p>
)}
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
{t("settings:codeIndex.openRouterApiKeyLabel")}

View file

@ -106,6 +106,8 @@
"openRouterApiKeyPlaceholder": "Enter your OpenRouter API key",
"openRouterProviderRoutingLabel": "OpenRouter Provider Routing",
"openRouterProviderRoutingDescription": "OpenRouter routes requests to the best available providers for your embedding model. By default, requests are load balanced across the top providers to maximize uptime. However, you can choose a specific provider to use for this model.",
"openRouterBaseUrlLabel": "OpenRouter Base URL",
"openRouterUrlPlaceholder": "https://openrouter.ai/api/v1",
"openaiCompatibleProvider": "OpenAI Compatible",
"openAiKeyLabel": "OpenAI API Key",
"openAiKeyPlaceholder": "Enter your OpenAI API key",