ta
TanStack
GitHub
ai
Workspace
GitHub
CI Pipeline Executions
Filtered
Runs
Demo
Insights
Compare tasks
Analytics
Sign in
Toggle sidebar
Overview
⌘K
ai
Loading workspace stats
Loading workspace insights...
Statistics interval
7 days
30 days
Latest CI Pipeline Executions
Status
Fix filter
Filter
Fuzzy
Filter range
Sort by
Sort by
Start time
Sort ascending
Sort descending
Succeeded
main
31de22b1 fix(ai): undo strict-mode null-widening before structured-output validation (#732) * fix(ai): undo strict-mode null-widening before structured-output validation Optional fields are widened to required+nullable for strict structured output, so providers return `null` for an absent optional. Validating that `null` against the original schema failed (`.optional()` is `T | undefined`, not `T | null`), surfacing as a StandardSchemaValidationError — most visibly through @tanstack/ai-openrouter, whose adapter preserves provider nulls. Add `undoNullWidening(value, schema)` to @tanstack/ai-utils: a schema-aware counterpart to `transformNullsToUndefined` that drops only synthesized nulls (those the original JSON Schema disallows) while preserving the ones a `.nullable()`/`.nullish()` field genuinely allows. The chat activity runs it on the structured-output result before Standard Schema validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-utils): handle tuple items and ambiguous anyOf branches in undoNullWidening Addresses CodeRabbit review on #732: - resolveSchema now descends only when exactly one non-null anyOf/oneOf branch matches the value's shape; ambiguous unions keep the original schema rather than risk stripping a null a sibling branch allows. - Array walking applies tuple-style `items: [a, b, …]` schemas per index instead of always using the first. Adds coverage for both and fixes the test's import order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai): record null-widening map at conversion time instead of re-deriving it Replace the schema-guessing `undoNullWidening` — which reverse-engineered which nulls strict-mode widening synthesized by pattern-matching response values against the un-widened schema's anyOf branches, and bailed on ambiguity — with a precise map recorded by the widening pass itself. `makeStructuredOutputCompatible` now returns the strict schema plus a `NullWideningMap` marking exactly the positions where it added `null`. The new `convertSchemaForStructuredOutput` exposes both, and the chat activity threads that map into `undoNullWidening`. This drops `resolveSchema`/`allowsNull` branch guessing, preserves `.nullish()` nulls by construction, and closes the ambiguous-union gap where synthesized nulls were previously left in place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai): un-widen structured-output nulls in the engine for both stream modes and every adapter Strict-mode structured output widens optional fields to `required` + nullable, so providers return `null` for an absent optional. That `null` fails validation against the original `.optional()` schema (`T | undefined`, not `T | null`). Previously only the Promise<T> path un-widened, and only for adapters that preserved provider nulls (OpenRouter). The OpenAI-family adapters instead blind-stripped every null via `transformStructuredOutput`, which masked the bug but also destroyed genuine `.nullable()` nulls — and the streaming path didn't un-widen at all. Move un-widening into the engine, the one layer that holds the schema's null-widening map: - Add `finalStructuredOutput.normalize`, applied the instant the structured output is captured, so it flows to BOTH the streaming `structured-output.complete` event and the Promise<T> result (plus the native-combined harvest path). Both activity callers now pass it via `convertSchemaForStructuredOutput`; streaming switches off the map-less `convertSchemaToJsonSchema`. Validation runs on already-normalized data. - openai-base `transformStructuredOutput` default is now a passthrough — the blind null-strip is gone (the engine un-widens precisely instead). Fixes the responses-text streaming path that bypassed the hook. OpenAI/Grok/Groq inherit this; OpenRouter's now-redundant override is simplified and its dead `transformNullsToUndefined` imports dropped. Genuine `.nullable()` nulls now survive on every adapter and both directions; synthesized optional nulls are dropped everywhere. Tests: streaming normalization + a converter→undo round-trip (closing the untested map-production gap); adapter passthrough tests updated; e2e gains an optional field returning `null` asserted un-widened across all 5 streaming providers (real regression guard for OpenRouter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai): cover native-combined + streaming-rewrite null normalization; fix comment Follow-up to the engine-level un-widening commit, addressing review gaps: - Fix an inaccurate inline comment: the `structured-output.complete` event's value is `{ object, raw, reasoning? }` — it carries no `messageId` (that's on `structured-output.start`). The outbound-chunk rewrite preserves `raw`, not `messageId`. - Add native-combined mode coverage (the `harvestCombinedStructuredOutput` capture site was untested): both the harvested Promise<T> result and the synthesized streaming complete event must un-widen. - Add a streaming-rewrite test asserting the engine replaces only `object` (un-widened) while spreading the event's sibling `raw`/`reasoning` through untouched — guards the `{ ...value, object }` contract. - Add a round-trip case proving a genuine `.nullable()` null inside an array item survives (the spot the array/tuple handling could wrongly strip). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(openai-base): assert structuredOutputStream passes provider nulls through Addresses CodeRabbit review (PR #732): the non-streaming passthrough assertion had no streaming sibling. Adds a `structuredOutputStream()` case emitting a provider `null` and asserting the terminal `structured-output.complete` object preserves it — guarding against the stream path regressing to a blind null-strip while the non-stream path relies on engine-level un-widening. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: apply automated fixes * test(react-native-smoke): register @tanstack/ai-utils in resolution configs The chat activity and schema-converter now import @tanstack/ai-utils, so the React Native smoke graph reaches it. The smoke fixture resolves workspace packages to source via explicit per-tool mappings, so add @tanstack/ai-utils (mirroring @tanstack/ai-event-client) to the tsconfig paths, metro packageEntryPoints, the esbuild alias map, and the import-surface walker. Fixes the TS2307 'Cannot find module @tanstack/ai-utils' in the smoke typecheck. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
by Drew Hoover
D
Succeeded
main
31de22b1 fix(ai): undo strict-mode null-widening before structured-output validation (#732) * fix(ai): undo strict-mode null-widening before structured-output validation Optional fields are widened to required+nullable for strict structured output, so providers return `null` for an absent optional. Validating that `null` against the original schema failed (`.optional()` is `T | undefined`, not `T | null`), surfacing as a StandardSchemaValidationError — most visibly through @tanstack/ai-openrouter, whose adapter preserves provider nulls. Add `undoNullWidening(value, schema)` to @tanstack/ai-utils: a schema-aware counterpart to `transformNullsToUndefined` that drops only synthesized nulls (those the original JSON Schema disallows) while preserving the ones a `.nullable()`/`.nullish()` field genuinely allows. The chat activity runs it on the structured-output result before Standard Schema validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-utils): handle tuple items and ambiguous anyOf branches in undoNullWidening Addresses CodeRabbit review on #732: - resolveSchema now descends only when exactly one non-null anyOf/oneOf branch matches the value's shape; ambiguous unions keep the original schema rather than risk stripping a null a sibling branch allows. - Array walking applies tuple-style `items: [a, b, …]` schemas per index instead of always using the first. Adds coverage for both and fixes the test's import order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ai): record null-widening map at conversion time instead of re-deriving it Replace the schema-guessing `undoNullWidening` — which reverse-engineered which nulls strict-mode widening synthesized by pattern-matching response values against the un-widened schema's anyOf branches, and bailed on ambiguity — with a precise map recorded by the widening pass itself. `makeStructuredOutputCompatible` now returns the strict schema plus a `NullWideningMap` marking exactly the positions where it added `null`. The new `convertSchemaForStructuredOutput` exposes both, and the chat activity threads that map into `undoNullWidening`. This drops `resolveSchema`/`allowsNull` branch guessing, preserves `.nullish()` nulls by construction, and closes the ambiguous-union gap where synthesized nulls were previously left in place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai): un-widen structured-output nulls in the engine for both stream modes and every adapter Strict-mode structured output widens optional fields to `required` + nullable, so providers return `null` for an absent optional. That `null` fails validation against the original `.optional()` schema (`T | undefined`, not `T | null`). Previously only the Promise<T> path un-widened, and only for adapters that preserved provider nulls (OpenRouter). The OpenAI-family adapters instead blind-stripped every null via `transformStructuredOutput`, which masked the bug but also destroyed genuine `.nullable()` nulls — and the streaming path didn't un-widen at all. Move un-widening into the engine, the one layer that holds the schema's null-widening map: - Add `finalStructuredOutput.normalize`, applied the instant the structured output is captured, so it flows to BOTH the streaming `structured-output.complete` event and the Promise<T> result (plus the native-combined harvest path). Both activity callers now pass it via `convertSchemaForStructuredOutput`; streaming switches off the map-less `convertSchemaToJsonSchema`. Validation runs on already-normalized data. - openai-base `transformStructuredOutput` default is now a passthrough — the blind null-strip is gone (the engine un-widens precisely instead). Fixes the responses-text streaming path that bypassed the hook. OpenAI/Grok/Groq inherit this; OpenRouter's now-redundant override is simplified and its dead `transformNullsToUndefined` imports dropped. Genuine `.nullable()` nulls now survive on every adapter and both directions; synthesized optional nulls are dropped everywhere. Tests: streaming normalization + a converter→undo round-trip (closing the untested map-production gap); adapter passthrough tests updated; e2e gains an optional field returning `null` asserted un-widened across all 5 streaming providers (real regression guard for OpenRouter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ai): cover native-combined + streaming-rewrite null normalization; fix comment Follow-up to the engine-level un-widening commit, addressing review gaps: - Fix an inaccurate inline comment: the `structured-output.complete` event's value is `{ object, raw, reasoning? }` — it carries no `messageId` (that's on `structured-output.start`). The outbound-chunk rewrite preserves `raw`, not `messageId`. - Add native-combined mode coverage (the `harvestCombinedStructuredOutput` capture site was untested): both the harvested Promise<T> result and the synthesized streaming complete event must un-widen. - Add a streaming-rewrite test asserting the engine replaces only `object` (un-widened) while spreading the event's sibling `raw`/`reasoning` through untouched — guards the `{ ...value, object }` contract. - Add a round-trip case proving a genuine `.nullable()` null inside an array item survives (the spot the array/tuple handling could wrongly strip). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(openai-base): assert structuredOutputStream passes provider nulls through Addresses CodeRabbit review (PR #732): the non-streaming passthrough assertion had no streaming sibling. Adds a `structuredOutputStream()` case emitting a provider `null` and asserting the terminal `structured-output.complete` object preserves it — guarding against the stream path regressing to a blind null-strip while the non-stream path relies on engine-level un-widening. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: apply automated fixes * test(react-native-smoke): register @tanstack/ai-utils in resolution configs The chat activity and schema-converter now import @tanstack/ai-utils, so the React Native smoke graph reaches it. The smoke fixture resolves workspace packages to source via explicit per-tool mappings, so add @tanstack/ai-utils (mirroring @tanstack/ai-event-client) to the tsconfig paths, metro packageEntryPoints, the esbuild alias map, and the import-surface walker. Fixes the TS2307 'Cannot find module @tanstack/ai-utils' in the smoke typecheck. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
by Drew Hoover
D
Succeeded
fix/structured-output-schema-aware-null-normalization
851bd248 Merge 88368c4ddcfa3fa85dea5080c2b7b357030e424a into 81e3aee318d0e6f869ee00c3d86a7475980d89df
by Drew Hoover
D
Succeeded
fix/structured-output-schema-aware-null-normalization
b95a0473 Merge 49f7a48ab53f5de760767d117542d3c66bbcd86c into 570c08a8d1a35746c3d31a63188249cba2d2475a
by Drew Hoover
D
Succeeded
fix/structured-output-schema-aware-null-normalization
0564e35c Merge 5b263ad17dabe09b7870607da68b417114549e7f into 570c08a8d1a35746c3d31a63188249cba2d2475a
by Drew Hoover
D
Succeeded
fix/structured-output-schema-aware-null-normalization
60da9f92 Merge 3f446789063365c1d937e6cf24fc9d0e6a2cc086 into e8ce0e1641618b56c5e157d4aaf8ad8aad640eb5
by Drew Hoover
D
Succeeded
fix/structured-output-schema-aware-null-normalization
60da9f92 fix(ai-utils): handle tuple items and ambiguous anyOf branches in undoNullWidening Addresses CodeRabbit review on #732: - resolveSchema now descends only when exactly one non-null anyOf/oneOf branch matches the value's shape; ambiguous unions keep the original schema rather than risk stripping a null a sibling branch allows. - Array walking applies tuple-style `items: [a, b, …]` schemas per index instead of always using the first. Adds coverage for both and fixes the test's import order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
by Drew Hoover
D
Previous
Next