add jinja UI controls, smooth download ETA, filter new kcpp spam, fix right-click in popups

This commit is contained in:
lone-cloud 2026-04-05 22:18:28 -07:00
parent ca7fd211ff
commit 826d63c68a
Signed by: lone-cloud
GPG key ID: B0848536D672CD8D
11 changed files with 122 additions and 32 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "gerbil", "name": "gerbil",
"version": "1.21.2", "version": "1.22.0",
"description": "Run Large Language Models locally", "description": "Run Large Language Models locally",
"keywords": [ "keywords": [
"ai", "ai",

8
pnpm-lock.yaml generated
View file

@ -1163,8 +1163,8 @@ packages:
base64-js@1.5.1: base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.10.14: baseline-browser-mapping@2.10.15:
resolution: {integrity: sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==} resolution: {integrity: sha512-1nfKCq9wuAZFTkA2ey/3OXXx7GzFjLdkTiFVNwlJ9WqdI706CZRIhEqjuwanjMIja+84jDLa9rcyZDPDiVkASQ==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
@ -4082,7 +4082,7 @@ snapshots:
base64-js@1.5.1: {} base64-js@1.5.1: {}
baseline-browser-mapping@2.10.14: {} baseline-browser-mapping@2.10.15: {}
bl@4.1.0: bl@4.1.0:
dependencies: dependencies:
@ -4108,7 +4108,7 @@ snapshots:
browserslist@4.28.2: browserslist@4.28.2:
dependencies: dependencies:
baseline-browser-mapping: 2.10.14 baseline-browser-mapping: 2.10.15
caniuse-lite: 1.0.30001785 caniuse-lite: 1.0.30001785
electron-to-chromium: 1.5.331 electron-to-chromium: 1.5.331
node-releases: 2.0.37 node-releases: 2.0.37

View file

@ -14,11 +14,17 @@ export const AdvancedTab = () => {
noavx2, noavx2,
failsafe, failsafe,
debugmode, debugmode,
jinja,
jinjatools,
jinjakwargs,
setAdditionalArguments, setAdditionalArguments,
setPreLaunchCommands, setPreLaunchCommands,
setNoavx2, setNoavx2,
setFailsafe, setFailsafe,
setDebugmode, setDebugmode,
setJinja,
setJinjatools,
setJinjakwargs,
} = useLaunchConfigStore(); } = useLaunchConfigStore();
const [commandLineModalOpen, setCommandLineModalOpen] = useState(false); const [commandLineModalOpen, setCommandLineModalOpen] = useState(false);
const [backendSupport, setBackendSupport] = useState<{ const [backendSupport, setBackendSupport] = useState<{
@ -86,11 +92,47 @@ export const AdvancedTab = () => {
label="Debug Mode" label="Debug Mode"
tooltip="Shows additional debug info in the terminal." tooltip="Shows additional debug info in the terminal."
/> />
<CheckboxWithTooltip
checked={jinja}
onChange={(val) => {
setJinja(val);
if (!val) setJinjatools(false);
}}
label="Use Jinja"
tooltip="Enables using jinja chat template formatting for chat completions endpoint."
/>
<CheckboxWithTooltip
checked={jinjatools}
onChange={(val) => {
setJinjatools(val);
if (val) setJinja(true);
}}
label="Jinja for Tools"
tooltip="Allows jinja even with tool calls. If unchecked, jinja will be disabled when tools are used."
/>
</SimpleGrid> </SimpleGrid>
</div> </div>
{(jinja || jinjatools) && (
<div>
<Group>
<Text size="sm" fw={500}>
Jinja Kwargs
</Text>
<InfoTooltip label="Set additional fields for the Jinja JSON template parser, must be a valid JSON object." />
</Group>
<TextInput
placeholder='e.g. {"enable_thinking":true}'
value={jinjakwargs}
onChange={(event) => setJinjakwargs(event.currentTarget.value)}
/>
</div>
)}
<div> <div>
<Group mb="xs" justify="space-between"> <Group justify="space-between">
<Group> <Group>
<Text size="sm" fw={500}> <Text size="sm" fw={500}>
Additional Arguments Additional Arguments
@ -109,7 +151,7 @@ export const AdvancedTab = () => {
</div> </div>
<div> <div>
<Group mb="xs"> <Group>
<Text size="sm" fw={500}> <Text size="sm" fw={500}>
Pre-Launch Commands Pre-Launch Commands
</Text> </Text>

View file

@ -59,6 +59,9 @@ const UI_COVERED_ARGS = new Set([
'--tensor_split', '--tensor_split',
'--debugmode', '--debugmode',
'--lowvram', '--lowvram',
'--jinja',
'--jinja_tools',
'--jinja_kwargs',
'--smartcache', '--smartcache',
'--pipelineparallel', '--pipelineparallel',
'--nopipelineparallel', '--nopipelineparallel',
@ -396,30 +399,6 @@ const COMMAND_LINE_ARGUMENTS = [
flag: '--chatcompletionsadapter', flag: '--chatcompletionsadapter',
metavar: '[filename]', metavar: '[filename]',
}, },
{
category: 'Advanced',
description:
'Enables using jinja chat template formatting for chat completions endpoint. Other endpoints are unaffected. Tool calls are done without jinja.',
flag: '--jinja',
type: 'boolean',
},
{
aliases: ['--jinja-tools', '--jinjatools'],
category: 'Advanced',
description:
'Enables using jinja chat template formatting for chat completions endpoint. Other endpoints are unaffected. Tool calls are done with jinja.',
flag: '--jinja_tools',
type: 'boolean',
},
{
aliases: ['--jinja-kwargs', '--jinjakwargs', '--chat-template-kwargs'],
category: 'Advanced',
default: '',
description:
'Set additional fields for Jinja JSON template parser, must be a valid JSON object.',
flag: '--jinja_kwargs',
metavar: '{"parameter":"value",...}',
},
{ {
category: 'Advanced', category: 'Advanced',
description: description:

View file

@ -68,6 +68,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
smartcache, smartcache,
pipelineparallel, pipelineparallel,
quantkv, quantkv,
jinja,
jinjatools,
jinjakwargs,
parseAndApplyConfigFile, parseAndApplyConfigFile,
loadConfigFromFile, loadConfigFromFile,
setModel, setModel,
@ -187,6 +190,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
usevulkan: acceleration === 'vulkan', usevulkan: acceleration === 'vulkan',
websearch, websearch,
quantkv, quantkv,
jinja,
jinjatools,
jinjakwargs,
}); });
const handleCreateNewConfig = async (configName: string) => { const handleCreateNewConfig = async (configName: string) => {
@ -301,6 +307,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
usemmap, usemmap,
websearch, websearch,
quantkv, quantkv,
jinja,
jinjatools,
jinjakwargs,
}); });
}, [ }, [
handleLaunch, handleLaunch,
@ -342,6 +351,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
smartcache, smartcache,
pipelineparallel, pipelineparallel,
quantkv, quantkv,
jinja,
jinjatools,
jinjakwargs,
]); ]);
return ( return (

View file

@ -47,6 +47,9 @@ interface LaunchArgs {
smartcache: boolean; smartcache: boolean;
pipelineparallel: boolean; pipelineparallel: boolean;
quantkv: number; quantkv: number;
jinja: boolean;
jinjatools: boolean;
jinjakwargs: string;
} }
const buildModelArgs = (model: string, sdmodel: string, launchArgs: LaunchArgs) => { const buildModelArgs = (model: string, sdmodel: string, launchArgs: LaunchArgs) => {
@ -162,6 +165,16 @@ const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => {
args.push('--quantkv', launchArgs.quantkv.toString()); args.push('--quantkv', launchArgs.quantkv.toString());
} }
if (launchArgs.jinjatools) {
args.push('--jinja_tools');
} else if (launchArgs.jinja) {
args.push('--jinja');
}
if ((launchArgs.jinja || launchArgs.jinjatools) && launchArgs.jinjakwargs.trim()) {
args.push('--jinja_kwargs', launchArgs.jinjakwargs.trim());
}
return args; return args;
}; };

