Skip to content

feat(opencode): Amazon Bedrock Model Provider Service support - #481

Open
bjanssen11 wants to merge 9 commits into
databricks:mainfrom
bjanssen11:feat/opencode-bedrock
Open

feat(opencode): Amazon Bedrock Model Provider Service support#481
bjanssen11 wants to merge 9 commits into
databricks:mainfrom
bjanssen11:feat/opencode-bedrock

Conversation

@bjanssen11

@bjanssen11 bjanssen11 commented Sep 3, 2026

Copy link
Copy Markdown

Closes #482

What this adds

Amazon Bedrock as a model provider for OpenCode, routed through the Databricks AI Gateway. It mirrors the Pi support in #478.

OpenCode uses the @ai-sdk/amazon-bedrock provider with a bearer apiKey (no AWS SigV4, no region) pointed at {workspace}/ai-gateway. The SDK turns that into /ai-gateway/model/{id}/converse-stream, which is the only dialect the gateway serves for the amazon_bedrock provider type. The Databricks-Model-Provider-Service header rides per-model, because OpenCode clobbers provider-level headers (the existing OpenCode providers already put the User-Agent per-model for the same reason).

I verified the whole path live: OpenCode installs @ai-sdk/amazon-bedrock on first use and a real opencode run returned Bedrock output.

The refresh fix

Like Pi, OpenCode bakes the token into its config and refreshes it at launch and every 30 minutes. _refresh_token_once now reads the existing config, and when it finds a databricks-bedrock block it re-applies that block with a fresh token (recovering the MPS name and target ids from the per-model headers) instead of dropping it. Without this the refresh would wipe the provider and the session would fall back to a system-hosted model.

How to use it

ucode opencode --provider <your-bedrock-mps>

If the MPS declares target models, they populate OpenCode's model list. If it is allow_all_targets with none declared, ucode prompts for a Bedrock model ID, for example us.amazon.nova-lite-v1:0 or us.anthropic.claude-sonnet-4-20250514-v1:0.

Install and try it locally

git fetch origin feat/opencode-bedrock
git checkout feat/opencode-bedrock
uv tool install --reinstall .
ucode opencode --provider <your-bedrock-mps>

Tests:

uv run pytest tests/test_agent_opencode.py

Scope and dependency

This stacks on #478 (Pi Bedrock), which introduces the shared amazon_bedrock provider-type support and the launch-time target resolution that OpenCode reuses. Fork PRs can't base on a fork branch, so this PR targets main. Until #478 merges, the diff here also shows #478's changes; once #478 lands, this collapses to just the OpenCode commit.

Codex remains unsupported for Bedrock (Converse-only gateway, see #476).

🤖 Generated with Claude Code

https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn

bjanssen11 and others added 9 commits September 3, 2026 12:20
Codex speaks the OpenAI-compatible API, which Bedrock also exposes.
`_TOOL_PROVIDER_TYPES` previously restricted codex to `openai` only, so
`ucode codex --provider <bedrock-mps>` always failed with "which codex
can't route to (supported: openai)."

Three changes in databricks.py:
- Add `amazon_bedrock` to codex's allowed provider types in
  `_TOOL_PROVIDER_TYPES`.
- Gate the "exposes no Claude models" check in `resolve_provider_service`
  on `tool == "claude"` so a Bedrock MPS with OpenAI-compatible (non-Claude)
  targets isn't rejected when codex selects it.
- Apply the same `tool == "claude"` guard in `service_usable_for_tool` so
  Bedrock services without Claude targets appear in the list when codex is
  the active tool.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Adds two new subcommands under `ucode providers` to inspect Model
Provider Services on the workspace:

- `ucode providers list [--tool TOOL]` — lists all MPS services with
  name, provider type, and declared targets. `--tool claude|codex`
  filters to services the given tool can actually route through.
- `ucode providers show <catalog.schema.service>` — shows full detail
  for one service: provider type, relay flag, allow_all_targets, and
  the complete targets list.

Motivation: after `ucode codex --provider eng_dev.ai_gateway.amazonbedrock`
launched without showing expected Bedrock models, there was no CLI to
inspect what targets an MPS exposes.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Wire `ucode pi --provider <bedrock-mps>` end-to-end:

- `build_pi_base_urls`: add "bedrock" key pointing at `{workspace}/ai-gateway`
  (NOT `/ai-gateway/amazonbedrock` — that path maps to the Bedrock control
  plane; the standard path routes to the runtime via the MPS header)
- `pi.render_overlay`: add `databricks-bedrock` provider block when
  `bedrock_targets` is supplied; defaults the session to the first target
- `pi.write_tool_config`: accept `provider` and `bedrock_targets` kwargs
- `agents.__init__.configure_tool`: pass `bedrock_targets` to Pi; allow
  Pi to launch without a model when a Bedrock provider + targets cover it
- `cli.py`: fetch MPS targets for Pi in the provider launch path; handle
  `allow_all_targets` with a text prompt; thread `bedrock_targets` through
  to `configure_tool`

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Without it, --provider fell into ctx.args and was forwarded to Pi itself
rather than being parsed by ucode, so the Bedrock target-fetching branch
never ran.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Without this entry, ucode pi --provider rejects any Bedrock MPS with
"pi can't route to (supported: none)" before ever fetching targets.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
…nfig

`_resolve_model_selector` returns Bedrock model IDs (e.g.
`anthropic.claude-3-haiku-20240307-v1:0`) unprefixed because they
contain no `/`. The old `if not resolved` guard never fired since the
ID is truthy. `_write_settings` then gets an empty model half from
`partition("/")` and exits early — defaultProvider stays on
databricks-claude instead of databricks-bedrock.

Fix: unconditionally set `resolved = f"databricks-bedrock/{targets[0]}"`
when the Bedrock provider block is present.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
…e UA

The launch-time and 30-minute token refresh re-rendered Pi's models.json
without the Bedrock provider, dropping the databricks-bedrock block and
falling back to a system-hosted model. _refresh_token_once now reads the
existing config and preserves a databricks-bedrock block, re-applying it
with a freshly refreshed token.

Also stop sending ucode's User-Agent on the Bedrock block: Pi's
bedrock-converse-stream client sets its own, and two values made the
gateway reject the request ("Header field 'user-agent' must only have a
single value").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
…path

Reflect that Pi now supports anthropic/amazon_bedrock provider services, and
clean up imports left unused once the codex Bedrock launch branch is excluded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Route OpenCode to an Amazon Bedrock MPS through the Databricks AI Gateway,
mirroring the Pi support. OpenCode uses the @ai-sdk/amazon-bedrock provider
with a bearer apiKey (no SigV4, no region) against {workspace}/ai-gateway,
which the SDK turns into /model/{id}/converse-stream. The
Databricks-Model-Provider-Service header rides per-model, since OpenCode
clobbers provider-level headers.

_refresh_token_once now preserves an existing databricks-bedrock block across
the launch-time and 30-minute token refresh (reading the saved MPS name and
target ids back out of the per-model headers), so the session keeps routing to
Bedrock instead of dropping to a system-hosted model.

Verified end to end against the live gateway: the generated config survives a
refresh and a real `opencode run` returns Bedrock output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017G9kjrbhqvWucH26GwykSn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Amazon Bedrock as a provider for OpenCode (via Converse through the AI Gateway)

1 participant