🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

refactor!: drop chat_model_configs provider column - #26877

Merged
mafredri merged 2 commits into
mainfrom
mathias/codagt-599-remove-chat-model-config-provider
Jul 1, 2026
Merged

refactor!: drop chat_model_configs provider column#26877
mafredri merged 2 commits into
mainfrom
mathias/codagt-599-remove-chat-model-config-provider

Conversation

@mafredri

@mafredri mafredri commented Jun 30, 2026

Copy link
Copy Markdown
Member

For release managers

Breaking change (experimental Coder Agents API). The experimental Coder Agents model-configuration API no longer returns provider; clients get the provider from ai_provider_id instead. Experimental APIs carry no stability guarantee, so there is no deprecation period.

Upgrade with coderd scaled to zero. This release runs a database migration that older coderd versions cannot read. In a rolling upgrade, a replica still on the old version can interrupt an in-progress Coder Agents conversation once the migration runs. Scale coderd to zero before upgrading to avoid interrupting active use. Rollback is unsupported; snapshot the database first.

Changelog entry:


Removes the denormalized provider string column from chat_model_configs. Provider identity now has a single source of truth: the ai_providers row reached through chat_model_configs.ai_provider_id. Every surface that needs a provider type derives it from ai_providers.type instead of a stored copy that a startup backfill had to keep in sync.

The stored string was already redundant. The chat_model_configs_ai_provider_required_when_active CHECK makes ai_provider_id mandatory for non-deleted rows, and routing already preferred ai_providers.type, reading the stored string only in an !AIProviderID.Valid branch the constraint makes unreachable for active rows. Those branches now return sql.ErrNoRows.

What changed

  • Backend stops reading the column: routing, prompt build, debug-run provenance, listing/availability, and the cost breakdown all derive the provider type from the linked ai_providers row. ORDER BY provider is retargeted to ap.type.
  • Telemetry keeps emitting provider, now sourced from ai_providers.type via a JOIN, so the BigQuery chat_model_configs.provider column and the Nexus dashboards that read it keep working unchanged.
  • The experimental SDK/HTTP ChatModelConfig drops provider and promotes ai_provider_id to a required field. Consumers resolve provider type from ai_provider_id plus GET /api/experimental/chats/providers. The experimental API carries no stability guarantee, which is what makes this acceptable.
  • A numbered migration (000534) drops the column and its two indexes. The obsolete BackfillChatModelConfigProviderStrings startup backfill and the unused DeleteChatModelConfigsByProvider query are deleted; BackfillBedrockProviderType stays.
  • Frontend resolves provider type from ai_provider_id through a providerTypeByID map and drops the dead provider:model legacy reference shims.

Upgrade contract

This ships in a single release with no compatibility window. Production reads the column through SELECT * positional scans, so a pre-drop binary fails config reads the instant the column disappears. Operators must scale to zero before upgrading. Coder does not support rollbacks.

Notes for reviewers

  • Frontend provider source is split by audience, intentionally. The plan named chatProviderConfigs() as the lookup source everywhere, but that endpoint (GET /api/v2/ai/providers) is owner-only, while the model selector serves all users. Using it everywhere would empty the selector for non-owners. Owner-only admin pages keep chatProviderConfigs(); user-facing chat pages use the all-users userChatProviderConfigs(). This preserves the selector for non-owners.
  • Telemetry and the API diverge by design. Telemetry keeps a derived provider because the analytics pipeline has no ai_provider_id/type dimension and both ends are owned internally; the API has a documented alternative.
  • Two Storybook interaction tests (Tool > MCP Tool Completed, AgentChatPageView > Scroll To Bottom Button Works With Inverse Scroll) fail in the local sandbox. Verified they fail identically on unmodified main and share no code with this change; they are an environment limitation, left for CI to validate.
Implementation plan (source of truth for this change)

Remove provider from chat_model_configs (CODAGT-599)

Direction (approval surface)

Outcome

The denormalized provider string is gone from Agents storage. Provider
identity for a model config has a single source of truth: the ai_providers
row reached through chat_model_configs.ai_provider_id. Every surface that
still needs a provider type derives it from ai_providers.type instead of
reading a stored copy.

Observable end state

  • chat_model_configs has no provider column, and no idx_chat_model_configs_provider
    or idx_chat_model_configs_provider_model index.
  • The experimental HTTP/SDK ChatModelConfig response has no provider field.
    Consumers resolve provider type from ai_provider_id plus
    GET /api/experimental/chats/providers (id -> provider type).
  • The Agents web UI selects icons, groups, sorts, and resolves legacy model
    references using the provider type derived from ai_provider_id, not a stored
    provider string.
  • Telemetry still emits provider for each model config, now computed from
    ai_providers.type at snapshot time. The BigQuery chat_model_configs.provider
    column and the Nexus dashboards that read it keep working unchanged.
  • The startup backfill BackfillChatModelConfigProviderStrings and the unused
    DeleteChatModelConfigsByProvider query are deleted. BackfillBedrockProviderType
    remains.
  • The API/SDK response makes ai_provider_id a required (non-omitempty) field
    for the configs it returns, which are always active, so consumers rely on it
    as the sole provider identity.
  • The coder-org repositories that read provider have a notice comment
    describing the removal and the ai_provider_id replacement path.

Recommended direction

Remove the column and every dependence on the stored string in a single
release, treating the change as one coordinated unit across schema, backend,
SDK, and frontend. Keep provider alive only where a consumer has a verified
dependency and no ai_provider_id/type dimension to derive from. That
condition holds for exactly one surface: the telemetry analytics projection.

Reason

Provider type already lives authoritatively in ai_providers.type, reachable
on every active row through ai_provider_id, which the CHECK constraint
chat_model_configs_ai_provider_required_when_active makes mandatory for
non-deleted rows (coderd/database/dump.sql:1927). Routing already prefers
ai_providers.type and reads the stored string only in an
!AIProviderID.Valid branch that the constraint makes unreachable for active
rows (coderd/x/chatd/model_routing_direct.go:85-92, coderd/x/chatd/subagent.go:536-553).
The stored provider is therefore a duplicate the system maintains with a
startup backfill but no longer needs as a source of truth.

The telemetry projection is kept because it is the only provider-identity
dimension in the analytics pipeline: the BigQuery schema bqChatModelConfig
(coder/coder-telemetry-server, convert.go) carries provider but no
ai_provider_id or type, and Nexus queries read coder-telemetry.coder.chat_model_configs.provider
directly (coder/nexus, util/sql/coder_agents_provider_trends.sql and four
sibling queries). Deriving that field from ai_providers.type preserves the
analytics contract while removing the stored duplication, so the projection is
computed from the source of truth rather than a denormalized copy.

High-level shape

  1. Backend stops reading the column: derive provider type from the loaded
    ai_providers row at each read site, retarget ORDER BY provider, and
    source the telemetry provider from a JOIN.
  2. SDK drops the response field; regenerate codersdk and the TypeScript
    types.
  3. Frontend moves icon, grouping, sort, and legacy-reference logic onto
    ai_provider_id, including fixtures and stories.
  4. A numbered migration drops the column and its indexes.
  5. Delete the obsolete backfill and the dead delete query, regenerate database
    code.
  6. Post a notice comment on the coder-org repositories that read the field.

Ruled-out directions

  • Two-step expand/contract (stop reads in release N, drop column in N+1).
    Ruled out by operator decision to ship in one release on the assumption of
    scale-to-zero cutovers. Recorded as a risk below, since production reads the
    column through SELECT * positional scans, so a running pre-drop binary
    fails those scans the instant the column disappears.
  • Keep the column, only stop depending on it. Ruled out: it does not
    satisfy the issue goal and leaves a dead column plus an obsolete backfill in
    place.
  • Keep a derived provider in the HTTP/SDK response. Ruled out by operator
    decision. coder-mobile-android is not an officially supported project, so
    its dependence does not justify retaining the API field; it gets a notice
    comment instead.
  • Drop provider from telemetry too and migrate the analytics pipeline.
    Ruled out by operator decision (kept derived). It would require coordinated
    changes in coder-telemetry-server (BigQuery schema) and nexus (five SQL
    queries) plus a story for historical BigQuery rows that only have provider.

Decisions already made

  • Single release, relying on scale-to-zero upgrade cutovers.
  • No compatibility guarantees during the migration window. Operators must scale
    to zero before upgrading. This is the accepted upgrade contract for the
    change.
  • ai_provider_id becomes a required (non-omitempty) field in the API/SDK and
    TypeScript response, since those endpoints return only active configs and
    active configs always have it.
  • API/SDK provider field is removed end-to-end; consumers migrate to
    ai_provider_id + the providers endpoint.
  • Frontend migrates onto ai_provider_id end-to-end.
  • Telemetry keeps provider, sourced from ai_providers.type.
  • External coder-org consumers get at most a notice comment, not code changes
    or a coordinated release.

Assumptions

  • Every active (non-deleted) chat_model_configs row has a non-null
    ai_provider_id. Verified at the database level: the CHECK constraint
    chat_model_configs_ai_provider_required_when_active was added without
    NOT VALID (migration 000505), so Postgres validated existing rows on add
    and the migration could not have succeeded with a non-conforming active row.
    The single production insert path also requires it (coderd/exp_chats.go:6920-6923),
    the update path never nulls it (coderd/exp_chats.go:7140-7162), and the FK
    has no ON DELETE SET NULL while ai_providers soft-deletes
    (coderd/database/dump.sql:5004). Soft-deleted rows may have null
    ai_provider_id but are never returned by the model-config endpoints.
  • No persisted model reference uses a provider:model string. Verified:
    chats.last_model_config_id is a uuid column (coderd/database/models.go:4786,
    @last_model_config_id::uuid; TS typesGenerated.ts:1563) resolved as a UUID
    (coderd/x/chatd/chatd.go:4425-4442), and ModelSelectorOption.id is always
    the config UUID. The frontend provider:model matching is dead and is removed
    in Part 4, not preserved.
  • A consumer can recover provider type from ai_provider_id. Verified:
    GET /api/experimental/chats/providers returns ChatProviderConfig{ID, Provider}
    where Provider is the type (codersdk/chats.go:1167-1180).

Constraints

Tradeoffs

  • Telemetry and the stored column diverge in treatment: telemetry keeps a
    derived provider, the API does not. This is intentional. Telemetry has a
    downstream consumer with no alternative dimension and both ends are owned
    internally; the API has a documented alternative (ai_provider_id + providers
    endpoint).

Risks affecting the direction

  • Rolling-upgrade skew (accepted). Production reads provider via
    SELECT *, so a pre-drop replica still serving during a rolling upgrade would
    fail config reads once the column is gone. Accepted: the change provides no
    compatibility guarantees during migration and requires operators to scale to
    zero before upgrading. Coder does not support rollbacks
    (docs/install/upgrade.md). This is a documented operating contract, not an
    open mitigation.
  • Frontend rows without ai_provider_id. Today the TypeScript type marks it
    optional and fixtures omit it. Making the response field required removes the
    runtime case for active configs (backend invariants guarantee the FK), so the
    residual work is mechanical: update fixtures and stories that omit it and
    remove the now-dead optional-handling branches rather than leaving them to
    silently fall back to a removed field.

Deferred decisions

  • Whether to tighten ai_provider_id to strictly NOT NULL (dropping the
    deleted = TRUE OR ... exemption). Out of scope here; soft-deleted rows
    legitimately hold null.
  • Whether to later migrate the telemetry/Nexus pipeline off the provider
    string onto a provider-type dimension. Deferred; not required by this change.

Implementation detail (preserves the approved direction)

Line numbers are anchors as of the researched revision and will drift; the named
symbols (functions, queries, struct fields) are authoritative. Grep the symbol
if a line number no longer matches.

Part 1: backend stops reading the column

