Files
OpenHands/frontend/src/components/SettingModal.tsx
Jim Su 0a8a857d00 fix: apply settings without explicit change (#541)
* fix: apply settings without explicit change
* Change default model to gpt-4-0125-preview and don't print settings selection on startup
2024-04-02 09:49:43 -04:00

156 lines
4.6 KiB
TypeScript

import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Input,
Button,
Autocomplete,
AutocompleteItem,
} from "@nextui-org/react";
import { KeyboardEvent } from "@react-types/shared/src/events";
import {
INITIAL_AGENTS,
fetchModels,
fetchAgents,
INITIAL_MODELS,
sendSettings,
} from "../services/settingsService";
import {
setModel,
setAgent,
setWorkspaceDirectory,
} from "../state/settingsSlice";
import store, { RootState } from "../store";
import socket from "../socket/socket";
interface Props {
isOpen: boolean;
onClose: () => void;
}
const cachedModels = JSON.parse(
localStorage.getItem("supportedModels") || "[]",
);
const cachedAgents = JSON.parse(
localStorage.getItem("supportedAgents") || "[]",
);
function SettingModal({ isOpen, onClose }: Props): JSX.Element {
const model = useSelector((state: RootState) => state.settings.model);
const agent = useSelector((state: RootState) => state.settings.agent);
const workspaceDirectory = useSelector(
(state: RootState) => state.settings.workspaceDirectory,
);
const [supportedModels, setSupportedModels] = useState(
cachedModels.length > 0 ? cachedModels : INITIAL_MODELS,
);
const [supportedAgents, setSupportedAgents] = useState(
cachedAgents.length > 0 ? cachedAgents : INITIAL_AGENTS,
);
useEffect(() => {
fetchModels().then((fetchedModels) => {
setSupportedModels(fetchedModels);
localStorage.setItem("supportedModels", JSON.stringify(fetchedModels));
});
fetchAgents().then((fetchedAgents) => {
setSupportedAgents(fetchedAgents);
localStorage.setItem("supportedAgents", JSON.stringify(fetchedAgents));
});
}, []);
const handleSaveCfg = () => {
sendSettings(socket, { model, agent, workspaceDirectory });
localStorage.setItem("model", model);
localStorage.setItem("workspaceDirectory", workspaceDirectory);
localStorage.setItem("agent", agent);
onClose();
};
const customFilter = (item: string, input: string) =>
item.toLowerCase().includes(input.toLowerCase());
return (
<Modal isOpen={isOpen} onClose={onClose} hideCloseButton backdrop="blur">
<ModalContent>
<>
<ModalHeader className="flex flex-col gap-1">
Configuration
</ModalHeader>
<ModalBody>
<Input
type="text"
label="OpenDevin Workspace Directory"
defaultValue={workspaceDirectory}
placeholder="Default: ./workspace"
onChange={(e) =>
store.dispatch(setWorkspaceDirectory(e.target.value))
}
/>
<Autocomplete
defaultItems={supportedModels.map((v: string) => ({
label: v,
value: v,
}))}
label="Model"
placeholder="Select a model"
defaultSelectedKey={model}
// className="max-w-xs"
onSelectionChange={(key) => {
store.dispatch(setModel(key as string));
}}
onKeyDown={(e: KeyboardEvent) => e.continuePropagation()}
defaultFilter={customFilter}
>
{(item: { label: string; value: string }) => (
<AutocompleteItem key={item.value} value={item.value}>
{item.label}
</AutocompleteItem>
)}
</Autocomplete>
<Autocomplete
defaultItems={supportedAgents.map((v: string) => ({
label: v,
value: v,
}))}
label="Agent"
placeholder="Select a agent"
defaultSelectedKey={agent}
// className="max-w-xs"
onSelectionChange={(key) => {
store.dispatch(setAgent(key as string));
}}
onKeyDown={(e: KeyboardEvent) => e.continuePropagation()}
defaultFilter={customFilter}
>
{(item: { label: string; value: string }) => (
<AutocompleteItem key={item.value} value={item.value}>
{item.label}
</AutocompleteItem>
)}
</Autocomplete>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<Button color="primary" onPress={handleSaveCfg}>
Save
</Button>
</ModalFooter>
</>
</ModalContent>
</Modal>
);
}
export default SettingModal;