This commit is contained in:
roomote-v0[bot] 2026-04-09 10:15:17 -06:00 committed by GitHub
commit dbbc7e00a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 364 additions and 58 deletions

View file

@ -347,6 +347,7 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
// Mock service factory instance
mockServiceFactoryInstance = {
createEmbedder: vi.fn().mockReturnValue(mockEmbedder),
createServices: vi.fn().mockReturnValue({
embedder: mockEmbedder,
vectorStore: mockVectorStore,
@ -389,9 +390,9 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
await (manager as any)._recreateServices()
// Assert
expect(mockServiceFactoryInstance.createEmbedder).toHaveBeenCalled()
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(mockEmbedder)
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockStateManager.setSystemState).not.toHaveBeenCalledWith("Error", expect.any(String))
})
@ -408,9 +409,8 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
)
// Assert other expectations
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockServiceFactoryInstance.createEmbedder).toHaveBeenCalled()
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(mockEmbedder)
expect(mockStateManager.setSystemState).toHaveBeenCalledWith(
"Error",
"embeddings:validation.authenticationFailed",
@ -431,9 +431,8 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
)
// Assert other expectations
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockServiceFactoryInstance.createEmbedder).toHaveBeenCalled()
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(mockEmbedder)
expect(mockStateManager.setSystemState).toHaveBeenCalledWith(
"Error",
"embeddings:validation.configurationError",
@ -441,8 +440,8 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
})
it("should handle embedder creation failure", async () => {
// Arrange
mockServiceFactoryInstance.createServices.mockImplementation(() => {
// Arrange - createEmbedder is now called before createServices
mockServiceFactoryInstance.createEmbedder.mockImplementation(() => {
throw new Error("Invalid configuration")
})
@ -540,6 +539,7 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
it("should allow re-initialization after recovery", async () => {
// Setup mock for re-initialization
const mockServiceFactoryInstance = {
createEmbedder: vi.fn().mockReturnValue({ embedderInfo: { name: "openai" } }),
createServices: vi.fn().mockReturnValue({
embedder: { embedderInfo: { name: "openai" } },
vectorStore: {},

View file

@ -364,6 +364,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"
@ -689,6 +808,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 = {

View file

@ -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"))

View file

@ -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)

View file

@ -276,10 +276,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
@ -293,7 +294,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") {

View file

@ -84,10 +84,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

View file

@ -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

View file

@ -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",
{

View file

@ -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, {

View file

@ -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, {

View file

@ -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, {

View file

@ -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

View file

@ -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
}

View file

@ -390,15 +390,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"
@ -406,6 +401,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!,

View file

@ -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(