Skip to content

Commit 2b3d8aa

Browse files
committed
feat: implement Qdrant memory optimization with constants in @roo-code/types
- Create comprehensive Qdrant configuration constants in @roo-code/types package - Add memory optimization config fields to codebase-index schema - Configure Qdrant to use on-disk storage for vectors and HNSW indexes by default - Add memory-mapped file support for segments larger than 50k vectors - Keep HNSW search parameter (ef) at 128 for testing purposes - Update all affected files to use constants from types package - Update all test files to handle new configuration parameters Fixes #6262
1 parent b117c0f commit 2b3d8aa

File tree

10 files changed

+465
-9
lines changed

10 files changed

+465
-9
lines changed

packages/types/src/codebase-index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const codebaseIndexConfigSchema = z.object({
3434
// OpenAI Compatible specific fields
3535
codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(),
3636
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
37+
// Memory optimization settings
38+
codebaseIndexUseOnDiskStorage: z.boolean().optional(),
39+
codebaseIndexMemoryMapThreshold: z.number().optional(),
40+
codebaseIndexHnswEfSearch: z.number().optional(),
3741
})
3842

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

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ export * from "./tool.js"
2121
export * from "./type-fu.js"
2222
export * from "./vscode.js"
2323
export * from "./todo.js"
24+
export * from "./qdrant.js"

packages/types/src/qdrant.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* Qdrant Vector Store Configuration Constants
3+
*
4+
* These constants define default values for Qdrant memory optimization settings
5+
* to reduce RAM usage by storing vectors and indexes on disk instead of in memory.
6+
*/
7+
8+
/**
9+
* Default memory optimization settings for Qdrant
10+
*/
11+
export const QDRANT_MEMORY_OPTIMIZATION_DEFAULTS = {
12+
/**
13+
* Enable on-disk storage for vectors and HNSW indexes by default
14+
* This significantly reduces memory usage at the cost of slightly slower access
15+
*/
16+
USE_ON_DISK_STORAGE: true,
17+
18+
/**
19+
* Number of vectors before using memory-mapped files
20+
* Segments larger than this threshold will use memory-mapped files for better memory management
21+
*/
22+
MEMORY_MAP_THRESHOLD: 50000,
23+
24+
/**
25+
* HNSW search parameter (ef) - controls search quality vs memory usage
26+
* Higher values = better search quality but more memory usage
27+
* Lower values = less memory usage but potentially lower search quality
28+
* Default: 128 (original value, not reduced for testing purposes)
29+
*/
30+
HNSW_EF_SEARCH: 128,
31+
} as const
32+
33+
/**
34+
* HNSW (Hierarchical Navigable Small World) index configuration constants
35+
*/
36+
export const QDRANT_HNSW_CONFIG_DEFAULTS = {
37+
/**
38+
* Number of bi-directional links created for each node during construction
39+
*/
40+
M: 16,
41+
42+
/**
43+
* Size of the dynamic list during index construction
44+
*/
45+
EF_CONSTRUCT: 100,
46+
47+
/**
48+
* Use full scan for collections smaller than this threshold
49+
*/
50+
FULL_SCAN_THRESHOLD: 10000,
51+
52+
/**
53+
* Maximum number of threads for indexing (0 = use all available CPU cores)
54+
*/
55+
MAX_INDEXING_THREADS: 0,
56+
57+
/**
58+
* Payload index configuration (null = use default)
59+
*/
60+
PAYLOAD_M: null,
61+
} as const
62+
63+
/**
64+
* Optimizer configuration constants for memory-mapped storage
65+
*/
66+
export const QDRANT_OPTIMIZER_CONFIG_DEFAULTS = {
67+
/**
68+
* Trigger optimization when this percentage of vectors are deleted
69+
*/
70+
DELETED_THRESHOLD: 0.2,
71+
72+
/**
73+
* Minimum number of vectors before vacuum operation
74+
*/
75+
VACUUM_MIN_VECTOR_NUMBER: 1000,
76+
77+
/**
78+
* Default number of segments to create
79+
*/
80+
DEFAULT_SEGMENT_NUMBER: 2,
81+
82+
/**
83+
* Maximum segment size (null = no limit)
84+
*/
85+
MAX_SEGMENT_SIZE: null,
86+
87+
/**
88+
* Start indexing after this many vectors
89+
*/
90+
INDEXING_THRESHOLD: 20000,
91+
92+
/**
93+
* Flush to disk interval in seconds
94+
*/
95+
FLUSH_INTERVAL_SEC: 5,
96+
97+
/**
98+
* Maximum optimization threads (0 = use all available CPU cores)
99+
*/
100+
MAX_OPTIMIZATION_THREADS: 0,
101+
} as const
102+
103+
/**
104+
* Quantization configuration for additional memory efficiency
105+
*/
106+
export const QDRANT_QUANTIZATION_CONFIG_DEFAULTS = {
107+
/**
108+
* Enable quantization for memory efficiency
109+
*/
110+
IGNORE: false,
111+
112+
/**
113+
* Rescore with original vectors for accuracy
114+
*/
115+
RESCORE: true,
116+
117+
/**
118+
* Oversample to maintain quality
119+
*/
120+
OVERSAMPLING: 2.0,
121+
} as const
122+
123+
/**
124+
* Memory optimization configuration interface
125+
*/
126+
export interface QdrantMemoryOptimizationConfig {
127+
/**
128+
* Enable on-disk storage for vectors and indexes
129+
*/
130+
useOnDiskStorage?: boolean
131+
132+
/**
133+
* Number of vectors before using memory-mapped files
134+
*/
135+
memoryMapThreshold?: number
136+
137+
/**
138+
* HNSW search parameter (ef) - controls search quality vs memory usage
139+
*/
140+
hnswEfSearch?: number
141+
}

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,18 @@ describe("CodeIndexConfigManager", () => {
104104
isConfigured: false,
105105
embedderProvider: "openai",
106106
modelId: undefined,
107+
modelDimension: undefined,
107108
openAiOptions: { openAiNativeApiKey: "" },
108109
ollamaOptions: { ollamaBaseUrl: "" },
110+
openAiCompatibleOptions: undefined,
111+
geminiOptions: undefined,
112+
mistralOptions: undefined,
109113
qdrantUrl: "http://localhost:6333",
110114
qdrantApiKey: "",
111115
searchMinScore: 0.4,
116+
useOnDiskStorage: true,
117+
memoryMapThreshold: 50000,
118+
hnswEfSearch: 128,
112119
})
113120
expect(result.requiresRestart).toBe(false)
114121
})
@@ -135,11 +142,18 @@ describe("CodeIndexConfigManager", () => {
135142
isConfigured: true,
136143
embedderProvider: "openai",
137144
modelId: "text-embedding-3-large",
145+
modelDimension: undefined,
138146
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
139147
ollamaOptions: { ollamaBaseUrl: "" },
148+
openAiCompatibleOptions: undefined,
149+
geminiOptions: undefined,
150+
mistralOptions: undefined,
140151
qdrantUrl: "http://qdrant.local",
141152
qdrantApiKey: "test-qdrant-key",
142153
searchMinScore: 0.4,
154+
useOnDiskStorage: true,
155+
memoryMapThreshold: 50000,
156+
hnswEfSearch: 128,
143157
})
144158
})
145159

@@ -168,15 +182,21 @@ describe("CodeIndexConfigManager", () => {
168182
isConfigured: true,
169183
embedderProvider: "openai-compatible",
170184
modelId: "text-embedding-3-large",
185+
modelDimension: undefined,
171186
openAiOptions: { openAiNativeApiKey: "" },
172187
ollamaOptions: { ollamaBaseUrl: "" },
173188
openAiCompatibleOptions: {
174189
baseUrl: "https://api.example.com/v1",
175190
apiKey: "test-openai-compatible-key",
176191
},
192+
geminiOptions: undefined,
193+
mistralOptions: undefined,
177194
qdrantUrl: "http://qdrant.local",
178195
qdrantApiKey: "test-qdrant-key",
179196
searchMinScore: 0.4,
197+
useOnDiskStorage: true,
198+
memoryMapThreshold: 50000,
199+
hnswEfSearch: 128,
180200
})
181201
})
182202

@@ -212,9 +232,14 @@ describe("CodeIndexConfigManager", () => {
212232
baseUrl: "https://api.example.com/v1",
213233
apiKey: "test-openai-compatible-key",
214234
},
235+
geminiOptions: undefined,
236+
mistralOptions: undefined,
215237
qdrantUrl: "http://qdrant.local",
216238
qdrantApiKey: "test-qdrant-key",
217239
searchMinScore: 0.4,
240+
useOnDiskStorage: true,
241+
memoryMapThreshold: 50000,
242+
hnswEfSearch: 128,
218243
})
219244
})
220245

@@ -243,16 +268,22 @@ describe("CodeIndexConfigManager", () => {
243268
isConfigured: true,
244269
embedderProvider: "openai-compatible",
245270
modelId: "custom-model",
271+
modelDimension: undefined,
246272
openAiOptions: { openAiNativeApiKey: "" },
247273
ollamaOptions: { ollamaBaseUrl: "" },
248274
openAiCompatibleOptions: {
249275
baseUrl: "https://api.example.com/v1",
250276
apiKey: "test-openai-compatible-key",
251277
// modelDimension is undefined when not set
252278
},
279+
geminiOptions: undefined,
280+
mistralOptions: undefined,
253281
qdrantUrl: "http://qdrant.local",
254282
qdrantApiKey: "test-qdrant-key",
255283
searchMinScore: 0.4,
284+
useOnDiskStorage: true,
285+
memoryMapThreshold: 50000,
286+
hnswEfSearch: 128,
256287
})
257288
})
258289

@@ -289,9 +320,13 @@ describe("CodeIndexConfigManager", () => {
289320
apiKey: "test-openai-compatible-key",
290321
},
291322
geminiOptions: undefined,
323+
mistralOptions: undefined,
292324
qdrantUrl: "http://qdrant.local",
293325
qdrantApiKey: "test-qdrant-key",
294326
searchMinScore: 0.4,
327+
useOnDiskStorage: true,
328+
memoryMapThreshold: 50000,
329+
hnswEfSearch: 128,
295330
})
296331
})
297332

@@ -1292,14 +1327,19 @@ describe("CodeIndexConfigManager", () => {
12921327
isConfigured: true,
12931328
embedderProvider: "openai",
12941329
modelId: "text-embedding-3-large",
1330+
modelDimension: undefined,
12951331
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
12961332
ollamaOptions: { ollamaBaseUrl: undefined },
12971333
geminiOptions: undefined,
12981334
openAiCompatibleOptions: undefined,
1335+
mistralOptions: undefined,
12991336
qdrantUrl: "http://qdrant.local",
13001337
qdrantApiKey: "test-qdrant-key",
13011338
searchMinScore: 0.4,
13021339
searchMaxResults: 50,
1340+
useOnDiskStorage: true,
1341+
memoryMapThreshold: 50000,
1342+
hnswEfSearch: 128,
13031343
})
13041344
})
13051345

0 commit comments

Comments
 (0)