import type { Api, Model } from "@mariozechner/pi-ai"; import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent"; import type { ClawdbotConfig } from "../../config/config.js"; import { resolveClawdbotAgentDir } from "../agent-paths.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; import { normalizeModelCompat } from "../model-compat.js"; export function buildModelAliasLines(cfg?: ClawdbotConfig) { const models = cfg?.agents?.defaults?.models ?? {}; const entries: Array<{ alias: string; model: string }> = []; for (const [keyRaw, entryRaw] of Object.entries(models)) { const model = String(keyRaw ?? "").trim(); if (!model) continue; const alias = String((entryRaw as { alias?: string } | undefined)?.alias ?? "").trim(); if (!alias) continue; entries.push({ alias, model }); } return entries .sort((a, b) => a.alias.localeCompare(b.alias)) .map((entry) => `- ${entry.alias}: ${entry.model}`); } export function resolveModel( provider: string, modelId: string, agentDir?: string, cfg?: ClawdbotConfig, ): { model?: Model; error?: string; authStorage: ReturnType; modelRegistry: ReturnType; } { const resolvedAgentDir = agentDir ?? resolveClawdbotAgentDir(); const authStorage = discoverAuthStorage(resolvedAgentDir); const modelRegistry = discoverModels(authStorage, resolvedAgentDir); const model = modelRegistry.find(provider, modelId) as Model | null; if (!model) { const providers = cfg?.models?.providers ?? {}; const inlineModels = providers[provider]?.models ?? Object.values(providers) .flatMap((entry) => entry?.models ?? []) .map((entry) => ({ ...entry, provider })); const inlineMatch = inlineModels.find((entry) => entry.id === modelId); if (inlineMatch) { const normalized = normalizeModelCompat(inlineMatch as Model); return { model: normalized, authStorage, modelRegistry, }; } const providerCfg = providers[provider]; if (providerCfg || modelId.startsWith("mock-")) { const fallbackModel: Model = normalizeModelCompat({ id: modelId, name: modelId, api: providerCfg?.api ?? "openai-responses", provider, reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: providerCfg?.models?.[0]?.contextWindow ?? DEFAULT_CONTEXT_TOKENS, maxTokens: providerCfg?.models?.[0]?.maxTokens ?? DEFAULT_CONTEXT_TOKENS, } as Model); return { model: fallbackModel, authStorage, modelRegistry }; } return { error: `Unknown model: ${provider}/${modelId}`, authStorage, modelRegistry, }; } return { model: normalizeModelCompat(model), authStorage, modelRegistry }; }