Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ From [obra/superpowers](https://github.com/obra/superpowers):
- **DeepWiki** - AI-powered documentation for any GitHub repository
- **Exa** - Web search and code context retrieval

## AI CLI Tools

Three AI coding CLIs are pre-configured with Databricks Model Serving:

| CLI | Command | Protocol | Models |
|-----|---------|----------|--------|
| **Claude Code** | `claude` | Anthropic-native | Claude Sonnet 4.5 |
| **OpenCode** | `opencode` | OpenAI-compatible | Claude, Gemini, Llama |
| **Gemini CLI** | `gemini` | Google Gemini-native | Gemini 2.5 Flash/Pro |

Switch models in OpenCode:
```bash
opencode -m databricks/databricks-gemini-2-5-flash
opencode -m databricks/databricks-claude-sonnet-4-5
```

Switch models in Gemini CLI:
```bash
gemini -m gemini-2.5-flash
gemini -m gemini-2.5-pro
```

## Databricks CLI

The Databricks CLI is pre-configured with your credentials. Test it:
Expand Down
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Just use it all on Databricks, from the browser. Wired up to model serving endpo

✅ **Claude Code CLI** - Pre-configured to use Databricks hosted models as the API endpoint

✅ **OpenCode CLI** - Pre-configured with Databricks as an OpenAI-compatible provider (Gemini, Claude, Llama models)

✅ **Gemini CLI** - Pre-configured with Databricks-hosted Gemini models via Google-native endpoint

✅ **Configurable Model** - Switch between Claude models via `app.yaml` (default: `databricks-claude-sonnet-4-5`)

✅ **Micro Editor** - Ships with [micro](https://micro-editor.github.io/), a modern terminal-based text editor
Expand Down Expand Up @@ -156,7 +160,10 @@ claude-code-cli-bricks/
├── CLAUDE.md # Claude Code welcome message
├── requirements.txt # Python dependencies
├── setup_claude.py # Claude Code CLI + MCP configuration
├── setup_opencode.py # OpenCode CLI + Databricks provider config
├── setup_gemini.py # Gemini CLI + Databricks endpoint config
├── setup_databricks.py # Databricks CLI configuration
├── test_integrations.py # Integration tests for all CLI tools
├── sync_to_workspace.py # Git hook for Databricks sync
├── static/
│ ├── index.html # Terminal UI
Expand Down Expand Up @@ -253,11 +260,60 @@ When deployed, git commits automatically sync your projects to Databricks Worksp

This is enabled via a git post-commit hook configured by `setup_claude.py`.

## AI CLI Tools

Three AI coding CLIs are pre-configured to use Databricks Model Serving:

### Claude Code
Default CLI. Uses Anthropic-native protocol via `/serving-endpoints/anthropic`.
```bash
claude # Start Claude Code
```

### OpenCode
Uses OpenAI-compatible protocol via `/serving-endpoints`. Supports multiple model providers.
```bash
opencode # Start with default model
opencode -m databricks/databricks-gemini-2-5-flash # Use Gemini Flash
opencode -m databricks/databricks-claude-sonnet-4-5 # Use Claude
opencode -m databricks/databricks-meta-llama-3-3-70b-instruct # Use Llama
```

### Gemini CLI
Uses Google Gemini-native protocol via `/serving-endpoints/google`.
```bash
gemini # Start Gemini CLI
gemini -m gemini-2.5-flash # Use Gemini 2.5 Flash
gemini -m gemini-2.5-pro # Use Gemini 2.5 Pro
```

### Available Databricks-Hosted Models

| Model | OpenCode Name | Gemini CLI Name |
|-------|---------------|-----------------|
| Claude Sonnet 4.5 | `databricks/databricks-claude-sonnet-4-5` | N/A (use Claude Code) |
| Gemini 2.5 Flash | `databricks/databricks-gemini-2-5-flash` | `gemini-2.5-flash` |
| Gemini 2.5 Pro | `databricks/databricks-gemini-2-5-pro` | `gemini-2.5-pro` |
| Llama 3.3 70B | `databricks/databricks-meta-llama-3-3-70b-instruct` | N/A |

### Testing Integrations

Run the integration test suite:
```bash
# Structural tests (no credentials needed)
python test_integrations.py

# Live API tests (with Databricks credentials)
DATABRICKS_HOST=https://your-workspace.cloud.databricks.com \
DATABRICKS_TOKEN=dapi-xxx \
python test_integrations.py
```

## Technologies

- **Backend**: Flask, Python PTY/termios
- **Frontend**: xterm.js, FitAddon
- **Integration**: Databricks SDK, Claude Agent SDK
- **Integration**: Databricks SDK, Claude Agent SDK, OpenCode, Gemini CLI

## License

Expand Down
2 changes: 1 addition & 1 deletion app.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
command:
- bash
- -c
- "mkdir -p ~/.local/bin && bash install_micro.sh && mv micro ~/.local/bin/ 2>/dev/null || true && python setup_claude.py && python setup_databricks.py && python app.py"
- "mkdir -p ~/.local/bin && bash install_micro.sh && mv micro ~/.local/bin/ 2>/dev/null || true && python setup_claude.py && python setup_opencode.py && python setup_gemini.py && python setup_databricks.py && python app.py"
env:
- name: HOME
value: /app/python/source_code
Expand Down
2 changes: 1 addition & 1 deletion app.yaml.template
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
command:
- bash
- -c
- "mkdir -p ~/.local/bin && bash install_micro.sh && mv micro ~/.local/bin/ 2>/dev/null || true && python setup_claude.py && python setup_databricks.py && python app.py"
- "mkdir -p ~/.local/bin && bash install_micro.sh && mv micro ~/.local/bin/ 2>/dev/null || true && python setup_claude.py && python setup_opencode.py && python setup_gemini.py && python setup_databricks.py && python app.py"
env:
- name: HOME
value: /app/python/source_code
Expand Down
86 changes: 86 additions & 0 deletions setup_gemini.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python
"""Configure Gemini CLI with Databricks Model Serving.

Gemini CLI uses the Google Generative Language API protocol, not OpenAI-compatible.
Databricks provides a Google-native endpoint at /serving-endpoints/google
(similar to /serving-endpoints/anthropic for Claude).

PR #11893 (by Databricks engineer AarushiShah) added auto-detection of *.databricks.com
URLs, switching to Bearer token auth automatically.

Auth: GEMINI_API_KEY_AUTH_MECHANISM=bearer sends Databricks PAT as Bearer token.
"""
import os
import json
import subprocess
from pathlib import Path

# Set HOME if not properly set
if not os.environ.get("HOME") or os.environ["HOME"] == "/":
os.environ["HOME"] = "/app/python/source_code"

home = Path(os.environ["HOME"])

host = os.environ.get("DATABRICKS_HOST", "")
token = os.environ.get("DATABRICKS_TOKEN", "")

if not host or not token:
print("Warning: DATABRICKS_HOST or DATABRICKS_TOKEN not set, skipping Gemini CLI config")
exit(0)

# Strip trailing slash from host
host = host.rstrip("/")

# 1. Install Gemini CLI if not present
gemini_installed = subprocess.run(
["which", "gemini"], capture_output=True, text=True
).returncode == 0

if not gemini_installed:
print("Installing Gemini CLI...")
result = subprocess.run(
["npm", "install", "-g", "@google/gemini-cli"],
capture_output=True, text=True,
env={**os.environ, "HOME": str(home)}
)
if result.returncode == 0:
print("Gemini CLI installed successfully")
else:
print(f"Gemini CLI install warning: {result.stderr}")
else:
print("Gemini CLI already installed")

# 2. Create ~/.gemini directory and configure environment
gemini_dir = home / ".gemini"
gemini_dir.mkdir(exist_ok=True)

# Write .env file with Databricks endpoint configuration
# Gemini CLI auto-loads env from ~/.gemini/.env
# The Google-native endpoint on Databricks mirrors /serving-endpoints/anthropic
env_content = f"""# Databricks Model Serving - Google Gemini native endpoint
GOOGLE_GEMINI_BASE_URL={host}/serving-endpoints/google
GEMINI_API_KEY={token}
GEMINI_API_KEY_AUTH_MECHANISM=bearer
"""

env_path = gemini_dir / ".env"
env_path.write_text(env_content)
env_path.chmod(0o600)
print(f"Gemini CLI env configured: {env_path}")

# 3. Write settings.json with model preferences
settings = {
"theme": "Default",
"selectedAuthType": "api-key"
}

settings_path = gemini_dir / "settings.json"
settings_path.write_text(json.dumps(settings, indent=2))
print(f"Gemini CLI settings configured: {settings_path}")

print("\nGemini CLI ready! Usage:")
print(" gemini # Start Gemini CLI")
print(f" gemini -m gemini-2.5-flash # Use Gemini 2.5 Flash")
print(f" gemini -m gemini-2.5-pro # Use Gemini 2.5 Pro")
print(f"\nEndpoint: {host}/serving-endpoints/google")
print("Auth: Bearer token (Databricks PAT)")
126 changes: 126 additions & 0 deletions setup_opencode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env python
"""Configure OpenCode CLI with Databricks Model Serving as an OpenAI-compatible provider."""
import os
import json
import subprocess
from pathlib import Path

# Set HOME if not properly set
if not os.environ.get("HOME") or os.environ["HOME"] == "/":
os.environ["HOME"] = "/app/python/source_code"

home = Path(os.environ["HOME"])

host = os.environ.get("DATABRICKS_HOST", "")
token = os.environ.get("DATABRICKS_TOKEN", "")

if not host or not token:
print("Warning: DATABRICKS_HOST or DATABRICKS_TOKEN not set, skipping OpenCode config")
exit(0)

# Strip trailing slash from host
host = host.rstrip("/")

# 1. Install OpenCode CLI if not present
opencode_bin = home / ".local" / "bin" / "opencode"
npm_global_bin = subprocess.run(
["npm", "config", "get", "prefix"],
capture_output=True, text=True
).stdout.strip()

# Check if opencode is already installed anywhere on PATH
opencode_installed = subprocess.run(
["which", "opencode"], capture_output=True, text=True
).returncode == 0

if not opencode_installed:
print("Installing OpenCode CLI...")
result = subprocess.run(
["npm", "install", "-g", "opencode-ai@latest"],
capture_output=True, text=True,
env={**os.environ, "HOME": str(home)}
)
if result.returncode == 0:
print("OpenCode CLI installed successfully")
else:
print(f"OpenCode install warning: {result.stderr}")
else:
print("OpenCode CLI already installed")

# 2. Write global opencode.json config
# OpenCode looks for config at ~/.config/opencode/opencode.json (global)
# and ./opencode.json (project-level)
opencode_config_dir = home / ".config" / "opencode"
opencode_config_dir.mkdir(parents=True, exist_ok=True)

# Databricks OpenAI-compatible endpoint: {host}/serving-endpoints
# Model names follow: databricks-<provider>-<model>
opencode_config = {
"$schema": "https://opencode.ai/config.json",
"provider": {
"databricks": {
"npm": "@ai-sdk/openai-compatible",
"name": "Databricks Model Serving",
"options": {
"baseURL": f"{host}/serving-endpoints",
"apiKey": "{env:DATABRICKS_TOKEN}"
},
"models": {
"databricks-claude-sonnet-4-5": {
"name": "Claude Sonnet 4.5 (Databricks)",
"limit": {
"context": 200000,
"output": 8192
}
},
"databricks-gemini-2-5-flash": {
"name": "Gemini 2.5 Flash (Databricks)",
"limit": {
"context": 1000000,
"output": 8192
}
},
"databricks-gemini-2-5-pro": {
"name": "Gemini 2.5 Pro (Databricks)",
"limit": {
"context": 1000000,
"output": 8192
}
},
"databricks-meta-llama-3-3-70b-instruct": {
"name": "Llama 3.3 70B (Databricks)",
"limit": {
"context": 128000,
"output": 4096
}
}
}
}
},
"model": "databricks/databricks-claude-sonnet-4-5"
}

config_path = opencode_config_dir / "opencode.json"
config_path.write_text(json.dumps(opencode_config, indent=2))
print(f"OpenCode configured: {config_path}")

# 3. Also create auth credentials for the databricks provider
# OpenCode stores credentials at ~/.local/share/opencode/auth.json
opencode_data_dir = home / ".local" / "share" / "opencode"
opencode_data_dir.mkdir(parents=True, exist_ok=True)

auth_data = {
"databricks": {
"api_key": token
}
}

auth_path = opencode_data_dir / "auth.json"
auth_path.write_text(json.dumps(auth_data, indent=2))
auth_path.chmod(0o600)
print(f"OpenCode auth configured: {auth_path}")

print("\nOpenCode ready! Usage:")
print(" opencode # Start OpenCode TUI")
print(" opencode -m databricks/databricks-gemini-2-5-flash # Use Gemini")
print(" opencode -m databricks/databricks-claude-sonnet-4-5 # Use Claude")
Loading