Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
a59d52a5 fix(ci): list @standard-schema/spec as devDep on framework packages
Two failures from the previous push, both stemming from the type-test
files I added: knip flagged @standard-schema/spec as an unlisted import
across ai-react, ai-vue, ai-solid, and ai-svelte test files, and
@tanstack/ai-vue:test:types failed because — unlike the other three —
its tsconfig included tests/, so tsc strictly resolved the import (which
isn't a direct dep, only transitively via @tanstack/ai).
Fixes:
- Add \`@standard-schema/spec: ^1.1.0\` to devDependencies on all four
framework packages. The import is purely for type-level construction
in the type tests (StandardJSONSchemaV1<Person, Person> — a phantom
branded type that simulates what a Zod schema's inferred type would
look like). devDep is the right scope.
- Align ai-vue's tsconfig with ai-react/ai-solid/ai-svelte by dropping
tests/ from the tsc include block. Tests are still type-checked by
vitest at runtime; tsc now only checks src/.
Verified locally: pnpm test:knip, pnpm test:sherif, and test:types on
all four framework packages pass. a59d52a5 fix(ci): list @standard-schema/spec as devDep on framework packages
Two failures from the previous push, both stemming from the type-test
files I added: knip flagged @standard-schema/spec as an unlisted import
across ai-react, ai-vue, ai-solid, and ai-svelte test files, and
@tanstack/ai-vue:test:types failed because — unlike the other three —
its tsconfig included tests/, so tsc strictly resolved the import (which
isn't a direct dep, only transitively via @tanstack/ai).
Fixes:
- Add \`@standard-schema/spec: ^1.1.0\` to devDependencies on all four
framework packages. The import is purely for type-level construction
in the type tests (StandardJSONSchemaV1<Person, Person> — a phantom
branded type that simulates what a Zod schema's inferred type would
look like). devDep is the right scope.
- Align ai-vue's tsconfig with ai-react/ai-solid/ai-svelte by dropping
tests/ from the tsc include block. Tests are still type-checked by
vitest at runtime; tsc now only checks src/.
Verified locally: pnpm test:knip, pnpm test:sherif, and test:types on
all four framework packages pass. 4f1c726b feat(ai-react): useChat managed partial/final for structured-output streaming
Pass the same schema you give chat() on the server to useChat() on the
client, and the hook tracks the progressive object and the validated
terminal payload for you — no external useState, no onChunk ceremony, no
parsePartialJSON calls in user code.
API:
const { sendMessage, isLoading, partial, final } = useChat({
connection: fetchServerSentEvents("/api/extract"),
outputSchema: PersonSchema,
})
// partial: DeepPartial<Person> — updates per TEXT_MESSAGE_CONTENT delta
// final: Person | null — snaps on structured-output.complete
Implementation:
- New generic param TSchema extends SchemaInput | undefined = undefined on
UseChatOptions / UseChatReturn / useChat.
- UseChatReturn is conditional on TSchema: when supplied, adds typed
partial/final; when undefined (default), return is unchanged. Inferred
automatically from outputSchema option.
- Internal onChunk handler tracks raw JSON buffer via ref, runs
parsePartialJSON on each TEXT_MESSAGE_CONTENT delta, snaps final on the
terminal CUSTOM structured-output.complete event, resets all three on
RUN_STARTED. User's own onChunk callback still fires after internal
processing — both compose.
- DeepPartial<T> exported for handlers that need to annotate.
The schema is used purely for client-side type inference; server-side
validation still runs against the schema passed to chat({ outputSchema })
on the server route. Works identically for non-streaming endpoints — for
those, partial stays {} and final populates when the single terminal
event arrives.
Type-level tests (tests/use-chat-types.test.ts) pin both branches of the
discriminated return type — useChat() without outputSchema rejects access
to partial/final via @ts-expect-error, useChat() with outputSchema asserts
typed DeepPartial<Person> / Person | null. e910f0c1 docs(chat/structured-outputs): lead with client+server flow, demote manual iteration to advanced
The previous streaming section opened with \`for await (const chunk of stream)\`
— that's the advanced/server-side-only path. The typical use case is a UI
streaming JSON deltas through SSE from a server endpoint, and the docs should
lead with it.
- New "Server endpoint" subsection: \`chat({outputSchema, stream: true})\` +
\`toServerSentEventsResponse(stream)\`. One short example, no ceremony.
- New "Client with useChat" subsection: \`useChat\` + \`fetchServerSentEvents\`
+ \`onChunk\`, with \`parsePartialJSON\` driving progressive UI. Shows where
the validated object lives (the terminal \`structured-output.complete\` event,
typed as \`T\` via the schema). Notes Vue/Solid/Svelte share the shape.
- "What the stream contains" + "Adapter coverage" tables retained verbatim.
- Old standalone \`for await\` example moved to a new "Advanced: iterating the
stream directly" subsection at the end, framed as the path for Node scripts,
CLIs, server-only flows, and tests.
- "Streaming with tools that may pause" reframed to use the \`onChunk\` signature
(matching the new primary path); a note points back to the advanced section
for callers iterating the stream directly.