diff --git a/coworker/providers/matrix.py b/coworker/providers/matrix.py index 052a19ff0..58b2405b2 100644 --- a/coworker/providers/matrix.py +++ b/coworker/providers/matrix.py @@ -16,7 +16,7 @@ showing a made-up denominator. Values entered 2026-07-28 from vendor docs; verify alongside the id refresh. -Resellers: Together + Fireworks + OpenRouter. TODO: add Groq entries here AND its +Resellers: Together + Fireworks + OpenRouter + DeepInfra. TODO: add Groq entries here AND its descriptor in ``registry.py`` once the current provider surface is tested — deliberately deferred to bound how much needs verifying at once. """ @@ -228,6 +228,32 @@ class ModelEntry: "openrouter:stealth/ox-alpha": ModelEntry( "Ox Alpha · via OpenRouter", _AGENTIC, 1_048_576 ), + # DeepInfra curated models (OpenAI-compatible inference). Latest generation: + # DeepSeek V4, GLM 5.3, Kimi K3. Pinned snapshots (Flash 0731, Pro 0813) and canonical slugs. + "deepinfra:deepseek-ai/DeepSeek-V4-Flash": ModelEntry( + "DeepSeek V4 Flash · via DeepInfra", _AGENTIC, 128_000 + ), + "deepinfra:deepseek-ai/DeepSeek-V4-Flash-0731": ModelEntry( + "DeepSeek V4 Flash 0731 · via DeepInfra", _AGENTIC, 128_000 + ), + "deepinfra:deepseek-ai/DeepSeek-V4-Pro": ModelEntry( + "DeepSeek V4 Pro · via DeepInfra", _AGENTIC, 128_000 + ), + "deepinfra:deepseek-ai/DeepSeek-V4-Pro-0813": ModelEntry( + "DeepSeek V4 Pro 0813 · via DeepInfra", _AGENTIC, 128_000 + ), + "deepinfra:zai-org/GLM-5.3-Flash": ModelEntry( + "GLM-5.3 Flash · via DeepInfra", _AGENTIC_VISION, 1_000_000 + ), + "deepinfra:zai-org/GLM-5.3": ModelEntry( + "GLM-5.3 · via DeepInfra", _AGENTIC, 1_000_000 + ), + "deepinfra:moonshotai/Kimi-K3": ModelEntry( + "Kimi K3 · via DeepInfra", _AGENTIC_VISION, 1_000_000 + ), + "deepinfra:moonshotai/Kimi-K2.7-Code": ModelEntry( + "Kimi K2.7 Code · via DeepInfra", _AGENTIC, 256_000 + ), # -- cloud accounts (models running in the user's own AWS/GCP) ---------------- # Bedrock ids carry a family segment (claude/ → native Anthropic path, other/ → # Converse) plus AWS's own `-v:` version suffix. Some regions require the diff --git a/coworker/providers/registry.py b/coworker/providers/registry.py index 397efee10..11308e026 100644 --- a/coworker/providers/registry.py +++ b/coworker/providers/registry.py @@ -668,6 +668,14 @@ def _responses_compat( recommended_model="z-ai/glm-5.2", env_key="OPENROUTER_API_KEY", ), + _compat( + "deepinfra", + "DeepInfra", + base_url="https://api.deepinfra.com/v1/openai", + recommended_model="deepseek-ai/DeepSeek-V4-Flash", + env_key="DEEPINFRA_API_KEY", + endpoint_help="Prefilled with DeepInfra's official OpenAI-compatible endpoint.", + ), ProviderDescriptor( name="ollama", title="Ollama (local models)", diff --git a/tests/test_provider_router.py b/tests/test_provider_router.py index b47bc3aaf..a2bf2d7ad 100644 --- a/tests/test_provider_router.py +++ b/tests/test_provider_router.py @@ -74,6 +74,22 @@ def __init__(self, **kwargs): assert captured["api_key"] == "ollama" # placeholder, Ollama ignores it +def test_build_deepinfra_client_uses_default_base_url(monkeypatch): + captured: dict = {} + + class FakeOpenAI: + def __init__(self, **kwargs): + captured.update(kwargs) + + monkeypatch.setattr("openai.OpenAI", FakeOpenAI) + client = build_provider_client( + "deepinfra", {"api_key": "di-test"}, secrets=None + ) + client._ensure_client() # type: ignore[attr-defined] + assert captured["base_url"] == "https://api.deepinfra.com/v1/openai" + assert captured["api_key"] == "di-test" + + # -- router routing ------------------------------------------------------------- class _Recorder(ProviderClient): def __init__(self, name: str): @@ -115,6 +131,12 @@ def test_router_routes_and_strips_prefix(monkeypatch): "llama3.3" ] # prefix stripped before delegating + turn = router.complete( + model="deepinfra:deepseek-ai/DeepSeek-V4-Flash", messages=[] + ) + assert turn.text == "deepinfra" + assert state["latest"]["deepinfra"].models == ["deepseek-ai/DeepSeek-V4-Flash"] + router.complete(model="gpt-5.5", messages=[]) # bare → default openai assert state["latest"]["openai"].models == ["gpt-5.5"] diff --git a/tests/test_provider_verify.py b/tests/test_provider_verify.py index a6e553afa..1a1dbc686 100644 --- a/tests/test_provider_verify.py +++ b/tests/test_provider_verify.py @@ -161,3 +161,13 @@ def test_verify_unexpected_status(monkeypatch): res = verify_provider_key("anthropic", api_key="sk-ant-x") assert res["ok"] is False assert "500" in res["error"] + + +def test_verify_deepinfra_default_endpoint(monkeypatch): + cap: dict = {} + _patch_get(monkeypatch, status=200, capture=cap) + + assert verify_provider_key("deepinfra", api_key="di-key") == {"ok": True} + assert cap["url"] == "https://api.deepinfra.com/v1/openai/models" + assert cap["headers"]["Authorization"] == "Bearer di-key" + diff --git a/tests/test_providers.py b/tests/test_providers.py index 99df73c3d..a1f6f4b2f 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -498,10 +498,15 @@ def test_matrix_answers_capabilities_for_reseller_ids(): "fireworks:accounts/fireworks/models/kimi-k2p6", "openrouter:z-ai/glm-5.2", "openrouter:meta-llama/llama-4-maverick", + "deepinfra:deepseek-ai/DeepSeek-V4-Flash", + "deepinfra:zai-org/GLM-5.3-Flash", ): caps = capabilities_for(mid) assert caps.tools and caps.parallel_tool_calls and caps.streaming + assert capabilities_for("deepinfra:zai-org/GLM-5.3-Flash").vision is True + assert capabilities_for("deepinfra:deepseek-ai/DeepSeek-V4-Flash").vision is False + def test_matrix_labels_and_custom_model_fallback(): from coworker.providers.matrix import MATRIX, model_labels @@ -509,10 +514,14 @@ def test_matrix_labels_and_custom_model_fallback(): labels = model_labels() assert labels["together:zai-org/GLM-5.2"] == "GLM-5.2 · via Together" assert labels["zai:glm-5.2"] == "GLM-5.2 · Z AI" + assert ( + labels["deepinfra:deepseek-ai/DeepSeek-V4-Flash"] + == "DeepSeek V4 Flash · via DeepInfra" + ) # Deliberately small: agent-capable current models only (owner call, 2026-07-04). - # 60→65 (2026-08-24): the stealth ox-alpha preview slug tipped it; reclaim slack by - # pruning retired entries before raising this again. - assert len(MATRIX) < 65 + # 60→65 (2026-08-24): the stealth ox-alpha preview slug tipped it; + # 65→75 (2026-09-04): added DeepInfra curated catalog (DeepSeek V4, GLM 5.3, Kimi K3). + assert len(MATRIX) < 75 assert all(e.caps.tools for e in MATRIX.values()) # A custom (unlisted) reseller model falls back to the conservative default — usable, # but at the user's own risk (no parallel tool calls assumed). @@ -526,7 +535,7 @@ def test_reseller_descriptors_and_matrix_stay_in_lockstep(): from coworker.providers.matrix import models_for_provider from coworker.providers.registry import get_descriptor - for name in ("together", "fireworks", "openrouter"): + for name in ("together", "fireworks", "openrouter", "deepinfra"): d = get_descriptor(name) assert d is not None and d.needs_key curated = models_for_provider(name) @@ -536,6 +545,16 @@ def test_reseller_descriptors_and_matrix_stay_in_lockstep(): assert base.default.startswith("https://") +def test_deepinfra_never_leaks_the_openai_key(monkeypatch): + import pytest + from coworker.providers.registry import build_provider_client + + monkeypatch.setenv("OPENAI_API_KEY", "sk-openai-real") + monkeypatch.delenv("DEEPINFRA_API_KEY", raising=False) + with pytest.raises(RuntimeError, match="DeepInfra"): + build_provider_client("deepinfra", {}, None) + + def test_foreign_sidecars_stripped_from_outbound_messages(): """Provider-private sidecars (`_gemini` thought signatures et al) must never reach the OpenAI wire — it and its compat servers reject unknown message fields.""" diff --git a/tests/test_token_usage.py b/tests/test_token_usage.py index 663bf7f11..117511788 100644 --- a/tests/test_token_usage.py +++ b/tests/test_token_usage.py @@ -359,4 +359,12 @@ def test_model_context_windows_covers_verified_entries_only(): windows = model_context_windows() assert windows["anthropic:claude-fable-5"] == 1_000_000 assert "together:thinkingmachines/Inkling" not in windows # unverified stays absent + assert windows["deepinfra:deepseek-ai/DeepSeek-V4-Flash"] == 128_000 + assert windows["deepinfra:deepseek-ai/DeepSeek-V4-Flash-0731"] == 128_000 + assert windows["deepinfra:deepseek-ai/DeepSeek-V4-Pro"] == 128_000 + assert windows["deepinfra:deepseek-ai/DeepSeek-V4-Pro-0813"] == 128_000 + assert windows["deepinfra:zai-org/GLM-5.3-Flash"] == 1_000_000 + assert windows["deepinfra:zai-org/GLM-5.3"] == 1_000_000 + assert windows["deepinfra:moonshotai/Kimi-K3"] == 1_000_000 + assert windows["deepinfra:moonshotai/Kimi-K2.7-Code"] == 256_000 assert all(isinstance(v, int) and v > 0 for v in windows.values())