feat(aigateway): add SDK support for AI Gateway resources#1402
Conversation
🦋 Changeset detectedLatest commit: 2db4d1f The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
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 |
f61144f to
8ebde2d
Compare
🗺️ ERD viewer previewSelf-contained HTML built for this run — open the artifact, then view it in your browser:
|
commit: |
This comment has been minimized.
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`.
024ba69 to
84f783a
Compare
- 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.
84f783a to
71f10be
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
📖 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.domainreturn placeholder strings (${name}:aigateway-url,${name}:aigateway-domain) likestaticwebsite.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:
- Explicitly state they return placeholder strings for type-safe configuration (similar to how static website URLs work)
- Note that resolution is not yet implemented (as the PR description mentions)
- 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:
- Add regex validation to the schema (like HTTP adapter and secrets)
- Clarify in the docs that this is a platform-enforced constraint, not validated SDK-side
- 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, definePluginsActual 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
- Update
packages/sdk/docs/services/aigateway.mdlines 70-73 to accurately describe the placeholder behavior ofgateway.urlandgateway.domain - Either add regex validation to
AIGatewaySchemaor clarify in docs that the name pattern is platform-enforced - Update
AGENTS.mdline 73 to includedefineAIGateway
Re-run this check by adding the
docs-checklabel to the PR.
| get url() { | ||
| return `${name}:aigateway-url` as const; | ||
| }, | ||
| get domain() { | ||
| return `${name}:aigateway-domain` as const; | ||
| }, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
| Object.entries(existingGateways).forEach(([name]) => { | ||
| const entry = existingGateways[name]; |
There was a problem hiding this comment.
nits
| Object.entries(existingGateways).forEach(([name]) => { | |
| const entry = existingGateways[name]; | |
| Object.entries(existingGateways).forEach(([name, entry]) => { |
- 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.
Code Metrics Report (packages/sdk)
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%)
SDK Configure Bundle Size
Runtime Performance
Type Performance (instantiations)
Reported by octocov |
Summary
defineAIGateway()for declaring AI Gateways intailor.config.ts. Configure withauthNamespace(required) and an optionalcorsallow-list; the returned object exposesurlanddomaingetters that correspond to the deployed gateway endpoint.tailor remove.aiGateway.corsaccepts${website.url}placeholders.planAIGatewayandapplyAIGatewayresolve them viaresolveStaticWebsiteUrlsexactly likeapplyApplicationdoes for app-level CORS, so first-deploy ordering and idempotent re-deploys both work.@tailor-platform/tailor-prototo pick up the publictailor.v1.AIGateway*messages andOperatorServiceRPCs (Create/Update/Delete/Get/List AIGateway). Other proto drift (auth, function_resource, meter, etc.) reflects unrelated upstream changes pulled in bybuf generate.Notes
gateway.url/gateway.domainreturn placeholder strings (${name}:aigateway-url,${name}:aigateway-domain) likestaticwebsite.url, but no resolver is wired for them yet — they don't auto-expand at deploy time. AresolveAIGatewayUrlsparallel toresolveStaticWebsiteUrlscan be added later if any consumer needs gateway URLs in its config (no current consumer in SDK config).