@assistant-ui/react-langgraph 0.5.0 → 0.5.1

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.
@@ -0,0 +1,20 @@
1
+ export type LangGraphMessagesEvent<TMessage> = {
2
+ event: "messages" | "messages/partial" | "messages/complete" | "metadata" | "updates" | string;
3
+ data: TMessage[] | any;
4
+ };
5
+ export type LangGraphStateAccumulatorConfig<TMessage> = {
6
+ initialMessages?: TMessage[];
7
+ appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;
8
+ };
9
+ export declare class LangGraphMessageAccumulator<TMessage extends {
10
+ id?: string;
11
+ }> {
12
+ private messagesMap;
13
+ private appendMessage;
14
+ constructor({ initialMessages, appendMessage, }?: LangGraphStateAccumulatorConfig<TMessage>);
15
+ private ensureMessageId;
16
+ addMessages(newMessages: TMessage[]): TMessage[];
17
+ getMessages(): TMessage[];
18
+ clear(): void;
19
+ }
20
+ //# sourceMappingURL=LangGraphMessageAccumulator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LangGraphMessageAccumulator.d.ts","sourceRoot":"","sources":["../src/LangGraphMessageAccumulator.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,sBAAsB,CAAC,QAAQ,IAAI;IAC7C,KAAK,EACD,UAAU,GACV,kBAAkB,GAClB,mBAAmB,GACnB,UAAU,GACV,SAAS,GACT,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,+BAA+B,CAAC,QAAQ,IAAI;IACtD,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;CAC1E,CAAC;AAEF,qBAAa,2BAA2B,CAAC,QAAQ,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE;IACvE,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,aAAa,CAGP;gBAEF,EACV,eAAoB,EACpB,aAGa,GACd,GAAE,+BAA+B,CAAC,QAAQ,CAAM;IAKjD,OAAO,CAAC,eAAe;IAIhB,WAAW,CAAC,WAAW,EAAE,QAAQ,EAAE;IAenC,WAAW,IAAI,QAAQ,EAAE;IAIzB,KAAK;CAGb"}
@@ -0,0 +1,3 @@
1
+ import { LangChainMessage, LangChainMessageChunk } from "./types";
2
+ export declare const appendLangChainChunk: (prev: LangChainMessage | undefined, curr: LangChainMessage | LangChainMessageChunk) => LangChainMessage;
3
+ //# sourceMappingURL=appendLangChainChunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appendLangChainChunk.d.ts","sourceRoot":"","sources":["../src/appendLangChainChunk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGlE,eAAO,MAAM,oBAAoB,GAC/B,MAAM,gBAAgB,GAAG,SAAS,EAClC,MAAM,gBAAgB,GAAG,qBAAqB,KAC7C,gBAgDF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { LangChainMessage } from "./types";
2
+ export declare const convertLangChainMessages: useExternalMessageConverter.Callback<LangChainMessage>;
3
+ //# sourceMappingURL=convertLangChainMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convertLangChainMessages.d.ts","sourceRoot":"","sources":["../src/convertLangChainMessages.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAiC3C,eAAO,MAAM,wBAAwB,EAAE,2BAA2B,CAAC,QAAQ,CACzE,gBAAgB,CA2CjB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export { useLangGraphRuntime, useLangGraphSend, useLangGraphSendCommand, useLangGraphInterruptState, } from "./useLangGraphRuntime";
2
+ export { useLangGraphMessages, type LangGraphInterruptState, type LangGraphCommand, type LangGraphSendMessageConfig, type LangGraphStreamCallback, type LangGraphMessagesEvent, } from "./useLangGraphMessages";
3
+ export { convertLangChainMessages } from "./convertLangChainMessages";
4
+ /**
5
+ * @deprecated Use `convertLangChainMessages` instead.
6
+ */
7
+ export { convertLangChainMessages as convertLangchainMessages } from "./convertLangChainMessages";
8
+ export type { LangChainMessage, LangChainEvent, LangChainToolCall, LangChainToolCallChunk, } from "./types";
9
+ export { LangGraphMessageAccumulator } from "./LangGraphMessageAccumulator";
10
+ export { appendLangChainChunk } from "./appendLangChainChunk";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,oBAAoB,EACpB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;GAEG;AACH,OAAO,EAAE,wBAAwB,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAElG,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n useLangGraphRuntime,\n useLangGraphSend,\n useLangGraphSendCommand,\n useLangGraphInterruptState,\n} from \"./useLangGraphRuntime\";\n\nexport {\n useLangGraphMessages,\n type LangGraphInterruptState,\n type LangGraphCommand,\n type LangGraphSendMessageConfig,\n type LangGraphStreamCallback,\n} from \"./useLangGraphMessages\";\nexport { convertLangChainMessages } from \"./convertLangChainMessages\";\n\n/**\n * @deprecated Use `convertLangChainMessages` instead.\n */\nexport { convertLangChainMessages as convertLangchainMessages } from \"./convertLangChainMessages\";\n\nexport type {\n LangChainMessage,\n LangChainEvent,\n LangChainToolCall,\n LangChainToolCallChunk,\n} from \"./types\";\n\nexport { LangGraphMessageAccumulator } from \"./LangGraphMessageAccumulator\";\nexport { appendLangChainChunk } from \"./appendLangChainChunk\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,OAKK;AACP,SAAS,gCAAgC;AAKzC,SAAqC,4BAA5BA,iCAA4D;AASrE,SAAS,mCAAmC;AAC5C,SAAS,4BAA4B;","names":["convertLangChainMessages"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n useLangGraphRuntime,\n useLangGraphSend,\n useLangGraphSendCommand,\n useLangGraphInterruptState,\n} from \"./useLangGraphRuntime\";\n\nexport {\n useLangGraphMessages,\n type LangGraphInterruptState,\n type LangGraphCommand,\n type LangGraphSendMessageConfig,\n type LangGraphStreamCallback,\n type LangGraphMessagesEvent,\n} from \"./useLangGraphMessages\";\nexport { convertLangChainMessages } from \"./convertLangChainMessages\";\n\n/**\n * @deprecated Use `convertLangChainMessages` instead.\n */\nexport { convertLangChainMessages as convertLangchainMessages } from \"./convertLangChainMessages\";\n\nexport type {\n LangChainMessage,\n LangChainEvent,\n LangChainToolCall,\n LangChainToolCallChunk,\n} from \"./types\";\n\nexport { LangGraphMessageAccumulator } from \"./LangGraphMessageAccumulator\";\nexport { appendLangChainChunk } from \"./appendLangChainChunk\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,OAMK;AACP,SAAS,gCAAgC;AAKzC,SAAqC,4BAA5BA,iCAA4D;AASrE,SAAS,mCAAmC;AAC5C,SAAS,4BAA4B;","names":["convertLangChainMessages"]}
@@ -0,0 +1,66 @@
1
+ import { ReadonlyJSONObject } from "assistant-stream/utils";
2
+ export type LangChainToolCallChunk = {
3
+ index: number;
4
+ id: string;
5
+ name: string;
6
+ args: string;
7
+ };
8
+ export type LangChainToolCall = {
9
+ id: string;
10
+ name: string;
11
+ argsText: string;
12
+ args: ReadonlyJSONObject;
13
+ };
14
+ type MessageContentText = {
15
+ type: "text";
16
+ text: string;
17
+ };
18
+ type MessageContentImageUrl = {
19
+ type: "image_url";
20
+ image_url: string | {
21
+ url: string;
22
+ };
23
+ };
24
+ type MessageContentToolUse = {
25
+ type: "tool_use";
26
+ };
27
+ type UserMessageContentComplex = MessageContentText | MessageContentImageUrl;
28
+ type AssistantMessageContentComplex = MessageContentText | MessageContentToolUse;
29
+ type UserMessageContent = string | UserMessageContentComplex[];
30
+ type AssistantMessageContent = string | AssistantMessageContentComplex[];
31
+ export type LangChainMessage = {
32
+ id?: string;
33
+ type: "system";
34
+ content: string;
35
+ } | {
36
+ id?: string;
37
+ type: "human";
38
+ content: UserMessageContent;
39
+ } | {
40
+ id?: string;
41
+ type: "tool";
42
+ content: string;
43
+ tool_call_id: string;
44
+ name: string;
45
+ artifact?: any;
46
+ } | {
47
+ id?: string;
48
+ type: "ai";
49
+ content: AssistantMessageContent;
50
+ tool_call_chunks?: LangChainToolCallChunk[];
51
+ tool_calls?: LangChainToolCall[];
52
+ };
53
+ export type LangChainMessageChunk = {
54
+ id: string;
55
+ type: "AIMessageChunk";
56
+ content: (AssistantMessageContentComplex & {
57
+ index: number;
58
+ })[];
59
+ tool_call_chunks: LangChainToolCallChunk[];
60
+ };
61
+ export type LangChainEvent = {
62
+ event: "messages/partial" | "messages/complete";
63
+ data: LangChainMessage[];
64
+ };
65
+ export {};
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,kBAAkB,CAAC;CAC1B,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,KAAK,yBAAyB,GAAG,kBAAkB,GAAG,sBAAsB,CAAC;AAC7E,KAAK,8BAA8B,GAC/B,kBAAkB,GAClB,qBAAqB,CAAC;AAE1B,KAAK,kBAAkB,GAAG,MAAM,GAAG,yBAAyB,EAAE,CAAC;AAC/D,KAAK,uBAAuB,GAAG,MAAM,GAAG,8BAA8B,EAAE,CAAC;AAEzE,MAAM,MAAM,gBAAgB,GACxB;IACE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,kBAAkB,CAAC;CAC7B,GACD;IACE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB,GACD;IACE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,uBAAuB,CAAC;IACjC,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAClC,CAAC;AAEN,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,CAAC,8BAA8B,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IAChE,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CAAC;IAChD,IAAI,EAAE,gBAAgB,EAAE,CAAC;CAC1B,CAAC"}
@@ -0,0 +1,34 @@
1
+ export type LangGraphCommand = {
2
+ resume: string;
3
+ };
4
+ export type LangGraphSendMessageConfig = {
5
+ command?: LangGraphCommand;
6
+ runConfig?: unknown;
7
+ };
8
+ export type LangGraphMessagesEvent<TMessage> = {
9
+ event: "messages" | "messages/partial" | "messages/complete" | "metadata" | "updates" | string;
10
+ data: TMessage[] | any;
11
+ };
12
+ export type LangGraphStreamCallback<TMessage> = (messages: TMessage[], config: LangGraphSendMessageConfig & {
13
+ abortSignal: AbortSignal;
14
+ }) => Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>> | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;
15
+ export type LangGraphInterruptState = {
16
+ value?: any;
17
+ resumable?: boolean;
18
+ when: string;
19
+ ns?: string[];
20
+ };
21
+ export declare const useLangGraphMessages: <TMessage extends {
22
+ id?: string;
23
+ }>({ stream, appendMessage, }: {
24
+ stream: LangGraphStreamCallback<TMessage>;
25
+ appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;
26
+ }) => {
27
+ interrupt: LangGraphInterruptState | undefined;
28
+ messages: TMessage[];
29
+ sendMessage: (newMessages: TMessage[], config: LangGraphSendMessageConfig) => Promise<void>;
30
+ cancel: () => void;
31
+ setInterrupt: import("react").Dispatch<import("react").SetStateAction<LangGraphInterruptState | undefined>>;
32
+ setMessages: import("react").Dispatch<import("react").SetStateAction<TMessage[]>>;
33
+ };
34
+ //# sourceMappingURL=useLangGraphMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLangGraphMessages.d.ts","sourceRoot":"","sources":["../src/useLangGraphMessages.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,QAAQ,IAAI;IAC7C,KAAK,EACD,UAAU,GACV,kBAAkB,GAClB,mBAAmB,GACnB,UAAU,GACV,SAAS,GACT,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,uBAAuB,CAAC,QAAQ,IAAI,CAC9C,QAAQ,EAAE,QAAQ,EAAE,EACpB,MAAM,EAAE,0BAA0B,GAAG;IAAE,WAAW,EAAE,WAAW,CAAA;CAAE,KAE/D,OAAO,CAAC,cAAc,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,GACzD,cAAc,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAErD,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;CACf,CAAC;AAOF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,4BAGpE;IACD,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;CAC1E;;;+BAQuB,QAAQ,EAAE,UAAU,0BAA0B;;;;CA6CrE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useLangGraphMessages.ts"],"sourcesContent":["import { useState, useCallback, useRef } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { LangGraphMessageAccumulator } from \"./LangGraphMessageAccumulator\";\n\nexport type LangGraphCommand = {\n resume: string;\n};\n\nexport type LangGraphSendMessageConfig = {\n command?: LangGraphCommand;\n runConfig?: unknown;\n};\n\ntype LangGraphMessagesEvent<TMessage> = {\n event:\n | \"messages\"\n | \"messages/partial\"\n | \"messages/complete\"\n | \"metadata\"\n | \"updates\"\n | string;\n data: TMessage[] | any;\n};\nexport type LangGraphStreamCallback<TMessage> = (\n messages: TMessage[],\n config: LangGraphSendMessageConfig & { abortSignal: AbortSignal },\n) =>\n | Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>>\n | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;\n\nexport type LangGraphInterruptState = {\n value?: any;\n resumable?: boolean;\n when: string;\n ns?: string[];\n};\n\nconst DEFAULT_APPEND_MESSAGE = <TMessage>(\n _: TMessage | undefined,\n curr: TMessage,\n) => curr;\n\nexport const useLangGraphMessages = <TMessage extends { id?: string }>({\n stream,\n appendMessage = DEFAULT_APPEND_MESSAGE,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\n appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;\n}) => {\n const [interrupt, setInterrupt] = useState<\n LangGraphInterruptState | undefined\n >();\n const [messages, setMessages] = useState<TMessage[]>([]);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[], config: LangGraphSendMessageConfig) => {\n // ensure all messages have an ID\n newMessages = newMessages.map((m) => (m.id ? m : { ...m, id: uuidv4() }));\n\n const accumulator = new LangGraphMessageAccumulator({\n initialMessages: messages,\n appendMessage,\n });\n setMessages(accumulator.addMessages(newMessages));\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n const response = await stream(newMessages, {\n ...config,\n abortSignal: abortController.signal,\n });\n\n for await (const chunk of response) {\n if (\n chunk.event === \"messages/partial\" ||\n chunk.event === \"messages/complete\"\n ) {\n setMessages(accumulator.addMessages(chunk.data));\n } else if (chunk.event === \"updates\") {\n setInterrupt(chunk.data.__interrupt__?.[0]);\n }\n }\n },\n [messages, stream, appendMessage],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return {\n interrupt,\n messages,\n sendMessage,\n cancel,\n setInterrupt,\n setMessages,\n };\n};\n"],"mappings":"AAAA,SAAS,UAAU,aAAa,cAAc;AAC9C,SAAS,MAAM,cAAc;AAC7B,SAAS,mCAAmC;AAmC5C,MAAM,yBAAyB,CAC7B,GACA,SACG;AAEE,MAAM,uBAAuB,CAAmC;AAAA,EACrE;AAAA,EACA,gBAAgB;AAClB,MAGM;AACJ,QAAM,CAAC,WAAW,YAAY,IAAI,SAEhC;AACF,QAAM,CAAC,UAAU,WAAW,IAAI,SAAqB,CAAC,CAAC;AACvD,QAAM,qBAAqB,OAA+B,IAAI;AAE9D,QAAM,cAAc;AAAA,IAClB,OAAO,aAAyB,WAAuC;AAErE,oBAAc,YAAY,IAAI,CAAC,MAAO,EAAE,KAAK,IAAI,EAAE,GAAG,GAAG,IAAI,OAAO,EAAE,CAAE;AAExE,YAAM,cAAc,IAAI,4BAA4B;AAAA,QAClD,iBAAiB;AAAA,QACjB;AAAA,MACF,CAAC;AACD,kBAAY,YAAY,YAAY,WAAW,CAAC;AAEhD,YAAM,kBAAkB,IAAI,gBAAgB;AAC5C,yBAAmB,UAAU;AAC7B,YAAM,WAAW,MAAM,OAAO,aAAa;AAAA,QACzC,GAAG;AAAA,QACH,aAAa,gBAAgB;AAAA,MAC/B,CAAC;AAED,uBAAiB,SAAS,UAAU;AAClC,YACE,MAAM,UAAU,sBAChB,MAAM,UAAU,qBAChB;AACA,sBAAY,YAAY,YAAY,MAAM,IAAI,CAAC;AAAA,QACjD,WAAW,MAAM,UAAU,WAAW;AACpC,uBAAa,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,UAAU,QAAQ,aAAa;AAAA,EAClC;AAEA,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,mBAAmB,SAAS;AAC9B,yBAAmB,QAAQ,MAAM;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,kBAAkB,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/useLangGraphMessages.ts"],"sourcesContent":["import { useState, useCallback, useRef } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { LangGraphMessageAccumulator } from \"./LangGraphMessageAccumulator\";\n\nexport type LangGraphCommand = {\n resume: string;\n};\n\nexport type LangGraphSendMessageConfig = {\n command?: LangGraphCommand;\n runConfig?: unknown;\n};\n\nexport type LangGraphMessagesEvent<TMessage> = {\n event:\n | \"messages\"\n | \"messages/partial\"\n | \"messages/complete\"\n | \"metadata\"\n | \"updates\"\n | string;\n data: TMessage[] | any;\n};\nexport type LangGraphStreamCallback<TMessage> = (\n messages: TMessage[],\n config: LangGraphSendMessageConfig & { abortSignal: AbortSignal },\n) =>\n | Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>>\n | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;\n\nexport type LangGraphInterruptState = {\n value?: any;\n resumable?: boolean;\n when: string;\n ns?: string[];\n};\n\nconst DEFAULT_APPEND_MESSAGE = <TMessage>(\n _: TMessage | undefined,\n curr: TMessage,\n) => curr;\n\nexport const useLangGraphMessages = <TMessage extends { id?: string }>({\n stream,\n appendMessage = DEFAULT_APPEND_MESSAGE,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\n appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;\n}) => {\n const [interrupt, setInterrupt] = useState<\n LangGraphInterruptState | undefined\n >();\n const [messages, setMessages] = useState<TMessage[]>([]);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[], config: LangGraphSendMessageConfig) => {\n // ensure all messages have an ID\n newMessages = newMessages.map((m) => (m.id ? m : { ...m, id: uuidv4() }));\n\n const accumulator = new LangGraphMessageAccumulator({\n initialMessages: messages,\n appendMessage,\n });\n setMessages(accumulator.addMessages(newMessages));\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n const response = await stream(newMessages, {\n ...config,\n abortSignal: abortController.signal,\n });\n\n for await (const chunk of response) {\n if (\n chunk.event === \"messages/partial\" ||\n chunk.event === \"messages/complete\"\n ) {\n setMessages(accumulator.addMessages(chunk.data));\n } else if (chunk.event === \"updates\") {\n setInterrupt(chunk.data.__interrupt__?.[0]);\n }\n }\n },\n [messages, stream, appendMessage],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return {\n interrupt,\n messages,\n sendMessage,\n cancel,\n setInterrupt,\n setMessages,\n };\n};\n"],"mappings":"AAAA,SAAS,UAAU,aAAa,cAAc;AAC9C,SAAS,MAAM,cAAc;AAC7B,SAAS,mCAAmC;AAmC5C,MAAM,yBAAyB,CAC7B,GACA,SACG;AAEE,MAAM,uBAAuB,CAAmC;AAAA,EACrE;AAAA,EACA,gBAAgB;AAClB,MAGM;AACJ,QAAM,CAAC,WAAW,YAAY,IAAI,SAEhC;AACF,QAAM,CAAC,UAAU,WAAW,IAAI,SAAqB,CAAC,CAAC;AACvD,QAAM,qBAAqB,OAA+B,IAAI;AAE9D,QAAM,cAAc;AAAA,IAClB,OAAO,aAAyB,WAAuC;AAErE,oBAAc,YAAY,IAAI,CAAC,MAAO,EAAE,KAAK,IAAI,EAAE,GAAG,GAAG,IAAI,OAAO,EAAE,CAAE;AAExE,YAAM,cAAc,IAAI,4BAA4B;AAAA,QAClD,iBAAiB;AAAA,QACjB;AAAA,MACF,CAAC;AACD,kBAAY,YAAY,YAAY,WAAW,CAAC;AAEhD,YAAM,kBAAkB,IAAI,gBAAgB;AAC5C,yBAAmB,UAAU;AAC7B,YAAM,WAAW,MAAM,OAAO,aAAa;AAAA,QACzC,GAAG;AAAA,QACH,aAAa,gBAAgB;AAAA,MAC/B,CAAC;AAED,uBAAiB,SAAS,UAAU;AAClC,YACE,MAAM,UAAU,sBAChB,MAAM,UAAU,qBAChB;AACA,sBAAY,YAAY,YAAY,MAAM,IAAI,CAAC;AAAA,QACjD,WAAW,MAAM,UAAU,WAAW;AACpC,uBAAa,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,UAAU,QAAQ,aAAa;AAAA,EAClC;AAEA,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,mBAAmB,SAAS;AAC9B,yBAAmB,QAAQ,MAAM;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,kBAAkB,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-langgraph",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,6 +13,7 @@
13
13
  "types": "./dist/index.d.ts",
14
14
  "files": [
15
15
  "dist",
16
+ "src",
16
17
  "README.md"
17
18
  ],
18
19
  "sideEffects": false,
@@ -22,9 +23,9 @@
22
23
  "zod": "^3.24.3"
23
24
  },
24
25
  "peerDependencies": {
25
- "@assistant-ui/react": "^0.10.0",
26
+ "@assistant-ui/react": "^0.10.1",
26
27
  "@types/react": "*",
27
- "assistant-stream": "^0.2.0",
28
+ "assistant-stream": "^0.2.1",
28
29
  "react": "^18 || ^19 || ^19.0.0-rc"
29
30
  },
30
31
  "peerDependenciesMeta": {
@@ -34,8 +35,8 @@
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^22.14.1",
37
- "@types/react": "^19.1.0",
38
- "@types/uuid": "^9.0.8",
38
+ "@types/react": "^19.1.2",
39
+ "@types/uuid": "^10.0.0",
39
40
  "eslint": "^9",
40
41
  "eslint-config-next": "15.3.1",
41
42
  "react": "^19.1.0",
@@ -50,7 +51,7 @@
50
51
  "homepage": "https://www.assistant-ui.com/",
51
52
  "repository": {
52
53
  "type": "git",
53
- "url": "git+https://github.com/assistant-ui/assistant-ui.git"
54
+ "url": "https://github.com/assistant-ui/assistant-ui/tree/main/packages/react-langgraph"
54
55
  },
55
56
  "bugs": {
56
57
  "url": "https://github.com/assistant-ui/assistant-ui/issues"
@@ -0,0 +1,63 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+
3
+ export type LangGraphMessagesEvent<TMessage> = {
4
+ event:
5
+ | "messages"
6
+ | "messages/partial"
7
+ | "messages/complete"
8
+ | "metadata"
9
+ | "updates"
10
+ | string;
11
+ data: TMessage[] | any;
12
+ };
13
+
14
+ export type LangGraphStateAccumulatorConfig<TMessage> = {
15
+ initialMessages?: TMessage[];
16
+ appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;
17
+ };
18
+
19
+ export class LangGraphMessageAccumulator<TMessage extends { id?: string }> {
20
+ private messagesMap = new Map<string, TMessage>();
21
+ private appendMessage: (
22
+ prev: TMessage | undefined,
23
+ curr: TMessage,
24
+ ) => TMessage;
25
+
26
+ constructor({
27
+ initialMessages = [],
28
+ appendMessage = ((_: TMessage | undefined, curr: TMessage) => curr) as (
29
+ prev: TMessage | undefined,
30
+ curr: TMessage,
31
+ ) => TMessage,
32
+ }: LangGraphStateAccumulatorConfig<TMessage> = {}) {
33
+ this.appendMessage = appendMessage;
34
+ this.addMessages(initialMessages);
35
+ }
36
+
37
+ private ensureMessageId(message: TMessage): TMessage {
38
+ return message.id ? message : { ...message, id: uuidv4() };
39
+ }
40
+
41
+ public addMessages(newMessages: TMessage[]) {
42
+ if (newMessages.length === 0) return this.getMessages();
43
+
44
+ for (const message of newMessages.map(this.ensureMessageId)) {
45
+ const previous = message.id
46
+ ? this.messagesMap.get(message.id)
47
+ : undefined;
48
+ this.messagesMap.set(
49
+ message.id ?? uuidv4(),
50
+ this.appendMessage(previous, message),
51
+ );
52
+ }
53
+ return this.getMessages();
54
+ }
55
+
56
+ public getMessages(): TMessage[] {
57
+ return [...this.messagesMap.values()];
58
+ }
59
+
60
+ public clear() {
61
+ this.messagesMap.clear();
62
+ }
63
+ }
@@ -0,0 +1,55 @@
1
+ import { LangChainMessage, LangChainMessageChunk } from "./types";
2
+ import { parsePartialJsonObject } from "assistant-stream/utils";
3
+
4
+ export const appendLangChainChunk = (
5
+ prev: LangChainMessage | undefined,
6
+ curr: LangChainMessage | LangChainMessageChunk,
7
+ ): LangChainMessage => {
8
+ if (curr.type !== "AIMessageChunk") {
9
+ return curr;
10
+ }
11
+
12
+ if (!prev || prev.type !== "ai") {
13
+ return {
14
+ ...curr,
15
+ type: curr.type.replace("MessageChunk", "").toLowerCase(),
16
+ } as LangChainMessage;
17
+ }
18
+
19
+ const newContent =
20
+ typeof prev.content === "string"
21
+ ? [{ type: "text" as const, text: prev.content }]
22
+ : [...prev.content];
23
+
24
+ for (const chunk of curr.content) {
25
+ if (chunk.type === "text") {
26
+ const existing = newContent[chunk.index] ?? { type: "text", text: "" };
27
+ if (existing.type !== "text") throw new Error("");
28
+ newContent[chunk.index] = {
29
+ ...existing,
30
+ ...chunk,
31
+ text: existing.text + chunk.text,
32
+ };
33
+ }
34
+ }
35
+
36
+ const newToolCalls = [...(prev.tool_calls ?? [])];
37
+ for (const chunk of curr.tool_call_chunks) {
38
+ const existing = newToolCalls[chunk.index - 1] ?? { argsText: "" };
39
+ const newArgsText = existing.argsText + chunk.args;
40
+ newToolCalls[chunk.index - 1] = {
41
+ ...chunk,
42
+ ...existing,
43
+ argsText: newArgsText,
44
+ args:
45
+ parsePartialJsonObject(newArgsText) ??
46
+ ("args" in existing ? existing.args : {}),
47
+ };
48
+ }
49
+
50
+ return {
51
+ ...prev,
52
+ content: newContent,
53
+ tool_calls: newToolCalls,
54
+ };
55
+ };
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ import { useExternalMessageConverter } from "@assistant-ui/react";
4
+ import { LangChainMessage } from "./types";
5
+ import { ToolCallContentPart } from "@assistant-ui/react";
6
+ import { ThreadUserMessage } from "@assistant-ui/react";
7
+
8
+ const contentToParts = (content: LangChainMessage["content"]) => {
9
+ if (typeof content === "string")
10
+ return [{ type: "text" as const, text: content }];
11
+ return content
12
+ .map((part): ThreadUserMessage["content"][number] | null => {
13
+ const type = part.type;
14
+ switch (type) {
15
+ case "text":
16
+ return { type: "text", text: part.text };
17
+ case "image_url":
18
+ if (typeof part.image_url === "string") {
19
+ return { type: "image", image: part.image_url };
20
+ } else {
21
+ return {
22
+ type: "image",
23
+ image: part.image_url.url,
24
+ };
25
+ }
26
+
27
+ case "tool_use":
28
+ return null;
29
+ default:
30
+ const _exhaustiveCheck: never = type;
31
+ throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);
32
+ }
33
+ })
34
+ .filter((a) => a !== null);
35
+ };
36
+
37
+ export const convertLangChainMessages: useExternalMessageConverter.Callback<
38
+ LangChainMessage
39
+ > = (message) => {
40
+ switch (message.type) {
41
+ case "system":
42
+ return {
43
+ role: "system",
44
+ id: message.id,
45
+ content: [{ type: "text", text: message.content }],
46
+ };
47
+ case "human":
48
+ return {
49
+ role: "user",
50
+ id: message.id,
51
+ content: contentToParts(message.content),
52
+ };
53
+ case "ai":
54
+ return {
55
+ role: "assistant",
56
+ id: message.id,
57
+ content: [
58
+ ...contentToParts(message.content),
59
+ ...(message.tool_calls?.map(
60
+ (chunk): ToolCallContentPart => ({
61
+ type: "tool-call",
62
+ toolCallId: chunk.id,
63
+ toolName: chunk.name,
64
+ args: chunk.args,
65
+ argsText:
66
+ message.tool_call_chunks?.find((c) => c.id === chunk.id)
67
+ ?.args ?? JSON.stringify(chunk.args),
68
+ }),
69
+ ) ?? []),
70
+ ],
71
+ };
72
+ case "tool":
73
+ return {
74
+ role: "tool",
75
+ toolName: message.name,
76
+ toolCallId: message.tool_call_id,
77
+ result: message.content,
78
+ artifact: message.artifact,
79
+ };
80
+ }
81
+ };
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ export {
2
+ useLangGraphRuntime,
3
+ useLangGraphSend,
4
+ useLangGraphSendCommand,
5
+ useLangGraphInterruptState,
6
+ } from "./useLangGraphRuntime";
7
+
8
+ export {
9
+ useLangGraphMessages,
10
+ type LangGraphInterruptState,
11
+ type LangGraphCommand,
12
+ type LangGraphSendMessageConfig,
13
+ type LangGraphStreamCallback,
14
+ type LangGraphMessagesEvent,
15
+ } from "./useLangGraphMessages";
16
+ export { convertLangChainMessages } from "./convertLangChainMessages";
17
+
18
+ /**
19
+ * @deprecated Use `convertLangChainMessages` instead.
20
+ */
21
+ export { convertLangChainMessages as convertLangchainMessages } from "./convertLangChainMessages";
22
+
23
+ export type {
24
+ LangChainMessage,
25
+ LangChainEvent,
26
+ LangChainToolCall,
27
+ LangChainToolCallChunk,
28
+ } from "./types";
29
+
30
+ export { LangGraphMessageAccumulator } from "./LangGraphMessageAccumulator";
31
+ export { appendLangChainChunk } from "./appendLangChainChunk";
package/src/types.ts ADDED
@@ -0,0 +1,76 @@
1
+ import { ReadonlyJSONObject } from "assistant-stream/utils";
2
+
3
+ export type LangChainToolCallChunk = {
4
+ index: number;
5
+ id: string;
6
+ name: string;
7
+ args: string;
8
+ };
9
+
10
+ export type LangChainToolCall = {
11
+ id: string;
12
+ name: string;
13
+ argsText: string;
14
+ args: ReadonlyJSONObject;
15
+ };
16
+
17
+ type MessageContentText = {
18
+ type: "text";
19
+ text: string;
20
+ };
21
+
22
+ type MessageContentImageUrl = {
23
+ type: "image_url";
24
+ image_url: string | { url: string };
25
+ };
26
+
27
+ type MessageContentToolUse = {
28
+ type: "tool_use";
29
+ };
30
+
31
+ type UserMessageContentComplex = MessageContentText | MessageContentImageUrl;
32
+ type AssistantMessageContentComplex =
33
+ | MessageContentText
34
+ | MessageContentToolUse;
35
+
36
+ type UserMessageContent = string | UserMessageContentComplex[];
37
+ type AssistantMessageContent = string | AssistantMessageContentComplex[];
38
+
39
+ export type LangChainMessage =
40
+ | {
41
+ id?: string;
42
+ type: "system";
43
+ content: string;
44
+ }
45
+ | {
46
+ id?: string;
47
+ type: "human";
48
+ content: UserMessageContent;
49
+ }
50
+ | {
51
+ id?: string;
52
+ type: "tool";
53
+ content: string;
54
+ tool_call_id: string;
55
+ name: string;
56
+ artifact?: any;
57
+ }
58
+ | {
59
+ id?: string;
60
+ type: "ai";
61
+ content: AssistantMessageContent;
62
+ tool_call_chunks?: LangChainToolCallChunk[];
63
+ tool_calls?: LangChainToolCall[];
64
+ };
65
+
66
+ export type LangChainMessageChunk = {
67
+ id: string;
68
+ type: "AIMessageChunk";
69
+ content: (AssistantMessageContentComplex & { index: number })[];
70
+ tool_call_chunks: LangChainToolCallChunk[];
71
+ };
72
+
73
+ export type LangChainEvent = {
74
+ event: "messages/partial" | "messages/complete";
75
+ data: LangChainMessage[];
76
+ };
@@ -0,0 +1,102 @@
1
+ import { useState, useCallback, useRef } from "react";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import { LangGraphMessageAccumulator } from "./LangGraphMessageAccumulator";
4
+
5
+ export type LangGraphCommand = {
6
+ resume: string;
7
+ };
8
+
9
+ export type LangGraphSendMessageConfig = {
10
+ command?: LangGraphCommand;
11
+ runConfig?: unknown;
12
+ };
13
+
14
+ export type LangGraphMessagesEvent<TMessage> = {
15
+ event:
16
+ | "messages"
17
+ | "messages/partial"
18
+ | "messages/complete"
19
+ | "metadata"
20
+ | "updates"
21
+ | string;
22
+ data: TMessage[] | any;
23
+ };
24
+ export type LangGraphStreamCallback<TMessage> = (
25
+ messages: TMessage[],
26
+ config: LangGraphSendMessageConfig & { abortSignal: AbortSignal },
27
+ ) =>
28
+ | Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>>
29
+ | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;
30
+
31
+ export type LangGraphInterruptState = {
32
+ value?: any;
33
+ resumable?: boolean;
34
+ when: string;
35
+ ns?: string[];
36
+ };
37
+
38
+ const DEFAULT_APPEND_MESSAGE = <TMessage>(
39
+ _: TMessage | undefined,
40
+ curr: TMessage,
41
+ ) => curr;
42
+
43
+ export const useLangGraphMessages = <TMessage extends { id?: string }>({
44
+ stream,
45
+ appendMessage = DEFAULT_APPEND_MESSAGE,
46
+ }: {
47
+ stream: LangGraphStreamCallback<TMessage>;
48
+ appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;
49
+ }) => {
50
+ const [interrupt, setInterrupt] = useState<
51
+ LangGraphInterruptState | undefined
52
+ >();
53
+ const [messages, setMessages] = useState<TMessage[]>([]);
54
+ const abortControllerRef = useRef<AbortController | null>(null);
55
+
56
+ const sendMessage = useCallback(
57
+ async (newMessages: TMessage[], config: LangGraphSendMessageConfig) => {
58
+ // ensure all messages have an ID
59
+ newMessages = newMessages.map((m) => (m.id ? m : { ...m, id: uuidv4() }));
60
+
61
+ const accumulator = new LangGraphMessageAccumulator({
62
+ initialMessages: messages,
63
+ appendMessage,
64
+ });
65
+ setMessages(accumulator.addMessages(newMessages));
66
+
67
+ const abortController = new AbortController();
68
+ abortControllerRef.current = abortController;
69
+ const response = await stream(newMessages, {
70
+ ...config,
71
+ abortSignal: abortController.signal,
72
+ });
73
+
74
+ for await (const chunk of response) {
75
+ if (
76
+ chunk.event === "messages/partial" ||
77
+ chunk.event === "messages/complete"
78
+ ) {
79
+ setMessages(accumulator.addMessages(chunk.data));
80
+ } else if (chunk.event === "updates") {
81
+ setInterrupt(chunk.data.__interrupt__?.[0]);
82
+ }
83
+ }
84
+ },
85
+ [messages, stream, appendMessage],
86
+ );
87
+
88
+ const cancel = useCallback(() => {
89
+ if (abortControllerRef.current) {
90
+ abortControllerRef.current.abort();
91
+ }
92
+ }, [abortControllerRef]);
93
+
94
+ return {
95
+ interrupt,
96
+ messages,
97
+ sendMessage,
98
+ cancel,
99
+ setInterrupt,
100
+ setMessages,
101
+ };
102
+ };
@@ -0,0 +1,273 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { LangChainMessage, LangChainToolCall } from "./types";
3
+ import {
4
+ useExternalMessageConverter,
5
+ useExternalStoreRuntime,
6
+ useThread,
7
+ useThreadListItemRuntime,
8
+ } from "@assistant-ui/react";
9
+ import { convertLangChainMessages } from "./convertLangChainMessages";
10
+ import {
11
+ LangGraphCommand,
12
+ LangGraphInterruptState,
13
+ LangGraphSendMessageConfig,
14
+ LangGraphStreamCallback,
15
+ useLangGraphMessages,
16
+ } from "./useLangGraphMessages";
17
+ import { AttachmentAdapter } from "@assistant-ui/react";
18
+ import { AppendMessage } from "@assistant-ui/react";
19
+ import { ExternalStoreAdapter } from "@assistant-ui/react";
20
+ import { FeedbackAdapter } from "@assistant-ui/react";
21
+ import { SpeechSynthesisAdapter } from "@assistant-ui/react";
22
+ import { appendLangChainChunk } from "./appendLangChainChunk";
23
+
24
+ const getPendingToolCalls = (messages: LangChainMessage[]) => {
25
+ const pendingToolCalls = new Map<string, LangChainToolCall>();
26
+ for (const message of messages) {
27
+ if (message.type === "ai") {
28
+ for (const toolCall of message.tool_calls ?? []) {
29
+ pendingToolCalls.set(toolCall.id, toolCall);
30
+ }
31
+ }
32
+ if (message.type === "tool") {
33
+ pendingToolCalls.delete(message.tool_call_id);
34
+ }
35
+ }
36
+
37
+ return [...pendingToolCalls.values()];
38
+ };
39
+
40
+ const getMessageContent = (msg: AppendMessage) => {
41
+ const allContent = [
42
+ ...msg.content,
43
+ ...(msg.attachments?.flatMap((a) => a.content) ?? []),
44
+ ];
45
+ const content = allContent.map((part) => {
46
+ const type = part.type;
47
+ switch (type) {
48
+ case "text":
49
+ return { type: "text" as const, text: part.text };
50
+ case "image":
51
+ return { type: "image_url" as const, image_url: { url: part.image } };
52
+
53
+ case "tool-call":
54
+ throw new Error("Tool call appends are not supported.");
55
+
56
+ default:
57
+ const _exhaustiveCheck: "reasoning" | "source" | "file" | "audio" =
58
+ type;
59
+ throw new Error(
60
+ `Unsupported append content part type: ${_exhaustiveCheck}`,
61
+ );
62
+ }
63
+ });
64
+
65
+ if (content.length === 1 && content[0]?.type === "text") {
66
+ return content[0].text ?? "";
67
+ }
68
+
69
+ return content;
70
+ };
71
+
72
+ const symbolLangGraphRuntimeExtras = Symbol("langgraph-runtime-extras");
73
+ type LangGraphRuntimeExtras = {
74
+ [symbolLangGraphRuntimeExtras]: true;
75
+ send: (
76
+ messages: LangChainMessage[],
77
+ config: LangGraphSendMessageConfig,
78
+ ) => Promise<void>;
79
+ interrupt: LangGraphInterruptState | undefined;
80
+ };
81
+
82
+ const asLangGraphRuntimeExtras = (extras: unknown): LangGraphRuntimeExtras => {
83
+ if (
84
+ typeof extras !== "object" ||
85
+ extras == null ||
86
+ !(symbolLangGraphRuntimeExtras in extras)
87
+ )
88
+ throw new Error(
89
+ "This method can only be called when you are using useLangGraphRuntime",
90
+ );
91
+
92
+ return extras as LangGraphRuntimeExtras;
93
+ };
94
+
95
+ export const useLangGraphInterruptState = () => {
96
+ const { interrupt } = useThread((t) => asLangGraphRuntimeExtras(t.extras));
97
+ return interrupt;
98
+ };
99
+
100
+ export const useLangGraphSend = () => {
101
+ const { send } = useThread((t) => asLangGraphRuntimeExtras(t.extras));
102
+ return send;
103
+ };
104
+
105
+ export const useLangGraphSendCommand = () => {
106
+ const send = useLangGraphSend();
107
+ return (command: LangGraphCommand) => send([], { command });
108
+ };
109
+
110
+ export const useLangGraphRuntime = ({
111
+ autoCancelPendingToolCalls,
112
+ adapters: { attachments, feedback, speech } = {},
113
+ unstable_allowCancellation,
114
+ stream,
115
+ threadId,
116
+ onSwitchToNewThread,
117
+ onSwitchToThread,
118
+ }: {
119
+ /**
120
+ * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.
121
+ */
122
+ threadId?: string | undefined;
123
+ autoCancelPendingToolCalls?: boolean | undefined;
124
+ unstable_allowCancellation?: boolean | undefined;
125
+ stream: LangGraphStreamCallback<LangChainMessage>;
126
+ /**
127
+ * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.
128
+ */
129
+ onSwitchToNewThread?: () => Promise<void> | void;
130
+ onSwitchToThread?: (threadId: string) => Promise<{
131
+ messages: LangChainMessage[];
132
+ interrupts?: LangGraphInterruptState[];
133
+ }>;
134
+ adapters?:
135
+ | {
136
+ attachments?: AttachmentAdapter;
137
+ speech?: SpeechSynthesisAdapter;
138
+ feedback?: FeedbackAdapter;
139
+ }
140
+ | undefined;
141
+ }) => {
142
+ const {
143
+ interrupt,
144
+ setInterrupt,
145
+ messages,
146
+ sendMessage,
147
+ cancel,
148
+ setMessages,
149
+ } = useLangGraphMessages({
150
+ appendMessage: appendLangChainChunk,
151
+ stream,
152
+ });
153
+
154
+ const [isRunning, setIsRunning] = useState(false);
155
+ const handleSendMessage = async (
156
+ messages: LangChainMessage[],
157
+ config: LangGraphSendMessageConfig,
158
+ ) => {
159
+ try {
160
+ setIsRunning(true);
161
+ await sendMessage(messages, config);
162
+ } catch (error) {
163
+ console.error("Error streaming messages:", error);
164
+ } finally {
165
+ setIsRunning(false);
166
+ }
167
+ };
168
+
169
+ const threadMessages = useExternalMessageConverter({
170
+ callback: convertLangChainMessages,
171
+ messages,
172
+ isRunning,
173
+ });
174
+
175
+ const switchToThread = !onSwitchToThread
176
+ ? undefined
177
+ : async (externalId: string) => {
178
+ const { messages, interrupts } = await onSwitchToThread(externalId);
179
+ setMessages(messages);
180
+ setInterrupt(interrupts?.[0]);
181
+ };
182
+
183
+ const threadList: NonNullable<
184
+ ExternalStoreAdapter["adapters"]
185
+ >["threadList"] = {
186
+ threadId,
187
+ onSwitchToNewThread: !onSwitchToNewThread
188
+ ? undefined
189
+ : async () => {
190
+ await onSwitchToNewThread();
191
+ setMessages([]);
192
+ },
193
+ onSwitchToThread: switchToThread,
194
+ };
195
+
196
+ const loadingRef = useRef(false);
197
+ const threadListItemRuntime = useThreadListItemRuntime({ optional: true });
198
+ useEffect(() => {
199
+ if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;
200
+
201
+ const externalId = threadListItemRuntime.getState().externalId;
202
+ if (externalId) {
203
+ loadingRef.current = true;
204
+ switchToThread(externalId).finally(() => {
205
+ loadingRef.current = false;
206
+ });
207
+ }
208
+ // eslint-disable-next-line react-hooks/exhaustive-deps
209
+ }, []);
210
+
211
+ return useExternalStoreRuntime({
212
+ isRunning,
213
+ messages: threadMessages,
214
+ adapters: {
215
+ attachments,
216
+ feedback,
217
+ speech,
218
+ threadList,
219
+ },
220
+ extras: {
221
+ [symbolLangGraphRuntimeExtras]: true,
222
+ interrupt,
223
+ send: handleSendMessage,
224
+ } satisfies LangGraphRuntimeExtras,
225
+ onNew: (msg) => {
226
+ const cancellations =
227
+ autoCancelPendingToolCalls !== false
228
+ ? getPendingToolCalls(messages).map(
229
+ (t) =>
230
+ ({
231
+ type: "tool",
232
+ name: t.name,
233
+ tool_call_id: t.id,
234
+ content: JSON.stringify({ cancelled: true }),
235
+ }) satisfies LangChainMessage & { type: "tool" },
236
+ )
237
+ : [];
238
+
239
+ return handleSendMessage(
240
+ [
241
+ ...cancellations,
242
+ {
243
+ type: "human",
244
+ content: getMessageContent(msg),
245
+ },
246
+ ],
247
+ {
248
+ runConfig: msg.runConfig,
249
+ },
250
+ );
251
+ },
252
+ onAddToolResult: async ({ toolCallId, toolName, result }) => {
253
+ // TODO parallel human in the loop calls
254
+ await handleSendMessage(
255
+ [
256
+ {
257
+ type: "tool",
258
+ name: toolName,
259
+ tool_call_id: toolCallId,
260
+ content: JSON.stringify(result),
261
+ },
262
+ ],
263
+ // TODO reuse runconfig here!
264
+ {},
265
+ );
266
+ },
267
+ onCancel: unstable_allowCancellation
268
+ ? async () => {
269
+ cancel();
270
+ }
271
+ : undefined,
272
+ });
273
+ };