11import type { Ignore } from "ignore"
22import path from "path"
33
4- import { getDefaultModelId } from "./model-registry"
4+ import { getDefaultModelId , getModelDimension } from "./model-registry"
55import { resolveEmbeddingProfile } from "./embedding-profile"
66
77import { OpenAiEmbedder } from "./embedders/openai"
@@ -61,9 +61,10 @@ export class CodeIndexServiceFactory {
6161 }
6262 }
6363
64- public createEmbedder ( ) : IEmbedder {
64+ public createEmbedder ( dimensionOverride ?: number ) : IEmbedder {
6565 const config = this . configManager . getConfig ( )
6666 const provider = config . embedderProvider
67+ const dimension = dimensionOverride ?? config . modelDimension
6768
6869 if ( provider === "kilo" ) {
6970 if ( ! config . kiloOptions ?. apiKey ) throw new Error ( "Kilo API key is required for embedding." )
@@ -73,7 +74,7 @@ export class CodeIndexServiceFactory {
7374 baseUrl : config . kiloOptions . baseUrl ,
7475 organizationId : config . kiloOptions . organizationId ,
7576 modelId : config . modelId ,
76- dimensions : config . modelDimension ,
77+ dimensions : dimension ,
7778 } )
7879 }
7980 if ( provider === "openai" ) {
@@ -82,7 +83,7 @@ export class CodeIndexServiceFactory {
8283 }
8384 if ( provider === "ollama" ) {
8485 if ( ! config . ollamaOptions ?. baseUrl ) throw new Error ( "Ollama base URL is required for embedding." )
85- return new CodeIndexOllamaEmbedder ( config . ollamaOptions . baseUrl , config . modelId , config . modelDimension )
86+ return new CodeIndexOllamaEmbedder ( config . ollamaOptions . baseUrl , config . modelId , dimension )
8687 }
8788 if ( provider === "openai-compatible" ) {
8889 if ( ! config . openAiCompatibleOptions ?. baseUrl ) throw new Error ( "OpenAI-compatible base URL is required." )
@@ -116,7 +117,7 @@ export class CodeIndexServiceFactory {
116117 config . modelId ,
117118 undefined ,
118119 config . openRouterOptions . specificProvider ,
119- config . modelDimension ,
120+ dimension ,
120121 )
121122 }
122123 if ( provider === "voyage" ) {
@@ -169,9 +170,9 @@ export class CodeIndexServiceFactory {
169170 }
170171 }
171172
172- public createVectorStore ( workspacePath = this . workspacePath ) : IVectorStore {
173+ public createVectorStore ( workspacePath = this . workspacePath , dimensionOverride ?: number ) : IVectorStore {
173174 const config = this . configManager . getConfig ( )
174- const profile = resolveEmbeddingProfile ( config . embedderProvider , config . modelId , config . modelDimension )
175+ const profile = resolveEmbeddingProfile ( config . embedderProvider , config . modelId , dimensionOverride ?? config . modelDimension )
175176
176177 if ( ! profile || profile . dimension <= 0 ) {
177178 throw new Error (
@@ -246,38 +247,63 @@ export class CodeIndexServiceFactory {
246247 )
247248 }
248249
249- public createServices (
250+ public async detectEmbeddingDimension ( embedder : IEmbedder ) : Promise < number > {
251+ log . info ( "detecting embedding dimension" , { provider : embedder . embedderInfo . name } )
252+ const response = await embedder . createEmbeddings ( [ "test" ] )
253+ const dimension = response . embeddings [ 0 ] ?. length
254+ if ( ! dimension || dimension <= 0 ) {
255+ throw new Error ( `Failed to detect embedding dimension: invalid response from ${ embedder . embedderInfo . name } ` )
256+ }
257+ log . info ( "detected embedding dimension" , { provider : embedder . embedderInfo . name , dimension } )
258+ return dimension
259+ }
260+
261+ public async createServices (
250262 cacheManager : CacheManager ,
251263 ignoreInstance : Ignore ,
252- ) : {
264+ ) : Promise < {
253265 embedder : IEmbedder
254266 vectorStore : IVectorStore
255267 parser : ICodeParser
256268 scanner : DirectoryScanner
257269 fileWatcher : IFileWatcher
258- } {
270+ } > {
259271 if ( ! this . configManager . isFeatureConfigured ) {
260272 throw new Error ( "Code indexing is not configured. Save your settings to start indexing." )
261273 }
262274
263275 const config = this . configManager . getConfig ( )
276+ const modelId = config . modelId ?? getDefaultModelId ( config . embedderProvider )
277+ const registryDimension = getModelDimension ( config . embedderProvider , modelId )
278+
264279 log . info ( "creating indexing services" , {
265280 workspacePath : this . workspacePath ,
266281 provider : config . embedderProvider ,
267282 vectorStore : config . vectorStoreProvider ,
268- model : config . modelId ?? getDefaultModelId ( config . embedderProvider ) ,
283+ model : modelId ,
269284 configured : config . isConfigured ,
270285 } )
271286
272- const embedder = this . createEmbedder ( )
273- const vectorStore = this . createVectorStore ( )
287+ let effectiveDimension : number | undefined
288+ if ( config . modelDimension ) {
289+ effectiveDimension = config . modelDimension
290+ } else if ( registryDimension ) {
291+ effectiveDimension = registryDimension
292+ } else {
293+ const detectionEmbedder = this . createEmbedder ( )
294+ effectiveDimension = await this . detectEmbeddingDimension ( detectionEmbedder )
295+ }
296+
297+ const embedder = this . createEmbedder ( effectiveDimension )
298+ const vectorStore = this . createVectorStore ( this . workspacePath , effectiveDimension )
274299 const parser = codeParser
275300 const scanner = this . createDirectoryScanner ( embedder , vectorStore , parser , ignoreInstance )
276301 const fileWatcher = this . createFileWatcher ( embedder , vectorStore , cacheManager , ignoreInstance )
277302
278303 log . info ( "indexing services created" , {
279304 workspacePath : this . workspacePath ,
280305 provider : embedder . embedderInfo . name ,
306+ dimension : effectiveDimension ,
281307 } )
282308
283309 return { embedder, vectorStore, parser, scanner, fileWatcher }
0 commit comments