feat(opencode): Add Databricks provider support#7984
feat(opencode): Add Databricks provider support#7984dgokeeffe wants to merge 31 commits intoanomalyco:devfrom
Conversation
|
The following comment was made by an LLM, it may be inaccurate: No duplicate PRs found |
0c9f889 to
7ae4802
Compare
|
@dgokeeffe I am really looking forward to this PR landing, especially after seeing your post on LinkedIn regarding running opencode on a cluster via I'm interested in running this locally, although without a Databricks PAT if possible. Can you provide a comment regarding auth via |
6d7ed29 to
3843161
Compare
|
@mdlam92 - I pulled the trigger a bit too quick on this PR. As I tested it more and more, I had to make some changes. Databricks should now appear as a provider on the Connect screen. There were a few changes I had to make that are in this PR:
Authentication options (in order of precedence):
Quick start Option 1: Using Databricks CLI (easiest) Option 2: Using environment variables Once auth is configured, Databricks should auto-load with default models (Claude, GPT-5, Gemini). You shouldn't need to manually edit opencode.json unless you want to add custom model endpoints. Let me know if you run into any issues, and anything I need to do to get this merged in. |
|
Just tested this PR and can confirm Databricks can be connected as a model provider. But gpt 5.2 endpoint needs to change to use the /responses endpoint.
|
00637c0 to
71e0ba2
Compare
f1ae801 to
08fa7f7
Compare
Add Databricks Foundation Model APIs as a new provider supporting OpenAI-compatible endpoints via /serving-endpoints. Authentication methods (in priority order): - Personal Access Token (DATABRICKS_TOKEN or stored auth) - OAuth M2M (DATABRICKS_CLIENT_ID + DATABRICKS_CLIENT_SECRET) - Azure AD Service Principal (ARM_CLIENT_ID/SECRET/TENANT_ID) - Azure CLI (for Azure Databricks workspaces) Includes default model definitions for Claude, Llama, GPT-5, and Gemini models available through Databricks pay-per-token endpoints. Fixes model transformation to use Provider.Model format with proper capabilities and API configuration.
Add optional host field to API authentication schema to support providers like Databricks that require both workspace URL and API key. Update ProviderAuth.api to accept optional host parameter and modify Databricks provider loader to prioritize stored auth host over config or environment variables for workspace URL resolution.
Implement DatabricksApiMethod component that prompts users for both workspace URL and Personal Access Token in sequence, eliminating the need for environment variables or custom configuration files. Enhance 'opencode auth login' to prompt for Databricks workspace URL before API key, storing both values for complete authentication.
Update generated TypeScript SDK types to reflect optional host field in API authentication schema.
- Make description prop a function in DatabricksApiMethod - Add escape key support to cancel dialog - Prevent default behavior on return key press
Apply message caching to Databricks models that support it (GPT and Gemini models with cache_read cost > 0). This improves performance and reduces costs for repeated requests.
…vider Add comprehensive model support including: - GPT-5 variants: GPT-5.2, GPT-5.1, GPT-5 (base), Codex variants, mini/nano - GPT-OSS models: 120B and 20B - Gemini models: Gemini 3 Pro/Flash, Gemini 2.5 Pro/Flash - Gemma 3 12B model All models include proper capabilities, cost, and limit configurations for pay-per-token Databricks endpoints.
Add comprehensive documentation for Databricks Foundation Model APIs provider including: - Setup instructions via /connect command - Authentication methods (PAT, OAuth M2M, Azure AD, Azure CLI) - Configuration options - Workspace URL formats for different cloud providers
- Extract Azure Databricks resource ID to constant - Improve error handling: use debug logging for OAuth failures instead of error - Add clarifying comments for OAuth M2M credential handling - Use constant instead of hardcoded resource ID value
Add proper auth.json backup and restoration in test to prevent test pollution and ensure tests don't affect each other.
Refactor dialog provider to split host and API key prompts Add support for reading Databricks CLI token cache for auth Implement token refresh logic for expired tokens Use DATABRICKS_HOST env var as default placeholder
This reverts commit a351e51.
Skip authentication prompts when valid CLI token cache exists Automatically bootstrap provider if CLI credentials are available
Read host from ~/.databrickscfg profiles via DATABRICKS_CONFIG_PROFILE Add getFreshToken function to refresh expired tokens from CLI cache Use custom fetch that refreshes tokens before each API request Support profile configuration in opencode.json config
Filter empty content messages for Databricks provider Ensure type: object on tool parameter schemas for Databricks Match Anthropic behavior for message normalization
Add quick start guide for Databricks CLI authentication Document CLI profile support and token refresh behavior Update authentication method priority order
Add @types/dompurify and @types/strip-ansi to fix typecheck errors after rebasing to dev branch. Co-Authored-By: Claude <noreply@anthropic.com>
Databricks and other OpenAI-compatible APIs reject messages with empty text content blocks. This fix ensures tool results always have non-empty text by using "[No output]" as a placeholder when the output is empty. Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for all Databricks model families: - GPT-5 models (5.2, 5.1, Codex, mini, nano, OSS) - Gemini models (3 Pro/Flash, 2.5 Pro/Flash, Gemma 3) - Claude models (Sonnet 4/4.5, Haiku 4.5, Opus 4.1/4.5, 3.7) - Llama models (4 Maverick, 3.3, 3.1) - Qwen models (Qwen3 Next) Tests verify capabilities, costs, API configuration, and model families are correctly set for each model type. Co-Authored-By: Claude <noreply@anthropic.com>
OpenCode requires tool calling for its agentic workflows, so models without reliable tool support are now excluded from the default list: - GPT OSS models (120B, 20B) - Gemma 3 12B - All Llama models (4 Maverick, 3.3 70B, 3.1 405B/8B) - Qwen3 Next 80B Tool-calling models still included: - GPT-5 family (5.2, 5.1, Codex variants, mini, nano) - Gemini family (3 Pro/Flash, 2.5 Pro/Flash) - Claude family (Sonnet 4/4.5, Haiku 4.5, Opus 4.1/4.5, 3.7) Updated tests to verify only tool-capable models are loaded. Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive tests for empty content filtering and tool calling across all Databricks model types: - Databricks Claude (Anthropic model via OpenAI-compatible API) - Databricks GPT-5 (OpenAI model via OpenAI-compatible API) - Databricks Gemini (Google model via OpenAI-compatible API) Tests verify: - Empty text parts are filtered while tool calls are preserved - Multiple tool calls in single message work correctly - Multi-turn conversations with tools maintain correct structure - Reasoning parts with empty text are properly filtered Co-Authored-By: Claude <noreply@anthropic.com>
The Databricks API rejects messages with empty string content, but the AI SDK sends content: "" for assistant messages that only contain tool calls. This fix implements a custom fetch wrapper that intercepts the request payload and converts content: "" to content: null for assistant messages with tool_calls. Also includes defensive improvements in message-v2.ts to skip empty text/reasoning parts and ensure non-empty tool output text. Co-Authored-By: Claude <noreply@anthropic.com>
- Add documentation link for Azure Databricks resource ID constant - Update model comments to be more specific about limitations: - GPT-5.1 Codex Mini: Only supports Responses API - GPT OSS models: Don't support full JSON Schema (maxLength) - Fix test isolation for auth/token-cache interference - Update test to exclude GPT-5.1 Codex Mini (not compatible) Co-Authored-By: Claude <noreply@anthropic.com>
…kens The getFreshToken function now attempts to refresh expired tokens using the refresh_token, matching the behavior of the initialization code. Previously, if a token expired during a session, the function would just log a warning and return the stale token, causing API calls to fail. Changes: - Added refresh_token and expires_in fields to cache type definition - Added token refresh logic when cached token is expired but has refresh_token - Updates token cache file after successful refresh - Falls back gracefully with warning if refresh fails Also adds tests for: - Successful token refresh with cache update verification - Graceful fallback when token refresh fails Co-Authored-By: Claude <noreply@anthropic.com>
21aeb63 to
211b238
Compare
|
I'm super stoked to see this! I searched for such a provider a few weeks ago but it had to have been just before you created the issue. Last week I took a go at adding support, but I did so as a plugin where you decided to integrate into the CLI. Official in-built support will be great but I had no idea what I was getting myself into so I wanted to start a bit more out of band. I got a working version and was just publishing it internally to my company today for folks to try out when someone informed me of your PR! I've a couple of thoughts/questions as I read and compare where I landed...
I ran into some other issues (errors about |
|
I got a chance to test last night. I'm excited to see this work happening but I had some pretty major troubles.... I'm going to detail my issues below. I'll post a second comment with a summary of all the translations that I had to make. Sorry/not-sorry about the amount of text that is about to appear. I'm happy to participate however you find it most helpful. I'm unsure at the moment about making my provider plugin public but I can pursue that internally if it would help. 1.
|
|
Regarding the rest of the transformations I had to make, I had Opus create the following summary of all of the specializations that I added. I honestly don't know if all of them are strictly required, but with these I found that all of my models worked without issue. 1. Gemini-Specific TransformationsTool Schema Sanitization (
|
| Model Family | stream_options |
|---|---|
| Llama | Omitted |
| Qwen | Omitted |
| Gemma | Omitted |
| GPT-OSS | Omitted |
| Claude | Included |
| Gemini | Included |
| Other | Included |
3. Message Format Transformations
AI SDK → OpenAI-compatible format (convert-to-openai-messages.ts)
| Message Type | Transformation |
|---|---|
| System | Passed through directly |
| User | Text and file parts converted; images formatted to data URLs |
| Assistant | Text extracted, tool calls converted, empty content → null |
| Tool | Result output converted to string (handles text, json, error-text, error-json types) |
Empty Content Normalization
When assistant has tool calls but no text, content becomes null (not empty string) - this is a Databricks requirement.
content: textContent || (toolCalls.length > 0 ? null : '')4. Image Formatting (utils/format-image.ts)
Normalizes image data to proper URLs:
| Input Type | Output |
|---|---|
| URL objects | String URL |
| Binary data (Uint8Array/ArrayBuffer) | Base64 data URL (chunked encoding to avoid stack overflow) |
| String data URLs | Passed through |
| HTTP/HTTPS URLs | Passed through |
| Raw base64 string | Prefixed with data:{mediaType};base64, |
5. Finish Reason Mapping (map-openai-finish-reason.ts)
Maps OpenAI finish reasons to AI SDK format:
| OpenAI | AI SDK |
|---|---|
stop |
stop |
length |
length |
content_filter |
content-filter |
tool_calls |
tool-calls |
function_call |
tool-calls |
| (other) | unknown |
6. Response Content Handling
Handles both response formats (databricks-chat-model.ts:291-309, 433-458):
- String content - Directly used
- Array of content parts - Text parts extracted and concatenated (for multi-modal responses)
Key Design Decisions
parallel_tool_callsomitted - Databricks doesn't support this parameter- No
cache_control- Stripped from text parts (Anthropic-specific) - Model-agnostic tool format - Converts to OpenAI function calling format universally
- Add Databricks to /connect provider list (was missing from models.dev) - Strip $schema and resolve $ref for Gemini tool schemas (Gemini rejects $schema) - Transform Gemini streaming responses (content array to string) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
elementalvoid
left a comment
There was a problem hiding this comment.
I saw your update and thought I'd give it another go. Here's what I found...
Side note: I said in a prior comment that not all of the hard coded models are available/are missing. I see now this is by design due to no or poor tool_use. 👍
| // For Databricks Gemini models, strip $schema and resolve $ref references | ||
| // Gemini API rejects tool schemas containing $schema field | ||
| if (model.id.includes("gemini")) { | ||
| const sanitizeForGemini = (obj: any, defs?: Record<string, any>): any => { |
There was a problem hiding this comment.
Looks like this works for (most?) built-in tools, but not MCP tools.
With context7 enabled:
❯ grep -A5 '"mcp":' .opencode/opencode.jsonc
"mcp": {
"context7": {
"enabled": true,
"type": "remote",
"url": "https://mcp.context7.com/mcp",
},
❯ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode run --model databricks/databricks-gemini-3-flash 'hello'
Error: Bad Request: {
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"$schema\" at 'tools[0].function_declarations[31].parameters': Cannot find field.\nInvalid JSON payload received. Unknown name \"$schema\" at 'tools[0].function_declarations[32].parameters': Cannot find field.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "tools[0].function_declarations[31].parameters",
"description": "Invalid JSON payload received. Unknown name \"$schema\" at 'tools[0].function_declarations[31].parameters': Cannot find field."
},
{
"field": "tools[0].function_declarations[32].parameters",
"description": "Invalid JSON payload received. Unknown name \"$schema\" at 'tools[0].function_declarations[32].parameters': Cannot find field."
}
]
}
]
}
}
If we disable context7 we get a fun new error on the builtin question tool:
❯ grep -A5 '"mcp":' .opencode/opencode.jsonc
"mcp": {
"context7": {
"enabled": false,
"type": "remote",
"url": "https://mcp.context7.com/mcp",
},
❯ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode run --model databricks/databricks-gemini-3-flash 'hello'
Error: Bad Request: {
"error": {
"code": 400,
"message": "Schema.ref 'QuestionOption' was set alongside unsupported fields. If a schema node has Schema.ref set, then only description and default can be set alongside it; other fields they would be replaced by the expanded reference.",
"status": "INVALID_ARGUMENT"
}
}
There was a problem hiding this comment.
I noticed a PR today that fixes just the Gemini issues... #12292
Might wait for / integrate that? ¯\_(ツ)_/¯
| id: "databricks", | ||
| name: "Databricks", | ||
| env: ["DATABRICKS_TOKEN"], | ||
| models: {}, |
There was a problem hiding this comment.
I'm not sure the best way to populate this, but I think models here being empty causes this error when no auth is configured (not in auth.json and not via env var):
❯ env | grep -c DATABRICKS
0
❯ grep -ic databricks ~/.local/share/opencode/auth.json
0
❯ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode
{
"name": "UnknownError",
"data": {
"message": "TypeError: undefined is not an object (evaluating 'Provider2.sort(Object.values(item.models))[0].id')\n at <anonymous> (src/server/routes/provider.ts:68:93)\n at o3 (../../node_modules/.bun/remeda@2.26.0/node_modules/remeda/dist/chunk-3ZJAREUD.js:1:137)\n at <anonymous> (src/server/routes/provider.ts:68:20)\n at processTicksAndRejections (native:7:39)"
}
}
A similar error condition might exist in packages/opencode/src/provider/provider.ts where the databricks provider is added to the models.dev "database"? It doesn't appear that that location uses the models array in any way, but I'm not positive on way or another.
There was a problem hiding this comment.
What this means in a usability sense is that you cannot use /connect inside of the TUI to configure the provider. You can use env vars or you can use opencode auth login though. Once env vars are set, models are accessible.
When Databricks auth is not configured, the provider has models: {}.
Provider.sort(Object.values({}))[0].id crashed with TypeError because
the sorted array is empty. Changed mapValues to Object.fromEntries
with a filter to skip providers with no models from the default map.
This also fixes /connect not showing the Databricks provider.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sanitizeForGemini function could infinite loop when a schema
contained circular $ref references (e.g., TreeNode referencing itself).
Added a Set to track refs currently being resolved. When a circular
ref is detected, returns {type: "object"} as a safe fallback,
matching the approach in PR anomalyco#12292.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Empty models crash: Provider.sort with empty array, route default map - Gemini stream transform: content array to string, multiple text parts, thoughtSignature filtering, empty array, string passthrough - Gemini schema: MCP $schema deeply nested, circular $ref, legacy definitions format, description preservation alongside $ref, non-Databricks providers keep $ref, $ref in array items Total: 163 tests (was 150), 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>


Summary
Adds Databricks Foundation Model APIs as a new provider, enabling opencode users to connect to their Databricks workspace's pay-per-token LLM endpoints.
Fixes #7983
Changes
provider.ts): Full Databricks provider with OpenAI-compatible endpoint support at/serving-endpointsauth.ts): Added Databricks to auth login flow with clear authentication instructionspreload.ts): Clear Databricks env vars between testsdatabricks.test.ts): 12 tests covering config parsing, auth precedence, URL handling, and model capabilitiesAuthentication Methods
Supports three auth methods (in priority order):
DATABRICKS_TOKENoropencode auth loginDATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRETARM_CLIENT_ID+ARM_CLIENT_SECRET+ARM_TENANT_IDDefault Models
Includes default definitions for common Foundation Model API endpoints (Claude, Llama, GPT-5, Gemini). Users can add custom model endpoints via
opencode.json.Verification
bun test packages/opencode/test/provider/databricks.test.ts