Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ headers (like build/examples/server/index.html.gz.hpp) that will be included
by server.cpp. This is done by building `llama-server` as described in the
[build](#build) section above.

Prompt and other configuration parameters are easily customisable with the json file `public/prompts.config.json`.
Examples from https://github.com/f/awesome-chatgpt-prompts have been already written in it.


NOTE: if you are using the vite dev server, you can change the API base URL to llama.cpp. To do that, run this code snippet in browser's console:

```js
Expand Down
Binary file modified examples/server/public/index.html.gz
Binary file not shown.
1,732 changes: 1,732 additions & 0 deletions examples/server/public/prompts.config.json

Large diffs are not rendered by default.

1,732 changes: 1,732 additions & 0 deletions examples/server/webui/public/prompts.config.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion examples/server/webui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function App() {
}

function AppLayout() {
const { showSettings, setShowSettings } = useAppContext();
const { showSettings, setShowSettings, settingsSeed } = useAppContext();
return (
<>
<Sidebar />
Expand All @@ -36,6 +36,7 @@ function AppLayout() {
</div>
{
<SettingDialog
key={settingsSeed}
show={showSettings}
onClose={() => setShowSettings(false)}
/>
Expand Down
122 changes: 117 additions & 5 deletions examples/server/webui/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { useEffect, useState } from 'react';
import StorageUtils from '../utils/storage';
import { useAppContext } from '../utils/app.context';
import { classNames } from '../utils/misc';
import { classNames, isBoolean, isNumeric, isString } from '../utils/misc';
import daisyuiThemes from 'daisyui/src/theming/themes';
import { THEMES } from '../Config';
import { THEMES, CONFIG_DEFAULT, isDev } from '../Config';
import { useNavigate } from 'react-router';

export const PROMPT_JSON = [
{
name: '',
lang: '',
config: CONFIG_DEFAULT,
},
];

export default function Header() {
const navigate = useNavigate();
const [selectedTheme, setSelectedTheme] = useState(StorageUtils.getTheme());
Expand All @@ -15,16 +23,41 @@ export default function Header() {
StorageUtils.setTheme(theme);
setSelectedTheme(theme);
};
const { config, saveConfig, resetSettings } = useAppContext();
// clone the config object to prevent direct mutation
const [localConfig] = useState<typeof CONFIG_DEFAULT>(
JSON.parse(JSON.stringify(config))
);

const [promptSelectOptions, setPromptSelectOptions] = useState([
{ key: -1, value: 'System prompt (none)' },
]);
const [promptSelectConfig, setPromptSelectConfig] = useState<
typeof PROMPT_JSON | null
>(null);
useEffect(() => {
document.body.setAttribute('data-theme', selectedTheme);
document.body.setAttribute(
'data-color-scheme',
// @ts-expect-error daisyuiThemes complains about index type, but it should work
daisyuiThemes[selectedTheme]?.['color-scheme'] ?? 'auto'
);
}, [selectedTheme]);

fetch('/prompts.config.json')
.then((response) => response.json())
.then((data) => {
const prt: { key: number; value: string }[] = [
{ key: -1, value: 'Choose a system config' },
];
if (data && data.prompts) {
setPromptSelectConfig(data.prompts);
Object.keys(data.prompts).forEach(function (key) {
const name = data.prompts[key].name;
prt.push({ key: parseInt(key), value: name });
});
}
setPromptSelectOptions(prt);
});
}, [promptSelectOptions, selectedTheme]);
const { isGenerating, viewingChat } = useAppContext();
const isCurrConvGenerating = isGenerating(viewingChat?.conv.id ?? '');

Expand Down Expand Up @@ -52,6 +85,72 @@ export default function Header() {
URL.revokeObjectURL(url);
};

const selectPrompt = (value: string) => {
if (parseInt(value) == -1) {
const newConfig: typeof CONFIG_DEFAULT = JSON.parse(
JSON.stringify(localConfig)
);
if (isDev) console.log('Old config', newConfig);
if (isDev) console.log('Saving config', newConfig);
saveConfig(newConfig);
return;
}
if (
promptSelectConfig &&
promptSelectConfig[parseInt(value)] &&
promptSelectConfig[parseInt(value)].config
) {
const newConfig: typeof CONFIG_DEFAULT = JSON.parse(
JSON.stringify(localConfig)
);
// validate the config
for (const key in promptSelectConfig[parseInt(value)].config) {
const val =
promptSelectConfig[parseInt(value)].config[
key as keyof typeof CONFIG_DEFAULT
];
const mustBeBoolean = isBoolean(
CONFIG_DEFAULT[key as keyof typeof CONFIG_DEFAULT]
);
const mustBeString = isString(
CONFIG_DEFAULT[key as keyof typeof CONFIG_DEFAULT]
);
const mustBeNumeric = isNumeric(
CONFIG_DEFAULT[key as keyof typeof CONFIG_DEFAULT]
);
if (mustBeString) {
if (!isString(val)) {
alert(`Value for ${key} must be string`);
return;
}
} else if (mustBeNumeric) {
const trimedValue = val.toString().trim();
const numVal = Number(trimedValue);
if (isNaN(numVal) || !isNumeric(numVal) || trimedValue.length === 0) {
alert(`Value for ${key} must be numeric`);
return;
}
// force conversion to number
// @ts-expect-error this is safe
newConfig[key] = numVal;
} else if (mustBeBoolean) {
if (!isBoolean(val)) {
alert(`Value for ${key} must be boolean`);
return;
}
} else {
console.error(`Unknown default type for key ${key}`);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
newConfig[key] = val;
}
if (isDev) console.log('Saving config', newConfig);
saveConfig(newConfig);
resetSettings();
}
};

return (
<div className="flex flex-row items-center pt-6 pb-6 sticky top-0 z-10 bg-base-100">
{/* open sidebar button */}
Expand All @@ -72,7 +171,20 @@ export default function Header() {
</label>

<div className="grow text-2xl font-bold ml-2">llama.cpp</div>

{promptSelectOptions.length > 1 ? (
<div className="grow text-2xl font-bold ml-2 pl-6 pr-6">
<select
className="select w-full max-w-xs"
onChange={(e) => selectPrompt(e.target.value)}
>
{[...promptSelectOptions].map((opt) => (
<option key={opt.key} value={opt.key}>
{opt.value}
</option>
))}
</select>
</div>
) : null}
{/* action buttons (top right) */}
<div className="flex items-center">
{viewingChat && (
Expand Down
8 changes: 8 additions & 0 deletions examples/server/webui/src/utils/app.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ interface AppContextValue {
config: typeof CONFIG_DEFAULT;
saveConfig: (config: typeof CONFIG_DEFAULT) => void;
showSettings: boolean;
settingsSeed: number;
setShowSettings: (show: boolean) => void;
resetSettings: () => void;
}

// this callback is used for scrolling to the bottom of the chat and switching to the last node
Expand Down Expand Up @@ -84,6 +86,10 @@ export const AppContextProvider = ({
const [config, setConfig] = useState(StorageUtils.getConfig());
const [canvasData, setCanvasData] = useState<CanvasData | null>(null);
const [showSettings, setShowSettings] = useState(false);
const [settingsSeed, setSettingsSeed] = useState(1);
const resetSettings = () => {
setSettingsSeed(Math.random());
};

// handle change when the convId from URL is changed
useEffect(() => {
Expand Down Expand Up @@ -377,6 +383,8 @@ export const AppContextProvider = ({
saveConfig,
showSettings,
setShowSettings,
settingsSeed,
resetSettings,
}}
>
{children}
Expand Down