mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
refactor(code-index): move default model ID logic to embedders
This commit is contained in:
parent
91cba934f7
commit
514eaa96da
5 changed files with 319 additions and 63 deletions
|
|
@ -584,4 +584,189 @@ describe("CodeIndexServiceFactory", () => {
|
|||
expect(() => factory.createVectorStore()).toThrow("Qdrant URL missing for vector store creation")
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateEmbedderConfig", () => {
|
||||
beforeEach(() => {
|
||||
vitest.clearAllMocks()
|
||||
// Mock the static validation methods
|
||||
MockedOpenAiEmbedder.validateEndpoint = vitest.fn().mockResolvedValue(true)
|
||||
MockedCodeIndexOllamaEmbedder.validateEndpoint = vitest.fn().mockResolvedValue(true)
|
||||
MockedOpenAICompatibleEmbedder.validateEndpoint = vitest.fn().mockResolvedValue(true)
|
||||
})
|
||||
|
||||
it("should validate OpenAI configuration with provided config", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-large",
|
||||
openAiOptions: {
|
||||
openAiNativeApiKey: "test-api-key",
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
const result = await factory.validateEmbedderConfig(providedConfig)
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true)
|
||||
expect(MockedOpenAiEmbedder.validateEndpoint).toHaveBeenCalledWith("test-api-key", "text-embedding-3-large")
|
||||
})
|
||||
|
||||
it("should validate Ollama configuration with provided config", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "ollama",
|
||||
modelId: "nomic-embed-text:latest",
|
||||
ollamaOptions: {
|
||||
ollamaBaseUrl: "http://localhost:11434",
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
const result = await factory.validateEmbedderConfig(providedConfig)
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true)
|
||||
expect(MockedCodeIndexOllamaEmbedder.validateEndpoint).toHaveBeenCalledWith(
|
||||
"http://localhost:11434",
|
||||
"nomic-embed-text:latest",
|
||||
)
|
||||
})
|
||||
|
||||
it("should validate OpenAI-compatible configuration with provided config", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
openAiCompatibleOptions: {
|
||||
baseUrl: "https://api.example.com/v1",
|
||||
apiKey: "test-api-key",
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
const result = await factory.validateEmbedderConfig(providedConfig)
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true)
|
||||
expect(MockedOpenAICompatibleEmbedder.validateEndpoint).toHaveBeenCalledWith(
|
||||
"https://api.example.com/v1",
|
||||
"test-api-key",
|
||||
"custom-model",
|
||||
)
|
||||
})
|
||||
|
||||
it("should use current config when no config is provided", async () => {
|
||||
// Arrange
|
||||
const currentConfig = {
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-small",
|
||||
openAiOptions: {
|
||||
openAiNativeApiKey: "current-api-key",
|
||||
},
|
||||
}
|
||||
mockConfigManager.getConfig.mockReturnValue(currentConfig as any)
|
||||
|
||||
// Act
|
||||
const result = await factory.validateEmbedderConfig()
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true)
|
||||
expect(mockConfigManager.getConfig).toHaveBeenCalled()
|
||||
expect(MockedOpenAiEmbedder.validateEndpoint).toHaveBeenCalledWith(
|
||||
"current-api-key",
|
||||
"text-embedding-3-small",
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw error for missing OpenAI API key", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-large",
|
||||
openAiOptions: {
|
||||
openAiNativeApiKey: undefined,
|
||||
},
|
||||
}
|
||||
|
||||
// Act & Assert
|
||||
await expect(factory.validateEmbedderConfig(providedConfig)).rejects.toThrow("OpenAI API key is required")
|
||||
})
|
||||
|
||||
it("should throw error for missing Ollama base URL", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "ollama",
|
||||
modelId: "nomic-embed-text:latest",
|
||||
ollamaOptions: {
|
||||
ollamaBaseUrl: undefined,
|
||||
},
|
||||
}
|
||||
|
||||
// Act & Assert
|
||||
await expect(factory.validateEmbedderConfig(providedConfig)).rejects.toThrow("Ollama base URL is required")
|
||||
})
|
||||
|
||||
it("should throw error for missing OpenAI-compatible credentials", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
openAiCompatibleOptions: {
|
||||
baseUrl: undefined,
|
||||
apiKey: "test-api-key",
|
||||
},
|
||||
}
|
||||
|
||||
// Act & Assert
|
||||
await expect(factory.validateEmbedderConfig(providedConfig)).rejects.toThrow(
|
||||
"OpenAI-compatible base URL and API key are required",
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw error for invalid embedder type", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "invalid-provider",
|
||||
modelId: "some-model",
|
||||
}
|
||||
|
||||
// Act & Assert
|
||||
await expect(factory.validateEmbedderConfig(providedConfig)).rejects.toThrow(
|
||||
"Invalid embedder type: invalid-provider",
|
||||
)
|
||||
})
|
||||
|
||||
it("should propagate validation errors from embedder", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-large",
|
||||
openAiOptions: {
|
||||
openAiNativeApiKey: "invalid-key",
|
||||
},
|
||||
}
|
||||
MockedOpenAiEmbedder.validateEndpoint = vitest.fn().mockRejectedValue(new Error("Invalid API key"))
|
||||
|
||||
// Act & Assert
|
||||
await expect(factory.validateEmbedderConfig(providedConfig)).rejects.toThrow("Invalid API key")
|
||||
})
|
||||
|
||||
it("should use default model ID when not provided", async () => {
|
||||
// Arrange
|
||||
const providedConfig = {
|
||||
embedderProvider: "openai",
|
||||
openAiOptions: {
|
||||
openAiNativeApiKey: "test-api-key",
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
const result = await factory.validateEmbedderConfig(providedConfig)
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true)
|
||||
expect(MockedOpenAiEmbedder.validateEndpoint).toHaveBeenCalledWith("test-api-key", undefined)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { ApiHandlerOptions } from "../../../shared/api"
|
||||
import { EmbedderInfo, EmbeddingResponse, IEmbedder } from "../interfaces"
|
||||
import { getModelQueryPrefix } from "../../../shared/embeddingModels"
|
||||
import { getModelQueryPrefix, getDefaultModelId } from "../../../shared/embeddingModels"
|
||||
import { MAX_ITEM_TOKENS } from "../constants"
|
||||
import { t } from "../../../i18n"
|
||||
import { serializeError } from "serialize-error"
|
||||
|
||||
/**
|
||||
* Implements the IEmbedder interface using a local Ollama instance.
|
||||
|
|
@ -113,7 +114,8 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
* @param modelId - The model ID to check
|
||||
* @returns A promise that resolves to true if valid, or throws an error with details
|
||||
*/
|
||||
static async validateEndpoint(baseUrl: string, modelId: string): Promise<boolean> {
|
||||
static async validateEndpoint(baseUrl: string, modelId: string | undefined): Promise<boolean> {
|
||||
const effectiveModelId = modelId || getDefaultModelId("ollama")
|
||||
const url = `${baseUrl}/api/tags`
|
||||
|
||||
try {
|
||||
|
|
@ -126,9 +128,15 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
throw new Error(`Ollama API not found at ${baseUrl}. Is Ollama running?`)
|
||||
throw new Error(t("embeddings:validation.apiNotFound", { provider: "Ollama", baseUrl }))
|
||||
}
|
||||
throw new Error(`Failed to connect to Ollama: ${response.status} ${response.statusText}`)
|
||||
throw new Error(
|
||||
t("embeddings:validation.connectionFailed", {
|
||||
provider: "Ollama",
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
|
@ -136,16 +144,47 @@ export class CodeIndexOllamaEmbedder implements IEmbedder {
|
|||
const modelNames = models.map((m: any) => m.name)
|
||||
|
||||
// Check if the specified model exists
|
||||
if (!modelNames.includes(modelId)) {
|
||||
throw new Error(`Model '${modelId}' not found. Available models: ${modelNames.join(", ") || "none"}`)
|
||||
if (!modelNames.includes(effectiveModelId)) {
|
||||
throw new Error(
|
||||
t("embeddings:validation.modelNotFound", {
|
||||
modelId: effectiveModelId,
|
||||
availableModels: modelNames.join(", ") || "none",
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error: any) {
|
||||
if (error.message.includes("fetch failed") || error.message.includes("ECONNREFUSED")) {
|
||||
throw new Error(`Cannot connect to Ollama at ${baseUrl}. Please ensure Ollama is running.`)
|
||||
// If it's already a translated error, re-throw it
|
||||
if (
|
||||
error?.message?.includes(
|
||||
t("embeddings:validation.modelNotFound", { modelId: "", availableModels: "" }).split(":")[0],
|
||||
) ||
|
||||
error?.message?.includes(
|
||||
t("embeddings:validation.apiNotFound", { provider: "", baseUrl: "" }).split(":")[0],
|
||||
) ||
|
||||
error?.message?.includes(
|
||||
t("embeddings:validation.connectionFailed", { provider: "", status: "", statusText: "" }).split(
|
||||
":",
|
||||
)[0],
|
||||
)
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
throw error
|
||||
|
||||
const serialized = serializeError(error)
|
||||
|
||||
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED")) {
|
||||
throw new Error(t("embeddings:validation.cannotConnect", { provider: "Ollama", baseUrl }))
|
||||
}
|
||||
|
||||
const errorDetails = serialized.message || t("embeddings:unknownError")
|
||||
throw new Error(
|
||||
t("embeddings:genericError", {
|
||||
provider: "Ollama",
|
||||
errorDetails,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "../constants"
|
||||
import { getDefaultModelId, getModelQueryPrefix } from "../../../shared/embeddingModels"
|
||||
import { t } from "../../../i18n"
|
||||
import { serializeError } from "serialize-error"
|
||||
|
||||
interface EmbeddingItem {
|
||||
embedding: string | number[]
|
||||
|
|
@ -50,10 +51,10 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
|
|||
*/
|
||||
constructor(baseUrl: string, apiKey: string, modelId?: string, maxItemTokens?: number) {
|
||||
if (!baseUrl) {
|
||||
throw new Error("Base URL is required for OpenAI Compatible embedder")
|
||||
throw new Error(t("embeddings:validation.baseUrlRequired", { provider: "OpenAI Compatible" }))
|
||||
}
|
||||
if (!apiKey) {
|
||||
throw new Error("API key is required for OpenAI Compatible embedder")
|
||||
throw new Error(t("embeddings:validation.apiKeyRequired", { provider: "OpenAI Compatible" }))
|
||||
}
|
||||
|
||||
this.baseUrl = baseUrl
|
||||
|
|
@ -76,36 +77,40 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
|
|||
* @returns Promise resolving to true if valid
|
||||
* @throws Error with descriptive message if validation fails
|
||||
*/
|
||||
static async validateEndpoint(baseUrl: string, apiKey: string, modelId?: string): Promise<boolean> {
|
||||
static async validateEndpoint(baseUrl: string, apiKey: string, modelId: string | undefined): Promise<boolean> {
|
||||
try {
|
||||
const client = new OpenAI({
|
||||
baseURL: baseUrl,
|
||||
apiKey: apiKey,
|
||||
})
|
||||
|
||||
const testModel = modelId || getDefaultModelId("openai-compatible")
|
||||
const effectiveModelId = modelId || getDefaultModelId("openai-compatible")
|
||||
|
||||
// Try a minimal embedding request
|
||||
await client.embeddings.create({
|
||||
input: "test",
|
||||
model: testModel,
|
||||
model: effectiveModelId,
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error: any) {
|
||||
let errorMessage = t("embeddings:unknownError")
|
||||
const serialized = serializeError(error)
|
||||
|
||||
if (error?.status === 401) {
|
||||
errorMessage = t("embeddings:authenticationFailed")
|
||||
throw new Error(t("embeddings:authenticationFailed"))
|
||||
} else if (error?.status === 404) {
|
||||
errorMessage = `Endpoint not found: ${baseUrl}`
|
||||
throw new Error(t("embeddings:validation.endpointNotFound", { baseUrl }))
|
||||
} else if (error?.code === "ECONNREFUSED" || error?.code === "ENOTFOUND") {
|
||||
errorMessage = `Cannot connect to ${baseUrl}`
|
||||
} else if (error?.message) {
|
||||
errorMessage = error.message
|
||||
throw new Error(t("embeddings:validation.cannotConnect", { provider: "OpenAI Compatible", baseUrl }))
|
||||
}
|
||||
|
||||
throw new Error(errorMessage)
|
||||
const errorDetails = serialized.message || t("embeddings:unknownError")
|
||||
throw new Error(
|
||||
t("embeddings:genericError", {
|
||||
provider: "OpenAI Compatible",
|
||||
errorDetails,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ import {
|
|||
MAX_BATCH_RETRIES as MAX_RETRIES,
|
||||
INITIAL_RETRY_DELAY_MS as INITIAL_DELAY_MS,
|
||||
} from "../constants"
|
||||
import { getModelQueryPrefix } from "../../../shared/embeddingModels"
|
||||
import { getModelQueryPrefix, getDefaultModelId } from "../../../shared/embeddingModels"
|
||||
import { t } from "../../../i18n"
|
||||
import { serializeError } from "serialize-error"
|
||||
|
||||
/**
|
||||
* OpenAI implementation of the embedder interface with batching and rate limiting
|
||||
|
|
@ -200,7 +201,8 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
|
|||
* @param modelId - The model ID to check
|
||||
* @returns A promise that resolves to true if valid, or throws an error with details
|
||||
*/
|
||||
static async validateEndpoint(apiKey: string, modelId: string): Promise<boolean> {
|
||||
static async validateEndpoint(apiKey: string, modelId: string | undefined): Promise<boolean> {
|
||||
const effectiveModelId = modelId || getDefaultModelId("openai")
|
||||
const client = new OpenAI({ apiKey })
|
||||
|
||||
try {
|
||||
|
|
@ -211,24 +213,45 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
|
|||
// Check if the specified embedding model exists or is a known model
|
||||
const knownEmbeddingModels = ["text-embedding-3-small", "text-embedding-3-large", "text-embedding-ada-002"]
|
||||
|
||||
if (!modelIds.includes(modelId) && !knownEmbeddingModels.includes(modelId)) {
|
||||
if (!modelIds.includes(effectiveModelId) && !knownEmbeddingModels.includes(effectiveModelId)) {
|
||||
throw new Error(
|
||||
`Model '${modelId}' not found. Available embedding models: ${knownEmbeddingModels.join(", ")}`,
|
||||
t("embeddings:validation.modelNotFound", {
|
||||
modelId: effectiveModelId,
|
||||
availableModels: knownEmbeddingModels.join(", "),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error: any) {
|
||||
// If it's already a translated error, re-throw it
|
||||
if (
|
||||
error?.message?.includes(
|
||||
t("embeddings:validation.modelNotFound", { modelId: "", availableModels: "" }).split(":")[0],
|
||||
)
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const serialized = serializeError(error)
|
||||
|
||||
if (error?.status === 401) {
|
||||
throw new Error("Invalid API key. Please check your OpenAI API key.")
|
||||
throw new Error(t("embeddings:validation.invalidApiKey", { provider: "OpenAI" }))
|
||||
}
|
||||
if (error?.status === 429) {
|
||||
throw new Error("Rate limit exceeded. Please try again later.")
|
||||
throw new Error(t("embeddings:validation.rateLimitExceeded"))
|
||||
}
|
||||
if (error?.message?.includes("fetch failed") || error?.message?.includes("ECONNREFUSED")) {
|
||||
throw new Error("Network error. Please check your internet connection.")
|
||||
throw new Error(t("embeddings:validation.networkError"))
|
||||
}
|
||||
throw new Error(`Failed to validate OpenAI configuration: ${error?.message || "Unknown error"}`)
|
||||
|
||||
const errorDetails = serialized.message || t("embeddings:unknownError")
|
||||
throw new Error(
|
||||
t("embeddings:genericError", {
|
||||
provider: "OpenAI",
|
||||
errorDetails: `${t("embeddings:validation.configurationFailed", { provider: "OpenAI" })}: ${errorDetails}`,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { ICodeParser, IEmbedder, IFileWatcher, IVectorStore } from "./interfaces
|
|||
import { CodeIndexConfigManager } from "./config-manager"
|
||||
import { CacheManager } from "./cache-manager"
|
||||
import { Ignore } from "ignore"
|
||||
import { t } from "../../i18n"
|
||||
|
||||
/**
|
||||
* Factory class responsible for creating and configuring code indexing service dependencies.
|
||||
|
|
@ -31,7 +32,7 @@ export class CodeIndexServiceFactory {
|
|||
|
||||
if (provider === "openai") {
|
||||
if (!config.openAiOptions?.openAiNativeApiKey) {
|
||||
throw new Error("OpenAI API key is required. Please configure it in the settings.")
|
||||
throw new Error(t("codeIndex:openAiApiKeyRequired"))
|
||||
}
|
||||
return new OpenAiEmbedder({
|
||||
...config.openAiOptions,
|
||||
|
|
@ -39,7 +40,7 @@ export class CodeIndexServiceFactory {
|
|||
})
|
||||
} else if (provider === "ollama") {
|
||||
if (!config.ollamaOptions?.ollamaBaseUrl) {
|
||||
throw new Error("Ollama base URL is required. Please configure it in the settings.")
|
||||
throw new Error(t("codeIndex:ollamaBaseUrlRequired"))
|
||||
}
|
||||
return new CodeIndexOllamaEmbedder({
|
||||
...config.ollamaOptions,
|
||||
|
|
@ -50,9 +51,7 @@ export class CodeIndexServiceFactory {
|
|||
const missing = []
|
||||
if (!config.openAiCompatibleOptions?.baseUrl) missing.push("base URL")
|
||||
if (!config.openAiCompatibleOptions?.apiKey) missing.push("API key")
|
||||
throw new Error(
|
||||
`OpenAI-compatible ${missing.join(" and ")} required. Please configure in the settings.`,
|
||||
)
|
||||
throw new Error(t("codeIndex:openAiCompatibleConfigRequired", { missing: missing.join(" and ") }))
|
||||
}
|
||||
return new OpenAICompatibleEmbedder(
|
||||
config.openAiCompatibleOptions.baseUrl,
|
||||
|
|
@ -66,45 +65,52 @@ export class CodeIndexServiceFactory {
|
|||
return new GeminiEmbedder(config.geminiOptions.apiKey)
|
||||
}
|
||||
|
||||
throw new Error(`Invalid embedder type configured: ${config.embedderProvider}`)
|
||||
throw new Error(t("codeIndex:invalidEmbedderType", { provider: config.embedderProvider }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the embedder configuration by testing the connection.
|
||||
* @param config - The configuration to validate (optional, defaults to current config)
|
||||
* @returns A promise that resolves to true if valid, or throws an error with details
|
||||
*/
|
||||
public async validateEmbedderConfig(): Promise<boolean> {
|
||||
const config = this.configManager.getConfig()
|
||||
const provider = config.embedderProvider as EmbedderProvider
|
||||
|
||||
public async validateEmbedderConfig(config?: any): Promise<boolean> {
|
||||
try {
|
||||
// Use provided config or fall back to current config
|
||||
const configToValidate = config || this.configManager.getConfig()
|
||||
const provider = configToValidate.embedderProvider as EmbedderProvider
|
||||
|
||||
if (provider === "openai") {
|
||||
if (!config.openAiOptions?.openAiNativeApiKey) {
|
||||
throw new Error("OpenAI API key is required")
|
||||
if (!configToValidate.openAiOptions?.openAiNativeApiKey) {
|
||||
throw new Error(t("codeIndex:openAiApiKeyRequiredValidation"))
|
||||
}
|
||||
const modelId = config.modelId || "text-embedding-3-small"
|
||||
return await OpenAiEmbedder.validateEndpoint(config.openAiOptions.openAiNativeApiKey, modelId)
|
||||
return await OpenAiEmbedder.validateEndpoint(
|
||||
configToValidate.openAiOptions.openAiNativeApiKey,
|
||||
configToValidate.modelId,
|
||||
)
|
||||
} else if (provider === "ollama") {
|
||||
if (!config.ollamaOptions?.ollamaBaseUrl) {
|
||||
throw new Error("Ollama base URL is required")
|
||||
if (!configToValidate.ollamaOptions?.ollamaBaseUrl) {
|
||||
throw new Error(t("codeIndex:ollamaBaseUrlRequiredValidation"))
|
||||
}
|
||||
const modelId = config.modelId || "nomic-embed-text:latest"
|
||||
return await CodeIndexOllamaEmbedder.validateEndpoint(config.ollamaOptions.ollamaBaseUrl, modelId)
|
||||
return await CodeIndexOllamaEmbedder.validateEndpoint(
|
||||
configToValidate.ollamaOptions.ollamaBaseUrl,
|
||||
configToValidate.modelId,
|
||||
)
|
||||
} else if (provider === "openai-compatible") {
|
||||
if (!config.openAiCompatibleOptions?.baseUrl || !config.openAiCompatibleOptions?.apiKey) {
|
||||
throw new Error("OpenAI-compatible base URL and API key are required")
|
||||
if (
|
||||
!configToValidate.openAiCompatibleOptions?.baseUrl ||
|
||||
!configToValidate.openAiCompatibleOptions?.apiKey
|
||||
) {
|
||||
throw new Error(t("codeIndex:openAiCompatibleConfigRequiredValidation"))
|
||||
}
|
||||
const modelId = config.modelId || "text-embedding-3-small"
|
||||
return await OpenAICompatibleEmbedder.validateEndpoint(
|
||||
config.openAiCompatibleOptions.baseUrl,
|
||||
config.openAiCompatibleOptions.apiKey,
|
||||
modelId,
|
||||
configToValidate.openAiCompatibleOptions.baseUrl,
|
||||
configToValidate.openAiCompatibleOptions.apiKey,
|
||||
configToValidate.modelId,
|
||||
)
|
||||
}
|
||||
throw new Error(`Invalid embedder type: ${provider}`)
|
||||
} catch (error: any) {
|
||||
// Re-throw with more context
|
||||
throw new Error(`${provider} validation failed: ${error.message}`)
|
||||
throw new Error(t("codeIndex:invalidEmbedderTypeValidation", { provider }))
|
||||
} catch (error) {
|
||||
throw new Error(t("codeIndex:embedderValidationFailed", { error: error.message }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,18 +142,16 @@ export class CodeIndexServiceFactory {
|
|||
}
|
||||
|
||||
if (vectorSize === undefined) {
|
||||
let errorMessage = `Could not determine vector dimension for model '${modelId}' with provider '${provider}'. `
|
||||
if (provider === "openai-compatible") {
|
||||
errorMessage += `Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.`
|
||||
throw new Error(t("codeIndex:vectorDimensionErrorOpenAiCompatible", { modelId, provider }))
|
||||
} else {
|
||||
errorMessage += `Check model profiles or configuration.`
|
||||
throw new Error(t("codeIndex:vectorDimensionErrorGeneral", { modelId, provider }))
|
||||
}
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
if (!config.qdrantUrl) {
|
||||
// This check remains important
|
||||
throw new Error("Qdrant URL missing for vector store creation")
|
||||
throw new Error(t("codeIndex:qdrantUrlMissing"))
|
||||
}
|
||||
|
||||
// Assuming constructor is updated: new QdrantVectorStore(workspacePath, url, vectorSize, apiKey?)
|
||||
|
|
@ -195,7 +199,7 @@ export class CodeIndexServiceFactory {
|
|||
fileWatcher: IFileWatcher
|
||||
} {
|
||||
if (!this.configManager.isFeatureConfigured) {
|
||||
throw new Error("Cannot create services: Code indexing is not properly configured")
|
||||
throw new Error(t("codeIndex:servicesNotConfigured"))
|
||||
}
|
||||
|
||||
const embedder = this.createEmbedder()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue