Skip to content

feat(aigateway): add SDK support for AI Gateway resources#1402

Merged
toiroakr merged 3 commits into
mainfrom
feat/aigateway-sdk-support
Jun 16, 2026
Merged

feat(aigateway): add SDK support for AI Gateway resources#1402
toiroakr merged 3 commits into
mainfrom
feat/aigateway-sdk-support

Conversation

@dragon3

@dragon3 dragon3 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds defineAIGateway() for declaring AI Gateways in tailor.config.ts. Configure with authNamespace (required) and an optional cors allow-list; the returned object exposes url and domain getters that correspond to the deployed gateway endpoint.
  • Wires AI Gateways through the deploy pipeline: plan/apply against the operator's AI Gateway API, change-set printing, owner/unmanaged conflict detection, important-deletion confirmation, and parallel teardown via tailor remove.
  • aiGateway.cors accepts ${website.url} placeholders. planAIGateway and applyAIGateway resolve them via resolveStaticWebsiteUrls exactly like applyApplication does for app-level CORS, so first-deploy ordering and idempotent re-deploys both work.
  • Regenerates @tailor-platform/tailor-proto to pick up the public tailor.v1.AIGateway* messages and OperatorService RPCs (Create/Update/Delete/Get/List AIGateway). Other proto drift (auth, function_resource, meter, etc.) reflects unrelated upstream changes pulled in by buf generate.

Notes

gateway.url / gateway.domain return placeholder strings (${name}:aigateway-url, ${name}:aigateway-domain) like staticwebsite.url, but no resolver is wired for them yet — they don't auto-expand at deploy time. A resolveAIGatewayUrls parallel to resolveStaticWebsiteUrls can be added later if any consumer needs gateway URLs in its config (no current consumer in SDK config).

@dragon3 dragon3 self-assigned this Jun 11, 2026
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2db4d1f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tailor-platform/sdk Minor
@tailor-platform/create-sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dragon3 dragon3 force-pushed the feat/aigateway-sdk-support branch from f61144f to 8ebde2d Compare June 11, 2026 07:31
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

🗺️ ERD viewer preview

Self-contained HTML built for this run — open the artifact, then view it in your browser:

@pkg-pr-new

pkg-pr-new Bot commented Jun 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@tailor-platform/create-sdk@2db4d1f
pnpm add https://pkg.pr.new/@tailor-platform/sdk@2db4d1f

commit: 2db4d1f

@github-actions

This comment has been minimized.

Add `defineAIGateway()` for declaring AI Gateways in `tailor.config.ts`,
along with plan/apply/remove wiring through the deploy pipeline against the
operator's `tailor.v1.AIGateway*` RPCs. `aiGateway.cors` resolves
`${website.url}` placeholders via `resolveStaticWebsiteUrls`, mirroring how
`applyApplication` handles app-level CORS.

Regenerates `@tailor-platform/tailor-proto` to pick up the public AIGateway
messages and OperatorService RPCs; other proto drift is unrelated upstream
content pulled in by `buf generate`.
@dragon3 dragon3 force-pushed the feat/aigateway-sdk-support branch from 024ba69 to 84f783a Compare June 15, 2026 23:32
- Add `CreateAIGateway` to `RETRY_SAFE_CREATE_METHODS` — `applyAIGateway`
  discards the create response, so post-retry `AlreadyExists` can be safely
  swallowed as success.
- Replace `[...input.cors].sort()` with `input.cors.toSorted()` to satisfy
  the `unicorn/no-array-sort` lint rule.
@dragon3 dragon3 force-pushed the feat/aigateway-sdk-support branch from 84f783a to 71f10be Compare June 15, 2026 23:36
@github-actions

This comment has been minimized.

@dragon3 dragon3 marked this pull request as ready for review June 16, 2026 04:39
@dragon3 dragon3 requested a review from a team June 16, 2026 04:39

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📖 Docs Quality & Consistency Check

⚠️ Consistency Issues

File Issue Suggested Fix
packages/sdk/docs/services/aigateway.md Lines 70-73 claim gateway.url and gateway.domain "correspond to the gateway's deployed endpoint" with descriptions like "the full base URL" and "the hostname", but these return unresolved placeholder strings (${name}:aigateway-url, ${name}:aigateway-domain) per the PR description and implementation Clarify that these return placeholder strings that can be used in type-safe configuration. Consider matching the static website documentation pattern or explicitly noting that resolution is not yet implemented
packages/sdk/docs/services/aigateway.md Line 24 documents a name pattern constraint (^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$) but packages/sdk/src/parser/service/aigateway/schema.ts does not validate this pattern (unlike HTTP adapter and secrets which do enforce their patterns) Either add regex validation to the schema, or clarify in docs that this is a platform-side constraint not validated by the SDK
AGENTS.md Line 73 lists defineStaticWebSite in the example file description but does not include defineAIGateway which is now also present in example/tailor.config.ts Update line 73 to include defineAIGateway in the list: defineConfig, defineAuth, defineIdp, defineStaticWebSite, defineAIGateway, definePlugins

