mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 09:33:10 -07:00
new CLI mode to proxy terminal commands to the kcpp binary
This commit is contained in:
parent
835eba34e1
commit
a24ec72088
4 changed files with 148 additions and 6 deletions
22
README.md
22
README.md
|
|
@ -83,6 +83,28 @@ For Friendly Kobold to work with this fork, [this issue must be fixed first](htt
|
|||
|
||||
Not all koboldcpp features have currently been ported over to the UI. As a workaround one may use the "Additional arguments" on the "Advanced" tab of the launcher to provide additional command line arguments if you know them.
|
||||
|
||||
## CLI Mode
|
||||
|
||||
The `--cli` argument allows you to use the Friendly Kobold binary as a proxy to the downloaded KoboldCpp binary. This enables you to run KoboldCpp from the command line using the same binary that the GUI has downloaded.
|
||||
|
||||
### Considerations
|
||||
|
||||
You might want to run CLI Mode if you're looking to use a different frontend, such as SillyTavern or OpenWebUI, than the ones bundled (eg. KoboldAI Lite, Stable UI) with KoboldCpp AND you're looking to minimize any resource utilization of this app. Note that at the time of this writing, Friendly Kobold takes about ~200MB of RAM and ~100MB of VRAM for its Chromium-based UI.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Basic usage - launch KoboldCpp with no arguments
|
||||
./friendly-kobold --cli
|
||||
|
||||
# Pass arguments to KoboldCpp
|
||||
./friendly-kobold --cli --help
|
||||
./friendly-kobold --cli --port 5001 --model /path/to/model.gguf
|
||||
|
||||
# Any KoboldCpp arguments are supported
|
||||
./friendly-kobold --cli --model /path/to/model.gguf --port 5001 --host 0.0.0.0 --multiuser 2
|
||||
```
|
||||
|
||||
## For Local Dev
|
||||
|
||||
### Prerequisites
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "friendly-kobold",
|
||||
"productName": "Friendly Kobold",
|
||||
"version": "0.6.5",
|
||||
"version": "0.7.0",
|
||||
"description": "A desktop app for running Large Language Models locally",
|
||||
"main": "out/main/index.js",
|
||||
"homepage": "./",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { GitHubService } from '@/main/services/GitHubService';
|
|||
import { HardwareService } from '@/main/services/HardwareService';
|
||||
import { BinaryService } from '@/main/services/BinaryService';
|
||||
import { IPCHandlers } from '@/main/utils/IPCHandlers';
|
||||
import { CliHandler } from '@/main/utils/CliHandler';
|
||||
import { APP_NAME, CONFIG_FILE_NAME } from '@/constants';
|
||||
|
||||
class FriendlyKoboldApp {
|
||||
|
|
@ -146,8 +147,25 @@ class FriendlyKoboldApp {
|
|||
}
|
||||
}
|
||||
|
||||
const friendlyKoboldApp = new FriendlyKoboldApp();
|
||||
friendlyKoboldApp.initialize().catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to initialize FriendlyKobold:', error);
|
||||
});
|
||||
const { isCliMode, args } = CliHandler.parseArguments(process.argv);
|
||||
|
||||
if (isCliMode) {
|
||||
async function runCliMode() {
|
||||
try {
|
||||
const cliHandler = new CliHandler();
|
||||
await cliHandler.handleCliMode(args);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('CLI mode error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runCliMode();
|
||||
} else {
|
||||
const friendlyKoboldApp = new FriendlyKoboldApp();
|
||||
friendlyKoboldApp.initialize().catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to initialize FriendlyKobold:', error);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
102
src/main/utils/CliHandler.ts
Normal file
102
src/main/utils/CliHandler.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { app } from 'electron';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { CONFIG_FILE_NAME } from '@/constants';
|
||||
|
||||
export class CliHandler {
|
||||
private configManager: ConfigManager;
|
||||
private logManager: LogManager;
|
||||
|
||||
constructor() {
|
||||
this.logManager = new LogManager();
|
||||
this.configManager = new ConfigManager(
|
||||
this.getConfigPath(),
|
||||
this.logManager
|
||||
);
|
||||
}
|
||||
|
||||
private getConfigPath(): string {
|
||||
return join(app.getPath('userData'), CONFIG_FILE_NAME);
|
||||
}
|
||||
|
||||
async handleCliMode(args: string[]): Promise<void> {
|
||||
const currentBinary = this.configManager.getCurrentKoboldBinary();
|
||||
|
||||
if (!currentBinary) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
'Error: No KoboldCpp binary found. Please run the GUI first to download KoboldCpp.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!existsSync(currentBinary)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Error: KoboldCpp binary not found at: ${currentBinary}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Please run the GUI to download or reconfigure KoboldCpp.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Launching KoboldCpp: ${currentBinary}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Arguments: ${args.join(' ')}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('─'.repeat(60));
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const child = spawn(currentBinary, args, {
|
||||
stdio: 'inherit',
|
||||
detached: false,
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`\nProcess terminated with signal: ${signal}`);
|
||||
process.exit(128 + (signal === 'SIGTERM' ? 15 : 2));
|
||||
} else if (code !== null) {
|
||||
process.exit(code);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to start KoboldCpp: ${error.message}`);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const handleSignal = () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\nReceived termination signal, terminating KoboldCpp...');
|
||||
if (!child.killed) {
|
||||
child.kill('SIGTERM');
|
||||
}
|
||||
};
|
||||
|
||||
process.on('SIGINT', handleSignal);
|
||||
process.on('SIGTERM', handleSignal);
|
||||
});
|
||||
}
|
||||
|
||||
static parseArguments(argv: string[]): {
|
||||
isCliMode: boolean;
|
||||
args: string[];
|
||||
} {
|
||||
const cliIndex = argv.indexOf('--cli');
|
||||
|
||||
if (cliIndex === -1) {
|
||||
return { isCliMode: false, args: [] };
|
||||
}
|
||||
|
||||
const koboldArgs = argv.slice(cliIndex + 1);
|
||||
return { isCliMode: true, args: koboldArgs };
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue