Skip to main content

TanStack AI Persistence, Resumable Streams & Memory with Redis

Production backends for TanStack AI on Upstash Redis — chat persistence, resumable streams, distributed locks, long-term memory, tool caching, rate limiting, and RAG tools.
4 min read

TanStack AI defines the contracts for an agent's production state — chat persistence, resumable streaming, locks, memory — and ships in-memory implementations that only work inside one process. @upstash/agentkit-tanstack-ai implements them on Upstash Redis, so they hold across serverless instances, page reloads, and devices.

ImportPlugs intoFeature
upstashPersistencewithPersistence(), withGenerationPersistence()Transcripts, runs, human-in-the-loop interrupts, metadata, generation jobs, and generated files.
upstashStreamdurability on the responseResume a stream after a reload, or open the same thread on another device.
upstashLockswithLocks()Distributed locks for TanStack AI middleware, such as its sandbox setup.
upstashMemorymemoryMiddleware()Long-term memory ranked in Redis Search.
toolCache, rateLimitmiddlewareSkip repeated tool calls; throttle users before the model runs.
createSearchToolstoolssearch / aggregate / count over your own documents (RAG).
Note

AgentKit reads UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN from the environment by default. Pass a redis client to any helper to use a different one.

How to persist TanStack AI chats in Redis#

Persistence plugs into TanStack AI's withPersistence() middleware, which comes from its persistence package:

This covers every TanStack AI persistence store: messages, runs, interrupts, and metadata for chats, plus generationRuns and artifacts for one-shot generation jobs such as images or speech. The blobs store for generated bytes is added when you pass an Upstash Blob bucket:

Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment. Bucket.fromEnv() reads UPSTASH_BLOB_TOKEN from the environment, which is only needed when you store generated files.

Runs are indexed by thread, so reconnecting to a live run (findActiveRun) is a single index read. Each write is one command or one Lua script, so concurrent instances cannot interleave it.

Options

Pass bucket to also store the bytes of generated files (images, audio, video) in Upstash Blob. Without it, there is no blobs store.

How to resume a TanStack AI stream after a reload#

Every chunk is written to a Redis Stream before it is sent. A client that reconnects with Last-Event-ID (or ?offset) replays what it missed and keeps following the live run, whichever instance serves the request. Without a Request, use upstashStream({ runId, offset }).

Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment.

Options

How to use distributed locks with TanStack AI#

withLocks doesn't lock anything by itself. It gives the lock store to later middleware, which lock the one step they must not run twice: withSandbox uses it so two concurrent requests for a thread don't both create a sandbox, and your own middleware can use it through getLocks(ctx). It does not serialize whole chat turns. Unlike TanStack's InMemoryLockStore, which only works inside one process, upstashLocks() coordinates across instances.

Each lock is a lease that is renewed while the critical section runs. If the lease is lost, the section's signal aborts.

Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment.

Options

How to add long-term memory to TanStack AI#

Memory plugs into TanStack AI's memoryMiddleware(), which comes from its memory package:

Before each turn, the most relevant memories for the user's message are added to the system prompt, labelled by where they came from. The model gets a save_memory tool for durable facts, and each turn's user message is captured too. Memory is per user across threads by default.

Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment.

Options

How to cache tools and rate limit with TanStack AI#

toolCache only caches the tools you list — list deterministic, side-effect-free tools only. rateLimit fails the run with RateLimitExceededError before the model is called. For an HTTP 429 instead, call createRateLimit({ limiter }).limit(userId) in your route before chat().

Both middlewares and createRateLimit read UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment.

How to add RAG with TanStack AI#

The tool descriptions are generated from the schema, and the index is created on first use.

Reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the environment.

Telemetry#

AgentKit adds its package name and version as a header on your Redis client's requests. To turn it off, set UPSTASH_DISABLE_TELEMETRY, or pass enableTelemetry: false to a helper.

Was this page helpful?