@langchain/svelte 0.1.3 → 0.2.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 +235 -60
- package/dist/context.cjs +86 -0
- package/dist/context.cjs.map +1 -0
- package/dist/context.d.cts +77 -0
- package/dist/context.d.cts.map +1 -0
- package/dist/context.d.ts +77 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +85 -0
- package/dist/context.js.map +1 -0
- package/dist/index.cjs +122 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -8
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +43 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -20
- package/dist/index.js.map +1 -1
- package/dist/stream.custom.cjs +79 -18
- package/dist/stream.custom.cjs.map +1 -1
- package/dist/stream.custom.js +80 -19
- package/dist/stream.custom.js.map +1 -1
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @langchain/svelte
|
|
2
2
|
|
|
3
|
-
Svelte SDK for building AI-powered applications with [LangChain](https://
|
|
3
|
+
Svelte SDK for building AI-powered applications with [Deep Agents](https://docs.langchain.com/oss/javascript/deepagents/overview), [LangChain](https://docs.langchain.com/oss/javascript/langchain/overview) and [LangGraph](https://docs.langchain.com/oss/javascript/langgraph/overview). It provides a `useStream` function that manages streaming, state, branching, and interrupts with a Svelte 5 runes-compatible reactive API.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Svelte SDK for building AI-powered applications with [LangChain](https://js.lang
|
|
|
8
8
|
npm install @langchain/svelte @langchain/core
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
**Peer dependencies:** `svelte` (^
|
|
11
|
+
**Peer dependencies:** `svelte` (^5.0.0), `@langchain/core` (^1.0.1)
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
@@ -16,27 +16,29 @@ npm install @langchain/svelte @langchain/core
|
|
|
16
16
|
<script lang="ts">
|
|
17
17
|
import { useStream } from "@langchain/svelte";
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const stream = useStream({
|
|
20
20
|
assistantId: "agent",
|
|
21
21
|
apiUrl: "http://localhost:2024",
|
|
22
22
|
});
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<div>
|
|
26
|
-
{#each
|
|
26
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
27
27
|
<div>{msg.content}</div>
|
|
28
28
|
{/each}
|
|
29
29
|
|
|
30
30
|
<button
|
|
31
|
-
disabled={
|
|
31
|
+
disabled={stream.isLoading}
|
|
32
32
|
onclick={() =>
|
|
33
|
-
void submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
33
|
+
void stream.submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
34
34
|
>
|
|
35
35
|
Send
|
|
36
36
|
</button>
|
|
37
37
|
</div>
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
All reactive properties (`messages`, `isLoading`, `values`, etc.) are accessed directly on the returned object — no `$` prefix needed. Avoid destructuring reactive properties; use `stream.messages` instead of `const { messages } = stream` to keep reactivity intact.
|
|
41
|
+
|
|
40
42
|
## `useStream` Options
|
|
41
43
|
|
|
42
44
|
| Option | Type | Description |
|
|
@@ -57,23 +59,23 @@ npm install @langchain/svelte @langchain/core
|
|
|
57
59
|
|
|
58
60
|
## Return Values
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
All reactive properties are exposed as getters on the returned object. They update automatically and can be read directly in Svelte 5 templates without the `$` prefix.
|
|
61
63
|
|
|
62
64
|
| Property | Type | Description |
|
|
63
65
|
|---|---|---|
|
|
64
|
-
| `values` | `
|
|
65
|
-
| `messages` | `
|
|
66
|
-
| `isLoading` | `
|
|
67
|
-
| `error` | `
|
|
68
|
-
| `interrupt` | `
|
|
69
|
-
| `branch` | `
|
|
66
|
+
| `values` | `StateType` | Current graph state. |
|
|
67
|
+
| `messages` | `BaseMessage[]` | Messages from the current state. |
|
|
68
|
+
| `isLoading` | `boolean` | Whether a stream is currently active. |
|
|
69
|
+
| `error` | `unknown` | The most recent error, if any. |
|
|
70
|
+
| `interrupt` | `Interrupt \| undefined` | Current interrupt requiring user input. |
|
|
71
|
+
| `branch` | `string` | Active branch identifier. |
|
|
70
72
|
| `submit(values, options?)` | `function` | Submit new input to the graph. When called while a stream is active, the run is created on the server with `multitaskStrategy: "enqueue"` and queued automatically. |
|
|
71
73
|
| `stop()` | `function` | Cancel the active stream. |
|
|
72
74
|
| `setBranch(branch)` | `function` | Switch to a different conversation branch. |
|
|
73
75
|
| `getMessagesMetadata(msg, index?)` | `function` | Get branching and checkpoint metadata for a message. |
|
|
74
76
|
| `switchThread(id)` | `(id: string \| null) => void` | Switch to a different thread. Pass `null` to start a new thread on next submit. |
|
|
75
|
-
| `queue.entries` | `
|
|
76
|
-
| `queue.size` | `
|
|
77
|
+
| `queue.entries` | `ReadonlyArray<QueueEntry>` | Pending server-side runs. Each entry has `id` (server run ID), `values`, `options`, and `createdAt`. |
|
|
78
|
+
| `queue.size` | `number` | Number of pending runs on the server. |
|
|
77
79
|
| `queue.cancel(id)` | `(id: string) => Promise<boolean>` | Cancel a pending run on the server by its run ID. |
|
|
78
80
|
| `queue.clear()` | `() => Promise<void>` | Cancel all pending runs on the server. |
|
|
79
81
|
|
|
@@ -91,7 +93,7 @@ Provide your state type as a generic parameter:
|
|
|
91
93
|
context?: string;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
const
|
|
96
|
+
const stream = useStream<MyState>({
|
|
95
97
|
assistantId: "my-graph",
|
|
96
98
|
apiUrl: "http://localhost:2024",
|
|
97
99
|
});
|
|
@@ -105,7 +107,7 @@ Provide your state type as a generic parameter:
|
|
|
105
107
|
import { useStream } from "@langchain/svelte";
|
|
106
108
|
import type { BaseMessage } from "langchain";
|
|
107
109
|
|
|
108
|
-
const
|
|
110
|
+
const stream = useStream<
|
|
109
111
|
{ messages: BaseMessage[] },
|
|
110
112
|
{ InterruptType: { question: string } }
|
|
111
113
|
>({
|
|
@@ -122,7 +124,7 @@ Provide your state type as a generic parameter:
|
|
|
122
124
|
import { useStream } from "@langchain/svelte";
|
|
123
125
|
import type { BaseMessage } from "langchain";
|
|
124
126
|
|
|
125
|
-
const
|
|
127
|
+
const stream = useStream<
|
|
126
128
|
{ messages: BaseMessage[] },
|
|
127
129
|
{ InterruptType: { question: string } }
|
|
128
130
|
>({
|
|
@@ -132,14 +134,14 @@ Provide your state type as a generic parameter:
|
|
|
132
134
|
</script>
|
|
133
135
|
|
|
134
136
|
<div>
|
|
135
|
-
{#each
|
|
137
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
136
138
|
<div>{msg.content}</div>
|
|
137
139
|
{/each}
|
|
138
140
|
|
|
139
|
-
{#if
|
|
141
|
+
{#if stream.interrupt}
|
|
140
142
|
<div>
|
|
141
|
-
<p>{
|
|
142
|
-
<button onclick={() => void submit(null, { command: { resume: "Approved" } })}>
|
|
143
|
+
<p>{stream.interrupt.value.question}</p>
|
|
144
|
+
<button onclick={() => void stream.submit(null, { command: { resume: "Approved" } })}>
|
|
143
145
|
Approve
|
|
144
146
|
</button>
|
|
145
147
|
</div>
|
|
@@ -147,7 +149,7 @@ Provide your state type as a generic parameter:
|
|
|
147
149
|
|
|
148
150
|
<button
|
|
149
151
|
onclick={() =>
|
|
150
|
-
void submit({ messages: [{ type: "human", content: "Hello" }] })}
|
|
152
|
+
void stream.submit({ messages: [{ type: "human", content: "Hello" }] })}
|
|
151
153
|
>
|
|
152
154
|
Send
|
|
153
155
|
</button>
|
|
@@ -162,7 +164,7 @@ Enable conversation branching with `fetchStateHistory: true`:
|
|
|
162
164
|
<script lang="ts">
|
|
163
165
|
import { useStream } from "@langchain/svelte";
|
|
164
166
|
|
|
165
|
-
const
|
|
167
|
+
const stream = useStream({
|
|
166
168
|
assistantId: "agent",
|
|
167
169
|
apiUrl: "http://localhost:2024",
|
|
168
170
|
fetchStateHistory: true,
|
|
@@ -170,27 +172,27 @@ Enable conversation branching with `fetchStateHistory: true`:
|
|
|
170
172
|
</script>
|
|
171
173
|
|
|
172
174
|
<div>
|
|
173
|
-
{#each
|
|
174
|
-
{@const metadata = getMessagesMetadata(msg, i)}
|
|
175
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
176
|
+
{@const metadata = stream.getMessagesMetadata(msg, i)}
|
|
175
177
|
{@const branchOptions = metadata?.branchOptions}
|
|
176
|
-
{@const
|
|
178
|
+
{@const currentBranch = metadata?.branch}
|
|
177
179
|
|
|
178
180
|
<div>
|
|
179
181
|
<p>{msg.content}</p>
|
|
180
182
|
|
|
181
|
-
{#if branchOptions &&
|
|
183
|
+
{#if branchOptions && currentBranch}
|
|
182
184
|
<button onclick={() => {
|
|
183
|
-
const prev = branchOptions[branchOptions.indexOf(
|
|
184
|
-
if (prev) setBranch(prev);
|
|
185
|
+
const prev = branchOptions[branchOptions.indexOf(currentBranch) - 1];
|
|
186
|
+
if (prev) stream.setBranch(prev);
|
|
185
187
|
}}>
|
|
186
188
|
Previous
|
|
187
189
|
</button>
|
|
188
190
|
<span>
|
|
189
|
-
{branchOptions.indexOf(
|
|
191
|
+
{branchOptions.indexOf(currentBranch) + 1} / {branchOptions.length}
|
|
190
192
|
</span>
|
|
191
193
|
<button onclick={() => {
|
|
192
|
-
const next = branchOptions[branchOptions.indexOf(
|
|
193
|
-
if (next) setBranch(next);
|
|
194
|
+
const next = branchOptions[branchOptions.indexOf(currentBranch) + 1];
|
|
195
|
+
if (next) stream.setBranch(next);
|
|
194
196
|
}}>
|
|
195
197
|
Next
|
|
196
198
|
</button>
|
|
@@ -200,7 +202,7 @@ Enable conversation branching with `fetchStateHistory: true`:
|
|
|
200
202
|
|
|
201
203
|
<button
|
|
202
204
|
onclick={() =>
|
|
203
|
-
void submit({ messages: [{ type: "human", content: "Hello" }] })}
|
|
205
|
+
void stream.submit({ messages: [{ type: "human", content: "Hello" }] })}
|
|
204
206
|
>
|
|
205
207
|
Send
|
|
206
208
|
</button>
|
|
@@ -215,40 +217,130 @@ When `submit()` is called while a stream is already active, the SDK automaticall
|
|
|
215
217
|
<script lang="ts">
|
|
216
218
|
import { useStream } from "@langchain/svelte";
|
|
217
219
|
|
|
218
|
-
const
|
|
220
|
+
const stream = useStream({
|
|
219
221
|
assistantId: "agent",
|
|
220
222
|
apiUrl: "http://localhost:2024",
|
|
221
223
|
});
|
|
222
|
-
|
|
223
|
-
const queueSize = queue.size;
|
|
224
|
-
const queueEntries = queue.entries;
|
|
225
224
|
</script>
|
|
226
225
|
|
|
227
226
|
<div>
|
|
228
|
-
{#each
|
|
227
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
229
228
|
<div>{msg.content}</div>
|
|
230
229
|
{/each}
|
|
231
230
|
|
|
232
|
-
{#if
|
|
231
|
+
{#if stream.queue.size > 0}
|
|
233
232
|
<div>
|
|
234
|
-
<p>{
|
|
235
|
-
<button onclick={() => void queue.clear()}>Clear Queue</button>
|
|
233
|
+
<p>{stream.queue.size} message(s) queued</p>
|
|
234
|
+
<button onclick={() => void stream.queue.clear()}>Clear Queue</button>
|
|
236
235
|
</div>
|
|
237
236
|
{/if}
|
|
238
237
|
|
|
239
238
|
<button
|
|
240
|
-
disabled={
|
|
239
|
+
disabled={stream.isLoading}
|
|
241
240
|
onclick={() =>
|
|
242
|
-
void submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
241
|
+
void stream.submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
243
242
|
>
|
|
244
243
|
Send
|
|
245
244
|
</button>
|
|
246
|
-
<button onclick={() => switchThread(null)}>New Thread</button>
|
|
245
|
+
<button onclick={() => stream.switchThread(null)}>New Thread</button>
|
|
247
246
|
</div>
|
|
248
247
|
```
|
|
249
248
|
|
|
250
249
|
Switching threads via `switchThread()` cancels all pending runs and clears the queue.
|
|
251
250
|
|
|
251
|
+
## Stream Context
|
|
252
|
+
|
|
253
|
+
Use `setStreamContext` and `getStreamContext` to share a single `useStream` instance across a component tree without prop drilling. This uses Svelte's built-in `setContext` / `getContext` under the hood.
|
|
254
|
+
|
|
255
|
+
### Setting context in a parent
|
|
256
|
+
|
|
257
|
+
Call `setStreamContext` during component initialisation to provide the stream to all descendants:
|
|
258
|
+
|
|
259
|
+
```svelte
|
|
260
|
+
<script lang="ts">
|
|
261
|
+
import { useStream, setStreamContext } from "@langchain/svelte";
|
|
262
|
+
import ChatMessages from "./ChatMessages.svelte";
|
|
263
|
+
import ChatInput from "./ChatInput.svelte";
|
|
264
|
+
|
|
265
|
+
const stream = useStream({
|
|
266
|
+
assistantId: "agent",
|
|
267
|
+
apiUrl: "http://localhost:2024",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
setStreamContext(stream);
|
|
271
|
+
</script>
|
|
272
|
+
|
|
273
|
+
<ChatMessages />
|
|
274
|
+
<ChatInput />
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Consuming context in a child
|
|
278
|
+
|
|
279
|
+
Call `getStreamContext` in any descendant to retrieve the stream. The returned value has the same shape and types as the `useStream` return value:
|
|
280
|
+
|
|
281
|
+
```svelte
|
|
282
|
+
<script lang="ts">
|
|
283
|
+
import { getStreamContext } from "@langchain/svelte";
|
|
284
|
+
|
|
285
|
+
const stream = getStreamContext();
|
|
286
|
+
</script>
|
|
287
|
+
|
|
288
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
289
|
+
<div>{msg.content}</div>
|
|
290
|
+
{/each}
|
|
291
|
+
|
|
292
|
+
{#if stream.isLoading}
|
|
293
|
+
<p>Thinking…</p>
|
|
294
|
+
{/if}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```svelte
|
|
298
|
+
<script lang="ts">
|
|
299
|
+
import { getStreamContext } from "@langchain/svelte";
|
|
300
|
+
|
|
301
|
+
const stream = getStreamContext();
|
|
302
|
+
|
|
303
|
+
let input = $state("");
|
|
304
|
+
</script>
|
|
305
|
+
|
|
306
|
+
<form onsubmit={(e) => { e.preventDefault(); void stream.submit({ messages: [{ type: "human", content: input }] }); input = ""; }}>
|
|
307
|
+
<input bind:value={input} />
|
|
308
|
+
<button type="submit">Send</button>
|
|
309
|
+
</form>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Type safety
|
|
313
|
+
|
|
314
|
+
`getStreamContext` accepts the same generic parameters as `useStream` so child components can be fully typed:
|
|
315
|
+
|
|
316
|
+
```svelte
|
|
317
|
+
<script lang="ts">
|
|
318
|
+
import { getStreamContext } from "@langchain/svelte";
|
|
319
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
320
|
+
|
|
321
|
+
interface MyState {
|
|
322
|
+
messages: BaseMessage[];
|
|
323
|
+
context?: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const stream = getStreamContext<MyState>();
|
|
327
|
+
</script>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`setStreamContext` returns the stream it was given, so you can inline both calls:
|
|
331
|
+
|
|
332
|
+
```svelte
|
|
333
|
+
<script lang="ts">
|
|
334
|
+
import { useStream, setStreamContext } from "@langchain/svelte";
|
|
335
|
+
|
|
336
|
+
const stream = setStreamContext(
|
|
337
|
+
useStream({ assistantId: "agent", apiUrl: "http://localhost:2024" }),
|
|
338
|
+
);
|
|
339
|
+
</script>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
> **Note:** Both functions must be called during component initialisation (i.e. at the top level of a `<script>` block), just like Svelte's own `setContext` / `getContext`. Calling `getStreamContext` without a parent `setStreamContext` throws an error.
|
|
343
|
+
|
|
252
344
|
## Custom Transport
|
|
253
345
|
|
|
254
346
|
Instead of connecting to a LangGraph API, you can provide your own streaming transport. Pass a `transport` object instead of `assistantId` to use a custom backend:
|
|
@@ -258,14 +350,7 @@ Instead of connecting to a LangGraph API, you can provide your own streaming tra
|
|
|
258
350
|
import { useStream, FetchStreamTransport } from "@langchain/svelte";
|
|
259
351
|
import type { BaseMessage } from "langchain";
|
|
260
352
|
|
|
261
|
-
const {
|
|
262
|
-
messages,
|
|
263
|
-
submit,
|
|
264
|
-
isLoading,
|
|
265
|
-
branch,
|
|
266
|
-
setBranch,
|
|
267
|
-
getMessagesMetadata,
|
|
268
|
-
} = useStream<{ messages: BaseMessage[] }>({
|
|
353
|
+
const stream = useStream<{ messages: BaseMessage[] }>({
|
|
269
354
|
transport: new FetchStreamTransport({
|
|
270
355
|
url: "https://my-api.example.com/stream",
|
|
271
356
|
}),
|
|
@@ -275,8 +360,8 @@ Instead of connecting to a LangGraph API, you can provide your own streaming tra
|
|
|
275
360
|
</script>
|
|
276
361
|
|
|
277
362
|
<div>
|
|
278
|
-
{#each
|
|
279
|
-
{@const metadata = getMessagesMetadata(msg, i)}
|
|
363
|
+
{#each stream.messages as msg, i (msg.id ?? i)}
|
|
364
|
+
{@const metadata = stream.getMessagesMetadata(msg, i)}
|
|
280
365
|
<div>
|
|
281
366
|
<p>{msg.content}</p>
|
|
282
367
|
{#if metadata?.streamMetadata}
|
|
@@ -285,23 +370,113 @@ Instead of connecting to a LangGraph API, you can provide your own streaming tra
|
|
|
285
370
|
</div>
|
|
286
371
|
{/each}
|
|
287
372
|
|
|
288
|
-
<p>Current branch: {
|
|
373
|
+
<p>Current branch: {stream.branch}</p>
|
|
289
374
|
|
|
290
375
|
<button
|
|
291
|
-
disabled={
|
|
376
|
+
disabled={stream.isLoading}
|
|
292
377
|
onclick={() =>
|
|
293
|
-
void submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
378
|
+
void stream.submit({ messages: [{ type: "human", content: "Hello!" }] })}
|
|
294
379
|
>
|
|
295
380
|
Send
|
|
296
381
|
</button>
|
|
297
382
|
</div>
|
|
298
383
|
```
|
|
299
384
|
|
|
300
|
-
The custom transport interface returns the same properties as the standard `useStream` function, including `getMessagesMetadata`, `branch`, `setBranch`, `switchThread`, and all message/interrupt/subagent helpers. When using a custom transport, `getMessagesMetadata` returns stream metadata sent alongside messages during streaming; `branch` and `setBranch` provide local branch state management.
|
|
385
|
+
The custom transport interface returns the same properties as the standard `useStream` function, including `getMessagesMetadata`, `branch`, `setBranch`, `switchThread`, and all message/interrupt/subagent helpers. When using a custom transport, `getMessagesMetadata` returns stream metadata sent alongside messages during streaming; `branch` and `setBranch` provide local branch state management. `onFinish` is also supported and receives a synthetic `ThreadState` built from the final locally streamed values; the run metadata argument is `undefined`.
|
|
386
|
+
|
|
387
|
+
## Sharing State with `provideStream`
|
|
388
|
+
|
|
389
|
+
When multiple components need access to the same stream (a message list, a header, an input bar), use `provideStream` and `getStream` to share a single stream instance via Svelte's context API:
|
|
390
|
+
|
|
391
|
+
```svelte
|
|
392
|
+
<!-- ChatContainer.svelte -->
|
|
393
|
+
<script lang="ts">
|
|
394
|
+
import { provideStream } from "@langchain/svelte";
|
|
395
|
+
import ChatHeader from "./ChatHeader.svelte";
|
|
396
|
+
import MessageList from "./MessageList.svelte";
|
|
397
|
+
import MessageInput from "./MessageInput.svelte";
|
|
398
|
+
|
|
399
|
+
provideStream({
|
|
400
|
+
assistantId: "agent",
|
|
401
|
+
apiUrl: "http://localhost:2024",
|
|
402
|
+
});
|
|
403
|
+
</script>
|
|
404
|
+
|
|
405
|
+
<ChatHeader />
|
|
406
|
+
<MessageList />
|
|
407
|
+
<MessageInput />
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```svelte
|
|
411
|
+
<!-- MessageList.svelte -->
|
|
412
|
+
<script lang="ts">
|
|
413
|
+
import { getStream } from "@langchain/svelte";
|
|
414
|
+
|
|
415
|
+
const stream = getStream();
|
|
416
|
+
</script>
|
|
417
|
+
|
|
418
|
+
{#each stream.messages as msg (msg.id)}
|
|
419
|
+
<div>{msg.content}</div>
|
|
420
|
+
{/each}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
```svelte
|
|
424
|
+
<!-- MessageInput.svelte -->
|
|
425
|
+
<script lang="ts">
|
|
426
|
+
import { getStream } from "@langchain/svelte";
|
|
427
|
+
|
|
428
|
+
const stream = getStream();
|
|
429
|
+
let input = $state("");
|
|
430
|
+
|
|
431
|
+
function send() {
|
|
432
|
+
stream.submit({ messages: [{ type: "human", content: input }] });
|
|
433
|
+
input = "";
|
|
434
|
+
}
|
|
435
|
+
</script>
|
|
436
|
+
|
|
437
|
+
<form onsubmit={send}>
|
|
438
|
+
<textarea bind:value={input}></textarea>
|
|
439
|
+
<button disabled={stream.isLoading} type="submit">Send</button>
|
|
440
|
+
</form>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
```svelte
|
|
444
|
+
<!-- ChatHeader.svelte -->
|
|
445
|
+
<script lang="ts">
|
|
446
|
+
import { getStream } from "@langchain/svelte";
|
|
447
|
+
|
|
448
|
+
const stream = getStream();
|
|
449
|
+
</script>
|
|
450
|
+
|
|
451
|
+
<header>
|
|
452
|
+
<h1>Chat</h1>
|
|
453
|
+
{#if stream.isLoading}
|
|
454
|
+
<span>Thinking...</span>
|
|
455
|
+
{/if}
|
|
456
|
+
{#if stream.error}
|
|
457
|
+
<span>Error occurred</span>
|
|
458
|
+
{/if}
|
|
459
|
+
</header>
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Multiple Agents
|
|
463
|
+
|
|
464
|
+
Nest `provideStream` calls for multi-agent scenarios — Svelte's context scoping ensures each subtree gets its own stream:
|
|
465
|
+
|
|
466
|
+
```svelte
|
|
467
|
+
<!-- ResearchPanel.svelte -->
|
|
468
|
+
<script lang="ts">
|
|
469
|
+
import { provideStream } from "@langchain/svelte";
|
|
470
|
+
provideStream({ assistantId: "researcher", apiUrl: "http://localhost:2024" });
|
|
471
|
+
</script>
|
|
472
|
+
|
|
473
|
+
<MessageList />
|
|
474
|
+
<MessageInput />
|
|
475
|
+
```
|
|
301
476
|
|
|
302
477
|
## Playground
|
|
303
478
|
|
|
304
|
-
For complete end-to-end examples with full agentic UIs, visit the [
|
|
479
|
+
For complete end-to-end examples with full agentic UIs, visit the [LangChain UI Playground](https://docs.langchain.com/playground).
|
|
305
480
|
|
|
306
481
|
## License
|
|
307
482
|
|
package/dist/context.cjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const require_index = require("./index.cjs");
|
|
2
|
+
let svelte = require("svelte");
|
|
3
|
+
//#region src/context.ts
|
|
4
|
+
const STREAM_CONTEXT_KEY = Symbol("langchain-stream");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a shared `useStream` instance and makes it available to all
|
|
7
|
+
* descendant components via Svelte's `setContext`/`getContext`.
|
|
8
|
+
*
|
|
9
|
+
* Call this in a parent component's `<script>` block. Children access
|
|
10
|
+
* the shared stream via `getStream()`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```svelte
|
|
14
|
+
* <!-- ChatContainer.svelte -->
|
|
15
|
+
* <script lang="ts">
|
|
16
|
+
* import { provideStream } from "@langchain/svelte";
|
|
17
|
+
*
|
|
18
|
+
* provideStream({
|
|
19
|
+
* assistantId: "agent",
|
|
20
|
+
* apiUrl: "http://localhost:2024",
|
|
21
|
+
* });
|
|
22
|
+
* <\/script>
|
|
23
|
+
*
|
|
24
|
+
* <ChatHeader />
|
|
25
|
+
* <MessageList />
|
|
26
|
+
* <MessageInput />
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @returns The stream instance (same as calling `useStream` directly).
|
|
30
|
+
*/
|
|
31
|
+
function provideStream(options) {
|
|
32
|
+
const stream = require_index.useStream(options);
|
|
33
|
+
(0, svelte.setContext)(STREAM_CONTEXT_KEY, stream);
|
|
34
|
+
return stream;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the shared stream instance from the nearest ancestor that
|
|
38
|
+
* called `provideStream()`.
|
|
39
|
+
*
|
|
40
|
+
* Throws if no ancestor has provided a stream.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```svelte
|
|
44
|
+
* <!-- MessageList.svelte -->
|
|
45
|
+
* <script lang="ts">
|
|
46
|
+
* import { getStream } from "@langchain/svelte";
|
|
47
|
+
*
|
|
48
|
+
* const stream = getStream();
|
|
49
|
+
* <\/script>
|
|
50
|
+
*
|
|
51
|
+
* {#each stream.messages as msg (msg.id)}
|
|
52
|
+
* <div>{msg.content}</div>
|
|
53
|
+
* {/each}
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```svelte
|
|
58
|
+
* <!-- MessageInput.svelte -->
|
|
59
|
+
* <script lang="ts">
|
|
60
|
+
* import { getStream } from "@langchain/svelte";
|
|
61
|
+
*
|
|
62
|
+
* const stream = getStream();
|
|
63
|
+
* let input = $state("");
|
|
64
|
+
*
|
|
65
|
+
* function send() {
|
|
66
|
+
* stream.submit({ messages: [{ type: "human", content: input }] });
|
|
67
|
+
* input = "";
|
|
68
|
+
* }
|
|
69
|
+
* <\/script>
|
|
70
|
+
*
|
|
71
|
+
* <form onsubmit={send}>
|
|
72
|
+
* <textarea bind:value={input}></textarea>
|
|
73
|
+
* <button disabled={stream.isLoading} type="submit">Send</button>
|
|
74
|
+
* </form>
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
function getStream() {
|
|
78
|
+
const context = (0, svelte.getContext)(STREAM_CONTEXT_KEY);
|
|
79
|
+
if (context == null) throw new Error("getStream() requires a parent component to call provideStream(). Add provideStream({ assistantId: '...' }) in an ancestor component.");
|
|
80
|
+
return context;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
exports.getStream = getStream;
|
|
84
|
+
exports.provideStream = provideStream;
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=context.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.cjs","names":["useStream"],"sources":["../src/context.ts"],"sourcesContent":["import { setContext, getContext } from \"svelte\";\nimport type { BagTemplate } from \"@langchain/langgraph-sdk\";\nimport type {\n ResolveStreamOptions,\n InferBag,\n InferStateType,\n UseStreamCustomOptions,\n} from \"@langchain/langgraph-sdk/ui\";\nimport { useStream } from \"./index.js\";\n\nconst STREAM_CONTEXT_KEY = Symbol(\"langchain-stream\");\n\n/**\n * Creates a shared `useStream` instance and makes it available to all\n * descendant components via Svelte's `setContext`/`getContext`.\n *\n * Call this in a parent component's `<script>` block. Children access\n * the shared stream via `getStream()`.\n *\n * @example\n * ```svelte\n * <!-- ChatContainer.svelte -->\n * <script lang=\"ts\">\n * import { provideStream } from \"@langchain/svelte\";\n *\n * provideStream({\n * assistantId: \"agent\",\n * apiUrl: \"http://localhost:2024\",\n * });\n * </script>\n *\n * <ChatHeader />\n * <MessageList />\n * <MessageInput />\n * ```\n *\n * @returns The stream instance (same as calling `useStream` directly).\n */\nexport function provideStream<\n T = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(\n options:\n | ResolveStreamOptions<T, InferBag<T, Bag>>\n | UseStreamCustomOptions<InferStateType<T>, InferBag<T, Bag>>,\n): ReturnType<typeof useStream<T, Bag>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = useStream<T, Bag>(options as any);\n setContext(STREAM_CONTEXT_KEY, stream);\n return stream;\n}\n\n/**\n * Retrieves the shared stream instance from the nearest ancestor that\n * called `provideStream()`.\n *\n * Throws if no ancestor has provided a stream.\n *\n * @example\n * ```svelte\n * <!-- MessageList.svelte -->\n * <script lang=\"ts\">\n * import { getStream } from \"@langchain/svelte\";\n *\n * const stream = getStream();\n * </script>\n *\n * {#each stream.messages as msg (msg.id)}\n * <div>{msg.content}</div>\n * {/each}\n * ```\n *\n * @example\n * ```svelte\n * <!-- MessageInput.svelte -->\n * <script lang=\"ts\">\n * import { getStream } from \"@langchain/svelte\";\n *\n * const stream = getStream();\n * let input = $state(\"\");\n *\n * function send() {\n * stream.submit({ messages: [{ type: \"human\", content: input }] });\n * input = \"\";\n * }\n * </script>\n *\n * <form onsubmit={send}>\n * <textarea bind:value={input}></textarea>\n * <button disabled={stream.isLoading} type=\"submit\">Send</button>\n * </form>\n * ```\n */\nexport function getStream<\n T = Record<string, unknown>,\n Bag extends BagTemplate = BagTemplate,\n>(): ReturnType<typeof useStream<T, Bag>> {\n const context = getContext(STREAM_CONTEXT_KEY);\n if (context == null) {\n throw new Error(\n \"getStream() requires a parent component to call provideStream(). \" +\n \"Add provideStream({ assistantId: '...' }) in an ancestor component.\",\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return context as any;\n}\n"],"mappings":";;;AAUA,MAAM,qBAAqB,OAAO,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BrD,SAAgB,cAId,SAGsC;CAEtC,MAAM,SAASA,cAAAA,UAAkB,QAAe;AAChD,EAAA,GAAA,OAAA,YAAW,oBAAoB,OAAO;AACtC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CT,SAAgB,YAG0B;CACxC,MAAM,WAAA,GAAA,OAAA,YAAqB,mBAAmB;AAC9C,KAAI,WAAW,KACb,OAAM,IAAI,MACR,uIAED;AAGH,QAAO"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useStream } from "./index.cjs";
|
|
2
|
+
import { InferBag, InferStateType, ResolveStreamOptions, UseStreamCustomOptions } from "@langchain/langgraph-sdk/ui";
|
|
3
|
+
import { BagTemplate } from "@langchain/langgraph-sdk";
|
|
4
|
+
|
|
5
|
+
//#region src/context.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Creates a shared `useStream` instance and makes it available to all
|
|
8
|
+
* descendant components via Svelte's `setContext`/`getContext`.
|
|
9
|
+
*
|
|
10
|
+
* Call this in a parent component's `<script>` block. Children access
|
|
11
|
+
* the shared stream via `getStream()`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```svelte
|
|
15
|
+
* <!-- ChatContainer.svelte -->
|
|
16
|
+
* <script lang="ts">
|
|
17
|
+
* import { provideStream } from "@langchain/svelte";
|
|
18
|
+
*
|
|
19
|
+
* provideStream({
|
|
20
|
+
* assistantId: "agent",
|
|
21
|
+
* apiUrl: "http://localhost:2024",
|
|
22
|
+
* });
|
|
23
|
+
* </script>
|
|
24
|
+
*
|
|
25
|
+
* <ChatHeader />
|
|
26
|
+
* <MessageList />
|
|
27
|
+
* <MessageInput />
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @returns The stream instance (same as calling `useStream` directly).
|
|
31
|
+
*/
|
|
32
|
+
declare function provideStream<T = Record<string, unknown>, Bag extends BagTemplate = BagTemplate>(options: ResolveStreamOptions<T, InferBag<T, Bag>> | UseStreamCustomOptions<InferStateType<T>, InferBag<T, Bag>>): ReturnType<typeof useStream<T, Bag>>;
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves the shared stream instance from the nearest ancestor that
|
|
35
|
+
* called `provideStream()`.
|
|
36
|
+
*
|
|
37
|
+
* Throws if no ancestor has provided a stream.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```svelte
|
|
41
|
+
* <!-- MessageList.svelte -->
|
|
42
|
+
* <script lang="ts">
|
|
43
|
+
* import { getStream } from "@langchain/svelte";
|
|
44
|
+
*
|
|
45
|
+
* const stream = getStream();
|
|
46
|
+
* </script>
|
|
47
|
+
*
|
|
48
|
+
* {#each stream.messages as msg (msg.id)}
|
|
49
|
+
* <div>{msg.content}</div>
|
|
50
|
+
* {/each}
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```svelte
|
|
55
|
+
* <!-- MessageInput.svelte -->
|
|
56
|
+
* <script lang="ts">
|
|
57
|
+
* import { getStream } from "@langchain/svelte";
|
|
58
|
+
*
|
|
59
|
+
* const stream = getStream();
|
|
60
|
+
* let input = $state("");
|
|
61
|
+
*
|
|
62
|
+
* function send() {
|
|
63
|
+
* stream.submit({ messages: [{ type: "human", content: input }] });
|
|
64
|
+
* input = "";
|
|
65
|
+
* }
|
|
66
|
+
* </script>
|
|
67
|
+
*
|
|
68
|
+
* <form onsubmit={send}>
|
|
69
|
+
* <textarea bind:value={input}></textarea>
|
|
70
|
+
* <button disabled={stream.isLoading} type="submit">Send</button>
|
|
71
|
+
* </form>
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare function getStream<T = Record<string, unknown>, Bag extends BagTemplate = BagTemplate>(): ReturnType<typeof useStream<T, Bag>>;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { getStream, provideStream };
|
|
77
|
+
//# sourceMappingURL=context.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.cts","names":[],"sources":["../src/context.ts"],"mappings":";;;;;;;AAsCA;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,KACV,MAAA,+BACQ,WAAA,GAAc,WAAA,CAAA,CAE1B,OAAA,EACI,oBAAA,CAAqB,CAAA,EAAG,QAAA,CAAS,CAAA,EAAG,GAAA,KACpC,sBAAA,CAAuB,cAAA,CAAe,CAAA,GAAI,QAAA,CAAS,CAAA,EAAG,GAAA,KACzD,UAAA,QAAkB,SAAA,CAAU,CAAA,EAAG,GAAA;;;;;;;;;;;;;;;;;;;;;;AAgDlC;;;;;;;;;;;;;;;;;;;;iBAAgB,SAAA,KACV,MAAA,+BACQ,WAAA,GAAc,WAAA,CAAA,CAAA,GACvB,UAAA,QAAkB,SAAA,CAAU,CAAA,EAAG,GAAA"}
|