From 3acb74fe0ad2fdcdb9aa678f9d9a53fc747a0ac8 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Mon, 10 Aug 2026 20:10:42 -0300 Subject: [PATCH] feat(providers): add Muse Code CLI provider preset (#9544) (#9670) * feat(providers): add Muse Code CLI provider preset (#9544) * fix(providers): register muse-code canonical provider + golden snapshot - Add muse-code to APIKEY_PROVIDERS_FRONTIER so check:provider-consistency passes - Regenerate translate-path golden snapshot to include the muse-code entry (20 additive lines, no other providers changed) --------- Co-authored-by: diegosouzapw --- .../features/9544-muse-code-cli-provider.md | 1 + open-sse/config/providers/index.ts | 2 + .../providers/registry/muse-code/index.ts | 106 ++++++++++++++++++ src/app/api/v1/muse-code/models/route.ts | 87 ++++++++++++++ .../constants/clientIdentityProfiles.ts | 8 ++ .../providers/apikey/frontier-labs.ts | 15 +++ tests/snapshots/provider/translate-path.json | 20 ++++ tests/unit/muse-code-models.test.ts | 81 +++++++++++++ tests/unit/muse-code-provider.test.ts | 91 +++++++++++++++ 9 files changed, 411 insertions(+) create mode 100644 changelog.d/features/9544-muse-code-cli-provider.md create mode 100644 open-sse/config/providers/registry/muse-code/index.ts create mode 100644 src/app/api/v1/muse-code/models/route.ts create mode 100644 tests/unit/muse-code-models.test.ts create mode 100644 tests/unit/muse-code-provider.test.ts diff --git a/changelog.d/features/9544-muse-code-cli-provider.md b/changelog.d/features/9544-muse-code-cli-provider.md new file mode 100644 index 0000000000..23994a628f --- /dev/null +++ b/changelog.d/features/9544-muse-code-cli-provider.md @@ -0,0 +1 @@ +- feat(providers): add Muse Code CLI provider preset (#9544) diff --git a/open-sse/config/providers/index.ts b/open-sse/config/providers/index.ts index ebfe5a7f03..80357cf981 100644 --- a/open-sse/config/providers/index.ts +++ b/open-sse/config/providers/index.ts @@ -228,6 +228,7 @@ import { digitaloceanProvider } from "./registry/digitalocean/index.ts"; import { hcnsecProvider } from "./registry/hcnsec/index.ts"; import { promptqlProvider } from "./registry/promptql/index.ts"; import { hyperagentProvider } from "./registry/hyperagent/index.ts"; +import { muse_codeProvider } from "./registry/muse-code/index.ts"; export const REGISTRY: Record = { aimlapi: aimlapiProvider, @@ -457,5 +458,6 @@ export const REGISTRY: Record = { hcnsec: hcnsecProvider, promptql: promptqlProvider, hyperagent: hyperagentProvider, + "muse-code": muse_codeProvider, unorouter: unorouterProvider, }; diff --git a/open-sse/config/providers/registry/muse-code/index.ts b/open-sse/config/providers/registry/muse-code/index.ts new file mode 100644 index 0000000000..66f59f9f42 --- /dev/null +++ b/open-sse/config/providers/registry/muse-code/index.ts @@ -0,0 +1,106 @@ +import type { RegistryEntry } from "../../shared.ts"; +import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts"; + +/** + * Muse Code CLI — Meta's agentic coding tool. + * + * Wire format: OpenAI Responses API (POST /responses). + * Auth: Bearer token from META_API_KEY env var. + * Reasoning efforts: xhigh/ultra -> high (handled generically). + * + * @see https://github.com/joymadhu49/muse-openrouter-shim + */ +export const muse_codeProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({ + id: "muse-code", + alias: "mc", + passthroughModels: true, + defaultContextLength: 200000, + models: [ + { + id: "llama-4-maverick", + name: "Llama 4 Maverick", + contextLength: 1048576, + maxOutputTokens: 131072, + supportsReasoning: true, + supportsXHighEffort: true, + toolCalling: true, + supportsVision: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs", "logitBias"], + }, + { + id: "llama-4-scout", + name: "Llama 4 Scout", + contextLength: 1048576, + maxOutputTokens: 131072, + supportsReasoning: true, + supportsXHighEffort: true, + toolCalling: true, + supportsVision: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs", "logitBias"], + }, + { + id: "llama-3.3-70b", + name: "Llama 3.3 70B", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + { + id: "llama-3.1-405b", + name: "Llama 3.1 405B", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + { + id: "llama-3.1-70b", + name: "Llama 3.1 70B", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + { + id: "llama-3.1-8b", + name: "Llama 3.1 8B", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + { + id: "llama-3.2-90b-vision", + name: "Llama 3.2 90B Vision", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + supportsVision: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + { + id: "llama-3.2-11b-vision", + name: "Llama 3.2 11B Vision", + contextLength: 131072, + maxOutputTokens: 32768, + supportsReasoning: false, + toolCalling: true, + supportsVision: true, + targetFormat: "openai-responses", + unsupportedParams: ["logprobs", "topLogprobs"], + }, + ], +}); diff --git a/src/app/api/v1/muse-code/models/route.ts b/src/app/api/v1/muse-code/models/route.ts new file mode 100644 index 0000000000..5fb1cf15be --- /dev/null +++ b/src/app/api/v1/muse-code/models/route.ts @@ -0,0 +1,87 @@ +/** + * Muse Code CLI proprietary model catalog endpoint. + * + * Muse CLI calls GET /muse-code/models (or --base-url/muse-code/models) + * to discover available models. Returns the proprietary Muse format: + * + * { object: "list", data: [{ id, object, created, owned_by, metadata }] } + * + * Each model's metadata includes: name, family, reasoning, tool_call, + * modalities, limit, cost. + */ + +import { muse_codeProvider } from "@omniroute/open-sse/config/providers/registry/muse-code/index.ts"; + +const MUSECODE_TIMESTAMP = Math.floor(Date.now() / 1000); + +interface MuseCodeModel { + id: string; + object: "model"; + created: number; + owned_by: string; + metadata: { + name: string; + family: string; + reasoning: boolean; + tool_call: boolean; + modalities: string[]; + limit: number; + cost: number; + }; +} + +function buildModelCatalog(): MuseCodeModel[] { + const data: MuseCodeModel[] = []; + + for (const model of muse_codeProvider.models) { + let family = "llama"; + if (model.id.includes("llama-4")) family = "llama-4"; + else if (model.id.includes("llama-3.3")) family = "llama-3.3"; + else if (model.id.includes("llama-3.2")) family = "llama-3.2"; + else if (model.id.includes("llama-3.1")) family = "llama-3.1"; + + const modalities: string[] = ["text"]; + if (model.supportsVision) modalities.push("image"); + + data.push({ + id: model.id, + object: "model", + created: MUSECODE_TIMESTAMP, + owned_by: "meta", + metadata: { + name: model.name, + family, + reasoning: !!model.supportsReasoning, + tool_call: !!model.toolCalling, + modalities, + limit: model.contextLength ?? 200_000, + cost: model.id.includes("maverick") || model.id.includes("405b") ? 3 : 1, + }, + }); + } + + return data; +} + +// Cache the catalog for the lifetime of the process — model list is static. +const CATALOG = buildModelCatalog(); +const CATALOG_PAYLOAD = JSON.stringify({ object: "list", data: CATALOG }, null, 2); + +export async function OPTIONS() { + return new Response(null, { + headers: { + "Access-Control-Allow-Methods": "GET, OPTIONS", + "Access-Control-Allow-Headers": "*", + }, + }); +} + +export async function GET() { + return new Response(CATALOG_PAYLOAD, { + status: 200, + headers: { + "content-type": "application/json", + "cache-control": "public, max-age=3600", + }, + }); +} diff --git a/src/shared/constants/clientIdentityProfiles.ts b/src/shared/constants/clientIdentityProfiles.ts index 35770fae17..ee16e1e51e 100644 --- a/src/shared/constants/clientIdentityProfiles.ts +++ b/src/shared/constants/clientIdentityProfiles.ts @@ -51,6 +51,13 @@ const GEMINI_CLI_PROFILE: ClientIdentityProfile = Object.freeze({ "User-Agent": "GeminiCLI/0.1.0 (linux; x64)", }), }); +const MUSE_CLI_PROFILE: ClientIdentityProfile = Object.freeze({ + id: "muse-cli", + label: "Muse Code CLI", + headers: Object.freeze({ + "User-Agent": "MuseCodeCLI/0.1.0 (linux; x64)", + }), +}); /** Ordered so `CLIENT_IDENTITY_PROFILE_OPTIONS` renders "Default" first. */ export const CLIENT_IDENTITY_PROFILES: Readonly> = @@ -59,6 +66,7 @@ export const CLIENT_IDENTITY_PROFILES: Readonly", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": {} + }, "muse-spark-web": { "format": "openai", "headers": { diff --git a/tests/unit/muse-code-models.test.ts b/tests/unit/muse-code-models.test.ts new file mode 100644 index 0000000000..6a12789e9b --- /dev/null +++ b/tests/unit/muse-code-models.test.ts @@ -0,0 +1,81 @@ +/** + * Tests for Muse Code CLI model catalog endpoint. + * + * Verifies GET /v1/muse-code/models returns the proprietary Muse format. + */ + +import test from "node:test"; +import assert from "node:assert/strict"; + +import { muse_codeProvider } from "../../open-sse/config/providers/registry/muse-code/index.ts"; + +// ── Model catalog shape ───────────────────────────────────────────────────── + +test("muse-code provider has at least one model", () => { + assert.ok(muse_codeProvider.models.length >= 1); +}); + +test("muse-code models have unique ids", () => { + const ids = muse_codeProvider.models.map((m) => m.id); + const unique = new Set(ids); + assert.equal(unique.size, ids.length, "model IDs must be unique"); +}); + +test("muse-code models include llama-4-maverick", () => { + const ids = muse_codeProvider.models.map((m) => m.id); + assert.ok(ids.includes("llama-4-maverick"), "must include llama-4-maverick"); +}); + +test("muse-code models include llama-4-scout", () => { + const ids = muse_codeProvider.models.map((m) => m.id); + assert.ok(ids.includes("llama-4-scout"), "must include llama-4-scout"); +}); + +test("muse-code models include llama-3.3-70b", () => { + const ids = muse_codeProvider.models.map((m) => m.id); + assert.ok(ids.includes("llama-3.3-70b"), "must include llama-3.3-70b"); +}); + +test("llama-4 models have supportsXHighEffort", () => { + const maverick = muse_codeProvider.models.find((m) => m.id === "llama-4-maverick"); + assert.ok(maverick, "llama-4-maverick must exist"); + assert.equal(maverick.supportsXHighEffort, true); + + const scout = muse_codeProvider.models.find((m) => m.id === "llama-4-scout"); + assert.ok(scout, "llama-4-scout must exist"); + assert.equal(scout.supportsXHighEffort, true); +}); + +test("llama-3.3-70b does not support reasoning", () => { + const model = muse_codeProvider.models.find((m) => m.id === "llama-3.3-70b"); + assert.ok(model, "llama-3.3-70b must exist"); + assert.equal(model.supportsReasoning, false); +}); + +test("non-reasoning models do not declare supportsXHighEffort", () => { + for (const model of muse_codeProvider.models) { + if (!model.supportsReasoning) { + assert.equal( + model.supportsXHighEffort, + undefined, + `${model.id} is not a reasoning model but has supportsXHighEffort` + ); + } + } +}); + +// ── Vision models ─────────────────────────────────────────────────────────── + +test("vision models have supportsVision: true", () => { + const expectedVision = [ + "llama-4-maverick", + "llama-4-scout", + "llama-3.2-90b-vision", + "llama-3.2-11b-vision", + ]; + for (const model of muse_codeProvider.models) { + if (expectedVision.includes(model.id)) { + assert.equal(model.supportsVision, true, `${model.id} should have supportsVision`); + } + } +}); diff --git a/tests/unit/muse-code-provider.test.ts b/tests/unit/muse-code-provider.test.ts new file mode 100644 index 0000000000..42c582aabe --- /dev/null +++ b/tests/unit/muse-code-provider.test.ts @@ -0,0 +1,91 @@ +/** + * Tests for Muse Code CLI provider registry entry. + * + * Verifies the provider entry loads correctly with expected config. + */ + +import test from "node:test"; +import assert from "node:assert/strict"; + +import { muse_codeProvider } from "../../open-sse/config/providers/registry/muse-code/index.ts"; +import { getRegistryEntry } from "../../open-sse/config/providerRegistry.ts"; + +// ── Registry entry structure ──────────────────────────────────────────────── + +test("muse-code provider entry has id", () => { + assert.equal(muse_codeProvider.id, "muse-code"); +}); + +test("muse-code provider entry has alias", () => { + assert.equal(muse_codeProvider.alias, "mc"); +}); + +test("muse-code provider uses openai format", () => { + assert.equal(muse_codeProvider.format, "openai"); +}); + +test("muse-code provider uses apikey auth", () => { + assert.equal(muse_codeProvider.authType, "apikey"); + assert.equal(muse_codeProvider.authHeader, "bearer"); +}); + +test("muse-code provider has passthroughModels enabled", () => { + assert.equal(muse_codeProvider.passthroughModels, true); +}); + +// ── Model entries ─────────────────────────────────────────────────────────── + +test("muse-code provider has curated models", () => { + assert.ok(muse_codeProvider.models.length > 0); +}); + +test("all muse-code models have contextLength", () => { + for (const model of muse_codeProvider.models) { + assert.ok( + typeof model.contextLength === "number" && model.contextLength > 0, + `${model.id} must have positive contextLength` + ); + } +}); + +test("all muse-code models have toolCalling: true", () => { + for (const model of muse_codeProvider.models) { + assert.equal(model.toolCalling, true, `${model.id} must have toolCalling enabled`); + } +}); + +test("all muse-code models have targetFormat: openai-responses", () => { + for (const model of muse_codeProvider.models) { + assert.equal( + model.targetFormat, + "openai-responses", + `${model.id} must use openai-responses target format` + ); + } +}); + +test("reasoning models have supportsXHighEffort", () => { + for (const model of muse_codeProvider.models) { + if (model.supportsReasoning) { + assert.equal( + model.supportsXHighEffort, + true, + `${model.id} is a reasoning model but missing supportsXHighEffort` + ); + } + } +}); + +// ── Registry discovery ────────────────────────────────────────────────────── + +test("muse-code is discoverable via getRegistryEntry", () => { + const entry = getRegistryEntry("muse-code"); + assert.ok(entry, "getRegistryEntry must return muse-code entry"); + assert.equal(entry.id, "muse-code"); +}); + +test("muse-code is discoverable via alias", () => { + const entry = getRegistryEntry("mc"); + assert.ok(entry, "getRegistryEntry must find muse-code by alias mc"); + assert.equal(entry.id, "muse-code"); +});