/**
 * Task-class (utility-tier) model routing — AI cost-control Phase 2.
 *
 * Background utility AI calls — image caption/OCR, conversation-title
 * generation, event summary/extraction — do NOT need the user's primary
 * (often expensive, e.g. Opus) model. This re-points them to a curated cheap
 * VISION-capable model of the SAME provider (same API key, no new credentials).
 * Interactive chat is never routed through here.
 *
 * Curated, NOT catalog-cheapest: sorting the catalog by raw cost picks weak
 * legacy/nano models (e.g. Claude 3 Haiku, gpt-*-nano) that tank caption/OCR
 * quality. The cheap-but-capable tier we actually want is hand-picked in the
 * `PROVIDER_MODELS` table (core/secrets/registry.ts) — the SAME table that
 * backs the recommendations dropdown — and read here via `utilityModelFor`.
 * A provider whose entry has `utility: null` (or no entry) keeps its primary;
 * that gap is observable via the usage ledger (feature+modelId) on
 * /api/admin/ai-usage rather than being silently swallowed.
 *
 * Fail-safe by construction: every uncertain branch returns the primary
 * unchanged, so a bad lookup, an unknown provider, or a catalog gap can never
 * break or silently disable a feature — at worst it just doesn't save money.
 */
import { getModel } from '@earendil-works/pi-ai';
import { supportsVision } from './model-capabilities.js';
import { utilityModelFor } from '../core/secrets/registry.js';
/** Sum of input+output $/1M from the catalog; null when the model is unknown. */
function catalogCost(provider, modelId) {
    try {
        const m = getModel(provider, modelId);
        if (!m?.cost)
            return null;
        return (m.cost.input ?? 0) + (m.cost.output ?? 0);
    }
    catch {
        return null;
    }
}
/**
 * Re-point a background utility call to the provider's curated cheap vision
 * model when that's STRICTLY cheaper than the primary. Returns the primary
 * unchanged in every other case (unknown provider, already on the tier, the
 * curated model isn't vision-capable, or either price is unknown). Same
 * provider + same apiKey — never crosses providers or needs new credentials.
 *
 * Kill switch: STUDIOGRAPH_DISABLE_UTILITY_ROUTING=1.
 */
export function resolveUtilityModelConfig(primary) {
    if (process.env.STUDIOGRAPH_DISABLE_UTILITY_ROUTING === '1')
        return primary;
    const utilityId = utilityModelFor(primary.provider);
    // Provider with no curated cheap tier (incl. the credits gateway and
    // providers whose flagship IS the cheapest vision model, e.g. xAI), or the
    // primary already IS the utility model → nothing to do.
    if (!utilityId || utilityId === primary.modelId)
        return primary;
    // The curated model must actually be vision-capable — captioning depends on it.
    if (!supportsVision(primary.provider, utilityId))
        return primary;
    // Only downgrade when the utility model is provably, strictly cheaper. If
    // either price is unknown, fail safe and keep the primary.
    const uCost = catalogCost(primary.provider, utilityId);
    const pCost = catalogCost(primary.provider, primary.modelId);
    if (uCost == null || pCost == null || uCost >= pCost)
        return primary;
    return { provider: primary.provider, modelId: utilityId, apiKey: primary.apiKey };
}
//# sourceMappingURL=utility-model-resolver.js.map