From 826d63c68a9e9addb9763b81d647b47add23d0bd Mon Sep 17 00:00:00 2001 From: lone-cloud Date: Sun, 5 Apr 2026 22:18:28 -0700 Subject: [PATCH] add jinja UI controls, smooth download ETA, filter new kcpp spam, fix right-click in popups --- package.json | 2 +- pnpm-lock.yaml | 8 ++-- src/components/screens/Launch/AdvancedTab.tsx | 46 ++++++++++++++++++- .../Launch/CommandLineArgumentsModal.tsx | 27 ++--------- src/components/screens/Launch/index.tsx | 12 +++++ src/hooks/useLaunchLogic.ts | 13 ++++++ .../modules/koboldcpp/launcher/patches.ts | 2 + src/main/modules/koboldcpp/model-download.ts | 7 ++- src/main/modules/window.ts | 4 ++ src/stores/launchConfig.ts | 30 ++++++++++++ src/types/electron.d.ts | 3 ++ 11 files changed, 122 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index a8081cc..379679a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gerbil", - "version": "1.21.2", + "version": "1.22.0", "description": "Run Large Language Models locally", "keywords": [ "ai", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0793ed5..7a8d2cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1163,8 +1163,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.14: - resolution: {integrity: sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==} + baseline-browser-mapping@2.10.15: + resolution: {integrity: sha512-1nfKCq9wuAZFTkA2ey/3OXXx7GzFjLdkTiFVNwlJ9WqdI706CZRIhEqjuwanjMIja+84jDLa9rcyZDPDiVkASQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -4082,7 +4082,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.14: {} + baseline-browser-mapping@2.10.15: {} bl@4.1.0: dependencies: @@ -4108,7 +4108,7 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.14 + baseline-browser-mapping: 2.10.15 caniuse-lite: 1.0.30001785 electron-to-chromium: 1.5.331 node-releases: 2.0.37 diff --git a/src/components/screens/Launch/AdvancedTab.tsx b/src/components/screens/Launch/AdvancedTab.tsx index 956adb6..92028e1 100644 --- a/src/components/screens/Launch/AdvancedTab.tsx +++ b/src/components/screens/Launch/AdvancedTab.tsx @@ -14,11 +14,17 @@ export const AdvancedTab = () => { noavx2, failsafe, debugmode, + jinja, + jinjatools, + jinjakwargs, setAdditionalArguments, setPreLaunchCommands, setNoavx2, setFailsafe, setDebugmode, + setJinja, + setJinjatools, + setJinjakwargs, } = useLaunchConfigStore(); const [commandLineModalOpen, setCommandLineModalOpen] = useState(false); const [backendSupport, setBackendSupport] = useState<{ @@ -86,11 +92,47 @@ export const AdvancedTab = () => { label="Debug Mode" tooltip="Shows additional debug info in the terminal." /> + + { + setJinja(val); + if (!val) setJinjatools(false); + }} + label="Use Jinja" + tooltip="Enables using jinja chat template formatting for chat completions endpoint." + /> + + { + 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." + /> + {(jinja || jinjatools) && ( +
+ + + Jinja Kwargs + + + + setJinjakwargs(event.currentTarget.value)} + /> +
+ )} +
- + Additional Arguments @@ -109,7 +151,7 @@ export const AdvancedTab = () => {
- + Pre-Launch Commands diff --git a/src/components/screens/Launch/CommandLineArgumentsModal.tsx b/src/components/screens/Launch/CommandLineArgumentsModal.tsx index feb98f2..e65189f 100644 --- a/src/components/screens/Launch/CommandLineArgumentsModal.tsx +++ b/src/components/screens/Launch/CommandLineArgumentsModal.tsx @@ -59,6 +59,9 @@ const UI_COVERED_ARGS = new Set([ '--tensor_split', '--debugmode', '--lowvram', + '--jinja', + '--jinja_tools', + '--jinja_kwargs', '--smartcache', '--pipelineparallel', '--nopipelineparallel', @@ -396,30 +399,6 @@ const COMMAND_LINE_ARGUMENTS = [ flag: '--chatcompletionsadapter', 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', description: diff --git a/src/components/screens/Launch/index.tsx b/src/components/screens/Launch/index.tsx index 672654a..ccb0bdf 100644 --- a/src/components/screens/Launch/index.tsx +++ b/src/components/screens/Launch/index.tsx @@ -68,6 +68,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => { smartcache, pipelineparallel, quantkv, + jinja, + jinjatools, + jinjakwargs, parseAndApplyConfigFile, loadConfigFromFile, setModel, @@ -187,6 +190,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => { usevulkan: acceleration === 'vulkan', websearch, quantkv, + jinja, + jinjatools, + jinjakwargs, }); const handleCreateNewConfig = async (configName: string) => { @@ -301,6 +307,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => { usemmap, websearch, quantkv, + jinja, + jinjatools, + jinjakwargs, }); }, [ handleLaunch, @@ -342,6 +351,9 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => { smartcache, pipelineparallel, quantkv, + jinja, + jinjatools, + jinjakwargs, ]); return ( diff --git a/src/hooks/useLaunchLogic.ts b/src/hooks/useLaunchLogic.ts index b320683..6dbeeef 100644 --- a/src/hooks/useLaunchLogic.ts +++ b/src/hooks/useLaunchLogic.ts @@ -47,6 +47,9 @@ interface LaunchArgs { smartcache: boolean; pipelineparallel: boolean; quantkv: number; + jinja: boolean; + jinjatools: boolean; + jinjakwargs: string; } const buildModelArgs = (model: string, sdmodel: string, launchArgs: LaunchArgs) => { @@ -162,6 +165,16 @@ const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => { 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; }; diff --git a/src/main/modules/koboldcpp/launcher/patches.ts b/src/main/modules/koboldcpp/launcher/patches.ts index c79d86c..fac03c1 100644 --- a/src/main/modules/koboldcpp/launcher/patches.ts +++ b/src/main/modules/koboldcpp/launcher/patches.ts @@ -149,6 +149,8 @@ export function filterSpam(output: string) { /^done_getting_tensors:/, /^sched_reserve:/, /^llama_memory_recurrent:/, + /^str: cannot properly format tensor name/, + /^tensor .+ buffer type overridden to /, ]; return output diff --git a/src/main/modules/koboldcpp/model-download.ts b/src/main/modules/koboldcpp/model-download.ts index c4cd395..d233768 100644 --- a/src/main/modules/koboldcpp/model-download.ts +++ b/src/main/modules/koboldcpp/model-download.ts @@ -142,6 +142,8 @@ async function downloadFile( let downloadedBytes = 0; let lastReportTime = Date.now(); let lastReportedBytes = 0; + const speedSamples: number[] = []; + const SPEED_WINDOW = 10; fileStream = createWriteStream(tempPath); @@ -154,7 +156,10 @@ async function downloadFile( if (timeDiff >= 0.5) { 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 downloadedMB = (downloadedBytes / 1024 / 1024).toFixed(2); const totalMB = (totalBytes / 1024 / 1024).toFixed(2); diff --git a/src/main/modules/window.ts b/src/main/modules/window.ts index dd4ced7..771a8a2 100644 --- a/src/main/modules/window.ts +++ b/src/main/modules/window.ts @@ -148,6 +148,10 @@ export async function createMainWindow(options?: { startHidden?: boolean }) { }, })); + mainWindow.webContents.on('did-create-window', (popupWindow) => { + setupContextMenu(popupWindow); + }); + mainWindow.on('close', (event) => { saveBounds(); if (getEnableSystemTray() && isTrayActive()) { diff --git a/src/stores/launchConfig.ts b/src/stores/launchConfig.ts index 7519504..945df8b 100644 --- a/src/stores/launchConfig.ts +++ b/src/stores/launchConfig.ts @@ -45,6 +45,9 @@ interface LaunchConfigState { smartcache: boolean; pipelineparallel: boolean; quantkv: number; + jinja: boolean; + jinjatools: boolean; + jinjakwargs: string; isImageGenerationMode: boolean; isTextMode: boolean; @@ -88,6 +91,9 @@ interface LaunchConfigState { setSmartcache: (smartcache: boolean) => void; setPipelineparallel: (pipelineparallel: boolean) => void; setQuantkv: (quantkv: number) => void; + setJinja: (jinja: boolean) => void; + setJinjatools: (jinjatools: boolean) => void; + setJinjakwargs: (jinjakwargs: string) => void; parseAndApplyConfigFile: (configPath: string) => Promise; loadConfigFromFile: ( @@ -400,11 +406,32 @@ export const useLaunchConfigStore = create((set, get) => ({ 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); } }, pipelineparallel: false, quantkv: 0, + jinja: false, + jinjatools: false, + jinjakwargs: '', port: undefined, preLaunchCommands: [''], quantmatmul: true, @@ -482,6 +509,9 @@ export const useLaunchConfigStore = create((set, get) => ({ setSdvae: (vae) => set({ sdvae: vae }), setSdvaecpu: (enabled) => set({ sdvaecpu: enabled }), setSmartcache: (smartcache) => set({ smartcache }), + setJinja: (jinja) => set({ jinja }), + setJinjatools: (jinjatools) => set({ jinjatools }), + setJinjakwargs: (jinjakwargs) => set({ jinjakwargs }), setTensorSplit: (split) => set({ tensorSplit: split }), setUsemmap: (usemmap) => set({ usemmap }), diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index b5436e2..3f38a4e 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -120,6 +120,9 @@ export interface KoboldConfig { smartcache?: boolean; pipelineparallel?: boolean; quantkv?: number; + jinja?: boolean; + jinjatools?: boolean; + jinjakwargs?: string; autoGpuLayers?: boolean; model?: string; backend?: string;