@tanstack/ai 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/esm/activities/chat/adapter.js +23 -16
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +10 -4
- package/dist/esm/activities/chat/agent-loop-strategies.js +75 -17
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
- package/dist/esm/activities/chat/cancel.d.ts +40 -0
- package/dist/esm/activities/chat/cancel.js +54 -0
- package/dist/esm/activities/chat/cancel.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +28 -16
- package/dist/esm/activities/chat/index.js +2100 -1744
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
- package/dist/esm/activities/chat/mcp/manager.js +90 -77
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
- package/dist/esm/activities/chat/messages.js +397 -346
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.js +17 -15
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
- package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
- package/dist/esm/activities/chat/middleware/compose.js +623 -531
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.js +12 -5
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
- package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
- package/dist/esm/activities/chat/middleware/locks.js +71 -0
- package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
- package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
- package/dist/esm/activities/chat/middleware/run-store.js +176 -0
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
- package/dist/esm/activities/chat/middleware/validate.js +23 -28
- package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
- package/dist/esm/activities/chat/stream/json-parser.js +39 -25
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
- package/dist/esm/activities/chat/stream/processor.js +1341 -1542
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.js +69 -53
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
- package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
- package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
- package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.js +85 -47
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +22 -15
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +4 -0
- package/dist/esm/activities/generateAudio/index.js +141 -105
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +22 -15
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +4 -0
- package/dist/esm/activities/generateImage/index.js +155 -111
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +22 -15
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
- package/dist/esm/activities/generateSpeech/index.js +159 -110
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +22 -15
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
- package/dist/esm/activities/generateTranscription/index.js +159 -100
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +36 -29
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +143 -19
- package/dist/esm/activities/generateVideo/index.js +456 -279
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.js +60 -48
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/index.js +8 -34
- package/dist/esm/activities/middleware/index.d.ts +1 -1
- package/dist/esm/activities/middleware/run.d.ts +10 -0
- package/dist/esm/activities/middleware/run.js +53 -29
- package/dist/esm/activities/middleware/run.js.map +1 -1
- package/dist/esm/activities/middleware/types.d.ts +44 -6
- package/dist/esm/activities/stream-generation-result.d.ts +4 -1
- package/dist/esm/activities/stream-generation-result.js +79 -44
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +22 -15
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +27 -0
- package/dist/esm/activities/summarize/index.js +268 -102
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -1
- package/dist/esm/adapter-internals.js +4 -11
- package/dist/esm/client.d.ts +25 -3
- package/dist/esm/client.js +131 -64
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/custom-events.d.ts +76 -0
- package/dist/esm/custom-events.js +37 -0
- package/dist/esm/custom-events.js.map +1 -0
- package/dist/esm/delivery-detach.d.ts +50 -0
- package/dist/esm/delivery-detach.js +71 -0
- package/dist/esm/delivery-detach.js.map +1 -0
- package/dist/esm/delivery-disconnect.d.ts +62 -0
- package/dist/esm/delivery-disconnect.js +81 -0
- package/dist/esm/delivery-disconnect.js.map +1 -0
- package/dist/esm/extend-adapter.js +19 -17
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +23 -5
- package/dist/esm/index.js +30 -97
- package/dist/esm/interrupt-resume.d.ts +71 -0
- package/dist/esm/interrupt-resume.js +438 -0
- package/dist/esm/interrupt-resume.js.map +1 -0
- package/dist/esm/interrupt-serialization.d.ts +12 -0
- package/dist/esm/interrupt-serialization.js +178 -0
- package/dist/esm/interrupt-serialization.js.map +1 -0
- package/dist/esm/interrupts.d.ts +84 -0
- package/dist/esm/interrupts.js +31 -0
- package/dist/esm/interrupts.js.map +1 -0
- package/dist/esm/locks.d.ts +10 -0
- package/dist/esm/locks.js +2 -0
- package/dist/esm/logger/console-logger.js +101 -78
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +104 -89
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +54 -49
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +1 -1
- package/dist/esm/middlewares/content-guard.js +142 -148
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/index.js +2 -6
- package/dist/esm/middlewares/otel.js +598 -732
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.js +47 -40
- package/dist/esm/middlewares/usage-attributes.js.map +1 -1
- package/dist/esm/realtime/event-emitter.js +24 -25
- package/dist/esm/realtime/event-emitter.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +5 -9
- package/dist/esm/realtime/index.js +29 -6
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/scope.d.ts +47 -0
- package/dist/esm/stream-durability.d.ts +171 -0
- package/dist/esm/stream-durability.js +295 -0
- package/dist/esm/stream-durability.js.map +1 -0
- package/dist/esm/stream-to-response.d.ts +178 -13
- package/dist/esm/stream-to-response.js +663 -115
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +30 -16
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/system-prompts.js +27 -21
- package/dist/esm/system-prompts.js.map +1 -1
- package/dist/esm/tool-registry.js +72 -45
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/tools/provider-tool.js +14 -5
- package/dist/esm/tools/provider-tool.js.map +1 -1
- package/dist/esm/types.d.ts +332 -21
- package/dist/esm/types.js +2 -0
- package/dist/esm/utilities/ag-ui-wire.js +79 -93
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +26 -4
- package/dist/esm/utilities/chat-params.js +218 -92
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/errors.js +28 -18
- package/dist/esm/utilities/errors.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +46 -41
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/numbers.js +13 -10
- package/dist/esm/utilities/numbers.js.map +1 -1
- package/dist/esm/utilities/provider-executed.js +20 -11
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/sampling-keys.js +31 -19
- package/dist/esm/utilities/sampling-keys.js.map +1 -1
- package/dist/esm/utilities/tool-result.js +42 -30
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/dist/esm/utilities/usage.js +27 -9
- package/dist/esm/utilities/usage.js.map +1 -1
- package/dist/esm/utils.js +26 -18
- package/dist/esm/utils.js.map +1 -1
- package/package.json +10 -6
- package/skills/ai-core/SKILL.md +69 -18
- package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
- package/skills/ai-core/chat-experience/SKILL.md +156 -11
- package/skills/ai-core/client-persistence/SKILL.md +277 -0
- package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/skills/ai-core/locks/SKILL.md +143 -0
- package/skills/ai-core/media-generation/SKILL.md +144 -12
- package/skills/ai-core/middleware/SKILL.md +258 -33
- package/skills/ai-core/structured-outputs/SKILL.md +1 -1
- package/skills/ai-core/tool-calling/SKILL.md +54 -59
- package/src/activities/chat/agent-loop-strategies.ts +10 -4
- package/src/activities/chat/cancel.ts +81 -0
- package/src/activities/chat/index.ts +1152 -153
- package/src/activities/chat/mcp/manager.ts +4 -4
- package/src/activities/chat/mcp/types.ts +2 -2
- package/src/activities/chat/messages.ts +5 -3
- package/src/activities/chat/middleware/builder.ts +1 -1
- package/src/activities/chat/middleware/compose.ts +186 -9
- package/src/activities/chat/middleware/index.ts +26 -0
- package/src/activities/chat/middleware/locks.ts +102 -0
- package/src/activities/chat/middleware/pending-turn.ts +47 -0
- package/src/activities/chat/middleware/run-disconnect.ts +62 -0
- package/src/activities/chat/middleware/run-store.ts +412 -0
- package/src/activities/chat/middleware/types.ts +62 -1
- package/src/activities/chat/stream/processor.ts +189 -5
- package/src/activities/chat/tools/approval-schema.ts +205 -0
- package/src/activities/chat/tools/tool-calls.ts +106 -13
- package/src/activities/chat/tools/tool-definition.ts +210 -39
- package/src/activities/generateAudio/index.ts +20 -3
- package/src/activities/generateImage/index.ts +20 -3
- package/src/activities/generateSpeech/index.ts +25 -3
- package/src/activities/generateTranscription/index.ts +26 -3
- package/src/activities/generateVideo/index.ts +345 -82
- package/src/activities/middleware/index.ts +2 -0
- package/src/activities/middleware/run.ts +31 -0
- package/src/activities/middleware/types.ts +49 -5
- package/src/activities/stream-generation-result.ts +30 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -0
- package/src/activities/summarize/index.ts +200 -10
- package/src/adapter-internals.ts +10 -1
- package/src/client.ts +244 -0
- package/src/custom-events.ts +107 -0
- package/src/delivery-detach.ts +72 -0
- package/src/delivery-disconnect.ts +84 -0
- package/src/index.ts +138 -0
- package/src/interrupt-resume.ts +824 -0
- package/src/interrupt-serialization.ts +183 -0
- package/src/interrupts.ts +146 -0
- package/src/locks.ts +17 -0
- package/src/logger/types.ts +1 -1
- package/src/middlewares/otel.ts +1 -0
- package/src/realtime/index.ts +5 -9
- package/src/scope.ts +47 -0
- package/src/stream-durability.ts +598 -0
- package/src/stream-to-response.ts +1051 -95
- package/src/strip-to-spec-middleware.ts +3 -3
- package/src/types.ts +416 -24
- package/src/utilities/chat-params.ts +245 -55
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/middlewares/index.js.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-core/locks
|
|
3
|
+
description: >
|
|
4
|
+
LockStore, InMemoryLockStore, LocksCapability and withLocks for
|
|
5
|
+
multi-instance coordination in TanStack AI. Ships in @tanstack/ai — NOT in
|
|
6
|
+
@tanstack/ai-persistence. Separate from AIPersistence state stores — not a
|
|
7
|
+
stores key, not composable. InMemoryLockStore vs a distributed (e.g.
|
|
8
|
+
Cloudflare Durable Object) lock, lease recovery, AbortSignal in critical
|
|
9
|
+
sections. Use when sandbox or other middleware needs cross-worker mutual
|
|
10
|
+
exclusion — NOT for storing messages/runs (use withPersistence).
|
|
11
|
+
type: sub-skill
|
|
12
|
+
library: tanstack-ai
|
|
13
|
+
library_version: '0.42.0'
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/ai:docs/advanced/locks.md'
|
|
16
|
+
- 'TanStack/ai:packages/ai/src/activities/chat/middleware/locks.ts'
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Locks (coordination — not persistence)
|
|
20
|
+
|
|
21
|
+
> **Dependency note:** This skill builds on ai-core and ai-core/middleware.
|
|
22
|
+
> `withLocks` is a ChatMiddleware that provides a capability. Locks are **not**
|
|
23
|
+
> part of `AIPersistence.stores` and are **not** composed with
|
|
24
|
+
> `composePersistence` — they ship in `@tanstack/ai`, independent of
|
|
25
|
+
> `@tanstack/ai-persistence`.
|
|
26
|
+
|
|
27
|
+
## Why separate?
|
|
28
|
+
|
|
29
|
+
State stores answer "what is durable chat data?"
|
|
30
|
+
Locks answer "who may run this critical section right now?"
|
|
31
|
+
|
|
32
|
+
`withPersistence` does **not** automatically lock a whole turn. Take a
|
|
33
|
+
per-thread (or other) lock yourself when multi-writer races matter.
|
|
34
|
+
|
|
35
|
+
## Wire locks
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks'
|
|
39
|
+
|
|
40
|
+
middleware: [
|
|
41
|
+
withLocks(new InMemoryLockStore()), // single process
|
|
42
|
+
]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Alongside persistence — optional, locks do not require it:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks'
|
|
49
|
+
import { withPersistence } from '@tanstack/ai-persistence'
|
|
50
|
+
|
|
51
|
+
middleware: [withPersistence(persistence), withLocks(new InMemoryLockStore())]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`withLocks` provides `LocksCapability` for downstream middleware (e.g.
|
|
55
|
+
sandbox). Order: usually state first, locks alongside or after depending on
|
|
56
|
+
who consumes the capability.
|
|
57
|
+
|
|
58
|
+
## The contract
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
interface LockStore {
|
|
62
|
+
withLock<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T>
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`InMemoryLockStore` ships in **`@tanstack/ai/locks`**: a per-key promise chain,
|
|
67
|
+
correct **within a single process only**. Multi-instance deployments need a
|
|
68
|
+
distributed implementation — you write it. The Cloudflare Durable Object recipe
|
|
69
|
+
is in **ai-persistence/build-cloudflare-adapter** (`@tanstack/ai-persistence`).
|
|
70
|
+
|
|
71
|
+
Type your own store with `defineLock` (autocomplete, no `: LockStore`
|
|
72
|
+
annotation), then hand it to `withLocks`. Acquire the key, run `fn`, release when
|
|
73
|
+
`fn` settles:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { defineLock, withLocks } from '@tanstack/ai/locks'
|
|
77
|
+
import { acquire } from './my-lock-backend'
|
|
78
|
+
|
|
79
|
+
const locks = defineLock({
|
|
80
|
+
async withLock(key, fn) {
|
|
81
|
+
const { release, signal } = await acquire(key)
|
|
82
|
+
try {
|
|
83
|
+
return await fn(signal)
|
|
84
|
+
} finally {
|
|
85
|
+
release()
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
middleware: [withLocks(locks)]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Lease semantics
|
|
94
|
+
|
|
95
|
+
A good `LockStore`:
|
|
96
|
+
|
|
97
|
+
- Serializes owners per key,
|
|
98
|
+
- Uses **leases** (or equivalent) so a crashed owner cannot block forever,
|
|
99
|
+
- Passes an `AbortSignal` into the critical section via `withLock`; when the
|
|
100
|
+
lease is lost, abort so work stops starting external mutations.
|
|
101
|
+
|
|
102
|
+
Callbacks must honor the signal and pass it to cancellable dependencies.
|
|
103
|
+
`InMemoryLockStore` never aborts its signal — within one process, ownership
|
|
104
|
+
cannot be lost.
|
|
105
|
+
|
|
106
|
+
## Capability identity
|
|
107
|
+
|
|
108
|
+
The `'locks'` capability token lives in `@tanstack/ai/locks`. Capability identity
|
|
109
|
+
is by **object reference**, so one shared token means a `withLocks` in the chain
|
|
110
|
+
reaches `withSandbox` automatically.
|
|
111
|
+
|
|
112
|
+
## Common mistakes
|
|
113
|
+
|
|
114
|
+
### HIGH: Importing locks from `@tanstack/ai-persistence`
|
|
115
|
+
|
|
116
|
+
They are not exported there. Use `@tanstack/ai`.
|
|
117
|
+
|
|
118
|
+
### HIGH: Putting `locks` on `AIPersistence.stores`
|
|
119
|
+
|
|
120
|
+
Not supported. `stores` accepts only `messages`, `runs`, `interrupts`,
|
|
121
|
+
`metadata` — never `locks`. Use `withLocks`.
|
|
122
|
+
|
|
123
|
+
### HIGH: Passing `locks` to `composePersistence` overrides
|
|
124
|
+
|
|
125
|
+
Same rejection, at the override layer. Locks are not state.
|
|
126
|
+
|
|
127
|
+
### HIGH: Passing `'locks'` to the conformance testkit's `skip`
|
|
128
|
+
|
|
129
|
+
`skip` accepts only chat state store keys. The suite does not cover locks
|
|
130
|
+
at all — test lease expiry and abort separately.
|
|
131
|
+
|
|
132
|
+
### HIGH: `InMemoryLockStore` across multiple processes
|
|
133
|
+
|
|
134
|
+
No mutual exclusion between machines — use a distributed lock store.
|
|
135
|
+
|
|
136
|
+
### MEDIUM: Ignoring lease abort
|
|
137
|
+
|
|
138
|
+
Continuing work after losing the lease races other owners.
|
|
139
|
+
|
|
140
|
+
## Cross-references
|
|
141
|
+
|
|
142
|
+
- See also: **ai-core/middleware/SKILL.md** -- the middleware chain and capability plumbing
|
|
143
|
+
- See also: **`@tanstack/ai-persistence` skills** (`skills/ai-persistence/SKILL.md` in that package) -- `ai-persistence/server` (state middleware) and `ai-persistence/build-cloudflare-adapter` (Durable Object lock recipe)
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
name: ai-core/media-generation
|
|
3
3
|
description: >
|
|
4
4
|
Image, audio, video, speech (TTS), and transcription generation using
|
|
5
|
-
activity-specific adapters: generateImage() with openaiImage/geminiImage,
|
|
5
|
+
activity-specific adapters: generateImage() with openaiImage/geminiImage/byteplusImage,
|
|
6
6
|
generateAudio() with geminiAudio/falAudio, generateVideo() with async
|
|
7
|
-
polling (openaiVideo/geminiVideo/grokVideo/falVideo, per-model typed
|
|
8
|
-
durations), generateSpeech() with openaiSpeech, generateTranscription()
|
|
9
|
-
with openaiTranscription. React hooks: useGenerateImage, useGenerateAudio,
|
|
7
|
+
polling (openaiVideo/geminiVideo/grokVideo/falVideo/byteplusVideo, per-model typed
|
|
8
|
+
durations), generateSpeech() with openaiSpeech/byteplusSpeech, generateTranscription()
|
|
9
|
+
with openaiTranscription/byteplusTranscription. React hooks: useGenerateImage, useGenerateAudio,
|
|
10
10
|
useGenerateSpeech, useTranscription, useGenerateVideo.
|
|
11
11
|
TanStack Start server function integration with toServerSentEventsResponse.
|
|
12
12
|
type: sub-skill
|
|
13
13
|
library: tanstack-ai
|
|
14
|
-
library_version: '0.
|
|
14
|
+
library_version: '0.42.0'
|
|
15
15
|
sources:
|
|
16
16
|
- 'TanStack/ai:docs/media/generations.md'
|
|
17
17
|
- 'TanStack/ai:docs/media/generation-hooks.md'
|
|
@@ -150,8 +150,16 @@ function ImageGenerator() {
|
|
|
150
150
|
### 1. Image Generation
|
|
151
151
|
|
|
152
152
|
Supported adapters: `openaiImage` (dall-e-2, dall-e-3, gpt-image-1,
|
|
153
|
-
gpt-image-1-mini, gpt-image-2)
|
|
154
|
-
gemini-3.1-flash-lite-image, imagen-4.0-generate-001, etc.)
|
|
153
|
+
gpt-image-1-mini, gpt-image-2), `geminiImage` (gemini-3.1-flash-image-preview,
|
|
154
|
+
gemini-3.1-flash-lite-image, imagen-4.0-generate-001, etc.) and `byteplusImage`
|
|
155
|
+
(Seedream — `seedream-4-0-250828`, `seedream-4-5-251128`, the 5.0 family).
|
|
156
|
+
|
|
157
|
+
> **Seedream quirks:** `watermark` defaults to **`true`** (pass
|
|
158
|
+
> `modelOptions: { watermark: false }` for a clean image), `size` is a token
|
|
159
|
+
> (`'1K'` | `'2K'` | `'4K'`) **or** explicit `'2048x2048'` pixels but never a
|
|
160
|
+
> mix, and `numberOfImages` is an **upper bound** — Seedream has no `n`, so it
|
|
161
|
+
> maps onto group-image mode and the model may return fewer. Reads
|
|
162
|
+
> `ARK_API_KEY`.
|
|
155
163
|
|
|
156
164
|
```typescript
|
|
157
165
|
import { generateImage } from '@tanstack/ai'
|
|
@@ -333,7 +341,19 @@ const { generate, result, isLoading } = useGenerateAudio({
|
|
|
333
341
|
|
|
334
342
|
### 3. Text-to-Speech
|
|
335
343
|
|
|
336
|
-
|
|
344
|
+
Adapters: `openaiSpeech` (tts-1, tts-1-hd, gpt-4o-audio-preview) and
|
|
345
|
+
`byteplusSpeech` (`seed-audio-1.0`).
|
|
346
|
+
|
|
347
|
+
> **BytePlus Seed Speech is a separate product from ModelArk** — it reads
|
|
348
|
+
> **`BYTEPLUS_VOICE_API_KEY`**, not `ARK_API_KEY`, and an Ark key there fails
|
|
349
|
+
> with `45000010 Invalid X-Api-Key`. Output is capped at **120 seconds**.
|
|
350
|
+
> There is no top-level `speaker` field — `voice` is sent as
|
|
351
|
+
> `references: [{ speaker }]`, and `modelOptions.references` **replaces** that
|
|
352
|
+
> array rather than merging, so passing `references` for voice cloning silently
|
|
353
|
+
> drops `voice`. Voice ids ending `_uranus_bigtts` are TTS 2.0,
|
|
354
|
+
> `_mars_bigtts` / `_moon_bigtts` are TTS 1.0, and `*_emo_v2_*` are the 1.0
|
|
355
|
+
> voices that accept emotion tags. Formats: `wav`, `mp3`, `pcm`, `ogg_opus`;
|
|
356
|
+
> `watermark` is also available on `modelOptions`.
|
|
337
357
|
|
|
338
358
|
```typescript
|
|
339
359
|
import { generateSpeech } from '@tanstack/ai'
|
|
@@ -367,8 +387,10 @@ const { generate, result, isLoading } = useGenerateSpeech({
|
|
|
367
387
|
|
|
368
388
|
### 4. Audio Transcription
|
|
369
389
|
|
|
370
|
-
|
|
371
|
-
gpt-4o-mini-transcribe, gpt-4o-transcribe-diarize)
|
|
390
|
+
Adapters: `openaiTranscription` (whisper-1, gpt-4o-transcribe,
|
|
391
|
+
gpt-4o-mini-transcribe, gpt-4o-transcribe-diarize) and `byteplusTranscription`
|
|
392
|
+
(`seed-asr` — synchronous, no polling; audio up to 2 hours / 100 MB; also reads
|
|
393
|
+
**`BYTEPLUS_VOICE_API_KEY`**).
|
|
372
394
|
|
|
373
395
|
> **Capturing audio in the browser:** Use `useAudioRecorder` from `@tanstack/ai-react` to record directly in the browser, then pass the recording as the `audio` input to `generate()`, or use `recording.part` as a prompt part in chat/generation calls. No transcoding or extra dependencies required — the recorder returns the native browser format (`audio/webm` or `audio/mp4`). For transcription, wrap it as a `data:` URL so the provider gets the real content type; passing raw `recording.base64` makes the adapter assume `audio/mpeg` and mislabel the webm/mp4 bytes.
|
|
374
396
|
>
|
|
@@ -517,7 +539,20 @@ durations 4/8/12s, single `input_reference` image prompt part), `grokVideo(...)`
|
|
|
517
539
|
(`grok-imagine-video` does text-to-video + image-to-video; `grok-imagine-video-1.5` is
|
|
518
540
|
image-to-video only — needs an `image` prompt part as the starting frame, text-only throws;
|
|
519
541
|
aspect-ratio size template like `'16:9_720p'`, integer durations 1-15s, reports
|
|
520
|
-
`usage.unitsBilled` seconds and exact `usage.cost`),
|
|
542
|
+
`usage.unitsBilled` seconds and exact `usage.cost`), `byteplusVideo(...)` (Seedance —
|
|
543
|
+
aspect-ratio size template like `'16:9_720p'`, durations 4-15s on the 2.0 family,
|
|
544
|
+
4-12s on 1.5-pro, 2-12s on the 1.0-pro models; reads `ARK_API_KEY`), and
|
|
545
|
+
`falVideo(...)` (hosted models, see cost tracking below).
|
|
546
|
+
|
|
547
|
+
> **Seedance option applicability is per model and enforced server-side** —
|
|
548
|
+
> Ark returns a 400 for an inapplicable field rather than ignoring it.
|
|
549
|
+
> `service_tier` / `camera_fixed` are Seedance 1.x only, `frames` is
|
|
550
|
+
> 1-0-pro + 1-0-pro-fast only, `draft` is 1-5-pro only, `priority` is the 2.0
|
|
551
|
+
> family only, and `duration: -1` works on 2.0 + 1-5-pro. There is no 2K tier
|
|
552
|
+
> on any model and `4k` exists only on `dreamina-seedance-2-0-260128`.
|
|
553
|
+
> **Video URLs expire 24 hours after the task completes** (task record kept 7
|
|
554
|
+
> days). Seedance is also reachable via `falVideo` — `byteplusVideo` is the
|
|
555
|
+
> direct-to-BytePlus path.
|
|
521
556
|
|
|
522
557
|
Client hook with job tracking:
|
|
523
558
|
|
|
@@ -563,6 +598,94 @@ if (result.usage?.unitsBilled != null) {
|
|
|
563
598
|
For video, the units arrive with the completed result: `getVideoJobStatus()`
|
|
564
599
|
returns `usage` and emits a `video:usage` devtools event when fal reports it.
|
|
565
600
|
|
|
601
|
+
### 7. Durable persistence (job lifecycle + artifact bytes)
|
|
602
|
+
|
|
603
|
+
To make generations survive a server restart and be re-served later, add
|
|
604
|
+
`withGenerationPersistence` from `@tanstack/ai-persistence` as generation
|
|
605
|
+
middleware. It requires `stores.generationRuns` (a `GenerationRunStore`, keyed on
|
|
606
|
+
the run's own `runId`, with a required `threadId` naming the stable slot the run
|
|
607
|
+
fills — that is what a client hydrates by) and, when you also pass an `stores.artifacts` +
|
|
608
|
+
`stores.blobs` **pair** (both or neither), it persists the generated media bytes
|
|
609
|
+
at blob key `artifacts/<runId>/<artifactId>` with an `ArtifactRecord` per file.
|
|
610
|
+
`memoryPersistence()` ships all three for dev/tests.
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
import { generateImage, toServerSentEventsResponse } from '@tanstack/ai'
|
|
614
|
+
import { openaiImage } from '@tanstack/ai-openai'
|
|
615
|
+
import {
|
|
616
|
+
withGenerationPersistence,
|
|
617
|
+
memoryPersistence,
|
|
618
|
+
retrieveArtifact,
|
|
619
|
+
retrieveBlob,
|
|
620
|
+
reconstructGeneration,
|
|
621
|
+
} from '@tanstack/ai-persistence'
|
|
622
|
+
|
|
623
|
+
const persistence = memoryPersistence() // swap for your DB/object-store adapter
|
|
624
|
+
|
|
625
|
+
export async function POST(req: Request) {
|
|
626
|
+
const { prompt, threadId } = await req.json()
|
|
627
|
+
return toServerSentEventsResponse(
|
|
628
|
+
generateImage({
|
|
629
|
+
adapter: openaiImage('gpt-image-1'),
|
|
630
|
+
prompt,
|
|
631
|
+
threadId, // the slot recorded on the job + artifacts
|
|
632
|
+
stream: true,
|
|
633
|
+
middleware: [
|
|
634
|
+
withGenerationPersistence(persistence, {
|
|
635
|
+
// Stamp a durable app-origin serve URL (the GET route below) onto
|
|
636
|
+
// each persisted artifact ref, and rewrite the live result's media to
|
|
637
|
+
// it. Both live and restored results then render from your origin.
|
|
638
|
+
artifactUrl: (ref) => `/api/artifacts?id=${ref.artifactId}`,
|
|
639
|
+
}),
|
|
640
|
+
],
|
|
641
|
+
}),
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Serve the stored bytes back (GET /api/artifacts?id=…):
|
|
646
|
+
export async function GET(req: Request) {
|
|
647
|
+
const id = new URL(req.url).searchParams.get('id') ?? ''
|
|
648
|
+
const record = await retrieveArtifact(persistence, id)
|
|
649
|
+
if (!record) return new Response('Not found', { status: 404 })
|
|
650
|
+
const blob = await retrieveBlob(persistence, record)
|
|
651
|
+
if (!blob?.body) return new Response('Not found', { status: 404 })
|
|
652
|
+
return new Response(blob.body, {
|
|
653
|
+
headers: { 'content-type': record.mimeType },
|
|
654
|
+
})
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
That route is enough for images. **Video needs `Range`**: seeking a `<video>`
|
|
659
|
+
is built on `206` / `Content-Range`, and Safari refuses to play a source that
|
|
660
|
+
ignores `Range` at all. Resolve the header against `record.size` (`416` when it
|
|
661
|
+
does not fit), pass `retrieveBlob(persistence, record, { range })`, and answer
|
|
662
|
+
`206` from the returned `blob.range` plus `accept-ranges: bytes`. The full
|
|
663
|
+
route is in the persistence docs under **Serve video: honour `Range`**.
|
|
664
|
+
|
|
665
|
+
On the client, `persistence` is **boolean only**: `persistence: true` hydrates
|
|
666
|
+
the last generation for the thread on mount, via the connection's
|
|
667
|
+
`hydrateGeneration` handler backed by a `reconstructGeneration` GET route. There
|
|
668
|
+
is no storage-adapter mode, so nothing about a generation is cached in the
|
|
669
|
+
browser.
|
|
670
|
+
|
|
671
|
+
**`persistence: true` requires a stable `threadId`**, and it is a type error to
|
|
672
|
+
set one without the other. That is the generation's scope, the slot successive
|
|
673
|
+
runs fill (e.g. `video-9-start-frame`), not a link to a chat. `id` is the
|
|
674
|
+
devtools label only and never a persistence key.
|
|
675
|
+
|
|
676
|
+
The hooks are transparent (like `useChat`): a reload repaints `status` /
|
|
677
|
+
`result` / `error`, not a separate `resumeSnapshot`. Because the `artifactUrl`
|
|
678
|
+
above stamps a durable URL onto each ref (carried on `result.artifacts`), the
|
|
679
|
+
restored `result` rebuilds its media from those refs and serves from your own
|
|
680
|
+
origin. Without byte storage, a reload restores `status` / `error` and `result`
|
|
681
|
+
stays `null`.
|
|
682
|
+
|
|
683
|
+
- Building the R2/D1-backed byte stores for a Cloudflare Worker:
|
|
684
|
+
**ai-persistence/build-cloudflare-artifact-store**.
|
|
685
|
+
- Store contracts, `composePersistence`, and the wiring end-to-end:
|
|
686
|
+
`docs/persistence/generation-persistence.md` and the
|
|
687
|
+
`ai-core/client-persistence` sub-skill.
|
|
688
|
+
|
|
566
689
|
---
|
|
567
690
|
|
|
568
691
|
## Common Hook API
|
|
@@ -577,7 +700,16 @@ All generation hooks return the same shape:
|
|
|
577
700
|
| `error` | `Error \| undefined` | Current error |
|
|
578
701
|
| `status` | `GenerationClientState` | `'idle' \| 'generating' \| 'success' \| 'error'` |
|
|
579
702
|
| `stop` | `() => void` | Abort current generation |
|
|
580
|
-
| `reset` | `() => void` | Clear state
|
|
703
|
+
| `reset` | `() => void` | Clear state and the in-memory snapshot |
|
|
704
|
+
| `runId` | `string \| null` | Id of the job WHILE it runs; null when idle |
|
|
705
|
+
|
|
706
|
+
The hook is **transparent**, mirroring `useChat`: there is no `resumeSnapshot`,
|
|
707
|
+
`resumeState`, `pendingArtifacts`, or `resultArtifacts` field. Hooks also accept
|
|
708
|
+
`persistence: true` plus a stable `threadId`: on mount the client hydrates the
|
|
709
|
+
last run for that scope from the server and repaints the **normal** `status` /
|
|
710
|
+
`result` / `error` fields, so the last run survives a reload (metadata only,
|
|
711
|
+
never media bytes; `result`'s media returns only with server byte storage +
|
|
712
|
+
`artifactUrl`). See `ai-core/client-persistence` for details.
|
|
581
713
|
|
|
582
714
|
Provide either `connection` (streaming SSE transport) or `fetcher`
|
|
583
715
|
(direct async call / server function returning `Response`). Use `onResult`
|