feat(persistence): generation persistence — client snapshot + durable media bytes#987
feat(persistence): generation persistence — client snapshot + durable media bytes#987AlemTuzlak wants to merge 11 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🚀 Changeset Version Preview15 package(s) bumped directly, 38 bumped as dependents. 🟥 Major bumps
🟨 Minor bumps
🟩 Patch bumps
|
|
View your CI Pipeline Execution ↗ for commit 11fad30
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-code-mode
@tanstack/ai-code-mode-skills
@tanstack/ai-codex
@tanstack/ai-devtools-core
@tanstack/ai-durable-stream
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-mcp
@tanstack/ai-mistral
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-persistence
@tanstack/ai-persistence-cloudflare
@tanstack/ai-persistence-drizzle
@tanstack/ai-persistence-prisma
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-sandbox
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-vercel
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-utils
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
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.
…istence (no call-site generic)
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.
c1cd561 to
a77d800
Compare
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)
2f56980 to
f3d9fe6
Compare
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).
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 apersistencestorage adapter. The option reuses the sharedChatStorageAdaptercontract, solocalStoragePersistence/sessionStoragePersistence/indexedDBPersistencework with no type argument:Hooks expose
resumeSnapshot/resumeState/pendingArtifacts/resultArtifactsacross react/solid/vue/svelte/angular. Noresume()action; reconnect to an in-flight stream is the delivery layer's job (resumable streams, #955, merged), wired via adurabilityadapter +GEThandler exactly as for chat.2. Durable media-byte storage (server, opt-in)
When the persistence backend provides both an
artifacts(ArtifactStore) and ablobs(BlobStore) store,withGenerationPersistencewrites each generated file's bytes to the blob store (keyartifacts/<runId>/<artifactId>), records anArtifactRecord, attachesPersistedArtifactRefs to the result, and emitsgeneration:artifactsso the client records them.memoryPersistence()ships both stores; any backend implementing the two contracts works. Extraction is customizable viaextractArtifacts/nameArtifact.Changes
@tanstack/ai— result-transform machinery (resultTransforms/artifactInputsonGenerationMiddlewareContext,applyGenerationResultTransforms),threadId/runIdoptions on the image/audio/speech/transcription activities, andgeneration:artifactsemission fromstreamGenerationResult.@tanstack/ai-utils—base64ToUint8Array.@tanstack/ai-persistence—ArtifactStore+BlobStorecontracts, in-memory impls inmemoryPersistence(), and byte-persistence inwithGenerationPersistence(extractArtifacts/nameArtifact).@tanstack/ai-client+ framework hooks (react/solid/vue/svelte/angular) — the client snapshot +persistenceoption + artifact refs.@tanstack/ai-event-client— optionalthreadId/runIdon generation events.docs/persistence/generation-persistence.md(client snapshot, byte storage + serve route, reconnect), kiira-verified.Verification
test:types— 0 errors in ai / ai-client / ai-persistence.Scope note
Durable SQL/R2 artifact+blob backends (Drizzle/Prisma/Cloudflare R2) are not in this PR:
memoryPersistenceplus any customArtifactStore+BlobStorecover 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).