import type { Api, Model } from "@mariozechner/pi-ai"; import type { ModelRegistry } from "@mariozechner/pi-coding-agent"; import type { AuthProfileStore } from "../../agents/auth-profiles.js"; import { loadModelCatalog } from "../../agents/model-catalog.js"; import { shouldSuppressBuiltInModel } from "../../agents/model-suppression.js"; import { resolveModelWithRegistry } from "../../agents/pi-embedded-runner/model.js"; import type { OpenClawConfig } from "../../config/config.js"; import { loadModelRegistry, toModelRow } from "./list.registry.js"; import type { ConfiguredEntry, ModelRow } from "./list.types.js"; import { isLocalBaseUrl, modelKey } from "./shared.js"; type ConfiguredByKey = Map; type RowFilter = { provider?: string; local?: boolean; }; type RowBuilderContext = { cfg: OpenClawConfig; authStore: AuthProfileStore; availableKeys?: Set; configuredByKey: ConfiguredByKey; discoveredKeys: Set; filter: RowFilter; }; function matchesRowFilter(filter: RowFilter, model: { provider: string; baseUrl?: string }) { if (filter.provider && model.provider.toLowerCase() !== filter.provider) { return false; } if (filter.local && !isLocalBaseUrl(model.baseUrl ?? "")) { return false; } return true; } function buildRow(params: { model: Model; key: string; context: RowBuilderContext; allowProviderAvailabilityFallback?: boolean; }): ModelRow { const configured = params.context.configuredByKey.get(params.key); return toModelRow({ model: params.model, key: params.key, tags: configured ? Array.from(configured.tags) : [], aliases: configured?.aliases ?? [], availableKeys: params.context.availableKeys, cfg: params.context.cfg, authStore: params.context.authStore, allowProviderAvailabilityFallback: params.allowProviderAvailabilityFallback ?? false, }); } export async function loadListModelRegistry( cfg: OpenClawConfig, opts?: { sourceConfig?: OpenClawConfig }, ) { const loaded = await loadModelRegistry(cfg, opts); return { ...loaded, discoveredKeys: new Set(loaded.models.map((model) => modelKey(model.provider, model.id))), }; } export function appendDiscoveredRows(params: { rows: ModelRow[]; models: Model[]; context: RowBuilderContext; }): Set { const seenKeys = new Set(); const sorted = [...params.models].toSorted((a, b) => { const providerCompare = a.provider.localeCompare(b.provider); if (providerCompare !== 0) { return providerCompare; } return a.id.localeCompare(b.id); }); for (const model of sorted) { if (shouldSuppressBuiltInModel({ provider: model.provider, id: model.id })) { continue; } if (!matchesRowFilter(params.context.filter, model)) { continue; } const key = modelKey(model.provider, model.id); params.rows.push( buildRow({ model, key, context: params.context, }), ); seenKeys.add(key); } return seenKeys; } export async function appendCatalogSupplementRows(params: { rows: ModelRow[]; modelRegistry: ModelRegistry; context: RowBuilderContext; seenKeys: Set; }): Promise { const catalog = await loadModelCatalog({ config: params.context.cfg }); for (const entry of catalog) { if ( params.context.filter.provider && entry.provider.toLowerCase() !== params.context.filter.provider ) { continue; } const key = modelKey(entry.provider, entry.id); if (params.seenKeys.has(key)) { continue; } const model = resolveModelWithRegistry({ provider: entry.provider, modelId: entry.id, modelRegistry: params.modelRegistry, cfg: params.context.cfg, }); if (!model || !matchesRowFilter(params.context.filter, model)) { continue; } params.rows.push( buildRow({ model, key, context: params.context, allowProviderAvailabilityFallback: !params.context.discoveredKeys.has(key), }), ); params.seenKeys.add(key); } } export function appendConfiguredRows(params: { rows: ModelRow[]; entries: ConfiguredEntry[]; modelRegistry: ModelRegistry; context: RowBuilderContext; }) { for (const entry of params.entries) { if ( params.context.filter.provider && entry.ref.provider.toLowerCase() !== params.context.filter.provider ) { continue; } const model = resolveModelWithRegistry({ provider: entry.ref.provider, modelId: entry.ref.model, modelRegistry: params.modelRegistry, cfg: params.context.cfg, }); if (params.context.filter.local && model && !isLocalBaseUrl(model.baseUrl ?? "")) { continue; } if (params.context.filter.local && !model) { continue; } params.rows.push( toModelRow({ model, key: entry.key, tags: Array.from(entry.tags), aliases: entry.aliases, availableKeys: params.context.availableKeys, cfg: params.context.cfg, authStore: params.context.authStore, allowProviderAvailabilityFallback: model ? !params.context.discoveredKeys.has(modelKey(model.provider, model.id)) : false, }), ); } }