mirror of
https://github.com/coder/coder.git
synced 2026-10-31 11:02:50 +08:00
feat!: scope chat model override settings to organizations (#28442)
Moves chat model override settings from deployment scope to organization scope, following the org-scoping of chat model configs in #28440. Depends on #28440 (based on `ethan/chat-models-availability-consolidation`). Remote dogfood UAT passed on this change, including multi-organization scenarios on a licensed deployment with real models. ## Problem Chat model configs are now owned by organizations, but the override settings that reference them stayed deployment-wide: admin subagent/title/compaction overrides in `site_configs`, personal overrides in `user_configs`, and the advisor model embedded in the advisor runtime JSON. A single deployment-level override can only point at one organization's model config, so overrides effectively worked for the default organization only, and title generation had to soften its hard-failure contract to tolerate unusable overrides. ## Fix Admin overrides (general, explore, title generation, compaction, advisor) and personal overrides (root, general, explore) become organization-scoped rows in two new typed tables, `chat_organization_model_overrides` and `chat_user_model_overrides`. Composite foreign keys on `(organization_id, model_config_id)` make cross-organization model references unrepresentable. The migration deletes the legacy serialized `site_configs`/`user_configs` override keys instead of migrating them; admins and users re-select models in the new organization-scoped settings. New endpoints live under `/api/experimental/organizations/{organization}/chats/model-overrides` (admin defaults) and `/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides` (personal overrides); the legacy deployment-level routes are removed. chatd resolves overrides via the chat's organization, restoring the hard-failure contract for configured-but-unusable title generation overrides. The deployment-level `AllowUsers` switch and advisor runtime limits stay deployment-scoped. The Coder Agents settings UI gains a per-organization "Defaults & overrides" page under AI Settings > Models, and the personal overrides page gains an organization selector for users in multiple organizations. Advisor telemetry now reports per-organization override rows. **BREAKING CHANGE**: Existing deployment-level overrides are dropped, not migrated. Every organization starts with no overrides after upgrade, and model selections must be re-applied in the new settings. The legacy deployment-level override endpoints are removed. Closes https://linear.app/coder/issue/CODAGT-872 > [!NOTE] > Xum (AI agent) authored this PR on behalf of @ibetitsmike. <!-- xum-attribution: model=claude-opus-4-6 thinking=high --> --------- Co-authored-by: Ethan Dickson <ethan@coder.com>
This commit is contained in:
co-authored by
Ethan Dickson
parent
3809314b31
commit
0ef331fcd0
@@ -298,37 +298,50 @@ contact an administrator.
|
||||
## Model overrides
|
||||
|
||||
Beyond the chat-level model picker, Coder Agents supports two override
|
||||
layers:
|
||||
layers. Both are stored per organization and resolve from the chat's
|
||||
organization:
|
||||
|
||||
- **Subagent overrides** (admin, deployment-wide): Pin specific subagent
|
||||
contexts to a particular model. Configure them at **Agents** >
|
||||
**Settings** > **Manage Agents** > **Agents**.
|
||||
- **Personal overrides** (per user, opt-in by admin): Let users override
|
||||
the model for their own root chats and delegated subagents. Admins
|
||||
enable the toggle on the same admin page; once on, each user sees an
|
||||
**Agents** tab in their personal **Agents** > **Settings**.
|
||||
- **Admin overrides** (per organization): Pin specific contexts to a
|
||||
particular model. Configure them on the **Defaults & overrides** tab
|
||||
under **AI Settings** > **Models** for the selected organization.
|
||||
- **Personal overrides** (per user and organization, opt-in by admin):
|
||||
Let users override the model for their own root chats and delegated
|
||||
subagents. Admins enable the deployment-wide toggle under
|
||||
**AI Settings** > **Coder Agents**; once on, each user sees an
|
||||
**Agents** tab in their personal **Agents** > **Settings**. Users in
|
||||
more than one organization pick which organization to configure.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When a deployment upgrades from the older deployment-wide override
|
||||
> storage, existing admin and personal overrides are deleted rather than
|
||||
> migrated. Admins and users must re-select their override models in
|
||||
> settings after the upgrade.
|
||||
|
||||
The configurable contexts:
|
||||
|
||||
| Context | Layer | Applies to |
|
||||
|----------------------|--------------|--------------------------------------------------------------------------------|
|
||||
| **General** | Admin + user | Write-capable subagents (`spawn_agent` with `type=general` or `computer_use`). |
|
||||
| **Explore** | Admin + user | Read-only subagents (`spawn_agent` with `type=explore`). |
|
||||
| **Title generation** | Admin only | Automatic title generation for new chats. |
|
||||
| **Root** | User only | The user's own root chats. |
|
||||
| Context | Layer | Applies to |
|
||||
|----------------------|--------------|----------------------------------------------------------------------------------------|
|
||||
| **General** | Admin + user | Write-capable subagents (`spawn_agent` with `type=general` or `computer_use`). |
|
||||
| **Explore** | Admin + user | Read-only subagents (`spawn_agent` with `type=explore`). |
|
||||
| **Title generation** | Admin only | Automatic title generation for new chats. |
|
||||
| **Compaction** | Admin only | Conversation summarization near the context limit. |
|
||||
| **Advisor** | Admin only | The [advisor](./platform-controls/advisor.md). Requires the `chat-advisor` experiment. |
|
||||
| **Root** | User only | The user's own root chats. |
|
||||
|
||||
Resolution order, evaluated per chat or subagent:
|
||||
Resolution order, evaluated per chat or subagent from the chat's
|
||||
organization:
|
||||
|
||||
1. Explicit `model_config_id` on the `spawn_agent` tool call (general and
|
||||
explore subagents only).
|
||||
1. Personal override (when the admin gate is on and a model is set).
|
||||
1. Admin subagent override.
|
||||
1. The chat's selected model (or the deployment default for new chats).
|
||||
1. Admin override.
|
||||
1. The chat's selected model (or the organization default for new chats).
|
||||
|
||||
If a referenced model is later disabled or deleted, that layer is skipped
|
||||
and resolution falls through to the next. Explicit `spawn_agent` selection
|
||||
is different: an unusable `model_config_id` fails the tool call instead of
|
||||
falling through. Agents discover selectable models (and their reasoning
|
||||
and resolution falls through to the next, with two exceptions: an unusable
|
||||
explicit `spawn_agent` `model_config_id` fails the tool call, and an
|
||||
unusable title generation override skips title generation instead of
|
||||
falling back. Agents discover selectable models (and their reasoning
|
||||
effort ranges) with the `list_subagent_models` tool, which only returns
|
||||
enabled models usable with the chat owner's credentials. Computer-use
|
||||
subagents always run on the administrator-configured computer-use model and
|
||||
@@ -336,8 +349,10 @@ reject explicit model selection.
|
||||
|
||||
> [!NOTE]
|
||||
> Both override layers may change between releases.
|
||||
> The same values are available through the chat
|
||||
> configuration API under `/api/v2/chats/config/`.
|
||||
> Admin overrides are available through the API at
|
||||
> `/api/experimental/organizations/{organization}/chats/model-overrides`
|
||||
> and personal overrides at
|
||||
> `/api/experimental/organizations/{organization}/members/{user}/chats/model-overrides`.
|
||||
|
||||
## User API keys (BYOK)
|
||||
|
||||
|
||||
@@ -29,20 +29,25 @@ after repeated failures, or risk reduction before a destructive operation.
|
||||
|
||||
## Configuration
|
||||
|
||||
Once the experiment is enabled, configure the advisor under **AI Settings** >
|
||||
**Coder Agents** > **Advisor**.
|
||||
Once the experiment is enabled, configure the advisor's runtime limits
|
||||
under **AI Settings** > **Coder Agents** > **Advisor**. These limits apply
|
||||
deployment-wide.
|
||||
|
||||
| Field | Default | Notes |
|
||||
|-------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| Max uses per turn | `0` (unlimited) | Caps how many times the root agent can call the advisor in a single chat turn. Must be a non-negative integer. |
|
||||
| Max output tokens | `0` (server default) | Caps the advisor model's response length. `0` uses the server default of 16,384 tokens. Must be a non-negative integer. |
|
||||
| Advisor model | Use chat model | Optional dedicated chat model config for the advisor. When unset, the advisor reuses the root agent's model. |
|
||||
| Reasoning effort | Model default | Overrides the selected advisor model's reasoning effort. Available only when the model supports selectable effort. |
|
||||
|
||||
The advisor model and its reasoning effort are organization-scoped
|
||||
[model overrides](../models.md#model-overrides). Configure them on the
|
||||
**Defaults & overrides** tab under **AI Settings** > **Models** for the
|
||||
selected organization. When no override is set, the advisor reuses the
|
||||
root agent's model.
|
||||
|
||||
The advisor is not available in plan mode or to subagents.
|
||||
Failed advisor invocations refund the per-turn budget.
|
||||
|
||||
The same configuration is available at:
|
||||
|
||||
- `GET /api/v2/chats/config/advisor`
|
||||
- `PUT /api/v2/chats/config/advisor`
|
||||
The same configuration is available through the API: runtime limits at
|
||||
`GET`/`PUT` `/api/experimental/chats/config/advisor`, and the advisor
|
||||
model override at
|
||||
`PUT /api/experimental/organizations/{organization}/chats/model-overrides/advisor`.
|
||||
|
||||
Generated
+211
@@ -4087,6 +4087,58 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
| `reasoning` | [codersdk.ChatModelReasoningOptions](#codersdkchatmodelreasoningoptions) | false | | |
|
||||
| `user` | string | false | | |
|
||||
|
||||
## codersdk.ChatModelOverrideContext
|
||||
|
||||
```json
|
||||
"general"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value(s) |
|
||||
|-------------------------------------------------------------------|
|
||||
| `advisor`, `compaction`, `explore`, `general`, `title_generation` |
|
||||
|
||||
## codersdk.ChatModelOverrideResponse
|
||||
|
||||
```json
|
||||
{
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `context` | [codersdk.ChatModelOverrideContext](#codersdkchatmodeloverridecontext) | false | | |
|
||||
| `model_config_id` | string | false | | |
|
||||
| `reasoning_effort` | string | false | | |
|
||||
|
||||
## codersdk.ChatModelOverridesResponse
|
||||
|
||||
```json
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------|-----------------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `overrides` | array of [codersdk.ChatModelOverrideResponse](#codersdkchatmodeloverrideresponse) | false | | |
|
||||
|
||||
## codersdk.ChatModelProviderDescriptor
|
||||
|
||||
```json
|
||||
@@ -4390,6 +4442,80 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
| `top_logprobs` | integer | false | | |
|
||||
| `user` | string | false | | |
|
||||
|
||||
## codersdk.ChatPersonalModelOverride
|
||||
|
||||
```json
|
||||
{
|
||||
"context": "root",
|
||||
"is_set": true,
|
||||
"mode": "deployment_default",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `context` | [codersdk.ChatPersonalModelOverrideContext](#codersdkchatpersonalmodeloverridecontext) | false | | |
|
||||
| `is_set` | boolean | false | | |
|
||||
| `mode` | [codersdk.ChatPersonalModelOverrideMode](#codersdkchatpersonalmodeloverridemode) | false | | |
|
||||
| `model_config_id` | string | false | | |
|
||||
| `reasoning_effort` | string | false | | |
|
||||
|
||||
## codersdk.ChatPersonalModelOverrideContext
|
||||
|
||||
```json
|
||||
"root"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value(s) |
|
||||
|------------------------------|
|
||||
| `explore`, `general`, `root` |
|
||||
|
||||
## codersdk.ChatPersonalModelOverrideDeploymentDefaults
|
||||
|
||||
```json
|
||||
{
|
||||
"explore": {
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
},
|
||||
"general": {
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------|--------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `explore` | [codersdk.ChatModelOverrideResponse](#codersdkchatmodeloverrideresponse) | false | | |
|
||||
| `general` | [codersdk.ChatModelOverrideResponse](#codersdkchatmodeloverrideresponse) | false | | |
|
||||
|
||||
## codersdk.ChatPersonalModelOverrideMode
|
||||
|
||||
```json
|
||||
"deployment_default"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value(s) |
|
||||
|-----------------------------------------------|
|
||||
| `chat_default`, `deployment_default`, `model` |
|
||||
|
||||
## codersdk.ChatPlanMode
|
||||
|
||||
```json
|
||||
@@ -14982,6 +15108,22 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
||||
| `user_roles` | object | false | | |
|
||||
| » `[any property]` | [codersdk.ChatRole](#codersdkchatrole) | false | | |
|
||||
|
||||
## codersdk.UpdateChatModelOverrideRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|--------|----------|--------------|-------------|
|
||||
| `model_config_id` | string | false | | |
|
||||
| `reasoning_effort` | string | false | | |
|
||||
|
||||
## codersdk.UpdateChatModelRequest
|
||||
|
||||
```json
|
||||
@@ -15485,6 +15627,24 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
||||
| `theme_light` | `dark`, `dark-protan-deuter`, `dark-tritan`, `light`, `light-protan-deuter`, `light-tritan` |
|
||||
| `theme_mode` | `single`, `sync` |
|
||||
|
||||
## codersdk.UpdateUserChatPersonalModelOverrideRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "deployment_default",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `mode` | [codersdk.ChatPersonalModelOverrideMode](#codersdkchatpersonalmodeloverridemode) | false | | |
|
||||
| `model_config_id` | string | false | | |
|
||||
| `reasoning_effort` | string | false | | |
|
||||
|
||||
## codersdk.UpdateUserNotificationPreferences
|
||||
|
||||
```json
|
||||
@@ -16111,6 +16271,57 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `theme_mode` | [codersdk.ThemeMode](#codersdkthememode) | false | | |
|
||||
| `theme_preference` | string | false | | Theme preference is the legacy single-field appearance setting. In "single" mode it mirrors the active theme. In "sync" mode modern clients normally mirror the active OS slot, but older clients can update only this field, so it may diverge from ThemeLight or ThemeDark until a modern client saves the full appearance state again. |
|
||||
|
||||
## codersdk.UserChatPersonalModelOverridesResponse
|
||||
|
||||
```json
|
||||
{
|
||||
"deployment_defaults": {
|
||||
"explore": {
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
},
|
||||
"general": {
|
||||
"context": "general",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
},
|
||||
"enabled": true,
|
||||
"explore": {
|
||||
"context": "root",
|
||||
"is_set": true,
|
||||
"mode": "deployment_default",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
},
|
||||
"general": {
|
||||
"context": "root",
|
||||
"is_set": true,
|
||||
"mode": "deployment_default",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
},
|
||||
"root": {
|
||||
"context": "root",
|
||||
"is_set": true,
|
||||
"mode": "deployment_default",
|
||||
"model_config_id": "string",
|
||||
"reasoning_effort": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-----------------------|--------------------------------------------------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `deployment_defaults` | [codersdk.ChatPersonalModelOverrideDeploymentDefaults](#codersdkchatpersonalmodeloverridedeploymentdefaults) | false | | |
|
||||
| `enabled` | boolean | false | | |
|
||||
| `explore` | [codersdk.ChatPersonalModelOverride](#codersdkchatpersonalmodeloverride) | false | | |
|
||||
| `general` | [codersdk.ChatPersonalModelOverride](#codersdkchatpersonalmodeloverride) | false | | |
|
||||
| `root` | [codersdk.ChatPersonalModelOverride](#codersdkchatpersonalmodeloverride) | false | | |
|
||||
|
||||
## codersdk.UserLatency
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user