View file

@ -149,6 +149,8 @@ export function filterSpam(output: string) {
/^done_getting_tensors:/, /^done_getting_tensors:/,
/^sched_reserve:/, /^sched_reserve:/,
/^llama_memory_recurrent:/, /^llama_memory_recurrent:/,
/^str: cannot properly format tensor name/,
/^tensor .+ buffer type overridden to /,
]; ];
return output return output

View file

@ -142,6 +142,8 @@ async function downloadFile(
let downloadedBytes = 0; let downloadedBytes = 0;
let lastReportTime = Date.now(); let lastReportTime = Date.now();
let lastReportedBytes = 0; let lastReportedBytes = 0;
const speedSamples: number[] = [];
const SPEED_WINDOW = 10;
fileStream = createWriteStream(tempPath); fileStream = createWriteStream(tempPath);
@ -154,7 +156,10 @@ async function downloadFile(
if (timeDiff >= 0.5) { if (timeDiff >= 0.5) {
const bytesDiff = downloadedBytes - lastReportedBytes; const bytesDiff = downloadedBytes - lastReportedBytes;
const speedBytesPerSec = bytesDiff / timeDiff; const instantSpeed = bytesDiff / timeDiff;
speedSamples.push(instantSpeed);
if (speedSamples.length > SPEED_WINDOW) speedSamples.shift();
const speedBytesPerSec = speedSamples.reduce((a, b) => a + b, 0) / speedSamples.length;
const percent = totalBytes ? Math.round((downloadedBytes / totalBytes) * 100) : 0; const percent = totalBytes ? Math.round((downloadedBytes / totalBytes) * 100) : 0;
const downloadedMB = (downloadedBytes / 1024 / 1024).toFixed(2); const downloadedMB = (downloadedBytes / 1024 / 1024).toFixed(2);
const totalMB = (totalBytes / 1024 / 1024).toFixed(2); const totalMB = (totalBytes / 1024 / 1024).toFixed(2);

View file

@ -148,6 +148,10 @@ export async function createMainWindow(options?: { startHidden?: boolean }) {
}, },
})); }));
mainWindow.webContents.on('did-create-window', (popupWindow) => {
setupContextMenu(popupWindow);
});
mainWindow.on('close', (event) => { mainWindow.on('close', (event) => {
saveBounds(); saveBounds();
if (getEnableSystemTray() && isTrayActive()) { if (getEnableSystemTray() && isTrayActive()) {

View file

@ -45,6 +45,9 @@ interface LaunchConfigState {
smartcache: boolean; smartcache: boolean;
pipelineparallel: boolean; pipelineparallel: boolean;
quantkv: number; quantkv: number;
jinja: boolean;
jinjatools: boolean;
jinjakwargs: string;
isImageGenerationMode: boolean; isImageGenerationMode: boolean;
isTextMode: boolean; isTextMode: boolean;
@ -88,6 +91,9 @@ interface LaunchConfigState {
setSmartcache: (smartcache: boolean) => void; setSmartcache: (smartcache: boolean) => void;
setPipelineparallel: (pipelineparallel: boolean) => void; setPipelineparallel: (pipelineparallel: boolean) => void;
setQuantkv: (quantkv: number) => void; setQuantkv: (quantkv: number) => void;
setJinja: (jinja: boolean) => void;
setJinjatools: (jinjatools: boolean) => void;
setJinjakwargs: (jinjakwargs: string) => void;
parseAndApplyConfigFile: (configPath: string) => Promise<void>; parseAndApplyConfigFile: (configPath: string) => Promise<void>;
loadConfigFromFile: ( loadConfigFromFile: (
@ -400,11 +406,32 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
updates.quantkv = 0; updates.quantkv = 0;
} }
if (typeof configData.jinja === 'boolean') {
updates.jinja = configData.jinja;
} else {
updates.jinja = false;
}
if (typeof configData.jinjatools === 'boolean') {
updates.jinjatools = configData.jinjatools;
} else {
updates.jinjatools = false;
}
if (typeof configData.jinjakwargs === 'string') {
updates.jinjakwargs = configData.jinjakwargs;
} else {
updates.jinjakwargs = '';
}
set(updates); set(updates);
} }
}, },
pipelineparallel: false, pipelineparallel: false,
quantkv: 0, quantkv: 0,
jinja: false,
jinjatools: false,
jinjakwargs: '',
port: undefined, port: undefined,
preLaunchCommands: [''], preLaunchCommands: [''],
quantmatmul: true, quantmatmul: true,
@ -482,6 +509,9 @@ export const useLaunchConfigStore = create<LaunchConfigState>((set, get) => ({
setSdvae: (vae) => set({ sdvae: vae }), setSdvae: (vae) => set({ sdvae: vae }),
setSdvaecpu: (enabled) => set({ sdvaecpu: enabled }), setSdvaecpu: (enabled) => set({ sdvaecpu: enabled }),
setSmartcache: (smartcache) => set({ smartcache }), setSmartcache: (smartcache) => set({ smartcache }),
setJinja: (jinja) => set({ jinja }),
setJinjatools: (jinjatools) => set({ jinjatools }),
setJinjakwargs: (jinjakwargs) => set({ jinjakwargs }),
setTensorSplit: (split) => set({ tensorSplit: split }), setTensorSplit: (split) => set({ tensorSplit: split }),
setUsemmap: (usemmap) => set({ usemmap }), setUsemmap: (usemmap) => set({ usemmap }),

View file

@ -120,6 +120,9 @@ export interface KoboldConfig {
smartcache?: boolean; smartcache?: boolean;
pipelineparallel?: boolean; pipelineparallel?: boolean;
quantkv?: number; quantkv?: number;
jinja?: boolean;
jinjatools?: boolean;
jinjakwargs?: string;
autoGpuLayers?: boolean; autoGpuLayers?: boolean;
model?: string; model?: string;
backend?: string; backend?: string;