Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'fix: route AI models through OpenRouter to avoid billing errors' (#172) from fix/ai-openrouter-model-ids into main

scott 14312007 0c6765bf

+19 -18
+1 -1
src/lib/ai-assistant.ts
··· 44 44 export function createAIConfig(gatewayUrl: string): AIConfig { 45 45 return { 46 46 gatewayUrl, 47 - model: 'claude-sonnet-4-6', 47 + model: 'anthropic/claude-sonnet-4.6', 48 48 defaultMaxTokens: 1024, 49 49 enabledActions: ['summarize', 'rewrite', 'expand', 'translate', 'fix-grammar', 'simplify', 'make-formal', 'make-casual'], 50 50 };
+11 -10
src/lib/ai-chat.ts
··· 34 34 const LS_MODEL = 'tools-ai-model'; 35 35 36 36 const DEFAULT_ENDPOINT = '/api/ai'; 37 - const DEFAULT_MODEL = 'claude-sonnet-4-6'; 37 + const DEFAULT_MODEL = 'anthropic/claude-sonnet-4.6'; 38 38 const DEFAULT_MAX_TOKENS = 4096; 39 39 40 40 export function loadConfig(): ChatConfig { ··· 63 63 } 64 64 65 65 export const MODEL_OPTIONS: ModelOption[] = [ 66 - { id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', provider: 'Anthropic' }, 67 - { id: 'claude-opus-4-6', label: 'Claude Opus 4.6', provider: 'Anthropic' }, 68 - { id: 'deepseek-chat', label: 'DeepSeek V3', provider: 'DeepSeek' }, 69 - { id: 'google/gemini-2.5-flash', label: 'Gemini 2.5 Flash', provider: 'Google' }, 70 - { id: 'google/gemini-2.5-pro', label: 'Gemini 2.5 Pro', provider: 'Google' }, 71 - { id: 'openai/gpt-4.1', label: 'GPT-4.1', provider: 'OpenAI' }, 72 - { id: 'openai/gpt-4.1-mini', label: 'GPT-4.1 Mini', provider: 'OpenAI' }, 73 - { id: 'qwen/qwen3-coder', label: 'Qwen3 Coder', provider: 'Qwen' }, 66 + { id: 'anthropic/claude-sonnet-4.6', label: 'Claude Sonnet 4.6', provider: 'OpenRouter' }, 67 + { id: 'anthropic/claude-opus-4.6', label: 'Claude Opus 4.6', provider: 'OpenRouter' }, 68 + { id: 'anthropic/claude-haiku-4.5', label: 'Claude Haiku 4.5', provider: 'OpenRouter' }, 69 + { id: 'google/gemini-2.5-flash', label: 'Gemini 2.5 Flash', provider: 'OpenRouter' }, 70 + { id: 'google/gemini-2.5-pro', label: 'Gemini 2.5 Pro', provider: 'OpenRouter' }, 71 + { id: 'openai/gpt-4.1', label: 'GPT-4.1', provider: 'OpenRouter' }, 72 + { id: 'openai/gpt-4.1-mini', label: 'GPT-4.1 Mini', provider: 'OpenRouter' }, 73 + { id: 'deepseek/deepseek-v3.2', label: 'DeepSeek V3.2', provider: 'OpenRouter' }, 74 + { id: 'qwen/qwen3-coder', label: 'Qwen3 Coder', provider: 'OpenRouter' }, 74 75 ]; 75 76 76 77 // ── State ────────────────────────────────────────────────────────────── ··· 701 702 702 703 chatConfig = { 703 704 endpoint: chatUI.endpointInput.value.trim(), 704 - model: model || 'claude-sonnet-4-6', 705 + model: model || 'anthropic/claude-sonnet-4.6', 705 706 maxTokens: chatConfig.maxTokens, 706 707 }; 707 708 opts.chatConfig = chatConfig;
+7 -7
tests/ai-chat.test.ts
··· 45 45 it('loadConfig returns defaults when localStorage is empty', () => { 46 46 const cfg = loadConfig(); 47 47 expect(cfg.endpoint).toBe('/api/ai'); 48 - expect(cfg.model).toBe('claude-sonnet-4-6'); 48 + expect(cfg.model).toBe('anthropic/claude-sonnet-4.6'); 49 49 expect(cfg.maxTokens).toBe(4096); 50 50 }); 51 51 ··· 464 464 // localStorage || operator means empty string falls through to default 465 465 localStorage.setItem('tools-ai-model', ''); 466 466 const cfg = loadConfig(); 467 - expect(cfg.model).toBe('claude-sonnet-4-6'); 467 + expect(cfg.model).toBe('anthropic/claude-sonnet-4.6'); 468 468 }); 469 469 470 470 it('loadConfig returns default endpoint when stored value is empty string', () => { ··· 1253 1253 const { wiring } = setup(); 1254 1254 const cfg = wiring.getConfig(); 1255 1255 expect(cfg.endpoint).toBe('/api/ai'); 1256 - expect(cfg.model).toBe('claude-sonnet-4-6'); 1256 + expect(cfg.model).toBe('anthropic/claude-sonnet-4.6'); 1257 1257 }); 1258 1258 1259 1259 it('getConfig reflects updated settings after persist', () => { 1260 1260 const { chatUI, wiring } = setup(); 1261 1261 chatUI.endpointInput.value = 'http://custom-endpoint'; 1262 - chatUI.modelSelect.value = 'claude-opus-4-6'; 1262 + chatUI.modelSelect.value = 'anthropic/claude-opus-4.6'; 1263 1263 wiring.persistSettings(); 1264 1264 const cfg = wiring.getConfig(); 1265 1265 expect(cfg.endpoint).toBe('http://custom-endpoint'); 1266 - expect(cfg.model).toBe('claude-opus-4-6'); 1266 + expect(cfg.model).toBe('anthropic/claude-opus-4.6'); 1267 1267 }); 1268 1268 1269 1269 it('togglePanel shows and hides the sidebar', () => { ··· 1331 1331 it('persists settings to localStorage', () => { 1332 1332 const { chatUI, wiring } = setup(); 1333 1333 chatUI.endpointInput.value = 'http://new-api'; 1334 - chatUI.modelSelect.value = 'deepseek-chat'; 1334 + chatUI.modelSelect.value = 'deepseek/deepseek-v3.2'; 1335 1335 wiring.persistSettings(); 1336 1336 expect(mockLS.getItem('tools-ai-endpoint')).toBe('http://new-api'); 1337 - expect(mockLS.getItem('tools-ai-model')).toBe('deepseek-chat'); 1337 + expect(mockLS.getItem('tools-ai-model')).toBe('deepseek/deepseek-v3.2'); 1338 1338 }); 1339 1339 });