Skip to content

feat(persistence): generation persistence — client snapshot + durable media bytes#987

Open
AlemTuzlak wants to merge 11 commits into
feat/persistence-corefrom
feat/generation-persistence
Open

feat(persistence): generation persistence — client snapshot + durable media bytes#987
AlemTuzlak wants to merge 11 commits into
feat/persistence-corefrom
feat/generation-persistence

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Generation Persistence

Stacked on #984 (feat/persistence-core) — please review/merge that first. Rebased onto its current tip.

Generation persistence in two halves:

1. Client resume snapshot (read-only)

As a media generation streams, the client builds a lightweight GenerationResumeSnapshot (run identity, status, errors, result metadata + artifact refs — never the bytes) and writes it to a persistence storage adapter. The option reuses the shared ChatStorageAdapter contract, so localStoragePersistence / sessionStoragePersistence / indexedDBPersistence work with no type argument:

const snapshots = localStoragePersistence({ keyPrefix: 'my-app:generation:' })
useGenerateImage({ connection, persistence: snapshots })

Hooks expose resumeSnapshot / resumeState / pendingArtifacts / resultArtifacts across react/solid/vue/svelte/angular. No resume() action; reconnect to an in-flight stream is the delivery layer's job (resumable streams, #955, merged), wired via a durability adapter + GET handler exactly as for chat.

2. Durable media-byte storage (server, opt-in)

When the persistence backend provides both an artifacts (ArtifactStore) and a blobs (BlobStore) store, withGenerationPersistence writes each generated file's bytes to the blob store (key artifacts/<runId>/<artifactId>), records an ArtifactRecord, attaches PersistedArtifactRefs to the result, and emits generation:artifacts so the client records them. memoryPersistence() ships both stores; any backend implementing the two contracts works. Extraction is customizable via extractArtifacts / nameArtifact.

Changes

  • @tanstack/ai — result-transform machinery (resultTransforms / artifactInputs on GenerationMiddlewareContext, applyGenerationResultTransforms), threadId/runId options on the image/audio/speech/transcription activities, and generation:artifacts emission from streamGenerationResult.
  • @tanstack/ai-utilsbase64ToUint8Array.
  • @tanstack/ai-persistenceArtifactStore + BlobStore contracts, in-memory impls in memoryPersistence(), and byte-persistence in withGenerationPersistence (extractArtifacts/nameArtifact).
  • @tanstack/ai-client + framework hooks (react/solid/vue/svelte/angular) — the client snapshot + persistence option + artifact refs.
  • @tanstack/ai-event-client — optional threadId/runId on generation events.
  • Docsdocs/persistence/generation-persistence.md (client snapshot, byte storage + serve route, reconnect), kiira-verified.
  • Changeset — minor across the affected packages.

Verification

  • Builds green across ai-utils, ai, ai-event-client, ai-client, ai-persistence.
  • test:types0 errors in ai / ai-client / ai-persistence.
  • Tests — ai-persistence 78 pass (incl. 12 artifact + the chat resume suites), ai-client generation 50 pass, ai core stream/middleware/activity 92 pass.
  • kiira — doc passes (3 snippets).

Scope note

Durable SQL/R2 artifact+blob backends (Drizzle/Prisma/Cloudflare R2) are not in this PR: memoryPersistence plus any custom ArtifactStore+BlobStore cover the contract today, and the durable backends (which add SQL schema + migrations per package) are a clean follow-up. generateVideo's own job-polling artifact path is likewise deferred (image/audio/speech/transcription persist bytes).

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbaa5dfb-c743-4357-8385-7381ad730d80

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/generation-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

