Skip to content

Commit fd65135

Browse files
committed
feat: Add configurable maximum search results setting for codebase indexing
- Add searchMaxResults property to CodeIndexConfig with default value of 50 - Update VectorStore interface to accept configurable maxResults parameter - Modify QdrantClient to use configurable limit instead of hardcoded MAX_SEARCH_RESULTS - Add slider control in CodeIndexSettings UI (range: 1-500) - Add localization keys for the new setting - Update all tests to include new configuration field Fixes #5149
1 parent d2002bc commit fd65135

File tree

9 files changed

+72
-4
lines changed

9 files changed

+72
-4
lines changed

packages/types/src/codebase-index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const codebaseIndexConfigSchema = z.object({
1010
codebaseIndexEmbedderProvider: z.enum(["openai", "ollama", "openai-compatible"]).optional(),
1111
codebaseIndexEmbedderBaseUrl: z.string().optional(),
1212
codebaseIndexEmbedderModelId: z.string().optional(),
13+
codebaseIndexSearchMaxResults: z.number().int().min(1).max(500).optional(),
1314
})
1415

1516
export type CodebaseIndexConfig = z.infer<typeof codebaseIndexConfigSchema>

src/services/code-index/__tests__/config-manager.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ describe("CodeIndexConfigManager", () => {
3636
modelId: undefined,
3737
openAiOptions: { openAiNativeApiKey: "" },
3838
ollamaOptions: { ollamaBaseUrl: "" },
39+
openAiCompatibleOptions: undefined,
3940
qdrantUrl: "http://localhost:6333",
4041
qdrantApiKey: "",
4142
searchMinScore: 0.4,
43+
searchMaxResults: 50,
4244
})
4345
expect(result.requiresRestart).toBe(false)
4446
})
@@ -67,9 +69,11 @@ describe("CodeIndexConfigManager", () => {
6769
modelId: "text-embedding-3-large",
6870
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
6971
ollamaOptions: { ollamaBaseUrl: "" },
72+
openAiCompatibleOptions: undefined,
7073
qdrantUrl: "http://qdrant.local",
7174
qdrantApiKey: "test-qdrant-key",
7275
searchMinScore: 0.4,
76+
searchMaxResults: 50,
7377
})
7478
})
7579

@@ -104,10 +108,12 @@ describe("CodeIndexConfigManager", () => {
104108
openAiCompatibleOptions: {
105109
baseUrl: "https://api.example.com/v1",
106110
apiKey: "test-openai-compatible-key",
111+
modelDimension: undefined,
107112
},
108113
qdrantUrl: "http://qdrant.local",
109114
qdrantApiKey: "test-qdrant-key",
110115
searchMinScore: 0.4,
116+
searchMaxResults: 50,
111117
})
112118
})
113119

@@ -148,6 +154,7 @@ describe("CodeIndexConfigManager", () => {
148154
qdrantUrl: "http://qdrant.local",
149155
qdrantApiKey: "test-qdrant-key",
150156
searchMinScore: 0.4,
157+
searchMaxResults: 50,
151158
})
152159
})
153160

@@ -183,10 +190,12 @@ describe("CodeIndexConfigManager", () => {
183190
openAiCompatibleOptions: {
184191
baseUrl: "https://api.example.com/v1",
185192
apiKey: "test-openai-compatible-key",
193+
modelDimension: undefined,
186194
},
187195
qdrantUrl: "http://qdrant.local",
188196
qdrantApiKey: "test-qdrant-key",
189197
searchMinScore: 0.4,
198+
searchMaxResults: 50,
190199
})
191200
})
192201

@@ -227,6 +236,7 @@ describe("CodeIndexConfigManager", () => {
227236
qdrantUrl: "http://qdrant.local",
228237
qdrantApiKey: "test-qdrant-key",
229238
searchMinScore: 0.4,
239+
searchMaxResults: 50,
230240
})
231241
})
232242

@@ -939,9 +949,11 @@ describe("CodeIndexConfigManager", () => {
939949
modelId: "text-embedding-3-large",
940950
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
941951
ollamaOptions: { ollamaBaseUrl: undefined },
952+
openAiCompatibleOptions: undefined,
942953
qdrantUrl: "http://qdrant.local",
943954
qdrantApiKey: "test-qdrant-key",
944955
searchMinScore: 0.4,
956+
searchMaxResults: 50,
945957
})
946958
})
947959

src/services/code-index/config-manager.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class CodeIndexConfigManager {
1919
private qdrantUrl?: string = "http://localhost:6333"
2020
private qdrantApiKey?: string
2121
private searchMinScore?: number
22+
private searchMaxResults?: number
2223

2324
constructor(private readonly contextProxy: ContextProxy) {
2425
// Initialize with current configuration to avoid false restart triggers
@@ -35,6 +36,7 @@ export class CodeIndexConfigManager {
3536
codebaseIndexEnabled: false,
3637
codebaseIndexQdrantUrl: "http://localhost:6333",
3738
codebaseIndexSearchMinScore: 0.4,
39+
codebaseIndexSearchMaxResults: 50,
3840
codebaseIndexEmbedderProvider: "openai",
3941
codebaseIndexEmbedderBaseUrl: "",
4042
codebaseIndexEmbedderModelId: "",
@@ -46,6 +48,7 @@ export class CodeIndexConfigManager {
4648
codebaseIndexEmbedderProvider,
4749
codebaseIndexEmbedderBaseUrl,
4850
codebaseIndexEmbedderModelId,
51+
codebaseIndexSearchMaxResults,
4952
} = codebaseIndexConfig
5053

5154
const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? ""
@@ -62,6 +65,7 @@ export class CodeIndexConfigManager {
6265
this.qdrantApiKey = qdrantApiKey ?? ""
6366
this.openAiOptions = { openAiNativeApiKey: openAiKey }
6467
this.searchMinScore = SEARCH_MIN_SCORE
68+
this.searchMaxResults = codebaseIndexSearchMaxResults ?? 50
6569

6670
// Set embedder provider with support for openai-compatible
6771
if (codebaseIndexEmbedderProvider === "ollama") {
@@ -104,6 +108,7 @@ export class CodeIndexConfigManager {
104108
qdrantUrl?: string
105109
qdrantApiKey?: string
106110
searchMinScore?: number
111+
searchMaxResults?: number
107112
}
108113
requiresRestart: boolean
109114
}> {
@@ -140,6 +145,7 @@ export class CodeIndexConfigManager {
140145
qdrantUrl: this.qdrantUrl,
141146
qdrantApiKey: this.qdrantApiKey,
142147
searchMinScore: this.searchMinScore,
148+
searchMaxResults: this.searchMaxResults,
143149
},
144150
requiresRestart,
145151
}
@@ -295,6 +301,7 @@ export class CodeIndexConfigManager {
295301
qdrantUrl: this.qdrantUrl,
296302
qdrantApiKey: this.qdrantApiKey,
297303
searchMinScore: this.searchMinScore,
304+
searchMaxResults: this.searchMaxResults,
298305
}
299306
}
300307

@@ -342,4 +349,11 @@ export class CodeIndexConfigManager {
342349
public get currentSearchMinScore(): number | undefined {
343350
return this.searchMinScore
344351
}
352+
353+
/**
354+
* Gets the configured maximum search results.
355+
*/
356+
public get currentSearchMaxResults(): number | undefined {
357+
return this.searchMaxResults
358+
}
345359
}

src/services/code-index/interfaces/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface CodeIndexConfig {
1515
qdrantUrl?: string
1616
qdrantApiKey?: string
1717
searchMinScore?: number
18+
searchMaxResults?: number
1819
}
1920

2021
/**

src/services/code-index/interfaces/vector-store.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ export interface IVectorStore {
2323
/**
2424
* Searches for similar vectors
2525
* @param queryVector Vector to search for
26-
* @param limit Maximum number of results to return
26+
* @param directoryPrefix Optional directory path to filter results by
27+
* @param minScore Optional minimum score threshold
28+
* @param maxResults Maximum number of results to return
2729
* @returns Promise resolving to search results
2830
*/
29-
search(queryVector: number[], directoryPrefix?: string, minScore?: number): Promise<VectorStoreSearchResult[]>
31+
search(
32+
queryVector: number[],
33+
directoryPrefix?: string,
34+
minScore?: number,
35+
maxResults?: number,
36+
): Promise<VectorStoreSearchResult[]>
3037

3138
/**
3239
* Deletes points by file path

src/services/code-index/search-service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class CodeIndexSearchService {
3030
}
3131

3232
const minScore = this.configManager.currentSearchMinScore
33+
const maxResults = this.configManager.currentSearchMaxResults ?? 50
3334

3435
const currentState = this.stateManager.getCurrentStatus().systemStatus
3536
if (currentState !== "Indexed" && currentState !== "Indexing") {
@@ -52,7 +53,7 @@ export class CodeIndexSearchService {
5253
}
5354

5455
// Perform search
55-
const results = await this.vectorStore.search(vector, normalizedPrefix, minScore)
56+
const results = await this.vectorStore.search(vector, normalizedPrefix, minScore, maxResults)
5657
return results
5758
} catch (error) {
5859
console.error("[CodeIndexSearchService] Error during search:", error)

src/services/code-index/vector-store/qdrant-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export class QdrantVectorStore implements IVectorStore {
278278
queryVector: number[],
279279
directoryPrefix?: string,
280280
minScore?: number,
281+
maxResults?: number,
281282
): Promise<VectorStoreSearchResult[]> {
282283
try {
283284
let filter = undefined
@@ -297,7 +298,7 @@ export class QdrantVectorStore implements IVectorStore {
297298
query: queryVector,
298299
filter,
299300
score_threshold: SEARCH_MIN_SCORE,
300-
limit: MAX_SEARCH_RESULTS,
301+
limit: maxResults ?? MAX_SEARCH_RESULTS,
301302
params: {
302303
hnsw_ef: 128,
303304
exact: false,

webview-ui/src/components/settings/CodeIndexSettings.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
AlertDialogHeader,
2828
AlertDialogTitle,
2929
AlertDialogTrigger,
30+
Slider,
3031
} from "@src/components/ui"
3132

3233
import { SetCachedStateField } from "./types"
@@ -449,6 +450,34 @@ export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
449450
</div>
450451
</div>
451452

453+
<div className="flex flex-col gap-3">
454+
<div className="flex items-center gap-4 font-bold">
455+
<div>{t("settings:codeIndex.searchMaxResultsLabel")}</div>
456+
</div>
457+
<div className="flex items-center gap-4">
458+
<Slider
459+
value={[codebaseIndexConfig.codebaseIndexSearchMaxResults || 50]}
460+
onValueChange={([value]) =>
461+
setCachedStateField("codebaseIndexConfig", {
462+
...codebaseIndexConfig,
463+
codebaseIndexSearchMaxResults: value,
464+
})
465+
}
466+
min={1}
467+
max={500}
468+
step={1}
469+
className="flex-1"
470+
data-testid="search-max-results-slider"
471+
/>
472+
<span className="text-sm text-vscode-descriptionForeground min-w-[3rem] text-right">
473+
{codebaseIndexConfig.codebaseIndexSearchMaxResults || 50}
474+
</span>
475+
</div>
476+
<p className="text-vscode-descriptionForeground text-sm mt-0">
477+
{t("settings:codeIndex.searchMaxResultsDescription")}
478+
</p>
479+
</div>
480+
452481
{(!areSettingsCommitted || !validateIndexingConfig(codebaseIndexConfig, apiConfiguration)) && (
453482
<p className="text-sm text-vscode-descriptionForeground mb-2">
454483
{t("settings:codeIndex.unsavedSettingsMessage")}

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
"ollamaUrlLabel": "Ollama URL:",
5757
"qdrantUrlLabel": "Qdrant URL",
5858
"qdrantKeyLabel": "Qdrant Key:",
59+
"searchMaxResultsLabel": "Maximum Search Results",
60+
"searchMaxResultsDescription": "Maximum number of search results returned by codebase search. Higher values provide more comprehensive results but may increase processing time and token usage.",
5961
"startIndexingButton": "Start Indexing",
6062
"clearIndexDataButton": "Clear Index Data",
6163
"unsavedSettingsMessage": "Please save your settings before starting the indexing process.",

0 commit comments

Comments
 (0)