Details

1. gateway.url / gateway.domain placeholder behavior (lines 70-73 of aigateway.md)

Current documentation states:

- `gateway.url` — the full base URL of the deployed gateway.
- `gateway.domain` — the hostname of the deployed gateway.

Actual behavior:
The implementation in packages/sdk/src/configure/services/aigateway/index.ts (lines 16-21) returns placeholder strings:

get url() {
  return `${name}:aigateway-url` as const;
},
get domain() {
  return `${name}:aigateway-domain` as const;
},

The PR description explicitly states:

gateway.url / gateway.domain return placeholder strings (${name}:aigateway-url, ${name}:aigateway-domain) like staticwebsite.url, but no resolver is wired for them yet — they don't auto-expand at deploy time.

Unlike static website URLs which are resolved via resolveStaticWebsiteUrls when used in CORS arrays or redirect URIs (as seen in packages/sdk/src/cli/commands/deploy/aigateway.ts lines 38, 48, 155), there is no resolveAIGatewayUrls function in the codebase. The placeholders remain unresolved.

Impact: Users reading the docs will expect these properties to return actual deployed URLs (like https://my-gateway.example.com), but they actually get placeholder strings like my-gateway:aigateway-url. The example on lines 80-82 shows usage but doesn't clarify what these properties actually return.

Recommended fix: Update the documentation to accurately describe the placeholder behavior. Options:

  1. Explicitly state they return placeholder strings for type-safe configuration (similar to how static website URLs work)
  2. Note that resolution is not yet implemented (as the PR description mentions)
  3. Remove the "deployed gateway endpoint" claim until resolution is implemented

2. Name pattern validation (line 24 of aigateway.md)

Current documentation states:

- **Name pattern**: `name` must match `^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$` (lowercase alphanumeric and hyphens, 3-30 characters)

Actual behavior:
The AIGatewaySchema in packages/sdk/src/parser/service/aigateway/schema.ts (lines 4-16) does not enforce this pattern:

export const AIGatewaySchema = z
  .object({
    name: z.string().describe("AI Gateway name"),
    authNamespace: z.string().describe("..."),
    cors: z.array(z.string()).optional().describe("..."),
  })
  .brand("AIGatewayConfig");

By contrast, HTTP adapter (packages/sdk/src/parser/service/http-adapter/schema.ts:4) and secrets (packages/sdk/src/parser/service/secrets/schema.ts:3-4) DO enforce similar patterns with regex validation.

Impact: If this is a platform-side constraint, SDK validation would provide earlier, clearer error messages. If it's not actually a constraint, the docs are incorrect.

Recommended fix: Either:

  1. Add regex validation to the schema (like HTTP adapter and secrets)
  2. Clarify in the docs that this is a platform-enforced constraint, not validated SDK-side
  3. Remove the constraint from the docs if it's not actually enforced anywhere

3. AGENTS.md missing defineAIGateway reference

Current state:
Line 73 of AGENTS.md describes the example config file:

- `example/tailor.config.ts` - Configuration with defineConfig, defineAuth, defineIdp, defineStaticWebSite, definePlugins

Actual state:
example/tailor.config.ts now imports and uses defineAIGateway (lines 2 and 23-26):

import {
  defineAIGateway,
  defineAuth,
  defineConfig,
  defineIdp,
  ...
}

const aiGateway = defineAIGateway("my-aigateway", {
  authNamespace: "default",
  cors: [website.url],
});

Impact: Developers reading AGENTS.md won't see that AI Gateway is demonstrated in the example.

Recommended fix: Update AGENTS.md line 73 to include defineAIGateway in the list.

Recommended Actions

  1. Update packages/sdk/docs/services/aigateway.md lines 70-73 to accurately describe the placeholder behavior of gateway.url and gateway.domain
  2. Either add regex validation to AIGatewaySchema or clarify in docs that the name pattern is platform-enforced
  3. Update AGENTS.md line 73 to include defineAIGateway

Re-run this check by adding the docs-check label to the PR.


Comment on lines +16 to +21
get url() {
return `${name}:aigateway-url` as const;
},
get domain() {
return `${name}:aigateway-domain` as const;
},

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.

gateway.url / gateway.domain return placeholder strings (${name}:aigateway-url, ${name}:aigateway-domain) like staticwebsite.url, but no resolver is wired for them yet — they don't auto-expand at deploy time. A resolveAIGatewayUrls parallel to resolveStaticWebsiteUrls can be added later if any consumer needs gateway URLs in its config (no current consumer in SDK config).

Perhaps it would be better not to provide this interface itself at this point?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed — dropped both url and domain getters in 2db4d1f. They returned only placeholder strings (${name}:aigateway-url) with no resolver wired anywhere, so the surface was advertising something it didn't deliver. We can add them back alongside a real resolveAIGatewayUrls when a consumer needs them in config (CORS / OAuth redirect URIs / etc).

Comment on lines +204 to +205
Object.entries(existingGateways).forEach(([name]) => {
const entry = existingGateways[name];

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.

nits

Suggested change
Object.entries(existingGateways).forEach(([name]) => {
const entry = existingGateways[name];
Object.entries(existingGateways).forEach(([name, entry]) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied in 2db4d1f, thanks.

- Remove `aiGateway.url` / `aiGateway.domain` getters from `defineAIGateway`
  and the corresponding section in `aigateway.md`. They returned literal
  placeholder strings (`${name}:aigateway-url`) with no resolver wired
  anywhere, so the docs claim of returning "the full base URL / hostname
  of the deployed gateway" was inaccurate. The interface can come back
  alongside a real `resolveAIGatewayUrls` when there is a consumer for it.
- Enforce the platform's `name` (`^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$`) and
  `authNamespace` (`^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$`) patterns in
  `AIGatewaySchema`, matching how the secrets and http-adapter schemas
  validate their own name shapes. Surfaces invalid input at config-load
  time instead of a server round-trip.
- Inline `existingGateways[name]` into the `Object.entries` destructure
  in `planAIGateway`.
- Add `defineAIGateway` to the `AGENTS.md` example file description.
- Rename `gateway` / `"my-gateway"` to `aiGateway` / `"my-aigateway"` in
  `aigateway.md` examples so the binding type is obvious in isolation.
@github-actions

Copy link
Copy Markdown

Code Metrics Report (packages/sdk)

main (02f78c6) #1402 (8d61f82) +/-
Coverage 68.5% 68.6% +0.0%
Code to Test Ratio 1:0.4 1:0.4 -0.1
Details
  |                    | main (02f78c6) | #1402 (8d61f82) |  +/-  |
  |--------------------|----------------|-----------------|-------|
+ | Coverage           |          68.5% |           68.6% | +0.0% |
  |   Files            |            416 |             420 |    +4 |
  |   Lines            |          14699 |           14772 |   +73 |
+ |   Covered          |          10083 |           10134 |   +51 |
- | Code to Test Ratio |          1:0.4 |           1:0.4 |  -0.1 |
  |   Code             |          98655 |           99148 |  +493 |
+ |   Test             |          43992 |           44181 |  +189 |

Code coverage of files in pull request scope (73.6% → 73.2%)

Files Coverage +/- Status
packages/sdk/src/cli/commands/deploy/aigateway.ts 78.7% +78.7% added
packages/sdk/src/cli/commands/deploy/deploy.ts 83.4% -0.3% modified
packages/sdk/src/cli/commands/deploy/label.ts 100.0% 0.0% modified
packages/sdk/src/cli/commands/remove.ts 1.3% -0.1% modified
packages/sdk/src/cli/services/application.ts 80.7% -2.2% modified
packages/sdk/src/cli/shared/client.ts 81.2% 0.0% modified
packages/sdk/src/configure/services/aigateway/index.ts 0.0% 0.0% added
packages/sdk/src/configure/services/index.ts 0.0% 0.0% modified
packages/sdk/src/parser/app-config/schema.ts 100.0% 0.0% modified
packages/sdk/src/parser/service/aigateway/index.ts 0.0% 0.0% added
packages/sdk/src/parser/service/aigateway/schema.ts 100.0% +100.0% added
packages/sdk/src/types/app-config.ts 0.0% 0.0% modified

SDK Configure Bundle Size

main (02f78c6) #1402 (8d61f82) +/-
configure-index-size 19.73KB 20.08KB 0.35KB
dependency-chunks-size 47.71KB 47.71KB 0KB
total-bundle-size 67.44KB 67.79KB 0.35KB

Runtime Performance

main (02f78c6) #1402 (8d61f82) +/-
Generate Median 2,912ms 2,832ms -80ms
Generate Max 2,946ms 2,976ms 30ms
Apply Build Median 2,976ms 2,905ms -71ms
Apply Build Max 3,049ms 2,987ms -62ms

Type Performance (instantiations)

main (02f78c6) #1402 (8d61f82) +/-
tailordb-basic 36,061 36,086 25
tailordb-optional 3,841 3,841 0
tailordb-relation 5,921 5,921 0
tailordb-validate 2,566 2,566 0
tailordb-hooks 5,767 5,767 0
tailordb-object 12,136 12,136 0
tailordb-enum 2,462 2,462 0
resolver-basic 9,424 9,424 0
resolver-nested 26,111 26,111 0
resolver-array 18,187 18,187 0
executor-schedule 4,234 4,234 0
executor-webhook 873 873 0
executor-record 6,659 6,659 0
executor-resolver 4,369 4,369 0
executor-operation-function 868 868 0
executor-operation-gql 869 869 0
executor-operation-webhook 888 888 0
executor-operation-workflow 1,714 1,714 0

Reported by octocov

@toiroakr toiroakr 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.

LGTM 👍
Thank you!

@toiroakr toiroakr merged commit 108dc6e into main Jun 16, 2026
45 checks passed
@toiroakr toiroakr deleted the feat/aigateway-sdk-support branch June 16, 2026 06:39
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.

2 participants