diff --git a/cmd/router/main.go b/cmd/router/main.go index 9a3c7abf..cb027152 100644 --- a/cmd/router/main.go +++ b/cmd/router/main.go @@ -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 @@ -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( diff --git a/install/cc-statusline.sh b/install/cc-statusline.sh index 8db5c448..11b0199c 100755 --- a/install/cc-statusline.sh +++ b/install/cc-statusline.sh @@ -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, @@ -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, diff --git a/install/install.sh b/install/install.sh index cdb29353..cdd52d9d 100755 --- a/install/install.sh +++ b/install/install.sh @@ -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, @@ -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, diff --git a/internal/observability/otel/pricing_test.go b/internal/observability/otel/pricing_test.go index 6049f5d8..82c5c776 100644 --- a/internal/observability/otel/pricing_test.go +++ b/internal/observability/otel/pricing_test.go @@ -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}, diff --git a/internal/providers/openaicompat/client.go b/internal/providers/openaicompat/client.go index 2d6e4468..a390eebb 100644 --- a/internal/providers/openaicompat/client.go +++ b/internal/providers/openaicompat/client.go @@ -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. + MetaBaseURL = "https://api.meta.ai/v1" ) // BedrockMantleBaseURLTemplate is the OpenAI-compatible bedrock-mantle endpoint diff --git a/internal/providers/provider.go b/internal/providers/provider.go index ae8bfe81..379c872d 100644 --- a/internal/providers/provider.go +++ b/internal/providers/provider.go @@ -55,6 +55,7 @@ const ( ProviderMakora = "makora" ProviderTogether = "together" ProviderXAI = "xai" + ProviderMeta = "meta" ) // TranslationFamily is the wire-format family a provider speaks; the proxy @@ -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 @@ -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 diff --git a/internal/proxy/force_model.go b/internal/proxy/force_model.go index 487faf14..6a22496c 100644 --- a/internal/proxy/force_model.go +++ b/internal/proxy/force_model.go @@ -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 diff --git a/internal/router/catalog/catalog.go b/internal/router/catalog/catalog.go index c5f165e8..08dc42f5 100644 --- a/internal/router/catalog/catalog.go +++ b/internal/router/catalog/catalog.go @@ -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. + {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 diff --git a/internal/router/catalog/catalog_test.go b/internal/router/catalog/catalog_test.go index 24a52b53..41f20a66 100644 --- a/internal/router/catalog/catalog_test.go +++ b/internal/router/catalog/catalog_test.go @@ -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 { diff --git a/internal/router/model.go b/internal/router/model.go index 1ed2c7f1..17e0d06f 100644 --- a/internal/router/model.go +++ b/internal/router/model.go @@ -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. + "muse-spark-1.1": openaiReasoning, + "gpt-5.4": openaiReasoning, "gpt-5.4-pro": openaiReasoning, "gpt-5.4-mini": openaiReasoning, diff --git a/internal/router/rl/mapping.go b/internal/router/rl/mapping.go index 68d43f15..e81f1971 100644 --- a/internal/router/rl/mapping.go +++ b/internal/router/rl/mapping.go @@ -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 } diff --git a/internal/router/rl/mapping_internal_test.go b/internal/router/rl/mapping_internal_test.go index ff638b76..5a044716 100644 --- a/internal/router/rl/mapping_internal_test.go +++ b/internal/router/rl/mapping_internal_test.go @@ -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 diff --git a/internal/translate/session_affinity_coverage_test.go b/internal/translate/session_affinity_coverage_test.go index 95447bbe..2b1af0ec 100644 --- a/internal/translate/session_affinity_coverage_test.go +++ b/internal/translate/session_affinity_coverage_test.go @@ -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. + providers.ProviderMeta: {}, } // TestSessionAffinityCoversEveryOpenAICompatProvider guards against a new