Files
OpenHands/frontend/src/components/SettingModal.tsx
Robert Brennan 9fd7068204 Fix for setting LLM model, frontend settings refactor (#1169)
* simplify frontend settings management

* add debug info to llm.py

* always reinitialize agent

* remove old config stuff

* delint

* fix first initialize event

* refactor settings management

* remove logs

* change endpoint to remove litellm reference

* actually fix socket issues

* refactor a bit

* delint

* remove isFirstRun

* delint

* delint python

* fix export

* fix up socket handshake

* fix types

* fix lint errors

* delint

* fix test names

* moar lint

* fix build errors

* remove newline

* Update frontend/src/services/settingsService.test.ts

* Update frontend/src/services/settingsService.test.ts
2024-04-17 17:49:38 +00:00

155 lines
4.8 KiB
TypeScript

import React, { useEffect, useState } from "react";
import {
Autocomplete,
AutocompleteItem,
Button,
Select,
SelectItem,
} from "@nextui-org/react";
import { KeyboardEvent } from "@react-types/shared/src/events";
import { useTranslation } from "react-i18next";
import {
fetchAgents,
fetchModels,
saveSettings,
getCurrentSettings,
Settings,
} from "../services/settingsService";
import { I18nKey } from "../i18n/declaration";
import { AvailableLanguages } from "../i18n";
import { ArgConfigType } from "../types/ConfigType";
import ODModal from "./ODModal";
interface Props {
isOpen: boolean;
onClose: () => void;
}
function InnerSettingModal({ isOpen, onClose }: Props): JSX.Element {
const currentSettings: Settings = getCurrentSettings();
const [model, setModel] = useState(currentSettings[ArgConfigType.LLM_MODEL]);
const [inputModel, setInputModel] = useState(
currentSettings[ArgConfigType.LLM_MODEL],
);
const [agent, setAgent] = useState(currentSettings[ArgConfigType.AGENT]);
const [language, setLanguage] = useState(
currentSettings[ArgConfigType.LANGUAGE],
);
const { t } = useTranslation();
const [supportedModels, setSupportedModels] = useState([]);
const [supportedAgents, setSupportedAgents] = useState([]);
useEffect(() => {
fetchModels().then((fetchedModels) => {
const sortedModels = fetchedModels.sort(); // Sorting the models alphabetically
setSupportedModels(sortedModels);
});
fetchAgents().then((fetchedAgents) => {
setSupportedAgents(fetchedAgents);
});
}, []);
const handleSaveCfg = () => {
saveSettings({
[ArgConfigType.LLM_MODEL]: model ?? inputModel,
[ArgConfigType.AGENT]: agent,
[ArgConfigType.LANGUAGE]: language,
});
onClose();
};
const customFilter = (item: string, input: string) =>
item.toLowerCase().includes(input.toLowerCase());
return (
<ODModal
isOpen={isOpen}
onClose={onClose}
title={t(I18nKey.CONFIGURATION$MODAL_TITLE)}
subtitle={t(I18nKey.CONFIGURATION$MODAL_SUB_TITLE)}
hideCloseButton
backdrop="blur"
size="sm"
primaryAction={
<Button className="bg-primary rounded-small" onPress={handleSaveCfg}>
{t(I18nKey.CONFIGURATION$MODAL_SAVE_BUTTON_LABEL)}
</Button>
}
secondaryAction={
<Button className="bg-neutral-500 rounded-small" onPress={onClose}>
{t(I18nKey.CONFIGURATION$MODAL_CLOSE_BUTTON_LABEL)}
</Button>
}
>
<>
<Autocomplete
defaultItems={supportedModels.map((v: string) => ({
label: v,
value: v,
}))}
label={t(I18nKey.CONFIGURATION$MODEL_SELECT_LABEL)}
placeholder={t(I18nKey.CONFIGURATION$MODEL_SELECT_PLACEHOLDER)}
selectedKey={model}
onSelectionChange={(key) => {
setModel(key as string);
}}
onInputChange={(e) => setInputModel(e)}
onKeyDown={(e: KeyboardEvent) => e.continuePropagation()}
defaultFilter={customFilter}
defaultInputValue={inputModel}
allowsCustomValue
>
{(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={t(I18nKey.CONFIGURATION$AGENT_SELECT_LABEL)}
placeholder={t(I18nKey.CONFIGURATION$AGENT_SELECT_PLACEHOLDER)}
defaultSelectedKey={agent}
onSelectionChange={(key) => {
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>
<Select
selectionMode="single"
onChange={(e) => setLanguage(e.target.value)}
selectedKeys={[language || ""]}
label={t(I18nKey.CONFIGURATION$LANGUAGE_SELECT_LABEL)}
>
{AvailableLanguages.map((lang) => (
<SelectItem key={lang.value} value={lang.value}>
{lang.label}
</SelectItem>
))}
</Select>
</>
</ODModal>
);
}
function SettingModal({ isOpen, onClose }: Props): JSX.Element {
// Do not render the modal if it is not open, prevents reading empty from localStorage after initialization
if (!isOpen) return <div />;
return <InnerSettingModal isOpen={isOpen} onClose={onClose} />;
}
export default SettingModal;