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 };