Files
coder/coderd/ai_providers_backfill.go
T
Mathias Fredriksson 047c47495b 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
2026-07-01 15:59:55 +03:00

69 lines
2.2 KiB
Go

package coderd
import (
"context"
"database/sql"
"errors"
"cdr.dev/slog/v3"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/database/dbauthz"
)
// BackfillBedrockProviderType promotes legacy ai_providers rows stored as
// type=anthropic with Bedrock settings to type=bedrock. Must run after newAPI
// so options.Database is dbcrypt-wrapped. Idempotent; errors are logged and
// startup continues.
func BackfillBedrockProviderType(ctx context.Context, db database.Store, logger slog.Logger) {
//nolint:gocritic // Startup-only backfill; no user actor is present.
sysCtx := dbauthz.AsSystemRestricted(ctx)
providers, err := db.GetAIProviders(sysCtx, database.GetAIProvidersParams{
IncludeDeleted: false,
IncludeDisabled: true,
})
if err != nil {
logger.Error(ctx, "backfill bedrock provider type: list providers", slog.Error(err))
return
}
var promoted int
for _, provider := range providers {
if provider.Type != database.AIProviderTypeAnthropic {
continue
}
settings, err := db2sdk.AIProviderSettings(provider.Settings)
if err != nil {
logger.Warn(ctx, "backfill bedrock provider type: skip provider with unparsable settings",
slog.F("provider_id", provider.ID), slog.Error(err))
continue
}
if settings.Bedrock == nil {
continue
}
_, err = db.UpdateAIProvider(sysCtx, database.UpdateAIProviderParams{
ID: provider.ID,
Type: database.AIProviderTypeBedrock,
DisplayName: provider.DisplayName,
Enabled: provider.Enabled,
BaseUrl: provider.BaseUrl,
Settings: provider.Settings,
// SettingsKeyID is re-set by the dbcrypt wrapper on write.
SettingsKeyID: sql.NullString{},
})
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
logger.Debug(ctx, "backfill bedrock provider type: provider deleted during backfill",
slog.F("provider_id", provider.ID))
continue
}
logger.Error(ctx, "backfill bedrock provider type: provider update failed and will re-attempt on next server startup",
slog.F("provider_id", provider.ID), slog.Error(err))
continue
}
promoted++
}
if promoted > 0 {
logger.Info(ctx, "backfilled bedrock provider types", slog.F("count", promoted))
}
}