diff --git a/src/services/code-index/__tests__/service-factory.spec.ts b/src/services/code-index/__tests__/service-factory.spec.ts index 1d8f7ba4786..ae229077d25 100644 --- a/src/services/code-index/__tests__/service-factory.spec.ts +++ b/src/services/code-index/__tests__/service-factory.spec.ts @@ -345,6 +345,125 @@ describe("CodeIndexServiceFactory", () => { mockGetDefaultModelId.mockReturnValue("default-model") }) + it("should prioritize detectedDimension over all other dimension sources", () => { + // Arrange + const testConfig = { + embedderProvider: "openai-compatible", + modelId: "custom-model", + modelDimension: 1024, // Manual config should be ignored + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(768) // Profile dimension should be ignored + + // Act - pass detected dimension from validation + factory.createVectorStore(4096) + + // Assert - should use detected dimension (4096), not profile (768) or manual (1024) + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 4096, // Auto-detected dimension takes priority + "test-key", + ) + }) + + it("should use detected dimension from Ollama embedder", () => { + // Arrange - simulates Ollama with qwen3-embedding returning 4096 dimensions + const testConfig = { + embedderProvider: "ollama", + modelId: "qwen3-embedding", + modelDimension: 1536, // User's incorrect manual config + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(undefined) // Unknown model + + // Act - pass detected dimension from validation (like the issue scenario) + factory.createVectorStore(4096) + + // Assert - should use auto-detected 4096, not user's incorrect 1536 + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 4096, + "test-key", + ) + }) + + it("should fall back to profile dimension when detected dimension is not provided", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: "text-embedding-3-large", + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(3072) + + // Act - no detected dimension provided + factory.createVectorStore() + + // Assert - should use profile dimension + expect(mockGetModelDimension).toHaveBeenCalledWith("openai", "text-embedding-3-large") + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 3072, + "test-key", + ) + }) + + it("should fall back to manual dimension when detected and profile are unavailable", () => { + // Arrange + const testConfig = { + embedderProvider: "openai-compatible", + modelId: "unknown-model", + modelDimension: 2048, + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(undefined) + + // Act - no detected dimension, no profile dimension + factory.createVectorStore() + + // Assert - should use manual dimension + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 2048, + "test-key", + ) + }) + + it("should ignore zero or negative detected dimension", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: "text-embedding-3-small", + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(1536) + + // Act - pass invalid detected dimension + factory.createVectorStore(0) + + // Assert - should fall back to profile dimension + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 1536, + "test-key", + ) + }) + it("should use config.modelId for OpenAI provider", () => { // Arrange const testModelId = "text-embedding-3-large" @@ -670,6 +789,58 @@ describe("CodeIndexServiceFactory", () => { } }) + it("should return detectedDimension from embedder validation", async () => { + // Arrange + const testConfig = { + embedderProvider: "ollama", + modelId: "qwen3-embedding", + ollamaOptions: { + ollamaBaseUrl: "http://localhost:11434", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + MockedCodeIndexOllamaEmbedder.mockImplementation(() => mockEmbedderInstance) + // Mock embedder returning detected dimension + mockEmbedderInstance.validateConfiguration.mockResolvedValue({ + valid: true, + detectedDimension: 4096, + }) + + // Act + const embedder = factory.createEmbedder() + const result = await factory.validateEmbedder(embedder) + + // Assert + expect(result).toEqual({ valid: true, detectedDimension: 4096 }) + expect(mockEmbedderInstance.validateConfiguration).toHaveBeenCalled() + }) + + it("should return detectedDimension from base64 embedding validation", async () => { + // Arrange + const testConfig = { + embedderProvider: "openai-compatible", + modelId: "custom-model", + openAiCompatibleOptions: { + baseUrl: "https://api.example.com/v1", + apiKey: "test-api-key", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + MockedOpenAICompatibleEmbedder.mockImplementation(() => mockEmbedderInstance) + // Mock embedder returning detected dimension from base64 parsing + mockEmbedderInstance.validateConfiguration.mockResolvedValue({ + valid: true, + detectedDimension: 1536, + }) + + // Act + const embedder = factory.createEmbedder() + const result = await factory.validateEmbedder(embedder) + + // Assert + expect(result).toEqual({ valid: true, detectedDimension: 1536 }) + }) + it("should validate OpenAI embedder successfully", async () => { // Arrange const testConfig = { diff --git a/src/services/code-index/embedders/__tests__/ollama.spec.ts b/src/services/code-index/embedders/__tests__/ollama.spec.ts index 650140beacf..4b9dffaeb1b 100644 --- a/src/services/code-index/embedders/__tests__/ollama.spec.ts +++ b/src/services/code-index/embedders/__tests__/ollama.spec.ts @@ -196,6 +196,7 @@ describe("CodeIndexOllamaEmbedder", () => { expect(result.valid).toBe(true) expect(result.error).toBeUndefined() + expect(result.detectedDimension).toBe(3) // Auto-detected from test embedding expect(mockFetch).toHaveBeenCalledTimes(2) // Check first call (GET /api/tags) @@ -214,6 +215,38 @@ describe("CodeIndexOllamaEmbedder", () => { expect(secondCall[1]?.signal).toBeDefined() // AbortSignal for timeout }) + it("should detect dimension from realistic embedding size", async () => { + // Mock successful /api/tags call + mockFetch.mockImplementationOnce(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => + Promise.resolve({ + models: [{ name: "nomic-embed-text:latest" }], + }), + } as Response), + ) + + // Mock successful /api/embed test call with 4096-dimension embedding (like qwen3-embedding) + const largeEmbedding = new Array(4096).fill(0).map((_, i) => i * 0.001) + mockFetch.mockImplementationOnce(() => + Promise.resolve({ + ok: true, + status: 200, + json: () => + Promise.resolve({ + embeddings: [largeEmbedding], + }), + } as Response), + ) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(true) + expect(result.detectedDimension).toBe(4096) + }) + it("should fail validation when service is not available", async () => { mockFetch.mockRejectedValueOnce(new Error("ECONNREFUSED")) diff --git a/src/services/code-index/embedders/__tests__/openai-compatible.spec.ts b/src/services/code-index/embedders/__tests__/openai-compatible.spec.ts index ecde7691515..3f16a67753e 100644 --- a/src/services/code-index/embedders/__tests__/openai-compatible.spec.ts +++ b/src/services/code-index/embedders/__tests__/openai-compatible.spec.ts @@ -978,6 +978,7 @@ describe("OpenAICompatibleEmbedder", () => { expect(result.valid).toBe(true) expect(result.error).toBeUndefined() + expect(result.detectedDimension).toBe(3) // Auto-detected from array embedding expect(mockEmbeddingsCreate).toHaveBeenCalledWith({ input: ["test"], model: testModelId, @@ -1003,6 +1004,7 @@ describe("OpenAICompatibleEmbedder", () => { expect(result.valid).toBe(true) expect(result.error).toBeUndefined() + expect(result.detectedDimension).toBe(3) // Auto-detected from array embedding expect(mockFetch).toHaveBeenCalledWith( fullUrl, expect.objectContaining({ @@ -1014,6 +1016,25 @@ describe("OpenAICompatibleEmbedder", () => { ) }) + it("should detect dimension from base64 encoded embedding", async () => { + embedder = new OpenAICompatibleEmbedder(testBaseUrl, testApiKey, testModelId) + + // Create a 1536-dimension embedding as base64 (like text-embedding-3-small) + const embedding = new Float32Array(1536).fill(0.1) + const base64String = Buffer.from(embedding.buffer).toString("base64") + + const mockResponse = { + data: [{ embedding: base64String }], + usage: { prompt_tokens: 2, total_tokens: 2 }, + } + mockEmbeddingsCreate.mockResolvedValue(mockResponse) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(true) + expect(result.detectedDimension).toBe(1536) // Auto-detected from base64 (1536 * 4 bytes / 4 = 1536) + }) + it("should fail validation with authentication error", async () => { embedder = new OpenAICompatibleEmbedder(testBaseUrl, testApiKey, testModelId) diff --git a/src/services/code-index/embedders/bedrock.ts b/src/services/code-index/embedders/bedrock.ts index e99d6ee25ec..4e9b7aca213 100644 --- a/src/services/code-index/embedders/bedrock.ts +++ b/src/services/code-index/embedders/bedrock.ts @@ -263,10 +263,11 @@ export class BedrockEmbedder implements IEmbedder { } /** - * Validates the Bedrock embedder configuration by attempting a minimal embedding request - * @returns Promise resolving to validation result with success status and optional error message + * Validates the Bedrock embedder configuration by attempting a minimal embedding request. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { return withValidationErrorHandling(async () => { try { // Test with a minimal embedding request @@ -280,7 +281,10 @@ export class BedrockEmbedder implements IEmbedder { } } - return { valid: true } + // Get the dimension from the embedding + const detectedDimension = result.embedding.length + + return { valid: true, detectedDimension } } catch (error: any) { // Check for specific AWS errors if (error.name === "UnrecognizedClientException") { diff --git a/src/services/code-index/embedders/gemini.ts b/src/services/code-index/embedders/gemini.ts index 7e795875c9d..99f2118e95b 100644 --- a/src/services/code-index/embedders/gemini.ts +++ b/src/services/code-index/embedders/gemini.ts @@ -63,10 +63,11 @@ export class GeminiEmbedder implements IEmbedder { } /** - * Validates the Gemini embedder configuration by delegating to the underlying OpenAI-compatible embedder - * @returns Promise resolving to validation result with success status and optional error message + * Validates the Gemini embedder configuration by delegating to the underlying OpenAI-compatible embedder. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { try { // Delegate validation to the OpenAI-compatible embedder // The error messages will be specific to Gemini since we're using Gemini's base URL diff --git a/src/services/code-index/embedders/mistral.ts b/src/services/code-index/embedders/mistral.ts index c23bcbba1d4..0d0cb429503 100644 --- a/src/services/code-index/embedders/mistral.ts +++ b/src/services/code-index/embedders/mistral.ts @@ -62,10 +62,11 @@ export class MistralEmbedder implements IEmbedder { } /** - * Validates the Mistral embedder configuration by delegating to the underlying OpenAI-compatible embedder - * @returns Promise resolving to validation result with success status and optional error message + * Validates the Mistral embedder configuration by delegating to the underlying OpenAI-compatible embedder. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { try { // Delegate validation to the OpenAI-compatible embedder // The error messages will be specific to Mistral since we're using Mistral's base URL diff --git a/src/services/code-index/embedders/ollama.ts b/src/services/code-index/embedders/ollama.ts index 9688a15ff04..668fc6b04b2 100644 --- a/src/services/code-index/embedders/ollama.ts +++ b/src/services/code-index/embedders/ollama.ts @@ -138,10 +138,11 @@ export class CodeIndexOllamaEmbedder implements IEmbedder { } /** - * Validates the Ollama embedder configuration by checking service availability and model existence - * @returns Promise resolving to validation result with success status and optional error message + * Validates the Ollama embedder configuration by checking service availability and model existence. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { return withValidationErrorHandling( async () => { // First check if Ollama service is running by trying to list models @@ -228,7 +229,19 @@ export class CodeIndexOllamaEmbedder implements IEmbedder { } } - return { valid: true } + // Parse the test response to get the embedding dimension + const testData = await testResponse.json() + const embeddings = testData.embeddings + let detectedDimension: number | undefined + + if (embeddings && Array.isArray(embeddings) && embeddings.length > 0) { + const firstEmbedding = embeddings[0] + if (Array.isArray(firstEmbedding)) { + detectedDimension = firstEmbedding.length + } + } + + return { valid: true, detectedDimension } }, "ollama", { diff --git a/src/services/code-index/embedders/openai-compatible.ts b/src/services/code-index/embedders/openai-compatible.ts index 6eaf2b6c2c1..9252797d173 100644 --- a/src/services/code-index/embedders/openai-compatible.ts +++ b/src/services/code-index/embedders/openai-compatible.ts @@ -357,10 +357,11 @@ export class OpenAICompatibleEmbedder implements IEmbedder { } /** - * Validates the OpenAI-compatible embedder configuration by testing endpoint connectivity and API key - * @returns Promise resolving to validation result with success status and optional error message + * Validates the OpenAI-compatible embedder configuration by testing endpoint connectivity and API key. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { return withValidationErrorHandling(async () => { try { // Test with a minimal embedding request @@ -389,7 +390,20 @@ export class OpenAICompatibleEmbedder implements IEmbedder { } } - return { valid: true } + // Convert base64 embedding to get the actual dimension + let detectedDimension: number | undefined + const firstItem = response.data[0] + if (firstItem?.embedding) { + if (typeof firstItem.embedding === "string") { + // Decode base64 to get float32 array length + const buffer = Buffer.from(firstItem.embedding, "base64") + detectedDimension = buffer.byteLength / 4 // 4 bytes per float32 + } else if (Array.isArray(firstItem.embedding)) { + detectedDimension = firstItem.embedding.length + } + } + + return { valid: true, detectedDimension } } catch (error) { // Capture telemetry for validation errors TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { diff --git a/src/services/code-index/embedders/openai.ts b/src/services/code-index/embedders/openai.ts index b993e280d98..e67af683384 100644 --- a/src/services/code-index/embedders/openai.ts +++ b/src/services/code-index/embedders/openai.ts @@ -187,10 +187,11 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder { } /** - * Validates the OpenAI embedder configuration by attempting a minimal embedding request - * @returns Promise resolving to validation result with success status and optional error message + * Validates the OpenAI embedder configuration by attempting a minimal embedding request. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { return withValidationErrorHandling(async () => { try { // Test with a minimal embedding request @@ -207,7 +208,10 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder { } } - return { valid: true } + // Get the dimension from the first embedding + const detectedDimension = response.data[0]?.embedding?.length + + return { valid: true, detectedDimension } } catch (error) { // Capture telemetry for validation errors TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { diff --git a/src/services/code-index/embedders/openrouter.ts b/src/services/code-index/embedders/openrouter.ts index 2ffdd7afb64..79785d22d20 100644 --- a/src/services/code-index/embedders/openrouter.ts +++ b/src/services/code-index/embedders/openrouter.ts @@ -286,10 +286,11 @@ export class OpenRouterEmbedder implements IEmbedder { } /** - * Validates the OpenRouter embedder configuration by testing API connectivity - * @returns Promise resolving to validation result with success status and optional error message + * Validates the OpenRouter embedder configuration by testing API connectivity. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { return withValidationErrorHandling(async () => { try { // Test with a minimal embedding request @@ -324,7 +325,20 @@ export class OpenRouterEmbedder implements IEmbedder { } } - return { valid: true } + // Detect the embedding dimension from the response + let detectedDimension: number | undefined + const firstItem = response.data[0] + if (firstItem?.embedding) { + if (typeof firstItem.embedding === "string") { + // Decode base64 to get float32 array length + const buffer = Buffer.from(firstItem.embedding, "base64") + detectedDimension = buffer.byteLength / 4 // 4 bytes per float32 + } else if (Array.isArray(firstItem.embedding)) { + detectedDimension = firstItem.embedding.length + } + } + + return { valid: true, detectedDimension } } catch (error) { // Capture telemetry for validation errors TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { diff --git a/src/services/code-index/embedders/vercel-ai-gateway.ts b/src/services/code-index/embedders/vercel-ai-gateway.ts index ec0888cd8b5..86c5817b29a 100644 --- a/src/services/code-index/embedders/vercel-ai-gateway.ts +++ b/src/services/code-index/embedders/vercel-ai-gateway.ts @@ -71,10 +71,11 @@ export class VercelAiGatewayEmbedder implements IEmbedder { } /** - * Validates the Vercel AI Gateway embedder configuration by delegating to the underlying OpenAI-compatible embedder - * @returns Promise resolving to validation result with success status and optional error message + * Validates the Vercel AI Gateway embedder configuration by delegating to the underlying OpenAI-compatible embedder. + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + async validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { try { // Delegate validation to the OpenAI-compatible embedder // The error messages will be specific to Vercel AI Gateway since we're using Vercel's base URL diff --git a/src/services/code-index/interfaces/embedder.ts b/src/services/code-index/interfaces/embedder.ts index aa8515d6986..0d36b568ecd 100644 --- a/src/services/code-index/interfaces/embedder.ts +++ b/src/services/code-index/interfaces/embedder.ts @@ -13,9 +13,10 @@ export interface IEmbedder { /** * Validates the embedder configuration by testing connectivity and credentials. - * @returns Promise resolving to validation result with success status and optional error message + * Also detects the actual embedding dimension from a test embedding. + * @returns Promise resolving to validation result with success status, optional error message, and detected dimension */ - validateConfiguration(): Promise<{ valid: boolean; error?: string }> + validateConfiguration(): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> get embedderInfo(): EmbedderInfo } diff --git a/src/services/code-index/manager.ts b/src/services/code-index/manager.ts index dd79a3f1616..7f9745832b9 100644 --- a/src/services/code-index/manager.ts +++ b/src/services/code-index/manager.ts @@ -332,15 +332,10 @@ export class CodeIndexManager { const rooIgnoreController = new RooIgnoreController(workspacePath) await rooIgnoreController.initialize() - // (Re)Create shared service instances - const { embedder, vectorStore, scanner, fileWatcher } = this._serviceFactory.createServices( - this.context, - this._cacheManager!, - ignoreInstance, - rooIgnoreController, - ) + // Create embedder first to validate and detect embedding dimension + const embedder = this._serviceFactory.createEmbedder() - // Validate embedder configuration before proceeding + // Validate embedder configuration and detect actual embedding dimension const validationResult = await this._serviceFactory.validateEmbedder(embedder) if (!validationResult.valid) { const errorMessage = validationResult.error || "Embedder configuration validation failed" @@ -348,6 +343,20 @@ export class CodeIndexManager { throw new Error(errorMessage) } + // Use the auto-detected dimension if available + // This ensures we always use the actual dimension from the model, + // preventing mismatches between configured and actual dimensions (Issue #10991) + const detectedDimension = validationResult.detectedDimension + + // (Re)Create shared service instances with the detected dimension + const { vectorStore, scanner, fileWatcher } = this._serviceFactory.createServices( + this.context, + this._cacheManager!, + ignoreInstance, + rooIgnoreController, + detectedDimension, + ) + // (Re)Initialize orchestrator this._orchestrator = new CodeIndexOrchestrator( this._configManager!, diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index d23eff4810b..b28293b1ae9 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -112,10 +112,13 @@ export class CodeIndexServiceFactory { /** * Validates an embedder instance to ensure it's properly configured. + * Also captures the detected embedding dimension from the test embedding. * @param embedder The embedder instance to validate - * @returns Promise resolving to validation result + * @returns Promise resolving to validation result with optional detected dimension */ - public async validateEmbedder(embedder: IEmbedder): Promise<{ valid: boolean; error?: string }> { + public async validateEmbedder( + embedder: IEmbedder, + ): Promise<{ valid: boolean; error?: string; detectedDimension?: number }> { try { return await embedder.validateConfiguration() } catch (error) { @@ -136,8 +139,10 @@ export class CodeIndexServiceFactory { /** * Creates a vector store instance using the current configuration. + * @param detectedDimension Optional embedding dimension auto-detected from a test embedding. + * When provided, this takes priority over profile-based or manual dimensions. */ - public createVectorStore(): IVectorStore { + public createVectorStore(detectedDimension?: number): IVectorStore { const config = this.configManager.getConfig() const provider = config.embedderProvider as EmbedderProvider @@ -147,12 +152,20 @@ export class CodeIndexServiceFactory { let vectorSize: number | undefined - // First try to get the model-specific dimension from profiles - vectorSize = getModelDimension(provider, modelId) + // Priority order for vector dimension: + // 1. Auto-detected dimension from test embedding (most reliable) + // 2. Model-specific dimension from profiles + // 3. Manual dimension from config (fallback for unknown models) + if (detectedDimension && detectedDimension > 0) { + vectorSize = detectedDimension + } else { + // Try to get the model-specific dimension from profiles + vectorSize = getModelDimension(provider, modelId) - // Only use manual dimension if model doesn't have a built-in dimension - if (!vectorSize && config.modelDimension && config.modelDimension > 0) { - vectorSize = config.modelDimension + // Only use manual dimension if model doesn't have a built-in dimension + if (!vectorSize && config.modelDimension && config.modelDimension > 0) { + vectorSize = config.modelDimension + } } if (vectorSize === undefined || vectorSize <= 0) { @@ -230,6 +243,11 @@ export class CodeIndexServiceFactory { /** * Creates all required service dependencies if the service is properly configured. + * @param context VSCode extension context + * @param cacheManager Cache manager instance + * @param ignoreInstance Ignore instance for .gitignore + * @param rooIgnoreController Optional RooIgnore controller + * @param detectedDimension Optional auto-detected embedding dimension from validation * @throws Error if the service is not properly configured */ public createServices( @@ -237,6 +255,7 @@ export class CodeIndexServiceFactory { cacheManager: CacheManager, ignoreInstance: Ignore, rooIgnoreController?: RooIgnoreController, + detectedDimension?: number, ): { embedder: IEmbedder vectorStore: IVectorStore @@ -249,7 +268,7 @@ export class CodeIndexServiceFactory { } const embedder = this.createEmbedder() - const vectorStore = this.createVectorStore() + const vectorStore = this.createVectorStore(detectedDimension) const parser = codeParser const scanner = this.createDirectoryScanner(embedder, vectorStore, parser, ignoreInstance) const fileWatcher = this.createFileWatcher(