Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions cmd/router/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,17 @@ func main() {
})
}

{
// Meta Model API (muse-spark-1.1) — first-party, public preview since
// 2026-07-09. Model ID matches our public slug 1:1, so no modelIDMap.
metaBaseURL := config.GetOr("META_BASE_URL", openaiCompatProvider.MetaBaseURL)
registerDeploymentKeyedProvider(providerMap, envKeyedProviders, logger,
providers.ProviderMeta, "Meta", "META_API_KEY", metaBaseURL, byokOnly,
func(key, baseURL string) providers.Client {
return openaiCompatProvider.NewClient(key, baseURL)
})
}

{
// "bedrock-mantle" OpenAI-compatible surface (AWS-recommended over
// bedrock-runtime/InvokeModel). Auth is a static Bedrock API key
Expand Down Expand Up @@ -1427,8 +1438,8 @@ func envVarHint(provider string) string {
// key (respecting byokOnly), constructs its client via newClient, registers
// it in providerMap, and logs its BYOK/keyed/passthrough state. Shared by the
// providers whose registration collapses to "resolve key -> build client ->
// three-way log switch" (Fireworks, Makora, Together, Bedrock, Google);
// OpenRouter and Anthropic/OpenAI have genuinely different gating
// three-way log switch" (Fireworks, Makora, Together, XAI, Bedrock, Meta,
// Google); OpenRouter and Anthropic/OpenAI have genuinely different gating
// logic and stay bespoke. extraLogAttrs are appended only to the
// deployment-keyed log line (e.g. Bedrock's region).
func registerDeploymentKeyedProvider(
Expand Down
2 changes: 2 additions & 0 deletions install/cc-statusline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ prices='{
"moonshotai/kimi-k2.5": 0.0006,
"moonshotai/kimi-k2.6": 0.00095,
"moonshotai/kimi-k2.7": 0.00095,
"muse-spark-1.1": 0.00125,
"qwen/qwen3-235b-a22b-2507": 0.0002266,
"qwen/qwen3-30b-a3b-instruct-2507": 0.00015,
"qwen/qwen3-coder": 0.0009,
Expand Down Expand Up @@ -245,6 +246,7 @@ prices='{
"moonshotai/kimi-k2.5": 0.003,
"moonshotai/kimi-k2.6": 0.004,
"moonshotai/kimi-k2.7": 0.004,
"muse-spark-1.1": 0.00425,
"qwen/qwen3-235b-a22b-2507": 0.0009064,
"qwen/qwen3-30b-a3b-instruct-2507": 0.0006,
"qwen/qwen3-coder": 0.0027,
Expand Down
2 changes: 2 additions & 0 deletions install/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,7 @@ prices='{
"moonshotai/kimi-k2.5": 0.0006,
"moonshotai/kimi-k2.6": 0.00095,
"moonshotai/kimi-k2.7": 0.00095,
"muse-spark-1.1": 0.00125,
"qwen/qwen3-235b-a22b-2507": 0.0002266,
"qwen/qwen3-30b-a3b-instruct-2507": 0.00015,
"qwen/qwen3-coder": 0.0009,
Expand Down Expand Up @@ -2190,6 +2191,7 @@ prices='{
"moonshotai/kimi-k2.5": 0.003,
"moonshotai/kimi-k2.6": 0.004,
"moonshotai/kimi-k2.7": 0.004,
"muse-spark-1.1": 0.00425,
"qwen/qwen3-235b-a22b-2507": 0.0009064,
"qwen/qwen3-30b-a3b-instruct-2507": 0.0006,
"qwen/qwen3-coder": 0.0027,
Expand Down
3 changes: 3 additions & 0 deletions internal/observability/otel/pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ func TestLookup(t *testing.T) {
// ── xAI Grok ───────────────────────────────────────────
{name: "grok-4.5", model: "grok-4.5", wantInput: 2.00, wantOutput: 6.00},

// ── Meta ───────────────────────────────────────────────
{name: "muse-spark-1.1", model: "muse-spark-1.1", wantInput: 1.25, wantOutput: 4.25},

// ── OpenAI GPT-5.4 ─────────────────────────────────────
{name: "gpt-5.4", model: "gpt-5.4", wantInput: 2.50, wantOutput: 15.00},
{name: "gpt-5.4-pro", model: "gpt-5.4-pro", wantInput: 30.00, wantOutput: 180.00},
Expand Down
5 changes: 5 additions & 0 deletions internal/providers/openaicompat/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const (
TogetherBaseURL = "https://api.together.xyz/v1"
// XAIBaseURL is SpaceXAI's OpenAI-compatible Chat Completions surface.
XAIBaseURL = "https://api.x.ai/v1"
// MetaBaseURL is Meta Superintelligence Labs' Model API — first-party
// upstream for muse-spark-1.1, in public preview (US-only) since
// 2026-07-09. OpenAI-compatible Chat Completions surface; model IDs match
// the router's public slug so no modelIDMap rewrite is needed.
Comment on lines +37 to +40

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// MetaBaseURL is Meta Superintelligence Labs' Model API — first-party
// upstream for muse-spark-1.1, in public preview (US-only) since
// 2026-07-09. OpenAI-compatible Chat Completions surface; model IDs match
// the router's public slug so no modelIDMap rewrite is needed.
// MetaBaseURL is Meta Superintelligence Labs' first-party Model API
// (public preview, US-only since 2026-07-09).

Was 4 lines; the last 2 restate what the constant name and value already show (OpenAI-compat surface, no ID rewrite needed).

MetaBaseURL = "https://api.meta.ai/v1"
)

// BedrockMantleBaseURLTemplate is the OpenAI-compatible bedrock-mantle endpoint
Expand Down
3 changes: 3 additions & 0 deletions internal/providers/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const (
ProviderMakora = "makora"
ProviderTogether = "together"
ProviderXAI = "xai"
ProviderMeta = "meta"
)

// TranslationFamily is the wire-format family a provider speaks; the proxy
Expand Down Expand Up @@ -89,6 +90,7 @@ var ProviderFamilies = map[string]TranslationFamily{
ProviderMakora: FamilyOpenAICompat,
ProviderTogether: FamilyOpenAICompat,
ProviderXAI: FamilyOpenAICompat,
ProviderMeta: FamilyOpenAICompat,
}

// FamilyFor returns the translation family for a provider, or FamilyUnknown
Expand Down Expand Up @@ -143,6 +145,7 @@ var APIKeyEnvVars = map[string]string{
ProviderMakora: "MAKORA_API_KEY",
ProviderTogether: "TOGETHER_API_KEY",
ProviderXAI: "XAI_API_KEY",
ProviderMeta: "META_API_KEY",
}

// APIKeyEnvVar returns the env-var name for the given provider, or empty
Expand Down
4 changes: 4 additions & 0 deletions internal/proxy/force_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ var forceModelAliases = map[string]string{
"minimax-m3": "minimax/minimax-m3",
"minimax-m2.7": "minimax/minimax-m2.7",
"mistral": "mistralai/mistral-small-2603",
"meta": "muse-spark-1.1",
"muse": "muse-spark-1.1",
"muse-spark": "muse-spark-1.1",
"musespark": "muse-spark-1.1",
}

// resolveForceModel maps a user-typed model identifier to its canonical
Expand Down
13 changes: 13 additions & 0 deletions internal/router/catalog/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,19 @@ var Models = []Model{
{Provider: providers.ProviderGoogle, UpstreamID: "gemma-4-26b-a4b-it", Price: Pricing{InputUSDPer1M: 0.15, OutputUSDPer1M: 0.60, CacheReadMultiplier: 0.10}},
}},

// --- Meta --- Muse Spark 1.1, GA 2026-07-09 (Meta's first paid model,
// Meta Model API public preview, US-only). Bare ID (not "meta/"-prefixed):
// direct first-party API like Anthropic/OpenAI/Google/xAI, not an
// aggregator-hosted OSS entry. Native 1M context (Meta docs list
// 1,048,576). Strong agentic/tool-use, mid-pack coding (SWE-Bench Pro
// 61.5 vs opus-4.8's 69.2) — priced accordingly below the high tier's
// flagships. Meta is the only wired provider (first-party, no OpenRouter
// fallback yet); add one if/when it earns roster traffic.
Comment on lines +327 to +334

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// --- Meta --- Muse Spark 1.1, GA 2026-07-09 (Meta's first paid model,
// Meta Model API public preview, US-only). Bare ID (not "meta/"-prefixed):
// direct first-party API like Anthropic/OpenAI/Google/xAI, not an
// aggregator-hosted OSS entry. Native 1M context (Meta docs list
// 1,048,576). Strong agentic/tool-use, mid-pack coding (SWE-Bench Pro
// 61.5 vs opus-4.8's 69.2) — priced accordingly below the high tier's
// flagships. Meta is the only wired provider (first-party, no OpenRouter
// fallback yet); add one if/when it earns roster traffic.
// --- Meta --- Muse Spark 1.1, first-party (bare ID, not "meta/"-prefixed,
// matching xAI precedent). Single provider; add OpenRouter fallback if/when
// it earns roster traffic.

Was 8 lines; SWE-bench scores and context-window verbosity don't belong in catalog source. The genuine WHYs (bare-ID convention, single-provider note) fit in 3.

{ID: "muse-spark-1.1", Tier: TierHigh, ContextWindow: 1_048_576, Providers: []ProviderBinding{
{Provider: providers.ProviderMeta,
Price: Pricing{InputUSDPer1M: 1.25, OutputUSDPer1M: 4.25, CacheReadMultiplier: 0.15 / 1.25}},
}},

// --- OSS pool ---
//
// Each row carries an ordered Providers list. Managed-prod ships only the
Expand Down
1 change: 1 addition & 0 deletions internal/router/catalog/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestCatalog_BindingsReferenceCanonicalProviders(t *testing.T) {
providers.ProviderMakora: {},
providers.ProviderTogether: {},
providers.ProviderXAI: {},
providers.ProviderMeta: {},
}
for _, m := range Models {
for i, b := range m.Providers {
Expand Down
8 changes: 8 additions & 0 deletions internal/router/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ var registry = map[string]ModelSpec{
// rejects stop / presence / frequency penalties (CapReasoning strips stop).
"grok-4.5": openaiReasoning,

// muse-spark-1.1: OpenAI-compat reasoning_effort, mandatory (cannot
// disable), supports minimal/low/medium/high/xhigh upstream — but neither
// tier is in router.ReasoningCapabilities.Levels' ordered vocabulary yet,
// so it shares openaiReasoning's low/medium/high; a request for xhigh or
// minimal clamps to the nearest of those (nearestReasoningLevel) rather
// than 400ing.
Comment on lines +170 to +175

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// muse-spark-1.1: OpenAI-compat reasoning_effort, mandatory (cannot
// disable), supports minimal/low/medium/high/xhigh upstream — but neither
// tier is in router.ReasoningCapabilities.Levels' ordered vocabulary yet,
// so it shares openaiReasoning's low/medium/high; a request for xhigh or
// minimal clamps to the nearest of those (nearestReasoningLevel) rather
// than 400ing.
// muse-spark-1.1: reasoning_effort mandatory; upstream also accepts
// minimal/xhigh but those clamp via nearestReasoningLevel rather than 400.

Was 6 lines; "OpenAI-compat" and the registry/vocabulary detail are noise — the only non-obvious fact is the clamp-not-400 behavior.

"muse-spark-1.1": openaiReasoning,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing max-output token cap

High Severity

muse-spark-1.1 is registered with CapReasoning (openaiReasoning) but has no modelMaxOutputTokens entry. Emit then clamps max_completion_tokens to the 8192 default via clampToModelOutputCap. Peer CapReasoning models (GPT-5.x, grok-4.5) use 128k–131k caps; Meta documents a much larger output budget. Claude Code's typical 32k–64k max_tokens would be cut down, so reasoning can exhaust the budget and truncate tool calls or content.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ace3832. Configure here.


"gpt-5.4": openaiReasoning,
"gpt-5.4-pro": openaiReasoning,
"gpt-5.4-mini": openaiReasoning,
Expand Down
2 changes: 2 additions & 0 deletions internal/router/rl/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func rosterIDFor(m catalog.Model) string {
return "google/" + m.ID
case providers.ProviderXAI:
return "x-ai/" + m.ID
case providers.ProviderMeta:
return "meta/" + m.ID
}
return m.ID
}
1 change: 1 addition & 0 deletions internal/router/rl/mapping_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var expectedRosterPrefix = map[string]string{
providers.ProviderOpenAI: "openai/",
providers.ProviderGoogle: "google/",
providers.ProviderXAI: "x-ai/",
providers.ProviderMeta: "meta/",
}

// defaultRosterPrefixProviders are providers intentionally left off
Expand Down
4 changes: 4 additions & 0 deletions internal/translate/session_affinity_coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var defaultMechanismProviders = map[string]struct{}{
providers.ProviderFireworks: {},
providers.ProviderMakora: {},
providers.ProviderTogether: {},
// Meta's docs advertise prompt caching but don't document a stickiness
// header (unlike xAI's x-grok-conv-id) — generic x-session-affinity is
// the safe default until proven otherwise.
Comment on lines +49 to +51

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Meta's docs advertise prompt caching but don't document a stickiness
// header (unlike xAI's x-grok-conv-id) — generic x-session-affinity is
// the safe default until proven otherwise.
// Meta's API has no documented stickiness header (unlike xAI's x-grok-conv-id).

Was 3 lines; the WHY fits in one.

providers.ProviderMeta: {},
}

// TestSessionAffinityCoversEveryOpenAICompatProvider guards against a new
Expand Down
Loading