UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

87 lines • 3.98 kB
/** * "First rank" Claude default: the newest Opus the account actually advertises. * * Server-side twin of the panel's `pickDefaultModel` (comfyui-mcp-panel, * web/js/lib/model-catalog.js). It has to be the SAME rule: the panel highlights * its pick as the default, and the orchestrator decides what a tab really spawns * on — two rules would put one model in the picker and another in the turn. * * Reading it from `supportedModels()` rather than baking an id is what makes it * safe to auto-upgrade: that list is per-account, so the pick is always a model * this subscription can actually run. A baked `claude-opus-5` was a generation * behind the moment the SDK learned `claude-opus-5-5`. */ const CLAUDE_FAMILIES = ["opus", "sonnet", "haiku", "fable", "mythos"]; /** Strip a trailing context marker (`[1m]`). A `-fast` suffix is deliberately kept: * it is a distinct, differently-priced variant and must fail the pinned-id parse. */ function stripModelSuffix(id) { return id.replace(/\[[^\]]*\]\s*$/, "").trim(); } export function parseClaudeModel(id, resolved) { const pinned = (src) => { const m = /^claude-(opus|sonnet|haiku|fable|mythos)-(\d+)(?:-(\d+))?$/.exec(stripModelSuffix(src).toLowerCase()); if (!m) return null; const version = [Number(m[2])]; if (m[3] !== undefined) version.push(Number(m[3])); return { family: m[1], version, alias: false }; }; const own = pinned(id); if (own) return own; const bare = stripModelSuffix(id).toLowerCase(); if (CLAUDE_FAMILIES.includes(bare)) { const r = resolved ? pinned(resolved) : null; return { family: bare, version: r ? r.version : null, alias: true }; } return resolved ? pinned(resolved) : null; } /** >0 when `a` is newer than `b`; null sorts lowest. */ export function cmpVersion(a, b) { if (!a && !b) return 0; if (!a) return -1; if (!b) return 1; for (let i = 0; i < Math.max(a.length, b.length); i++) { const d = (a[i] ?? 0) - (b[i] ?? 0); if (d) return d; } return 0; } /** On a version tie: a plain pinned id beats a `[1m]`-suffixed one, and both beat an * alias. An alias resolves to whatever the installed SDK thinks is newest, which is * exactly what lagged in #377 (`opus` still meaning 4.8 after Opus 5 shipped). */ function tieRank(value, parsed) { if (parsed.alias) return 2; return stripModelSuffix(value) === value.trim() ? 0 : 1; } /** The value to default to, or undefined for an empty list. Newest Opus first; with no * recognizable Opus, the first opus-ish row, then the SDK's own first row. * * The SDK's `default` row ("Default (recommended)") is never a candidate, exactly as * the panel's presentableModels drops it before its own pickDefaultModel runs. It is * not a family, it is whatever Anthropic currently recommends — free to point at a * Sonnet tomorrow. And because it is not a family name, parseClaudeModel reads it * through its resolved id as if it were PINNED, so it won every tie against `opus`: * on a live account (2026-10-13) the newest Opus is advertised ONLY as the `default` * and `opus` aliases, and beta.5 picked `default` while the panel picked `opus`. */ export function pickTopClaudeModel(models) { const pool = models.filter((m) => m.value !== "default"); const candidates = pool.length ? pool : models; const opus = candidates .map((m, i) => ({ m, i, c: parseClaudeModel(m.value, m.resolvedModel) })) .filter((x) => x.c?.family === "opus"); if (opus.length) { opus.sort((a, b) => cmpVersion(b.c.version, a.c.version) || tieRank(a.m.value, a.c) - tieRank(b.m.value, b.c) || a.i - b.i); return opus[0].m.value; } return (candidates.find((m) => /opus/i.test(m.value)) ?? candidates[0])?.value; } //# sourceMappingURL=model-defaults.js.map