@assistant-ui/react-langchain 0.0.14 → 0.0.17
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 +1 -1
- package/dist/convertMessages.d.ts +37 -3
- package/dist/convertMessages.d.ts.map +1 -1
- package/dist/convertMessages.js +63 -11
- package/dist/convertMessages.js.map +1 -1
- package/dist/findForkCheckpointInHistory.d.ts +26 -0
- package/dist/findForkCheckpointInHistory.d.ts.map +1 -0
- package/dist/findForkCheckpointInHistory.js +34 -0
- package/dist/findForkCheckpointInHistory.js.map +1 -0
- package/dist/hooks.d.ts +88 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +99 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -2
- package/dist/resolveForkCheckpoint.d.ts +17 -0
- package/dist/resolveForkCheckpoint.d.ts.map +1 -0
- package/dist/resolveForkCheckpoint.js +26 -0
- package/dist/resolveForkCheckpoint.js.map +1 -0
- package/dist/runtimeExtras.d.ts +7 -0
- package/dist/runtimeExtras.d.ts.map +1 -0
- package/dist/runtimeExtras.js +7 -0
- package/dist/runtimeExtras.js.map +1 -0
- package/dist/streamingTiming.d.ts +16 -0
- package/dist/streamingTiming.d.ts.map +1 -0
- package/dist/streamingTiming.js +53 -0
- package/dist/streamingTiming.js.map +1 -0
- package/dist/types.d.ts +113 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/uiMessages.d.ts +24 -0
- package/dist/uiMessages.d.ts.map +1 -0
- package/dist/uiMessages.js +68 -0
- package/dist/uiMessages.js.map +1 -0
- package/dist/useStreamRuntime.d.ts +15 -76
- package/dist/useStreamRuntime.d.ts.map +1 -1
- package/dist/useStreamRuntime.js +190 -111
- package/dist/useStreamRuntime.js.map +1 -1
- package/package.json +9 -9
- package/src/__tests__/langChainTestUtils.ts +22 -0
- package/src/convertMessages.test.ts +210 -0
- package/src/convertMessages.ts +111 -11
- package/src/findForkCheckpointInHistory.test.ts +263 -0
- package/src/findForkCheckpointInHistory.ts +68 -0
- package/src/groupUIMessagesByParent.test.ts +61 -0
- package/src/hooks.ts +156 -0
- package/src/index.ts +20 -3
- package/src/resolveForkCheckpoint.test.ts +202 -0
- package/src/resolveForkCheckpoint.ts +49 -0
- package/src/runConfigToSubmitOptions.test.ts +24 -0
- package/src/runtimeExtras.ts +5 -0
- package/src/streamingTiming.test.ts +118 -0
- package/src/streamingTiming.ts +85 -0
- package/src/types.ts +147 -3
- package/src/uiMessages.test.ts +190 -0
- package/src/uiMessages.ts +91 -0
- package/src/useLangChainError.test.tsx +48 -0
- package/src/useLangChainInterrupts.test.tsx +48 -0
- package/src/useLangChainRespond.test.tsx +49 -0
- package/src/useLangChainRespondAll.test.tsx +52 -0
- package/src/useLangChainState.test.tsx +14 -26
- package/src/useLangChainStream.test.tsx +42 -0
- package/src/useLangChainSubagents.test.tsx +48 -0
- package/src/useLangChainSubgraphs.test.tsx +48 -0
- package/src/useLangChainToolCalls.test.tsx +48 -0
- package/src/useStreamRuntime.test.tsx +232 -0
- package/src/useStreamRuntime.ts +311 -234
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { getMessageType } from "./convertMessages.js";
|
|
3
|
+
import { useStreamingTiming } from "@assistant-ui/core/react";
|
|
4
|
+
//#region src/streamingTiming.ts
|
|
5
|
+
const findAiMessage = (messages, messageId) => messages.find((m) => getMessageType(m) === "ai" && m.id === messageId);
|
|
6
|
+
const reasoningTextLength = (part) => {
|
|
7
|
+
if (part.summary && part.summary.length > 0) return part.summary.map((s) => s?.text ?? "").join("\n\n\n").length;
|
|
8
|
+
return part.reasoning?.length ?? 0;
|
|
9
|
+
};
|
|
10
|
+
const getTextLength = (messages, messageId) => {
|
|
11
|
+
const m = findAiMessage(messages, messageId);
|
|
12
|
+
if (!m) return 0;
|
|
13
|
+
const content = m.content;
|
|
14
|
+
if (typeof content === "string") return content.length;
|
|
15
|
+
if (!Array.isArray(content)) return 0;
|
|
16
|
+
let len = 0;
|
|
17
|
+
for (const part of content) switch (part.type) {
|
|
18
|
+
case "text":
|
|
19
|
+
case "text_delta":
|
|
20
|
+
if (typeof part.text === "string") len += part.text.length;
|
|
21
|
+
break;
|
|
22
|
+
case "thinking":
|
|
23
|
+
if (typeof part.thinking === "string") len += part.thinking.length;
|
|
24
|
+
break;
|
|
25
|
+
case "reasoning":
|
|
26
|
+
len += reasoningTextLength(part);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
return len;
|
|
30
|
+
};
|
|
31
|
+
const getToolCallCount = (messages, messageId) => findAiMessage(messages, messageId)?.tool_calls?.length ?? 0;
|
|
32
|
+
const getAssistantMessageId = (messages) => {
|
|
33
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
34
|
+
const m = messages[i];
|
|
35
|
+
if (m && getMessageType(m) === "ai" && m.id) return m.id;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const langChainStreamingTimingAccessors = {
|
|
39
|
+
getAssistantMessageId,
|
|
40
|
+
getTextLength,
|
|
41
|
+
getToolCallCount
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Tracks per-message streaming timing for LangChain messages. Delegates to
|
|
45
|
+
* the shared `useStreamingTiming` primitive in `@assistant-ui/core/react`,
|
|
46
|
+
* adapted to the `LangChainBaseMessage` shape (`_getType() -> "ai"`, content
|
|
47
|
+
* blocks including text/thinking/reasoning, `tool_calls`).
|
|
48
|
+
*/
|
|
49
|
+
const useLangChainStreamingTiming = (messages, isRunning) => useStreamingTiming(messages, isRunning, langChainStreamingTimingAccessors);
|
|
50
|
+
//#endregion
|
|
51
|
+
export { langChainStreamingTimingAccessors, useLangChainStreamingTiming };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=streamingTiming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamingTiming.js","names":[],"sources":["../src/streamingTiming.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageTiming } from \"@assistant-ui/core\";\nimport {\n useStreamingTiming,\n type StreamingTimingAccessors,\n} from \"@assistant-ui/core/react\";\nimport type { LangChainBaseMessage, LangChainContentBlock } from \"./types\";\nimport { getMessageType } from \"./convertMessages\";\n\nconst findAiMessage = (\n messages: readonly LangChainBaseMessage[],\n messageId: string,\n): LangChainBaseMessage | undefined =>\n messages.find((m) => getMessageType(m) === \"ai\" && m.id === messageId);\n\nconst reasoningTextLength = (part: {\n readonly summary?: ReadonlyArray<{ readonly text?: string }>;\n readonly reasoning?: string;\n}): number => {\n if (part.summary && part.summary.length > 0)\n return part.summary.map((s) => s?.text ?? \"\").join(\"\\n\\n\\n\").length;\n return part.reasoning?.length ?? 0;\n};\n\nconst getTextLength = (\n messages: readonly LangChainBaseMessage[],\n messageId: string,\n): number => {\n const m = findAiMessage(messages, messageId);\n if (!m) return 0;\n const content = m.content;\n if (typeof content === \"string\") return content.length;\n if (!Array.isArray(content)) return 0;\n let len = 0;\n for (const part of content as readonly LangChainContentBlock[]) {\n switch (part.type) {\n case \"text\":\n case \"text_delta\":\n if (typeof part.text === \"string\") len += part.text.length;\n break;\n case \"thinking\":\n if (typeof part.thinking === \"string\") len += part.thinking.length;\n break;\n case \"reasoning\":\n len += reasoningTextLength(part);\n break;\n }\n }\n return len;\n};\n\nconst getToolCallCount = (\n messages: readonly LangChainBaseMessage[],\n messageId: string,\n): number => findAiMessage(messages, messageId)?.tool_calls?.length ?? 0;\n\nconst getAssistantMessageId = (\n messages: readonly LangChainBaseMessage[],\n): string | undefined => {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m && getMessageType(m) === \"ai\" && m.id) return m.id;\n }\n return undefined;\n};\n\nexport const langChainStreamingTimingAccessors: StreamingTimingAccessors<LangChainBaseMessage> =\n {\n getAssistantMessageId,\n getTextLength,\n getToolCallCount,\n };\n\n/**\n * Tracks per-message streaming timing for LangChain messages. Delegates to\n * the shared `useStreamingTiming` primitive in `@assistant-ui/core/react`,\n * adapted to the `LangChainBaseMessage` shape (`_getType() -> \"ai\"`, content\n * blocks including text/thinking/reasoning, `tool_calls`).\n */\nexport const useLangChainStreamingTiming = (\n messages: readonly LangChainBaseMessage[],\n isRunning: boolean,\n): Record<string, MessageTiming> =>\n useStreamingTiming(messages, isRunning, langChainStreamingTimingAccessors);\n"],"mappings":";;;;AAUA,MAAM,iBACJ,UACA,cAEA,SAAS,MAAM,MAAM,eAAe,CAAC,MAAM,QAAQ,EAAE,OAAO,SAAS;AAEvE,MAAM,uBAAuB,SAGf;CACZ,IAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,GACxC,OAAO,KAAK,QAAQ,KAAK,MAAM,GAAG,QAAQ,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;CAC/D,OAAO,KAAK,WAAW,UAAU;AACnC;AAEA,MAAM,iBACJ,UACA,cACW;CACX,MAAM,IAAI,cAAc,UAAU,SAAS;CAC3C,IAAI,CAAC,GAAG,OAAO;CACf,MAAM,UAAU,EAAE;CAClB,IAAI,OAAO,YAAY,UAAU,OAAO,QAAQ;CAChD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO;CACpC,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,SACjB,QAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;GACH,IAAI,OAAO,KAAK,SAAS,UAAU,OAAO,KAAK,KAAK;GACpD;EACF,KAAK;GACH,IAAI,OAAO,KAAK,aAAa,UAAU,OAAO,KAAK,SAAS;GAC5D;EACF,KAAK;GACH,OAAO,oBAAoB,IAAI;GAC/B;CACJ;CAEF,OAAO;AACT;AAEA,MAAM,oBACJ,UACA,cACW,cAAc,UAAU,SAAS,CAAC,EAAE,YAAY,UAAU;AAEvE,MAAM,yBACJ,aACuB;CACvB,KAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAC7C,MAAM,IAAI,SAAS;EACnB,IAAI,KAAK,eAAe,CAAC,MAAM,QAAQ,EAAE,IAAI,OAAO,EAAE;CACxD;AAEF;AAEA,MAAa,oCACX;CACE;CACA;CACA;AACF;;;;;;;AAQF,MAAa,+BACX,UACA,cAEA,mBAAmB,UAAU,WAAW,iCAAiC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { MessageStatus } from "@assistant-ui/core";
|
|
1
|
+
import { AttachmentAdapter, DictationAdapter, ExternalStoreSharedOptions, FeedbackAdapter, MessageStatus, RealtimeVoiceAdapter, RemoteThreadListAdapter, SpeechSynthesisAdapter } from "@assistant-ui/core";
|
|
2
|
+
import { AnyStream, AssembledToolCall, SubagentDiscoverySnapshot, SubgraphDiscoverySnapshot, UseStreamOptions } from "@langchain/react";
|
|
3
|
+
import { AssistantCloud } from "assistant-cloud";
|
|
2
4
|
|
|
3
5
|
//#region src/types.d.ts
|
|
4
6
|
/** Known content block types from @langchain/core messages. */
|
|
@@ -11,17 +13,26 @@ type LangChainContentBlock = {
|
|
|
11
13
|
} | {
|
|
12
14
|
type: "image_url";
|
|
13
15
|
image_url: string | {
|
|
14
|
-
url
|
|
16
|
+
url?: string;
|
|
15
17
|
};
|
|
16
18
|
} | {
|
|
17
19
|
type: "thinking";
|
|
18
20
|
thinking: string;
|
|
19
21
|
} | {
|
|
20
22
|
type: "reasoning";
|
|
21
|
-
summary
|
|
23
|
+
summary?: Array<{
|
|
22
24
|
type: "summary_text";
|
|
23
|
-
text
|
|
25
|
+
text?: string;
|
|
24
26
|
}>;
|
|
27
|
+
reasoning?: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "file";
|
|
30
|
+
data: string;
|
|
31
|
+
mime_type: string;
|
|
32
|
+
source_type?: "base64";
|
|
33
|
+
metadata?: {
|
|
34
|
+
filename?: string;
|
|
35
|
+
};
|
|
25
36
|
} | {
|
|
26
37
|
type: "tool_use" | "input_json_delta";
|
|
27
38
|
};
|
|
@@ -30,6 +41,36 @@ type LangChainToolCall = {
|
|
|
30
41
|
name: string;
|
|
31
42
|
args: Record<string, unknown>;
|
|
32
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* A generative UI component the graph accumulates in its state. Read from
|
|
46
|
+
* `stream.values[uiStateKey]` and rendered via `makeAssistantDataUI`.
|
|
47
|
+
*
|
|
48
|
+
* The parent assistant message is carried in `metadata.message_id` by the
|
|
49
|
+
* Python SDK and in `metadata.id` by the JS SDK; the runtime reads either.
|
|
50
|
+
*/
|
|
51
|
+
type UIMessage<TName extends string = string, TProps extends Record<string, unknown> = Record<string, unknown>> = {
|
|
52
|
+
type: "ui";
|
|
53
|
+
id: string;
|
|
54
|
+
name: TName;
|
|
55
|
+
props: TProps;
|
|
56
|
+
metadata?: {
|
|
57
|
+
merge?: boolean;
|
|
58
|
+
run_id?: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
tags?: string[];
|
|
61
|
+
message_id?: string;
|
|
62
|
+
id?: string;
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Emitted on the live `custom` channel by the graph's `remove_ui_message`
|
|
68
|
+
* helper to drop a pushed `UIMessage` by id.
|
|
69
|
+
*/
|
|
70
|
+
type RemoveUIMessage = {
|
|
71
|
+
type: "remove-ui";
|
|
72
|
+
id: string;
|
|
73
|
+
};
|
|
33
74
|
/**
|
|
34
75
|
* Minimal duck-typed interface for BaseMessage class instances returned by
|
|
35
76
|
* `useStream`. Used internally by the message converter.
|
|
@@ -48,6 +89,73 @@ type LangChainBaseMessage = {
|
|
|
48
89
|
status?: MessageStatus | "success" | "error" | undefined; /** Present on ToolMessage */
|
|
49
90
|
artifact?: unknown;
|
|
50
91
|
};
|
|
92
|
+
type LangChainRuntimeExtras = {
|
|
93
|
+
interrupt: {
|
|
94
|
+
value?: unknown;
|
|
95
|
+
} | undefined;
|
|
96
|
+
interrupts: readonly {
|
|
97
|
+
id?: string;
|
|
98
|
+
value?: unknown;
|
|
99
|
+
}[];
|
|
100
|
+
toolCalls: readonly AssembledToolCall[];
|
|
101
|
+
subagents: ReadonlyMap<string, SubagentDiscoverySnapshot>;
|
|
102
|
+
subgraphs: ReadonlyMap<string, SubgraphDiscoverySnapshot>;
|
|
103
|
+
stream: AnyStream;
|
|
104
|
+
error: unknown;
|
|
105
|
+
submit: (values: Record<string, unknown> | null | undefined, options?: Record<string, unknown>) => Promise<void>;
|
|
106
|
+
respond: (response: unknown, options?: Record<string, unknown>) => Promise<void>;
|
|
107
|
+
respondAll: (responsesById: Record<string, unknown>, options?: Record<string, unknown>) => Promise<void>;
|
|
108
|
+
values: Record<string, unknown>;
|
|
109
|
+
messagesKey: string;
|
|
110
|
+
};
|
|
111
|
+
type LangChainRuntimeExtraOptions = ExternalStoreSharedOptions & {
|
|
112
|
+
/**
|
|
113
|
+
* Called whenever the active thread's canonical (remote) ID changes, so the
|
|
114
|
+
* value can be treated as a managed/controlled variable (e.g. synced to a URL
|
|
115
|
+
* query param). Only the settled remote ID is emitted: while a freshly created
|
|
116
|
+
* thread is still optimistic the value is `undefined`, and the real ID is
|
|
117
|
+
* emitted once the thread is initialized; the transient local ID is never
|
|
118
|
+
* surfaced.
|
|
119
|
+
*/
|
|
120
|
+
onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
|
|
121
|
+
cloud?: AssistantCloud | undefined;
|
|
122
|
+
adapters?: {
|
|
123
|
+
attachments?: AttachmentAdapter | undefined;
|
|
124
|
+
speech?: SpeechSynthesisAdapter | undefined;
|
|
125
|
+
dictation?: DictationAdapter | undefined;
|
|
126
|
+
voice?: RealtimeVoiceAdapter | undefined;
|
|
127
|
+
feedback?: FeedbackAdapter | undefined;
|
|
128
|
+
} | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* When the user sends a new message while previous tool calls are
|
|
131
|
+
* still pending, automatically submit `tool` messages that cancel
|
|
132
|
+
* them so the agent's tool-call accounting stays consistent.
|
|
133
|
+
* Defaults to `true`.
|
|
134
|
+
*/
|
|
135
|
+
autoCancelPendingToolCalls?: boolean | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Routes the Cancel button's click to `useStream().stop()`. On by
|
|
138
|
+
* default. Pass `false` to disable the Cancel button.
|
|
139
|
+
*/
|
|
140
|
+
unstable_allowCancellation?: boolean | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* Custom `RemoteThreadListAdapter`. When provided, replaces the
|
|
143
|
+
* cloud-backed thread list adapter.
|
|
144
|
+
*/
|
|
145
|
+
unstable_threadListAdapter?: RemoteThreadListAdapter | undefined; /** Custom thread-creation hook, forwarded to the cloud adapter. */
|
|
146
|
+
create?: (() => Promise<{
|
|
147
|
+
externalId: string | undefined;
|
|
148
|
+
}>) | undefined; /** Custom thread-deletion hook, forwarded to the cloud adapter. */
|
|
149
|
+
delete?: ((threadId: string) => Promise<void>) | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* State key the graph accumulates generative `UIMessage`s under. Each UI
|
|
152
|
+
* is attached to the assistant message identified by its `metadata.message_id`
|
|
153
|
+
* (Python SDK) or `metadata.id` (JS SDK) and emitted as a `data` part.
|
|
154
|
+
* Defaults to `"ui"`.
|
|
155
|
+
*/
|
|
156
|
+
uiStateKey?: string | undefined;
|
|
157
|
+
};
|
|
158
|
+
type UseStreamRuntimeOptions = UseStreamOptions extends infer O ? O extends UseStreamOptions ? O & LangChainRuntimeExtraOptions : never : never;
|
|
51
159
|
//#endregion
|
|
52
|
-
export { LangChainBaseMessage, LangChainContentBlock, LangChainToolCall };
|
|
160
|
+
export { LangChainBaseMessage, LangChainContentBlock, LangChainRuntimeExtraOptions, LangChainRuntimeExtras, LangChainToolCall, RemoveUIMessage, UIMessage, UseStreamRuntimeOptions };
|
|
53
161
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;KAoBY,qBAAA;EACN,IAAA;EAAc,IAAA;AAAA;EACd,IAAA;EAAoB,IAAA;AAAA;EACpB,IAAA;EAAmB,SAAA;IAAsB,GAAA;EAAA;AAAA;EACzC,IAAA;EAAkB,QAAA;AAAA;EAElB,IAAA;EACA,OAAA,GAAU,KAAK;IAAG,IAAA;IAAsB,IAAA;EAAA;EACxC,SAAA;AAAA;EAGA,IAAA;EACA,IAAA;EACA,SAAA;EACA,WAAA;EACA,QAAA;IAAa,QAAA;EAAA;AAAA;EAEb,IAAA;AAAA;AAAA,KAEM,iBAAA;EACV,EAAA;EACA,IAAA;EACA,IAAA,EAAM,MAAM;AAAA;;AAAA;AAUd;;;;;KAAY,SAAA,+CAEK,MAAA,oBAA0B,MAAA;EAEzC,IAAA;EACA,EAAA;EACA,IAAA,EAAM,KAAA;EACN,KAAA,EAAO,MAAA;EACP,QAAA;IACE,KAAA;IACA,MAAA;IACA,IAAA;IACA,IAAA;IACA,UAAA;IACA,EAAA;IAAA,CACC,GAAA;EAAA;AAAA;;;;;KAQO,eAAA;EACV,IAAA;EACA,EAAE;AAAA;AAVY;AAQhB;;;;AAEI;AAUJ;AApBgB,KAoBJ,oBAAA;EACV,QAAA;EACA,OAAA;EACA,EAAA;EACA,IAAA;EACA,iBAAA,GAAoB,MAAA,+BAME;EAJtB,UAAA,YAAsB,iBAAA,gBALtB;EAOA,YAAA,uBALA;EAOA,MAAA,GAAS,aAAA,oCANW;EAQpB,QAAA;AAAA;AAAA,KAGU,sBAAA;EACV,SAAA;IAAa,KAAA;EAAA;EACb,UAAA;IAAuB,EAAA;IAAa,KAAA;EAAA;EACpC,SAAA,WAAoB,iBAAA;EACpB,SAAA,EAAW,WAAA,SAAoB,yBAAA;EAC/B,SAAA,EAAW,WAAA,SAAoB,yBAAA;EAC/B,MAAA,EAAQ,SAAA;EACR,KAAA;EACA,MAAA,GACE,MAAA,EAAQ,MAAA,sCACR,OAAA,GAAU,MAAA,sBACP,OAAA;EACL,OAAA,GACE,QAAA,WACA,OAAA,GAAU,MAAA,sBACP,OAAA;EACL,UAAA,GACE,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,MAAA,sBACP,OAAA;EACL,MAAA,EAAQ,MAAA;EACR,WAAA;AAAA;AAAA,KAGU,4BAAA,GAA+B,0BAAA;EAPxB;;;;;;;;EAgBjB,gBAAA,KAAqB,QAAA;EACrB,KAAA,GAAQ,cAAA;EACR,QAAA;IAEM,WAAA,GAAc,iBAAA;IACd,MAAA,GAAS,sBAAA;IACT,SAAA,GAAY,gBAAA;IACZ,KAAA,GAAQ,oBAAA;IACR,QAAA,GAAW,eAAA;EAAA;EApCN;;;;;;EA6CX,0BAAA;EAzCE;;;;EA8CF,0BAAA;EA1CE;;;;EA+CF,0BAAA,GAA6B,uBAAA,cA3CZ;EA6CjB,MAAA,UAAgB,OAAA;IAAU,UAAA;EAAA,iBA3CrB;EA6CL,MAAA,KAAW,QAAA,aAAqB,OAAA;EA5CxB;;;AACG;AAGb;;EA+CE,UAAA;AAAA;AAAA,KAOU,uBAAA,GAA0B,gBAAA,mBAClC,CAAA,SAAU,gBAAA,GACR,CAAA,GAAI,4BAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RemoveUIMessage, UIMessage } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/uiMessages.d.ts
|
|
4
|
+
type UIUpdate = UIMessage | RemoveUIMessage;
|
|
5
|
+
declare const isUIUpdate: (value: unknown) => value is UIUpdate | readonly UIUpdate[];
|
|
6
|
+
declare const applyUIUpdate: (list: readonly UIMessage[], update: UIUpdate | readonly UIUpdate[]) => UIMessage[];
|
|
7
|
+
/**
|
|
8
|
+
* Pulls a UI update out of a raw `custom`-channel event. The graph writes the
|
|
9
|
+
* `UIMessage` straight to the channel, so it lands at `params.data`; some
|
|
10
|
+
* transports wrap it one level deeper at `params.data.payload`.
|
|
11
|
+
*/
|
|
12
|
+
declare const extractUIUpdate: (event: unknown) => UIUpdate | readonly UIUpdate[] | undefined;
|
|
13
|
+
declare const foldUIUpdates: (events: readonly unknown[]) => UIMessage[];
|
|
14
|
+
/**
|
|
15
|
+
* Merges live-streamed UI with the state snapshot. The snapshot is
|
|
16
|
+
* authoritative by id: once a UI lands in graph state it supersedes its live
|
|
17
|
+
* copy. A consequence is that a live `remove-ui` is overridden while the
|
|
18
|
+
* snapshot still contains that id; the removal only takes visible effect once
|
|
19
|
+
* the snapshot catches up.
|
|
20
|
+
*/
|
|
21
|
+
declare const mergeUIMessages: (live: readonly UIMessage[], snapshot: unknown) => UIMessage[];
|
|
22
|
+
//#endregion
|
|
23
|
+
export { UIUpdate, applyUIUpdate, extractUIUpdate, foldUIUpdates, isUIUpdate, mergeUIMessages };
|
|
24
|
+
//# sourceMappingURL=uiMessages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiMessages.d.ts","names":[],"sources":["../src/uiMessages.ts"],"mappings":";;;KAEY,QAAA,GAAW,SAAA,GAAY,eAAe;AAAA,cAErC,UAAA,GACX,KAAA,cACC,KAAA,IAAS,QAAA,YAAoB,QAAQ;AAAA,cAmB3B,aAAA,GACX,IAAA,WAAe,SAAA,IACf,MAAA,EAAQ,QAAA,YAAoB,QAAA,OAC3B,SAAA;;;;AA1B+C;AAElD;cAkDa,eAAA,GACX,KAAA,cACC,QAAA,YAAoB,QAAQ;AAAA,cAQlB,aAAA,GAAiB,MAAA,yBAA6B,SAAS;;;;;;;AA1D5B;cA0E3B,eAAA,GACX,IAAA,WAAe,SAAA,IACf,QAAA,cACC,SAAS"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//#region src/uiMessages.ts
|
|
2
|
+
const isUIUpdate = (value) => {
|
|
3
|
+
if (Array.isArray(value)) return value.length > 0 && value.every(isUIUpdate);
|
|
4
|
+
if (value == null || typeof value !== "object") return false;
|
|
5
|
+
const v = value;
|
|
6
|
+
if (typeof v.id !== "string") return false;
|
|
7
|
+
if (v.type === "remove-ui") return true;
|
|
8
|
+
return v.type === "ui" && typeof v.name === "string" && typeof v.props === "object" && v.props !== null;
|
|
9
|
+
};
|
|
10
|
+
const applyUIUpdate = (list, update) => {
|
|
11
|
+
const events = Array.isArray(update) ? update : [update];
|
|
12
|
+
let next = list.slice();
|
|
13
|
+
for (const event of events) {
|
|
14
|
+
if (event.type === "remove-ui") {
|
|
15
|
+
next = next.filter((ui) => ui.id !== event.id);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const index = next.findIndex((ui) => ui.id === event.id);
|
|
19
|
+
if (index === -1) {
|
|
20
|
+
next.push(event);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
next[index] = event.metadata?.merge === true ? {
|
|
24
|
+
...event,
|
|
25
|
+
props: {
|
|
26
|
+
...next[index].props,
|
|
27
|
+
...event.props
|
|
28
|
+
}
|
|
29
|
+
} : event;
|
|
30
|
+
}
|
|
31
|
+
return next;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Pulls a UI update out of a raw `custom`-channel event. The graph writes the
|
|
35
|
+
* `UIMessage` straight to the channel, so it lands at `params.data`; some
|
|
36
|
+
* transports wrap it one level deeper at `params.data.payload`.
|
|
37
|
+
*/
|
|
38
|
+
const extractUIUpdate = (event) => {
|
|
39
|
+
const data = event?.params?.data;
|
|
40
|
+
if (isUIUpdate(data)) return data;
|
|
41
|
+
const payload = data?.payload;
|
|
42
|
+
if (isUIUpdate(payload)) return payload;
|
|
43
|
+
};
|
|
44
|
+
const foldUIUpdates = (events) => {
|
|
45
|
+
let acc = [];
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
const update = extractUIUpdate(event);
|
|
48
|
+
if (update) acc = applyUIUpdate(acc, update);
|
|
49
|
+
}
|
|
50
|
+
return acc;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Merges live-streamed UI with the state snapshot. The snapshot is
|
|
54
|
+
* authoritative by id: once a UI lands in graph state it supersedes its live
|
|
55
|
+
* copy. A consequence is that a live `remove-ui` is overridden while the
|
|
56
|
+
* snapshot still contains that id; the removal only takes visible effect once
|
|
57
|
+
* the snapshot catches up.
|
|
58
|
+
*/
|
|
59
|
+
const mergeUIMessages = (live, snapshot) => {
|
|
60
|
+
const byId = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const ui of live) byId.set(ui.id, ui);
|
|
62
|
+
if (Array.isArray(snapshot)) for (const ui of snapshot) byId.set(ui.id, ui);
|
|
63
|
+
return [...byId.values()];
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { applyUIUpdate, extractUIUpdate, foldUIUpdates, isUIUpdate, mergeUIMessages };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=uiMessages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiMessages.js","names":[],"sources":["../src/uiMessages.ts"],"sourcesContent":["import type { RemoveUIMessage, UIMessage } from \"./types\";\n\nexport type UIUpdate = UIMessage | RemoveUIMessage;\n\nexport const isUIUpdate = (\n value: unknown,\n): value is UIUpdate | readonly UIUpdate[] => {\n if (Array.isArray(value)) return value.length > 0 && value.every(isUIUpdate);\n if (value == null || typeof value !== \"object\") return false;\n const v = value as {\n type?: unknown;\n id?: unknown;\n name?: unknown;\n props?: unknown;\n };\n if (typeof v.id !== \"string\") return false;\n if (v.type === \"remove-ui\") return true;\n return (\n v.type === \"ui\" &&\n typeof v.name === \"string\" &&\n typeof v.props === \"object\" &&\n v.props !== null\n );\n};\n\nexport const applyUIUpdate = (\n list: readonly UIMessage[],\n update: UIUpdate | readonly UIUpdate[],\n): UIMessage[] => {\n const events = Array.isArray(update) ? update : [update as UIUpdate];\n let next = list.slice();\n for (const event of events) {\n if (event.type === \"remove-ui\") {\n next = next.filter((ui) => ui.id !== event.id);\n continue;\n }\n const index = next.findIndex((ui) => ui.id === event.id);\n if (index === -1) {\n next.push(event);\n continue;\n }\n next[index] =\n event.metadata?.merge === true\n ? { ...event, props: { ...next[index]!.props, ...event.props } }\n : event;\n }\n return next;\n};\n\n/**\n * Pulls a UI update out of a raw `custom`-channel event. The graph writes the\n * `UIMessage` straight to the channel, so it lands at `params.data`; some\n * transports wrap it one level deeper at `params.data.payload`.\n */\nexport const extractUIUpdate = (\n event: unknown,\n): UIUpdate | readonly UIUpdate[] | undefined => {\n const data = (event as { params?: { data?: unknown } } | null)?.params?.data;\n if (isUIUpdate(data)) return data;\n const payload = (data as { payload?: unknown } | undefined)?.payload;\n if (isUIUpdate(payload)) return payload;\n return undefined;\n};\n\nexport const foldUIUpdates = (events: readonly unknown[]): UIMessage[] => {\n let acc: UIMessage[] = [];\n for (const event of events) {\n const update = extractUIUpdate(event);\n if (update) acc = applyUIUpdate(acc, update);\n }\n return acc;\n};\n\n/**\n * Merges live-streamed UI with the state snapshot. The snapshot is\n * authoritative by id: once a UI lands in graph state it supersedes its live\n * copy. A consequence is that a live `remove-ui` is overridden while the\n * snapshot still contains that id; the removal only takes visible effect once\n * the snapshot catches up.\n */\nexport const mergeUIMessages = (\n live: readonly UIMessage[],\n snapshot: unknown,\n): UIMessage[] => {\n const byId = new Map<string, UIMessage>();\n for (const ui of live) byId.set(ui.id, ui);\n if (Array.isArray(snapshot)) {\n for (const ui of snapshot as UIMessage[]) byId.set(ui.id, ui);\n }\n return [...byId.values()];\n};\n"],"mappings":";AAIA,MAAa,cACX,UAC4C;CAC5C,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,SAAS,KAAK,MAAM,MAAM,UAAU;CAC3E,IAAI,SAAS,QAAQ,OAAO,UAAU,UAAU,OAAO;CACvD,MAAM,IAAI;CAMV,IAAI,OAAO,EAAE,OAAO,UAAU,OAAO;CACrC,IAAI,EAAE,SAAS,aAAa,OAAO;CACnC,OACE,EAAE,SAAS,QACX,OAAO,EAAE,SAAS,YAClB,OAAO,EAAE,UAAU,YACnB,EAAE,UAAU;AAEhB;AAEA,MAAa,iBACX,MACA,WACgB;CAChB,MAAM,SAAS,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAkB;CACnE,IAAI,OAAO,KAAK,MAAM;CACtB,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,aAAa;GAC9B,OAAO,KAAK,QAAQ,OAAO,GAAG,OAAO,MAAM,EAAE;GAC7C;EACF;EACA,MAAM,QAAQ,KAAK,WAAW,OAAO,GAAG,OAAO,MAAM,EAAE;EACvD,IAAI,UAAU,IAAI;GAChB,KAAK,KAAK,KAAK;GACf;EACF;EACA,KAAK,SACH,MAAM,UAAU,UAAU,OACtB;GAAE,GAAG;GAAO,OAAO;IAAE,GAAG,KAAK,MAAM,CAAE;IAAO,GAAG,MAAM;GAAM;EAAE,IAC7D;CACR;CACA,OAAO;AACT;;;;;;AAOA,MAAa,mBACX,UAC+C;CAC/C,MAAM,OAAQ,OAAkD,QAAQ;CACxE,IAAI,WAAW,IAAI,GAAG,OAAO;CAC7B,MAAM,UAAW,MAA4C;CAC7D,IAAI,WAAW,OAAO,GAAG,OAAO;AAElC;AAEA,MAAa,iBAAiB,WAA4C;CACxE,IAAI,MAAmB,CAAC;CACxB,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,SAAS,gBAAgB,KAAK;EACpC,IAAI,QAAQ,MAAM,cAAc,KAAK,MAAM;CAC7C;CACA,OAAO;AACT;;;;;;;;AASA,MAAa,mBACX,MACA,aACgB;CAChB,MAAM,uBAAO,IAAI,IAAuB;CACxC,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE;CACzC,IAAI,MAAM,QAAQ,QAAQ,GACxB,KAAK,MAAM,MAAM,UAAyB,KAAK,IAAI,GAAG,IAAI,EAAE;CAE9D,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC;AAC1B"}
|
|
@@ -1,42 +1,20 @@
|
|
|
1
1
|
/// <reference types="@assistant-ui/core/store" />
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { UseStreamOptions } from "@langchain/react";
|
|
5
|
-
import { AssistantCloud } from "assistant-cloud";
|
|
2
|
+
import { UIMessage, UseStreamRuntimeOptions } from "./types.js";
|
|
3
|
+
import { AppendMessage } from "@assistant-ui/core";
|
|
6
4
|
|
|
7
5
|
//#region src/useStreamRuntime.d.ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* them so the agent's tool-call accounting stays consistent.
|
|
21
|
-
* Defaults to `true`.
|
|
22
|
-
*/
|
|
23
|
-
autoCancelPendingToolCalls?: boolean | undefined;
|
|
24
|
-
/**
|
|
25
|
-
* Routes the Cancel button's click to `useStream().stop()`. On by
|
|
26
|
-
* default. Pass `false` to disable the Cancel button.
|
|
27
|
-
*/
|
|
28
|
-
unstable_allowCancellation?: boolean | undefined;
|
|
29
|
-
/**
|
|
30
|
-
* Custom `RemoteThreadListAdapter`. When provided, replaces the
|
|
31
|
-
* cloud-backed thread list adapter.
|
|
32
|
-
*/
|
|
33
|
-
unstable_threadListAdapter?: RemoteThreadListAdapter | undefined; /** Custom thread-creation hook, forwarded to the cloud adapter. */
|
|
34
|
-
create?: (() => Promise<{
|
|
35
|
-
externalId: string | undefined;
|
|
36
|
-
}>) | undefined; /** Custom thread-deletion hook, forwarded to the cloud adapter. */
|
|
37
|
-
delete?: ((threadId: string) => Promise<void>) | undefined;
|
|
38
|
-
};
|
|
39
|
-
type UseStreamRuntimeOptions = UseStreamOptions extends infer O ? O extends UseStreamOptions ? O & LangChainRuntimeExtraOptions : never : never;
|
|
6
|
+
declare const runConfigToSubmitOptions: (runConfig: AppendMessage["runConfig"]) => {
|
|
7
|
+
config: {
|
|
8
|
+
configurable: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
} | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Group the graph's accumulated `UIMessage`s by the assistant message they
|
|
13
|
+
* belong to. Non-array state and entries without a parent link are dropped.
|
|
14
|
+
* The parent id comes from `metadata.message_id` (Python SDK) or
|
|
15
|
+
* `metadata.id` (JS SDK).
|
|
16
|
+
*/
|
|
17
|
+
declare const groupUIMessagesByParent: (value: unknown) => Map<string, UIMessage[]>;
|
|
40
18
|
/**
|
|
41
19
|
* Creates an assistant-ui runtime backed by LangChain's `useStream` hook.
|
|
42
20
|
* Accepts the same options as `useStream` from `@langchain/react`, plus
|
|
@@ -62,45 +40,6 @@ type UseStreamRuntimeOptions = UseStreamOptions extends infer O ? O extends UseS
|
|
|
62
40
|
* ```
|
|
63
41
|
*/
|
|
64
42
|
declare const useStreamRuntime: (rawOptions: UseStreamRuntimeOptions) => import("@assistant-ui/core").AssistantRuntime;
|
|
65
|
-
/**
|
|
66
|
-
* Read the current LangGraph interrupt state from the runtime extras.
|
|
67
|
-
*/
|
|
68
|
-
declare const useLangChainInterruptState: () => {
|
|
69
|
-
value?: unknown;
|
|
70
|
-
} | undefined;
|
|
71
|
-
/**
|
|
72
|
-
* Returns a function to submit raw state updates to the LangGraph agent,
|
|
73
|
-
* bypassing the normal message flow. Useful for sending interrupt resume
|
|
74
|
-
* commands.
|
|
75
|
-
*/
|
|
76
|
-
declare const useLangChainSubmit: () => (values: Record<string, unknown> | null | undefined, options?: Record<string, unknown>) => Promise<void>;
|
|
77
|
-
/**
|
|
78
|
-
* Submit a list of LangChain-shaped messages on the current thread.
|
|
79
|
-
* Parity helper for migrating from `useLangGraphSend`. Routes to
|
|
80
|
-
* `useStream().submit({ [messagesKey]: messages }, options)`.
|
|
81
|
-
*/
|
|
82
|
-
declare const useLangChainSend: () => (messages: readonly LangChainBaseMessage[], options?: Record<string, unknown>) => Promise<void>;
|
|
83
|
-
/**
|
|
84
|
-
* Submit a `useStream` command (e.g. interrupt resume). Parity helper
|
|
85
|
-
* for migrating from `useLangGraphSendCommand`. Note that v1's command
|
|
86
|
-
* shape (`{ resume?, goto?, update? }`) differs from the legacy
|
|
87
|
-
* `{ resume: string }` form — to carry a payload, use the input or
|
|
88
|
-
* `stream.respond` instead.
|
|
89
|
-
*/
|
|
90
|
-
declare const useLangChainSendCommand: () => (command: Record<string, unknown>) => Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Read a custom LangGraph state key from the current thread. Mirrors
|
|
93
|
-
* `useStream().values[key]` from `@langchain/react` and updates when the
|
|
94
|
-
* stream emits new state.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```tsx
|
|
98
|
-
* const todos = useLangChainState<Todo[]>("todos");
|
|
99
|
-
* const files = useLangChainState<Record<string, string>>("files", {});
|
|
100
|
-
* ```
|
|
101
|
-
*/
|
|
102
|
-
declare function useLangChainState<T>(key: string): T | undefined;
|
|
103
|
-
declare function useLangChainState<T>(key: string, defaultValue: T): T;
|
|
104
43
|
//#endregion
|
|
105
|
-
export {
|
|
44
|
+
export { groupUIMessagesByParent, runConfigToSubmitOptions, useStreamRuntime };
|
|
106
45
|
//# sourceMappingURL=useStreamRuntime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStreamRuntime.d.ts","names":[],"sources":["../src/useStreamRuntime.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"useStreamRuntime.d.ts","names":[],"sources":["../src/useStreamRuntime.ts"],"mappings":";;;;cAsCa,wBAAA,GACX,SAAA,EAAW,aAAA;;kBAAA,MAAA;EAAA;AAAA;;;;;;;cAYA,uBAAA,GACX,KAAA,cACC,GAAG,SAAS,SAAA;;AAFf;;;;;;;;AAEwB;AAyZxB;;;;;;;;AA4BC;;;;;;cA5BY,gBAAA,GAAoB,UAAA,EAAY,uBAAuB,kCAAA,gBAAA"}
|