diff --git a/src/shared/__tests__/embeddingModels.spec.ts b/src/shared/__tests__/embeddingModels.spec.ts index 16aa019c7f..cb2d23428a 100644 --- a/src/shared/__tests__/embeddingModels.spec.ts +++ b/src/shared/__tests__/embeddingModels.spec.ts @@ -57,6 +57,25 @@ describe("embeddingModels", () => { expect(getModelDimension("openai", "text-embedding-3-large")).toBe(3072) expect(getModelDimension("openai", "text-embedding-ada-002")).toBe(1536) }) + + it("should return correct dimensions for openai-compatible third-party models", () => { + // Qwen models (commonly available on Featherless AI, Together AI, etc.) + expect(getModelDimension("openai-compatible", "Qwen/Qwen3-Embedding-0.6B")).toBe(1024) + expect(getModelDimension("openai-compatible", "Qwen/Qwen3-Embedding-4B")).toBe(2560) + expect(getModelDimension("openai-compatible", "Qwen/Qwen3-Embedding-8B")).toBe(4096) + // BAAI BGE models + expect(getModelDimension("openai-compatible", "BAAI/bge-large-en-v1.5")).toBe(1024) + expect(getModelDimension("openai-compatible", "BAAI/bge-base-en-v1.5")).toBe(768) + expect(getModelDimension("openai-compatible", "BAAI/bge-small-en-v1.5")).toBe(384) + // Nomic models + expect(getModelDimension("openai-compatible", "nomic-embed-text")).toBe(768) + // Mixedbread models + expect(getModelDimension("openai-compatible", "mixedbread-ai/mxbai-embed-large-v1")).toBe(1024) + }) + + it("should still return undefined for unknown models on openai-compatible", () => { + expect(getModelDimension("openai-compatible", "some-unknown-model")).toBeUndefined() + }) }) describe("getModelScoreThreshold", () => { diff --git a/src/shared/embeddingModels.ts b/src/shared/embeddingModels.ts index 7f5c9fac2b..1f56da3c7c 100644 --- a/src/shared/embeddingModels.ts +++ b/src/shared/embeddingModels.ts @@ -32,6 +32,19 @@ export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = { scoreThreshold: 0.15, queryPrefix: "Represent this query for searching relevant code: ", }, + // Nomic models (commonly available on third-party providers) + "nomic-embed-text": { dimension: 768, scoreThreshold: 0.4 }, + "nomic-ai/nomic-embed-text-v1.5": { dimension: 768, scoreThreshold: 0.4 }, + // Qwen embedding models (Featherless AI, Together AI, etc.) + "Qwen/Qwen3-Embedding-0.6B": { dimension: 1024, scoreThreshold: 0.4 }, + "Qwen/Qwen3-Embedding-4B": { dimension: 2560, scoreThreshold: 0.4 }, + "Qwen/Qwen3-Embedding-8B": { dimension: 4096, scoreThreshold: 0.4 }, + // BAAI BGE models (commonly self-hosted) + "BAAI/bge-large-en-v1.5": { dimension: 1024, scoreThreshold: 0.4 }, + "BAAI/bge-base-en-v1.5": { dimension: 768, scoreThreshold: 0.4 }, + "BAAI/bge-small-en-v1.5": { dimension: 384, scoreThreshold: 0.4 }, + // Mixedbread models + "mixedbread-ai/mxbai-embed-large-v1": { dimension: 1024, scoreThreshold: 0.4 }, }, gemini: { "gemini-embedding-001": { dimension: 3072, scoreThreshold: 0.4 }, diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 763c243ec1..c14a13ae3d 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -539,6 +539,15 @@ export const CodeIndexPopover: React.FC = ({ // Prepare settings to save const settingsToSave: any = {} + // Keys whose string values should be trimmed before saving + const trimKeys = new Set([ + "codebaseIndexQdrantUrl", + "codebaseIndexEmbedderBaseUrl", + "codebaseIndexOpenAiCompatibleBaseUrl", + "codebaseIndexEmbedderModelId", + "codebaseIndexQdrantApiKey", + ]) + // Iterate through all current settings for (const [key, value] of Object.entries(currentSettings)) { // For secret fields with placeholder, don't send the placeholder @@ -549,8 +558,13 @@ export const CodeIndexPopover: React.FC = ({ continue } - // Include all other fields, including empty strings (which clear secrets) - settingsToSave[key] = value + // Trim whitespace from URL and identifier fields to prevent silent failures + if (trimKeys.has(key) && typeof value === "string") { + settingsToSave[key] = value.trim() + } else { + // Include all other fields, including empty strings (which clear secrets) + settingsToSave[key] = value + } } // Always include codebaseIndexEnabled to ensure it's persisted