refactor: drop chat_model_configs provider column (#26877)

The provider type already lives authoritatively in ai_providers.type,
reachable on every active row through ai_provider_id, which the
chat_model_configs_ai_provider_required_when_active CHECK makes
mandatory. The stored provider string was a denormalized copy the system
kept in sync with a startup backfill and no longer needs.

Every surface now derives provider type from the linked ai_providers
row. Telemetry is the one exception: it keeps emitting provider, now
sourced from ai_providers.type via a JOIN, so the BigQuery column and the
Nexus dashboards that read it are unaffected. The experimental HTTP/SDK
response drops provider and makes ai_provider_id required, since those
endpoints return only active configs; consumers resolve provider type
from ai_provider_id and the AI providers listing.

This ships in a single release with no compatibility window: production
reads the table via SELECT *, so a pre-drop binary fails config reads the
moment the column is gone. Operators must scale to zero before upgrading,
and there is no rollback.

Closes CODAGT-599
This commit is contained in:
Mathias Fredriksson
2026-07-01 15:59:55 +03:00
committed by GitHub
parent cf75dd0f46
commit 047c47495b
92 changed files with 1072 additions and 1197 deletions
+1 -15
View File
@@ -1724,13 +1724,6 @@ func (q *querier) AutoArchiveInactiveChats(ctx context.Context, arg database.Aut
return q.db.AutoArchiveInactiveChats(ctx, arg)
}
func (q *querier) BackfillChatModelConfigProvider(ctx context.Context, arg database.BackfillChatModelConfigProviderParams) (sql.Result, error) {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return nil, err
}
return q.db.BackfillChatModelConfigProvider(ctx, arg)
}
func (q *querier) BackoffChatDiffStatus(ctx context.Context, arg database.BackoffChatDiffStatusParams) error {
// This is a system-level operation used by the gitsync
// background worker to reschedule failed refreshes. Same
@@ -2113,13 +2106,6 @@ func (q *querier) DeleteChatModelConfigsByAIProviderID(ctx context.Context, aiPr
return q.db.DeleteChatModelConfigsByAIProviderID(ctx, aiProviderID)
}
func (q *querier) DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
}
return q.db.DeleteChatModelConfigsByProvider(ctx, provider)
}
func (q *querier) DeleteChatQueuedMessage(ctx context.Context, arg database.DeleteChatQueuedMessageParams) error {
chat, err := q.db.GetChatByID(ctx, arg.ChatID)
if err != nil {
@@ -3677,7 +3663,7 @@ func (q *querier) GetEnabledChatModelConfigByID(ctx context.Context, id uuid.UUI
return q.db.GetEnabledChatModelConfigByID(ctx, id)
}
func (q *querier) GetEnabledChatModelConfigs(ctx context.Context) ([]database.ChatModelConfig, error) {
func (q *querier) GetEnabledChatModelConfigs(ctx context.Context) ([]database.GetEnabledChatModelConfigsRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceDeploymentConfig); err != nil {
return nil, err
}
+5 -20
View File
@@ -657,11 +657,6 @@ func (s *MethodTestSuite) TestChats() {
dbm.EXPECT().DeleteChatModelConfigByID(gomock.Any(), id).Return(nil).AnyTimes()
check.Args(id).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("DeleteChatModelConfigsByProvider", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
providerName := "test-provider"
dbm.EXPECT().DeleteChatModelConfigsByProvider(gomock.Any(), providerName).Return(nil).AnyTimes()
check.Args(providerName).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("DeleteChatModelConfigsByAIProviderID", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
providerID := uuid.New()
dbm.EXPECT().DeleteChatModelConfigsByAIProviderID(gomock.Any(), providerID).Return(nil).AnyTimes()
@@ -1212,10 +1207,10 @@ func (s *MethodTestSuite) TestChats() {
check.Args(config.ID).Asserts(rbac.ResourceDeploymentConfig, policy.ActionRead).Returns(config)
}))
s.Run("GetEnabledChatModelConfigs", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
configA := testutil.Fake(s.T(), faker, database.ChatModelConfig{})
configB := testutil.Fake(s.T(), faker, database.ChatModelConfig{})
dbm.EXPECT().GetEnabledChatModelConfigs(gomock.Any()).Return([]database.ChatModelConfig{configA, configB}, nil).AnyTimes()
check.Args().Asserts(rbac.ResourceDeploymentConfig, policy.ActionRead).Returns([]database.ChatModelConfig{configA, configB})
rowA := testutil.Fake(s.T(), faker, database.GetEnabledChatModelConfigsRow{})
rowB := testutil.Fake(s.T(), faker, database.GetEnabledChatModelConfigsRow{})
dbm.EXPECT().GetEnabledChatModelConfigs(gomock.Any()).Return([]database.GetEnabledChatModelConfigsRow{rowA, rowB}, nil).AnyTimes()
check.Args().Asserts(rbac.ResourceDeploymentConfig, policy.ActionRead).Returns([]database.GetEnabledChatModelConfigsRow{rowA, rowB})
}))
s.Run("GetStaleChats", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
@@ -1257,12 +1252,11 @@ func (s *MethodTestSuite) TestChats() {
}))
s.Run("InsertChatModelConfig", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
arg := database.InsertChatModelConfigParams{
Provider: "test-provider",
Model: "test-model",
DisplayName: "Test Model",
Enabled: true,
}
config := testutil.Fake(s.T(), faker, database.ChatModelConfig{Provider: arg.Provider, Model: arg.Model, DisplayName: arg.DisplayName, Enabled: arg.Enabled})
config := testutil.Fake(s.T(), faker, database.ChatModelConfig{Model: arg.Model, DisplayName: arg.DisplayName, Enabled: arg.Enabled})
dbm.EXPECT().InsertChatModelConfig(gomock.Any(), arg).Return(config, nil).AnyTimes()
check.Args(arg).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate).Returns(config)
}))
@@ -1515,7 +1509,6 @@ func (s *MethodTestSuite) TestChats() {
config := testutil.Fake(s.T(), faker, database.ChatModelConfig{})
arg := database.UpdateChatModelConfigParams{
ID: config.ID,
Provider: "updated-provider",
Model: "updated-model",
DisplayName: "Updated Model",
Enabled: true,
@@ -6859,14 +6852,6 @@ func (s *MethodTestSuite) TestAIBridge() {
dbm.EXPECT().DeleteAIProviderByID(gomock.Any(), provider.ID).Return(nil).AnyTimes()
check.Args(provider.ID).Asserts(rbac.ResourceAIProvider, policy.ActionDelete).Returns()
}))
s.Run("BackfillChatModelConfigProvider", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.BackfillChatModelConfigProviderParams{
OldProvider: "anthropic",
NewProvider: "bedrock",
}
dbm.EXPECT().BackfillChatModelConfigProvider(gomock.Any(), arg).Return(nil, nil).AnyTimes()
check.Args(arg).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("UpdateEncryptedAIProviderSettings", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
provider := testutil.Fake(s.T(), faker, database.AIProvider{})
arg := database.UpdateEncryptedAIProviderSettingsParams{
+4 -4
View File
@@ -160,14 +160,15 @@ const (
func ChatModelConfig(t testing.TB, db database.Store, seed database.ChatModelConfig, munge ...func(*database.InsertChatModelConfigParams)) database.ChatModelConfig {
t.Helper()
providerName := takeFirst(seed.Provider, "openai")
aiProviderID := seed.AIProviderID
if !aiProviderID.Valid {
// No AIProviderID supplied: reuse or create a default openai provider.
// Tests needing a specific provider type should pass seed.AIProviderID.
providers, err := db.GetAIProviders(genCtx, database.GetAIProvidersParams{IncludeDisabled: true})
require.NoError(t, err, "get ai providers")
var provider database.AIProvider
for _, candidate := range providers {
if candidate.Type != database.AIProviderType(providerName) {
if candidate.Type != database.AIProviderTypeOpenai {
continue
}
if provider.ID == uuid.Nil || candidate.CreatedAt.After(provider.CreatedAt) {
@@ -176,13 +177,12 @@ func ChatModelConfig(t testing.TB, db database.Store, seed database.ChatModelCon
}
if provider.ID == uuid.Nil {
provider = AIProvider(t, db, database.AIProvider{
Type: database.AIProviderType(providerName),
Type: database.AIProviderTypeOpenai,
})
}
aiProviderID = uuid.NullUUID{UUID: provider.ID, Valid: true}
}
params := database.InsertChatModelConfigParams{
Provider: providerName,
Model: takeFirst(seed.Model, "gpt-4o-mini"),
DisplayName: takeFirst(seed.DisplayName, "Test Model"),
CreatedBy: seed.CreatedBy,
+10 -6
View File
@@ -296,7 +296,9 @@ func TestGenerator(t *testing.T) {
// Defaults.
cfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{})
require.NotEqual(t, uuid.Nil, cfg.ID)
require.Equal(t, "openai", cfg.Provider)
prov, err := db.GetAIProviderByID(context.Background(), cfg.AIProviderID.UUID)
require.NoError(t, err)
require.Equal(t, "openai", string(prov.Type))
require.Equal(t, "gpt-4o-mini", cfg.Model)
require.Equal(t, "Test Model", cfg.DisplayName)
require.True(t, cfg.Enabled)
@@ -304,13 +306,15 @@ func TestGenerator(t *testing.T) {
require.Equal(t, int32(70), cfg.CompressionThreshold)
// Overrides.
_ = dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "anthropic"})
anthropicProvider := dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "anthropic"})
cfg2 := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "anthropic",
AIProviderID: uuid.NullUUID{UUID: anthropicProvider.ID, Valid: true},
Model: "claude-4",
ContextLimit: 200000,
})
require.Equal(t, "anthropic", cfg2.Provider)
prov2, err := db.GetAIProviderByID(context.Background(), cfg2.AIProviderID.UUID)
require.NoError(t, err)
require.Equal(t, "anthropic", string(prov2.Type))
require.Equal(t, "claude-4", cfg2.Model)
require.Equal(t, int64(200000), cfg2.ContextLimit)
})
@@ -325,7 +329,7 @@ func TestGenerator(t *testing.T) {
OrganizationID: o.ID,
})
p := dbgen.ChatProvider(t, db, database.ChatProvider{})
m := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{Provider: p.Provider})
m := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{AIProviderID: uuid.NullUUID{UUID: p.ID, Valid: true}})
// Defaults.
chat := dbgen.Chat(t, db, database.Chat{
@@ -360,7 +364,7 @@ func TestGenerator(t *testing.T) {
OrganizationID: o.ID,
})
p := dbgen.ChatProvider(t, db, database.ChatProvider{})
m := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{Provider: p.Provider})
m := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{AIProviderID: uuid.NullUUID{UUID: p.ID, Valid: true}})
chat := dbgen.Chat(t, db, database.Chat{
OwnerID: u.ID,
OrganizationID: o.ID,
+1 -18
View File
@@ -5,7 +5,6 @@ package dbmetrics
import (
"context"
"database/sql"
"encoding/json"
"slices"
"time"
@@ -186,14 +185,6 @@ func (m queryMetricsStore) AutoArchiveInactiveChats(ctx context.Context, arg dat
return r0, r1
}
func (m queryMetricsStore) BackfillChatModelConfigProvider(ctx context.Context, arg database.BackfillChatModelConfigProviderParams) (sql.Result, error) {
start := time.Now()
r0, r1 := m.s.BackfillChatModelConfigProvider(ctx, arg)
m.queryLatencies.WithLabelValues("BackfillChatModelConfigProvider").Observe(time.Since(start).Seconds())
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "BackfillChatModelConfigProvider").Inc()
return r0, r1
}
func (m queryMetricsStore) BackoffChatDiffStatus(ctx context.Context, arg database.BackoffChatDiffStatusParams) error {
start := time.Now()
r0 := m.s.BackoffChatDiffStatus(ctx, arg)
@@ -538,14 +529,6 @@ func (m queryMetricsStore) DeleteChatModelConfigsByAIProviderID(ctx context.Cont
return r0
}
func (m queryMetricsStore) DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error {
start := time.Now()
r0 := m.s.DeleteChatModelConfigsByProvider(ctx, provider)
m.queryLatencies.WithLabelValues("DeleteChatModelConfigsByProvider").Observe(time.Since(start).Seconds())
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteChatModelConfigsByProvider").Inc()
return r0
}
func (m queryMetricsStore) DeleteChatQueuedMessage(ctx context.Context, arg database.DeleteChatQueuedMessageParams) error {
start := time.Now()
r0 := m.s.DeleteChatQueuedMessage(ctx, arg)
@@ -2018,7 +2001,7 @@ func (m queryMetricsStore) GetEnabledChatModelConfigByID(ctx context.Context, id
return r0, r1
}
func (m queryMetricsStore) GetEnabledChatModelConfigs(ctx context.Context) ([]database.ChatModelConfig, error) {
func (m queryMetricsStore) GetEnabledChatModelConfigs(ctx context.Context) ([]database.GetEnabledChatModelConfigsRow, error) {
start := time.Now()
r0, r1 := m.s.GetEnabledChatModelConfigs(ctx)
m.queryLatencies.WithLabelValues("GetEnabledChatModelConfigs").Observe(time.Since(start).Seconds())
+2 -32
View File
@@ -11,7 +11,6 @@ package dbmock
import (
context "context"
sql "database/sql"
json "encoding/json"
reflect "reflect"
time "time"
@@ -194,21 +193,6 @@ func (mr *MockStoreMockRecorder) AutoArchiveInactiveChats(ctx, arg any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoArchiveInactiveChats", reflect.TypeOf((*MockStore)(nil).AutoArchiveInactiveChats), ctx, arg)
}
// BackfillChatModelConfigProvider mocks base method.
func (m *MockStore) BackfillChatModelConfigProvider(ctx context.Context, arg database.BackfillChatModelConfigProviderParams) (sql.Result, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BackfillChatModelConfigProvider", ctx, arg)
ret0, _ := ret[0].(sql.Result)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BackfillChatModelConfigProvider indicates an expected call of BackfillChatModelConfigProvider.
func (mr *MockStoreMockRecorder) BackfillChatModelConfigProvider(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BackfillChatModelConfigProvider", reflect.TypeOf((*MockStore)(nil).BackfillChatModelConfigProvider), ctx, arg)
}
// BackoffChatDiffStatus mocks base method.
func (m *MockStore) BackoffChatDiffStatus(ctx context.Context, arg database.BackoffChatDiffStatusParams) error {
m.ctrl.T.Helper()
@@ -877,20 +861,6 @@ func (mr *MockStoreMockRecorder) DeleteChatModelConfigsByAIProviderID(ctx, aiPro
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatModelConfigsByAIProviderID", reflect.TypeOf((*MockStore)(nil).DeleteChatModelConfigsByAIProviderID), ctx, aiProviderID)
}
// DeleteChatModelConfigsByProvider mocks base method.
func (m *MockStore) DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteChatModelConfigsByProvider", ctx, provider)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteChatModelConfigsByProvider indicates an expected call of DeleteChatModelConfigsByProvider.
func (mr *MockStoreMockRecorder) DeleteChatModelConfigsByProvider(ctx, provider any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatModelConfigsByProvider", reflect.TypeOf((*MockStore)(nil).DeleteChatModelConfigsByProvider), ctx, provider)
}
// DeleteChatQueuedMessage mocks base method.
func (m *MockStore) DeleteChatQueuedMessage(ctx context.Context, arg database.DeleteChatQueuedMessageParams) error {
m.ctrl.T.Helper()
@@ -3733,10 +3703,10 @@ func (mr *MockStoreMockRecorder) GetEnabledChatModelConfigByID(ctx, id any) *gom
}
// GetEnabledChatModelConfigs mocks base method.
func (m *MockStore) GetEnabledChatModelConfigs(ctx context.Context) ([]database.ChatModelConfig, error) {
func (m *MockStore) GetEnabledChatModelConfigs(ctx context.Context) ([]database.GetEnabledChatModelConfigsRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetEnabledChatModelConfigs", ctx)
ret0, _ := ret[0].([]database.ChatModelConfig)
ret0, _ := ret[0].([]database.GetEnabledChatModelConfigsRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-2
View File
@@ -2084,7 +2084,6 @@ func TestPurgeChatDebugRuns(t *testing.T) {
DisplayName: "OpenAI",
})
modelConfig := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
ContextLimit: 8192,
})
@@ -2311,7 +2310,6 @@ func TestDeleteOldChatFiles(t *testing.T) {
DisplayName: "OpenAI",
})
mc := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
ContextLimit: 8192,
})
-5
View File
@@ -1909,7 +1909,6 @@ ALTER SEQUENCE chat_messages_id_seq OWNED BY chat_messages.id;
CREATE TABLE chat_model_configs (
id uuid DEFAULT gen_random_uuid() NOT NULL,
provider text NOT NULL,
model text NOT NULL,
display_name text DEFAULT ''::text NOT NULL,
created_by uuid,
@@ -4625,10 +4624,6 @@ CREATE INDEX idx_chat_model_configs_ai_provider_id ON chat_model_configs USING b
CREATE INDEX idx_chat_model_configs_enabled ON chat_model_configs USING btree (enabled);
CREATE INDEX idx_chat_model_configs_provider ON chat_model_configs USING btree (provider);
CREATE INDEX idx_chat_model_configs_provider_model ON chat_model_configs USING btree (provider, model);
CREATE UNIQUE INDEX idx_chat_model_configs_single_default ON chat_model_configs USING btree ((1)) WHERE ((is_default = true) AND (deleted = false));
CREATE INDEX idx_chat_queued_messages_chat_id ON chat_queued_messages USING btree (chat_id);
@@ -0,0 +1,13 @@
ALTER TABLE chat_model_configs ADD COLUMN provider text;
UPDATE chat_model_configs cmc
SET provider = ap.type::text
FROM ai_providers ap
WHERE ap.id = cmc.ai_provider_id;
UPDATE chat_model_configs SET provider = '' WHERE provider IS NULL;
ALTER TABLE chat_model_configs ALTER COLUMN provider SET NOT NULL;
CREATE INDEX idx_chat_model_configs_provider ON chat_model_configs USING btree (provider);
CREATE INDEX idx_chat_model_configs_provider_model ON chat_model_configs USING btree (provider, model);
@@ -0,0 +1,4 @@
DROP INDEX idx_chat_model_configs_provider;
DROP INDEX idx_chat_model_configs_provider_model;
ALTER TABLE chat_model_configs DROP COLUMN provider;
@@ -0,0 +1,41 @@
-- Soft-deleted chat model config whose provider never had an ai_providers
-- backfill match, so it reaches later migrations with ai_provider_id IS NULL.
--
-- This row exercises the 000534 down migration's
-- `UPDATE ... SET provider = '' WHERE provider IS NULL` sweep: its NULL
-- ai_provider_id means the backfill join leaves provider NULL, and the sweep
-- must populate it before `ALTER COLUMN provider SET NOT NULL`.
--
-- It is inserted at version 000475 (after 000474 dropped the provider foreign
-- key) so the provider value need not reference a chat_providers row, and the
-- 000504/000505 backfill (which matches on `cmc.provider = cp.provider`) skips
-- it. `deleted = TRUE` keeps it out of idx_chat_model_configs_single_default
-- and satisfies chat_model_configs_ai_provider_required_when_active (added in
-- 000505), which permits a NULL ai_provider_id only for deleted rows.
INSERT INTO chat_model_configs (
id,
provider,
model,
display_name,
enabled,
is_default,
deleted,
deleted_at,
context_limit,
compression_threshold,
created_at,
updated_at
) VALUES (
'b3a1d2c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
'legacy-removed',
'legacy-model',
'Legacy Soft Deleted',
FALSE,
FALSE,
TRUE,
'2024-01-01 00:00:00+00',
200000,
70,
'2024-01-01 00:00:00+00',
'2024-01-01 00:00:00+00'
);
-1
View File
@@ -4953,7 +4953,6 @@ type ChatMessage struct {
type ChatModelConfig struct {
ID uuid.UUID `db:"id" json:"id"`
Provider string `db:"provider" json:"provider"`
Model string `db:"model" json:"model"`
DisplayName string `db:"display_name" json:"display_name"`
CreatedBy uuid.NullUUID `db:"created_by" json:"created_by"`
+2 -8
View File
@@ -6,7 +6,6 @@ package database
import (
"context"
"database/sql"
"encoding/json"
"time"
@@ -71,11 +70,6 @@ type sqlcQuerier interface {
// created_at ASC flows through to dbpurge's digest truncation; see
// buildDigestData in dbpurge.go for the tradeoff rationale.
AutoArchiveInactiveChats(ctx context.Context, arg AutoArchiveInactiveChatsParams) ([]AutoArchiveInactiveChatsRow, error)
// old_provider is matched as text; new_provider is also cast to ai_provider_type
// for the EXISTS check against ai_providers.type.
// ai_provider_id IS NOT NULL is defensive; the check constraint already
// enforces that non-deleted rows always have a provider ID.
BackfillChatModelConfigProvider(ctx context.Context, arg BackfillChatModelConfigProviderParams) (sql.Result, error)
BackoffChatDiffStatus(ctx context.Context, arg BackoffChatDiffStatusParams) error
// Deletes heartbeat rows for the supplied (chat_id, runner_id) pairs.
BatchDeleteChatHeartbeats(ctx context.Context, arg BatchDeleteChatHeartbeatsParams) (int64, error)
@@ -150,7 +144,6 @@ type sqlcQuerier interface {
DeleteChatDebugDataByChatID(ctx context.Context, arg DeleteChatDebugDataByChatIDParams) (int64, error)
DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error
DeleteChatModelConfigsByAIProviderID(ctx context.Context, aiProviderID uuid.UUID) error
DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error
DeleteChatQueuedMessage(ctx context.Context, arg DeleteChatQueuedMessageParams) error
// Deletes a queued message, scoped to the parent chat. Returns the
// number of affected rows so callers can detect missing rows without
@@ -449,6 +442,7 @@ type sqlcQuerier interface {
GetChatModelConfigByID(ctx context.Context, id uuid.UUID) (ChatModelConfig, error)
GetChatModelConfigs(ctx context.Context) ([]ChatModelConfig, error)
// Returns all model configurations for telemetry snapshot collection.
// deleted = false guarantees ai_provider_id is non-null, so INNER JOIN is safe.
GetChatModelConfigsForTelemetry(ctx context.Context) ([]GetChatModelConfigsForTelemetryRow, error)
// GetChatPersonalModelOverridesEnabled returns whether users may configure
// personal chat model overrides. It defaults to false when unset.
@@ -539,7 +533,7 @@ type sqlcQuerier interface {
// Providers can be disabled independently of their model configs.
// Check both to ensure the selected config is actually usable.
GetEnabledChatModelConfigByID(ctx context.Context, id uuid.UUID) (ChatModelConfig, error)
GetEnabledChatModelConfigs(ctx context.Context) ([]ChatModelConfig, error)
GetEnabledChatModelConfigs(ctx context.Context) ([]GetEnabledChatModelConfigsRow, error)
GetEnabledMCPServerConfigs(ctx context.Context) ([]MCPServerConfig, error)
// GetExternalAgentTokensByTemplateID returns the auth tokens for all
// non-deleted external agents on the latest build of every running workspace
+27 -52
View File
@@ -1250,7 +1250,6 @@ func TestChatContextHydration(t *testing.T) {
owner := dbgen.User(t, db, database.User{})
_ = dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "openai", DisplayName: "OpenAI"})
modelCfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
UpdatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -1381,7 +1380,6 @@ func TestGetAuthorizedChats(t *testing.T) {
DisplayName: "OpenAI",
})
modelCfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
UpdatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -1645,7 +1643,6 @@ func TestGetAuthorizedChatsACLSharing(t *testing.T) {
dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "openai", DisplayName: "OpenAI"})
modelCfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
UpdatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -1766,7 +1763,6 @@ func TestGetAuthorizedChatsACLSharingGroupACL(t *testing.T) {
dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "openai", DisplayName: "OpenAI"})
modelCfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
UpdatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -1869,7 +1865,6 @@ func TestGetAuthorizedChatsByChatFileIDACLSharing(t *testing.T) {
dbgen.ChatProvider(t, db, database.ChatProvider{Provider: "openai", DisplayName: "OpenAI"})
modelCfg := dbgen.ChatModelConfig(t, db, database.ChatModelConfig{
Provider: "openai",
Model: "test-model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
UpdatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -11097,24 +11092,21 @@ func TestGetEnabledChatModelConfigsUsesAIProviders(t *testing.T) {
params.Enabled = false
})
enabledConfig := dbgen.ChatModelConfig(t, store, database.ChatModelConfig{
Provider: string(enabledProvider.Type),
Model: "openrouter-model-" + uuid.NewString(),
Model: "openrouter-model-" + uuid.NewString(),
AIProviderID: uuid.NullUUID{
UUID: enabledProvider.ID,
Valid: true,
},
})
disabledProviderConfig := dbgen.ChatModelConfig(t, store, database.ChatModelConfig{
Provider: string(disabledProvider.Type),
Model: "vercel-model-" + uuid.NewString(),
Model: "vercel-model-" + uuid.NewString(),
AIProviderID: uuid.NullUUID{
UUID: disabledProvider.ID,
Valid: true,
},
})
disabledModelConfig := dbgen.ChatModelConfig(t, store, database.ChatModelConfig{
Provider: string(enabledProvider.Type),
Model: "disabled-model-" + uuid.NewString(),
Model: "disabled-model-" + uuid.NewString(),
AIProviderID: uuid.NullUUID{
UUID: enabledProvider.ID,
Valid: true,
@@ -11125,14 +11117,14 @@ func TestGetEnabledChatModelConfigsUsesAIProviders(t *testing.T) {
configs, err := store.GetEnabledChatModelConfigs(ctx)
require.NoError(t, err)
require.True(t, slices.ContainsFunc(configs, func(config database.ChatModelConfig) bool {
return config.ID == enabledConfig.ID
require.True(t, slices.ContainsFunc(configs, func(row database.GetEnabledChatModelConfigsRow) bool {
return row.ChatModelConfig.ID == enabledConfig.ID
}))
require.False(t, slices.ContainsFunc(configs, func(config database.ChatModelConfig) bool {
return config.ID == disabledProviderConfig.ID
require.False(t, slices.ContainsFunc(configs, func(row database.GetEnabledChatModelConfigsRow) bool {
return row.ChatModelConfig.ID == disabledProviderConfig.ID
}))
require.False(t, slices.ContainsFunc(configs, func(config database.ChatModelConfig) bool {
return config.ID == disabledModelConfig.ID
require.False(t, slices.ContainsFunc(configs, func(row database.GetEnabledChatModelConfigsRow) bool {
return row.ChatModelConfig.ID == disabledModelConfig.ID
}))
config, err := store.GetEnabledChatModelConfigByID(ctx, enabledConfig.ID)
@@ -11150,16 +11142,16 @@ func insertChatModelConfigForTest(
ctx context.Context,
t testing.TB,
store database.Store,
providerType string,
params database.InsertChatModelConfigParams,
) (database.ChatModelConfig, error) {
t.Helper()
if params.AIProviderID.Valid {
return store.InsertChatModelConfig(ctx, params)
}
providerName := params.Provider
providerName := providerType
if providerName == "" {
providerName = "openai"
params.Provider = providerName
}
providers, err := store.GetAIProviders(ctx, database.GetAIProvidersParams{IncludeDisabled: true})
if err != nil {
@@ -11198,8 +11190,7 @@ func TestInsertChatMessages(t *testing.T) {
) database.ChatModelConfig {
t.Helper()
modelConfig, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: provider,
modelConfig, err := insertChatModelConfigForTest(ctx, t, store, provider, database.InsertChatModelConfigParams{
Model: model,
DisplayName: displayName,
CreatedBy: uuid.NullUUID{UUID: userID, Valid: true},
@@ -11410,8 +11401,7 @@ func TestGetChatMessagesForPromptByChatID(t *testing.T) {
APIKey: "test-key",
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, "openai", database.InsertChatModelConfigParams{
AIProviderID: uuid.NullUUID{UUID: provider.ID, Valid: true},
Model: "test-model",
DisplayName: "Test Model",
@@ -11786,8 +11776,7 @@ func TestChatPinOrderQueries(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(bg, t, db, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(bg, t, db, "openai", database.InsertChatModelConfigParams{
Model: "test-model",
DisplayName: "Test Model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -11966,8 +11955,7 @@ func TestChatPinOrderConstraints(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(bg, t, db, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(bg, t, db, "openai", database.InsertChatModelConfigParams{
Model: "test-model",
DisplayName: "Test Model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -12058,8 +12046,7 @@ func TestChatLabels(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, "openai", database.InsertChatModelConfigParams{
Model: "test-model",
DisplayName: "Test Model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -12357,8 +12344,7 @@ func TestUpdateChatLastTurnSummary(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(ctx, t, db, "openai", database.InsertChatModelConfigParams{
Model: "test-model",
DisplayName: "Test Model",
CreatedBy: uuid.NullUUID{UUID: owner.ID, Valid: true},
@@ -12494,8 +12480,7 @@ func TestDeleteChatDebugDataAfterMessageIDIncludesTriggeredRuns(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -12686,8 +12671,7 @@ func TestDeleteChatDebugDataAfterMessageIDStepLevelFieldBoundariesAndNulls(t *te
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -12943,8 +12927,7 @@ func TestFinalizeStaleChatDebugRows(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13381,8 +13364,7 @@ func TestChatDebugSQLGuards(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13514,8 +13496,7 @@ func TestChatDebugRunCOALESCEPreservation(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13628,8 +13609,7 @@ func TestChatDebugStepCOALESCEPreservation(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13752,8 +13732,7 @@ func TestDeleteChatDebugDataAfterMessageIDNullMessagesSurvive(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13849,8 +13828,7 @@ func TestDeleteChatDebugDataAfterMessageIDStartedBeforeFiltersNewerRuns(t *testi
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -13960,8 +13938,7 @@ func TestDeleteChatDebugDataByChatIDStartedBeforeFiltersNewerRuns(t *testing.T)
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: providerName,
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, providerName, database.InsertChatModelConfigParams{
Model: modelName,
DisplayName: "Debug Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
@@ -14045,7 +14022,6 @@ func TestGetChatsFilter(t *testing.T) {
}, "test-key")
modelCfg, err := store.InsertChatModelConfig(ctx, database.InsertChatModelConfigParams{
Provider: "openai",
AIProviderID: uuid.NullUUID{UUID: provider.ID, Valid: true},
Model: "test-model-" + uuid.NewString(),
DisplayName: "Test Model",
@@ -14343,8 +14319,7 @@ func TestChatHasUnread(t *testing.T) {
CentralApiKeyEnabled: true,
})
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, database.InsertChatModelConfigParams{
Provider: "openai",
modelCfg, err := insertChatModelConfigForTest(ctx, t, store, "openai", database.InsertChatModelConfigParams{
Model: "test-model-" + uuid.NewString(),
DisplayName: "Test Model",
CreatedBy: uuid.NullUUID{UUID: user.ID, Valid: true},
+65 -114
View File
@@ -5025,37 +5025,6 @@ func (q *sqlQuerier) InsertChatFile(ctx context.Context, arg InsertChatFileParam
return i, err
}
const backfillChatModelConfigProvider = `-- name: BackfillChatModelConfigProvider :execresult
UPDATE
chat_model_configs
SET
provider = $1::text,
updated_at = NOW()
WHERE
provider = $2::text
AND deleted = FALSE
AND ai_provider_id IS NOT NULL
AND EXISTS (
SELECT 1 FROM ai_providers
WHERE id = chat_model_configs.ai_provider_id
AND type = $1::ai_provider_type
AND deleted = FALSE
)
`
type BackfillChatModelConfigProviderParams struct {
NewProvider string `db:"new_provider" json:"new_provider"`
OldProvider string `db:"old_provider" json:"old_provider"`
}
// old_provider is matched as text; new_provider is also cast to ai_provider_type
// for the EXISTS check against ai_providers.type.
// ai_provider_id IS NOT NULL is defensive; the check constraint already
// enforces that non-deleted rows always have a provider ID.
func (q *sqlQuerier) BackfillChatModelConfigProvider(ctx context.Context, arg BackfillChatModelConfigProviderParams) (sql.Result, error) {
return q.db.ExecContext(ctx, backfillChatModelConfigProvider, arg.NewProvider, arg.OldProvider)
}
const deleteChatModelConfigByID = `-- name: DeleteChatModelConfigByID :exec
UPDATE
chat_model_configs
@@ -5089,26 +5058,9 @@ func (q *sqlQuerier) DeleteChatModelConfigsByAIProviderID(ctx context.Context, a
return err
}
const deleteChatModelConfigsByProvider = `-- name: DeleteChatModelConfigsByProvider :exec
UPDATE
chat_model_configs
SET
deleted = TRUE,
deleted_at = NOW(),
updated_at = NOW()
WHERE
provider = $1::text
AND deleted = FALSE
`
func (q *sqlQuerier) DeleteChatModelConfigsByProvider(ctx context.Context, provider string) error {
_, err := q.db.ExecContext(ctx, deleteChatModelConfigsByProvider, provider)
return err
}
const getChatModelConfigByID = `-- name: GetChatModelConfigByID :one
SELECT
id, provider, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
id, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
FROM
chat_model_configs
WHERE
@@ -5121,7 +5073,6 @@ func (q *sqlQuerier) GetChatModelConfigByID(ctx context.Context, id uuid.UUID) (
var i ChatModelConfig
err := row.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -5142,16 +5093,18 @@ func (q *sqlQuerier) GetChatModelConfigByID(ctx context.Context, id uuid.UUID) (
const getChatModelConfigs = `-- name: GetChatModelConfigs :many
SELECT
id, provider, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
cmc.id, cmc.model, cmc.display_name, cmc.created_by, cmc.updated_by, cmc.enabled, cmc.is_default, cmc.deleted, cmc.deleted_at, cmc.created_at, cmc.updated_at, cmc.context_limit, cmc.compression_threshold, cmc.options, cmc.ai_provider_id
FROM
chat_model_configs
chat_model_configs cmc
LEFT JOIN
ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE
deleted = FALSE
cmc.deleted = FALSE
ORDER BY
provider ASC,
model ASC,
updated_at DESC,
id DESC
ap.type::text ASC,
cmc.model ASC,
cmc.updated_at DESC,
cmc.id DESC
`
func (q *sqlQuerier) GetChatModelConfigs(ctx context.Context) ([]ChatModelConfig, error) {
@@ -5165,7 +5118,6 @@ func (q *sqlQuerier) GetChatModelConfigs(ctx context.Context) ([]ChatModelConfig
var i ChatModelConfig
if err := rows.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -5196,7 +5148,7 @@ func (q *sqlQuerier) GetChatModelConfigs(ctx context.Context) ([]ChatModelConfig
const getDefaultChatModelConfig = `-- name: GetDefaultChatModelConfig :one
SELECT
id, provider, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
id, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
FROM
chat_model_configs
WHERE
@@ -5209,7 +5161,6 @@ func (q *sqlQuerier) GetDefaultChatModelConfig(ctx context.Context) (ChatModelCo
var i ChatModelConfig
err := row.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -5230,7 +5181,7 @@ func (q *sqlQuerier) GetDefaultChatModelConfig(ctx context.Context) (ChatModelCo
const getEnabledChatModelConfigByID = `-- name: GetEnabledChatModelConfigByID :one
SELECT
cmc.id, cmc.provider, cmc.model, cmc.display_name, cmc.created_by, cmc.updated_by, cmc.enabled, cmc.is_default, cmc.deleted, cmc.deleted_at, cmc.created_at, cmc.updated_at, cmc.context_limit, cmc.compression_threshold, cmc.options, cmc.ai_provider_id
cmc.id, cmc.model, cmc.display_name, cmc.created_by, cmc.updated_by, cmc.enabled, cmc.is_default, cmc.deleted, cmc.deleted_at, cmc.created_at, cmc.updated_at, cmc.context_limit, cmc.compression_threshold, cmc.options, cmc.ai_provider_id
FROM
chat_model_configs cmc
JOIN
@@ -5250,7 +5201,6 @@ func (q *sqlQuerier) GetEnabledChatModelConfigByID(ctx context.Context, id uuid.
var i ChatModelConfig
err := row.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -5271,7 +5221,8 @@ func (q *sqlQuerier) GetEnabledChatModelConfigByID(ctx context.Context, id uuid.
const getEnabledChatModelConfigs = `-- name: GetEnabledChatModelConfigs :many
SELECT
cmc.id, cmc.provider, cmc.model, cmc.display_name, cmc.created_by, cmc.updated_by, cmc.enabled, cmc.is_default, cmc.deleted, cmc.deleted_at, cmc.created_at, cmc.updated_at, cmc.context_limit, cmc.compression_threshold, cmc.options, cmc.ai_provider_id
cmc.id, cmc.model, cmc.display_name, cmc.created_by, cmc.updated_by, cmc.enabled, cmc.is_default, cmc.deleted, cmc.deleted_at, cmc.created_at, cmc.updated_at, cmc.context_limit, cmc.compression_threshold, cmc.options, cmc.ai_provider_id,
ap.type::text AS provider
FROM
chat_model_configs cmc
JOIN
@@ -5282,38 +5233,43 @@ WHERE
AND ap.enabled = TRUE
AND ap.deleted = FALSE
ORDER BY
cmc.provider ASC,
ap.type::text ASC,
cmc.model ASC,
cmc.updated_at DESC,
cmc.id DESC
`
func (q *sqlQuerier) GetEnabledChatModelConfigs(ctx context.Context) ([]ChatModelConfig, error) {
type GetEnabledChatModelConfigsRow struct {
ChatModelConfig ChatModelConfig `db:"chat_model_config" json:"chat_model_config"`
Provider string `db:"provider" json:"provider"`
}
func (q *sqlQuerier) GetEnabledChatModelConfigs(ctx context.Context) ([]GetEnabledChatModelConfigsRow, error) {
rows, err := q.db.QueryContext(ctx, getEnabledChatModelConfigs)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ChatModelConfig
var items []GetEnabledChatModelConfigsRow
for rows.Next() {
var i ChatModelConfig
var i GetEnabledChatModelConfigsRow
if err := rows.Scan(
&i.ID,
&i.ChatModelConfig.ID,
&i.ChatModelConfig.Model,
&i.ChatModelConfig.DisplayName,
&i.ChatModelConfig.CreatedBy,
&i.ChatModelConfig.UpdatedBy,
&i.ChatModelConfig.Enabled,
&i.ChatModelConfig.IsDefault,
&i.ChatModelConfig.Deleted,
&i.ChatModelConfig.DeletedAt,
&i.ChatModelConfig.CreatedAt,
&i.ChatModelConfig.UpdatedAt,
&i.ChatModelConfig.ContextLimit,
&i.ChatModelConfig.CompressionThreshold,
&i.ChatModelConfig.Options,
&i.ChatModelConfig.AIProviderID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
&i.UpdatedBy,
&i.Enabled,
&i.IsDefault,
&i.Deleted,
&i.DeletedAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.ContextLimit,
&i.CompressionThreshold,
&i.Options,
&i.AIProviderID,
); err != nil {
return nil, err
}
@@ -5330,7 +5286,6 @@ func (q *sqlQuerier) GetEnabledChatModelConfigs(ctx context.Context) ([]ChatMode
const insertChatModelConfig = `-- name: InsertChatModelConfig :one
INSERT INTO chat_model_configs (
provider,
model,
display_name,
created_by,
@@ -5344,22 +5299,20 @@ INSERT INTO chat_model_configs (
) VALUES (
$1::text,
$2::text,
$3::text,
$3::uuid,
$4::uuid,
$5::uuid,
$5::boolean,
$6::boolean,
$7::boolean,
$8::bigint,
$9::integer,
$10::jsonb,
$11::uuid
$7::bigint,
$8::integer,
$9::jsonb,
$10::uuid
)
RETURNING
id, provider, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
id, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
`
type InsertChatModelConfigParams struct {
Provider string `db:"provider" json:"provider"`
Model string `db:"model" json:"model"`
DisplayName string `db:"display_name" json:"display_name"`
CreatedBy uuid.NullUUID `db:"created_by" json:"created_by"`
@@ -5374,7 +5327,6 @@ type InsertChatModelConfigParams struct {
func (q *sqlQuerier) InsertChatModelConfig(ctx context.Context, arg InsertChatModelConfigParams) (ChatModelConfig, error) {
row := q.db.QueryRowContext(ctx, insertChatModelConfig,
arg.Provider,
arg.Model,
arg.DisplayName,
arg.CreatedBy,
@@ -5389,7 +5341,6 @@ func (q *sqlQuerier) InsertChatModelConfig(ctx context.Context, arg InsertChatMo
var i ChatModelConfig
err := row.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -5428,26 +5379,24 @@ const updateChatModelConfig = `-- name: UpdateChatModelConfig :one
UPDATE
chat_model_configs
SET
provider = $1::text,
model = $2::text,
display_name = $3::text,
updated_by = $4::uuid,
enabled = $5::boolean,
is_default = $6::boolean,
context_limit = $7::bigint,
compression_threshold = $8::integer,
options = $9::jsonb,
ai_provider_id = $10::uuid,
model = $1::text,
display_name = $2::text,
updated_by = $3::uuid,
enabled = $4::boolean,
is_default = $5::boolean,
context_limit = $6::bigint,
compression_threshold = $7::integer,
options = $8::jsonb,
ai_provider_id = $9::uuid,
updated_at = NOW()
WHERE
id = $11::uuid
id = $10::uuid
AND deleted = FALSE
RETURNING
id, provider, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
id, model, display_name, created_by, updated_by, enabled, is_default, deleted, deleted_at, created_at, updated_at, context_limit, compression_threshold, options, ai_provider_id
`
type UpdateChatModelConfigParams struct {
Provider string `db:"provider" json:"provider"`
Model string `db:"model" json:"model"`
DisplayName string `db:"display_name" json:"display_name"`
UpdatedBy uuid.NullUUID `db:"updated_by" json:"updated_by"`
@@ -5462,7 +5411,6 @@ type UpdateChatModelConfigParams struct {
func (q *sqlQuerier) UpdateChatModelConfig(ctx context.Context, arg UpdateChatModelConfigParams) (ChatModelConfig, error) {
row := q.db.QueryRowContext(ctx, updateChatModelConfig,
arg.Provider,
arg.Model,
arg.DisplayName,
arg.UpdatedBy,
@@ -5477,7 +5425,6 @@ func (q *sqlQuerier) UpdateChatModelConfig(ctx context.Context, arg UpdateChatMo
var i ChatModelConfig
err := row.Scan(
&i.ID,
&i.Provider,
&i.Model,
&i.DisplayName,
&i.CreatedBy,
@@ -6967,7 +6914,7 @@ const getChatCostPerModel = `-- name: GetChatCostPerModel :many
SELECT
cmc.id AS model_config_id,
cmc.display_name,
cmc.provider,
COALESCE(ap.type::text, '')::text AS provider,
cmc.model,
COALESCE(SUM(cm.total_cost_micros), 0)::bigint AS total_cost_micros,
COUNT(*) FILTER (
@@ -6988,13 +6935,15 @@ JOIN
chats c ON c.id = cm.chat_id
JOIN
chat_model_configs cmc ON cmc.id = cm.model_config_id
LEFT JOIN
ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE
c.owner_id = $1::uuid
AND cm.role = 'assistant'
AND cm.created_at >= $2::timestamptz
AND cm.created_at < $3::timestamptz
GROUP BY
cmc.id, cmc.display_name, cmc.provider, cmc.model
cmc.id, cmc.display_name, ap.type, cmc.model
ORDER BY
total_cost_micros DESC
`
@@ -7960,9 +7909,10 @@ func (q *sqlQuerier) GetChatMessagesForPromptByChatID(ctx context.Context, chatI
}
const getChatModelConfigsForTelemetry = `-- name: GetChatModelConfigsForTelemetry :many
SELECT id, provider, model, context_limit, enabled, is_default
FROM chat_model_configs
WHERE deleted = false
SELECT cmc.id, ap.type::text AS provider, cmc.model, cmc.context_limit, cmc.enabled, cmc.is_default
FROM chat_model_configs cmc
JOIN ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE cmc.deleted = false
`
type GetChatModelConfigsForTelemetryRow struct {
@@ -7975,6 +7925,7 @@ type GetChatModelConfigsForTelemetryRow struct {
}
// Returns all model configurations for telemetry snapshot collection.
// deleted = false guarantees ai_provider_id is non-null, so INNER JOIN is safe.
func (q *sqlQuerier) GetChatModelConfigsForTelemetry(ctx context.Context) ([]GetChatModelConfigsForTelemetryRow, error) {
rows, err := q.db.QueryContext(ctx, getChatModelConfigsForTelemetry)
if err != nil {
+12 -44
View File
@@ -18,20 +18,23 @@ WHERE
-- name: GetChatModelConfigs :many
SELECT
*
cmc.*
FROM
chat_model_configs
chat_model_configs cmc
LEFT JOIN
ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE
deleted = FALSE
cmc.deleted = FALSE
ORDER BY
provider ASC,
model ASC,
updated_at DESC,
id DESC;
ap.type::text ASC,
cmc.model ASC,
cmc.updated_at DESC,
cmc.id DESC;
-- name: GetEnabledChatModelConfigs :many
SELECT
cmc.*
sqlc.embed(cmc),
ap.type::text AS provider
FROM
chat_model_configs cmc
JOIN
@@ -42,7 +45,7 @@ WHERE
AND ap.enabled = TRUE
AND ap.deleted = FALSE
ORDER BY
cmc.provider ASC,
ap.type::text ASC,
cmc.model ASC,
cmc.updated_at DESC,
cmc.id DESC;
@@ -65,7 +68,6 @@ WHERE
-- name: InsertChatModelConfig :one
INSERT INTO chat_model_configs (
provider,
model,
display_name,
created_by,
@@ -77,7 +79,6 @@ INSERT INTO chat_model_configs (
options,
ai_provider_id
) VALUES (
@provider::text,
@model::text,
@display_name::text,
sqlc.narg('created_by')::uuid,
@@ -96,7 +97,6 @@ RETURNING
UPDATE
chat_model_configs
SET
provider = @provider::text,
model = @model::text,
display_name = @display_name::text,
updated_by = sqlc.narg('updated_by')::uuid,
@@ -133,38 +133,6 @@ SET
WHERE
id = @id::uuid;
-- name: DeleteChatModelConfigsByProvider :exec
UPDATE
chat_model_configs
SET
deleted = TRUE,
deleted_at = NOW(),
updated_at = NOW()
WHERE
provider = @provider::text
AND deleted = FALSE;
-- name: BackfillChatModelConfigProvider :execresult
-- old_provider is matched as text; new_provider is also cast to ai_provider_type
-- for the EXISTS check against ai_providers.type.
-- ai_provider_id IS NOT NULL is defensive; the check constraint already
-- enforces that non-deleted rows always have a provider ID.
UPDATE
chat_model_configs
SET
provider = @new_provider::text,
updated_at = NOW()
WHERE
provider = @old_provider::text
AND deleted = FALSE
AND ai_provider_id IS NOT NULL
AND EXISTS (
SELECT 1 FROM ai_providers
WHERE id = chat_model_configs.ai_provider_id
AND type = @new_provider::ai_provider_type
AND deleted = FALSE
);
-- name: DeleteChatModelConfigsByAIProviderID :exec
UPDATE
chat_model_configs
+9 -5
View File
@@ -2220,7 +2220,7 @@ WHERE
SELECT
cmc.id AS model_config_id,
cmc.display_name,
cmc.provider,
COALESCE(ap.type::text, '')::text AS provider,
cmc.model,
COALESCE(SUM(cm.total_cost_micros), 0)::bigint AS total_cost_micros,
COUNT(*) FILTER (
@@ -2241,13 +2241,15 @@ JOIN
chats c ON c.id = cm.chat_id
JOIN
chat_model_configs cmc ON cmc.id = cm.model_config_id
LEFT JOIN
ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE
c.owner_id = @owner_id::uuid
AND cm.role = 'assistant'
AND cm.created_at >= @start_date::timestamptz
AND cm.created_at < @end_date::timestamptz
GROUP BY
cmc.id, cmc.display_name, cmc.provider, cmc.model
cmc.id, cmc.display_name, ap.type, cmc.model
ORDER BY
total_cost_micros DESC;
@@ -2580,9 +2582,11 @@ GROUP BY cm.chat_id;
-- name: GetChatModelConfigsForTelemetry :many
-- Returns all model configurations for telemetry snapshot collection.
SELECT id, provider, model, context_limit, enabled, is_default
FROM chat_model_configs
WHERE deleted = false;
-- deleted = false guarantees ai_provider_id is non-null, so INNER JOIN is safe.
SELECT cmc.id, ap.type::text AS provider, cmc.model, cmc.context_limit, cmc.enabled, cmc.is_default
FROM chat_model_configs cmc
JOIN ai_providers ap ON ap.id = cmc.ai_provider_id
WHERE cmc.deleted = false;
-- name: GetActiveChatsByAgentID :many
SELECT *
FROM chats_expanded