15 package(s) bumped directly, 38 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-angular 0.3.1 → 1.0.0 Changeset
@tanstack/ai-durable-stream 0.0.0 → 1.0.0 Changeset
@tanstack/ai-persistence 0.0.0 → 1.0.0 Changeset
@tanstack/ai-persistence-cloudflare 0.0.0 → 1.0.0 Changeset
@tanstack/ai-persistence-drizzle 0.0.0 → 1.0.0 Changeset
@tanstack/ai-persistence-prisma 0.0.0 → 1.0.0 Changeset
@tanstack/ai-preact 0.11.1 → 1.0.0 Changeset
@tanstack/ai-react 0.18.1 → 1.0.0 Changeset
@tanstack/ai-solid 0.15.1 → 1.0.0 Changeset
@tanstack/ai-svelte 0.15.1 → 1.0.0 Changeset
@tanstack/ai-vue 0.15.1 → 1.0.0 Changeset
@tanstack/ai-acp 0.2.3 → 1.0.0 Dependent
@tanstack/ai-anthropic 0.16.3 → 1.0.0 Dependent
@tanstack/ai-bedrock 0.1.4 → 1.0.0 Dependent
@tanstack/ai-claude-code 0.2.3 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.3.8 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.3.11 → 1.0.0 Dependent
@tanstack/ai-codex 0.2.3 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.34 → 1.0.0 Dependent
@tanstack/ai-fal 0.9.12 → 1.0.0 Dependent
@tanstack/ai-gemini 0.20.1 → 1.0.0 Dependent
@tanstack/ai-grok 0.14.9 → 1.0.0 Dependent
@tanstack/ai-grok-build 0.2.3 → 1.0.0 Dependent
@tanstack/ai-groq 0.5.3 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.47 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.47 → 1.0.0 Dependent
@tanstack/ai-mistral 0.2.3 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.16 → 1.0.0 Dependent
@tanstack/ai-openai 0.17.1 → 1.0.0 Dependent
@tanstack/ai-opencode 0.2.3 → 1.0.0 Dependent
@tanstack/ai-openrouter 0.15.10 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.15 → 1.0.0 Dependent
@tanstack/ai-sandbox 0.2.4 → 1.0.0 Dependent
@tanstack/ai-sandbox-cloudflare 0.2.4 → 1.0.0 Dependent
@tanstack/ai-sandbox-daytona 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-docker 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-local-process 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-sprites 0.2.1 → 1.0.0 Dependent
@tanstack/ai-sandbox-vercel 0.2.0 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.14 → 1.0.0 Dependent
@tanstack/openai-base 0.9.9 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.42.0 → 0.43.0 Changeset
@tanstack/ai-client 0.22.1 → 0.23.0 Changeset
@tanstack/ai-event-client 0.6.8 → 0.7.0 Changeset
@tanstack/ai-utils 0.3.1 → 0.4.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.4.24 → 0.4.25 Dependent
@tanstack/ai-isolate-cloudflare 0.2.38 → 0.2.39 Dependent
@tanstack/ai-mcp 0.2.5 → 0.2.6 Dependent
@tanstack/ai-vue-ui 0.2.34 → 0.2.35 Dependent
@tanstack/preact-ai-devtools 0.1.67 → 0.1.68 Dependent
@tanstack/react-ai-devtools 0.2.67 → 0.2.68 Dependent
@tanstack/solid-ai-devtools 0.2.67 → 0.2.68 Dependent
ag-ui 0.0.2 → 0.0.3 Dependent

@nx-cloud

nx-cloud Bot commented Jul 23, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 11fad30

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-23 15:15:35 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@987

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@987

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@987

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@987

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@987

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@987

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@987

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@987

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-skills@987

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@987

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@987

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@987

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@987

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@987

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@987

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@987

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@987

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@987

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@987

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@987

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@987

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@987

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@987

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@987

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@987

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@987

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@987

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@987

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@987

@tanstack/ai-persistence-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-cloudflare@987

@tanstack/ai-persistence-drizzle

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-drizzle@987

@tanstack/ai-persistence-prisma

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-prisma@987

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@987

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@987

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@987

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@987

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@987

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@987

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@987

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@987

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@987

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@987

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@987

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@987

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@987

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@987

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@987

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@987

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@987

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@987

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@987

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@987

commit: 11fad30

Layer a lightweight, read-only resume snapshot onto media generation.
As a run streams, the client builds a GenerationResumeSnapshot (run
identity, status, errors, result metadata + artifact refs — never media
bytes) and writes it to an optional GenerationServerPersistence store.

- ai-client: GenerationResumeSnapshot types + updateGenerationResumeSnapshot
  reducer; GenerationClient/VideoGenerationClient observe chunks, persist
  snapshots (serialized queue, warn-not-throw), expose getResumeSnapshot();
  disposed guard. No resume() action (stream re-attach is PR #955).
- ai-event-client: optional threadId/runId on generation events.
- react/solid/vue/svelte/angular hooks: persistence + initialResumeSnapshot
  options; expose resumeSnapshot/resumeState (+ pending/result artifacts).
- example: Persisted mode on the image generation route.
- docs: persistence/generation-persistence.md + nav entry.

Pairs with the existing withGenerationPersistence server middleware.
Drop the bespoke `GenerationServerPersistence` type and the `{ server }`
option wrapper. The `persistence` option is now a bare storage adapter
reusing the shared `ChatStorageAdapter` contract (aliased as
`GenerationPersistence`), so `localStoragePersistence` /
`sessionStoragePersistence` / `indexedDBPersistence` work for generations
exactly as they do for chat — matching main's ergonomics.
Default `localStoragePersistence` / `sessionStoragePersistence` /
`indexedDBPersistence` to a value-agnostic `TValue` so a bare, unannotated
call works for BOTH chat and generation persistence — the consuming
`persistence` option constrains the stored value. Generation docs/example now
use `localStoragePersistence({ keyPrefix })` with no type declaration.
PR #955 (resumable streams) is merged, so delivery durability is available
today — it was wrongly described as an unlanded future feature. Rewrite the
generation-persistence doc: the server example now wires a durability adapter
+ GET handler, and the delivery section explains that a dropped mid-generation
connection re-attaches through the same adapters useChat uses. Clarify that the
read-only snapshot carries run state (incl. runId) across reloads, while
hooks do not auto-resume on mount.
@AlemTuzlak
AlemTuzlak force-pushed the feat/generation-persistence branch from c1cd561 to a77d800 Compare July 23, 2026 14:38
Server-side artifact + blob storage for generated media, layered on
withGenerationPersistence.

- @tanstack/ai: result-transform machinery (resultTransforms/artifactInputs
  on GenerationMiddlewareContext, applyGenerationResultTransforms), threadId/
  runId on the image/audio/speech/transcription activities, and
  generation:artifacts emission from streamGenerationResult.
- @tanstack/ai-utils: base64ToUint8Array.
- @tanstack/ai-persistence: ArtifactStore + BlobStore contracts, in-memory
  impls in memoryPersistence(), and withGenerationPersistence byte-persistence
  (writes bytes to blobs, records ArtifactRecord, attaches PersistedArtifactRef,
  emits generation:artifacts) with extractArtifacts/nameArtifact options.
…+ blobs, serve route, extractArtifacts/nameArtifact)
@AlemTuzlak
AlemTuzlak force-pushed the feat/generation-persistence branch from 2f56980 to f3d9fe6 Compare July 23, 2026 14:45
@AlemTuzlak AlemTuzlak changed the title feat(persistence): client-side generation persistence feat(persistence): generation persistence — client snapshot + durable media bytes Jul 23, 2026
Add retrieveArtifact(persistence, id) and retrieveBlob(persistence, idOrRecord)
so a serve handler fetches a persisted generation artifact's metadata and bytes
without hand-rolling the blob key. artifactBlobKey is the shared key builder used
by both withGenerationPersistence (write) and retrieveBlob (read).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant