Skip to content

feat: route aibridge through ai_providers IDs#25606

Draft
dannykopping wants to merge 2 commits into
dk/accept-delegated-key-idfrom
dk/aibridge-provider-id
Draft

feat: route aibridge through ai_providers IDs#25606
dannykopping wants to merge 2 commits into
dk/accept-delegated-key-idfrom
dk/aibridge-provider-id

Conversation

@dannykopping
Copy link
Copy Markdown
Contributor

@dannykopping dannykopping commented May 22, 2026

Release note: the migration may take some time to run if aibridge_interceptions is large.

@github-actions
Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/cli/aibridge_interceptions_list.md

Copy link
Copy Markdown
Contributor Author

dannykopping commented May 22, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@dannykopping dannykopping force-pushed the dk/aibridge-startup-ordering branch 2 times, most recently from ab0d49f to 0e7f01a Compare May 22, 2026 10:34
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 7b99bcd to b546f4a Compare May 22, 2026 10:34
@dannykopping dannykopping changed the base branch from dk/aibridge-startup-ordering to graphite-base/25606 May 22, 2026 10:45
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from b546f4a to a090eae Compare May 22, 2026 10:45
@dannykopping dannykopping force-pushed the graphite-base/25606 branch from 0e7f01a to ef6ee2a Compare May 22, 2026 10:45
@graphite-app graphite-app Bot changed the base branch from graphite-base/25606 to main May 22, 2026 10:46
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch 2 times, most recently from c85bf0b to be0b8d1 Compare May 22, 2026 10:58
Copy link
Copy Markdown
Contributor Author

/coder-agents-review

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

First-pass review (Netero only). This is a mechanical scan; the full review panel has not yet reviewed this PR. The panel will review after these findings are addressed.

The migration strategy (two-pass backfill, fallback filter in SQL, ON DELETE SET NULL) is well-structured. The provider interface extension is clean, and the CLI flag mutual-exclusivity check is a good UX detail.

1 P2, 2 P3, 1 Nit.

"Zero test coverage." — Netero, staring at BuildProvidersFromDB

🤖 This review was automatically generated with Coder Agents.

Comment thread cli/aibridged.go Outdated
Comment thread codersdk/aibridge.go Outdated
Comment thread enterprise/coderd/aibridge_test.go
Comment thread coderd/database/migrations/000506_aibridge_interception_provider_id.up.sql Outdated
Copy link
Copy Markdown
Contributor Author

/coder-agents-review

Copy link
Copy Markdown
Contributor Author

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

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

Addressed all review comments in 63fcc71.

Comment thread coderd/database/migrations/000507_aibridge_interception_provider_id.up.sql Outdated
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Round 2 (Netero + panel of 17). R1 findings DEREM-1 through DEREM-4 all addressed cleanly in 63fcc71, with generalized sibling fixes and appropriate test coverage.

The migration strategy, provider interface extension, and SQL fallback design are solid. The release note warning for large tables is appreciated.

1 P1, 3 P2, 3 P3, 3 Nit.

The P1 is a migration blocker: the backfill casts free text to an enum, which aborts on any value outside the enum (copilot is the known case). The P2s are an unsupported-type server startup failure, an RBAC permission gap for auditors, and zero test coverage on the SQL legacy-row fallback.

"Enum-to-text never fails. Text-to-enum is the one that breaks." — Hisoka


cli/aibridged.go:188

P3 [DEREM-5] BuildProviders (env-driven constructor) has zero production callers after this PR. Both cli/server.go and enterprise/cli/server.go now call BuildProvidersFromDB. The function, its ~174 lines, and its dedicated tests remain as dead code. (Netero, Zoro P4)

🤖

enterprise/coderd/aibridge.go:107

