mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-10 22:41:14 +00:00
feat: Add API version support for Azure OpenAI in codebase indexing
- Add optional apiVersion field to OpenAI Compatible provider schema - Update config manager to handle API version parameter - Modify OpenAICompatibleEmbedder to append api-version query parameter - Add API version input field to settings UI with validation - Include comprehensive test coverage for API version functionality - Add i18n support for new API version field Fixes #5212: Azure OpenAI embedding requests now include required api-version parameter
This commit is contained in:
parent
3a8ba27615
commit
5ab3ae8f3a
8 changed files with 79 additions and 6 deletions
|
|
@ -36,6 +36,7 @@ export const codebaseIndexProviderSchema = z.object({
|
|||
codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(),
|
||||
codebaseIndexOpenAiCompatibleApiKey: z.string().optional(),
|
||||
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
|
||||
codebaseIndexOpenAiCompatibleApiVersion: z.string().optional(),
|
||||
})
|
||||
|
||||
export type CodebaseIndexProvider = z.infer<typeof codebaseIndexProviderSchema>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class CodeIndexConfigManager {
|
|||
private modelId?: string
|
||||
private openAiOptions?: ApiHandlerOptions
|
||||
private ollamaOptions?: ApiHandlerOptions
|
||||
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
|
||||
private openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number; apiVersion?: string }
|
||||
private qdrantUrl?: string = "http://localhost:6333"
|
||||
private qdrantApiKey?: string
|
||||
private searchMinScore?: number
|
||||
|
|
@ -55,6 +55,8 @@ export class CodeIndexConfigManager {
|
|||
const openAiCompatibleModelDimension = this.contextProxy?.getGlobalState(
|
||||
"codebaseIndexOpenAiCompatibleModelDimension",
|
||||
) as number | undefined
|
||||
const openAiCompatibleApiVersion =
|
||||
this.contextProxy?.getGlobalState("codebaseIndexOpenAiCompatibleApiVersion") ?? ""
|
||||
|
||||
// Update instance variables with configuration
|
||||
this.isEnabled = codebaseIndexEnabled || false
|
||||
|
|
@ -84,6 +86,7 @@ export class CodeIndexConfigManager {
|
|||
baseUrl: openAiCompatibleBaseUrl,
|
||||
apiKey: openAiCompatibleApiKey,
|
||||
modelDimension: openAiCompatibleModelDimension,
|
||||
apiVersion: openAiCompatibleApiVersion || undefined,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
|
@ -100,7 +103,7 @@ export class CodeIndexConfigManager {
|
|||
modelId?: string
|
||||
openAiOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: ApiHandlerOptions
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string; apiVersion?: string }
|
||||
qdrantUrl?: string
|
||||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
|
|
@ -118,6 +121,7 @@ export class CodeIndexConfigManager {
|
|||
openAiCompatibleBaseUrl: this.openAiCompatibleOptions?.baseUrl ?? "",
|
||||
openAiCompatibleApiKey: this.openAiCompatibleOptions?.apiKey ?? "",
|
||||
openAiCompatibleModelDimension: this.openAiCompatibleOptions?.modelDimension,
|
||||
openAiCompatibleApiVersion: this.openAiCompatibleOptions?.apiVersion ?? "",
|
||||
qdrantUrl: this.qdrantUrl ?? "",
|
||||
qdrantApiKey: this.qdrantApiKey ?? "",
|
||||
}
|
||||
|
|
@ -185,6 +189,7 @@ export class CodeIndexConfigManager {
|
|||
const prevOpenAiCompatibleBaseUrl = prev?.openAiCompatibleBaseUrl ?? ""
|
||||
const prevOpenAiCompatibleApiKey = prev?.openAiCompatibleApiKey ?? ""
|
||||
const prevOpenAiCompatibleModelDimension = prev?.openAiCompatibleModelDimension
|
||||
const prevOpenAiCompatibleApiVersion = prev?.openAiCompatibleApiVersion ?? ""
|
||||
const prevQdrantUrl = prev?.qdrantUrl ?? ""
|
||||
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
|
||||
|
||||
|
|
@ -233,10 +238,12 @@ export class CodeIndexConfigManager {
|
|||
const currentOpenAiCompatibleBaseUrl = this.openAiCompatibleOptions?.baseUrl ?? ""
|
||||
const currentOpenAiCompatibleApiKey = this.openAiCompatibleOptions?.apiKey ?? ""
|
||||
const currentOpenAiCompatibleModelDimension = this.openAiCompatibleOptions?.modelDimension
|
||||
const currentOpenAiCompatibleApiVersion = this.openAiCompatibleOptions?.apiVersion ?? ""
|
||||
if (
|
||||
prevOpenAiCompatibleBaseUrl !== currentOpenAiCompatibleBaseUrl ||
|
||||
prevOpenAiCompatibleApiKey !== currentOpenAiCompatibleApiKey ||
|
||||
prevOpenAiCompatibleModelDimension !== currentOpenAiCompatibleModelDimension
|
||||
prevOpenAiCompatibleModelDimension !== currentOpenAiCompatibleModelDimension ||
|
||||
prevOpenAiCompatibleApiVersion !== currentOpenAiCompatibleApiVersion
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,39 @@ describe("OpenAICompatibleEmbedder", () => {
|
|||
"Base URL is required for OpenAI Compatible embedder",
|
||||
)
|
||||
})
|
||||
|
||||
it("should append api-version query parameter when provided", () => {
|
||||
const apiVersion = "2023-05-15"
|
||||
embedder = new OpenAICompatibleEmbedder(testBaseUrl, testApiKey, testModelId, apiVersion)
|
||||
|
||||
expect(MockedOpenAI).toHaveBeenCalledWith({
|
||||
baseURL: `${testBaseUrl}?api-version=${apiVersion}`,
|
||||
apiKey: testApiKey,
|
||||
})
|
||||
expect(embedder).toBeDefined()
|
||||
})
|
||||
|
||||
it("should handle api-version with existing query parameters", () => {
|
||||
const baseUrlWithParams = "https://api.example.com/v1?existing=param"
|
||||
const apiVersion = "2023-05-15"
|
||||
embedder = new OpenAICompatibleEmbedder(baseUrlWithParams, testApiKey, testModelId, apiVersion)
|
||||
|
||||
expect(MockedOpenAI).toHaveBeenCalledWith({
|
||||
baseURL: `${baseUrlWithParams}&api-version=${apiVersion}`,
|
||||
apiKey: testApiKey,
|
||||
})
|
||||
expect(embedder).toBeDefined()
|
||||
})
|
||||
|
||||
it("should not modify baseURL when api-version is not provided", () => {
|
||||
embedder = new OpenAICompatibleEmbedder(testBaseUrl, testApiKey, testModelId)
|
||||
|
||||
expect(MockedOpenAI).toHaveBeenCalledWith({
|
||||
baseURL: testBaseUrl,
|
||||
apiKey: testApiKey,
|
||||
})
|
||||
expect(embedder).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("embedderInfo", () => {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
|
|||
* @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 apiVersion Optional API version for Azure OpenAI compatibility
|
||||
*/
|
||||
constructor(baseUrl: string, apiKey: string, modelId?: string) {
|
||||
constructor(baseUrl: string, apiKey: string, modelId?: string, apiVersion?: string) {
|
||||
if (!baseUrl) {
|
||||
throw new Error("Base URL is required for OpenAI Compatible embedder")
|
||||
}
|
||||
|
|
@ -44,8 +45,15 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
|
|||
throw new Error("API key is required for OpenAI Compatible embedder")
|
||||
}
|
||||
|
||||
// For Azure OpenAI, we need to append the api-version query parameter to the baseURL
|
||||
let finalBaseUrl = baseUrl
|
||||
if (apiVersion) {
|
||||
const separator = baseUrl.includes("?") ? "&" : "?"
|
||||
finalBaseUrl = `${baseUrl}${separator}api-version=${apiVersion}`
|
||||
}
|
||||
|
||||
this.embeddingsClient = new OpenAI({
|
||||
baseURL: baseUrl,
|
||||
baseURL: finalBaseUrl,
|
||||
apiKey: apiKey,
|
||||
})
|
||||
this.defaultModelId = modelId || getDefaultModelId("openai-compatible")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export interface CodeIndexConfig {
|
|||
modelId?: string
|
||||
openAiOptions?: ApiHandlerOptions
|
||||
ollamaOptions?: ApiHandlerOptions
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number }
|
||||
openAiCompatibleOptions?: { baseUrl: string; apiKey: string; modelDimension?: number; apiVersion?: string }
|
||||
qdrantUrl?: string
|
||||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
|
|
@ -30,6 +30,7 @@ export type PreviousConfigSnapshot = {
|
|||
openAiCompatibleBaseUrl?: string
|
||||
openAiCompatibleApiKey?: string
|
||||
openAiCompatibleModelDimension?: number
|
||||
openAiCompatibleApiVersion?: string
|
||||
qdrantUrl?: string
|
||||
qdrantApiKey?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export class CodeIndexServiceFactory {
|
|||
config.openAiCompatibleOptions.baseUrl,
|
||||
config.openAiCompatibleOptions.apiKey,
|
||||
config.modelId,
|
||||
config.openAiCompatibleOptions.apiVersion,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
|
|||
.int("Dimension must be an integer")
|
||||
.positive("Dimension must be a positive number")
|
||||
.optional(),
|
||||
codebaseIndexOpenAiCompatibleApiVersion: z.string().optional(),
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +162,7 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
|
|||
codebaseIndexOpenAiCompatibleBaseUrl: apiConfig.codebaseIndexOpenAiCompatibleBaseUrl,
|
||||
codebaseIndexOpenAiCompatibleApiKey: apiConfig.codebaseIndexOpenAiCompatibleApiKey,
|
||||
codebaseIndexOpenAiCompatibleModelDimension: apiConfig.codebaseIndexOpenAiCompatibleModelDimension,
|
||||
codebaseIndexOpenAiCompatibleApiVersion: apiConfig.codebaseIndexOpenAiCompatibleApiVersion,
|
||||
})
|
||||
return true
|
||||
} catch {
|
||||
|
|
@ -320,6 +322,24 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
|
|||
}
|
||||
style={{ width: "100%" }}></VSCodeTextField>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 font-bold">
|
||||
<div>{t("settings:codeIndex.openaiCompatibleApiVersionLabel")}</div>
|
||||
</div>
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration.codebaseIndexOpenAiCompatibleApiVersion || ""}
|
||||
onInput={(e: any) =>
|
||||
setApiConfigurationField(
|
||||
"codebaseIndexOpenAiCompatibleApiVersion",
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
placeholder="2023-05-15"
|
||||
style={{ width: "100%" }}></VSCodeTextField>
|
||||
<p className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
{t("settings:codeIndex.openaiCompatibleApiVersionDescription")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
"openaiKeyLabel": "OpenAI Key:",
|
||||
"openaiCompatibleBaseUrlLabel": "Base URL:",
|
||||
"openaiCompatibleApiKeyLabel": "API Key:",
|
||||
"openaiCompatibleApiVersionLabel": "API Version:",
|
||||
"openaiCompatibleApiVersionDescription": "Required for Azure OpenAI. Common values: 2023-05-15, 2024-02-01, 2024-06-01. Leave empty for other OpenAI-compatible providers.",
|
||||
"openaiCompatibleModelDimensionLabel": "Embedding Dimension:",
|
||||
"openaiCompatibleModelDimensionPlaceholder": "e.g., 1536",
|
||||
"openaiCompatibleModelDimensionDescription": "The embedding dimension (output size) for your model. Check your provider's documentation for this value. Common values: 384, 768, 1536, 3072.",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue