Browse Source

Merge pull request #432 from samhvw8/fix/config-profile-and-input-lagging

fix: config-manager
Matt Rubens 11 months ago
parent
commit
0f1f4f737f

+ 20 - 26
webview-ui/src/components/settings/ApiOptions.tsx

@@ -45,7 +45,7 @@ interface ApiOptionsProps {
 }
 
 const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) => {
-	const { apiConfiguration, setApiConfiguration, uriScheme, onUpdateApiConfig } = useExtensionState()
+	const { apiConfiguration, setApiConfiguration, uriScheme, handleInputChange } = useExtensionState()
 	const [ollamaModels, setOllamaModels] = useState<string[]>([])
 	const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
 	const [vsCodeLmModels, setVsCodeLmModels] = useState<vscodemodels.LanguageModelChatSelector[]>([])
@@ -53,12 +53,6 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 	const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion)
 	const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
 
-	const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
-		const apiConfig = { ...apiConfiguration, [field]: event.target.value }
-		onUpdateApiConfig(apiConfig)
-		// setApiConfiguration(apiConfig)
-	}
-
 	const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => {
 		return normalizeApiConfiguration(apiConfiguration)
 	}, [apiConfiguration])
@@ -162,7 +156,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.apiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("apiKey")}
+						onChange={handleInputChange("apiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>Anthropic API Key</span>
 					</VSCodeTextField>
@@ -183,7 +177,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 							value={apiConfiguration?.anthropicBaseUrl || ""}
 							style={{ width: "100%", marginTop: 3 }}
 							type="url"
-							onInput={handleInputChange("anthropicBaseUrl")}
+							onChange={handleInputChange("anthropicBaseUrl")}
 							placeholder="Default: https://api.anthropic.com"
 						/>
 					)}
@@ -212,7 +206,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.glamaApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("glamaApiKey")}
+						onChange={handleInputChange("glamaApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>Glama API Key</span>
 					</VSCodeTextField>
@@ -241,7 +235,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.openAiNativeApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("openAiNativeApiKey")}
+						onChange={handleInputChange("openAiNativeApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>OpenAI API Key</span>
 					</VSCodeTextField>
@@ -269,7 +263,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.mistralApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("mistralApiKey")}
+						onChange={handleInputChange("mistralApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>Mistral API Key</span>
 					</VSCodeTextField>
@@ -300,7 +294,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.openRouterApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("openRouterApiKey")}
+						onChange={handleInputChange("openRouterApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>OpenRouter API Key</span>
 					</VSCodeTextField>
@@ -346,7 +340,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.awsAccessKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("awsAccessKey")}
+						onChange={handleInputChange("awsAccessKey")}
 						placeholder="Enter Access Key...">
 						<span style={{ fontWeight: 500 }}>AWS Access Key</span>
 					</VSCodeTextField>
@@ -354,7 +348,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.awsSecretKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("awsSecretKey")}
+						onChange={handleInputChange("awsSecretKey")}
 						placeholder="Enter Secret Key...">
 						<span style={{ fontWeight: 500 }}>AWS Secret Key</span>
 					</VSCodeTextField>
@@ -362,7 +356,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.awsSessionToken || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("awsSessionToken")}
+						onChange={handleInputChange("awsSessionToken")}
 						placeholder="Enter Session Token...">
 						<span style={{ fontWeight: 500 }}>AWS Session Token</span>
 					</VSCodeTextField>
@@ -428,7 +422,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 					<VSCodeTextField
 						value={apiConfiguration?.vertexProjectId || ""}
 						style={{ width: "100%" }}
-						onInput={handleInputChange("vertexProjectId")}
+						onChange={handleInputChange("vertexProjectId")}
 						placeholder="Enter Project ID...">
 						<span style={{ fontWeight: 500 }}>Google Cloud Project ID</span>
 					</VSCodeTextField>
@@ -486,7 +480,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.geminiApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("geminiApiKey")}
+						onChange={handleInputChange("geminiApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>Gemini API Key</span>
 					</VSCodeTextField>
@@ -514,7 +508,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.openAiBaseUrl || ""}
 						style={{ width: "100%" }}
 						type="url"
-						onInput={handleInputChange("openAiBaseUrl")}
+						onChange={handleInputChange("openAiBaseUrl")}
 						placeholder={"Enter base URL..."}>
 						<span style={{ fontWeight: 500 }}>Base URL</span>
 					</VSCodeTextField>
@@ -522,7 +516,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.openAiApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("openAiApiKey")}
+						onChange={handleInputChange("openAiApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>API Key</span>
 					</VSCodeTextField>
@@ -552,7 +546,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						<VSCodeTextField
 							value={apiConfiguration?.azureApiVersion || ""}
 							style={{ width: "100%", marginTop: 3 }}
-							onInput={handleInputChange("azureApiVersion")}
+							onChange={handleInputChange("azureApiVersion")}
 							placeholder={`Default: ${azureOpenAiDefaultApiVersion}`}
 						/>
 					)}
@@ -576,14 +570,14 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.lmStudioBaseUrl || ""}
 						style={{ width: "100%" }}
 						type="url"
-						onInput={handleInputChange("lmStudioBaseUrl")}
+						onChange={handleInputChange("lmStudioBaseUrl")}
 						placeholder={"Default: http://localhost:1234"}>
 						<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
 					</VSCodeTextField>
 					<VSCodeTextField
 						value={apiConfiguration?.lmStudioModelId || ""}
 						style={{ width: "100%" }}
-						onInput={handleInputChange("lmStudioModelId")}
+						onChange={handleInputChange("lmStudioModelId")}
 						placeholder={"e.g. meta-llama-3.1-8b-instruct"}>
 						<span style={{ fontWeight: 500 }}>Model ID</span>
 					</VSCodeTextField>
@@ -645,7 +639,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.deepSeekApiKey || ""}
 						style={{ width: "100%" }}
 						type="password"
-						onInput={handleInputChange("deepSeekApiKey")}
+						onChange={handleInputChange("deepSeekApiKey")}
 						placeholder="Enter API Key...">
 						<span style={{ fontWeight: 500 }}>DeepSeek API Key</span>
 					</VSCodeTextField>
@@ -735,14 +729,14 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
 						value={apiConfiguration?.ollamaBaseUrl || ""}
 						style={{ width: "100%" }}
 						type="url"
-						onInput={handleInputChange("ollamaBaseUrl")}
+						onChange={handleInputChange("ollamaBaseUrl")}
 						placeholder={"Default: http://localhost:11434"}>
 						<span style={{ fontWeight: 500 }}>Base URL (optional)</span>
 					</VSCodeTextField>
 					<VSCodeTextField
 						value={apiConfiguration?.ollamaModelId || ""}
 						style={{ width: "100%" }}
-						onInput={handleInputChange("ollamaModelId")}
+						onChange={handleInputChange("ollamaModelId")}
 						placeholder={"e.g. llama3.1"}>
 						<span style={{ fontWeight: 500 }}>Model ID</span>
 					</VSCodeTextField>

+ 10 - 1
webview-ui/src/components/settings/GlamaModelPicker.tsx

@@ -167,9 +167,18 @@ const GlamaModelPicker: React.FC = () => {
 						placeholder="Search and select a model..."
 						value={searchTerm}
 						onInput={(e) => {
-							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+							const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
+							const apiConfig = {
+								...apiConfiguration,
+								openAiModelId: newModelId,
+							}
+							setApiConfiguration(apiConfig)
+							setSearchTerm(newModelId)
 							setIsDropdownVisible(true)
 						}}
+						onChange={(e) => {
+							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+						}}
 						onFocus={() => setIsDropdownVisible(true)}
 						onKeyDown={handleKeyDown}
 						style={{ width: "100%", zIndex: GLAMA_MODEL_PICKER_Z_INDEX, position: "relative" }}>

+ 10 - 3
webview-ui/src/components/settings/OpenAiModelPicker.tsx

@@ -25,8 +25,6 @@ const OpenAiModelPicker: React.FC = () => {
 		}
 		setApiConfiguration(apiConfig)
 		onUpdateApiConfig(apiConfig)
-
-		setSearchTerm(newModelId)
 	}
 
 	useEffect(() => {
@@ -161,9 +159,18 @@ const OpenAiModelPicker: React.FC = () => {
 						placeholder="Search and select a model..."
 						value={searchTerm}
 						onInput={(e) => {
-							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+							const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
+							const apiConfig = {
+								...apiConfiguration,
+								openAiModelId: newModelId,
+							}
+							setApiConfiguration(apiConfig)
+							setSearchTerm(newModelId)
 							setIsDropdownVisible(true)
 						}}
+						onChange={(e) => {
+							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+						}}
 						onFocus={() => setIsDropdownVisible(true)}
 						onKeyDown={handleKeyDown}
 						style={{ width: "100%", zIndex: OPENAI_MODEL_PICKER_Z_INDEX, position: "relative" }}>

+ 10 - 1
webview-ui/src/components/settings/OpenRouterModelPicker.tsx

@@ -167,9 +167,18 @@ const OpenRouterModelPicker: React.FC = () => {
 						placeholder="Search and select a model..."
 						value={searchTerm}
 						onInput={(e) => {
-							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+							const newModelId = (e.target as HTMLInputElement)?.value?.toLowerCase()
+							const apiConfig = {
+								...apiConfiguration,
+								openAiModelId: newModelId,
+							}
+							setApiConfiguration(apiConfig)
+							setSearchTerm(newModelId)
 							setIsDropdownVisible(true)
 						}}
+						onChange={(e) => {
+							handleModelChange((e.target as HTMLInputElement)?.value?.toLowerCase())
+						}}
 						onFocus={() => setIsDropdownVisible(true)}
 						onKeyDown={handleKeyDown}
 						style={{ width: "100%", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX, position: "relative" }}>

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

@@ -65,6 +65,7 @@ export interface ExtensionStateContextType extends ExtensionState {
 	setExperimentalDiffStrategy: (value: boolean) => void
 	autoApprovalEnabled?: boolean
 	setAutoApprovalEnabled: (value: boolean) => void
+	handleInputChange: (field: keyof ApiConfiguration) => (event: any) => void
 }
 
 export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@@ -119,13 +120,27 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setState((currentState) => {
 			vscode.postMessage({
 				type: "upsertApiConfiguration",
-				text: currentState.currentApiConfigName, // Access latest state
+				text: currentState.currentApiConfigName,
 				apiConfiguration: apiConfig,
 			})
 			return currentState // No state update needed
 		})
 	}, [])
 
+	const handleInputChange = useCallback(
+		(field: keyof ApiConfiguration) => (event: any) => {
+			setState((currentState) => {
+				vscode.postMessage({
+					type: "upsertApiConfiguration",
+					text: currentState.currentApiConfigName,
+					apiConfiguration: { ...currentState.apiConfiguration, [field]: event.target.value },
+				})
+				return currentState // No state update needed
+			})
+		},
+		[],
+	)
+
 	const handleMessage = useCallback(
 		(event: MessageEvent) => {
 			const message: ExtensionMessage = event.data
@@ -258,6 +273,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setExperimentalDiffStrategy: (value) =>
 			setState((prevState) => ({ ...prevState, experimentalDiffStrategy: value })),
 		setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })),
+		handleInputChange,
 	}
 
 	return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>