Nit [DEREM-14] Swagger @Param annotation still lists provider as an available filter key; the actual key is now provider_id. Same at line 222 for sessions. (Chopper, Knov P3)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread cli/aibridged.go
Comment thread coderd/searchquery/search.go
Comment thread enterprise/coderd/aibridge_test.go
Comment thread enterprise/cli/aibridge.go Outdated
Comment thread cli/aibridged.go Outdated
Comment thread coderd/searchquery/search.go Outdated
Comment thread aibridge/recorder/types.go Outdated
Comment thread cli/aibridged_internal_test.go Outdated
Comment thread enterprise/coderd/aibridge_test.go
@dannykopping dannykopping changed the base branch from main to graphite-base/25606 May 22, 2026 13:41
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 63fcc71 to 8b2c09c Compare May 22, 2026 13:41
@dannykopping dannykopping changed the base branch from graphite-base/25606 to dk/aibridge-copilot-enum-value May 22, 2026 13:41
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 8b2c09c to 73a836a Compare May 22, 2026 14:00
@dannykopping dannykopping changed the base branch from dk/aibridge-copilot-enum-value to graphite-base/25606 May 22, 2026 14:10
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 73a836a to 98d68fd Compare May 22, 2026 14:10
@dannykopping dannykopping force-pushed the graphite-base/25606 branch from 56bf0c1 to 0d9718e Compare May 22, 2026 14:10
@graphite-app graphite-app Bot changed the base branch from graphite-base/25606 to main May 22, 2026 14:11
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 98d68fd to bcaf3ed Compare May 22, 2026 14:11
Comment thread aibridge/recorder/types.go Outdated
Comment thread cli/aibridged_internal_test.go Outdated
Comment thread cli/aibridged_internal_test.go
Comment thread coderd/searchquery/search.go
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from bcaf3ed to 30e7ff0 Compare May 22, 2026 14:47
Copy link
Copy Markdown
Contributor Author

/coder-agents-review

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Round 3 blocked. Two findings from round 2 have no author response:

DEREM-5 (P3): BuildProviders (env-driven constructor at cli/aibridged.go:188) has zero production callers after this PR. Both server paths now call BuildProvidersFromDB. The function (~174 lines) and its dedicated tests remain as dead code. Response needed: remove, or acknowledge with a ticket for later cleanup.

DEREM-14 (Nit): Swagger @Param annotation at enterprise/coderd/aibridge.go:107 (and line 222 for sessions) still lists provider as an available filter key; the actual key is now provider_id. API consumers following the generated docs will use the wrong key.

Both findings were folded into the review body (not inline threads) because their lines were outside the diff. Further review is blocked until the author responds or pushes fixes for these items.

🤖 This review was automatically generated with Coder Agents.

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review
Copy link
Copy Markdown

coder-agents-review Bot commented May 22, 2026

Review in progress | Chat
Requested: 2026-05-22 15:36 UTC by @dannykopping

deep-review v0.5.0 | Round 4 | 0d9718e..01702e8