Replace each read of the stored Provider with the provider type from the
loaded ai_providers row.

  • Routing fallbacks: coderd/x/chatd/model_routing_direct.go:84-92 and
    coderd/x/chatd/subagent.go:172-190,553. In the !AIProviderID.Valid branch,
    return sql.ErrNoRows (the signal these call sites already treat as "no
    usable model config") instead of reading the stored string. Active rows always
    have the FK, so the branch is unreachable for them.
  • Prompt build: coderd/x/chatd/generation_preparer.go:263 (chatFileResolver)
    takes the resolved provider type from the route loaded for the call,
    string(route.Provider.Type).
  • Logging and debug-run records in coderd/x/chatd/chatd.go, two treatments:
    • Persisted debug-run provenance writes to chat_debug_runs.provider, a
      separate column kept by this change. Set it from string(route.Provider.Type)
      at the WrapModel RecorderOptions.Provider site (:2622) and the
      CreateRun Provider site (:2653), where route is resolved in scope.
    • Pure log fields slog.F("provider", ...) (:2608,2615,2665,2791,2806) sit
      in failure branches where no route resolved: delete the field.
  • Listing/availability: coderd/exp_chats.go:889-917 builds ConfiguredModel.Provider;
    source it from ai_providers.type. This requires GetEnabledChatModelConfigs
    to return ap.type (it already JOINs ai_providers,
    coderd/database/queries/chatmodelconfigs.sql:37-38).
  • Cost breakdown: coderd/exp_chats.go:6533 from GetChatCostPerModel
    (coderd/database/queries/chats.sql). This query joins chat_model_configs
    without a deleted = FALSE filter, so it can reference soft-deleted configs
    that may carry a null ai_provider_id. Use LEFT JOIN ai_providers ap ON ap.id = cmc.ai_provider_id (no ap.deleted filter, so soft-deleted providers
    still resolve) and select COALESCE(ap.type::text, '') AS provider. Drop
    cmc.provider from the SELECT and the GROUP BY.
  • Retarget the two ORDER BY provider clauses
    (coderd/database/queries/chatmodelconfigs.sql:27,45) to ap.type (and
    model).

Part 2: telemetry keeps provider, derived

  • Change GetChatModelConfigsForTelemetry (coderd/database/queries/chats.sql,
    currently SELECT id, provider, model, ... WHERE deleted = false) to
    JOIN ai_providers ap ON ap.id = cmc.ai_provider_id and select
    ap.type AS provider. The query already filters deleted = false, so every
    row has a non-null ai_provider_id; a plain INNER JOIN is correct and no
    coalesce is needed.
  • coderd/telemetry/telemetry.go:2304 and the struct field :2475 keep their
    shape. bqChatModelConfig and Nexus SQL need no change.

Part 3: SDK drops the field

  • Remove the Provider field from codersdk.ChatModelConfig
    (codersdk/chats.go:1248-1262) and from convertChatModelConfig
    (coderd/exp_chats.go:7505).
  • Remove the Provider field from CreateChatModelConfigRequest and
    UpdateChatModelConfigRequest (both define it, codersdk/chats.go) and the
    provider-string handling in the handlers: delete the legacy branch at
    coderd/exp_chats.go:7128-7137 (the "AI provider ID is required when updating
    provider" path) and the insert/update param wiring at
    coderd/exp_chats.go:6997,7220,7429. Leave the request AIProviderID fields
    as optional pointers: create validates presence at runtime
    (coderd/exp_chats.go:6920-6923), update omits it to keep the existing value.
    Only the response field changes shape.
  • Make ai_provider_id required: change codersdk.ChatModelConfig.AIProviderID
    from *uuid.UUID with omitempty (codersdk/chats.go:1251) to a non-pointer
    uuid.UUID. The list and get endpoints return only deleted = FALSE configs,
    which always carry the FK, so the field is never absent in responses.
    convertChatModelConfig (coderd/exp_chats.go:7498) is the only construction
    site; it assigns config.AIProviderID.UUID directly and relies on the CHECK
    constraint, with no runtime null-handling. The invariant is covered by a test,
    not by a defensive branch.
  • Run make gen to regenerate codersdk consumers and the TypeScript types:
    provider leaves ChatModelConfig,
    CreateChatModelConfigRequest, UpdateChatModelConfigRequest, and
    ai_provider_id becomes required (ai_provider_id: string)
    (site/src/api/typesGenerated.ts:2488,2489,3502,8890).

Part 4: frontend moves onto ai_provider_id

Introduce one provider-type lookup and use it everywhere a provider type is
needed; do not read config.provider anywhere. Build
providerTypeByID: ReadonlyMap<string, string> from chatProviderConfigs()
(site/src/api/queries/chats.ts:1847-1853), mapping ChatProviderConfig.id to
ChatProviderConfig.provider. Each site below resolves the provider type as
providerTypeByID.get(config.ai_provider_id) and receives the map as a prop or
argument from the page that already loads the providers query.

  • Icon reads ModelRow.tsx:39 and UserCompactionThresholdSettings.tsx:285.
    UserCompactionThresholdSettings gains a providerTypeByID prop sourced by
    its parent page from chatProviderConfigs(). Replace
    provider={modelConfig.provider} with
    provider={providerTypeByID.get(modelConfig.ai_provider_id)}.
  • Model selector options: add a providerTypeByID parameter to
    getModelOptionsFromConfigs (modelOptions.ts:73,185,197,205) and set
    ModelSelectorOption.provider from the map; callers pass the map. Update the
    grouping and labels in ModelSelector.tsx:83,88,127,129 and the option built
    in SubagentModelOverrideSettings.tsx:48 to use the same value.
  • Sort key ModelsPage.tsx:24 sorts by the mapped provider type.
  • Legacy reference shim: delete the dead provider:model matching in
    modelDisplayName.ts:29,42,48 and chatHelpers.ts:84,101, and the .provider
    reads that feed it. It cannot match real data: getModelOptionsFromConfigs
    always sets ModelSelectorOption.id to the config UUID, and
    Chat.last_model_config_id is a uuid string (typesGenerated.ts:1563).
  • Delete the provider-string fallback branches in
    providerStates.ts:124-133,224-236 and AgentSettingsAPIKeysPageView.tsx:91-95;
    they are dead once ai_provider_id is required.
  • Stop sending provider in ModelForm.tsx:155,184; send only ai_provider_id.
  • Update every fixture and story that sets provider or omits ai_provider_id
    on a ChatModelConfig: testHelpers/chatModels.ts and
    ModelsPage/testFixtures.ts, plus the stories the TypeScript compiler flags
    once ai_provider_id is required (the required field turns every omission
    into a compile error, so the compiler enumerates them). For each, add
    ai_provider_id and remove provider.

Part 5: schema migration

  • New numbered migration up: DROP INDEX idx_chat_model_configs_provider,
    idx_chat_model_configs_provider_model; then ALTER TABLE chat_model_configs DROP COLUMN provider.
  • Paired .down.sql must restore the original schema exactly, since the
    migration test runs up then down: ADD COLUMN provider text; backfill
    UPDATE chat_model_configs cmc SET provider = ap.type FROM ai_providers ap WHERE ap.id = cmc.ai_provider_id; set provider = '' for any rows still null
    after the backfill (soft-deleted, unlinked); ALTER COLUMN provider SET NOT NULL; recreate both indexes.
  • Update coderd/database/dump.sql and the migration test fixtures via
    make gen.

Part 6: remove obsolete code

  • Delete BackfillChatModelConfigProviderStrings (coderd/ai_providers_backfill.go:74-95)
    and its startup call (cli/server.go:1119-1122). Keep BackfillBedrockProviderType.
  • Delete the BackfillChatModelConfigProvider query
    (coderd/database/queries/chatmodelconfigs.sql:147-166) and the unused
    DeleteChatModelConfigsByProvider (:136-145); DeleteChatModelConfigsByAIProviderID
    (:168) covers provider-scoped deletion.
  • Remove Provider from database.ChatModelConfig and regenerate
    queries.sql.go, querier.go, dbauthz, dbmetrics, dbmock. Remove the
    dead-query plumbing and its dbauthz_test.go case. No audit-table entry
    exists for chat_model_configs.

Part 7: external consumers (coder org)

  • Post a notice comment on the coder-org repositories that read the field,
    describing the removal and the replacement path (ai_provider_id plus
    GET /api/experimental/chats/providers, mapping id -> provider type):
    coder/coder-mobile-android (ChatModelConfigDto.provider),
    coder/demo-aigov-rhaiis-rhsummit-2026, coder/usgov-coderdemo,
    coder/pixel-playground.
  • No code changes or release coordination are owed to these repositories.

Ordering and dependencies

Parts 1, 2, and 3 must land before Part 5; the column cannot drop while code
still scans it. Part 4 depends on Part 3 (regenerated types). Part 6 deletes
code only after Part 1 removes the last reader. Part 7 is independent and can
proceed in parallel.

Verification

  • Before writing the migration, grep coderd/database/dump.sql for any view or
    trigger referencing chat_model_configs.provider; precedent column drops had
    to rebuild dependent views. None is expected (the relevant SQL are queries,
    not views), but confirm.
  • make gen, make lint, make test for backend and generated code.
  • Frontend: Storybook stories for the model selector, model rows, and settings
    pages exercise icon, grouping, sort, and legacy-reference behavior without the
    provider field.
  • Confirm the telemetry snapshot still produces a provider value after the
    JOIN change; the query returns only active rows. Confirm the cost breakdown
    still returns a row for messages whose model config was later soft-deleted.

Invariants

  • Active chat_model_configs rows always have a resolvable ai_provider_id;
    provider type is read from ai_providers.type, never from a stored copy.
  • The telemetry provider value equals the linked provider's type. The cost
    breakdown tolerates soft-deleted configs with a null ai_provider_id by
    emitting an empty provider rather than dropping the row.
  • No code path scans a provider column from chat_model_configs after the
    migration.
  • Every ChatModelConfig the API returns carries a non-null ai_provider_id,
    because those endpoints return only active configs. This holds only while no
    endpoint returns soft-deleted configs and the
    chat_model_configs_ai_provider_required_when_active CHECK stays in place;
    both are load-bearing for the required-field contract.

References

🤖 This PR was created with the help of Coder Agents, and will be reviewed by a human. 🏂🏻

@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

CODAGT-599

@mafredri

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-01 08:34 UTC by @mafredri
Spend: $79.61 / $100.00

Review history
  • R1 (2026-06-30): 16 reviewers, 3 Nit, 1 P2, 7 P3, COMMENT. Review
  • R2 (2026-07-01): 5 reviewers, 3 Nit, 1 P2, 7 P3, APPROVE. Review

deep-review v0.9.0 | Round 2 | 6b3341a..6de8ea0

Last posted: Round 2, 11 findings (1 P2, 7 P3, 3 Nit), APPROVE. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Author fixed (6de8ea0) quickgen.go:266 Silently discarded error from providerHint() when override route is set R1 Netero Yes
CRF-2 P2 Author fixed (6de8ea0) AgentChatPage.tsx:807 Loading race: "No Models Available" flash until provider-configs query settles R1 Nami Yes
CRF-3 P3 Author fixed (6de8ea0) chatd.go:2655 Debug run records empty provider when route resolution fails R1 Leorio P3, Chopper P3, Kite P3, Knov P3, Meruem P3 Yes
CRF-4 P3 Author fixed (6de8ea0) modelOptions.test.ts:34 No test for config whose ai_provider_id is absent from providerTypeByID map R1 Bisky Yes
CRF-5 P3 Author fixed (6de8ea0) dbgen.go:165 Comment narrates code; trim to contract statement R1 Gon Yes
CRF-6 P3 Author fixed (6de8ea0) exp_chats.go:969 Comment restates return value; trim to invariant R1 Gon Yes
CRF-7 P3 Author fixed (6de8ea0) exp_chats.go:7472 Four-line comment narrates assignment; trim to invariant R1 Gon Yes
CRF-8 P3 Author fixed (6de8ea0) subagent.go:520 Comment restates code; trim to invariant R1 Gon Yes
CRF-9 Nit Author fixed (6de8ea0) chats.sql:2585 SQL comment pads invariant with derivation narration R1 Gon Yes
CRF-10 Nit Author fixed (6de8ea0) dbgen.go:173 String literal should use AIProviderTypeOpenai constant R1 Ging-Go Yes
CRF-11 Nit Author fixed (6de8ea0) AgentChatPage.tsx:810 providerTypeByID map constructed inline in 7 call sites R1 Meruem Yes

Contested and acknowledged

(none)

Round log

Round 1

Panel: Netero + 16 reviewers. 1 P2, 4 P3, 3 Nit. Reviewed against 7179be2..7c4d325.

Round 2

Churn guard: PROCEED. 11/11 findings addressed. Netero: no findings. Law: don't split. Panel (Bisky, Mafuuu, Mafu-san, Nami, Kite): no new findings. All R1 fixes verified. Reviewed against 6b3341a..6de8ea0.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the denormalized chat_model_configs.provider column and migrates all backend, SDK, and frontend surfaces to treat ai_providers.type (reachable via chat_model_configs.ai_provider_id) as the single source of truth for provider identity. It also keeps telemetry emitting a provider string by deriving it from ai_providers.type at query time.

Changes:

  • Drops chat_model_configs.provider (and related indexes) via migration 000534, and updates SQL queries/code to derive provider type from ai_providers.
  • Updates experimental API/SDK ChatModelConfig to remove provider and require ai_provider_id in responses; updates Go + TS generated types and all call sites.
  • Updates the web UI to resolve provider type through an ai_provider_id -> provider type map (from owner-only or user-visible provider endpoints, depending on audience).

Reviewed changes

Copilot reviewed 86 out of 90 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
site/src/testHelpers/chatModels.ts Updates model-config test helper to use ai_provider_id instead of provider.
site/src/pages/AISettingsPage/ModelsPage/testFixtures.ts Removes provider from model fixtures and relies on ai_provider_id.
site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.tsx Plumbs providerTypeByID through the models page view.
site/src/pages/AISettingsPage/ModelsPage/ModelsPageView.stories.tsx Updates story args to include a providerTypeByID map.
site/src/pages/AISettingsPage/ModelsPage/ModelsPage.tsx Builds providerTypeByID from provider configs and sorts models using it.
site/src/pages/AISettingsPage/ModelsPage/components/ModelRow.tsx Resolves provider icon input via providerTypeByID + ai_provider_id.
site/src/pages/AISettingsPage/ModelsPage/components/ModelForm.tsx Stops sending provider in create/update requests; uses ai_provider_id.
site/src/pages/AISettingsPage/CoderAgentsPage/components/SubagentModelOverrideSettings.tsx Resolves model option provider via providerTypeByID + ai_provider_id.
site/src/pages/AISettingsPage/CoderAgentsPage/CoderAgentsPageView.tsx Plumbs providerTypeByID through Coder Agents settings view.
site/src/pages/AISettingsPage/CoderAgentsPage/CoderAgentsPageView.stories.tsx Updates stories to use ai_provider_id and a providerTypeByID map.
site/src/pages/AISettingsPage/CoderAgentsPage/CoderAgentsPage.tsx Loads provider configs and builds providerTypeByID for settings pages.
site/src/pages/AgentsPage/utils/modelOptions.ts Updates model option derivation to use ai_provider_id + providerTypeByID.
site/src/pages/AgentsPage/utils/modelOptions.test.ts Updates tests for new getModelOptionsFromConfigs signature and behavior.
site/src/pages/AgentsPage/components/UserCompactionThresholdSettings.tsx Resolves provider icon via providerTypeByID instead of modelConfig.provider.
site/src/pages/AgentsPage/components/UserCompactionThresholdSettings.stories.tsx Updates stories to set ai_provider_id and pass providerTypeByID.
site/src/pages/AgentsPage/components/ChatsSidebar/tree/modelDisplayName.ts Removes legacy provider:model fallback logic; uses config UUID resolution.
site/src/pages/AgentsPage/components/ChatsSidebar/ChatsSidebar.test.tsx Removes test coverage for legacy provider:model sidebar fallback.
site/src/pages/AgentsPage/components/ChatsSidebar/ChatsSidebar.stories.tsx Updates stories to set ai_provider_id instead of provider.
site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.ts Simplifies model resolution logic (exact id match only).
site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.test.ts Updates tests to match the simplified resolution behavior.
site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx Updates story model configs to set ai_provider_id.
site/src/pages/AgentsPage/components/AdvisorSettings.stories.tsx Updates story model configs to set ai_provider_id.
site/src/pages/AgentsPage/AgentsPageView.stories.tsx Updates stories/mocks for ChatModelConfig shape and provider key configs.
site/src/pages/AgentsPage/AgentsPage.tsx Uses user-visible provider configs to build provider-type map for model options.
site/src/pages/AgentsPage/AgentSettingsUserAgentsPageView.stories.tsx Updates stories to set ai_provider_id and explicit providers in options.
site/src/pages/AgentsPage/AgentSettingsUserAgentsPage.tsx Loads user-visible provider configs and passes providerTypeByID to option builder.
site/src/pages/AgentsPage/AgentSettingsCompactionPageView.tsx Adds providerTypeByID prop pass-through for compaction settings.
site/src/pages/AgentsPage/AgentSettingsCompactionPageView.stories.tsx Updates story args for required ai_provider_id and providerTypeByID.
site/src/pages/AgentsPage/AgentSettingsCompactionPage.tsx Loads user-visible provider configs and builds providerTypeByID.
site/src/pages/AgentsPage/AgentSettingsAPIKeysPageView.tsx Removes legacy fallback matching based on provider string.
site/src/pages/AgentsPage/AgentSettingsAPIKeysPage.stories.tsx Updates story models to use ai_provider_id.
site/src/pages/AgentsPage/AgentCreatePage.tsx Uses user-visible provider configs to build provider-type map for model options.
site/src/pages/AgentsPage/AgentChatPage.tsx Uses user-visible provider configs to build provider-type map for model options.
site/src/pages/AgentsPage/AgentChatPage.stories.tsx Updates story mocks for user provider key configs.
site/src/modules/aiModels/providerStates.ts Removes provider-string fallbacks and keys provider state strictly by ai_provider_id.
site/src/modules/aiModels/providerStates.test.ts Updates provider state tests for ai_provider_id-only behavior.
site/src/api/typesGenerated.ts Updates generated TS types: remove provider, require ai_provider_id in responses.
scaletest/chat/provider.go Updates scaletest client logic for non-pointer AIProviderID.
enterprise/coderd/exp_chats_test.go Updates enterprise tests to stop sending/expecting provider in requests/responses.
codersdk/chats.go Removes provider field from SDK model config and requests; makes AIProviderID required in response model.
coderd/x/chatd/turn_summary_internal_test.go Updates insert params for removed Provider field.
coderd/x/chatd/title_override_internal_test.go Updates mocks and test data for provider resolution via ai_provider_id.
coderd/x/chatd/tasks_test.go Updates test seed to not rely on config Provider.
coderd/x/chatd/subagent.go Removes now-dead fallback branch when AIProviderID is missing, returning sql.ErrNoRows.
coderd/x/chatd/subagent_internal_test.go Updates internal tests and seeds to link model configs to the intended provider rows.
coderd/x/chatd/quickgen.go Updates title-generation path to get provider type from resolved route/provider row.
coderd/x/chatd/quickgen_internal_test.go Updates tests for changed query return type and config/provider handling.
coderd/x/chatd/model_routing_internal_test.go Updates routing tests to not rely on stored config Provider.
coderd/x/chatd/model_routing_direct.go Returns sql.ErrNoRows when AIProviderID is missing instead of using removed column.
coderd/x/chatd/integration_test.go Removes sending Provider in config creation requests.
coderd/x/chatd/helpers_test.go Updates test seed to not set config Provider.
coderd/x/chatd/generation_preparer.go Resolves provider type via modelRoute.providerHint() for file prompt conversion.
coderd/x/chatd/generation_preparer_internal_test.go Updates test seeds for removed config Provider.
coderd/x/chatd/configcache_internal_test.go Updates test model config helper for removed Provider.
coderd/x/chatd/chatstate/trigger_test.go Updates test seed for removed config Provider.
coderd/x/chatd/chatstate/machine_test.go Updates test seed for removed config Provider.
coderd/x/chatd/chatstate/family_test.go Updates test seed for removed config Provider.
coderd/x/chatd/chatprompt/chatprompt_test.go Updates test to link model config to provider row via ai_provider_id.
coderd/x/chatd/chatdebug/service_test.go Updates debug provenance assertions to derive provider type from provider row.
coderd/x/chatd/chatd.go Updates debug-run provider provenance wiring away from config Provider.
coderd/x/chatd/chatd_test.go Updates many test helpers to link model configs via ai_provider_id.
coderd/x/chatd/chatd_retry_test.go Updates retry tests for removed config Provider.
coderd/x/chatd/chatd_internal_test.go Updates mocks to include GetAIProviderByID / GetAIProviderKeysByProviderID expectations.
coderd/x/chatd/chatd_chainmode_test.go Updates chainmode tests for removed config Provider.
coderd/x/chatd/advisor_internal_test.go Updates advisor override tests to resolve provider type via ai_provider_id.
coderd/workspaceagents_active_chat_internal_test.go Updates seed helper to not set config Provider.
coderd/telemetry/telemetry_test.go Updates telemetry tests to link model configs via ai_provider_id.
coderd/exp_chats.go Removes provider-string handling in create/update/list paths; assumes ai_provider_id invariant for active configs.
coderd/exp_chats_test.go Updates API tests to no longer use/expect config Provider.
coderd/database/queries/chats.sql Derives provider strings via joins for cost breakdown + telemetry snapshot.
coderd/database/queries/chatmodelconfigs.sql Removes provider column usage; introduces GetEnabledChatModelConfigs row type with derived provider.
coderd/database/queries.sql.go Regenerates SQLC output to reflect schema/query changes and new row types.
coderd/database/querier.go Updates querier interface for new GetEnabledChatModelConfigs return type; removes deleted queries.
coderd/database/querier_test.go Updates tests to match new insert helper signature and new enabled-config row type.
coderd/database/models.go Removes Provider from database.ChatModelConfig.
coderd/database/migrations/000534_drop_chat_model_configs_provider.up.sql Drops provider indexes and column.
coderd/database/migrations/000534_drop_chat_model_configs_provider.down.sql Restores provider column/indexes and backfills from ai_providers.type.
coderd/database/dump.sql Updates canonical schema dump: removes chat_model_configs.provider and its indexes.
coderd/database/dbpurge/dbpurge_test.go Updates purge tests for removed config Provider.
coderd/database/dbmock/dbmock.go Regenerates mocks for removed queries + updated return types.
coderd/database/dbmetrics/querymetrics.go Removes metrics wrappers for deleted DB methods; updates enabled-configs return type.
coderd/database/dbgen/dbgen.go Updates DB test generator to default/link an OpenAI provider via ai_provider_id.
coderd/database/dbgen/dbgen_test.go Updates assertions to check provider type via provider row, not config column.
coderd/database/dbauthz/dbauthz.go Removes authorization wrappers for deleted DB methods; updates enabled-configs return type.
coderd/database/dbauthz/dbauthz_test.go Updates authorization tests for removed methods and updated insert/update params.
coderd/coderdtest/chat.go Removes sending Provider in test helper for creating model configs.
coderd/ai_providers_backfill.go Deletes startup backfill for syncing provider strings; keeps Bedrock provider-type promotion backfill.
coderd/ai_providers_backfill_test.go Removes tests that covered the deleted provider-string backfill.
cli/server.go Stops invoking the deleted startup backfill.
cli/exp_scaletest_chat_test.go Updates scaletest CLI for non-pointer AIProviderID.
Files not reviewed (4)
  • coderd/database/dbmetrics/querymetrics.go: Generated file
  • coderd/database/dbmock/dbmock.go: Generated file
  • coderd/database/models.go: Generated file
  • coderd/database/querier.go: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread coderd/database/queries/chatmodelconfigs.sql
Comment thread coderd/database/queries/chatmodelconfigs.sql
Comment thread coderd/x/chatd/chatd.go
@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch from 7c4d325 to ded1375 Compare June 30, 2026 16:25
@mafredri
mafredri requested review from ibetitsmike and johnstcn June 30, 2026 16:29

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean decomposition of a single logical change across schema, backend, SDK, frontend, and telemetry. The invariant chain (CHECK constraint guaranteeing ai_provider_id for active rows, FK without CASCADE/SET NULL, soft-delete semantics) is verified and documented at every load-bearing site. Telemetry INNER JOIN vs cost LEFT JOIN distinction is correctly designed for their respective filter contexts. Test density at 67.3% is strong.

"I tried to build a case against this change and couldn't." (Pariston)

Severity count: 1 P2, 4 P3, 3 Nit.

The P2 is a frontend loading race: the new dependency on userChatProviderConfigs() is not reflected in the loading guard, so users see a flash of "No Models Available" on cold loads until the provider query settles. Five reviewers independently converged on the debug-run empty-provider finding (P3), which loses the provider dimension in diagnostic records when route resolution fails. Four comments repeat a pattern of restating the code around otherwise correct invariant documentation (P3).

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/AgentChatPage.tsx Outdated
Comment thread coderd/x/chatd/chatd.go
Comment thread site/src/pages/AgentsPage/utils/modelOptions.test.ts
Comment thread coderd/x/chatd/quickgen.go Outdated
Comment thread coderd/database/dbgen/dbgen.go Outdated
Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/x/chatd/subagent.go Outdated
Comment thread coderd/database/queries/chats.sql Outdated
Comment thread coderd/database/dbgen/dbgen.go Outdated
Comment thread site/src/pages/AgentsPage/AgentChatPage.tsx Outdated

@johnstcn johnstcn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial review, will continue later

Comment thread cli/server.go
Comment on lines -1120 to -1122
// Must run after BackfillBedrockProviderType; shares aibridgeInitCtx so
// a timeout on the first backfill will skip this one until next startup.
coderd.BackfillChatModelConfigProviderStrings(aibridgeInitCtx, options.Database, logger.Named("aibridge.backfill"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewer note: added in v2.34.0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed. BackfillChatModelConfigProviderStrings (v2.34.0) only repaired the chat_model_configs.provider text column that this PR drops. Migration 000534 just DROP COLUMN provider and never reads it; ai_provider_id is populated independently. So an upgrade that skips v2.34 is harmless: the data the backfill repaired is being removed.

🤖 Posted using /amend-review skill via Coder Agents.

Comment on lines +1210 to +1211
rowA := testutil.Fake(s.T(), faker, database.GetEnabledChatModelConfigsRow{})
rowB := testutil.Fake(s.T(), faker, database.GetEnabledChatModelConfigsRow{})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why the naming change from config[AB] to row[AB]?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept rowA/rowB: the return type changed to GetEnabledChatModelConfigsRow (the embedded config plus the derived provider), so row is accurate and config would mislead.

🤖 Posted using /amend-review skill via Coder Agents.

Comment thread coderd/database/queries/chatmodelconfigs.sql
Comment thread coderd/x/chatd/chatd.go
Comment thread coderd/x/chatd/chatd_test.go Outdated
Comment on lines +8502 to +8521
func reuseOrCreateAIProviderOfType(t *testing.T, db database.Store, providerType string) uuid.UUID {
t.Helper()
providers, err := db.GetAIProviders(context.Background(), database.GetAIProvidersParams{IncludeDisabled: true})
require.NoError(t, err)
var provider database.AIProvider
for _, candidate := range providers {
if candidate.Type != database.AIProviderType(providerType) {
continue
}
if provider.ID == uuid.Nil || candidate.CreatedAt.After(provider.CreatedAt) {
provider = candidate
}
}
if provider.ID == uuid.Nil {
provider = dbgen.AIProvider(t, db, database.AIProvider{
Type: database.AIProviderType(providerType),
})
}
return provider.ID
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we instead ensure that the test fixtures are created as expected?
Also, this is only used once.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlined the single-use helper into its caller (and did the same for the sibling reuseOrCreateInternalAIProvider), addressing the used-once point. I kept the reuse-newest-of-type behavior rather than threading a provider ID from the seed helper: the test must link the config to the provider seeded earlier in the test (which carries the mock base URL and key), and explicit threading through the call sites is churn with no behavioral gain.

🤖 Posted using /amend-review skill via Coder Agents.

@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch from ded1375 to 40cf684 Compare June 30, 2026 17:49

@johnstcn johnstcn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will review once more when rebased, but I don't see anything blocking here 👍

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 duplicate kept
in sync by a startup backfill that the system no longer needs.

Every read site now derives the provider type from the linked
ai_providers row. The routing fallbacks that read the stored string sit
behind an !ai_provider_id branch that is unreachable for active rows, so
they return sql.ErrNoRows instead. Telemetry keeps emitting provider,
now sourced from ai_providers.type via JOIN, so the BigQuery column and
Nexus dashboards keep working. The experimental API drops provider and
makes ai_provider_id required, since those endpoints only return active
configs.

Ships in a single release: production reads the column via SELECT *, so
operators must scale to zero before upgrading. There is no rollback.

CODAGT-599
@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch from 40cf684 to 6de8ea0 Compare July 1, 2026 08:03
@mafredri
mafredri requested a review from Copilot July 1, 2026 08:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 92 changed files in this pull request and generated no new comments.

Files not reviewed (4)
  • coderd/database/dbmetrics/querymetrics.go: Generated file
  • coderd/database/dbmock/dbmock.go: Generated file
  • coderd/database/models.go: Generated file
  • coderd/database/querier.go: Generated file

@mafredri

mafredri commented Jul 1, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 11 R1 findings addressed with root-cause fixes, not patches. No new findings from the R2 panel (Bisky, Mafuuu, Mafu-san, Nami, Kite) or from Netero.

The fixes are well-structured. CRF-2 (loading race) was fixed with a useModelOptions hook that folds three query loading states into one derived source, making the race structurally impossible for consumers rather than guarding it per page. CRF-3 (debug-run empty provider) was fixed with a enabledAIProviderByID fallback when route resolution fails, covered by a test that forces the exact failure path. CRF-11 (inline map construction) was fixed with providerTypeByIDFromConfigs and providerTypeByIDFromUserConfigs helpers that bake in the correct source field per audience. Comment trims (CRF-5 through CRF-9) land at the right level: invariant naming without mechanism narration.

Test density rose from 67.3% to 72.6%. New tests exercise the exact gaps R1 identified: the provider-map-miss drop behavior, the pending-provider-query loading race, and the route-failure provider-derivation fallback.

"Every stone is real." (Bisky)

🤖 This review was automatically generated with Coder Agents.

@mafredri
mafredri marked this pull request as ready for review July 1, 2026 09:16
@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch from 11c116b to fda47e4 Compare July 1, 2026 10:29
@mafredri
mafredri requested a review from DanielleMaywood July 1, 2026 10:50

@DanielleMaywood DanielleMaywood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick frontend review. As most of it is prop drilling not much to comment on

Comment on lines +22 to +53
/**
* useModelOptions owns the three queries the user-facing model selector needs
* (model configs, the model catalog, and the user provider configs) and
* derives its own loading flag.
*
* Provider identity lives in a separate query (userChatProviderConfigs), so a
* page that renders with configs loaded but that query still pending would
* build an empty provider map, drop every option, and flash "No Models". By
* folding all three loading states into a single derived source, that race is
* structurally impossible for every consumer instead of being guarded per page.
*/
export const useModelOptions = (): UseModelOptionsResult => {
const modelConfigsQuery = useQuery(chatModelConfigs());
const catalogQuery = useQuery(chatModels());
const providerConfigsQuery = useQuery(userChatProviderConfigs());

const options = getModelOptionsFromConfigs(
modelConfigsQuery.data,
catalogQuery.data,
providerTypeByIDFromUserConfigs(providerConfigsQuery.data),
);

return {
options,
isModelCatalogLoading:
modelConfigsQuery.isLoading ||
catalogQuery.isLoading ||
providerConfigsQuery.isLoading,
modelCatalog: catalogQuery.data,
hasConfiguredModels: hasConfiguredModelsInCatalog(catalogQuery.data),
};
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely convinced on the value of this being a hook. I know it is re-used in multiple places but I dunno

At each current call site, most of the data is already fetched:

AgentCreatePage: already has chatModels and chatModelConfigs
AgentChatPage: already has chatModels and chatModelConfigs
AgentSettingsUserAgentsPage: already has chatModels and chatModelConfigs
parent AgentsPage now fetches all three pieces, including userChatProviderConfigs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DanielleMaywood fair. My thinking was that we fix the root cause rather than add guards in every place that needs to ensure all the data is present.

@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch 2 times, most recently from b45eb9a to d895b9f Compare July 1, 2026 11:28
@mafredri
mafredri requested a review from DanielleMaywood July 1, 2026 12:30
Follow-up to removing chat_model_configs.provider: dead paths the column
removal orphaned or a legacy shim it missed, one pre-existing label
inconsistency in the touched surface, and a test-quality fix.

Frontend:
- Delete getNormalizedModelRef: the getModelOptionsFromConfigs rewrite
  removed its last production caller; only its own test kept it alive.
- Drop the unreachable provider:model legacy branch in
  resolveModelOptionId (both inputs are config UUIDs), matching the
  shims already removed from modelDisplayName and chatHelpers.
- Remove the dead inner optional chain on ai_provider_id in ModelForm
  now that the field is required.
- Default ModelSelector's formatProviderLabel to the canonical formatter
  and delete the naive capitalize-first fallback, so every consumer
  renders "OpenAI" rather than "Openai".
- Simplify the now-unused RuntimeModelRef/CatalogModelLike shapes.
- Replace the useModelOptions hook with a pure resolveModelSelector
  function: it uses nothing that requires a hook, so the pages pass
  their already-fetched queries in and the util centralizes the loading
  coordination without a second query observer.

Backend:
- Call prepareManualTitleDebugRun as a bare statement in its test rather
  than consuming its returns with needless assertions to satisfy dogsled.
@mafredri
mafredri force-pushed the mathias/codagt-599-remove-chat-model-config-provider branch from d895b9f to aaa0315 Compare July 1, 2026 12:44
@mafredri
mafredri merged commit 047c474 into main Jul 1, 2026
28 of 29 checks passed
@mafredri
mafredri deleted the mathias/codagt-599-remove-chat-model-config-provider branch July 1, 2026 12:59
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 1, 2026
@mafredri mafredri added cherry-pick/v2.34 Cherry-pick PR targeting release/2.34 cherry-pick and removed cherry-pick/v2.34 Cherry-pick PR targeting release/2.34 cherry-pick labels Jul 1, 2026
@ethanndickson ethanndickson added the release/breaking This label is applied to PRs to detect breaking changes as part of the release process label Jul 7, 2026
@github-actions github-actions Bot changed the title refactor: drop chat_model_configs provider column refactor!: drop chat_model_configs provider column Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cherry-pick release/breaking This label is applied to PRs to detect breaking changes as part of the release process

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants