diff --git a/src/cli.py b/src/cli.py index c0edc7bb0..72e50244b 100644 --- a/src/cli.py +++ b/src/cli.py @@ -470,7 +470,7 @@ def _build_parser() -> argparse.ArgumentParser: '--provider', type=str, default=None, - help='Override the provider (anthropic, openai, zai, minimax, openrouter, deepseek)', + help='Override the provider (anthropic, openai, zai, minimax, openrouter, deepseek, meta)', ) noninteractive.add_argument( '--allowed-tools', diff --git a/src/models/configs.py b/src/models/configs.py index ffe33b984..04e67352f 100644 --- a/src/models/configs.py +++ b/src/models/configs.py @@ -187,6 +187,36 @@ class ModelConfig: max_output_tokens=8_192, supports_cache=True, ), + # Meta Muse Spark 1.1 (api.meta.ai, OpenAI-compatible). Muse Spark is a + # server-side reasoning model (usage reports ``reasoning_tokens``); like + # DeepSeek/GLM it exposes no Anthropic-style thinking blocks (the + # ``thinking=`` kwarg is gated on ``is_anthropic`` in query.py), so the + # capability flags keep their defaults. Pricing lives in + # ``services/pricing.py`` (single source) — the cost_* fields are unset. + # A future ``muse-spark-2.x`` would prefix-match this row via + # ``get_model_config`` (base ``muse-spark``); register such variants + # explicitly, as the DeepSeek/GLM rows above note. + # + # context_window=1_048_576 (2^20): Meta's documented window — the + # api.meta.ai overview page states 1,048,576 tokens. Same 1M-class tier as + # the DeepSeek-V4 / GLM-5.2 rows above. + # + # max_output_tokens=16_384 is NOT sent as the wire ``max_tokens``: + # query.py forwards ``resolve_max_output_tokens()`` only for + # Anthropic/Minimax providers; OpenAI-compatible providers send no cap and + # rely on the server default (verified — a ~2.3K-token answer returns + # ``finish_reason="stop"``, not truncated). The value's only live effect is + # the auto-compact output reservation (``token_warning`` -> ``autocompact``, + # clamped at 20_000); 16_384 reserves more output headroom than DeepSeek/ + # GLM's 8_192, which suits a model that spends part of its budget on + # reasoning tokens. + "muse-spark-1.1": ModelConfig( + model_id="muse-spark-1.1", + display_name="Muse Spark 1.1", + context_window=1_048_576, + max_output_tokens=16_384, + supports_cache=True, + ), } diff --git a/src/providers/openai_compatible_specs.py b/src/providers/openai_compatible_specs.py index c66b867b4..d96ad5b66 100644 --- a/src/providers/openai_compatible_specs.py +++ b/src/providers/openai_compatible_specs.py @@ -311,6 +311,19 @@ def resolved_class_name(self) -> str: env_vars=("DEEPINFRA_API_KEY", "DEEPINFRA_TOKEN"), aliases=("deep-infra", "deep_infra"), ), + # Meta's first-party API (api.meta.ai) — not a DeepSeek gateway; it serves + # its own ``muse-spark-1.1`` reasoning model over the OpenAI-compatible + # ``/v1/chat/completions`` endpoint (Bearer auth). ``muse``/``muse-spark`` + # aliases mirror how ``kimi`` aliases ``moonshot``. + ProviderSpec( + id="meta", + label="Meta", + default_base_url="https://api.meta.ai/v1", + default_model="muse-spark-1.1", + available_models=("muse-spark-1.1",), + env_vars=("META_API_KEY", "META_AI_API_KEY"), + aliases=("meta-ai", "meta_ai", "muse", "muse-spark"), + ), ) diff --git a/src/services/pricing.py b/src/services/pricing.py index 06deedeb9..90c1187a5 100644 --- a/src/services/pricing.py +++ b/src/services/pricing.py @@ -78,6 +78,24 @@ "cache_creation": 0.435 / 1_000_000, "cache_read": 0.003625 / 1_000_000, } +# Meta Muse Spark 1.1 (api.meta.ai, OpenAI-compatible). Meta's published rates: +# $1.25/M input, $4.25/M output, $0.15/M cached input. OpenAI-style caching has +# no separate cache-write charge, so ``cache_creation`` mirrors ``input``. +# NOTE: the generic OpenAI-compat usage builder does not (yet) map +# ``prompt_tokens_details.cached_tokens`` onto ``cache_read_input_tokens`` — +# only the hand-written DeepSeek provider does — so today ``cache_read`` is +# inert for Meta: cached input is billed at the full input rate in the cost +# display, an over-estimate on the cached portion ($1.25 vs $0.15/M, ~8x). +# The displayed cost is thus a safe upper bound, consistent with the other +# registry providers; wiring the mapping into ``_build_usage_dict`` is a +# separate change (it would affect all OpenAI-compat providers). The real +# cache-read rate is recorded here for when that lands. +_TIER_MUSE_SPARK = { + "input": 1.25 / 1_000_000, + "output": 4.25 / 1_000_000, + "cache_creation": 1.25 / 1_000_000, + "cache_read": 0.15 / 1_000_000, +} # Exact-match table — keyed by canonical model name. Order DOESN'T matter @@ -106,6 +124,8 @@ # every proxied model is priced at its upstream rate. "deepseek-v4-flash": _TIER_DEEPSEEK_FLASH, "deepseek-v4-pro": _TIER_DEEPSEEK_PRO, + # Meta Muse Spark (api.meta.ai) + "muse-spark-1.1": _TIER_MUSE_SPARK, } diff --git a/tests/test_provider_registry.py b/tests/test_provider_registry.py index 8b9b5e2ae..4dcf421f4 100644 --- a/tests/test_provider_registry.py +++ b/tests/test_provider_registry.py @@ -26,7 +26,7 @@ build_provider_class, ) -# The 18 OpenAI-compatible providers added via the registry. +# The 19 OpenAI-compatible providers added via the registry. EXPECTED_NEW_PROVIDERS = { "nvidia-nim", "atlascloud", @@ -46,6 +46,7 @@ "together", "stepfun", "deepinfra", + "meta", } # A sample of (id -> (base_url, default_model)) — each vendor's published @@ -58,6 +59,7 @@ "deepinfra": ("https://api.deepinfra.com/v1/openai", "deepseek-ai/DeepSeek-V4-Pro"), "stepfun": ("https://api.stepfun.ai/v1", "step-3.7-flash"), "siliconflow-cn": ("https://api.siliconflow.cn/v1", "deepseek-ai/DeepSeek-V4-Pro"), + "meta": ("https://api.meta.ai/v1", "muse-spark-1.1"), }