@tanstack/ai-durable-stream 0.1.14 → 0.1.16
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 +117 -0
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source
|
|
4
|
+
media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://tanstack.com/api/readme/ai.png?theme=dark"
|
|
6
|
+
/>
|
|
7
|
+
<source
|
|
8
|
+
media="(prefers-color-scheme: light)"
|
|
9
|
+
srcset="https://tanstack.com/api/readme/ai.png"
|
|
10
|
+
/>
|
|
11
|
+
<img
|
|
12
|
+
src="https://tanstack.com/api/readme/ai.png"
|
|
13
|
+
alt="TanStack AI"
|
|
14
|
+
width="900"
|
|
15
|
+
/>
|
|
16
|
+
</picture>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<br />
|
|
20
|
+
|
|
21
|
+
# @tanstack/ai-durable-stream
|
|
22
|
+
|
|
23
|
+
Delivery durability for TanStack AI over the durable-streams HTTP protocol — a resumable `StreamDurability` transport sink that stores zero delivery events itself.
|
|
24
|
+
|
|
25
|
+
`durableStream(request, options)` is the production adapter for [Resumable Streams](https://tanstack.com/ai/latest/docs/resumable-streams/overview): when a client's connection drops mid-answer, it reconnects and the run is replayed from a log instead of re-run. `memoryStream` from `@tanstack/ai` keeps that log in process memory for development; this package writes it to an external [Durable Streams](https://durablestreams.com) backend, so it works when requests span many processes — and because the producer runs against the backend rather than the client's socket, a reconnect can pick up a run that is still producing.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @tanstack/ai-durable-stream
|
|
31
|
+
# or
|
|
32
|
+
pnpm add @tanstack/ai-durable-stream
|
|
33
|
+
# or
|
|
34
|
+
yarn add @tanstack/ai-durable-stream
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Wrap the response with the adapter. Everything else about the route stays the same as with `memoryStream`.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import {
|
|
43
|
+
chat,
|
|
44
|
+
chatParamsFromRequest,
|
|
45
|
+
toServerSentEventsResponse,
|
|
46
|
+
} from '@tanstack/ai'
|
|
47
|
+
import { durableStream } from '@tanstack/ai-durable-stream'
|
|
48
|
+
import { openaiText } from '@tanstack/ai-openai'
|
|
49
|
+
import { getDurableStreamsToken } from './auth'
|
|
50
|
+
|
|
51
|
+
const durableOptions = {
|
|
52
|
+
server: 'https://streams.example.com',
|
|
53
|
+
streamPrefix: 'chat-runs',
|
|
54
|
+
headers: async () => ({
|
|
55
|
+
Authorization: `Bearer ${await getDurableStreamsToken()}`,
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function POST(request: Request) {
|
|
60
|
+
const { messages, threadId, runId } = await chatParamsFromRequest(request)
|
|
61
|
+
const stream = chat({
|
|
62
|
+
adapter: openaiText('gpt-5.5'),
|
|
63
|
+
messages,
|
|
64
|
+
threadId,
|
|
65
|
+
runId,
|
|
66
|
+
})
|
|
67
|
+
return toServerSentEventsResponse(stream, {
|
|
68
|
+
durability: { adapter: durableStream(request, durableOptions), batch: 32 },
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Add a `GET` on the same route that replays the run — `resumeServerSentEventsResponse({ adapter: durableStream(request, durableOptions) })` — and the client reconnects to it; the [Overview](https://tanstack.com/ai/latest/docs/resumable-streams/overview) has the full handler and the one gotcha (guard your side effects behind `resumeFrom()` so a reconnect does not run them twice).
|
|
74
|
+
|
|
75
|
+
### Options
|
|
76
|
+
|
|
77
|
+
- `server` — base URL of the Durable Streams backend.
|
|
78
|
+
- `streamPrefix` — prefix for the stream names the adapter creates.
|
|
79
|
+
- `headers` — a static object for fixed credentials, or an async resolver for rotating tokens. The resolver runs for every create, append, read, and close.
|
|
80
|
+
- `fetch` — injectable `fetch`, for routing through something other than the network (see below).
|
|
81
|
+
- `batch` (on `durability`, not the adapter) — how many chunks are buffered per log append; default 32.
|
|
82
|
+
|
|
83
|
+
The backend must return a non-empty `Stream-Next-Offset` header on create, append, and close; a missing header fails loudly with `DurableStreamError`. The adapter never guesses an offset.
|
|
84
|
+
|
|
85
|
+
### On Cloudflare
|
|
86
|
+
|
|
87
|
+
Durable Streams ships a Workers + Durable Objects backend that speaks this protocol, so `durableStream` talks to it with no extra adapter. When your endpoint also runs on Workers, pass the service binding's `fetch` and skip `server`:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { durableStream } from '@tanstack/ai-durable-stream'
|
|
91
|
+
|
|
92
|
+
interface Env {
|
|
93
|
+
DURABLE_STREAMS: { fetch: typeof fetch }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cloudflareAdapter(request: Request, env: Env) {
|
|
97
|
+
return durableStream(request, {
|
|
98
|
+
streamPrefix: 'chat-runs',
|
|
99
|
+
fetch: env.DURABLE_STREAMS.fetch.bind(env.DURABLE_STREAMS),
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## What it is not
|
|
105
|
+
|
|
106
|
+
The durability log replays chunks. It answers "what did this run stream?", not "what has this user said?" — keep authoritative conversation state in your own storage, for example with [`@tanstack/ai-persistence`](https://tanstack.com/ai/latest/docs/persistence/overview).
|
|
107
|
+
|
|
108
|
+
`durableStream` returns a plain `StreamDurability` with no `upsert`: offsets embed a backend-assigned cursor, so code that requires `UpsertableStreamDurability` fails to compile at the wiring site rather than at run time.
|
|
109
|
+
|
|
110
|
+
## Documentation
|
|
111
|
+
|
|
112
|
+
- [Resumable Streams overview](https://tanstack.com/ai/latest/docs/resumable-streams/overview): pick an adapter, wrap the response, add the `GET`
|
|
113
|
+
- [Advanced](https://tanstack.com/ai/latest/docs/resumable-streams/advanced): every `durableStream` option, reconnection bounding, offset ownership, Cloudflare deployment, process death
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-durable-stream",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Delivery durability for TanStack AI over the durable-streams HTTP protocol — a resumable StreamDurability transport sink (append/read/resume) that stores zero delivery events itself.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"src"
|
|
32
32
|
],
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@tanstack/ai": "^0.
|
|
34
|
+
"@tanstack/ai": "^0.58.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitest/coverage-v8": "4.1.10",
|
|
38
|
-
"@tanstack/ai": "0.
|
|
39
|
-
"@tanstack/ai
|
|
38
|
+
"@tanstack/ai-sandbox": "0.5.12",
|
|
39
|
+
"@tanstack/ai": "0.58.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "vite build",
|