mirror of
https://github.com/coder/coder.git
synced 2026-11-02 05:23:10 +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
+15
-1
@@ -1,7 +1,11 @@
|
||||
// Package ptr contains some utility methods related to pointers.
|
||||
package ptr
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type number interface {
|
||||
constraints.Integer | constraints.Float
|
||||
@@ -38,3 +42,13 @@ func NilToDefault[T any](s *T, def T) T {
|
||||
func NilOrZero[T number](v *T) bool {
|
||||
return v == nil || *v == 0
|
||||
}
|
||||
|
||||
// FromNullString returns a pointer to the string when it is valid and nil
|
||||
// otherwise.
|
||||
func FromNullString(v sql.NullString) *string {
|
||||
if !v.Valid {
|
||||
return nil
|
||||
}
|
||||
value := v.String
|
||||
return &value
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ptr_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -101,3 +102,18 @@ func Test_NilOrZero(t *testing.T) {
|
||||
assert.False(t, ptr.NilOrZero(&nonZeroFloat64))
|
||||
assert.False(t, ptr.NilOrZero(&nonZeroDuration))
|
||||
}
|
||||
|
||||
func Test_FromNullString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Nil(t, ptr.FromNullString(sql.NullString{}))
|
||||
assert.Nil(t, ptr.FromNullString(sql.NullString{String: "ignored"}))
|
||||
|
||||
valid := ptr.FromNullString(sql.NullString{String: "value", Valid: true})
|
||||
assert.NotNil(t, valid)
|
||||
assert.Equal(t, "value", *valid)
|
||||
|
||||
empty := ptr.FromNullString(sql.NullString{Valid: true})
|
||||
assert.NotNil(t, empty)
|
||||
assert.Equal(t, "", *empty)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user