Summary
ucode configure --agents opencode registers only two Databricks providers in the managed opencode.json — databricks-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.
Summary
ucode configure --agents opencoderegisters only two Databricks providers in the managedopencode.json—databricks-anthropic(Claude) anddatabricks-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
piadapter 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
Observed:
Expected (matches
ucode configure --agents piagainst the same workspace):opencode modelsafter configure shows no entry under anydatabricks-openai/*selector, and OpenCode cannot drivedatabricks-gpt-5-5even though the AI Gateway path/ai-gateway/codex/v1/responsesis reachable for the same token.Root cause in the current code
Two missing wires:
(a) The discovery flag in
src/ucode/cli.py:153excludesopencode:When the user passes
--agents opencode,want_codexisFalse, sodiscover_codex_models(...)at line 168 is never called andstate["codex_models"]is never populated.(b)
src/ucode/databricks.py:957–961(build_opencode_base_urls) returns only two keys:No
openai/codexbase URL is ever produced.(c)
src/ucode/agents/opencode.py:67–123(render_overlay) builds providers only fromopencode_models["anthropic"]andopencode_models["gemini"].Interestingly,
opencode.py:148already listsdatabricks-openaias a stale-provider name to clean up:…suggesting an earlier draft of this adapter intended to write it. The producer side was never added.
The
piadapter atsrc/ucode/agents/pi.py:131–140shows 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
mainat6d35a8d.1.
src/ucode/cli.py— includeopencodeinwant_codexThen 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_models2.
src/ucode/databricks.py— add the codex base URLdef 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 thedatabricks-openaiproviderIn
_resolve_model_selector(line 51), extend the heuristic:In
render_overlay(line 83+), after thegemini_modelsblock: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_KEYSat 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 NoneTests
A regression test fits next to the existing OpenCode tests under
tests/:Verification we have done
ucode configure --agents opencode --workspaces <logfood>end-to-end against a workspace with all three families served — confirmed the resultingopencode.jsoncarries only two providers./ai-gateway/codex/v1/responsesaccepts the same bearer token that ucode already injects for the other two providers (directcurlreturns a function-call output).ucode configure --agents piagainst the same workspace writesdatabricks-openaiviaagents/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-5is the most heavily-served Databricks code model. Ifucode configure --agents opencodecannot point OpenCode at it, every user picking the OpenCode + Databricks path throughucodeis 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 inucodeis a prerequisite for retiring that fork.