feat(backend): auto-stop/start daemon when switching backends
- Add local_providers with ollama and llamacpp configurations - /backend command now stops current daemon before starting new one - Start backends as detached processes to avoid blocking TUI - Wait 500ms for daemon to initialize before switching
This commit is contained in:
+11
-9
@@ -55,17 +55,19 @@ models:
|
||||
# fallback_chain. Useful for secondary API accounts or self-hosted
|
||||
# endpoints that aren't tied to a specific tier.
|
||||
#
|
||||
# local_providers:
|
||||
# openrouter-backup:
|
||||
# provider: openrouter
|
||||
# model: anthropic/claude-sonnet-4
|
||||
# ollama-big:
|
||||
# provider: ollama
|
||||
# model: llama3.1:70b
|
||||
# endpoint: http://gpu-server:11434
|
||||
# Use /backend <name> in the TUI to switch between these providers
|
||||
local_providers:
|
||||
ollama:
|
||||
provider: ollama
|
||||
model: glm-4.7-flash
|
||||
endpoint: http://localhost:11434
|
||||
llamacpp:
|
||||
provider: llamacpp
|
||||
model: gpt-oss-20b
|
||||
endpoint: http://localhost:8080
|
||||
#
|
||||
# Then reference them in fallback_chain:
|
||||
# fallback_chain: [openrouter-backup, ollama-big, local]
|
||||
# fallback_chain: [ollama, llamacpp, local]
|
||||
|
||||
hooks:
|
||||
confirm:
|
||||
|
||||
@@ -194,7 +194,7 @@ export class MinimalTui {
|
||||
break;
|
||||
|
||||
case 'backend':
|
||||
this.handleBackendCommand(command.provider);
|
||||
await this.handleBackendCommand(command.provider);
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
@@ -277,7 +277,7 @@ export class MinimalTui {
|
||||
}
|
||||
}
|
||||
|
||||
private handleBackendCommand(provider?: string): void {
|
||||
private async handleBackendCommand(provider?: string): Promise<void> {
|
||||
const router = this.config.modelRouter;
|
||||
if (!router) {
|
||||
console.log('Backend switching not available.\n');
|
||||
@@ -300,6 +300,17 @@ export class MinimalTui {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop current daemon if running
|
||||
const currentBackend = router.getLocalProviderName();
|
||||
if (currentBackend && currentBackend !== provider) {
|
||||
console.log(`${colors.gray}Stopping ${currentBackend}...${colors.reset}`);
|
||||
await this.stopBackend(currentBackend);
|
||||
}
|
||||
|
||||
// Start new daemon
|
||||
console.log(`${colors.gray}Starting ${provider}...${colors.reset}`);
|
||||
await this.startBackend(provider, providerConfig);
|
||||
|
||||
const client = this.createLocalClient(providerConfig);
|
||||
if (!client) {
|
||||
console.log(`Failed to create client for '${provider}'.\n`);
|
||||
@@ -307,7 +318,53 @@ export class MinimalTui {
|
||||
}
|
||||
|
||||
router.setLocalClient(client, provider);
|
||||
console.log(`Switched to backend: ${provider}\n`);
|
||||
console.log(`${colors.gray}Switched to backend: ${provider}${colors.reset}\n`);
|
||||
}
|
||||
|
||||
private async stopBackend(provider: string): Promise<void> {
|
||||
try {
|
||||
const { spawn } = await import('child_process');
|
||||
let processName: string;
|
||||
switch (provider) {
|
||||
case 'ollama':
|
||||
processName = 'ollama';
|
||||
break;
|
||||
case 'llamacpp':
|
||||
processName = 'llama-server';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve) => {
|
||||
spawn('pkill', [processName]).on('close', resolve);
|
||||
});
|
||||
} catch (error) {
|
||||
// Ignore errors stopping backends
|
||||
}
|
||||
}
|
||||
|
||||
private async startBackend(provider: string, config: ModelConfig): Promise<void> {
|
||||
try {
|
||||
const { spawn } = await import('child_process');
|
||||
const args: string[] = [];
|
||||
|
||||
switch (provider) {
|
||||
case 'ollama':
|
||||
spawn('ollama', ['serve'], { detached: true, stdio: 'ignore' }).unref();
|
||||
break;
|
||||
case 'llamacpp':
|
||||
args.push('--model', config.model);
|
||||
args.push('--port', new URL(config.endpoint ?? 'http://localhost:8080').port || '8080');
|
||||
args.push('--host', '0.0.0.0');
|
||||
spawn('llama-server', args, { detached: true, stdio: 'ignore' }).unref();
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait briefly for the daemon to start
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
} catch (error) {
|
||||
console.log(`${colors.gray}Warning: Failed to start ${provider}: ${error instanceof Error ? error.message : String(error)}${colors.reset}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleLoginCommand(provider?: string): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user