From 7313753f3c3ffc96a40f52b82c5ec76439889338 Mon Sep 17 00:00:00 2001 From: Lars Moan Date: Mon, 31 Aug 2026 08:58:12 +0000 Subject: [PATCH] fix(opencode): make the OSS provider usable at all Every model in the `databricks-oss` bucket failed on its first request. Two separate fields in the request body reached the gateway's strict validator and each one earned an HTTP 400. --- src/ucode/agents/opencode.py | 6 ++++++ src/ucode/databricks.py | 11 +++++++++-- tests/test_agent_opencode.py | 15 +++++++++++++++ tests/test_databricks.py | 14 ++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/ucode/agents/opencode.py b/src/ucode/agents/opencode.py index 19adff71..16e3bf84 100644 --- a/src/ucode/agents/opencode.py +++ b/src/ucode/agents/opencode.py @@ -143,6 +143,12 @@ def render_overlay( "baseURL": opencode_base_urls["oss"], "apiKey": token, "headers": auth_headers, + # OpenCode stamps `prompt_cache_key: ` on every + # `@ai-sdk/openai` request; the Databricks gateway's strict + # validator rejects the unknown field with HTTP 400. OpenCode + # reads this opt-out from the provider `options` only — a + # per-model `options` entry does not suppress the field. + "setCacheKey": False, }, "models": {m: _oss_model_overlay(m, ua_header) for m in oss_models}, } diff --git a/src/ucode/databricks.py b/src/ucode/databricks.py index 3350e99c..40742cc5 100644 --- a/src/ucode/databricks.py +++ b/src/ucode/databricks.py @@ -1495,9 +1495,16 @@ def classify_model_family(model_id: str) -> str | None: # context and output together. Keyed by family substring; add an entry to bound # a new model. _MODEL_TOKEN_LIMITS: dict[str, dict[str, int]] = { - # GLM-4.6: 200k context, but the gateway caps output well below the model's - # native 128k — pin 25k so requests aren't rejected. + # Every `output` here is the gateway's cap, which sits below the model's + # native output length. Neither the model-services listing nor the + # serving-endpoints API reports the cap, so it cannot be discovered. "glm": {"context": 200_000, "output": 25_000}, + "qwen": {"context": 262_144, "output": 25_000}, + "gpt-oss": {"context": 131_072, "output": 25_000}, + # Keyed on the full name, not `llama`: the Llama 3 endpoints have a 128k + # context, so a bare `llama` key would pin 1M on them. + "llama-4-maverick": {"context": 1_000_000, "output": 8_192}, + "gemma": {"context": 131_072, "output": 8_192}, } diff --git a/tests/test_agent_opencode.py b/tests/test_agent_opencode.py index c83e8458..f0e55a25 100644 --- a/tests/test_agent_opencode.py +++ b/tests/test_agent_opencode.py @@ -116,6 +116,21 @@ def test_non_glm_oss_model_has_no_output_cap(self): kimi = overlay["provider"]["databricks-oss"]["models"]["system.ai.kimi-k2-7-code"] assert "limit" not in kimi + def test_qwen_gets_token_limits(self): + model = "system.ai.qwen35-122b-a10b" + overlay, _ = opencode.render_overlay(model, "tok", _base_urls(), {"oss": [model]}) + qwen = overlay["provider"]["databricks-oss"]["models"][model] + assert qwen["limit"] == {"context": 262144, "output": 25000} + + def test_oss_provider_opts_out_of_the_prompt_cache_key(self): + # OpenCode stamps `prompt_cache_key` on every `@ai-sdk/openai` request and + # the gateway rejects the unknown field. It reads the opt-out from the + # provider options only, so a per-model entry would not suppress it. + model = "system.ai.qwen35-122b-a10b" + overlay, _ = opencode.render_overlay(model, "tok", _base_urls(), {"oss": [model]}) + assert overlay["provider"]["databricks-oss"]["options"]["setCacheKey"] is False + assert "setCacheKey" not in overlay["provider"]["databricks-oss"]["models"][model] + def test_token_in_api_key(self): models = {"anthropic": ["claude-sonnet"]} overlay, _ = opencode.render_overlay("claude-sonnet", "mytoken", _base_urls(), models) diff --git a/tests/test_databricks.py b/tests/test_databricks.py index 392e59da..4fbcedcd 100644 --- a/tests/test_databricks.py +++ b/tests/test_databricks.py @@ -244,6 +244,20 @@ def test_glm_matches_any_version(self): def test_uncapped_model_returns_none(self): assert db_mod.model_token_limits("system.ai.kimi-k2-7-code") is None + def test_gateway_caps_measured_against_the_workspace(self): + # Each `output` is the cap the gateway enforces; a request above it fails + # with HTTP 400. + assert db_mod.model_token_limits("system.ai.qwen35-122b-a10b")["output"] == 25_000 + assert db_mod.model_token_limits("system.ai.gpt-oss-120b")["output"] == 25_000 + assert db_mod.model_token_limits("system.ai.gpt-oss-20b")["output"] == 25_000 + assert db_mod.model_token_limits("system.ai.llama-4-maverick")["output"] == 8_192 + assert db_mod.model_token_limits("system.ai.gemma-3-12b")["output"] == 8_192 + + def test_llama_3_endpoints_do_not_take_the_maverick_context(self): + # A bare `llama` key would pin Maverick's 1M context on the 128k Llama 3 + # endpoints. + assert db_mod.model_token_limits("system.ai.meta-llama-3-3-70b-instruct") is None + class TestDiscoverModelServices: def test_buckets_families_by_name(self, monkeypatch):