From f891c7aee832bf2c619b089e7d93e8d8d7fa39c7 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Thu, 5 Feb 2026 10:56:40 -0800 Subject: [PATCH] fix: add API key/auth token support across all model clients --- src/backends/native/agent.ts | 23 +++++++++++++++++++---- src/config/schema.ts | 2 ++ src/daemon/index.ts | 14 ++++++++++++-- src/frontends/telegram/bot.ts | 20 +++++++++++++++++++- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/backends/native/agent.ts b/src/backends/native/agent.ts index a724b95..cf42654 100644 --- a/src/backends/native/agent.ts +++ b/src/backends/native/agent.ts @@ -1,17 +1,19 @@ import type { ModelClient, Message } from '../../models/types.js'; +import type { ModelRouter, ModelTier } from '../../models/router.js'; import type { Session } from '../../session/index.js'; export interface NativeAgentConfig { - modelClient: ModelClient; + modelClient: ModelClient | ModelRouter; systemPrompt: string; session?: Session; } export class NativeAgent { - private modelClient: ModelClient; + private modelClient: ModelClient | ModelRouter; private systemPrompt: string; private session?: Session; private inMemoryHistory: Message[] = []; + private currentTier: ModelTier = 'default'; constructor(config: NativeAgentConfig) { this.modelClient = config.modelClient; @@ -32,10 +34,15 @@ export class NativeAgent { this.inMemoryHistory.push(userMsg); } - const response = await this.modelClient.chat({ + const request = { messages: this.history, system: this.systemPrompt, - }); + }; + + // Use tier if modelClient is a ModelRouter + const response = 'getClient' in this.modelClient + ? await (this.modelClient as ModelRouter).chat(request, this.currentTier) + : await this.modelClient.chat(request); const assistantMsg: Message = { role: 'assistant', content: response.content }; @@ -59,4 +66,12 @@ export class NativeAgent { getHistory(): Message[] { return [...this.history]; } + + setModelTier(tier: ModelTier): void { + this.currentTier = tier; + } + + getModelTier(): ModelTier { + return this.currentTier; + } } diff --git a/src/config/schema.ts b/src/config/schema.ts index a9bfba6..b24a10b 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -15,6 +15,8 @@ const modelConfigSchema = z.object({ provider: z.enum(['anthropic', 'openai', 'gemini', 'ollama', 'llamacpp']), model: z.string(), endpoint: z.string().optional(), + api_key: z.string().optional(), + auth_token: z.string().optional(), for: z.array(z.string()).optional(), }); diff --git a/src/daemon/index.ts b/src/daemon/index.ts index cb1e484..a8cdffa 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -30,6 +30,8 @@ function createModelRouter(config: Config): ModelRouter { const defaultClient = new AnthropicClient({ model: models.default.model, + apiKey: models.default.api_key, + authToken: models.default.auth_token, }); let fastClient; @@ -37,11 +39,19 @@ function createModelRouter(config: Config): ModelRouter { let localClient; if (models.fast) { - fastClient = new AnthropicClient({ model: models.fast.model }); + fastClient = new AnthropicClient({ + model: models.fast.model, + apiKey: models.fast.api_key, + authToken: models.fast.auth_token, + }); } if (models.complex) { - complexClient = new AnthropicClient({ model: models.complex.model }); + complexClient = new AnthropicClient({ + model: models.complex.model, + apiKey: models.complex.api_key, + authToken: models.complex.auth_token, + }); } if (models.local) { diff --git a/src/frontends/telegram/bot.ts b/src/frontends/telegram/bot.ts index 5ada652..9ee83f9 100644 --- a/src/frontends/telegram/bot.ts +++ b/src/frontends/telegram/bot.ts @@ -68,10 +68,28 @@ export function createTelegramBot(config: TelegramBotConfig): Bot { bot.command('status', async (ctx) => { const pending = hookEngine?.getPendingConfirmations() ?? []; - const statusMsg = `Flynn is running.\nPending confirmations: ${pending.length}`; + const currentTier = config.agent.getModelTier(); + const tierDisplay = currentTier === 'local' ? '🏠 Local (Qwen)' : '☁️ Cloud (Claude)'; + const statusMsg = `Flynn is running.\nModel: ${tierDisplay}\nPending confirmations: ${pending.length}`; await ctx.reply(statusMsg); }); + bot.command('local', async (ctx) => { + config.agent.setModelTier('local'); + await ctx.reply('🏠 Switched to local model (Qwen 2.5 14B)'); + }); + + bot.command('cloud', async (ctx) => { + config.agent.setModelTier('default'); + await ctx.reply('☁️ Switched to cloud model (Claude Opus 4.5)'); + }); + + bot.command('model', async (ctx) => { + const currentTier = config.agent.getModelTier(); + const tierDisplay = currentTier === 'local' ? '🏠 Local (Qwen 2.5 14B)' : '☁️ Cloud (Claude Opus 4.5)'; + await ctx.reply(`Current model: ${tierDisplay}\n\nCommands:\n/local - Switch to local model\n/cloud - Switch to cloud model`); + }); + // Message handler bot.on('message:text', async (ctx) => { const text = ctx.message.text;