Skip to content

copilot: run ug copilot on a Unity Catalog model location - #1256

Open
xi-db wants to merge 7 commits into
mainfrom
xi-db/stack/copilot-model-location
Open

xi-db wants to merge 7 commits into
mainfrom
xi-db/stack/copilot-model-location

Conversation

@xi-db

@xi-db xi-db commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

🥞 Stacked PR

Use this link to review incremental changes.


What did you change, and why?

Change: ug copilot --model-location <catalog>.<schema> runs Copilot on the Unity Catalog model services of one schema, with them in /model. Claude Code and Codex already support model locations; Copilot only had system.ai and, in #1220, provider services.

  • Discovery, as for Claude Code.
    • The models come from the gateway's Anthropic model listing with the Databricks-Model-Service-Parent-Schema header, the same listing Claude Code's picker uses.
    • The listing names services by gateway alias (anthropic-aigw-<hash>-<catalog.schema.name>); Copilot registers and sends the full name, which the gateway routes as a model service.
    • The header only scopes the listing. Inference routes by the model id, so the registry carries no header.
  • Wire per service.
    • Services named like Claude or GPT models (…claude-sonnet-4-6, …gpt-5-6-luna) use the same wire as the system.ai catalog. GPT 5+ goes on Responses on /ai-gateway/mlflow/v1, because GPT 5+ rejects the temperature Copilot sends on the Anthropic wire.
    • Everything else (names that don't say which model backs them) goes on the Anthropic wire, as Claude Code sends every service. The gateway translates for other models.
    • claude-opus-4-1 is skipped, as in the system.ai catalog.
    • Claude limits are capped at 200K where Copilot's catalog limits exceed it, as for provider services, since a model service may not serve the 1M window.
  • Selection. --model-location, the saved "Custom Model Location" in ug configure and the managed unity_catalog_location now apply to Copilot like Claude Code and Codex (MODEL_LOCATION_TOOLS). --provider and --model-location stay mutually exclusive. The managed Databricks model list is ignored under a location.

How do you know it works?

Testing:

  • Unit:
    • location catalog: alias stripping, the name-based wire with the Anthropic fallback, claude-opus-4-1 skipped, the listing called with the schema, an empty schema;
    • a launch through the real proxy with a stubbed Copilot process: registry ids and wires, no header, 200K limits, start model;
    • ug copilot --model-location reaching the launch.
    • just lint is clean.
  • Integration (new tests/integration/test_ug_copilot_model_location.py, non-blocking Copilot lane): from fresh state, ug copilot --workspace … --model-location <model-discovery schema> --model <its Claude-compatible service> -p … completes a file task, with the answering model checked from Copilot's own session events.
  • Live (ug from this branch, a Databricks workspace):
    • --model-location system.ai started on system.ai.claude-sonnet-5-5 and answered.
    • --model system.ai.gpt-5-6-luna answered on dbx-responses. The same model on the Anthropic wire failed with "Unsupported parameter: 'temperature'", which is why GPT-named services use Responses.

Limits: a GPT 5+ model behind a service whose name doesn't say GPT goes on the Anthropic wire and can hit the same temperature rejection. Telling those apart needs each service's backing model, a possible follow-up.

Stacked on #1220.

This pull request and its description were written by Isaac.

@xi-db xi-db added the ug-review Run the automated UG review label Oct 22, 2026
@xi-db
xi-db force-pushed the xi-db/stack/copilot-model-location branch 9 times, most recently from f8ffea6 to 0e9aa5b Compare October 23, 2026 14:50
@xi-db
xi-db marked this pull request as ready for review October 23, 2026 15:03
@xi-db
xi-db requested a review from masonc08 October 23, 2026 15:03
@masonc08
masonc08 requested a review from andy-xu-db October 23, 2026 20:15
@masonc08

Copy link
Copy Markdown
Collaborator

Deferring to @andy-xu-db to review as he is more familiar with --model-location implementation on other harnesses

yaoharry pushed a commit to yaoharry/unity-gateway that referenced this pull request Oct 24, 2026
…#1220)

## 🥞 Stacked PR
Use this
[link](https://github.com/databricks/unity-gateway/pull/1220/files) to
review incremental changes.
-
[stack/copilot-auth-proxy](databricks#1217)
[[Files
changed](https://github.com/databricks/unity-gateway/pull/1217/files)]
[MERGED]
-
[stack/copilot-model-discovery](databricks#1218)
[[Files
changed](https://github.com/databricks/unity-gateway/pull/1218/files)]
[MERGED]
-
[**stack/copilot-provider-services**](databricks#1220)
[[Files
changed](https://github.com/databricks/unity-gateway/pull/1220/files)] ←
_this PR_
-
[stack/copilot-model-location](databricks#1256)
[[Files
changed](https://github.com/databricks/unity-gateway/pull/1256/files/a6c28cbc82fe5d36f47fdbf8f06744d4a2019427..f8ffea64fa222d47f61b3e53375bca312ebc780a)]

---------
## What did you change, and why?

**Change:** `ug copilot --provider <catalog>.<schema>.<name>` runs
Copilot through a Unity Catalog Model Provider Service, with that
service's models in `/model`. Claude Code and Codex already support
provider services; Copilot refused all of them ("supported: none").

- **Routing.** The session registry from databricks#1218 gets the service's
models. Every registry provider carries `headers:
{"Databricks-Model-Provider-Service": <service>}`, which Copilot sends
on every request, and the loopback proxy forwards it unchanged. The
proxy removes `reasoning_effort` from Chat Completions requests: Copilot
sends it, and OpenAI and Azure reject it for the models on that wire.
  - Claude models -> `dbx-anthropic` on `/ai-gateway/anthropic`.
- Other chat models -> `dbx-responses` (GPT 5+ and o-series, which
reject Copilot's `temperature: 0` on Chat Completions) or
`dbx-completions`, both on `/ai-gateway/openai/v1`, the gateway's native
OpenAI route, which serves provider services untranslated.
Databricks-hosted models keep `/ai-gateway/mlflow/v1`.
- **Provider types.**
- Supported: `anthropic`, `amazon_bedrock` and `bedrock_mantle` (Claude
targets only), `openai`, `azure_openai`.
- Refused with an actionable error: subscription-relay Anthropic
services (Claude Code only), Bedrock services without Claude targets,
other provider types. `microsoft_foundry` is left for a follow-up once
there is a test fixture for it.
- **Models.**
- Provider ids are classified whole (`gpt-5.5`, `gpt-4o`,
`us.anthropic.claude-sonnet-4-6`). The `system.ai` name logic took the
text after the last dot, which dropped `gpt-5.5` and `gpt-4o`.
- Dated, versioned and region-less Claude ids
(`claude-sonnet-4-5-20250929`, `...-20250929-v1:0`,
`anthropic.claude-opus-4-8`) map to Copilot's catalog id
(`claude-sonnet-4.5`), so Copilot sends the right Claude settings.
Provider Claude models whose catalog limits exceed 200K are capped at
200K, because a provider may not serve the 1M window.
- Declared targets are used as is. A service that allows every model
registers the gateway's listing for it (Anthropic `/v1/models`, Codex
`/v1/models`) plus `--model` (plain id or `dbx-<wire>/<id>`) or the
managed default. Without either, the session starts on Copilot's default
pick from the listing, as `ug codex` and `ug claude` do; the listing is
a fixed catalog, not what the provider account serves, so that pick can
fail. An empty or failed listing asks for `--model`, and a failed
listing is reported with its cause.
- Targets whose declared APIs (`native_api_types`) include no chat API,
such as embedding deployments, are skipped with a warning. The declared
APIs only exclude; the wire still comes from the model name. GPT and
o-series versions sort newest first.
- **Selection.** `ug configure` offers matching services for Copilot
under External Models, and a managed config's `model_provider_service`
for Copilot applies, the same way as for Claude Code and Codex. Under a
provider the managed Databricks model list is ignored.
- **Workspace.** `ug copilot --workspace <url>` targets that workspace
for the launch and sets it up first if needed, as `ug claude` and `ug
codex` do. Before, the flag was passed through to Copilot.

## How do you know it works?

**Testing:**
- **Unit:**
- provider classifier (wire per provider type, non-chat and non-Claude
exclusions), catalog order, Claude catalog ids for dated/Bedrock ids;
- provider catalog: declared targets, allow-all listing for
Anthropic/Bedrock and OpenAI, `--model` on allow-all, empty listing, no
usable model, skipped-model warning, invalid service;
- registry rendering with the header, native OpenAI base URL and 200K
Claude limits;
- launch through the real proxy with a stubbed Copilot process: registry
content, start model, a model the service doesn't serve fails before
Copilot starts;
- `resolve_provider_service` / `service_usable_for_tool` for Copilot,
and `ug copilot --provider` reaching the launch.
- The full unit suite passes except tests that also fail on the base in
this environment (`TestInstallToolBinary`, `test_e2e_user_agent`, three
`test_smart_router` cases). `just lint` is clean.
- **Integration (new `tests/integration/test_ug_copilot_providers.py`,
non-blocking Copilot lane):** file tasks through the existing Anthropic
MPS fixture (no `--model`), the OpenAI and Azure OpenAI MPS fixtures on
`gpt-5-nano`, and the Bedrock allow-all fixture with a named model, with
the answering model checked from Copilot's own session events; the
relayed fixture fails before Copilot starts. These run in CI for the
first time on this PR.
- **Live (`ug` from this branch, one provider service per row unless
noted):** One headless `ug copilot --provider <service> [--model <id>]
-p "Reply with exactly: OK"` per model, plus each service once without
`--model` to check the start model. The model shown is the one Copilot's
session log says answered.

| Provider type | Model | Copilot wire | Result |
|---|---|---|---|
| OpenAI | `gpt-5.4-nano`, `gpt-5.6-luna`, default -> `gpt-5.6-luna` |
Responses | ✅ OK |
| OpenAI (allow-all) | `gpt-5-nano`, default -> `gpt-6.1-sol` (from the
gateway listing) | Responses | ✅ OK |
| OpenAI (allow-all) | `gpt-4o-mini` | Chat Completions | ✅ OK |
| Azure OpenAI | `gpt-5.5`, `gpt-5.6-luna`, default start model |
Responses | ✅ OK |
| Azure OpenAI | `gpt-4.1-mini`, `gpt-4o-mini` | Chat Completions | ✅ OK
|
| Anthropic | `claude-haiku-4-5`, `claude-sonnet-5`, default ->
`claude-sonnet-5` | Anthropic | ✅ OK |
| Anthropic (allow-all) | `claude-haiku-4-5`, `claude-sonnet-4-5`,
default -> `claude-sonnet-5-5` (from the gateway listing) | Anthropic |
✅ OK |
| Amazon Bedrock (with non-Claude targets) |
`us.anthropic.claude-haiku-4-5-20251001-v1:0`,
`us.anthropic.claude-sonnet-4-6`, default ->
`us.anthropic.claude-sonnet-4-6` | Anthropic | ✅ OK; the service's
non-Claude targets (`grok-4.6`, `gpt-oss`) are left out of `/model`,
with a warning |
| Amazon Bedrock (service credential, allow-all; 3 services) |
`us.anthropic.claude-haiku-4-5-20251001-v1:0` | Anthropic | ✅ OK |
| Relayed Anthropic subscription | - | - | ✅ refused before launch:
"relays a Claude subscription, which only Claude Code can use" |

**Not in this PR:** `--model-location` for Copilot (next), Microsoft
Foundry services (needs a fixture), mixing Databricks and provider
models in one session.

Stacked on databricks#1218.

This pull request and its description were written by Isaac.

---------

Co-authored-by: Isaac <no-reply@databricks.com>
xi-db and others added 7 commits October 24, 2026 10:38
ug copilot --model-location <catalog>.<schema> registers the schema's model
services in the session registry, from the gateway's Anthropic model listing
as for Claude Code. Services are sent by full name; the gateway routes them
as model services. Services named like Claude or GPT models use their usual
wire (GPT 5+ on Responses, since GPT 5+ rejects the temperature Copilot sends
on the Anthropic wire); the rest use the Anthropic wire, which the gateway
translates for other models. The saved Custom Model Location and the managed
unity_catalog_location apply to Copilot like Claude Code and Codex.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Under a managed unity_catalog_location or model_provider_service, Copilot
dropped the budget recommendation and the managed default_model. README now
lists Copilot for the saved Custom Model Location.

Co-authored-by: Isaac <no-reply@databricks.com>
…Code and Codex

Unit tests for the saved location and its precedence with --provider, a managed location or
provider, the configure picker, and workspace and single-agent configure. Single-agent
`ug configure --agent copilot` now applies the managed model source like the other location
agents. The launch branch per agent uses the AGENT_* constants.

Co-authored-by: Isaac <no-reply@databricks.com>
… for ug agents add

The location catalog strips gateway aliases with smart routing's
unwrap_anthropic_gateway_model instead of its own pattern. The single-agent AI-tools install
forces a managed-config refresh unless one already ran, so ug agents add copilot keeps it.

Co-authored-by: Isaac <no-reply@databricks.com>
…gents

Co-authored-by: Isaac <no-reply@databricks.com>
… path

Since the workspace policy install landed, only Claude Code and Codex install the policy at
configure; Copilot reads it, so its AI-tools install still forces a fresh managed-config read.

Co-authored-by: Isaac <no-reply@databricks.com>
@xi-db
xi-db force-pushed the xi-db/stack/copilot-model-location branch from e9f4e4e to 9add893 Compare October 24, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ug-review Run the automated UG review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants