fix: TUI now uses shared model router with auto-fallback support
The TUI was building its own ModelRouter with a duplicated client factory that lacked auto same-model fallback, local_providers resolution, retry config, and per-tier fallback logic. When Anthropic failed, it skipped GitHub Models and fell straight to the local Ollama model. Replace the duplicated ~50-line createClient + router setup in tui.ts with a single call to the daemon's createModelRouter(), which already handles all of these correctly. This removes ~50 lines of duplicated code and ensures TUI and daemon have identical fallback behavior.
This commit is contained in:
+4
-60
@@ -43,77 +43,21 @@ export function registerTuiCommand(program: Command): void {
|
||||
|
||||
// Dynamic imports to keep CLI startup fast
|
||||
const { SessionStore, SessionManager } = await import('../session/index.js');
|
||||
const { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, GitHubModelsClient, GeminiClient, BedrockClient, ModelRouter } = await import('../models/index.js');
|
||||
const { MinimalTui, startFullscreenTui } = await import('../frontends/tui/index.js');
|
||||
const { NativeAgent } = await import('../backends/index.js');
|
||||
const { ToolRegistry, ToolExecutor, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager } = await import('../tools/index.js');
|
||||
const { HookEngine } = await import('../hooks/index.js');
|
||||
const { createModelRouter } = await import('../daemon/index.js');
|
||||
|
||||
const dataDir = resolve(homedir(), '.local/share/flynn');
|
||||
mkdirSync(dataDir, { recursive: true });
|
||||
|
||||
const sessionStore = new SessionStore(resolve(dataDir, 'sessions.db'));
|
||||
const sessionManager = new SessionManager(sessionStore);
|
||||
const models = config.models;
|
||||
|
||||
// Provider-agnostic client factory for TUI
|
||||
function createClient(cfg: typeof models.default) {
|
||||
switch (cfg.provider) {
|
||||
case 'anthropic':
|
||||
return new AnthropicClient({ model: cfg.model, apiKey: cfg.api_key, authToken: cfg.auth_token });
|
||||
case 'openai':
|
||||
return new OpenAIClient({ model: cfg.model, apiKey: cfg.api_key });
|
||||
case 'gemini':
|
||||
return new GeminiClient({ model: cfg.model, apiKey: cfg.api_key });
|
||||
case 'ollama':
|
||||
return new OllamaClient({ model: cfg.model, host: cfg.endpoint, numGpu: cfg.num_gpu });
|
||||
case 'llamacpp':
|
||||
return new LlamaCppClient({ endpoint: cfg.endpoint ?? 'http://localhost:8080', model: cfg.model, authToken: cfg.auth_token });
|
||||
case 'openrouter':
|
||||
return new OpenAIClient({ model: cfg.model, apiKey: cfg.api_key ?? process.env.OPENROUTER_API_KEY, baseURL: cfg.endpoint ?? 'https://openrouter.ai/api/v1' });
|
||||
case 'bedrock':
|
||||
return new BedrockClient({ model: cfg.model, region: cfg.endpoint, accessKeyId: cfg.api_key, secretAccessKey: cfg.auth_token });
|
||||
case 'github':
|
||||
return new GitHubModelsClient({
|
||||
model: cfg.model,
|
||||
apiKey: cfg.api_key,
|
||||
endpoint: cfg.endpoint,
|
||||
onLoginRequired: async () => {
|
||||
const { loginGitHub } = await import('../auth/index.js');
|
||||
console.log('\nGitHub authentication required. Starting login flow...');
|
||||
return loginGitHub((userCode, verificationUri) => {
|
||||
console.log(`\nVisit: ${verificationUri}`);
|
||||
console.log(`Enter code: ${userCode}\n`);
|
||||
console.log('Waiting for authorization...');
|
||||
});
|
||||
},
|
||||
});
|
||||
default:
|
||||
throw new Error(`Unknown provider: ${cfg.provider}`);
|
||||
}
|
||||
}
|
||||
|
||||
const defaultClient = createClient(models.default);
|
||||
const fastClient = models.fast ? createClient(models.fast) : undefined;
|
||||
const complexClient = models.complex ? createClient(models.complex) : undefined;
|
||||
const localClient = models.local ? createClient(models.local) : undefined;
|
||||
|
||||
const fallbackChain = [];
|
||||
for (const providerName of models.fallback_chain) {
|
||||
if (providerName === 'openai') {
|
||||
fallbackChain.push(new OpenAIClient({ model: 'gpt-4o' }));
|
||||
} else if (providerName === 'local' && localClient) {
|
||||
fallbackChain.push(localClient);
|
||||
}
|
||||
}
|
||||
|
||||
const modelRouter = new ModelRouter({
|
||||
default: defaultClient,
|
||||
fast: fastClient,
|
||||
complex: complexClient,
|
||||
local: localClient,
|
||||
fallbackChain,
|
||||
});
|
||||
// Reuse the daemon's model router factory — includes auto-fallback,
|
||||
// local_providers, retry config, and per-tier fallback logic.
|
||||
const modelRouter = createModelRouter(config);
|
||||
|
||||
const systemPrompt = loadSystemPrompt();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user