Просмотр исходного кода

Move codebase indexing out of experimental (#5481)

Daniel 5 месяцев назад
Родитель
Сommit
09e348e21c

+ 2 - 2
src/core/webview/ClineProvider.ts

@@ -1510,7 +1510,7 @@ export class ClineProvider
 			customCondensingPrompt,
 			codebaseIndexModels: codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
 			codebaseIndexConfig: codebaseIndexConfig ?? {
-				codebaseIndexEnabled: false,
+				codebaseIndexEnabled: true,
 				codebaseIndexQdrantUrl: "http://localhost:6333",
 				codebaseIndexEmbedderProvider: "openai",
 				codebaseIndexEmbedderBaseUrl: "",
@@ -1668,7 +1668,7 @@ export class ClineProvider
 			customCondensingPrompt: stateValues.customCondensingPrompt,
 			codebaseIndexModels: stateValues.codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
 			codebaseIndexConfig: stateValues.codebaseIndexConfig ?? {
-				codebaseIndexEnabled: false,
+				codebaseIndexEnabled: true,
 				codebaseIndexQdrantUrl: "http://localhost:6333",
 				codebaseIndexEmbedderProvider: "openai",
 				codebaseIndexEmbedderBaseUrl: "",

+ 1 - 1
src/core/webview/__tests__/ClineProvider.spec.ts

@@ -501,7 +501,7 @@ describe("ClineProvider", () => {
 			alwaysAllowReadOnlyOutsideWorkspace: false,
 			alwaysAllowWrite: false,
 			codebaseIndexConfig: {
-				codebaseIndexEnabled: false,
+				codebaseIndexEnabled: true,
 				codebaseIndexQdrantUrl: "",
 				codebaseIndexEmbedderProvider: "openai",
 				codebaseIndexEmbedderBaseUrl: "",

+ 0 - 15
src/core/webview/webviewMessageHandler.ts

@@ -1221,21 +1221,6 @@ export const webviewMessageHandler = async (
 			break
 		case "browserToolEnabled":
 			await updateGlobalState("browserToolEnabled", message.bool ?? true)
-			await provider.postStateToWebview()
-			break
-		case "codebaseIndexEnabled":
-			// Update the codebaseIndexConfig with the new enabled state
-			const currentCodebaseConfig = getGlobalState("codebaseIndexConfig") || {}
-			await updateGlobalState("codebaseIndexConfig", {
-				...currentCodebaseConfig,
-				codebaseIndexEnabled: message.bool ?? false,
-			})
-
-			// Notify the code index manager about the change
-			if (provider.codeIndexManager) {
-				await provider.codeIndexManager.handleSettingsChange()
-			}
-
 			await provider.postStateToWebview()
 			break
 		case "language":

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

@@ -32,7 +32,7 @@ describe("CodeIndexConfigManager", () => {
 	describe("constructor", () => {
 		it("should initialize with ContextProxy", () => {
 			expect(configManager).toBeDefined()
-			expect(configManager.isFeatureEnabled).toBe(false)
+			expect(configManager.isFeatureEnabled).toBe(true)
 			expect(configManager.currentEmbedderProvider).toBe("openai")
 		})
 	})
@@ -45,7 +45,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: false,
 				isConfigured: false,
 				embedderProvider: "openai",
 				modelId: undefined,
@@ -77,7 +76,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai",
 				modelId: "text-embedding-3-large",
@@ -111,7 +109,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai-compatible",
 				modelId: "text-embedding-3-large",
@@ -149,7 +146,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai-compatible",
 				modelId: "custom-model",
@@ -188,7 +184,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai-compatible",
 				modelId: "custom-model",
@@ -227,7 +222,6 @@ describe("CodeIndexConfigManager", () => {
 			const result = await configManager.loadConfiguration()
 
 			expect(result.currentConfig).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai-compatible",
 				modelId: "custom-model",
@@ -326,14 +320,14 @@ describe("CodeIndexConfigManager", () => {
 		})
 
 		it("should detect restart requirement when transitioning to enabled+configured", async () => {
-			// Initial state - disabled
+			// Initial state - enabled but not configured
 			mockContextProxy.getGlobalState.mockReturnValue({
-				codebaseIndexEnabled: false,
+				codebaseIndexEnabled: true,
 			})
 
 			await configManager.loadConfiguration()
 
-			// Enable and configure
+			// Configure the feature
 			mockContextProxy.getGlobalState.mockReturnValue({
 				codebaseIndexEnabled: true,
 				codebaseIndexQdrantUrl: "http://qdrant.local",
@@ -689,29 +683,28 @@ describe("CodeIndexConfigManager", () => {
 				expect(result.requiresRestart).toBe(true)
 			})
 
-			it("should not require restart when disabled remains disabled", async () => {
-				// Initial state - disabled but configured
+			it("should require restart when enabled and provider changes even if unconfigured", async () => {
+				// Initial state - enabled but not configured (missing API key)
 				mockContextProxy.getGlobalState.mockReturnValue({
-					codebaseIndexEnabled: false,
+					codebaseIndexEnabled: true,
 					codebaseIndexQdrantUrl: "http://qdrant.local",
 					codebaseIndexEmbedderProvider: "openai",
 				})
-				setupSecretMocks({
-					codeIndexOpenAiKey: "test-key",
-				})
+				setupSecretMocks({})
 
 				await configManager.loadConfiguration()
 
-				// Still disabled but change other settings
+				// Still enabled but change provider while remaining unconfigured
 				mockContextProxy.getGlobalState.mockReturnValue({
-					codebaseIndexEnabled: false,
-					codebaseIndexQdrantUrl: "http://different-qdrant.local",
+					codebaseIndexEnabled: true,
+					codebaseIndexQdrantUrl: "http://qdrant.local",
 					codebaseIndexEmbedderProvider: "ollama",
 					codebaseIndexEmbedderBaseUrl: "http://ollama.local",
 				})
 
 				const result = await configManager.loadConfiguration()
-				expect(result.requiresRestart).toBe(false)
+				// Should require restart because provider changed while enabled
+				expect(result.requiresRestart).toBe(true)
 			})
 
 			it("should not require restart when unconfigured remains unconfigured", async () => {
@@ -970,7 +963,7 @@ describe("CodeIndexConfigManager", () => {
 			it("should not require restart when API keys transition from undefined to empty string", async () => {
 				// Initial state with undefined API keys
 				mockContextProxy.getGlobalState.mockReturnValue({
-					codebaseIndexEnabled: false, // Start disabled to avoid restart due to enable+configure
+					codebaseIndexEnabled: true, // Always enabled now
 					codebaseIndexQdrantUrl: "http://qdrant.local",
 					codebaseIndexEmbedderProvider: "openai",
 				})
@@ -1208,7 +1201,6 @@ describe("CodeIndexConfigManager", () => {
 		it("should return correct configuration via getConfig", () => {
 			const config = configManager.getConfig()
 			expect(config).toEqual({
-				isEnabled: true,
 				isConfigured: true,
 				embedderProvider: "openai",
 				modelId: "text-embedding-3-large",
@@ -1267,7 +1259,7 @@ describe("CodeIndexConfigManager", () => {
 		it("should properly initialize with current config to prevent false restarts", async () => {
 			// Setup configuration
 			mockContextProxy.getGlobalState.mockReturnValue({
-				codebaseIndexEnabled: false, // Start disabled to avoid transition restart
+				codebaseIndexEnabled: true, // Always enabled now
 				codebaseIndexQdrantUrl: "http://qdrant.local",
 				codebaseIndexEmbedderProvider: "openai",
 				codebaseIndexEmbedderModelId: "text-embedding-3-small",

+ 0 - 3
src/services/code-index/__tests__/manager.spec.ts

@@ -81,7 +81,6 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
 				isFeatureConfigured: true,
 				isFeatureEnabled: true,
 				getConfig: vi.fn().mockReturnValue({
-					isEnabled: true,
 					isConfigured: true,
 					embedderProvider: "openai",
 					modelId: "text-embedding-3-small",
@@ -149,7 +148,6 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
 				isFeatureConfigured: true,
 				isFeatureEnabled: true,
 				getConfig: vi.fn().mockReturnValue({
-					isEnabled: true,
 					isConfigured: true,
 					embedderProvider: "openai",
 					modelId: "text-embedding-3-small",
@@ -276,7 +274,6 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
 				isFeatureConfigured: true,
 				isFeatureEnabled: true,
 				getConfig: vitest.fn().mockReturnValue({
-					isEnabled: true,
 					isConfigured: true,
 					embedderProvider: "openai",
 					modelId: "text-embedding-3-small",

+ 45 - 53
src/services/code-index/config-manager.ts

@@ -10,7 +10,6 @@ import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "..
  * Handles loading, validating, and providing access to configuration values.
  */
 export class CodeIndexConfigManager {
-	private isEnabled: boolean = false
 	private embedderProvider: EmbedderProvider = "openai"
 	private modelId?: string
 	private modelDimension?: number
@@ -42,7 +41,7 @@ export class CodeIndexConfigManager {
 	private _loadAndSetConfiguration(): void {
 		// Load configuration from storage
 		const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? {
-			codebaseIndexEnabled: false,
+			codebaseIndexEnabled: true,
 			codebaseIndexQdrantUrl: "http://localhost:6333",
 			codebaseIndexEmbedderProvider: "openai",
 			codebaseIndexEmbedderBaseUrl: "",
@@ -69,7 +68,7 @@ export class CodeIndexConfigManager {
 		const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
 
 		// Update instance variables with configuration
-		this.isEnabled = codebaseIndexEnabled || false
+		// Note: codebaseIndexEnabled is no longer used as the feature is always enabled
 		this.qdrantUrl = codebaseIndexQdrantUrl
 		this.qdrantApiKey = qdrantApiKey ?? ""
 		this.searchMinScore = codebaseIndexSearchMinScore
@@ -127,7 +126,6 @@ export class CodeIndexConfigManager {
 	public async loadConfiguration(): Promise<{
 		configSnapshot: PreviousConfigSnapshot
 		currentConfig: {
-			isEnabled: boolean
 			isConfigured: boolean
 			embedderProvider: EmbedderProvider
 			modelId?: string
@@ -144,7 +142,7 @@ export class CodeIndexConfigManager {
 	}> {
 		// Capture the ACTUAL previous state before loading new configuration
 		const previousConfigSnapshot: PreviousConfigSnapshot = {
-			enabled: this.isEnabled,
+			enabled: true, // Feature is always enabled
 			configured: this.isConfigured(),
 			embedderProvider: this.embedderProvider,
 			modelId: this.modelId,
@@ -169,7 +167,6 @@ export class CodeIndexConfigManager {
 		return {
 			configSnapshot: previousConfigSnapshot,
 			currentConfig: {
-				isEnabled: this.isEnabled,
 				isConfigured: this.isConfigured(),
 				embedderProvider: this.embedderProvider,
 				modelId: this.modelId,
@@ -246,66 +243,62 @@ export class CodeIndexConfigManager {
 		const prevQdrantUrl = prev?.qdrantUrl ?? ""
 		const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
 
-		// 1. Transition from disabled/unconfigured to enabled+configured
-		if ((!prevEnabled || !prevConfigured) && this.isEnabled && nowConfigured) {
+		// 1. Transition from unconfigured to configured
+		// Since the feature is always enabled, we only check configuration status
+		if (!prevConfigured && nowConfigured) {
 			return true
 		}
 
-		// 2. If was disabled and still is, no restart needed
-		if (!prevEnabled && !this.isEnabled) {
-			return false
-		}
-
 		// 3. If wasn't ready before and isn't ready now, no restart needed
 		if (!prevConfigured && !nowConfigured) {
 			return false
 		}
 
 		// 4. CRITICAL CHANGES - Always restart for these
-		if (this.isEnabled || prevEnabled) {
-			// Provider change
-			if (prevProvider !== this.embedderProvider) {
-				return true
-			}
+		// Since feature is always enabled, we always check for critical changes
 
-			// Authentication changes (API keys)
-			const currentOpenAiKey = this.openAiOptions?.openAiNativeApiKey ?? ""
-			const currentOllamaBaseUrl = this.ollamaOptions?.ollamaBaseUrl ?? ""
-			const currentOpenAiCompatibleBaseUrl = this.openAiCompatibleOptions?.baseUrl ?? ""
-			const currentOpenAiCompatibleApiKey = this.openAiCompatibleOptions?.apiKey ?? ""
-			const currentModelDimension = this.modelDimension
-			const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
-			const currentQdrantUrl = this.qdrantUrl ?? ""
-			const currentQdrantApiKey = this.qdrantApiKey ?? ""
-
-			if (prevOpenAiKey !== currentOpenAiKey) {
-				return true
-			}
+		// Provider change
+		if (prevProvider !== this.embedderProvider) {
+			return true
+		}
 
-			if (prevOllamaBaseUrl !== currentOllamaBaseUrl) {
-				return true
-			}
+		// Authentication changes (API keys)
+		const currentOpenAiKey = this.openAiOptions?.openAiNativeApiKey ?? ""
+		const currentOllamaBaseUrl = this.ollamaOptions?.ollamaBaseUrl ?? ""
+		const currentOpenAiCompatibleBaseUrl = this.openAiCompatibleOptions?.baseUrl ?? ""
+		const currentOpenAiCompatibleApiKey = this.openAiCompatibleOptions?.apiKey ?? ""
+		const currentModelDimension = this.modelDimension
+		const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
+		const currentQdrantUrl = this.qdrantUrl ?? ""
+		const currentQdrantApiKey = this.qdrantApiKey ?? ""
+
+		if (prevOpenAiKey !== currentOpenAiKey) {
+			return true
+		}
 
-			if (
-				prevOpenAiCompatibleBaseUrl !== currentOpenAiCompatibleBaseUrl ||
-				prevOpenAiCompatibleApiKey !== currentOpenAiCompatibleApiKey
-			) {
-				return true
-			}
+		if (prevOllamaBaseUrl !== currentOllamaBaseUrl) {
+			return true
+		}
 
-			// Check for model dimension changes (generic for all providers)
-			if (prevModelDimension !== currentModelDimension) {
-				return true
-			}
+		if (
+			prevOpenAiCompatibleBaseUrl !== currentOpenAiCompatibleBaseUrl ||
+			prevOpenAiCompatibleApiKey !== currentOpenAiCompatibleApiKey
+		) {
+			return true
+		}
 
-			if (prevQdrantUrl !== currentQdrantUrl || prevQdrantApiKey !== currentQdrantApiKey) {
-				return true
-			}
+		// Check for model dimension changes (generic for all providers)
+		if (prevModelDimension !== currentModelDimension) {
+			return true
+		}
 
-			// Vector dimension changes (still important for compatibility)
-			if (this._hasVectorDimensionChanged(prevProvider, prev?.modelId)) {
-				return true
-			}
+		if (prevQdrantUrl !== currentQdrantUrl || prevQdrantApiKey !== currentQdrantApiKey) {
+			return true
+		}
+
+		// Vector dimension changes (still important for compatibility)
+		if (this._hasVectorDimensionChanged(prevProvider, prev?.modelId)) {
+			return true
 		}
 
 		return false
@@ -342,7 +335,6 @@ export class CodeIndexConfigManager {
 	 */
 	public getConfig(): CodeIndexConfig {
 		return {
-			isEnabled: this.isEnabled,
 			isConfigured: this.isConfigured(),
 			embedderProvider: this.embedderProvider,
 			modelId: this.modelId,
@@ -362,7 +354,7 @@ export class CodeIndexConfigManager {
 	 * Gets whether the code indexing feature is enabled
 	 */
 	public get isFeatureEnabled(): boolean {
-		return this.isEnabled
+		return true
 	}
 
 	/**

+ 0 - 1
src/services/code-index/interfaces/config.ts

@@ -5,7 +5,6 @@ import { EmbedderProvider } from "./manager"
  * Configuration state for the code indexing feature
  */
 export interface CodeIndexConfig {
-	isEnabled: boolean
 	isConfigured: boolean
 	embedderProvider: EmbedderProvider
 	modelId?: string

+ 2 - 2
webview-ui/src/components/chat/CodeIndexPopover.tsx

@@ -81,7 +81,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 
 	// Default settings template
 	const getDefaultSettings = (): LocalCodeIndexSettings => ({
-		codebaseIndexEnabled: false,
+		codebaseIndexEnabled: true,
 		codebaseIndexQdrantUrl: "",
 		codebaseIndexEmbedderProvider: "openai",
 		codebaseIndexEmbedderBaseUrl: "",
@@ -111,7 +111,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 	useEffect(() => {
 		if (codebaseIndexConfig) {
 			const settings = {
-				codebaseIndexEnabled: codebaseIndexConfig.codebaseIndexEnabled || false,
+				codebaseIndexEnabled: codebaseIndexConfig.codebaseIndexEnabled ?? true,
 				codebaseIndexQdrantUrl: codebaseIndexConfig.codebaseIndexQdrantUrl || "",
 				codebaseIndexEmbedderProvider: codebaseIndexConfig.codebaseIndexEmbedderProvider || "openai",
 				codebaseIndexEmbedderBaseUrl: codebaseIndexConfig.codebaseIndexEmbedderBaseUrl || "",

+ 1 - 41
webview-ui/src/components/settings/ExperimentalSettings.tsx

@@ -1,40 +1,26 @@
 import { HTMLAttributes } from "react"
 import { FlaskConical } from "lucide-react"
-import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
-import { Trans } from "react-i18next"
 
-import type { Experiments, CodebaseIndexConfig, CodebaseIndexModels } from "@roo-code/types"
+import type { Experiments } from "@roo-code/types"
 
 import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments"
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { cn } from "@src/lib/utils"
-import { buildDocLink } from "@src/utils/docLinks"
 
 import { SetExperimentEnabled } from "./types"
 import { SectionHeader } from "./SectionHeader"
 import { Section } from "./Section"
 import { ExperimentalFeature } from "./ExperimentalFeature"
-import { SetCachedStateField } from "./types"
 
 type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	experiments: Experiments
 	setExperimentEnabled: SetExperimentEnabled
-	// CodeIndexSettings props
-	codebaseIndexModels: CodebaseIndexModels | undefined
-	codebaseIndexConfig: CodebaseIndexConfig | undefined
-	// For codebase index enabled toggle
-	codebaseIndexEnabled?: boolean
-	setCachedStateField?: SetCachedStateField<any>
 }
 
 export const ExperimentalSettings = ({
 	experiments,
 	setExperimentEnabled,
-	codebaseIndexModels,
-	codebaseIndexConfig,
-	codebaseIndexEnabled,
-	setCachedStateField,
 	className,
 	...props
 }: ExperimentalSettingsProps) => {
@@ -79,32 +65,6 @@ export const ExperimentalSettings = ({
 							/>
 						)
 					})}
-
-				{/* Codebase Indexing Enable/Disable Toggle */}
-				<div className="mt-4">
-					<div className="flex items-center gap-2">
-						<VSCodeCheckbox
-							checked={codebaseIndexEnabled || false}
-							onChange={(e: any) => {
-								const newEnabledState = e.target.checked
-								if (setCachedStateField && codebaseIndexConfig) {
-									setCachedStateField("codebaseIndexConfig", {
-										...codebaseIndexConfig,
-										codebaseIndexEnabled: newEnabledState,
-									})
-								}
-							}}>
-							<span className="font-medium">{t("settings:codeIndex.enableLabel")}</span>
-						</VSCodeCheckbox>
-					</div>
-					<p className="text-vscode-descriptionForeground text-sm mt-1 ml-6">
-						<Trans i18nKey="settings:codeIndex.enableDescription">
-							<VSCodeLink
-								href={buildDocLink("features/experimental/codebase-indexing", "settings")}
-								style={{ display: "inline" }}></VSCodeLink>
-						</Trans>
-					</p>
-				</div>
 			</Section>
 		</div>
 	)

+ 1 - 13
webview-ui/src/components/settings/SettingsView.tsx

@@ -170,8 +170,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		maxConcurrentFileReads,
 		condensingApiConfigId,
 		customCondensingPrompt,
-		codebaseIndexConfig,
-		codebaseIndexModels,
 		customSupportPrompts,
 		profileThresholds,
 		alwaysAllowFollowupQuestions,
@@ -322,9 +320,6 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} })
 			vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
 			vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
-			if (codebaseIndexConfig) {
-				vscode.postMessage({ type: "codebaseIndexEnabled", bool: codebaseIndexConfig.codebaseIndexEnabled })
-			}
 			vscode.postMessage({ type: "profileThresholds", values: profileThresholds })
 			setChangeDetected(false)
 		}
@@ -691,14 +686,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 
 					{/* Experimental Section */}
 					{activeTab === "experimental" && (
-						<ExperimentalSettings
-							setExperimentEnabled={setExperimentEnabled}
-							experiments={experiments}
-							codebaseIndexModels={codebaseIndexModels}
-							codebaseIndexConfig={codebaseIndexConfig}
-							codebaseIndexEnabled={codebaseIndexConfig?.codebaseIndexEnabled}
-							setCachedStateField={setCachedStateField}
-						/>
+						<ExperimentalSettings setExperimentEnabled={setExperimentEnabled} experiments={experiments} />
 					)}
 
 					{/* Language Section */}

+ 1 - 1
webview-ui/src/context/ExtensionStateContext.tsx

@@ -214,7 +214,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		autoCondenseContextPercent: 100,
 		profileThresholds: {},
 		codebaseIndexConfig: {
-			codebaseIndexEnabled: false,
+			codebaseIndexEnabled: true,
 			codebaseIndexQdrantUrl: "http://localhost:6333",
 			codebaseIndexEmbedderProvider: "openai",
 			codebaseIndexEmbedderBaseUrl: "",