Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions src/services/code-index/__tests__/config-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1689,15 +1689,15 @@ describe("CodeIndexConfigManager", () => {
vi.clearAllMocks()
})

it("should return model's built-in dimension when available", async () => {
it("should prioritize user-configured dimension over model's built-in dimension", async () => {
// Mock getModelDimension to return a built-in dimension
mockedGetModelDimension.mockReturnValue(1536)

mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,
codebaseIndexEmbedderProvider: "openai",
codebaseIndexEmbedderModelId: "text-embedding-3-small",
codebaseIndexEmbedderModelDimension: 2048, // Custom dimension should be ignored
codebaseIndexEmbedderModelDimension: 2048, // Custom dimension takes priority
codebaseIndexQdrantUrl: "http://localhost:6333",
})
mockContextProxy.getSecret.mockImplementation((key: string) => {
Expand All @@ -1708,12 +1708,36 @@ describe("CodeIndexConfigManager", () => {
configManager = new CodeIndexConfigManager(mockContextProxy)
await configManager.loadConfiguration()

// Should return model's built-in dimension, not custom
// Should return user-configured dimension, not model's built-in
expect(configManager.currentModelDimension).toBe(2048)
// getModelDimension should not be called when user has configured dimension
})

it("should fall back to model's built-in dimension when no custom dimension set", async () => {
// Mock getModelDimension to return a built-in dimension
mockedGetModelDimension.mockReturnValue(1536)

mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,
codebaseIndexEmbedderProvider: "openai",
codebaseIndexEmbedderModelId: "text-embedding-3-small",
// No custom dimension set
codebaseIndexQdrantUrl: "http://localhost:6333",
})
mockContextProxy.getSecret.mockImplementation((key: string) => {
if (key === "codeIndexOpenAiKey") return "test-key"
return undefined
})

configManager = new CodeIndexConfigManager(mockContextProxy)
await configManager.loadConfiguration()

// Should fall back to model's built-in dimension
expect(configManager.currentModelDimension).toBe(1536)
expect(mockedGetModelDimension).toHaveBeenCalledWith("openai", "text-embedding-3-small")
})

it("should use custom dimension only when model has no built-in dimension", async () => {
it("should use custom dimension even when model has no built-in dimension", async () => {
// Mock getModelDimension to return undefined (no built-in dimension)
mockedGetModelDimension.mockReturnValue(undefined)

Expand All @@ -1732,9 +1756,8 @@ describe("CodeIndexConfigManager", () => {
configManager = new CodeIndexConfigManager(mockContextProxy)
await configManager.loadConfiguration()

// Should use custom dimension as fallback
// Custom dimension takes priority regardless of model's built-in dimension
expect(configManager.currentModelDimension).toBe(2048)
expect(mockedGetModelDimension).toHaveBeenCalledWith("openai-compatible", "custom-model")
})

it("should return undefined when neither model dimension nor custom dimension is available", async () => {
Expand Down
46 changes: 37 additions & 9 deletions src/services/code-index/__tests__/service-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,15 +420,15 @@ describe("CodeIndexServiceFactory", () => {
)
})

it("should prioritize getModelDimension over manual modelDimension for OpenAI Compatible provider", () => {
it("should prioritize user-configured modelDimension over model's built-in dimension", () => {
// Arrange
const testModelId = "custom-model"
const manualDimension = 1024
const modelDimension = 768
const testConfig = {
embedderProvider: "openai-compatible",
modelId: testModelId,
modelDimension: manualDimension, // This should be ignored when model has built-in dimension
modelDimension: manualDimension, // User-configured dimension takes priority
openAiCompatibleOptions: {
baseUrl: "https://api.example.com/v1",
apiKey: "test-api-key",
Expand All @@ -437,22 +437,21 @@ describe("CodeIndexServiceFactory", () => {
qdrantApiKey: "test-key",
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
mockGetModelDimension.mockReturnValue(modelDimension) // This should be used
mockGetModelDimension.mockReturnValue(modelDimension) // This should be ignored

// Act
factory.createVectorStore()

// Assert
expect(mockGetModelDimension).toHaveBeenCalledWith("openai-compatible", testModelId)
// Assert - user-configured dimension takes priority as single source of truth
expect(MockedQdrantVectorStore).toHaveBeenCalledWith(
"/test/workspace",
"http://localhost:6333",
modelDimension, // Should use model's built-in dimension, not manual
manualDimension, // Should use user-configured dimension
"test-key",
)
})

it("should use manual modelDimension only when model has no built-in dimension", () => {
it("should fall back to model's built-in dimension when no user-configured dimension", () => {
// Arrange
const testModelId = "unknown-model"
const manualDimension = 1024
Expand All @@ -473,12 +472,41 @@ describe("CodeIndexServiceFactory", () => {
// Act
factory.createVectorStore()

// Assert
// Assert - user-configured dimension is used
expect(MockedQdrantVectorStore).toHaveBeenCalledWith(
"/test/workspace",
"http://localhost:6333",
manualDimension,
"test-key",
)
})

it("should use model dimension when user has not configured a dimension", () => {
// Arrange
const testModelId = "text-embedding-3-small"
const testConfig = {
embedderProvider: "openai-compatible",
modelId: testModelId,
// No modelDimension configured by user
openAiCompatibleOptions: {
baseUrl: "https://api.example.com/v1",
apiKey: "test-api-key",
},
qdrantUrl: "http://localhost:6333",
qdrantApiKey: "test-key",
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)
mockGetModelDimension.mockReturnValue(1536) // Model's built-in dimension

// Act
factory.createVectorStore()

// Assert - falls back to model's built-in dimension
expect(mockGetModelDimension).toHaveBeenCalledWith("openai-compatible", testModelId)
expect(MockedQdrantVectorStore).toHaveBeenCalledWith(
"/test/workspace",
"http://localhost:6333",
manualDimension, // Should use manual dimension as fallback
1536, // Should use model's built-in dimension as fallback
"test-key",
)
})
Expand Down
16 changes: 8 additions & 8 deletions src/services/code-index/config-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,19 +503,19 @@ export class CodeIndexConfigManager {

/**
* Gets the current model dimension being used for embeddings.
* Returns the model's built-in dimension if available, otherwise falls back to custom dimension.
* Prioritizes user-configured dimension as the single source of truth.
* Falls back to model's built-in dimension only when user hasn't configured one.
*/
public get currentModelDimension(): number | undefined {
// First try to get the model-specific dimension
const modelId = this.modelId ?? getDefaultModelId(this.embedderProvider)
const modelDimension = getModelDimension(this.embedderProvider, modelId)

// Only use custom dimension if model doesn't have a built-in dimension
if (!modelDimension && this.modelDimension && this.modelDimension > 0) {
// User-configured dimension takes priority as the single source of truth
// This ensures consistency between collection creation and embedding generation
if (this.modelDimension && this.modelDimension > 0) {
return this.modelDimension
}

return modelDimension
// Fall back to model's built-in dimension when user hasn't configured one
const modelId = this.modelId ?? getDefaultModelId(this.embedderProvider)
return getModelDimension(this.embedderProvider, modelId)
}

/**
Expand Down
11 changes: 6 additions & 5 deletions src/services/code-index/service-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,13 @@ export class CodeIndexServiceFactory {

let vectorSize: number | undefined

// First 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) {
// User-configured dimension takes priority as the single source of truth
// This ensures consistency between collection creation and embedding generation
if (config.modelDimension && config.modelDimension > 0) {
vectorSize = config.modelDimension
} else {
// Fall back to model's built-in dimension from profiles
vectorSize = getModelDimension(provider, modelId)
}

if (vectorSize === undefined || vectorSize <= 0) {
Expand Down
Loading