Last posted: Round 4, 21 findings (1 P1, 4 P2, 9 P3, 7 Nit), APPROVE. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
DEREM-1 P2 Author fixed (63fcc71) cli/aibridged.go:60 BuildProvidersFromDB has zero test coverage R1 Netero Yes
DEREM-2 P3 Author fixed (63fcc71) codersdk/aibridge.go:178 omitempty on uuid.UUID is a no-op R1 Netero Yes
DEREM-3 P3 Author fixed (63fcc71) enterprise/coderd/aibridge_test.go:440 Provider/NoMatch test removed with no replacement R1 Netero Yes
DEREM-4 Nit Author fixed (63fcc71) coderd/database/migrations/000506_aibridge_interception_provider_id.up.sql:7 Comment references wrong migration number (000503 should be 000458) R1 Netero Yes
DEREM-5 P3 Author fixed (01702e8) cli/aibridged.go:188 BuildProviders is dead code (zero production callers after this PR) R2 Netero Yes
DEREM-6 Nit Author fixed (2417bff9d7) cli/aibridged_internal_test.go:11 Consecutive blank lines in import block R2 Netero Yes
DEREM-7 Nit Author fixed (2417bff9d7) enterprise/coderd/aibridge_test.go:551 Consecutive blank lines between test blocks R2 Netero Yes
DEREM-8 P1 Author fixed (2417bff9d7) coderd/database/migrations/000506_aibridge_interception_provider_id.up.sql:33 Backfill pass 2 casts text to enum; fails on copilot rows R2 Hisoka P1, Mafuuu P1, Ryosuke P1, Knuckle P1, Kite P1, Meruem P1, Razor P1 Yes
DEREM-9 P2 Author fixed (2417bff9d7) cli/aibridged.go:180 BuildProvidersFromDB fails server startup on any unsupported provider type R2 Hisoka P2, Mafuuu P2, Pariston P1 Yes
DEREM-10 P2 Author fixed (2417bff9d7) coderd/searchquery/search.go:695 parseAIProvider RBAC gap: auditors cannot use provider_id filter R2 Ryosuke P2, Chopper P2, Kite P2, Knov P1 Yes
DEREM-11 P2 Author fixed (2417bff9d7) enterprise/coderd/aibridge_test.go:437 SQL fallback for legacy rows (provider_id IS NULL) has zero test coverage R2 Bisky P2, Meruem P2, Chopper P3 Yes
DEREM-12 P3 Author fixed (2417bff9d7) enterprise/cli/aibridge.go:82 --provider flag description claims snapshot matching but code does live API lookup R2 Pariston P3, Gon P3, Leorio P3, Kite P2 Yes
DEREM-13 P3 Author fixed (2417bff9d7) cli/aibridged.go:93 N+1 key fetch: buildProviderFromRow uses singular query inside loop R2 Robin Yes
DEREM-14 Nit Author fixed (01702e8) enterprise/coderd/aibridge.go:107 Swagger annotation still lists provider instead of provider_id R2 Chopper, Knov P3 Yes
DEREM-15 Nit Author fixed (2417bff9d7) coderd/searchquery/search.go:699 fmt.Sprintf with constant arg; pattern inconsistency R2 Gon Yes
DEREM-16 Nit Author fixed (2417bff9d7) aibridge/recorder/types.go:38 Comment says "Empty" but wire value is zero UUID string R2 Razor Yes
CRF-17 P3 Open cli/aibridged_internal_test.go No test for Copilot provider type in TestBuildProvidersFromDB R4 Bisky Yes
CRF-18 P3 Open cli/aibridged_internal_test.go AnthropicProviderWithBedrock asserts name/type but not Bedrock wiring R4 Bisky Yes
CRF-19 P3 Open coderd/searchquery/search.go:714 Soft-deleted provider UUID returns 400, stranding historical audit data R4 Hisoka P3 Yes
CRF-20 P3 Open coderd/database/queries/aibridge.sql:143 Runtime type fallback lacks uniqueness guard the migration has R4 Mafuuu P3, Hisoka Note Yes
CRF-21 Nit Open cli/aibridged_internal_test.go:27 Consecutive blank lines between testLogger and providerNames R4 Bisky Yes

Contested and acknowledged

(none)

Round log

Round 1

Netero-only. 1 P2, 2 P3, 1 Nit. Reviewed against ef6ee2a..be0b8d1.

Round 2

Panel (17 reviewers). DEREM-1 through DEREM-4 addressed. 1 P1, 3 P2, 3 P3, 3 Nit new (Netero: 1 P3, 2 Nit; Panel: 1 P1, 3 P2, 2 P3, 1 Nit). Reviewed against ef6ee2a..63fcc71.

Round 3

BLOCKED. DEREM-5 (P3) and DEREM-14 (Nit) silent. No review.

Round 4

Panel (4 reviewers). DEREM-5 through DEREM-16 all addressed. 4 P3, 1 Nit new. No findings from Netero or Chopper. Reviewed against 0d9718e..01702e8.

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
Kurapika security
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.

dannykopping and others added 2 commits May 22, 2026 17:45
Adds provider_id (FK to ai_providers, ON DELETE SET NULL) on
aibridge_interceptions, populated via two-pass backfill (by name, then
unambiguous type). Provider IDs are plumbed through aibridge.Provider,
the dRPC proto, and aibridgedserver. aibridged is now seeded from the
database via BuildProvidersFromDB so each in-memory Provider carries
its row UUID, removing the per-request name->ID lookup.

The interception/session list and count filters take a provider_id UUID;
the SQL coalesces back to the legacy provider_name/provider snapshot
columns for rows that pre-date the column or could not be unambiguously
resolved at backfill time. Telemetry summaries continue to aggregate by
provider type. Response payloads are unchanged; the legacy provider and
provider_name text columns are kept until a follow-up PR drops them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… key

Address review findings:

DEREM-5: Remove BuildProviders (env-driven constructor) and its helpers
(bedrockConfigFromProvider, getBedrockConfig) which have zero production
callers after the migration to BuildProvidersFromDB. Remove the dedicated
TestBuildProviders test suite. Update enterprise TestDomainsFromProviders
to construct providers directly instead of via BuildProviders.

DEREM-14: Fix Swagger @Param annotations in aiBridgeListInterceptions
and aiBridgeListSessions to document 'provider_id' instead of 'provider'
as the search query key, matching the actual parseAIProvider
implementation.
@dannykopping dannykopping changed the base branch from main to graphite-base/25606 May 22, 2026 15:46
@dannykopping dannykopping force-pushed the dk/aibridge-provider-id branch from 01702e8 to 3eea892 Compare May 22, 2026 15:46
@dannykopping dannykopping changed the base branch from graphite-base/25606 to dk/accept-delegated-key-id May 22, 2026 15:46
Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Round 4 (Netero + panel of 4). All 12 prior findings (DEREM-5 through DEREM-16) verified as fixed. The P1 migration cast, P2 server startup blocker, P2 RBAC gap, and P2 legacy fallback coverage gap are all resolved. Dead code removed. Swagger annotations corrected. N+1 query eliminated.

The author's approach for DEREM-10 (granting auditors read access to providers instead of using AsSystemRestricted) is cleaner than the suggestion; it keeps the permission model explicit.

4 P3, 1 Nit remaining. These are test coverage gaps and design-level observations, none blocking.

"Shall I show you? Deploy two openai providers." — Hisoka, narrating the type fallback edge case

🤖 This review was automatically generated with Coder Agents.

"encoding/json"
"testing"

"github.com/google/uuid"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 [CRF-17] TestBuildProvidersFromDB has no subtest for the Copilot provider type. The buildProviderFromRow switch has three live branches (OpenAI, Anthropic, Copilot), but only OpenAI and Anthropic are tested. The Copilot path has unique behavior (ignores API keys, no Bedrock, no key pool). Deleting the case database.AiProviderTypeCopilot block would leave all tests green. (Bisky)

🤖

t.Helper()
return slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 [CRF-18] AnthropicProviderWithBedrock asserts p.Name() and p.Type() but nothing about the Bedrock wiring. If the if settings.Bedrock != nil guard were inverted, the test would still pass. Adding assert.Equal(t, "https://api.anthropic.com/", p.BaseURL()) would distinguish "Bedrock config was parsed" from "regular Anthropic pointing at a Bedrock URL." (Bisky)

🤖

if id == uuid.Nil {
return uuid.Nil, "", ""
}
p, err := db.GetAIProviderByID(ctx, id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 [CRF-19] Filtering by a soft-deleted provider's UUID returns a 400 validation error instead of filtered results. parseAIProvider calls GetAIProviderByID which filters deleted = FALSE. Once an admin soft-deletes a provider, its historical interceptions become unfilterable by provider_id. The old text-based filter worked regardless. This is a design trade-off worth documenting; if unintentional, the lookup could include soft-deleted rows (read-only resolution is safe). (Hisoka)

🤖

AND (
(@provider_name::text != '' AND aibridge_interceptions.provider_name = @provider_name::text)
OR (aibridge_interceptions.provider_name = '' AND @provider_type::text != '' AND aibridge_interceptions.provider = @provider_type::text)
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 [CRF-20] The runtime type fallback for legacy rows (provider_name = '', provider_id IS NULL) matches on provider type string without the uniqueness guard the migration backfill has (HAVING COUNT(*) = 1). With two providers of the same type, legacy rows match whichever provider UUID is used in the filter, potentially misattributing traffic. Impact is bounded (only pre-000458 rows), but the mismatch between migration and runtime semantics is worth a SQL comment. (Mafuuu P3, Hisoka Note)

🤖

t.Helper()
return slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit [CRF-21] Two consecutive blank lines between testLogger and providerNames. Single blank line suffices. (Bisky)

🤖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant