Skip to content

feat: add AIProvider types and client methods#24893

Merged
dannykopping merged 5 commits into
mainfrom
dk/aibridge-providers-sdk
May 18, 2026
Merged

feat: add AIProvider types and client methods#24893
dannykopping merged 5 commits into
mainfrom
dk/aibridge-providers-sdk

Conversation

@dannykopping
Copy link
Copy Markdown
Contributor

@dannykopping dannykopping commented May 1, 2026

Disclaimer: implemented by a Coder Agent using Claude Opus 4.7

Part of the implementation of RFC: Common AI Provider Configs (AIGOV-201).

Adds the codersdk types and client methods for runtime AI provider management. AI Bridge is the initial consumer but the data model is provider-generic — other Coder features (Agents, Chat, etc.) can read the same rows over time.

What's in this PR

  • AIProvider, AIProviderType, AIProviderSettings, AIProviderBedrockSettings
  • CreateAIProviderRequest, UpdateAIProviderRequest
  • AIProviderKey, CreateAIProviderKeyRequest
  • Client.AIProviders, AIProvider, CreateAIProvider, UpdateAIProvider, DeleteAIProvider
  • Client.CreateAIProviderKey, DeleteAIProviderKey (no list method — keys are write-managed)

AIProviderSettings is a discriminated, versioned container: the JSON wire form carries _type and _version keys alongside the type-specific fields. Only providers that need custom configuration get a struct (just Bedrock for now); other types store SQL NULL.

Bedrock access_key / access_key_secret are write-only and never appear in responses. HTTP handlers are in a follow-up PR.

TestAIProviderSettings_Marshal / _Unmarshal / _Roundtrip cover the discriminator routing: null encode/decode, missing/unknown _type, unsupported version, bedrock encode and decode, malformed headers, and the reset-between-calls invariant.

Notes

  • biome.jsonc adds a noEmptyInterface exemption for typesGenerated.ts. AIProviderSettings.Bedrock is json:"-" (custom marshal handles the discriminator), so the generator emits an empty interface and Biome flags it; suppress at the generated-file level rather than restructure the wrapper.

@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch 2 times, most recently from b401da5 to 4a69dee Compare May 1, 2026 19:24
@dannykopping dannykopping changed the title feat(codersdk): add AIProvider types and client methods feat: add AIProvider types and client methods May 1, 2026
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 4a69dee to 8271800 Compare May 13, 2026 14:19
@dannykopping dannykopping force-pushed the dk/aibridge-providers-db branch from 0c4482a to eacc052 Compare May 13, 2026 14:38
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 8271800 to b697680 Compare May 13, 2026 14:39
@dannykopping dannykopping force-pushed the dk/aibridge-providers-db branch from eacc052 to 401fd51 Compare May 13, 2026 14:44
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from b697680 to 5221fc9 Compare May 13, 2026 14:44
@dannykopping dannykopping force-pushed the dk/aibridge-providers-db branch from 401fd51 to 78ea362 Compare May 13, 2026 15:05
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 5221fc9 to fb7c328 Compare May 13, 2026 15:06
@dannykopping dannykopping force-pushed the dk/aibridge-providers-db branch 2 times, most recently from 73ce74c to 1a61871 Compare May 14, 2026 09:42
@dannykopping dannykopping changed the base branch from dk/aibridge-providers-db to graphite-base/24893 May 14, 2026 10:57
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from fb7c328 to 9f6b23e Compare May 14, 2026 10:57
@dannykopping dannykopping changed the base branch from graphite-base/24893 to dk/aibridge-providers-dbcrypt May 14, 2026 10:58
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch from 2573348 to a650b5f Compare May 14, 2026 11:23
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 9f6b23e to 42dbe58 Compare May 14, 2026 11:23
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch from a650b5f to fffa3e9 Compare May 14, 2026 11:41
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 42dbe58 to 4f9930f Compare May 14, 2026 11:41
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch from fffa3e9 to 1d16ab9 Compare May 14, 2026 13:20
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 4f9930f to 02883ed Compare May 14, 2026 13:20
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch from 1d16ab9 to da18210 Compare May 14, 2026 13:38
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 02883ed to b79d62d Compare May 14, 2026 13:38
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch 2 times, most recently from 86dbfac to f78b55a Compare May 14, 2026 14:11
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from b79d62d to 6e813a9 Compare May 14, 2026 14:11
@dannykopping dannykopping force-pushed the dk/aibridge-providers-dbcrypt branch from f78b55a to f16f248 Compare May 15, 2026 11:39
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch 2 times, most recently from 984c734 to 5bfb65f Compare May 15, 2026 13:16
@dannykopping dannykopping changed the base branch from dk/aibridge-providers-dbcrypt to graphite-base/24893 May 15, 2026 13:26
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 5bfb65f to 80d35d3 Compare May 15, 2026 14:26
@dannykopping dannykopping force-pushed the graphite-base/24893 branch from 148bd38 to 5840ac5 Compare May 15, 2026 14:26
@dannykopping dannykopping changed the base branch from graphite-base/24893 to main May 15, 2026 14:26
Copy link
Copy Markdown
Contributor Author

/coder-agents-review

Copy link
Copy Markdown
Contributor

@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). The full review panel has not yet reviewed this PR; they will review after these findings are addressed.

Severity count: 1 P1, 1 P2, 1 P3, 1 Note.

The discriminated-union design for settings is well-structured, and the double-marshal pattern for injecting _type/_version keys is the right Go approach. The client methods follow established codersdk conventions cleanly.

The main gap is the custom JSON marshal/unmarshal logic: five distinct code paths (null input, missing discriminator, wrong version, unknown type, successful decode) with zero test coverage. The TS codegen limitation for the discriminated union is also worth addressing before consumers land.

"329 lines of new Go code across two files, 0 lines of test. Test:code ratio is 0:329." (Netero)

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/aiproviders.go
Comment thread site/src/api/typesGenerated.ts
Comment thread codersdk/aiproviders.go
Comment thread site/src/api/typesGenerated.ts
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from 80d35d3 to 69d019e Compare May 15, 2026 16:07
@dannykopping dannykopping marked this pull request as ready for review May 15, 2026 16:16
@dannykopping dannykopping requested a review from johnstcn May 15, 2026 16:17
dannykopping and others added 4 commits May 18, 2026 08:14
Adds the codersdk surface for runtime AI provider management. AI
Bridge is the initial consumer but the data model and HTTP API are
not aibridge-specific.

- AIProvider, AIProviderSettings, AIProviderType
- CreateAIProviderRequest, UpdateAIProviderRequest
- Client.AIProviders, AIProvider, CreateAIProvider, UpdateAIProvider,
  DeleteAIProvider

API key and Bedrock secret are write-only and never returned by the
server; the type definitions reflect that.

Refs RFC: https://www.notion.so/coderhq/RFC-Common-AI-Provider-Configs-34bd579be59280ed958feffb82024797
The keys table refactor (ai_provider_keys) moves api keys out of the
provider type and into a separate sub-resource. Update the codersdk
surface to match:

- AIProvider, CreateAIProviderRequest, UpdateAIProviderRequest no
  longer carry an APIKey field. AIProvider still includes Settings
  (Bedrock credentials live there).
- AIProviderSettings gains BedrockAccessKey alongside the existing
  BedrockAccessKeySecret so a Bedrock provider can carry both halves
  of its AWS credentials in one Settings blob.
- New AIProviderKey + CreateAIProviderKeyRequest types.
- New Client methods: AIProviderKeys, CreateAIProviderKey,
  DeleteAIProviderKey under /api/v2/ai/providers/{idOrName}/keys.
- typesGenerated.ts refreshed via make gen.
Convert AIProviderSettings from a flat Bedrock-only struct into a
discriminated container. On the wire each provider that needs custom
settings has its own struct (just Bedrock for now), and the JSON
object carries _type and _version discriminator keys.

- AIProviderSettings is now a wrapper struct with a single
  *AIProviderBedrockSettings field. Custom (Un)MarshalJSON routes
  between concrete types using the _type discriminator.
- AIProviderBedrockSettings carries the Bedrock-specific fields
  (Region, Model, SmallFastModel, AccessKey, AccessKeySecret) and
  the bedrock_ prefix on the JSON keys goes away — the discriminator
  carries that information.
- AIProviderSettings.IsZero() lets request bodies use json:",omitzero"
  instead of the non-functional omitempty on a struct field.

This is the SDK-side of the unification described in AIGOV-201; the
on-disk and wire formats now both look like
{"_type":"bedrock","_version":1,...} so future provider types can
be added without grepping every consumer.
Replace bare require.Error / loose Contains checks with specific
substring assertions so a regression in any of the discriminator
error paths fails the test instead of silently passing on an
unrelated error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@johnstcn johnstcn left a comment

Choose a reason for hiding this comment

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

One non-blocking comment.

Comment thread codersdk/aiproviders_test.go
Use require.JSONEq to compare the entire marshal output instead of
decoding into a map and checking individual fields. Any unexpected
change to the response (added field, removed field, type change) now
fails the test immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dannykopping dannykopping force-pushed the dk/aibridge-providers-sdk branch from b63803f to c76e3dc Compare May 18, 2026 08:49
@dannykopping dannykopping merged commit 0770428 into main May 18, 2026
24 of 26 checks passed
@dannykopping dannykopping deleted the dk/aibridge-providers-sdk branch May 18, 2026 09:10
@github-actions github-actions Bot locked and limited conversation to collaborators May 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants