@assistant-ui/react-langchain 0.0.6 → 0.0.8
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 +9 -82
- package/dist/convertMessages.d.ts +8 -3
- package/dist/convertMessages.d.ts.map +1 -1
- package/dist/convertMessages.js +93 -105
- package/dist/convertMessages.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +3 -3
- package/dist/types.d.ts +36 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -2
- package/dist/useStreamRuntime.d.ts +57 -18
- package/dist/useStreamRuntime.d.ts.map +1 -1
- package/dist/useStreamRuntime.js +220 -198
- package/dist/useStreamRuntime.js.map +1 -1
- package/package.json +12 -12
- package/src/convertMessages.ts +1 -1
- package/src/index.ts +2 -0
- package/src/useStreamRuntime.ts +151 -28
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.js.map +0 -1
package/src/useStreamRuntime.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
AppendMessage,
|
|
6
6
|
AttachmentAdapter,
|
|
7
7
|
FeedbackAdapter,
|
|
8
|
+
RemoteThreadListAdapter,
|
|
8
9
|
SpeechSynthesisAdapter,
|
|
9
10
|
} from "@assistant-ui/core";
|
|
10
11
|
import {
|
|
@@ -18,8 +19,8 @@ import {
|
|
|
18
19
|
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
19
20
|
import type { AssistantCloud } from "assistant-cloud";
|
|
20
21
|
import { useStream, type UseStreamOptions } from "@langchain/react";
|
|
21
|
-
import type { LangChainBaseMessage } from "./types";
|
|
22
|
-
import { convertLangChainBaseMessage } from "./convertMessages";
|
|
22
|
+
import type { LangChainBaseMessage, LangChainToolCall } from "./types";
|
|
23
|
+
import { convertLangChainBaseMessage, getMessageType } from "./convertMessages";
|
|
23
24
|
|
|
24
25
|
const symbolLangChainRuntimeExtras = Symbol("langchain-runtime-extras");
|
|
25
26
|
|
|
@@ -32,6 +33,7 @@ type LangChainRuntimeExtras = {
|
|
|
32
33
|
options?: Record<string, unknown>,
|
|
33
34
|
) => Promise<void>;
|
|
34
35
|
values: Record<string, unknown>;
|
|
36
|
+
messagesKey: string;
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
const asLangChainRuntimeExtras = (extras: unknown): LangChainRuntimeExtras => {
|
|
@@ -46,7 +48,7 @@ const asLangChainRuntimeExtras = (extras: unknown): LangChainRuntimeExtras => {
|
|
|
46
48
|
return extras as LangChainRuntimeExtras;
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
type LangChainRuntimeExtraOptions = {
|
|
50
52
|
cloud?: AssistantCloud | undefined;
|
|
51
53
|
adapters?:
|
|
52
54
|
| {
|
|
@@ -55,10 +57,54 @@ export type UseStreamRuntimeOptions = UseStreamOptions & {
|
|
|
55
57
|
feedback?: FeedbackAdapter | undefined;
|
|
56
58
|
}
|
|
57
59
|
| undefined;
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
+
/**
|
|
61
|
+
* When the user sends a new message while previous tool calls are
|
|
62
|
+
* still pending, automatically submit `tool` messages that cancel
|
|
63
|
+
* them so the agent's tool-call accounting stays consistent.
|
|
64
|
+
* Defaults to `true`.
|
|
65
|
+
*/
|
|
66
|
+
autoCancelPendingToolCalls?: boolean | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Routes the Cancel button's click to `useStream().stop()`. On by
|
|
69
|
+
* default. Pass `false` to disable the Cancel button.
|
|
70
|
+
*/
|
|
71
|
+
unstable_allowCancellation?: boolean | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Custom `RemoteThreadListAdapter`. When provided, replaces the
|
|
74
|
+
* cloud-backed thread list adapter.
|
|
75
|
+
*/
|
|
76
|
+
unstable_threadListAdapter?: RemoteThreadListAdapter | undefined;
|
|
77
|
+
/** Custom thread-creation hook, forwarded to the cloud adapter. */
|
|
78
|
+
create?: (() => Promise<{ externalId: string | undefined }>) | undefined;
|
|
79
|
+
/** Custom thread-deletion hook, forwarded to the cloud adapter. */
|
|
80
|
+
delete?: ((threadId: string) => Promise<void>) | undefined;
|
|
60
81
|
};
|
|
61
82
|
|
|
83
|
+
const getPendingToolCalls = (
|
|
84
|
+
messages: readonly LangChainBaseMessage[],
|
|
85
|
+
): LangChainToolCall[] => {
|
|
86
|
+
const pending = new Map<string, LangChainToolCall>();
|
|
87
|
+
for (const m of messages) {
|
|
88
|
+
const type = getMessageType(m);
|
|
89
|
+
if (type === "ai") {
|
|
90
|
+
for (const tc of m.tool_calls ?? []) pending.set(tc.id, tc);
|
|
91
|
+
} else if (type === "tool" && m.tool_call_id) {
|
|
92
|
+
pending.delete(m.tool_call_id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return [...pending.values()];
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Distribute the intersection through the union arms of `UseStreamOptions`
|
|
99
|
+
// (`AgentServerOptions | CustomAdapterOptions`). Writing `UseStreamOptions & X`
|
|
100
|
+
// directly collapses arm tracking, so `Omit<…, "cloud">` and the like would
|
|
101
|
+
// produce a flattened structural type that no longer matches either arm.
|
|
102
|
+
export type UseStreamRuntimeOptions = UseStreamOptions extends infer O
|
|
103
|
+
? O extends UseStreamOptions
|
|
104
|
+
? O & LangChainRuntimeExtraOptions
|
|
105
|
+
: never
|
|
106
|
+
: never;
|
|
107
|
+
|
|
62
108
|
const getMessageContent = (msg: AppendMessage) => {
|
|
63
109
|
const allContent = [
|
|
64
110
|
...msg.content,
|
|
@@ -91,8 +137,12 @@ const getMessageContent = (msg: AppendMessage) => {
|
|
|
91
137
|
case "tool-call":
|
|
92
138
|
throw new Error("Tool call appends are not supported.");
|
|
93
139
|
default: {
|
|
94
|
-
const _exhaustiveCheck:
|
|
95
|
-
|
|
140
|
+
const _exhaustiveCheck:
|
|
141
|
+
| "reasoning"
|
|
142
|
+
| "source"
|
|
143
|
+
| "audio"
|
|
144
|
+
| "data"
|
|
145
|
+
| "generative-ui" = type;
|
|
96
146
|
throw new Error(
|
|
97
147
|
`Unsupported append message part type: ${_exhaustiveCheck}`,
|
|
98
148
|
);
|
|
@@ -106,21 +156,31 @@ const getMessageContent = (msg: AppendMessage) => {
|
|
|
106
156
|
return content;
|
|
107
157
|
};
|
|
108
158
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
159
|
+
type DistributiveOmit<T, K extends keyof any> = T extends unknown
|
|
160
|
+
? Omit<T, K>
|
|
161
|
+
: never;
|
|
162
|
+
|
|
163
|
+
const useStreamThreadRuntime = (
|
|
164
|
+
options: DistributiveOmit<
|
|
165
|
+
UseStreamRuntimeOptions,
|
|
166
|
+
"cloud" | "unstable_threadListAdapter" | "create" | "delete"
|
|
167
|
+
>,
|
|
168
|
+
) => {
|
|
169
|
+
const { adapters, autoCancelPendingToolCalls, unstable_allowCancellation } =
|
|
170
|
+
options;
|
|
171
|
+
const messagesKey = options.messagesKey ?? "messages";
|
|
172
|
+
|
|
114
173
|
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
115
174
|
const externalId = useAuiState((s) => s.threadListItem.externalId) as
|
|
116
175
|
| string
|
|
117
176
|
| null;
|
|
177
|
+
// Mutate in place rather than `{ ...options, threadId }`: spreading
|
|
178
|
+
// `UseStreamOptions` (a discriminated union on `transport`) into an object
|
|
179
|
+
// literal merges both arms' transport types, breaking arm assignment.
|
|
180
|
+
options.threadId = externalId;
|
|
118
181
|
|
|
119
182
|
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
120
|
-
const stream = useStream(
|
|
121
|
-
...streamOptions,
|
|
122
|
-
threadId: externalId,
|
|
123
|
-
});
|
|
183
|
+
const stream = useStream(options);
|
|
124
184
|
|
|
125
185
|
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
126
186
|
const [toolStatuses, setToolStatuses] = useState<
|
|
@@ -185,8 +245,15 @@ const useStreamThreadRuntime = ({
|
|
|
185
245
|
interrupts: stream.interrupts,
|
|
186
246
|
submit: stream.submit,
|
|
187
247
|
values: stream.values,
|
|
248
|
+
messagesKey,
|
|
188
249
|
}),
|
|
189
|
-
[
|
|
250
|
+
[
|
|
251
|
+
stream.interrupt,
|
|
252
|
+
stream.interrupts,
|
|
253
|
+
stream.submit,
|
|
254
|
+
stream.values,
|
|
255
|
+
messagesKey,
|
|
256
|
+
],
|
|
190
257
|
);
|
|
191
258
|
|
|
192
259
|
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
@@ -198,8 +265,20 @@ const useStreamThreadRuntime = ({
|
|
|
198
265
|
onNew: async (msg) => {
|
|
199
266
|
await toolInvocations.abort();
|
|
200
267
|
const content = getMessageContent(msg);
|
|
268
|
+
const cancellations =
|
|
269
|
+
autoCancelPendingToolCalls !== false
|
|
270
|
+
? getPendingToolCalls(
|
|
271
|
+
streamRef.current.messages as readonly LangChainBaseMessage[],
|
|
272
|
+
).map((t) => ({
|
|
273
|
+
type: "tool" as const,
|
|
274
|
+
name: t.name,
|
|
275
|
+
tool_call_id: t.id,
|
|
276
|
+
content: JSON.stringify({ cancelled: true }),
|
|
277
|
+
status: "error" as const,
|
|
278
|
+
}))
|
|
279
|
+
: [];
|
|
201
280
|
await stream.submit({
|
|
202
|
-
[messagesKey]: [{ type: "human", content }],
|
|
281
|
+
[messagesKey]: [...cancellations, { type: "human", content }],
|
|
203
282
|
});
|
|
204
283
|
},
|
|
205
284
|
onAddToolResult: async ({
|
|
@@ -222,10 +301,13 @@ const useStreamThreadRuntime = ({
|
|
|
222
301
|
],
|
|
223
302
|
});
|
|
224
303
|
},
|
|
225
|
-
onCancel:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
304
|
+
onCancel:
|
|
305
|
+
unstable_allowCancellation !== false
|
|
306
|
+
? async () => {
|
|
307
|
+
await stream.stop();
|
|
308
|
+
await toolInvocations.abort();
|
|
309
|
+
}
|
|
310
|
+
: undefined,
|
|
229
311
|
});
|
|
230
312
|
|
|
231
313
|
return runtime;
|
|
@@ -255,20 +337,31 @@ const useStreamThreadRuntime = ({
|
|
|
255
337
|
* }
|
|
256
338
|
* ```
|
|
257
339
|
*/
|
|
258
|
-
export const useStreamRuntime = ({
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
340
|
+
export const useStreamRuntime = (rawOptions: UseStreamRuntimeOptions) => {
|
|
341
|
+
const {
|
|
342
|
+
cloud,
|
|
343
|
+
unstable_threadListAdapter,
|
|
344
|
+
create,
|
|
345
|
+
delete: deleteFn,
|
|
346
|
+
...options
|
|
347
|
+
} = rawOptions;
|
|
348
|
+
|
|
262
349
|
const optionsRef = useRef(options);
|
|
263
350
|
optionsRef.current = options;
|
|
264
351
|
|
|
265
|
-
const cloudAdapter = useCloudThreadListAdapter({
|
|
352
|
+
const cloudAdapter = useCloudThreadListAdapter({
|
|
353
|
+
cloud,
|
|
354
|
+
create,
|
|
355
|
+
delete: deleteFn,
|
|
356
|
+
});
|
|
357
|
+
const adapter = unstable_threadListAdapter ?? cloudAdapter;
|
|
358
|
+
|
|
266
359
|
return useRemoteThreadListRuntime({
|
|
267
360
|
runtimeHook: function RuntimeHook() {
|
|
268
361
|
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
269
362
|
return useStreamThreadRuntime(optionsRef.current);
|
|
270
363
|
},
|
|
271
|
-
adapter
|
|
364
|
+
adapter,
|
|
272
365
|
allowNesting: true,
|
|
273
366
|
});
|
|
274
367
|
};
|
|
@@ -301,6 +394,36 @@ export const useLangChainSubmit = () => {
|
|
|
301
394
|
};
|
|
302
395
|
};
|
|
303
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Submit a list of LangChain-shaped messages on the current thread.
|
|
399
|
+
* Parity helper for migrating from `useLangGraphSend`. Routes to
|
|
400
|
+
* `useStream().submit({ [messagesKey]: messages }, options)`.
|
|
401
|
+
*/
|
|
402
|
+
export const useLangChainSend = () => {
|
|
403
|
+
const aui = useAui();
|
|
404
|
+
return (
|
|
405
|
+
messages: readonly LangChainBaseMessage[],
|
|
406
|
+
options?: Record<string, unknown>,
|
|
407
|
+
) => {
|
|
408
|
+
const { submit, messagesKey } = asLangChainRuntimeExtras(
|
|
409
|
+
aui.thread().getState().extras,
|
|
410
|
+
);
|
|
411
|
+
return submit({ [messagesKey]: messages }, options);
|
|
412
|
+
};
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Submit a `useStream` command (e.g. interrupt resume). Parity helper
|
|
417
|
+
* for migrating from `useLangGraphSendCommand`. Note that v1's command
|
|
418
|
+
* shape (`{ resume?, goto?, update? }`) differs from the legacy
|
|
419
|
+
* `{ resume: string }` form — to carry a payload, use the input or
|
|
420
|
+
* `stream.respond` instead.
|
|
421
|
+
*/
|
|
422
|
+
export const useLangChainSendCommand = () => {
|
|
423
|
+
const submit = useLangChainSubmit();
|
|
424
|
+
return (command: Record<string, unknown>) => submit(null, { command });
|
|
425
|
+
};
|
|
426
|
+
|
|
304
427
|
/**
|
|
305
428
|
* Read a custom LangGraph state key from the current thread. Mirrors
|
|
306
429
|
* `useStream().values[key]` from `@langchain/react` and updates when the
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,GACnB,8BAA2B;AAC5B,YAAY,EAAE,uBAAuB,EAAE,8BAA2B;AAElE,OAAO,EAAE,2BAA2B,EAAE,6BAA0B;AAEhE,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,GAClB,mBAAgB"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,GACnB,8BAA2B;AAG5B,OAAO,EAAE,2BAA2B,EAAE,6BAA0B"}
|
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|