import type { Api, Model } from "@mariozechner/pi-ai"; import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent"; import type { ClawdbotConfig } from "../../config/config.js"; import type { ModelDefinitionConfig } from "../../config/types.js"; import { resolveClawdbotAgentDir } from "../agent-paths.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; import { normalizeModelCompat } from "../model-compat.js"; import { normalizeProviderId } from "../model-selection.js"; import { resolveGithubCopilotUserAgent } from "../../providers/github-copilot-utils.js"; type InlineModelEntry = ModelDefinitionConfig & { provider: string }; function applyProviderModelOverrides(model: Model): Model { if (model.provider === "github-copilot") { const headers = model.headers ? { ...model.headers, "User-Agent": resolveGithubCopilotUserAgent() } : { "User-Agent": resolveGithubCopilotUserAgent() }; return { ...model, headers }; } return model; } export function buildInlineProviderModels( providers: Record, ): InlineModelEntry[] { return Object.entries(providers).flatMap(([providerId, entry]) => { const trimmed = providerId.trim(); if (!trimmed) return []; return (entry?.models ?? []).map((model) => ({ ...model, provider: trimmed })); }); } 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 = buildInlineProviderModels(providers); const normalizedProvider = normalizeProviderId(provider); const inlineMatch = inlineModels.find( (entry) => normalizeProviderId(entry.provider) === normalizedProvider && entry.id === modelId, ); if (inlineMatch) { const normalized = normalizeModelCompat(inlineMatch as Model); return { model: applyProviderModelOverrides(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: applyProviderModelOverrides(fallbackModel), authStorage, modelRegistry }; } return { error: `Unknown model: ${provider}/${modelId}`, authStorage, modelRegistry, }; } return { model: applyProviderModelOverrides(normalizeModelCompat(model)), authStorage, modelRegistry, }; }