mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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
This commit is contained in:
parent
b117c0fe52
commit
2b3d8aa4d1
10 changed files with 465 additions and 9 deletions
|
|
@ -34,6 +34,10 @@ export const codebaseIndexConfigSchema = z.object({
|
|||
// OpenAI Compatible specific fields
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(),
|
||||
codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
|
||||
// Memory optimization settings
|
||||
codebaseIndexUseOnDiskStorage: z.boolean().optional(),
|
||||
codebaseIndexMemoryMapThreshold: z.number().optional(),
|
||||
codebaseIndexHnswEfSearch: z.number().optional(),
|
||||
})
|
||||
|
||||
export type CodebaseIndexConfig = z.infer<typeof codebaseIndexConfigSchema>
|
||||
|
|
|
|||
|
|
@ -21,3 +21,4 @@ export * from "./tool.js"
|
|||
export * from "./type-fu.js"
|
||||
export * from "./vscode.js"
|
||||
export * from "./todo.js"
|
||||
export * from "./qdrant.js"
|
||||
|
|
|
|||
141
packages/types/src/qdrant.ts
Normal file
141
packages/types/src/qdrant.ts
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* Qdrant Vector Store Configuration Constants
|
||||
*
|
||||
* These constants define default values for Qdrant memory optimization settings
|
||||
* to reduce RAM usage by storing vectors and indexes on disk instead of in memory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default memory optimization settings for Qdrant
|
||||
*/
|
||||
export const QDRANT_MEMORY_OPTIMIZATION_DEFAULTS = {
|
||||
/**
|
||||
* Enable on-disk storage for vectors and HNSW indexes by default
|
||||
* This significantly reduces memory usage at the cost of slightly slower access
|
||||
*/
|
||||
USE_ON_DISK_STORAGE: true,
|
||||
|
||||
/**
|
||||
* Number of vectors before using memory-mapped files
|
||||
* Segments larger than this threshold will use memory-mapped files for better memory management
|
||||
*/
|
||||
MEMORY_MAP_THRESHOLD: 50000,
|
||||
|
||||
/**
|
||||
* HNSW search parameter (ef) - controls search quality vs memory usage
|
||||
* Higher values = better search quality but more memory usage
|
||||
* Lower values = less memory usage but potentially lower search quality
|
||||
* Default: 128 (original value, not reduced for testing purposes)
|
||||
*/
|
||||
HNSW_EF_SEARCH: 128,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* HNSW (Hierarchical Navigable Small World) index configuration constants
|
||||
*/
|
||||
export const QDRANT_HNSW_CONFIG_DEFAULTS = {
|
||||
/**
|
||||
* Number of bi-directional links created for each node during construction
|
||||
*/
|
||||
M: 16,
|
||||
|
||||
/**
|
||||
* Size of the dynamic list during index construction
|
||||
*/
|
||||
EF_CONSTRUCT: 100,
|
||||
|
||||
/**
|
||||
* Use full scan for collections smaller than this threshold
|
||||
*/
|
||||
FULL_SCAN_THRESHOLD: 10000,
|
||||
|
||||
/**
|
||||
* Maximum number of threads for indexing (0 = use all available CPU cores)
|
||||
*/
|
||||
MAX_INDEXING_THREADS: 0,
|
||||
|
||||
/**
|
||||
* Payload index configuration (null = use default)
|
||||
*/
|
||||
PAYLOAD_M: null,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Optimizer configuration constants for memory-mapped storage
|
||||
*/
|
||||
export const QDRANT_OPTIMIZER_CONFIG_DEFAULTS = {
|
||||
/**
|
||||
* Trigger optimization when this percentage of vectors are deleted
|
||||
*/
|
||||
DELETED_THRESHOLD: 0.2,
|
||||
|
||||
/**
|
||||
* Minimum number of vectors before vacuum operation
|
||||
*/
|
||||
VACUUM_MIN_VECTOR_NUMBER: 1000,
|
||||
|
||||
/**
|
||||
* Default number of segments to create
|
||||
*/
|
||||
DEFAULT_SEGMENT_NUMBER: 2,
|
||||
|
||||
/**
|
||||
* Maximum segment size (null = no limit)
|
||||
*/
|
||||
MAX_SEGMENT_SIZE: null,
|
||||
|
||||
/**
|
||||
* Start indexing after this many vectors
|
||||
*/
|
||||
INDEXING_THRESHOLD: 20000,
|
||||
|
||||
/**
|
||||
* Flush to disk interval in seconds
|
||||
*/
|
||||
FLUSH_INTERVAL_SEC: 5,
|
||||
|
||||
/**
|
||||
* Maximum optimization threads (0 = use all available CPU cores)
|
||||
*/
|
||||
MAX_OPTIMIZATION_THREADS: 0,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Quantization configuration for additional memory efficiency
|
||||
*/
|
||||
export const QDRANT_QUANTIZATION_CONFIG_DEFAULTS = {
|
||||
/**
|
||||
* Enable quantization for memory efficiency
|
||||
*/
|
||||
IGNORE: false,
|
||||
|
||||
/**
|
||||
* Rescore with original vectors for accuracy
|
||||
*/
|
||||
RESCORE: true,
|
||||
|
||||
/**
|
||||
* Oversample to maintain quality
|
||||
*/
|
||||
OVERSAMPLING: 2.0,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Memory optimization configuration interface
|
||||
*/
|
||||
export interface QdrantMemoryOptimizationConfig {
|
||||
/**
|
||||
* Enable on-disk storage for vectors and indexes
|
||||
*/
|
||||
useOnDiskStorage?: boolean
|
||||
|
||||
/**
|
||||
* Number of vectors before using memory-mapped files
|
||||
*/
|
||||
memoryMapThreshold?: number
|
||||
|
||||
/**
|
||||
* HNSW search parameter (ef) - controls search quality vs memory usage
|
||||
*/
|
||||
hnswEfSearch?: number
|
||||
}
|
||||
|
|
@ -104,11 +104,18 @@ describe("CodeIndexConfigManager", () => {
|
|||
isConfigured: false,
|
||||
embedderProvider: "openai",
|
||||
modelId: undefined,
|
||||
modelDimension: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "" },
|
||||
ollamaOptions: { ollamaBaseUrl: "" },
|
||||
openAiCompatibleOptions: undefined,
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://localhost:6333",
|
||||
qdrantApiKey: "",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
expect(result.requiresRestart).toBe(false)
|
||||
})
|
||||
|
|
@ -135,11 +142,18 @@ describe("CodeIndexConfigManager", () => {
|
|||
isConfigured: true,
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-large",
|
||||
modelDimension: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
|
||||
ollamaOptions: { ollamaBaseUrl: "" },
|
||||
openAiCompatibleOptions: undefined,
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -168,15 +182,21 @@ describe("CodeIndexConfigManager", () => {
|
|||
isConfigured: true,
|
||||
embedderProvider: "openai-compatible",
|
||||
modelId: "text-embedding-3-large",
|
||||
modelDimension: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "" },
|
||||
ollamaOptions: { ollamaBaseUrl: "" },
|
||||
openAiCompatibleOptions: {
|
||||
baseUrl: "https://api.example.com/v1",
|
||||
apiKey: "test-openai-compatible-key",
|
||||
},
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -212,9 +232,14 @@ describe("CodeIndexConfigManager", () => {
|
|||
baseUrl: "https://api.example.com/v1",
|
||||
apiKey: "test-openai-compatible-key",
|
||||
},
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -243,6 +268,7 @@ describe("CodeIndexConfigManager", () => {
|
|||
isConfigured: true,
|
||||
embedderProvider: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
modelDimension: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "" },
|
||||
ollamaOptions: { ollamaBaseUrl: "" },
|
||||
openAiCompatibleOptions: {
|
||||
|
|
@ -250,9 +276,14 @@ describe("CodeIndexConfigManager", () => {
|
|||
apiKey: "test-openai-compatible-key",
|
||||
// modelDimension is undefined when not set
|
||||
},
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -289,9 +320,13 @@ describe("CodeIndexConfigManager", () => {
|
|||
apiKey: "test-openai-compatible-key",
|
||||
},
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -1292,14 +1327,19 @@ describe("CodeIndexConfigManager", () => {
|
|||
isConfigured: true,
|
||||
embedderProvider: "openai",
|
||||
modelId: "text-embedding-3-large",
|
||||
modelDimension: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "test-openai-key" },
|
||||
ollamaOptions: { ollamaBaseUrl: undefined },
|
||||
geminiOptions: undefined,
|
||||
openAiCompatibleOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
qdrantUrl: "http://qdrant.local",
|
||||
qdrantApiKey: "test-qdrant-key",
|
||||
searchMinScore: 0.4,
|
||||
searchMaxResults: 50,
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
|
||||
mockConfigManager = {
|
||||
getConfig: vitest.fn(),
|
||||
memoryOptimizationConfig: {
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
}
|
||||
|
||||
mockCacheManager = {}
|
||||
|
|
@ -367,6 +372,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -392,6 +402,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
768,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -417,6 +432,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -449,6 +469,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
modelDimension, // Should use model's built-in dimension, not manual
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -480,6 +505,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
manualDimension, // Should use manual dimension as fallback
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -509,6 +539,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
768,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -578,6 +613,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -603,6 +643,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -627,6 +672,11 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
1536,
|
||||
"test-key",
|
||||
{
|
||||
useOnDiskStorage: true,
|
||||
memoryMapThreshold: 50000,
|
||||
hnswEfSearch: 128,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { EmbedderProvider } from "./interfaces/manager"
|
|||
import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config"
|
||||
import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants"
|
||||
import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../shared/embeddingModels"
|
||||
import { QDRANT_MEMORY_OPTIMIZATION_DEFAULTS } from "@roo-code/types"
|
||||
|
||||
/**
|
||||
* Manages configuration state and validation for the code indexing feature.
|
||||
|
|
@ -23,6 +24,10 @@ export class CodeIndexConfigManager {
|
|||
private qdrantApiKey?: string
|
||||
private searchMinScore?: number
|
||||
private searchMaxResults?: number
|
||||
// Memory optimization settings
|
||||
private useOnDiskStorage?: boolean
|
||||
private memoryMapThreshold?: number
|
||||
private hnswEfSearch?: number
|
||||
|
||||
constructor(private readonly contextProxy: ContextProxy) {
|
||||
// Initialize with current configuration to avoid false restart triggers
|
||||
|
|
@ -50,6 +55,9 @@ export class CodeIndexConfigManager {
|
|||
codebaseIndexEmbedderModelId: "",
|
||||
codebaseIndexSearchMinScore: undefined,
|
||||
codebaseIndexSearchMaxResults: undefined,
|
||||
codebaseIndexUseOnDiskStorage: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
codebaseIndexMemoryMapThreshold: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.MEMORY_MAP_THRESHOLD,
|
||||
codebaseIndexHnswEfSearch: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.HNSW_EF_SEARCH,
|
||||
}
|
||||
|
||||
const {
|
||||
|
|
@ -60,6 +68,9 @@ export class CodeIndexConfigManager {
|
|||
codebaseIndexEmbedderModelId,
|
||||
codebaseIndexSearchMinScore,
|
||||
codebaseIndexSearchMaxResults,
|
||||
codebaseIndexUseOnDiskStorage,
|
||||
codebaseIndexMemoryMapThreshold,
|
||||
codebaseIndexHnswEfSearch,
|
||||
} = codebaseIndexConfig
|
||||
|
||||
const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? ""
|
||||
|
|
@ -76,6 +87,10 @@ export class CodeIndexConfigManager {
|
|||
this.qdrantApiKey = qdrantApiKey ?? ""
|
||||
this.searchMinScore = codebaseIndexSearchMinScore
|
||||
this.searchMaxResults = codebaseIndexSearchMaxResults
|
||||
this.useOnDiskStorage = codebaseIndexUseOnDiskStorage ?? QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE
|
||||
this.memoryMapThreshold =
|
||||
codebaseIndexMemoryMapThreshold ?? QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.MEMORY_MAP_THRESHOLD
|
||||
this.hnswEfSearch = codebaseIndexHnswEfSearch ?? QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.HNSW_EF_SEARCH
|
||||
|
||||
// Validate and set model dimension
|
||||
const rawDimension = codebaseIndexConfig.codebaseIndexEmbedderModelDimension
|
||||
|
|
@ -144,6 +159,9 @@ export class CodeIndexConfigManager {
|
|||
qdrantUrl?: string
|
||||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
useOnDiskStorage?: boolean
|
||||
memoryMapThreshold?: number
|
||||
hnswEfSearch?: number
|
||||
}
|
||||
requiresRestart: boolean
|
||||
}> {
|
||||
|
|
@ -187,6 +205,9 @@ export class CodeIndexConfigManager {
|
|||
qdrantUrl: this.qdrantUrl,
|
||||
qdrantApiKey: this.qdrantApiKey,
|
||||
searchMinScore: this.currentSearchMinScore,
|
||||
useOnDiskStorage: this.useOnDiskStorage,
|
||||
memoryMapThreshold: this.memoryMapThreshold,
|
||||
hnswEfSearch: this.hnswEfSearch,
|
||||
},
|
||||
requiresRestart,
|
||||
}
|
||||
|
|
@ -379,6 +400,9 @@ export class CodeIndexConfigManager {
|
|||
qdrantApiKey: this.qdrantApiKey,
|
||||
searchMinScore: this.currentSearchMinScore,
|
||||
searchMaxResults: this.currentSearchMaxResults,
|
||||
useOnDiskStorage: this.useOnDiskStorage,
|
||||
memoryMapThreshold: this.memoryMapThreshold,
|
||||
hnswEfSearch: this.hnswEfSearch,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -413,6 +437,21 @@ export class CodeIndexConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memory optimization settings
|
||||
*/
|
||||
public get memoryOptimizationConfig(): {
|
||||
useOnDiskStorage?: boolean
|
||||
memoryMapThreshold?: number
|
||||
hnswEfSearch?: number
|
||||
} {
|
||||
return {
|
||||
useOnDiskStorage: this.useOnDiskStorage,
|
||||
memoryMapThreshold: this.memoryMapThreshold,
|
||||
hnswEfSearch: this.hnswEfSearch,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current model ID being used for embeddings.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ApiHandlerOptions } from "../../../shared/api" // Adjust path if needed
|
||||
import { EmbedderProvider } from "./manager"
|
||||
import { QdrantMemoryOptimizationConfig } from "@roo-code/types"
|
||||
|
||||
/**
|
||||
* Configuration state for the code indexing feature
|
||||
|
|
@ -18,6 +19,10 @@ export interface CodeIndexConfig {
|
|||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
searchMaxResults?: number
|
||||
// Memory optimization settings
|
||||
useOnDiskStorage?: boolean
|
||||
memoryMapThreshold?: number
|
||||
hnswEfSearch?: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -37,4 +42,8 @@ export type PreviousConfigSnapshot = {
|
|||
mistralApiKey?: string
|
||||
qdrantUrl?: string
|
||||
qdrantApiKey?: string
|
||||
// Memory optimization settings
|
||||
useOnDiskStorage?: boolean
|
||||
memoryMapThreshold?: number
|
||||
hnswEfSearch?: number
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,8 +136,17 @@ export class CodeIndexServiceFactory {
|
|||
throw new Error(t("embeddings:serviceFactory.qdrantUrlMissing"))
|
||||
}
|
||||
|
||||
// Assuming constructor is updated: new QdrantVectorStore(workspacePath, url, vectorSize, apiKey?)
|
||||
return new QdrantVectorStore(this.workspacePath, config.qdrantUrl, vectorSize, config.qdrantApiKey)
|
||||
// Get memory optimization config from config manager
|
||||
const memoryOptimization = this.configManager.memoryOptimizationConfig
|
||||
|
||||
// Create QdrantVectorStore with memory optimization settings
|
||||
return new QdrantVectorStore(
|
||||
this.workspacePath,
|
||||
config.qdrantUrl,
|
||||
vectorSize,
|
||||
config.qdrantApiKey,
|
||||
memoryOptimization,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -528,6 +528,25 @@ describe("QdrantVectorStore", () => {
|
|||
vectors: {
|
||||
size: mockVectorSize,
|
||||
distance: "Cosine", // Assuming 'Cosine' is the DISTANCE_METRIC
|
||||
on_disk: true, // Default memory optimization
|
||||
},
|
||||
hnsw_config: {
|
||||
m: 16,
|
||||
ef_construct: 100,
|
||||
full_scan_threshold: 10000,
|
||||
max_indexing_threads: 0,
|
||||
on_disk: true,
|
||||
payload_m: null,
|
||||
},
|
||||
optimizers_config: {
|
||||
deleted_threshold: 0.2,
|
||||
vacuum_min_vector_number: 1000,
|
||||
default_segment_number: 2,
|
||||
max_segment_size: null,
|
||||
memmap_threshold: 50000,
|
||||
indexing_threshold: 20000,
|
||||
flush_interval_sec: 5,
|
||||
max_optimization_threads: 0,
|
||||
},
|
||||
})
|
||||
expect(mockQdrantClientInstance.deleteCollection).not.toHaveBeenCalled()
|
||||
|
|
@ -606,6 +625,25 @@ describe("QdrantVectorStore", () => {
|
|||
vectors: {
|
||||
size: mockVectorSize, // Should use the new, correct vector size
|
||||
distance: "Cosine",
|
||||
on_disk: true, // Default memory optimization
|
||||
},
|
||||
hnsw_config: {
|
||||
m: 16,
|
||||
ef_construct: 100,
|
||||
full_scan_threshold: 10000,
|
||||
max_indexing_threads: 0,
|
||||
on_disk: true,
|
||||
payload_m: null,
|
||||
},
|
||||
optimizers_config: {
|
||||
deleted_threshold: 0.2,
|
||||
vacuum_min_vector_number: 1000,
|
||||
default_segment_number: 2,
|
||||
max_segment_size: null,
|
||||
memmap_threshold: 50000,
|
||||
indexing_threshold: 20000,
|
||||
flush_interval_sec: 5,
|
||||
max_optimization_threads: 0,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -899,6 +937,25 @@ describe("QdrantVectorStore", () => {
|
|||
vectors: {
|
||||
size: newVectorSize, // Should create with new 768 dimensions
|
||||
distance: "Cosine",
|
||||
on_disk: true, // Default memory optimization
|
||||
},
|
||||
hnsw_config: {
|
||||
m: 16,
|
||||
ef_construct: 100,
|
||||
full_scan_threshold: 10000,
|
||||
max_indexing_threads: 0,
|
||||
on_disk: true,
|
||||
payload_m: null,
|
||||
},
|
||||
optimizers_config: {
|
||||
deleted_threshold: 0.2,
|
||||
vacuum_min_vector_number: 1000,
|
||||
default_segment_number: 2,
|
||||
max_segment_size: null,
|
||||
memmap_threshold: 50000,
|
||||
indexing_threshold: 20000,
|
||||
flush_interval_sec: 5,
|
||||
max_optimization_threads: 0,
|
||||
},
|
||||
})
|
||||
expect(mockQdrantClientInstance.createPayloadIndex).toHaveBeenCalledTimes(5)
|
||||
|
|
@ -1244,8 +1301,13 @@ describe("QdrantVectorStore", () => {
|
|||
score_threshold: DEFAULT_SEARCH_MIN_SCORE,
|
||||
limit: DEFAULT_MAX_SEARCH_RESULTS,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: 128, // Default memory optimized value
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: false,
|
||||
rescore: true,
|
||||
oversampling: 2.0,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
@ -1295,8 +1357,13 @@ describe("QdrantVectorStore", () => {
|
|||
score_threshold: DEFAULT_SEARCH_MIN_SCORE,
|
||||
limit: DEFAULT_MAX_SEARCH_RESULTS,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: 128, // Default memory optimized value
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: false,
|
||||
rescore: true,
|
||||
oversampling: 2.0,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
@ -1321,8 +1388,13 @@ describe("QdrantVectorStore", () => {
|
|||
score_threshold: customMinScore,
|
||||
limit: DEFAULT_MAX_SEARCH_RESULTS,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: 128, // Default memory optimized value
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: false,
|
||||
rescore: true,
|
||||
oversampling: 2.0,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
@ -1345,8 +1417,13 @@ describe("QdrantVectorStore", () => {
|
|||
score_threshold: DEFAULT_SEARCH_MIN_SCORE,
|
||||
limit: customMaxResults,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: 128, // Default memory optimized value
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: false,
|
||||
rescore: true,
|
||||
oversampling: 2.0,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
@ -1492,8 +1569,13 @@ describe("QdrantVectorStore", () => {
|
|||
score_threshold: DEFAULT_SEARCH_MIN_SCORE,
|
||||
limit: DEFAULT_MAX_SEARCH_RESULTS,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: 128, // Default memory optimized value
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: false,
|
||||
rescore: true,
|
||||
oversampling: 2.0,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ import { IVectorStore } from "../interfaces/vector-store"
|
|||
import { Payload, VectorStoreSearchResult } from "../interfaces"
|
||||
import { DEFAULT_MAX_SEARCH_RESULTS, DEFAULT_SEARCH_MIN_SCORE } from "../constants"
|
||||
import { t } from "../../../i18n"
|
||||
import {
|
||||
QdrantMemoryOptimizationConfig,
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS,
|
||||
QDRANT_HNSW_CONFIG_DEFAULTS,
|
||||
QDRANT_OPTIMIZER_CONFIG_DEFAULTS,
|
||||
QDRANT_QUANTIZATION_CONFIG_DEFAULTS,
|
||||
} from "@roo-code/types"
|
||||
|
||||
/**
|
||||
* Qdrant implementation of the vector store interface
|
||||
|
|
@ -17,13 +24,23 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
private client: QdrantClient
|
||||
private readonly collectionName: string
|
||||
private readonly qdrantUrl: string = "http://localhost:6333"
|
||||
private readonly memoryOptimization: QdrantMemoryOptimizationConfig
|
||||
|
||||
/**
|
||||
* Creates a new Qdrant vector store
|
||||
* @param workspacePath Path to the workspace
|
||||
* @param url Optional URL to the Qdrant server
|
||||
* @param vectorSize Size of the vectors
|
||||
* @param apiKey Optional API key for authentication
|
||||
* @param memoryOptimization Optional memory optimization settings
|
||||
*/
|
||||
constructor(workspacePath: string, url: string, vectorSize: number, apiKey?: string) {
|
||||
constructor(
|
||||
workspacePath: string,
|
||||
url: string,
|
||||
vectorSize: number,
|
||||
apiKey?: string,
|
||||
memoryOptimization?: QdrantMemoryOptimizationConfig,
|
||||
) {
|
||||
// Parse the URL to determine the appropriate QdrantClient configuration
|
||||
const parsedUrl = this.parseQdrantUrl(url)
|
||||
|
||||
|
|
@ -79,6 +96,11 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
const hash = createHash("sha256").update(workspacePath).digest("hex")
|
||||
this.vectorSize = vectorSize
|
||||
this.collectionName = `ws-${hash.substring(0, 16)}`
|
||||
this.memoryOptimization = memoryOptimization || {
|
||||
useOnDiskStorage: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
memoryMapThreshold: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.MEMORY_MAP_THRESHOLD,
|
||||
hnswEfSearch: QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.HNSW_EF_SEARCH,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,6 +177,33 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
vectors: {
|
||||
size: this.vectorSize,
|
||||
distance: this.DISTANCE_METRIC,
|
||||
on_disk:
|
||||
this.memoryOptimization.useOnDiskStorage ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
},
|
||||
// Configure HNSW index for memory efficiency
|
||||
hnsw_config: {
|
||||
m: QDRANT_HNSW_CONFIG_DEFAULTS.M,
|
||||
ef_construct: QDRANT_HNSW_CONFIG_DEFAULTS.EF_CONSTRUCT,
|
||||
full_scan_threshold: QDRANT_HNSW_CONFIG_DEFAULTS.FULL_SCAN_THRESHOLD,
|
||||
max_indexing_threads: QDRANT_HNSW_CONFIG_DEFAULTS.MAX_INDEXING_THREADS,
|
||||
on_disk:
|
||||
this.memoryOptimization.useOnDiskStorage ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
payload_m: QDRANT_HNSW_CONFIG_DEFAULTS.PAYLOAD_M,
|
||||
},
|
||||
// Enable memory-mapped storage for better memory management
|
||||
optimizers_config: {
|
||||
deleted_threshold: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.DELETED_THRESHOLD,
|
||||
vacuum_min_vector_number: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.VACUUM_MIN_VECTOR_NUMBER,
|
||||
default_segment_number: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.DEFAULT_SEGMENT_NUMBER,
|
||||
max_segment_size: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.MAX_SEGMENT_SIZE,
|
||||
memmap_threshold:
|
||||
this.memoryOptimization.memoryMapThreshold ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.MEMORY_MAP_THRESHOLD,
|
||||
indexing_threshold: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.INDEXING_THRESHOLD,
|
||||
flush_interval_sec: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.FLUSH_INTERVAL_SEC,
|
||||
max_optimization_threads: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.MAX_OPTIMIZATION_THREADS,
|
||||
},
|
||||
})
|
||||
created = true
|
||||
|
|
@ -244,6 +293,33 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
vectors: {
|
||||
size: this.vectorSize,
|
||||
distance: this.DISTANCE_METRIC,
|
||||
on_disk:
|
||||
this.memoryOptimization.useOnDiskStorage ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
},
|
||||
// Configure HNSW index for memory efficiency
|
||||
hnsw_config: {
|
||||
m: QDRANT_HNSW_CONFIG_DEFAULTS.M,
|
||||
ef_construct: QDRANT_HNSW_CONFIG_DEFAULTS.EF_CONSTRUCT,
|
||||
full_scan_threshold: QDRANT_HNSW_CONFIG_DEFAULTS.FULL_SCAN_THRESHOLD,
|
||||
max_indexing_threads: QDRANT_HNSW_CONFIG_DEFAULTS.MAX_INDEXING_THREADS,
|
||||
on_disk:
|
||||
this.memoryOptimization.useOnDiskStorage ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.USE_ON_DISK_STORAGE,
|
||||
payload_m: QDRANT_HNSW_CONFIG_DEFAULTS.PAYLOAD_M,
|
||||
},
|
||||
// Enable memory-mapped storage for better memory management
|
||||
optimizers_config: {
|
||||
deleted_threshold: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.DELETED_THRESHOLD,
|
||||
vacuum_min_vector_number: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.VACUUM_MIN_VECTOR_NUMBER,
|
||||
default_segment_number: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.DEFAULT_SEGMENT_NUMBER,
|
||||
max_segment_size: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.MAX_SEGMENT_SIZE,
|
||||
memmap_threshold:
|
||||
this.memoryOptimization.memoryMapThreshold ??
|
||||
QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.MEMORY_MAP_THRESHOLD,
|
||||
indexing_threshold: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.INDEXING_THRESHOLD,
|
||||
flush_interval_sec: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.FLUSH_INTERVAL_SEC,
|
||||
max_optimization_threads: QDRANT_OPTIMIZER_CONFIG_DEFAULTS.MAX_OPTIMIZATION_THREADS,
|
||||
},
|
||||
})
|
||||
console.log(`[QdrantVectorStore] Successfully created new collection ${this.collectionName}`)
|
||||
|
|
@ -391,8 +467,13 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
score_threshold: minScore ?? DEFAULT_SEARCH_MIN_SCORE,
|
||||
limit: maxResults ?? DEFAULT_MAX_SEARCH_RESULTS,
|
||||
params: {
|
||||
hnsw_ef: 128,
|
||||
hnsw_ef: this.memoryOptimization.hnswEfSearch ?? QDRANT_MEMORY_OPTIMIZATION_DEFAULTS.HNSW_EF_SEARCH,
|
||||
exact: false,
|
||||
quantization: {
|
||||
ignore: QDRANT_QUANTIZATION_CONFIG_DEFAULTS.IGNORE,
|
||||
rescore: QDRANT_QUANTIZATION_CONFIG_DEFAULTS.RESCORE,
|
||||
oversampling: QDRANT_QUANTIZATION_CONFIG_DEFAULTS.OVERSAMPLING,
|
||||
},
|
||||
},
|
||||
with_payload: {
|
||||
include: ["filePath", "codeChunk", "startLine", "endLine", "pathSegments"],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue