411 lines
14 KiB
TypeScript
411 lines
14 KiB
TypeScript
import React, { useState, useEffect, ReactElement } from 'react';
|
|
// import FormGroup from '@mui/material/FormGroup';
|
|
// import FormControlLabel from '@mui/material/FormControlLabel';
|
|
// import Switch from '@mui/material/Switch';
|
|
// import Divider from '@mui/material/Divider';
|
|
// import TextField from '@mui/material/TextField';
|
|
import Accordion from '@mui/material/Accordion';
|
|
import AccordionActions from '@mui/material/AccordionActions';
|
|
import AccordionSummary from '@mui/material/AccordionSummary';
|
|
import AccordionDetails from '@mui/material/AccordionDetails';
|
|
import Typography from '@mui/material/Typography';
|
|
// import Button from '@mui/material/Button';
|
|
// import Box from '@mui/material/Box';
|
|
// import ResetIcon from '@mui/icons-material/History';
|
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
|
|
import { BackstoryPageProps } from '../../components/BackstoryTab';
|
|
import { useAppState } from 'hooks/GlobalContext';
|
|
import { useAuth } from 'hooks/AuthContext';
|
|
import * as Types from 'types/types';
|
|
|
|
// interface ServerTunables {
|
|
// system_prompt: string,
|
|
// tools: Tool[],
|
|
// rags: Tool[]
|
|
// };
|
|
|
|
// type Tool = {
|
|
// type: string,
|
|
// enabled: boolean
|
|
// name: string,
|
|
// description: string,
|
|
// parameters?: any,
|
|
// returns?: any
|
|
// };
|
|
|
|
const SystemInfoComponent: React.FC<{
|
|
systemInfo: Types.SystemInfo | undefined;
|
|
}> = ({ systemInfo }) => {
|
|
const [systemElements, setSystemElements] = useState<ReactElement[]>([]);
|
|
|
|
const convertToSymbols = (text: string) => {
|
|
return text
|
|
.replace(/\(R\)/g, '®') // Replace (R) with the ® symbol
|
|
.replace(/\(C\)/g, '©') // Replace (C) with the © symbol
|
|
.replace(/\(TM\)/g, '™'); // Replace (TM) with the ™ symbol
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (systemInfo === undefined) {
|
|
return;
|
|
}
|
|
const elements = Object.entries(systemInfo).flatMap(([k, v]) => {
|
|
// If v is an array, repeat for each card
|
|
if (Array.isArray(v)) {
|
|
return v.map((card, index) => (
|
|
<div key={index} className="SystemInfoItem">
|
|
<div>
|
|
{convertToSymbols(k)} {index}
|
|
</div>
|
|
<div>
|
|
{convertToSymbols(card.name)}{' '}
|
|
{card.discrete
|
|
? `w/ ${Math.round(card.memory / (1024 * 1024 * 1024))}GB RAM`
|
|
: '(integrated)'}
|
|
</div>
|
|
</div>
|
|
));
|
|
}
|
|
|
|
// If it's not an array, handle normally
|
|
return (
|
|
<div key={k} className="SystemInfoItem">
|
|
<div>{convertToSymbols(k)}</div>
|
|
<div>{convertToSymbols(String(v))}</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
setSystemElements(elements);
|
|
}, [systemInfo]);
|
|
|
|
return <div className="SystemInfo">{systemElements}</div>;
|
|
};
|
|
|
|
const Settings = (_props: BackstoryPageProps) => {
|
|
const { apiClient } = useAuth();
|
|
const { setSnack } = useAppState();
|
|
// const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
|
|
const [systemInfo, setSystemInfo] = useState<Types.SystemInfo | undefined>(undefined);
|
|
// const [tools, setTools] = useState<Tool[]>([]);
|
|
// const [rags, setRags] = useState<Tool[]>([]);
|
|
// const [systemPrompt, setSystemPrompt] = useState<string>("");
|
|
// const [messageHistoryLength, setMessageHistoryLength] = useState<number>(5);
|
|
// const [serverTunables, setServerTunables] = useState<ServerTunables | undefined>(undefined);
|
|
|
|
// useEffect(() => {
|
|
// if (serverTunables === undefined || systemPrompt === serverTunables.system_prompt || !systemPrompt.trim()) {
|
|
// return;
|
|
// }
|
|
// const sendSystemPrompt = async (prompt: string) => {
|
|
// try {
|
|
// const response = await fetch(connectionBase + `/api/1.0/tunables`, {
|
|
// method: 'PUT',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// 'Accept': 'application/json',
|
|
// },
|
|
// body: JSON.stringify({ "system_prompt": prompt }),
|
|
// });
|
|
|
|
// const tunables = await response.json();
|
|
// serverTunables.system_prompt = tunables.system_prompt;
|
|
// console.log(tunables);
|
|
// setSystemPrompt(tunables.system_prompt)
|
|
// setSnack("System prompt updated", "success");
|
|
// } catch (error) {
|
|
// console.error('Fetch error:', error);
|
|
// setSnack("System prompt update failed", "error");
|
|
// }
|
|
// };
|
|
|
|
// sendSystemPrompt(systemPrompt);
|
|
|
|
// }, [systemPrompt, setSnack, serverTunables]);
|
|
|
|
// const reset = async (types: ("rags" | "tools" | "history" | "system_prompt")[], message: string = "Update successful.") => {
|
|
// try {
|
|
// const response = await fetch(connectionBase + `/api/1.0/reset/`, {
|
|
// method: 'PUT',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// 'Accept': 'application/json',
|
|
// },
|
|
// body: JSON.stringify({ "reset": types }),
|
|
// });
|
|
|
|
// if (!response.ok) {
|
|
// throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
|
|
// }
|
|
|
|
// if (!response.body) {
|
|
// throw new Error('Response body is null');
|
|
// }
|
|
|
|
// const data = await response.json();
|
|
// if (data.error) {
|
|
// throw Error(data.error);
|
|
// }
|
|
|
|
// for (const [key, value] of Object.entries(data)) {
|
|
// switch (key) {
|
|
// case "rags":
|
|
// setRags(value as Tool[]);
|
|
// break;
|
|
// case "tools":
|
|
// setTools(value as Tool[]);
|
|
// break;
|
|
// case "system_prompt":
|
|
// setSystemPrompt((value as ServerTunables)["system_prompt"].trim());
|
|
// break;
|
|
// case "history":
|
|
// console.log('TODO: handle history reset');
|
|
// break;
|
|
// }
|
|
// }
|
|
// setSnack(message, "success");
|
|
// } catch (error) {
|
|
// console.error('Fetch error:', error);
|
|
// setSnack("Unable to restore defaults", "error");
|
|
// }
|
|
// };
|
|
|
|
// Get the system information
|
|
useEffect(() => {
|
|
if (systemInfo !== undefined) {
|
|
return;
|
|
}
|
|
const fetchSystemInfo = async () => {
|
|
try {
|
|
const response: Types.SystemInfo = await apiClient.getSystemInfo();
|
|
setSystemInfo(response);
|
|
} catch (error) {
|
|
console.error('Error obtaining system information:', error);
|
|
setSnack('Unable to obtain system information.', 'error');
|
|
}
|
|
};
|
|
|
|
fetchSystemInfo();
|
|
}, [systemInfo, setSystemInfo, setSnack, apiClient]);
|
|
|
|
// useEffect(() => {
|
|
// if (!systemPrompt) {
|
|
// return;
|
|
// }
|
|
// setEditSystemPrompt(systemPrompt.trim());
|
|
// }, [systemPrompt, setEditSystemPrompt]);
|
|
|
|
// const toggleRag = async (tool: Tool) => {
|
|
// tool.enabled = !tool.enabled
|
|
// try {
|
|
// const response = await fetch(connectionBase + `/api/1.0/tunables`, {
|
|
// method: 'PUT',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// 'Accept': 'application/json',
|
|
// },
|
|
// body: JSON.stringify({ "rags": [{ "name": tool?.name, "enabled": tool.enabled }] }),
|
|
// });
|
|
|
|
// const tunables: ServerTunables = await response.json();
|
|
// setRags(tunables.rags)
|
|
// setSnack(`${tool?.name} ${tool.enabled ? "enabled" : "disabled"}`);
|
|
// } catch (error) {
|
|
// console.error('Fetch error:', error);
|
|
// setSnack(`${tool?.name} ${tool.enabled ? "enabling" : "disabling"} failed.`, "error");
|
|
// tool.enabled = !tool.enabled
|
|
// }
|
|
// };
|
|
|
|
// const toggleTool = async (tool: Tool) => {
|
|
// tool.enabled = !tool.enabled
|
|
// try {
|
|
// const response = await fetch(connectionBase + `/api/1.0/tunables`, {
|
|
// method: 'PUT',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// 'Accept': 'application/json',
|
|
// },
|
|
// body: JSON.stringify({ "tools": [{ "name": tool.name, "enabled": tool.enabled }] }),
|
|
// });
|
|
|
|
// const tunables: ServerTunables = await response.json();
|
|
// setTools(tunables.tools)
|
|
// setSnack(`${tool.name} ${tool.enabled ? "enabled" : "disabled"}`);
|
|
// } catch (error) {
|
|
// console.error('Fetch error:', error);
|
|
// setSnack(`${tool.name} ${tool.enabled ? "enabling" : "disabling"} failed.`, "error");
|
|
// tool.enabled = !tool.enabled
|
|
// }
|
|
// };
|
|
|
|
// If the systemPrompt has not been set, fetch it from the server
|
|
// useEffect(() => {
|
|
// if (serverTunables !== undefined) {
|
|
// return;
|
|
// }
|
|
// const fetchTunables = async () => {
|
|
// try {
|
|
// // Make the fetch request with proper headers
|
|
// const response = await fetch(connectionBase + `/api/1.0/tunables`, {
|
|
// method: 'GET',
|
|
// headers: {
|
|
// 'Content-Type': 'application/json',
|
|
// 'Accept': 'application/json',
|
|
// },
|
|
// });
|
|
// const data = await response.json();
|
|
// // console.log("Server tunables: ", data);
|
|
// setServerTunables(data);
|
|
// setSystemPrompt(data["system_prompt"]);
|
|
// setTools(data["tools"]);
|
|
// setRags(data["rags"]);
|
|
// } catch (error) {
|
|
// console.error('Fetch error:', error);
|
|
// setSnack("System prompt update failed", "error");
|
|
// }
|
|
// }
|
|
|
|
// fetchTunables();
|
|
// }, [setServerTunables, setSystemPrompt, setMessageHistoryLength, serverTunables, setTools, setRags, setSnack]);
|
|
|
|
// const toggle = async (type: string, index: number) => {
|
|
// switch (type) {
|
|
// case "rag":
|
|
// if (rags === undefined) {
|
|
// return;
|
|
// }
|
|
// toggleRag(rags[index])
|
|
// break;
|
|
// case "tool":
|
|
// if (tools === undefined) {
|
|
// return;
|
|
// }
|
|
// toggleTool(tools[index]);
|
|
// }
|
|
// };
|
|
|
|
// const handleKeyPress = (event: any) => {
|
|
// if (event.key === 'Enter' && event.ctrlKey) {
|
|
// setSystemPrompt(editSystemPrompt);
|
|
// }
|
|
// };
|
|
|
|
return (
|
|
<div className="Controls">
|
|
{/* <Typography component="span" sx={{ mb: 1 }}>
|
|
You can change the information available to the LLM by adjusting the following settings:
|
|
</Typography>
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography component="span">System Prompt</Typography>
|
|
</AccordionSummary>
|
|
<AccordionActions style={{ display: "flex", flexDirection: "column" }}>
|
|
<TextField
|
|
variant="outlined"
|
|
fullWidth
|
|
multiline
|
|
slotProps={{
|
|
htmlInput: { style: { fontSize: "0.85rem", lineHeight: "1.25rem" } }
|
|
}}
|
|
type="text"
|
|
value={editSystemPrompt}
|
|
onChange={(e) => setEditSystemPrompt(e.target.value)}
|
|
onKeyDown={handleKeyPress}
|
|
placeholder="Enter the new system prompt.."
|
|
/>
|
|
<Box sx={{ display: "flex", flexDirection: "row", gap: "8px", paddingTop: "8px" }}>
|
|
<Button variant="contained" disabled={editSystemPrompt.trim() === systemPrompt.trim()} onClick={() => { setSystemPrompt(editSystemPrompt.trim()); }}>Set</Button>
|
|
<Button variant="outlined" onClick={() => { reset(["system_prompt"], "System prompt reset."); }} color="error">Reset</Button>
|
|
</Box>
|
|
</AccordionActions>
|
|
</Accordion>
|
|
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography component="span">Tunables</Typography>
|
|
</AccordionSummary>
|
|
<AccordionActions style={{ flexDirection: "column" }}>
|
|
<TextField
|
|
id="outlined-number"
|
|
label="Message history"
|
|
type="number"
|
|
helperText="Only use this many messages as context. 0 = All. Keeping this low will reduce context growth and improve performance."
|
|
value={messageHistoryLength}
|
|
onChange={(e: any) => setMessageHistoryLength(e.target.value)}
|
|
slotProps={{
|
|
htmlInput: {
|
|
min: 0
|
|
},
|
|
inputLabel: {
|
|
shrink: true,
|
|
},
|
|
}}
|
|
/>
|
|
</AccordionActions>
|
|
</Accordion>
|
|
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography component="span">Tools</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
These tools can be made available to the LLM for obtaining real-time information from the Internet. The description provided to the LLM is provided for reference.
|
|
</AccordionDetails>
|
|
<AccordionActions>
|
|
<FormGroup sx={{ p: 1 }}>
|
|
{
|
|
(tools || []).map((tool, index) =>
|
|
<Box key={index}>
|
|
<Divider />
|
|
<FormControlLabel control={<Switch checked={tool.enabled} />} onChange={() => toggle("tool", index)} label={tool.name} />
|
|
<Typography sx={{ fontSize: "0.8rem", mb: 1 }}>{tool.description}</Typography>
|
|
</Box>
|
|
)
|
|
}</FormGroup>
|
|
</AccordionActions>
|
|
</Accordion>
|
|
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography component="span">RAG</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
These RAG databases can be enabled / disabled for adding additional context based on the chat request.
|
|
</AccordionDetails>
|
|
<AccordionActions>
|
|
<FormGroup sx={{ p: 1, flexGrow: 1, justifyContent: "flex-start" }}>
|
|
{
|
|
(rags || []).map((rag, index) =>
|
|
<Box key={index} sx={{ display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
|
<Divider />
|
|
<FormControlLabel
|
|
control={<Switch checked={rag.enabled} />}
|
|
onChange={() => toggle("rag", index)} label={rag.name}
|
|
/>
|
|
<Typography>{rag.description}</Typography>
|
|
</Box>
|
|
)
|
|
}</FormGroup>
|
|
</AccordionActions>
|
|
</Accordion> */}
|
|
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography component="span">System Information</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>The server is running on the following hardware:</AccordionDetails>
|
|
<AccordionActions>
|
|
<SystemInfoComponent systemInfo={systemInfo} />
|
|
</AccordionActions>
|
|
</Accordion>
|
|
|
|
{/* <Button startIcon={<ResetIcon />} onClick={() => { reset(["history"], "History cleared."); }}>Delete Backstory History</Button>
|
|
<Button onClick={() => { reset(["rags", "tools", "system_prompt"], "Default settings restored.") }}>Reset system prompt, tunables, and RAG to defaults</Button> */}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export { Settings };
|