mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: add Qdrant collection name field to codebase indexing settings
- Add UI field in CodeIndexPopover for viewing/editing collection name - Update configuration interfaces to include qdrantCollectionName - Modify QdrantVectorStore to accept optional collection name parameter - Update config manager to handle collection name in settings - Add translation keys for new UI labels - Update tests to account for new parameter Fixes #7728
This commit is contained in:
parent
e8deedd91b
commit
43f94412e1
10 changed files with 85 additions and 8 deletions
|
|
@ -21,6 +21,7 @@ export const CODEBASE_INDEX_DEFAULTS = {
|
|||
export const codebaseIndexConfigSchema = z.object({
|
||||
codebaseIndexEnabled: z.boolean().optional(),
|
||||
codebaseIndexQdrantUrl: z.string().optional(),
|
||||
codebaseIndexQdrantCollectionName: z.string().optional(),
|
||||
codebaseIndexEmbedderProvider: z
|
||||
.enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway"])
|
||||
.optional(),
|
||||
|
|
|
|||
|
|
@ -2270,6 +2270,7 @@ export const webviewMessageHandler = async (
|
|||
...currentConfig,
|
||||
codebaseIndexEnabled: settings.codebaseIndexEnabled,
|
||||
codebaseIndexQdrantUrl: settings.codebaseIndexQdrantUrl,
|
||||
codebaseIndexQdrantCollectionName: settings.codebaseIndexQdrantCollectionName,
|
||||
codebaseIndexEmbedderProvider: settings.codebaseIndexEmbedderProvider,
|
||||
codebaseIndexEmbedderBaseUrl: settings.codebaseIndexEmbedderBaseUrl,
|
||||
codebaseIndexEmbedderModelId: settings.codebaseIndexEmbedderModelId,
|
||||
|
|
|
|||
|
|
@ -104,8 +104,14 @@ describe("CodeIndexConfigManager", () => {
|
|||
modelId: undefined,
|
||||
openAiOptions: { openAiNativeApiKey: "" },
|
||||
ollamaOptions: { ollamaBaseUrl: "" },
|
||||
geminiOptions: undefined,
|
||||
mistralOptions: undefined,
|
||||
openAiCompatibleOptions: undefined,
|
||||
vercelAiGatewayOptions: undefined,
|
||||
modelDimension: undefined,
|
||||
qdrantUrl: "http://localhost:6333",
|
||||
qdrantApiKey: "",
|
||||
qdrantCollectionName: "",
|
||||
searchMinScore: 0.4,
|
||||
})
|
||||
expect(result.requiresRestart).toBe(false)
|
||||
|
|
|
|||
|
|
@ -367,6 +367,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -392,6 +393,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
768,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -417,6 +419,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -449,6 +452,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
modelDimension, // Should use model's built-in dimension, not manual
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -480,6 +484,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
manualDimension, // Should use manual dimension as fallback
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -509,6 +514,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
768,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -578,6 +584,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -603,6 +610,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
3072,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -627,6 +635,7 @@ describe("CodeIndexServiceFactory", () => {
|
|||
"http://localhost:6333",
|
||||
1536,
|
||||
"test-key",
|
||||
undefined, // collectionName
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export class CodeIndexConfigManager {
|
|||
private mistralOptions?: { apiKey: string }
|
||||
private vercelAiGatewayOptions?: { apiKey: string }
|
||||
private qdrantUrl?: string = "http://localhost:6333"
|
||||
private qdrantCollectionName?: string
|
||||
private qdrantApiKey?: string
|
||||
private searchMinScore?: number
|
||||
private searchMaxResults?: number
|
||||
|
|
@ -46,6 +47,7 @@ export class CodeIndexConfigManager {
|
|||
const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? {
|
||||
codebaseIndexEnabled: true,
|
||||
codebaseIndexQdrantUrl: "http://localhost:6333",
|
||||
codebaseIndexQdrantCollectionName: "",
|
||||
codebaseIndexEmbedderProvider: "openai",
|
||||
codebaseIndexEmbedderBaseUrl: "",
|
||||
codebaseIndexEmbedderModelId: "",
|
||||
|
|
@ -56,6 +58,7 @@ export class CodeIndexConfigManager {
|
|||
const {
|
||||
codebaseIndexEnabled,
|
||||
codebaseIndexQdrantUrl,
|
||||
codebaseIndexQdrantCollectionName,
|
||||
codebaseIndexEmbedderProvider,
|
||||
codebaseIndexEmbedderBaseUrl,
|
||||
codebaseIndexEmbedderModelId,
|
||||
|
|
@ -75,6 +78,7 @@ export class CodeIndexConfigManager {
|
|||
// Update instance variables with configuration
|
||||
this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
|
||||
this.qdrantUrl = codebaseIndexQdrantUrl
|
||||
this.qdrantCollectionName = codebaseIndexQdrantCollectionName
|
||||
this.qdrantApiKey = qdrantApiKey ?? ""
|
||||
this.searchMinScore = codebaseIndexSearchMinScore
|
||||
this.searchMaxResults = codebaseIndexSearchMaxResults
|
||||
|
|
@ -148,6 +152,7 @@ export class CodeIndexConfigManager {
|
|||
mistralOptions?: { apiKey: string }
|
||||
vercelAiGatewayOptions?: { apiKey: string }
|
||||
qdrantUrl?: string
|
||||
qdrantCollectionName?: string
|
||||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
}
|
||||
|
|
@ -168,6 +173,7 @@ export class CodeIndexConfigManager {
|
|||
mistralApiKey: this.mistralOptions?.apiKey ?? "",
|
||||
vercelAiGatewayApiKey: this.vercelAiGatewayOptions?.apiKey ?? "",
|
||||
qdrantUrl: this.qdrantUrl ?? "",
|
||||
qdrantCollectionName: this.qdrantCollectionName ?? "",
|
||||
qdrantApiKey: this.qdrantApiKey ?? "",
|
||||
}
|
||||
|
||||
|
|
@ -193,6 +199,7 @@ export class CodeIndexConfigManager {
|
|||
mistralOptions: this.mistralOptions,
|
||||
vercelAiGatewayOptions: this.vercelAiGatewayOptions,
|
||||
qdrantUrl: this.qdrantUrl,
|
||||
qdrantCollectionName: this.qdrantCollectionName,
|
||||
qdrantApiKey: this.qdrantApiKey,
|
||||
searchMinScore: this.currentSearchMinScore,
|
||||
},
|
||||
|
|
@ -270,6 +277,7 @@ export class CodeIndexConfigManager {
|
|||
const prevMistralApiKey = prev?.mistralApiKey ?? ""
|
||||
const prevVercelAiGatewayApiKey = prev?.vercelAiGatewayApiKey ?? ""
|
||||
const prevQdrantUrl = prev?.qdrantUrl ?? ""
|
||||
const prevQdrantCollectionName = prev?.qdrantCollectionName ?? ""
|
||||
const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
|
||||
|
||||
// 1. Transition from disabled/unconfigured to enabled/configured
|
||||
|
|
@ -308,6 +316,7 @@ export class CodeIndexConfigManager {
|
|||
const currentMistralApiKey = this.mistralOptions?.apiKey ?? ""
|
||||
const currentVercelAiGatewayApiKey = this.vercelAiGatewayOptions?.apiKey ?? ""
|
||||
const currentQdrantUrl = this.qdrantUrl ?? ""
|
||||
const currentQdrantCollectionName = this.qdrantCollectionName ?? ""
|
||||
const currentQdrantApiKey = this.qdrantApiKey ?? ""
|
||||
|
||||
if (prevOpenAiKey !== currentOpenAiKey) {
|
||||
|
|
@ -342,7 +351,11 @@ export class CodeIndexConfigManager {
|
|||
return true
|
||||
}
|
||||
|
||||
if (prevQdrantUrl !== currentQdrantUrl || prevQdrantApiKey !== currentQdrantApiKey) {
|
||||
if (
|
||||
prevQdrantUrl !== currentQdrantUrl ||
|
||||
prevQdrantCollectionName !== currentQdrantCollectionName ||
|
||||
prevQdrantApiKey !== currentQdrantApiKey
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -396,6 +409,7 @@ export class CodeIndexConfigManager {
|
|||
mistralOptions: this.mistralOptions,
|
||||
vercelAiGatewayOptions: this.vercelAiGatewayOptions,
|
||||
qdrantUrl: this.qdrantUrl,
|
||||
qdrantCollectionName: this.qdrantCollectionName,
|
||||
qdrantApiKey: this.qdrantApiKey,
|
||||
searchMinScore: this.currentSearchMinScore,
|
||||
searchMaxResults: this.currentSearchMaxResults,
|
||||
|
|
@ -426,9 +440,10 @@ export class CodeIndexConfigManager {
|
|||
/**
|
||||
* Gets the current Qdrant configuration
|
||||
*/
|
||||
public get qdrantConfig(): { url?: string; apiKey?: string } {
|
||||
public get qdrantConfig(): { url?: string; collectionName?: string; apiKey?: string } {
|
||||
return {
|
||||
url: this.qdrantUrl,
|
||||
collectionName: this.qdrantCollectionName,
|
||||
apiKey: this.qdrantApiKey,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export interface CodeIndexConfig {
|
|||
mistralOptions?: { apiKey: string }
|
||||
vercelAiGatewayOptions?: { apiKey: string }
|
||||
qdrantUrl?: string
|
||||
qdrantCollectionName?: string
|
||||
qdrantApiKey?: string
|
||||
searchMinScore?: number
|
||||
searchMaxResults?: number
|
||||
|
|
@ -38,5 +39,6 @@ export type PreviousConfigSnapshot = {
|
|||
mistralApiKey?: string
|
||||
vercelAiGatewayApiKey?: string
|
||||
qdrantUrl?: string
|
||||
qdrantCollectionName?: string
|
||||
qdrantApiKey?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,8 +145,14 @@ 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)
|
||||
// Pass collection name if configured, otherwise let QdrantVectorStore generate it
|
||||
return new QdrantVectorStore(
|
||||
this.workspacePath,
|
||||
config.qdrantUrl,
|
||||
vectorSize,
|
||||
config.qdrantApiKey,
|
||||
config.qdrantCollectionName,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
* @param workspacePath Path to the workspace
|
||||
* @param url Optional URL to the Qdrant server
|
||||
*/
|
||||
constructor(workspacePath: string, url: string, vectorSize: number, apiKey?: string) {
|
||||
constructor(workspacePath: string, url: string, vectorSize: number, apiKey?: string, collectionName?: string) {
|
||||
// Parse the URL to determine the appropriate QdrantClient configuration
|
||||
const parsedUrl = this.parseQdrantUrl(url)
|
||||
|
||||
|
|
@ -77,10 +77,17 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
})
|
||||
}
|
||||
|
||||
// Generate collection name from workspace path
|
||||
const hash = createHash("sha256").update(workspacePath).digest("hex")
|
||||
// Use provided collection name or generate from workspace path
|
||||
if (collectionName && collectionName.trim()) {
|
||||
// Sanitize the collection name to ensure it's valid for Qdrant
|
||||
// Qdrant collection names must match: ^[a-zA-Z0-9_-]+$
|
||||
this.collectionName = collectionName.trim().replace(/[^a-zA-Z0-9_-]/g, "-")
|
||||
} else {
|
||||
// Generate collection name from workspace path (default behavior)
|
||||
const hash = createHash("sha256").update(workspacePath).digest("hex")
|
||||
this.collectionName = `ws-${hash.substring(0, 16)}`
|
||||
}
|
||||
this.vectorSize = vectorSize
|
||||
this.collectionName = `ws-${hash.substring(0, 16)}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ interface LocalCodeIndexSettings {
|
|||
// Global state settings
|
||||
codebaseIndexEnabled: boolean
|
||||
codebaseIndexQdrantUrl: string
|
||||
codebaseIndexQdrantCollectionName?: string
|
||||
codebaseIndexEmbedderProvider: EmbedderProvider
|
||||
codebaseIndexEmbedderBaseUrl?: string
|
||||
codebaseIndexEmbedderModelId: string
|
||||
|
|
@ -181,6 +182,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
const getDefaultSettings = (): LocalCodeIndexSettings => ({
|
||||
codebaseIndexEnabled: true,
|
||||
codebaseIndexQdrantUrl: "",
|
||||
codebaseIndexQdrantCollectionName: "",
|
||||
codebaseIndexEmbedderProvider: "openai",
|
||||
codebaseIndexEmbedderBaseUrl: "",
|
||||
codebaseIndexEmbedderModelId: "",
|
||||
|
|
@ -213,6 +215,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
const settings = {
|
||||
codebaseIndexEnabled: codebaseIndexConfig.codebaseIndexEnabled ?? true,
|
||||
codebaseIndexQdrantUrl: codebaseIndexConfig.codebaseIndexQdrantUrl || "",
|
||||
codebaseIndexQdrantCollectionName: codebaseIndexConfig.codebaseIndexQdrantCollectionName || "",
|
||||
codebaseIndexEmbedderProvider: codebaseIndexConfig.codebaseIndexEmbedderProvider || "openai",
|
||||
codebaseIndexEmbedderBaseUrl: codebaseIndexConfig.codebaseIndexEmbedderBaseUrl || "",
|
||||
codebaseIndexEmbedderModelId: codebaseIndexConfig.codebaseIndexEmbedderModelId || "",
|
||||
|
|
@ -1160,6 +1163,30 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
{t("settings:codeIndex.qdrantCollectionNameLabel")}
|
||||
</label>
|
||||
<VSCodeTextField
|
||||
value={currentSettings.codebaseIndexQdrantCollectionName || ""}
|
||||
onInput={(e: any) =>
|
||||
updateSetting("codebaseIndexQdrantCollectionName", e.target.value)
|
||||
}
|
||||
placeholder={t("settings:codeIndex.qdrantCollectionNamePlaceholder")}
|
||||
className={cn("w-full", {
|
||||
"border-red-500": formErrors.codebaseIndexQdrantCollectionName,
|
||||
})}
|
||||
/>
|
||||
{formErrors.codebaseIndexQdrantCollectionName && (
|
||||
<p className="text-xs text-vscode-errorForeground mt-1 mb-0">
|
||||
{formErrors.codebaseIndexQdrantCollectionName}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-vscode-descriptionForeground mt-1">
|
||||
{t("settings:codeIndex.qdrantCollectionNameDescription")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
{t("settings:codeIndex.qdrantApiKeyLabel")}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@
|
|||
"qdrantKeyLabel": "Qdrant Key:",
|
||||
"qdrantApiKeyLabel": "Qdrant API Key",
|
||||
"qdrantApiKeyPlaceholder": "Enter your Qdrant API key (optional)",
|
||||
"qdrantCollectionNameLabel": "Qdrant Collection Name",
|
||||
"qdrantCollectionNamePlaceholder": "Enter collection name (optional)",
|
||||
"qdrantCollectionNameDescription": "Custom collection name for storing embeddings. If empty, a name will be auto-generated from your workspace path.",
|
||||
"setupConfigLabel": "Setup",
|
||||
"advancedConfigLabel": "Advanced Configuration",
|
||||
"searchMinScoreLabel": "Search Score Threshold",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue