Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
0a89d62e chore(changeset): include all six framework packages bumped by PR #577
The structured-output-as-message-part changeset only listed
`@tanstack/ai`, `@tanstack/ai-client`, and `@tanstack/ai-react` from
when the original PR was scoped to React. Now that the same per-message
typed `StructuredOutputPart` design, schema-generic threading through
`UIMessage<TTools, TData>`, and `partial`/`final` derivation has landed
in `@tanstack/ai-vue`, `@tanstack/ai-solid`, and `@tanstack/ai-svelte`,
the changeset needs to bump those at minor too — otherwise the parity
ships without a version bump and consumers on Vue/Solid/Svelte get the
new behavior without notice in their lockfile diff.
Added the three framework packages at `minor` (matching the other
three) and expanded the description to:
- Cover all four hook surfaces (`useChat` for React/Vue/Solid,
`createChat` for Svelte) instead of just `useChat`.
- Document the schema-generic threading (`StructuredOutputPart<TData
= unknown>`, `MessagePart<TTools, TData>`, `UIMessage<TTools,
TData>`) since that's the user-facing shape change agents reading
the release notes care about. Default `TData = unknown` preserves
source compatibility for consumers who don't pass a schema.
- Note the adapter fallback path (Anthropic / Gemini / Ollama still
emit one terminal `structured-output.complete` and the per-turn
typed part still lands — consumer code is identical).
- Add a "breaking-shape note" callout that explicitly stays minor:
with `outputSchema` set, `TEXT_MESSAGE_CONTENT` deltas no longer
create a `TextPart`. The old "filter out TextPart to hide JSON"
workaround the prior docs recommended is now a no-op (and removable),
not a regression — no `TextPart` is produced.
Verified via `pnpm exec changeset status`: six packages bump at minor
(the ones listed above), no major bumps, downstream consumers
auto-patch via `updateInternalDependencies: "patch"`.
The sibling `use-chat-reset-on-action.md` changeset is from PR #576
(merged earlier into this branch) — left untouched; it documents an
ai-react-only fix at patch severity and that's accurate for that
change. 0a89d62e chore(changeset): include all six framework packages bumped by PR #577
The structured-output-as-message-part changeset only listed
`@tanstack/ai`, `@tanstack/ai-client`, and `@tanstack/ai-react` from
when the original PR was scoped to React. Now that the same per-message
typed `StructuredOutputPart` design, schema-generic threading through
`UIMessage<TTools, TData>`, and `partial`/`final` derivation has landed
in `@tanstack/ai-vue`, `@tanstack/ai-solid`, and `@tanstack/ai-svelte`,
the changeset needs to bump those at minor too — otherwise the parity
ships without a version bump and consumers on Vue/Solid/Svelte get the
new behavior without notice in their lockfile diff.
Added the three framework packages at `minor` (matching the other
three) and expanded the description to:
- Cover all four hook surfaces (`useChat` for React/Vue/Solid,
`createChat` for Svelte) instead of just `useChat`.
- Document the schema-generic threading (`StructuredOutputPart<TData
= unknown>`, `MessagePart<TTools, TData>`, `UIMessage<TTools,
TData>`) since that's the user-facing shape change agents reading
the release notes care about. Default `TData = unknown` preserves
source compatibility for consumers who don't pass a schema.
- Note the adapter fallback path (Anthropic / Gemini / Ollama still
emit one terminal `structured-output.complete` and the per-turn
typed part still lands — consumer code is identical).
- Add a "breaking-shape note" callout that explicitly stays minor:
with `outputSchema` set, `TEXT_MESSAGE_CONTENT` deltas no longer
create a `TextPart`. The old "filter out TextPart to hide JSON"
workaround the prior docs recommended is now a no-op (and removable),
not a regression — no `TextPart` is produced.
Verified via `pnpm exec changeset status`: six packages bump at minor
(the ones listed above), no major bumps, downstream consumers
auto-patch via `updateInternalDependencies: "patch"`.
The sibling `use-chat-reset-on-action.md` changeset is from PR #576
(merged earlier into this branch) — left untouched; it documents an
ai-react-only fix at patch severity and that's accurate for that
change. 66cf1525 feat(ai-vue,ai-solid,ai-svelte): bring structured-output parity from ai-react
PR #577 landed the typed per-message `StructuredOutputPart` design in
`@tanstack/ai-react` but left the other framework packages on the old
hook-level singleton state + RUN_STARTED reset. Bringing all four
hook surfaces back to parity so users on Vue, Solid, and Svelte get
the same multi-turn structured-output story (history-preserving
typed parts on every assistant message, schema generic flowing
through `messages` to `parts[i].data`) the React docs and skill
already advertise.
Per-package mirror of the React work:
- `BaseUseChatReturn<TTools, TData>` (Vue, Solid) and
`BaseCreateChatReturn<TTools, TData>` (Svelte) take the new generic.
`UseChatReturn<TTools, TSchema>` / `CreateChatReturn<TTools, TSchema>`
substitute `TData = InferSchemaType<TSchema>` when a schema is
supplied, so `messages` is typed as `Array<UIMessage<TTools, T>>`
and `messages[i].parts.find(p => p.type === 'structured-output').data`
resolves to `T` (not `unknown`) — same end-user ergonomics as React.
- `setMessages` / `append` parameter types thread `TData` through as
well so the public surface is consistent.
`use-chat.ts` (Vue), `use-chat.ts` (Solid), and
`create-chat.svelte.ts` (Svelte):
- Dropped the `parsePartialJSON` import, the `rawJson` accumulator,
and the `onChunk`-based `RUN_STARTED → reset → TEXT_MESSAGE_CONTENT
→ parse → structured-output.complete → snap` state machine. None of
it is needed anymore — the per-message `StructuredOutputPart` on
`messages` already carries everything.
- Added `activeStructuredPart` derivations using each framework's
primitive: `computed()` (Vue), `createMemo()` (Solid),
`$derived.by()` (Svelte). All three implement the same `messages`
scan as React's `use-chat.ts`: walk backwards to the latest user
message, then scan forward for an assistant message carrying a
structured-output part. Return null when no user message exists yet
so a stale `final` from `initialMessages` can't leak in on first
render (the SFH-6 fix from PR #577).
- `partial` and `final` are now `computed` / `createMemo` / `$derived`
reading from `activeStructuredPart` — `partial = part.partial ??
part.data ?? {}`, `final = part.status === 'complete' ? part.data :
null`. Identical semantics across all four frameworks.
Type-level tests in each package's `tests/use-chat-types.test.ts` /
`tests/create-chat-types.test.ts` extended with two new assertions:
- When `outputSchema` is supplied, the structured-output variant of
`messages[i].parts[j]` resolves to `StructuredOutputPart<Person>`
(not the default `<unknown>`), and `data` types as `Person |
undefined`. Locks in the schema-generic flow at the type level.
- Without `outputSchema`, the variant defaults to
`StructuredOutputPart<unknown>` and `data` is `unknown | undefined`.
Locks in the backward-compatible default.
`@tanstack/ai-preact` deliberately left alone — it doesn't support
`outputSchema` at all (verified by grep), which matches the docs'
"useChat (React, Vue, Solid) and createChat (Svelte) all accept the
same outputSchema option" claim. Adding it to preact is a separate
feature, not parity work.
Verification:
- All three packages build cleanly (`nx run-many -t build`).
- Type-level tests pass and now exercise the schema-generic flow.
- `nx run-many -t test:lib,test:types,test:eslint` clean across the
monorepo. 256 tests across the three frameworks (94 + 58 + 104),
all green.
End-to-end coverage is currently React-only (the e2e harness ships a
React app at `testing/e2e/src/routes/`) — adding parallel Vue / Solid
/ Svelte harness apps is a separate, larger lift and out of scope for
this parity pass. The unit-level type tests pin the API contract;
runtime behavior is identical to React because all four hooks ride
the same `ChatClient` + `StreamProcessor` underneath. 4becd394 skill(ai-core/structured-outputs): fact-check correction — fallback partial behavior
Fact-check pass surfaced one soft hallucination in the Pattern 4
"non-streaming adapters" bullet. The skill claimed `partial` stays `{}`
and `final` populates on arrival when an adapter uses the
non-streaming fallback. Reality: `fallbackStructuredOutputStream`
emits one whole-JSON `TEXT_MESSAGE_CONTENT` (the full `result.rawText`
as a single delta) before yielding `structured-output.complete`. The
processor routes that delta through `appendStructuredOutputDelta` and
the progressive JSON parser, so `partial` does populate — just in the
same render tick that `final` snaps, rather than incrementally
field-by-field like the native-streaming path.
User-facing effect is identical for almost every consumer (one render
with both `partial` and `final` populated), but the literal "`partial`
stays `{}`" claim was wrong. Reworded to describe what actually
happens: one whole-JSON delta → `partial` populates and `final` snaps
in the same tick, no field-by-field reveal.
Verified the remaining 20+ skill claims (frontmatter source paths,
import surface, partial/final derivation, generic flow, round-trip
behavior, "filter TextParts" historical note, etc.) against current
code — clean. 5f17d95d skill(ai-core/structured-outputs): cover useChat + multi-turn patterns
The structured-outputs agent skill was scoped to the server-side
`chat({ outputSchema })` activity (patterns 1-3) and never mentioned the
client-side hook surface. With PR #577 the structured-output payload
becomes a typed `MessagePart` on `useChat`'s `messages` array and the
multi-turn recipe-builder pattern lights up — the skill needs to teach
both, otherwise agents will keep generating the old "hide the TextPart"
hack and treat `partial` / `final` as singleton hook-level state.
Skill updates:
- Sources updated to point at the new top-level `docs/structured-outputs/*`
pages (overview, one-shot, streaming, multi-turn, with-tools). The
old stub at `docs/chat/structured-outputs.md` redirects to those.
- Description rewritten to mention `useChat`, the per-turn
`StructuredOutputPart`, and the derived `partial` / `final`.
- New "decision: which pattern fits" table at the top so agents pick the
right pattern by what they're building instead of pattern-matching the
first example.
- New Pattern 4 ("useChat with outputSchema — progressive UI") covers
the server endpoint + client hook shape with typed `partial` / `final`
and the same-shape-on-fallback-adapters note.
- New Pattern 5 ("multi-turn structured chat") covers the recipe-builder
shape — walking `messages` to render history, the `RecipePart =
StructuredOutputPart<Recipe>` alias for typed `find()`, the
schema-generic flow `useChat<TSchema>` → `UIMessage<TTools, TData>` →
`MessagePart<TTools, TData>` → `StructuredOutputPart<TData>`, the
partial/final-are-derived semantic, and the round-trip story.
- Pattern 3 reframed as "direct stream iteration" (Node / CLI / tests)
with a pointer to Pattern 4 for the in-browser case.
Two new HIGH-severity common-mistake entries:
- Filtering `TextPart`s out of `useChat` renderers when using
`outputSchema`. That hack was needed when JSON deltas landed in a
`TextPart`; with PR #577 they land in a dedicated `StructuredOutputPart`
and the guard now hides legitimate text content. The entry shows the
obsolete pattern explicitly and the correct part-type narrowing.
- Treating `partial` / `final` as sticky state across turns. They're
derived from the latest assistant message's `structured-output` part,
not a singleton slot — `partial` reads `{}` between `sendMessage()`
and the first chunk, `final` only reflects the most recent turn.
Renders that need history must walk `messages` directly. The entry
contrasts "render `final` only" (loses history) with "render every
assistant's part" (correct).
Cross-references add a pointer to `docs/structured-outputs/with-tools.md`
on the tool-calling cross-link. Existing pre-multi-turn mistake entries
(parsing partial JSON deltas yourself, provider-specific strategies,
raw JSON Schema instead of project's library) preserved verbatim.
`library_version` left as `0.10.0` — that bumps via changesets on
release, not from this PR. 5f17d95d skill(ai-core/structured-outputs): cover useChat + multi-turn patterns
The structured-outputs agent skill was scoped to the server-side
`chat({ outputSchema })` activity (patterns 1-3) and never mentioned the
client-side hook surface. With PR #577 the structured-output payload
becomes a typed `MessagePart` on `useChat`'s `messages` array and the
multi-turn recipe-builder pattern lights up — the skill needs to teach
both, otherwise agents will keep generating the old "hide the TextPart"
hack and treat `partial` / `final` as singleton hook-level state.
Skill updates:
- Sources updated to point at the new top-level `docs/structured-outputs/*`
pages (overview, one-shot, streaming, multi-turn, with-tools). The
old stub at `docs/chat/structured-outputs.md` redirects to those.
- Description rewritten to mention `useChat`, the per-turn
`StructuredOutputPart`, and the derived `partial` / `final`.
- New "decision: which pattern fits" table at the top so agents pick the
right pattern by what they're building instead of pattern-matching the
first example.
- New Pattern 4 ("useChat with outputSchema — progressive UI") covers
the server endpoint + client hook shape with typed `partial` / `final`
and the same-shape-on-fallback-adapters note.
- New Pattern 5 ("multi-turn structured chat") covers the recipe-builder
shape — walking `messages` to render history, the `RecipePart =
StructuredOutputPart<Recipe>` alias for typed `find()`, the
schema-generic flow `useChat<TSchema>` → `UIMessage<TTools, TData>` →
`MessagePart<TTools, TData>` → `StructuredOutputPart<TData>`, the
partial/final-are-derived semantic, and the round-trip story.
- Pattern 3 reframed as "direct stream iteration" (Node / CLI / tests)
with a pointer to Pattern 4 for the in-browser case.
Two new HIGH-severity common-mistake entries:
- Filtering `TextPart`s out of `useChat` renderers when using
`outputSchema`. That hack was needed when JSON deltas landed in a
`TextPart`; with PR #577 they land in a dedicated `StructuredOutputPart`
and the guard now hides legitimate text content. The entry shows the
obsolete pattern explicitly and the correct part-type narrowing.
- Treating `partial` / `final` as sticky state across turns. They're
derived from the latest assistant message's `structured-output` part,
not a singleton slot — `partial` reads `{}` between `sendMessage()`
and the first chunk, `final` only reflects the most recent turn.
Renders that need history must walk `messages` directly. The entry
contrasts "render `final` only" (loses history) with "render every
assistant's part" (correct).
Cross-references add a pointer to `docs/structured-outputs/with-tools.md`
on the tool-calling cross-link. Existing pre-multi-turn mistake entries
(parsing partial JSON deltas yourself, provider-specific strategies,
raw JSON Schema instead of project's library) preserved verbatim.
`library_version` left as `0.10.0` — that bumps via changesets on
release, not from this PR. 7c112fd9 test(e2e): cover multi-turn structured chat across every provider
Adds an end-to-end test for the multi-turn-structured-chat pattern
documented in `docs/structured-outputs/multi-turn.md` and shipped as the
recipe-builder example. Runs across every provider that supports both
multi-turn and structured-output — native-streaming (openai, groq, grok,
openrouter) and fallback paths (anthropic, gemini, ollama). All seven
providers pass.
New feature flag `multi-turn-structured` in the e2e harness:
- `testing/e2e/src/lib/types.ts`: added to `Feature` union and
`ALL_FEATURES`.
- `testing/e2e/src/lib/feature-support.ts`: support set matches
`multi-turn ∩ structured-output` — all seven supported providers.
- `testing/e2e/src/lib/schemas.ts`: added `recipeSchema` mirroring the
example app's RecipeSchema (title, cuisine, servings, estimatedCostUsd,
ingredients[], steps[], tips[]) so the harness exercises the same shape
end users see in the docs.
- `testing/e2e/src/lib/features.ts`: per-feature `systemPrompt` override
(defaulting to the existing guitar-store prompt for everything else);
the new feature uses a chef persona.
- `testing/e2e/src/routes/api.chat.ts`: routes the new feature through
`chat({ outputSchema: recipeSchema, stream: true })`. Branched per
feature so TS picks the right `chat<TSchema>()` overload without a
`never` cast.
Test (`testing/e2e/tests/multi-turn-structured.spec.ts`) walks a
three-turn conversation — pasta -> vegan variant -> gluten-free + side
salad — and asserts the load-bearing claims from the doc:
1. Each user prompt produces a new assistant message with its own
`structured-output` part (one part per turn, all preserved as the
conversation grows from one to three turns).
2. The first turn's recipe is NOT clobbered when the second turn lands
(a single hook-level partial/final slot would fail this assertion —
this is exactly the bug the per-message `StructuredOutputPart`
design eliminates).
3. The hook-level `final` (exposed in the harness as the
`structured-output-complete` testid's `data-structured-output`
attribute) reflects only the LATEST turn's data — proving the
derivation walks back to the most recent assistant message's part.
Fixtures: `testing/e2e/fixtures/multi-turn-structured/conversation.json`
ships three deterministic recipe responses keyed by user-message prefix.
Each turn's response is valid JSON against `recipeSchema` with a unique
title substring (Pomodoro / Vegan / Gluten-Free) so the test can pin the
three turns apart without depending on the LLM.
Routing assertion added to `structured-output-stream.spec.ts`:
The streaming-UI doc claims `TEXT_MESSAGE_CONTENT` deltas land on a
`structured-output` part — not a `text` part with raw JSON. Tightened
the existing per-provider test so the assistant message must have
exactly one `structured-output-part` and zero `text-part`s. To support
this, added `data-testid="text-part"` to the `ChatUI` text renderer
(previously untagged because tests read text via `assistant-message`
content). Doesn't change behavior; gives the assertion something to
count.
Verified: `playwright test --grep "structured-output-stream|multi-turn-structured"`
runs 15 tests across 7 providers, all pass.
Out of scope for this commit: extending `with-tools.md` coverage to
exercise tool approval inside a structured-output run. The basic
agentic+structured path is already covered by `agentic-structured.spec.ts`;
approval-mid-structured-run is a follow-up. 7c112fd9 test(e2e): cover multi-turn structured chat across every provider
Adds an end-to-end test for the multi-turn-structured-chat pattern
documented in `docs/structured-outputs/multi-turn.md` and shipped as the
recipe-builder example. Runs across every provider that supports both
multi-turn and structured-output — native-streaming (openai, groq, grok,
openrouter) and fallback paths (anthropic, gemini, ollama). All seven
providers pass.
New feature flag `multi-turn-structured` in the e2e harness:
- `testing/e2e/src/lib/types.ts`: added to `Feature` union and
`ALL_FEATURES`.
- `testing/e2e/src/lib/feature-support.ts`: support set matches
`multi-turn ∩ structured-output` — all seven supported providers.
- `testing/e2e/src/lib/schemas.ts`: added `recipeSchema` mirroring the
example app's RecipeSchema (title, cuisine, servings, estimatedCostUsd,
ingredients[], steps[], tips[]) so the harness exercises the same shape
end users see in the docs.
- `testing/e2e/src/lib/features.ts`: per-feature `systemPrompt` override
(defaulting to the existing guitar-store prompt for everything else);
the new feature uses a chef persona.
- `testing/e2e/src/routes/api.chat.ts`: routes the new feature through
`chat({ outputSchema: recipeSchema, stream: true })`. Branched per
feature so TS picks the right `chat<TSchema>()` overload without a
`never` cast.
Test (`testing/e2e/tests/multi-turn-structured.spec.ts`) walks a
three-turn conversation — pasta -> vegan variant -> gluten-free + side
salad — and asserts the load-bearing claims from the doc:
1. Each user prompt produces a new assistant message with its own
`structured-output` part (one part per turn, all preserved as the
conversation grows from one to three turns).
2. The first turn's recipe is NOT clobbered when the second turn lands
(a single hook-level partial/final slot would fail this assertion —
this is exactly the bug the per-message `StructuredOutputPart`
design eliminates).
3. The hook-level `final` (exposed in the harness as the
`structured-output-complete` testid's `data-structured-output`
attribute) reflects only the LATEST turn's data — proving the
derivation walks back to the most recent assistant message's part.
Fixtures: `testing/e2e/fixtures/multi-turn-structured/conversation.json`
ships three deterministic recipe responses keyed by user-message prefix.
Each turn's response is valid JSON against `recipeSchema` with a unique
title substring (Pomodoro / Vegan / Gluten-Free) so the test can pin the
three turns apart without depending on the LLM.
Routing assertion added to `structured-output-stream.spec.ts`:
The streaming-UI doc claims `TEXT_MESSAGE_CONTENT` deltas land on a
`structured-output` part — not a `text` part with raw JSON. Tightened
the existing per-provider test so the assistant message must have
exactly one `structured-output-part` and zero `text-part`s. To support
this, added `data-testid="text-part"` to the `ChatUI` text renderer
(previously untagged because tests read text via `assistant-message`
content). Doesn't change behavior; gives the assertion something to
count.
Verified: `playwright test --grep "structured-output-stream|multi-turn-structured"`
runs 15 tests across 7 providers, all pass.
Out of scope for this commit: extending `with-tools.md` coverage to
exercise tool approval inside a structured-output run. The basic
agentic+structured path is already covered by `agentic-structured.spec.ts`;
approval-mid-structured-run is a follow-up. aef68b7f docs(structured-outputs): fact-check pass — fix hallucinated API claims
Spawned a fact-checker over the five new pages against the actual code.
Four real hallucinations surfaced, all fixed:
- `one-shot.md` return-type table for `chat()` was missing the
`stream: false` case. The full surface is four rows, not three:
`Promise<string>` (no schema, no stream), `AsyncIterable<StreamChunk>`
(no schema, streaming default), `Promise<InferSchemaType<T>>` (with
schema, non-streaming), `StructuredOutputStream<…>` (with schema,
streaming). The middle two were collapsed before.
- `multi-turn.md` repeatedly claimed `MessagePart<TTools, TData>` and
`UIMessage<TTools, TData>` as if those generics live everywhere. The
core `@tanstack/ai` types are single-generic (`<TData>` only); only
the `@tanstack/ai-client` types — which the framework hook packages
re-export — carry both generics. Added a callout explaining the
split so readers don't reach for the wrong import.
- `with-tools.md` showed an `onToolCall` callback being passed to
`useChat({ … })` for "manual control" of a client tool. That option
doesn't exist on `ChatClientOptions` / `UseChatOptions` — `onToolCall`
is an internal event on the `StreamProcessor` that the `ChatClient`
subscribes to in order to auto-execute `.client()`-registered tools.
The user-facing path is `toolDefinition(...).client((input) => ...)`;
there's no manual handler shape. Rewrote the section to show the
canonical `.client(...)` + `clientTools(...)` flow and explicitly note
there's no `onToolCall` option. Also corrected the `clientTools`
import — it lives in `@tanstack/ai-client`, not `@tanstack/ai`.
- `streaming.md` showed `messageId: string` as a public field on the
terminal `structured-output.complete` event's `value`. The runtime
does attach it on every emit, but the exported
`StructuredOutputCompleteEvent<T>` type only declares
`{ object, raw, reasoning? }`. Dropped `messageId` from the typed
payload diagram and explained that the `messageId` on the
preceding `structured-output.start` event is the canonical place to
read it from, with a footnote that the same value is attached on
`complete` at runtime for consumers who need it.
Imports re-audited across all five pages — every `import { … } from
"@tanstack/…"` line now matches an actual exported symbol from that
package's `index.ts`. `pnpm test:docs` link checker still passes
(288 markdown files, no broken links).
Items the fact-checker marked as plausible-but-unverified (provider
wire-knob specifics in the overview table, exact tool-loop ordering
in `with-tools.md`) were left as-is — they match the pre-existing
documentation and would need adapter-level source review to either
confirm or weaken; out of scope for this pass. aef68b7f docs(structured-outputs): fact-check pass — fix hallucinated API claims
Spawned a fact-checker over the five new pages against the actual code.
Four real hallucinations surfaced, all fixed:
- `one-shot.md` return-type table for `chat()` was missing the
`stream: false` case. The full surface is four rows, not three:
`Promise<string>` (no schema, no stream), `AsyncIterable<StreamChunk>`
(no schema, streaming default), `Promise<InferSchemaType<T>>` (with
schema, non-streaming), `StructuredOutputStream<…>` (with schema,
streaming). The middle two were collapsed before.
- `multi-turn.md` repeatedly claimed `MessagePart<TTools, TData>` and
`UIMessage<TTools, TData>` as if those generics live everywhere. The
core `@tanstack/ai` types are single-generic (`<TData>` only); only
the `@tanstack/ai-client` types — which the framework hook packages
re-export — carry both generics. Added a callout explaining the
split so readers don't reach for the wrong import.
- `with-tools.md` showed an `onToolCall` callback being passed to
`useChat({ … })` for "manual control" of a client tool. That option
doesn't exist on `ChatClientOptions` / `UseChatOptions` — `onToolCall`
is an internal event on the `StreamProcessor` that the `ChatClient`
subscribes to in order to auto-execute `.client()`-registered tools.
The user-facing path is `toolDefinition(...).client((input) => ...)`;
there's no manual handler shape. Rewrote the section to show the
canonical `.client(...)` + `clientTools(...)` flow and explicitly note
there's no `onToolCall` option. Also corrected the `clientTools`
import — it lives in `@tanstack/ai-client`, not `@tanstack/ai`.
- `streaming.md` showed `messageId: string` as a public field on the
terminal `structured-output.complete` event's `value`. The runtime
does attach it on every emit, but the exported
`StructuredOutputCompleteEvent<T>` type only declares
`{ object, raw, reasoning? }`. Dropped `messageId` from the typed
payload diagram and explained that the `messageId` on the
preceding `structured-output.start` event is the canonical place to
read it from, with a footnote that the same value is attached on
`complete` at runtime for consumers who need it.
Imports re-audited across all five pages — every `import { … } from
"@tanstack/…"` line now matches an actual exported symbol from that
package's `index.ts`. `pnpm test:docs` link checker still passes
(288 markdown files, no broken links).
Items the fact-checker marked as plausible-but-unverified (provider
wire-knob specifics in the overview table, exact tool-loop ordering
in `with-tools.md`) were left as-is — they match the pre-existing
documentation and would need adapter-level source review to either
confirm or weaken; out of scope for this pass. 3d81fe41 docs(structured-outputs): split into top-level section with per-journey pages
The single `chat/structured-outputs.md` page had grown to cover four
non-overlapping consumer journeys — non-streaming extraction, streaming
UIs, multi-turn structured chat, and the agent-loop combination — all
under one heading. Readers landing on it had to scroll for their use
case, the streaming section's "hide raw TextPart" advice was now wrong
(this PR routes JSON deltas into a typed `StructuredOutputPart`, not a
`TextPart`), and the new multi-turn capability had no documented home.
Reorganized into a dedicated top-level `Structured Outputs` nav section
with one page per journey:
- `overview` — schema libraries, provider-support table, persona-based
"which page do I read?" router. Single landing page that points at the
three specific journeys + the with-tools combination.
- `one-shot` — single prompt in, single typed object out. Non-streaming
`chat({ outputSchema })`. Field descriptions, nested schemas, plain
JSON Schema, error handling, best practices.
- `streaming` — progressive UI with `useChat({ outputSchema })` and the
hook-level `partial` / `final`. Corrected chunk-type table (deltas
route into `StructuredOutputPart`, not `TextPart`). Migration note
about the removed "hide TextPart" hack. Direct iteration sub-section.
- `multi-turn` — per-message typed structured-output parts. Walks
`messages[i].parts.find(p => p.type === 'structured-output')` with the
schema generic flowing through `UIMessage<TTools, TData>` and
`MessagePart<TTools, TData>`. Recipe-builder example with the typed
`RecipePart` alias pattern. Round-trip serialization story.
- `with-tools` — combining `outputSchema` with the agent loop. Pause /
resume points for server-tool approvals and client-tool invocations
inside a structured run.
`docs/chat/structured-outputs.md` reduced to a short redirect-style stub
that keeps the existing URL working (same `id` for stable bookmarks) and
links into the new section. Existing cross-links in `adapters/openai.md`
and `migration/migration-from-vercel-ai.md` updated to point at the new
overview page. `docs/config.json` adds the new `Structured Outputs`
section after `Chat` and removes the old `Chat → Structured Outputs`
child.
Verification: `pnpm test:docs` (the in-repo link checker) passes —
288 markdown files cross-checked with no broken links. 3d81fe41 docs(structured-outputs): split into top-level section with per-journey pages
The single `chat/structured-outputs.md` page had grown to cover four
non-overlapping consumer journeys — non-streaming extraction, streaming
UIs, multi-turn structured chat, and the agent-loop combination — all
under one heading. Readers landing on it had to scroll for their use
case, the streaming section's "hide raw TextPart" advice was now wrong
(this PR routes JSON deltas into a typed `StructuredOutputPart`, not a
`TextPart`), and the new multi-turn capability had no documented home.
Reorganized into a dedicated top-level `Structured Outputs` nav section
with one page per journey:
- `overview` — schema libraries, provider-support table, persona-based
"which page do I read?" router. Single landing page that points at the
three specific journeys + the with-tools combination.
- `one-shot` — single prompt in, single typed object out. Non-streaming
`chat({ outputSchema })`. Field descriptions, nested schemas, plain
JSON Schema, error handling, best practices.
- `streaming` — progressive UI with `useChat({ outputSchema })` and the
hook-level `partial` / `final`. Corrected chunk-type table (deltas
route into `StructuredOutputPart`, not `TextPart`). Migration note
about the removed "hide TextPart" hack. Direct iteration sub-section.
- `multi-turn` — per-message typed structured-output parts. Walks
`messages[i].parts.find(p => p.type === 'structured-output')` with the
schema generic flowing through `UIMessage<TTools, TData>` and
`MessagePart<TTools, TData>`. Recipe-builder example with the typed
`RecipePart` alias pattern. Round-trip serialization story.
- `with-tools` — combining `outputSchema` with the agent loop. Pause /
resume points for server-tool approvals and client-tool invocations
inside a structured run.
`docs/chat/structured-outputs.md` reduced to a short redirect-style stub
that keeps the existing URL working (same `id` for stable bookmarks) and
links into the new section. Existing cross-links in `adapters/openai.md`
and `migration/migration-from-vercel-ai.md` updated to point at the new
overview page. `docs/config.json` adds the new `Structured Outputs`
section after `Chat` and removes the old `Chat → Structured Outputs`
child.
Verification: `pnpm test:docs` (the in-repo link checker) passes —
288 markdown files cross-checked with no broken links.