Skip to content

Commit 4ca7256

Browse files
tombeckenhamclaude
andcommitted
feat(schemas): add @tanstack/ai-schemas with nightly OpenAPI sync (closes #619)
JSON Schema + Zod definitions for AI provider generation endpoints, generated from each provider's official OpenAPI spec (or equivalent): OpenAI, Anthropic, Gemini, ElevenLabs, xAI Grok, and FAL (600+ models). Endpoints are grouped per provider by activity, aligned with the core library's activities — chat, image, video, audio (TTS, transcription, music, SFX in one group), embeddings, moderation — behind @tanstack/ai-schemas/{provider}/{activity}/{json-schema,zod} subpaths. Platform/admin endpoints (projects, invites, certificates, fine-tuning, file stores, workspaces) are excluded from generation. JSON Schemas ship self-contained with their $ref closures bundled under $defs; binary- response media endpoints map input-only entries. Pipeline (ported from fal-ai/fal-js#212, generalised multi-provider): fetch-schemas (per-provider fetchers) → @hey-api/openapi-ts (pinned, JSON Schema + Zod 4 plugins, per-activity spec splits with orphan pruning) → generate-endpoint-maps ($defs bundling, endpoint-id-keyed maps). Post-processing strips hey-api's schema-name discriminator grafts (which made discriminated unions reject every valid payload), accepts multipart/form-data bodies, and resolves dedup-renamed schema refs. .github/workflows/sync-schemas.yml refreshes specs daily and opens an automated PR on diff, following the sync-models.yml pattern. FAL specs are committed like the other providers'; the fetcher needs FAL_KEY (repo secret) and the plain `expand=openapi-3.0` query param. Squashed and rebased onto the flattened packages/* monorepo layout; the package lives at packages/ai-schemas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ff267a5 commit 4ca7256

199 files changed

Lines changed: 1461412 additions & 4 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/initial-ai-schemas.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@tanstack/ai-schemas': minor
3+
---
4+
5+
Initial release of `@tanstack/ai-schemas` — JSON Schema and Zod schemas for AI provider generation endpoints, generated nightly from upstream OpenAPI specs.
6+
7+
Covers OpenAI, Anthropic, Gemini, ElevenLabs, xAI Grok, and FAL. Endpoints are grouped per provider by activity — `chat`, `image`, `video`, `audio` (TTS, transcription, music, sound effects in one group), `embeddings`, `moderation` — behind `@tanstack/ai-schemas/{provider}/{activity}/{json-schema,zod}` subpaths. Platform/admin endpoints are excluded from generation. Architecture ported from fal-ai/fal-js PR #212; extended to every provider that publishes an OpenAPI spec.
8+
9+
Sources:
10+
11+
- OpenAI: `github.com/openai/openai-openapi`
12+
- Anthropic: Stainless OpenAPI resolved via `anthropic-sdk-typescript/.stats.yml`
13+
- Gemini: Google Generative Language Discovery doc, converted to OpenAPI in-pipeline
14+
- ElevenLabs: `api.elevenlabs.io/openapi.json`
15+
- xAI Grok: `docs.x.ai/openapi.json`
16+
- FAL: per-model OpenAPI from the FAL models API (requires `FAL_KEY`)
17+
18+
The `.github/workflows/sync-schemas.yml` workflow runs daily and opens a PR when any provider's spec changes. Resolves #619.

.github/workflows/sync-schemas.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Sync Provider Schemas
2+
3+
on:
4+
schedule:
5+
# 05:00 UTC daily — one hour before sync-models.yml so the two automated
6+
# PRs don't contend on the changeset / branch push at the same minute.
7+
- cron: '0 5 * * *'
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
jobs:
19+
sync:
20+
name: Sync Schemas
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
25+
with:
26+
fetch-depth: 0
27+
persist-credentials: true
28+
29+
- name: Setup Tools
30+
uses: TanStack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 # main
31+
32+
- name: Fetch provider OpenAPI specs
33+
run: pnpm --filter @tanstack/ai-schemas fetch-schemas
34+
env:
35+
FAL_KEY: ${{ secrets.FAL_KEY }}
36+
37+
- name: Generate JSON Schemas + Zod
38+
run: pnpm --filter @tanstack/ai-schemas generate-schemas
39+
40+
- name: Generate endpoint maps and barrels
41+
run: pnpm --filter @tanstack/ai-schemas generate-endpoint-maps
42+
43+
- name: Check for package changes
44+
id: changes
45+
run: |
46+
if git diff --quiet -- packages/ai-schemas/; then
47+
echo "changed=false" >> $GITHUB_OUTPUT
48+
else
49+
echo "changed=true" >> $GITHUB_OUTPUT
50+
fi
51+
52+
- name: Write changeset and commit
53+
if: steps.changes.outputs.changed == 'true'
54+
run: |
55+
git config user.name "github-actions[bot]"
56+
git config user.email "github-actions[bot]@users.noreply.github.com"
57+
mkdir -p .changeset
58+
cat > .changeset/automated-sync-schemas.md <<'EOF'
59+
---
60+
"@tanstack/ai-schemas": patch
61+
---
62+
63+
Nightly sync of provider OpenAPI schemas.
64+
EOF
65+
git add packages/ai-schemas/ .changeset/
66+
git commit -m "chore(schemas): sync provider OpenAPI schemas"
67+
git push --force origin HEAD:automated/sync-schemas
68+
69+
- name: Create or update PR
70+
if: steps.changes.outputs.changed == 'true'
71+
env:
72+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73+
shell: bash
74+
run: |
75+
BRANCH="automated/sync-schemas"
76+
EXISTING_PR=$(gh pr list --head "$BRANCH" --base main --json number --jq '.[0].number' 2>/dev/null || true)
77+
if [ -z "$EXISTING_PR" ] || [ "$EXISTING_PR" = "null" ]; then
78+
BODY=$(cat <<'PRBODY'
79+
Automated nightly sync of provider OpenAPI schemas.
80+
81+
- Fetches upstream specs (OpenAI, Anthropic, Gemini, ElevenLabs, FAL).
82+
- Runs `@hey-api/openapi-ts` to regenerate JSON Schemas + Zod.
83+
- Bundles each schema's `$defs` closure and regenerates endpoint maps.
84+
- Patch changeset for `@tanstack/ai-schemas`.
85+
86+
Providers with missing secrets are skipped; the diff reflects only the
87+
providers whose specs the workflow could fetch.
88+
PRBODY
89+
)
90+
gh pr create \
91+
--title "chore(schemas): sync provider OpenAPI schemas" \
92+
--body "$BODY" \
93+
--base main \
94+
--head "$BRANCH"
95+
fi

docs/advanced/ai-schemas.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
title: '@tanstack/ai-schemas: provider endpoint schemas'
3+
id: ai-schemas
4+
---
5+
6+
`@tanstack/ai-schemas` is a separate package that ships **JSON Schema** and **Zod** schemas for the generation endpoints of every supported provider (OpenAI, Anthropic, Gemini, ElevenLabs, xAI Grok, FAL). The schemas are generated nightly from each provider's official OpenAPI spec, so they track upstream changes automatically.
7+
8+
It complements the `model-meta.ts` shipped with each provider adapter: where `model-meta` describes coarse facts (context window, modalities, pricing), `ai-schemas` describes the rich per-endpoint constraint surface — allowed video durations, image sizes, voice IDs, prompt-length caps, etc.
9+
10+
## When to reach for it
11+
12+
- **Runtime validation before submitting a request** — surface bad inputs client-side instead of paying a round-trip and waiting on a vague upstream error.
13+
- **Discovering allowed values** — populate dropdowns or pick lists with the exact enum a model accepts.
14+
- **Feeding an LLM tool API** — each JSON Schema is self-contained (its `$ref` closure is bundled under `$defs`), so it can be passed straight to OpenAI / Anthropic / Gemini tool-use APIs without extra resolution.
15+
- **Optimising prompts across models** — the schemas expose every model's constraints in a comparable shape, which is the foundation for prompt-portability helpers.
16+
17+
## Install
18+
19+
```bash
20+
pnpm add @tanstack/ai-schemas
21+
# Optional: only required if you import from `@tanstack/ai-schemas/{provider}/{activity}/zod`
22+
pnpm add zod
23+
```
24+
25+
## Activity groups
26+
27+
Each provider's endpoints are grouped by **activity**, matching the core library's activities (chat, generateImage, generateVideo, and the audio family — collapsed into a single `audio` group):
28+
29+
| Activity | Covers |
30+
| ------------ | ----------------------------------------------------------------------- |
31+
| `chat` | Text generation: chat/completions, responses, messages, generateContent |
32+
| `image` | Image generation and edits |
33+
| `video` | Video generation, edits, extensions |
34+
| `audio` | All audio: TTS, transcription, music, sound effects, voices, dubbing |
35+
| `embeddings` | Embedding endpoints |
36+
| `moderation` | Moderation endpoints |
37+
38+
Platform/admin endpoints (projects, invites, certificates, fine-tuning jobs, file stores, …) are excluded from generation entirely — they aren't generation constraint surface.
39+
40+
## Subpath imports
41+
42+
Provider-first — pick the provider, then the activity, then `json-schema` or `zod`:
43+
44+
```ts
45+
// JSON Schemas (no `zod` peer required).
46+
import { geminiChatEndpointSchemaMap } from '@tanstack/ai-schemas/gemini/chat/json-schema'
47+
48+
// Zod (requires `zod ^4`).
49+
import { openaiChatEndpointZodMap } from '@tanstack/ai-schemas/openai/chat/zod'
50+
import { elevenlabsAudioEndpointZodMap } from '@tanstack/ai-schemas/elevenlabs/audio/zod'
51+
import { falVideoEndpointZodMap } from '@tanstack/ai-schemas/fal/video/zod'
52+
```
53+
54+
There is **no aggregator barrel**. `import … from '@tanstack/ai-schemas/gemini/chat/json-schema'` only ships Gemini's chat JSON Schemas — no other provider's or activity's bytes leak into the consumer's bundle.
55+
56+
Endpoints that stream binary media (e.g. ElevenLabs text-to-speech) map only an `input` schema — there is no JSON output to describe.
57+
58+
## Validate a video-generation request
59+
60+
```ts
61+
import { falVideoEndpointZodMap } from '@tanstack/ai-schemas/fal/video/zod'
62+
63+
const result = falVideoEndpointZodMap[
64+
'fal-ai/kling-video/o3/pro/text-to-video'
65+
].input.safeParse({
66+
prompt: 'A mecha lands on the ground to save the city, in anime style',
67+
duration: '8',
68+
aspect_ratio: '9:16',
69+
})
70+
71+
if (!result.success) console.error(result.error.issues)
72+
```
73+
74+
## Discover what a model supports
75+
76+
```ts
77+
import { KlingVideoO3ProTextToVideoInputSchema } from '@tanstack/ai-schemas/fal/video/json-schema'
78+
79+
KlingVideoO3ProTextToVideoInputSchema.properties.duration.enum
80+
// ['3', '4', …, '15']
81+
82+
KlingVideoO3ProTextToVideoInputSchema.properties.aspect_ratio.enum
83+
// ['16:9', '9:16', '1:1']
84+
```
85+
86+
## OpenAI structured-outputs strict mode
87+
88+
```ts
89+
import { toOpenAIStrict } from '@tanstack/ai-schemas/openai-strict'
90+
import { Veo3InputSchema } from '@tanstack/ai-schemas/fal/video/json-schema'
91+
92+
await openai.chat.completions.create({
93+
model: 'gpt-5',
94+
messages: [...],
95+
response_format: {
96+
type: 'json_schema',
97+
json_schema: {
98+
name: 'veo3_input',
99+
schema: toOpenAIStrict(Veo3InputSchema),
100+
strict: true,
101+
},
102+
},
103+
})
104+
```
105+
106+
## How it stays current
107+
108+
The `.github/workflows/sync-schemas.yml` workflow runs daily and:
109+
110+
1. Fetches upstream OpenAPI specs for every provider.
111+
2. Re-runs `@hey-api/openapi-ts` to regenerate JSON Schemas + Zod.
112+
3. Bundles each schema's `$ref` closure, regenerates endpoint maps.
113+
4. If anything changed: bumps the package patch version, writes a changeset, opens an automated PR.
114+
115+
Provider sources:
116+
117+
| Provider | Source | Notes |
118+
| ---------- | ------------------------------------------------------------------------------- | ---------------------------------------------------- |
119+
| OpenAI | `github.com/openai/openai-openapi` | Public, no API key required. |
120+
| Anthropic | Stainless-generated OpenAPI (resolved via `anthropic-sdk-typescript/.stats.yml`) | Public. |
121+
| Gemini | `generativelanguage.googleapis.com/$discovery/rest?version=v1beta` | Google Discovery doc converted to OpenAPI in pipeline. |
122+
| ElevenLabs | `api.elevenlabs.io/openapi.json` | Public. |
123+
| xAI Grok | `docs.x.ai/openapi.json` | Public. |
124+
| FAL | `api.fal.ai/v1/models?status=active&expand=openapi-3.0` (per-model) | Needs `FAL_KEY`. Model categories regroup into the shared activities. |

docs/config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,11 @@
326326
"label": "Typed Pre-Configured Options",
327327
"to": "advanced/typed-options",
328328
"addedAt": "2026-05-25"
329+
},
330+
{
331+
"label": "Provider Endpoint Schemas",
332+
"to": "advanced/ai-schemas",
333+
"addedAt": "2026-06-11"
329334
}
330335
]
331336
},

knip.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@
4444
},
4545
"packages/ai-vue-ui": {
4646
"ignore": ["src/use-chat-context.ts"]
47+
},
48+
"packages/ai-schemas": {
49+
"ignore": [
50+
"src/providers/**/*.gen.ts",
51+
"scripts/**",
52+
"openapi-ts.config.ts"
53+
],
54+
"ignoreDependencies": ["zod", "vite"]
4755
}
4856
}
4957
}

packages/ai-schemas/README.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# @tanstack/ai-schemas
2+
3+
Runtime schemas for AI provider endpoints. Ships **JSON Schema** definitions for tooling plus **Zod** schemas for runtime validation — no hand-maintained TypeScript types. Derive types from Zod with `z.infer<typeof someSchema>`.
4+
5+
Schemas are generated nightly from each provider's official OpenAPI spec (or equivalent), so the package tracks upstream changes automatically.
6+
7+
## Install
8+
9+
```bash
10+
pnpm add @tanstack/ai-schemas
11+
# Zod is an optional peer; only required if you import from `@tanstack/ai-schemas/{provider}/{activity}/zod`.
12+
pnpm add zod
13+
```
14+
15+
## Providers covered
16+
17+
| Provider | Source | Notes |
18+
| ---------- | --------------------------------------------------------------------------- | ------------------------------------------------------ |
19+
| OpenAI | `github.com/openai/openai-openapi` (raw `openapi.yaml`) | Public, no API key required. |
20+
| Anthropic | Official OpenAPI from `anthropic-sdk-typescript` repo | Public. |
21+
| Gemini | `generativelanguage.googleapis.com/$discovery/rest?version=v1beta` | Google Discovery doc converted to OpenAPI in-pipeline. |
22+
| ElevenLabs | `api.elevenlabs.io/openapi.json` | Public. |
23+
| xAI Grok | `docs.x.ai/openapi.json` | Public. |
24+
| FAL | `api.fal.ai/v1/models?status=active&expand=openapi-3.0` (per-model OpenAPI) | Needs `FAL_KEY` to fetch. |
25+
26+
Other OpenAI-compatible providers (e.g. Groq) reuse the OpenAI schemas.
27+
28+
## Activity groups
29+
30+
Each provider's endpoints are grouped by **activity**, matching the core library's activities (`@tanstack/ai`'s chat, generateImage, generateVideo, and the audio family):
31+
32+
| Activity | Covers |
33+
| ------------ | ----------------------------------------------------------------------- |
34+
| `chat` | Text generation: chat/completions, responses, messages, generateContent |
35+
| `image` | Image generation and edits |
36+
| `video` | Video generation, edits, extensions |
37+
| `audio` | All audio: TTS, transcription, music, sound effects, voices, dubbing |
38+
| `embeddings` | Embedding endpoints |
39+
| `moderation` | Moderation endpoints |
40+
41+
Not every provider has every group — `ls node_modules/@tanstack/ai-schemas/dist/esm/providers/<provider>` or your editor's import autocomplete shows what exists. Platform/admin endpoints (projects, invites, certificates, fine-tuning jobs, file stores, workspaces, …) are excluded from generation entirely: they aren't generation constraint surface, and dropping them keeps the shipped schemas lean.
42+
43+
## Entry points
44+
45+
ES modules only. Provider-first subpaths — pick the provider, then the activity, then the format:
46+
47+
```ts
48+
// JSON Schemas (no `zod` peer required).
49+
import { openaiChatEndpointSchemaMap } from '@tanstack/ai-schemas/openai/chat/json-schema'
50+
51+
// Zod (requires `zod ^4`).
52+
import { openaiChatEndpointZodMap } from '@tanstack/ai-schemas/openai/chat/zod'
53+
import { elevenlabsAudioEndpointZodMap } from '@tanstack/ai-schemas/elevenlabs/audio/zod'
54+
55+
// OpenAI structured-outputs strict-mode helper.
56+
import { toOpenAIStrict } from '@tanstack/ai-schemas/openai-strict'
57+
```
58+
59+
There is **no aggregator barrel** — provider-first, activity-grouped imports mean bundlers tree-shake by file. Importing `@tanstack/ai-schemas/openai/image/json-schema` ships only OpenAI's image schemas; no chat, no other providers, no platform noise.
60+
61+
## Examples
62+
63+
Validate a chat request before hitting the network:
64+
65+
```ts
66+
import { openaiChatEndpointZodMap } from '@tanstack/ai-schemas/openai/chat/zod'
67+
68+
const result = openaiChatEndpointZodMap['chat/completions'].input.safeParse({
69+
model: 'gpt-4o',
70+
messages: [{ role: 'user', content: 'hi' }],
71+
})
72+
73+
if (!result.success) console.error(result.error.issues)
74+
```
75+
76+
Validate a video generation request (FAL):
77+
78+
```ts
79+
import { falVideoEndpointZodMap } from '@tanstack/ai-schemas/fal/video/zod'
80+
81+
const result = falVideoEndpointZodMap[
82+
'fal-ai/kling-video/o3/pro/text-to-video'
83+
].input.safeParse({
84+
prompt: 'A mecha lands on the ground to save the city, in anime style',
85+
duration: '8',
86+
aspect_ratio: '9:16',
87+
})
88+
```
89+
90+
Discover what a model supports (build a duration picker):
91+
92+
```ts
93+
import { KlingVideoO3ProTextToVideoInputSchema } from '@tanstack/ai-schemas/fal/video/json-schema'
94+
95+
KlingVideoO3ProTextToVideoInputSchema.properties.duration.enum
96+
// ['3', '4', …, '15']
97+
```
98+
99+
Media generation endpoints that stream binary audio/video (e.g. ElevenLabs `v1/text-to-speech/{voice_id}`) map only an `input` schema — there is no JSON output to describe.
100+
101+
## Bundle size and tree-shaking
102+
103+
The package is `sideEffects: false` and JSON Schemas ship self-contained — each schema bundles its `$ref` closure under `$defs`. Importing one schema pulls only that schema's transitive closure, not the whole activity group. The provider-first subpaths mean an `import … from '@tanstack/ai-schemas/openai/chat/json-schema'` carries no other provider's or activity's bytes.
104+
105+
## How updates work
106+
107+
The `.github/workflows/sync-schemas.yml` workflow runs nightly:
108+
109+
1. `fetch-schemas` — pulls upstream OpenAPI specs (or equivalents) per provider.
110+
2. `generate-schemas` — runs `@hey-api/openapi-ts` to emit per-(provider, activity) `schemas.gen.ts` (JSON Schemas) and `zod.gen.ts` (Zod).
111+
3. `generate-endpoint-maps` — emits endpoint-id-keyed maps and bundles `$defs` closures into each JSON Schema.
112+
113+
If any provider's spec changes, the workflow bumps the package version, creates a changeset, and opens an automated PR.
114+
115+
## Local development
116+
117+
```bash
118+
# Pull every provider's spec.
119+
pnpm fetch-schemas
120+
121+
# Pull a single provider.
122+
pnpm fetch-schemas --provider=openai
123+
124+
# Full regeneration.
125+
pnpm update-schemas
126+
```
127+
128+
`FAL_KEY` must be set in your environment to fetch FAL specs.

0 commit comments

Comments
 (0)