Skip to content

ucode opencode adapter omits databricks-openai provider — opencode loses access to GPT-5 / Codex Responses models #97

@dgokeeffe

Description

@dgokeeffe

Summary

ucode configure --agents opencode registers only two Databricks providers in the managed opencode.jsondatabricks-anthropic (Claude) and databricks-google (Gemini). The third Databricks model family — GPT-5 / Codex via the Responses API (databricks-gpt-5-5, databricks-gpt-codex, etc.) — is silently dropped from OpenCode's model selector.

This is inconsistent with the pi adapter in the same repo, which already configures all three families against the same workspace.

We hit this while testing whether ucode + stock OpenCode could replace a Databricks-aware OpenCode fork we maintain. Result: it cannot, because the third family never gets wired up.

Reproduction

DATABRICKS_CONFIG_PROFILE=logfood \
  uv run ucode configure --agents opencode --workspaces https://<workspace>
cat ~/.config/ucode/opencode-xdg/opencode/opencode.json | jq '.provider | keys'

Observed:

["databricks-anthropic", "databricks-google"]

Expected (matches ucode configure --agents pi against the same workspace):

["databricks-anthropic", "databricks-google", "databricks-openai"]

opencode models after configure shows no entry under any databricks-openai/* selector, and OpenCode cannot drive databricks-gpt-5-5 even though the AI Gateway path /ai-gateway/codex/v1/responses is reachable for the same token.

Root cause in the current code

Two missing wires:

(a) The discovery flag in src/ucode/cli.py:153 excludes opencode:

want_codex = fetch_all or "codex" in tools or "copilot" in tools or "pi" in tools

When the user passes --agents opencode, want_codex is False, so discover_codex_models(...) at line 168 is never called and state["codex_models"] is never populated.

(b) src/ucode/databricks.py:957–961 (build_opencode_base_urls) returns only two keys:

def build_opencode_base_urls(workspace: str) -> dict[str, str]:
    return {
        "anthropic": build_tool_base_url("claude", workspace) + "/v1",
        "gemini": build_tool_base_url("gemini", workspace) + "/v1beta",
    }

No openai / codex base URL is ever produced.

(c) src/ucode/agents/opencode.py:67–123 (render_overlay) builds providers only from opencode_models["anthropic"] and opencode_models["gemini"].

Interestingly, opencode.py:148 already lists databricks-openai as a stale-provider name to clean up:

for stale in ("databricks-anthropic", "databricks-google", "databricks-openai"):
    providers.pop(stale, None)

…suggesting an earlier draft of this adapter intended to write it. The producer side was never added.

The pi adapter at src/ucode/agents/pi.py:131–140 shows the exact shape that should be written for OpenCode — same gateway path, same Responses dialect, same bearer-auth plumbing.

Proposed fix (full patch — drop in)

Three diffs, no new files. All against main at 6d35a8d.

1. src/ucode/cli.py — include opencode in want_codex

-    want_codex = fetch_all or "codex" in tools or "copilot" in tools or "pi" in tools
+    want_codex = (
+        fetch_all
+        or "codex" in tools
+        or "copilot" in tools
+        or "opencode" in tools
+        or "pi" in tools
+    )

Then add the codex models to the opencode-models map alongside anthropic/gemini (around cli.py:171–175):

     opencode_models: dict[str, list[str]] = {}
     if want_claude:
         opencode_models["anthropic"] = list(claude_models.values())
     if want_gemini:
         opencode_models["gemini"] = gemini_models
+    if want_codex:
+        opencode_models["openai"] = codex_models

2. src/ucode/databricks.py — add the codex base URL

 def build_opencode_base_urls(workspace: str) -> dict[str, str]:
     return {
         "anthropic": build_tool_base_url("claude", workspace) + "/v1",
         "gemini": build_tool_base_url("gemini", workspace) + "/v1beta",
+        # @ai-sdk/openai appends "/responses" (or "/chat/completions") to baseURL,
+        # so stop just before that — matches the Pi adapter at build_pi_base_urls.
+        "openai": build_tool_base_url("codex", workspace),
     }

3. src/ucode/agents/opencode.py — render the databricks-openai provider

In _resolve_model_selector (line 51), extend the heuristic:

 def _resolve_model_selector(model: str, opencode_models: dict[str, list[str]]) -> str:
-    if model.startswith("databricks-anthropic/") or model.startswith("databricks-google/"):
+    if (
+        model.startswith("databricks-anthropic/")
+        or model.startswith("databricks-google/")
+        or model.startswith("databricks-openai/")
+    ):
         return model

     anthropic_models = opencode_models.get("anthropic") or []
     if model in anthropic_models:
         return f"databricks-anthropic/{model}"

     gemini_models = opencode_models.get("gemini") or []
     if model in gemini_models:
         return f"databricks-google/{model}"

+    openai_models = opencode_models.get("openai") or []
+    if model in openai_models:
+        return f"databricks-openai/{model}"
+
     return model

In render_overlay (line 83+), after the gemini_models block:

     anthropic_models = opencode_models.get("anthropic") or []
     gemini_models = opencode_models.get("gemini") or []
+    openai_models = opencode_models.get("openai") or []

     providers: dict = {}
     keys: list[list[str]] = [["model"]]
     if anthropic_models:
         ...
     if gemini_models:
         ...
+    if openai_models:
+        providers["databricks-openai"] = {
+            "npm": "@ai-sdk/openai",
+            "options": {
+                "baseURL": opencode_base_urls["openai"],
+                "apiKey": token,
+                "headers": auth_headers,
+            },
+            # @ai-sdk/openai supports the Responses API; opencode reads the
+            # per-model flag from `models.<m>.options.useResponsesApi`.
+            # Databricks Codex / GPT-5 are Responses-only on /ai-gateway/codex/v1.
+            "models": {
+                m: {
+                    "headers": ua_header,
+                    "options": {"useResponsesApi": True},
+                }
+                for m in openai_models
+            },
+        }
+        keys.append(["provider", "databricks-openai"])

Update PROVIDER_KEYS at the top of the file (line 41):

 PROVIDER_KEYS: list[list[str]] = [
     ["provider", "databricks-anthropic"],
     ["provider", "databricks-google"],
+    ["provider", "databricks-openai"],
 ]

default_model(state) (line 192) should also prefer codex models when no claude model is configured, to preserve a sensible fallback ordering:

 def default_model(state: dict) -> str | None:
     opencode_models = state.get("opencode_models") or {}
     anthropic = opencode_models.get("anthropic") or []
     if anthropic:
         return anthropic[0]
+    openai = opencode_models.get("openai") or []
+    if openai:
+        return openai[0]
     gemini = opencode_models.get("gemini") or []
     return gemini[0] if gemini else None

Tests

A regression test fits next to the existing OpenCode tests under tests/:

def test_opencode_overlay_includes_databricks_openai():
    from ucode.agents.opencode import render_overlay
    overlay, keys = render_overlay(
        model="databricks-gpt-5-5",
        token="t",
        opencode_base_urls={
            "anthropic": "https://w/ai-gateway/anthropic/v1",
            "gemini": "https://w/ai-gateway/gemini/v1beta",
            "openai":  "https://w/ai-gateway/codex/v1",
        },
        opencode_models={"openai": ["databricks-gpt-5-5"]},
    )
    assert "databricks-openai" in overlay["provider"]
    p = overlay["provider"]["databricks-openai"]
    assert p["npm"] == "@ai-sdk/openai"
    assert p["options"]["baseURL"].endswith("/ai-gateway/codex/v1")
    assert p["models"]["databricks-gpt-5-5"]["options"]["useResponsesApi"] is True
    assert overlay["model"] == "databricks-openai/databricks-gpt-5-5"

Verification we have done

  • Ran ucode configure --agents opencode --workspaces <logfood> end-to-end against a workspace with all three families served — confirmed the resulting opencode.json carries only two providers.
  • Confirmed the AI Gateway Codex endpoint /ai-gateway/codex/v1/responses accepts the same bearer token that ucode already injects for the other two providers (direct curl returns a function-call output).
  • Confirmed ucode configure --agents pi against the same workspace writes databricks-openai via agents/pi.py — so this is a missed-wiring on the OpenCode adapter, not a missing capability.

Why this matters for "is ucode a substitute for a Databricks fork of OpenCode?"

databricks-gpt-5-5 is the most heavily-served Databricks code model. If ucode configure --agents opencode cannot point OpenCode at it, every user picking the OpenCode + Databricks path through ucode is restricted to Claude and Gemini, regardless of what their workspace serves. A Databricks-aware OpenCode fork supports all three families today via its own provider; closing this gap in ucode is a prerequisite for retiring that fork.

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already exists

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions