9export type SourceId = 'mixroute' | 'moonshot' | 'kimicode' | 'llama' | 'openrouter' 10export type AiUse = 'smallchat' | 'oc' | 'draw' 12export const mixrouteBaseUrl = 'https://api.mixroute.ai/v1' 13export const moonshotBaseUrl = 'https://api.moonshot.ai/v1' 14export const kimicodeBaseUrl = 'https://api.kimi.com/coding/v1' 15export const llamaBaseUrl = 'http://127.0.0.1:8081/v1' 17type OcProvider = { name: string, api: string, apiKey?: string } 18type OcModel = { name: string, ctx?: number, out?: number, toolCall?: boolean, temperature?: boolean } 20export type ModelEntry = { 22 src: Partial<Record<SourceId, string>> 28export const aiModelPool: ModelEntry[] = [ 29 { id: 'gpt-5', use: ['oc'], vendor: 'openai', src: { mixroute: 'gpt-5', openrouter: 'openai/gpt-5' }, oc: { name: 'GPT-5', ctx: 400000, out: 128000, toolCall: true, temperature: true } }, 30 { id: 'gemini-3.5-flash', use: ['oc'], vendor: 'google', src: { mixroute: 'gemini-3.5-flash' }, oc: { name: 'Gemini 3.5 Flash', toolCall: true, temperature: true } }, 31 { id: 'kimi-k2.5', use: ['oc'], vendor: 'moonshotai', src: { mixroute: 'kimi-k2.5' }, oc: { name: 'Kimi K2.5', toolCall: true, temperature: true } }, 32 { id: 'kimi-k2.7-code', use: ['oc'], vendor: 'moonshotai', src: { moonshot: 'kimi-k2.7-code' }, oc: { name: 'Kimi K2.7 Code', toolCall: true, temperature: true, ctx: 262144, out: 65536 } }, 33 { id: 'k3', use: ['oc'], vendor: 'moonshotai', src: { kimicode: 'k3' }, oc: { name: 'Kimi K3', toolCall: true, temperature: true, ctx: 1000000, out: 65536 } }, 34 { id: 'claude-haiku-4-5', use: ['oc'], vendor: 'anthropic', src: { mixroute: 'claude-haiku-4-5', openrouter: 'anthropic/claude-haiku-4-5' }, oc: { name: 'Claude Haiku 4.5', toolCall: true, temperature: true } }, 35 { id: 'gpt-4.1-nano', use: ['oc', 'smallchat'], vendor: 'openai', src: { mixroute: 'gpt-4.1-nano', openrouter: 'openai/gpt-4.1-nano' }, oc: { name: 'GPT-4.1 Nano', ctx: 1047576, out: 32768, toolCall: true, temperature: true } }, 36 { id: 'gpt-5-nano', use: ['oc', 'smallchat'], vendor: 'openai', src: { mixroute: 'gpt-5-nano' }, oc: { name: 'GPT-5 Nano', toolCall: true, temperature: true } }, 37 { id: 'gemini-2.5-flash', use: ['oc', 'smallchat'], vendor: 'google', src: { mixroute: 'gemini-2.5-flash', openrouter: 'google/gemini-2.5-flash' }, oc: { name: 'Gemini 2.5 Flash', ctx: 1048576, out: 65536, toolCall: true, temperature: true } }, 38 { id: 'gpt-oss-120b', use: ['oc', 'smallchat'], vendor: 'openai', src: { llama: 'gpt-oss-120b' }, oc: { name: 'GPT-OSS 120B (local)', ctx: 32768, out: 8192, toolCall: true, temperature: true } }, 39 { id: 'mistral-small-2603', use: ['smallchat'], vendor: 'mistralai', src: { openrouter: 'mistralai/mistral-small-2603' } }, 40 { id: 'gpt-4.1', use: ['draw'], vendor: 'openai', src: { mixroute: 'gpt-4.1', openrouter: 'openai/gpt-4.1' } }, 41 { id: 'gemini-2.5-flash-image', use: ['draw'], vendor: 'google', src: { mixroute: 'gemini-2.5-flash-image', openrouter: 'google/gemini-2.5-flash-image' } }, 42 { id: 'deepseek-v4-flash', use: ['draw'], vendor: 'deepseek', src: { mixroute: 'deepseek-v4-flash', openrouter: 'deepseek/deepseek-v4-flash' } }, 45export const resolveVendor = (modelId: string): string | null => { 46 const slash = modelId.indexOf('/') 47 if (slash > 0) return modelId.slice(0, slash) 48 const entry = aiModelPool.find(m => m.id === modelId || Object.values(m.src).some(v => v === modelId)) 49 return entry?.vendor ?? null 52export type PromptOpts = { promptStr?: string, aiMessages?: any[], stream?: boolean, max_tokens?: number, modalities?: string[], temperature?: number, response_format?: unknown, [k: string]: unknown } 57 call: (nativeModel: string, promptStr: string, key: string) => Promise<string> 58 prompt?: (nativeModel: string, opts: PromptOpts, key: string) => Promise<any> 59 ocProvider?: OcProvider 62export const aiSources: AiSource[] = [ 65 baseUrl: mixrouteBaseUrl, 66 call: (m, p, k) => opnRtrStrSimple({ model: m, promptStr: p, apiKey: k, baseUrl: mixrouteBaseUrl, label: 'Mixroute' }), 67 prompt: (m, o, k) => openRouterPrompt({ ...o, model: m, apiKey: k, baseUrl: mixrouteBaseUrl, trackCost: false }), 68 ocProvider: { name: 'Mixroute', api: 'openai' }, 72 baseUrl: moonshotBaseUrl, 73 call: (m, p, k) => opnRtrStrSimple({ model: m, promptStr: p, apiKey: k, baseUrl: moonshotBaseUrl, label: 'Moonshot' }), 74 prompt: (m, o, k) => openRouterPrompt({ ...o, model: m, apiKey: k, baseUrl: moonshotBaseUrl, trackCost: false }), 75 ocProvider: { name: 'Moonshot', api: 'openai' }, 79 baseUrl: kimicodeBaseUrl, 80 call: (m, p, k) => opnRtrStrSimple({ model: m, promptStr: p, apiKey: k, baseUrl: kimicodeBaseUrl, label: 'Kimi Code' }), 81 prompt: (m, o, k) => openRouterPrompt({ ...o, model: m, apiKey: k, baseUrl: kimicodeBaseUrl, trackCost: false }), 82 ocProvider: { name: 'Kimi Code', api: 'openai' }, 86 baseUrl: llamaBaseUrl, 87 call: (m, p, _k) => withTimeout(opnRtrStrSimple({ model: m, promptStr: p, apiKey: 'no-key', baseUrl: llamaBaseUrl, label: 'llama' }), 30000), 88 prompt: (m, o, _k) => openRouterPrompt({ ...o, model: m, apiKey: 'no-key', baseUrl: llamaBaseUrl, trackCost: false }), 89 ocProvider: { name: 'Local llama.cpp', api: 'openai', apiKey: 'no-key' }, 93 baseUrl: 'https://openrouter.ai/api/v1', 94 call: (m, p, k) => opnRtrStrSimple({ model: m, promptStr: p, apiKey: k, label: 'OpenRouter' }), 99const tryPlainSec = (secName: secretNameType): string | null => { 103export const keyBySourceFor = (openrouterKey: string | null): Record<SourceId, string | null> => ({ 104 mixroute: tryPlainSec(tsSecs.mixrouteApiToken), 105 moonshot: tryPlainSec(tsSecs.moonshotApiToken), 106 kimicode: tryPlainSec(tsSecs.kimicodeApiToken), 108 openrouter: openrouterKey, 111export const findPoolEntry = (modelId: string): { src: Partial<Record<SourceId, string>> } => 112 aiModelPool.find(m => m.id === modelId || m.src.openrouter === modelId) || { src: { openrouter: modelId } } 114export const smallChatCandidates = (openrouterKey: string | null): string[] => { 115 const keyBySource = keyBySourceFor(openrouterKey) 116 const candidates: string[] = [] 117 for (const s of aiSources) { 118 if (keyBySource[s.id] == null) continue 119 for (const m of aiModelPool) { 120 if (!m.use.includes('smallchat')) continue 121 const native = m.src[s.id] 122 if (native) candidates.push(`${s.id}/${native}`) 128export const runSmallChatWithCandidates = async (candidates: string[], promptStr: string, { context, openrouterKey, isSuccess = (r: string) => !!r?.trim() }: { context: string, openrouterKey: string | null, isSuccess?: (r: string) => boolean }) => { 129 const keyBySource = keyBySourceFor(openrouterKey) 133 callFn: (scoped) => { 135 const slashIdx = scoped.indexOf('/') 136 const sid = scoped.slice(0, slashIdx) as SourceId 137 const native = scoped.slice(slashIdx + 1) 138 const s = aiSources.find(x => x.id === sid) 140 const key = keyBySource[sid] 142 return s.call(native, promptStr, key) 147 return { result, attempts, usedModel } 150export const runSmallChatSources = async (promptStr: string, { context, openrouterKey }: { context: string, openrouterKey: string | null }) => { 151 const candidates = smallChatCandidates(openrouterKey) 152 return runSmallChatWithCandidates(candidates, promptStr, { context, openrouterKey }) 155export const promptCandidatesFor = (modelIds: string[]): string[] => { 156 const keyBySource = keyBySourceFor(tryPlainSec(tsSecs.openrouterApiToken)) 157 const candidates: string[] = [] 158 for (const modelId of modelIds) { 159 const entry = findPoolEntry(modelId) 160 for (const s of aiSources) { 161 if (keyBySource[s.id] == null) continue 162 const native = entry.src[s.id] 163 if (native) candidates.push(`${s.id}/${native}`) 169export const runPromptSources = async ({ modelIds, opts, context }: { modelIds: string[], opts: PromptOpts, context: string }) => { 170 const keyBySource = keyBySourceFor(tryPlainSec(tsSecs.openrouterApiToken)) 171 const candidates = promptCandidatesFor(modelIds) 175 callFn: (scoped) => { 177 const slashIdx = scoped.indexOf('/') 178 const sid = scoped.slice(0, slashIdx) as SourceId 179 const native = scoped.slice(slashIdx + 1) 180 const s = aiSources.find(x => x.id === sid) 182 const key = keyBySource[sid] 184 return s.prompt!(native, opts, key) 188 return { result, attempts, usedModel }