@assistant-ui/react-langgraph 0.0.13 → 0.0.14

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/dist/index.d.mts CHANGED
@@ -927,10 +927,26 @@ type LangChainToolCall = {
927
927
  name: string;
928
928
  args: Record<string, unknown>;
929
929
  };
930
+ type MessageContentText = {
931
+ type: "text";
932
+ text: string;
933
+ };
934
+ type MessageContentImageUrl = {
935
+ type: "image_url";
936
+ image_url: string | {
937
+ url: string;
938
+ };
939
+ };
940
+ type MessageContentComplex = MessageContentText | MessageContentImageUrl;
941
+ type MessageContent = string | MessageContentComplex[];
930
942
  type LangChainMessage = {
931
943
  id?: string;
932
- type: "human" | "system";
944
+ type: "system";
933
945
  content: string;
946
+ } | {
947
+ id?: string;
948
+ type: "human";
949
+ content: MessageContent;
934
950
  } | {
935
951
  id?: string;
936
952
  type: "tool";
package/dist/index.d.ts CHANGED
@@ -927,10 +927,26 @@ type LangChainToolCall = {
927
927
  name: string;
928
928
  args: Record<string, unknown>;
929
929
  };
930
+ type MessageContentText = {
931
+ type: "text";
932
+ text: string;
933
+ };
934
+ type MessageContentImageUrl = {
935
+ type: "image_url";
936
+ image_url: string | {
937
+ url: string;
938
+ };
939
+ };
940
+ type MessageContentComplex = MessageContentText | MessageContentImageUrl;
941
+ type MessageContent = string | MessageContentComplex[];
930
942
  type LangChainMessage = {
931
943
  id?: string;
932
- type: "human" | "system";
944
+ type: "system";
933
945
  content: string;
946
+ } | {
947
+ id?: string;
948
+ type: "human";
949
+ content: MessageContent;
934
950
  } | {
935
951
  id?: string;
936
952
  type: "tool";
package/dist/index.js CHANGED
@@ -40,10 +40,36 @@ var convertLangchainMessages = (message) => {
40
40
  content: [{ type: "text", text: message.content }]
41
41
  };
42
42
  case "human":
43
+ let content;
44
+ if (typeof message.content === "string") {
45
+ content = [{ type: "text", text: message.content }];
46
+ } else {
47
+ content = message.content.map((part) => {
48
+ if (typeof part === "string") {
49
+ return { type: "text", text: part };
50
+ } else {
51
+ const type = part.type;
52
+ switch (type) {
53
+ case "text":
54
+ return { type: "text", text: part.text };
55
+ case "image_url":
56
+ return {
57
+ type: "image",
58
+ image: typeof part.image_url === "string" ? part.image_url : part.image_url.url
59
+ };
60
+ default:
61
+ const unhandledType = type;
62
+ throw new Error(
63
+ `Unhandled content part type: ${unhandledType}`
64
+ );
65
+ }
66
+ }
67
+ });
68
+ }
43
69
  return {
44
70
  role: "user",
45
71
  id: message.id,
46
- content: [{ type: "text", text: message.content }]
72
+ content
47
73
  };
48
74
  case "ai":
49
75
  return {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["export { useLangGraphRuntime } from \"./useLangGraphRuntime\";\n\nexport { useLangGraphMessages } from \"./useLangGraphMessages\";\nexport { convertLangchainMessages } from \"./convertLangchainMessages\";\nexport type {\n LangChainMessage,\n LangChainEvent,\n LangChainToolCall,\n LangChainToolCallChunk,\n} from \"./types\";\n","import { useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport { useLangGraphMessages } from \"./useLangGraphMessages\";\n\nconst getPendingToolCalls = (messages: LangChainMessage[]) => {\n const pendingToolCalls = new Map<string, LangChainToolCall>();\n for (const message of messages) {\n if (message.type === \"ai\") {\n for (const toolCall of message.tool_calls ?? []) {\n pendingToolCalls.set(toolCall.id, toolCall);\n }\n }\n if (message.type === \"tool\") {\n pendingToolCalls.delete(message.tool_call_id);\n }\n }\n\n return [...pendingToolCalls.values()];\n};\n\nexport const useLangGraphRuntime = ({\n threadId,\n autoCancelPendingToolCalls,\n stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n stream: (messages: LangChainMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n}) => {\n const { messages, sendMessage, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (messages: LangChainMessage[]) => {\n try {\n setIsRunning(true);\n await sendMessage(messages);\n } catch (error) {\n console.error(\"Error streaming messages:\", error);\n } finally {\n setIsRunning(false);\n }\n };\n\n const threadMessages = useExternalMessageConverter({\n callback: convertLangchainMessages,\n messages,\n isRunning,\n });\n\n return useExternalStoreRuntime({\n threadId,\n isRunning,\n messages: threadMessages,\n onNew: (msg) => {\n if (msg.content.length !== 1 || msg.content[0]?.type !== \"text\")\n throw new Error(\"Only text messages are supported\");\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(messages).map(\n (t) =>\n ({\n type: \"tool\",\n name: t.name,\n tool_call_id: t.id,\n content: JSON.stringify({ cancelled: true }),\n }) satisfies LangChainMessage & { type: \"tool\" },\n )\n : [];\n\n return handleSendMessage([\n ...cancellations,\n {\n type: \"human\",\n content: msg.content[0].text,\n },\n ]);\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n await handleSendMessage([\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ]);\n },\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: !onSwitchToThread\n ? undefined\n : async (threadId) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n },\n });\n};\n","\"use client\";\n\nimport { useExternalMessageConverter } from \"@assistant-ui/react\";\nimport { LangChainMessage } from \"./types\";\nimport { ToolCallContentPart } from \"@assistant-ui/react\";\n\nexport const convertLangchainMessages: useExternalMessageConverter.Callback<\n LangChainMessage\n> = (message) => {\n switch (message.type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text: message.content,\n },\n ...(message.tool_calls?.map(\n (chunk): ToolCallContentPart => ({\n type: \"tool-call\",\n toolCallId: chunk.id,\n toolName: chunk.name,\n args: chunk.args,\n argsText:\n message.tool_call_chunks?.find((c) => c.id === chunk.id)\n ?.args ?? JSON.stringify(chunk.args),\n }),\n ) ?? []),\n ],\n };\n case \"tool\":\n return {\n role: \"tool\",\n toolName: message.name,\n toolCallId: message.tool_call_id,\n result: message.content,\n };\n }\n};\n","import { useState, useCallback } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: (messages: TMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n}) => {\n const [messages, setMessages] = useState<TMessage[]>([]);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[]) => {\n const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\n\n const response = await stream(newMessages);\n\n const completeMessages: TMessage[] = [];\n let partialMessages: Map<string, TMessage> = new Map();\n for await (const chunk of response) {\n if (chunk.event === \"messages/partial\") {\n // bug fix: messages/complete is not sent for some python langchain backends\n if (completeMessages.length === 0) {\n completeMessages.push(...optimisticMessages);\n }\n\n for (const message of chunk.data) {\n if (!message.id) throw new Error(\"Partial message missing id\");\n\n partialMessages.set(message.id, message);\n }\n } else if (chunk.event === \"messages/complete\") {\n for (const message of chunk.data) {\n if (message.id) {\n partialMessages.delete(message.id);\n }\n\n // bug fix: tool results can arrive before the message is complete ?\n // if we have partial messages left, we add complete message to partial messages\n if (partialMessages.size > 0) {\n partialMessages.set(message.id ?? generateId(), message);\n } else {\n completeMessages.push(message);\n }\n }\n } else {\n continue;\n }\n\n setMessages([...completeMessages, ...partialMessages.values()]);\n }\n // if (partialMessages.size > 0) {\n // throw new Error(\"A partial message was not marked as complete\");\n // }\n },\n [stream],\n );\n console.log(\"messages\", messages);\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":"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","names":["import_react","import_react","messages","threadId"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["export { useLangGraphRuntime } from \"./useLangGraphRuntime\";\n\nexport { useLangGraphMessages } from \"./useLangGraphMessages\";\nexport { convertLangchainMessages } from \"./convertLangchainMessages\";\nexport type {\n LangChainMessage,\n LangChainEvent,\n LangChainToolCall,\n LangChainToolCallChunk,\n} from \"./types\";\n","import { useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport { useLangGraphMessages } from \"./useLangGraphMessages\";\n\nconst getPendingToolCalls = (messages: LangChainMessage[]) => {\n const pendingToolCalls = new Map<string, LangChainToolCall>();\n for (const message of messages) {\n if (message.type === \"ai\") {\n for (const toolCall of message.tool_calls ?? []) {\n pendingToolCalls.set(toolCall.id, toolCall);\n }\n }\n if (message.type === \"tool\") {\n pendingToolCalls.delete(message.tool_call_id);\n }\n }\n\n return [...pendingToolCalls.values()];\n};\n\nexport const useLangGraphRuntime = ({\n threadId,\n autoCancelPendingToolCalls,\n stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n stream: (messages: LangChainMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n}) => {\n const { messages, sendMessage, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (messages: LangChainMessage[]) => {\n try {\n setIsRunning(true);\n await sendMessage(messages);\n } catch (error) {\n console.error(\"Error streaming messages:\", error);\n } finally {\n setIsRunning(false);\n }\n };\n\n const threadMessages = useExternalMessageConverter({\n callback: convertLangchainMessages,\n messages,\n isRunning,\n });\n\n return useExternalStoreRuntime({\n threadId,\n isRunning,\n messages: threadMessages,\n onNew: (msg) => {\n if (msg.content.length !== 1 || msg.content[0]?.type !== \"text\")\n throw new Error(\"Only text messages are supported\");\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(messages).map(\n (t) =>\n ({\n type: \"tool\",\n name: t.name,\n tool_call_id: t.id,\n content: JSON.stringify({ cancelled: true }),\n }) satisfies LangChainMessage & { type: \"tool\" },\n )\n : [];\n\n return handleSendMessage([\n ...cancellations,\n {\n type: \"human\",\n content: msg.content[0].text,\n },\n ]);\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n await handleSendMessage([\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ]);\n },\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: !onSwitchToThread\n ? undefined\n : async (threadId) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n },\n });\n};\n","\"use client\";\n\nimport { useExternalMessageConverter } from \"@assistant-ui/react\";\nimport { LangChainMessage } from \"./types\";\nimport { ToolCallContentPart } from \"@assistant-ui/react\";\nimport { ThreadUserMessage } from \"@assistant-ui/react\";\n\nexport const convertLangchainMessages: useExternalMessageConverter.Callback<\n LangChainMessage\n> = (message) => {\n switch (message.type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"human\":\n let content: ThreadUserMessage[\"content\"];\n\n if (typeof message.content === \"string\") {\n content = [{ type: \"text\" as const, text: message.content }];\n } else {\n content = message.content.map((part) => {\n if (typeof part === \"string\") {\n return { type: \"text\", text: part };\n } else {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n\n case \"image_url\":\n return {\n type: \"image\" as const,\n image:\n typeof part.image_url === \"string\"\n ? part.image_url\n : part.image_url.url,\n };\n default:\n const unhandledType: never = type;\n throw new Error(\n `Unhandled content part type: ${unhandledType}`,\n );\n }\n }\n });\n }\n\n return {\n role: \"user\",\n id: message.id,\n content,\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text: message.content,\n },\n ...(message.tool_calls?.map(\n (chunk): ToolCallContentPart => ({\n type: \"tool-call\",\n toolCallId: chunk.id,\n toolName: chunk.name,\n args: chunk.args,\n argsText:\n message.tool_call_chunks?.find((c) => c.id === chunk.id)\n ?.args ?? JSON.stringify(chunk.args),\n }),\n ) ?? []),\n ],\n };\n case \"tool\":\n return {\n role: \"tool\",\n toolName: message.name,\n toolCallId: message.tool_call_id,\n result: message.content,\n };\n }\n};\n","import { useState, useCallback } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: (messages: TMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n}) => {\n const [messages, setMessages] = useState<TMessage[]>([]);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[]) => {\n const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\n\n const response = await stream(newMessages);\n\n const completeMessages: TMessage[] = [];\n let partialMessages: Map<string, TMessage> = new Map();\n for await (const chunk of response) {\n if (chunk.event === \"messages/partial\") {\n // bug fix: messages/complete is not sent for some python langchain backends\n if (completeMessages.length === 0) {\n completeMessages.push(...optimisticMessages);\n }\n\n for (const message of chunk.data) {\n if (!message.id) throw new Error(\"Partial message missing id\");\n\n partialMessages.set(message.id, message);\n }\n } else if (chunk.event === \"messages/complete\") {\n for (const message of chunk.data) {\n if (message.id) {\n partialMessages.delete(message.id);\n }\n\n // bug fix: tool results can arrive before the message is complete ?\n // if we have partial messages left, we add complete message to partial messages\n if (partialMessages.size > 0) {\n partialMessages.set(message.id ?? generateId(), message);\n } else {\n completeMessages.push(message);\n }\n }\n } else {\n continue;\n }\n\n setMessages([...completeMessages, ...partialMessages.values()]);\n }\n // if (partialMessages.size > 0) {\n // throw new Error(\"A partial message was not marked as complete\");\n // }\n },\n [stream],\n );\n console.log(\"messages\", messages);\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":"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","names":["import_react","import_react","messages","threadId"]}
package/dist/index.mjs CHANGED
@@ -15,10 +15,36 @@ var convertLangchainMessages = (message) => {
15
15
  content: [{ type: "text", text: message.content }]
16
16
  };
17
17
  case "human":
18
+ let content;
19
+ if (typeof message.content === "string") {
20
+ content = [{ type: "text", text: message.content }];
21
+ } else {
22
+ content = message.content.map((part) => {
23
+ if (typeof part === "string") {
24
+ return { type: "text", text: part };
25
+ } else {
26
+ const type = part.type;
27
+ switch (type) {
28
+ case "text":
29
+ return { type: "text", text: part.text };
30
+ case "image_url":
31
+ return {
32
+ type: "image",
33
+ image: typeof part.image_url === "string" ? part.image_url : part.image_url.url
34
+ };
35
+ default:
36
+ const unhandledType = type;
37
+ throw new Error(
38
+ `Unhandled content part type: ${unhandledType}`
39
+ );
40
+ }
41
+ }
42
+ });
43
+ }
18
44
  return {
19
45
  role: "user",
20
46
  id: message.id,
21
- content: [{ type: "text", text: message.content }]
47
+ content
22
48
  };
23
49
  case "ai":
24
50
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport { useLangGraphMessages } from \"./useLangGraphMessages\";\n\nconst getPendingToolCalls = (messages: LangChainMessage[]) => {\n const pendingToolCalls = new Map<string, LangChainToolCall>();\n for (const message of messages) {\n if (message.type === \"ai\") {\n for (const toolCall of message.tool_calls ?? []) {\n pendingToolCalls.set(toolCall.id, toolCall);\n }\n }\n if (message.type === \"tool\") {\n pendingToolCalls.delete(message.tool_call_id);\n }\n }\n\n return [...pendingToolCalls.values()];\n};\n\nexport const useLangGraphRuntime = ({\n threadId,\n autoCancelPendingToolCalls,\n stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n stream: (messages: LangChainMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n}) => {\n const { messages, sendMessage, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (messages: LangChainMessage[]) => {\n try {\n setIsRunning(true);\n await sendMessage(messages);\n } catch (error) {\n console.error(\"Error streaming messages:\", error);\n } finally {\n setIsRunning(false);\n }\n };\n\n const threadMessages = useExternalMessageConverter({\n callback: convertLangchainMessages,\n messages,\n isRunning,\n });\n\n return useExternalStoreRuntime({\n threadId,\n isRunning,\n messages: threadMessages,\n onNew: (msg) => {\n if (msg.content.length !== 1 || msg.content[0]?.type !== \"text\")\n throw new Error(\"Only text messages are supported\");\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(messages).map(\n (t) =>\n ({\n type: \"tool\",\n name: t.name,\n tool_call_id: t.id,\n content: JSON.stringify({ cancelled: true }),\n }) satisfies LangChainMessage & { type: \"tool\" },\n )\n : [];\n\n return handleSendMessage([\n ...cancellations,\n {\n type: \"human\",\n content: msg.content[0].text,\n },\n ]);\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n await handleSendMessage([\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ]);\n },\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: !onSwitchToThread\n ? undefined\n : async (threadId) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n },\n });\n};\n","\"use client\";\n\nimport { useExternalMessageConverter } from \"@assistant-ui/react\";\nimport { LangChainMessage } from \"./types\";\nimport { ToolCallContentPart } from \"@assistant-ui/react\";\n\nexport const convertLangchainMessages: useExternalMessageConverter.Callback<\n LangChainMessage\n> = (message) => {\n switch (message.type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text: message.content,\n },\n ...(message.tool_calls?.map(\n (chunk): ToolCallContentPart => ({\n type: \"tool-call\",\n toolCallId: chunk.id,\n toolName: chunk.name,\n args: chunk.args,\n argsText:\n message.tool_call_chunks?.find((c) => c.id === chunk.id)\n ?.args ?? JSON.stringify(chunk.args),\n }),\n ) ?? []),\n ],\n };\n case \"tool\":\n return {\n role: \"tool\",\n toolName: message.name,\n toolCallId: message.tool_call_id,\n result: message.content,\n };\n }\n};\n","import { useState, useCallback } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: (messages: TMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n}) => {\n const [messages, setMessages] = useState<TMessage[]>([]);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[]) => {\n const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\n\n const response = await stream(newMessages);\n\n const completeMessages: TMessage[] = [];\n let partialMessages: Map<string, TMessage> = new Map();\n for await (const chunk of response) {\n if (chunk.event === \"messages/partial\") {\n // bug fix: messages/complete is not sent for some python langchain backends\n if (completeMessages.length === 0) {\n completeMessages.push(...optimisticMessages);\n }\n\n for (const message of chunk.data) {\n if (!message.id) throw new Error(\"Partial message missing id\");\n\n partialMessages.set(message.id, message);\n }\n } else if (chunk.event === \"messages/complete\") {\n for (const message of chunk.data) {\n if (message.id) {\n partialMessages.delete(message.id);\n }\n\n // bug fix: tool results can arrive before the message is complete ?\n // if we have partial messages left, we add complete message to partial messages\n if (partialMessages.size > 0) {\n partialMessages.set(message.id ?? generateId(), message);\n } else {\n completeMessages.push(message);\n }\n }\n } else {\n continue;\n }\n\n setMessages([...completeMessages, ...partialMessages.values()]);\n }\n // if (partialMessages.size > 0) {\n // throw new Error(\"A partial message was not marked as complete\");\n // }\n },\n [stream],\n );\n console.log(\"messages\", messages);\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":"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","names":["useState","useState","messages","threadId"]}
1
+ {"version":3,"sources":["../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport { useLangGraphMessages } from \"./useLangGraphMessages\";\n\nconst getPendingToolCalls = (messages: LangChainMessage[]) => {\n const pendingToolCalls = new Map<string, LangChainToolCall>();\n for (const message of messages) {\n if (message.type === \"ai\") {\n for (const toolCall of message.tool_calls ?? []) {\n pendingToolCalls.set(toolCall.id, toolCall);\n }\n }\n if (message.type === \"tool\") {\n pendingToolCalls.delete(message.tool_call_id);\n }\n }\n\n return [...pendingToolCalls.values()];\n};\n\nexport const useLangGraphRuntime = ({\n threadId,\n autoCancelPendingToolCalls,\n stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n stream: (messages: LangChainMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n}) => {\n const { messages, sendMessage, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (messages: LangChainMessage[]) => {\n try {\n setIsRunning(true);\n await sendMessage(messages);\n } catch (error) {\n console.error(\"Error streaming messages:\", error);\n } finally {\n setIsRunning(false);\n }\n };\n\n const threadMessages = useExternalMessageConverter({\n callback: convertLangchainMessages,\n messages,\n isRunning,\n });\n\n return useExternalStoreRuntime({\n threadId,\n isRunning,\n messages: threadMessages,\n onNew: (msg) => {\n if (msg.content.length !== 1 || msg.content[0]?.type !== \"text\")\n throw new Error(\"Only text messages are supported\");\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(messages).map(\n (t) =>\n ({\n type: \"tool\",\n name: t.name,\n tool_call_id: t.id,\n content: JSON.stringify({ cancelled: true }),\n }) satisfies LangChainMessage & { type: \"tool\" },\n )\n : [];\n\n return handleSendMessage([\n ...cancellations,\n {\n type: \"human\",\n content: msg.content[0].text,\n },\n ]);\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n await handleSendMessage([\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ]);\n },\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: !onSwitchToThread\n ? undefined\n : async (threadId) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n },\n });\n};\n","\"use client\";\n\nimport { useExternalMessageConverter } from \"@assistant-ui/react\";\nimport { LangChainMessage } from \"./types\";\nimport { ToolCallContentPart } from \"@assistant-ui/react\";\nimport { ThreadUserMessage } from \"@assistant-ui/react\";\n\nexport const convertLangchainMessages: useExternalMessageConverter.Callback<\n LangChainMessage\n> = (message) => {\n switch (message.type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: message.content }],\n };\n case \"human\":\n let content: ThreadUserMessage[\"content\"];\n\n if (typeof message.content === \"string\") {\n content = [{ type: \"text\" as const, text: message.content }];\n } else {\n content = message.content.map((part) => {\n if (typeof part === \"string\") {\n return { type: \"text\", text: part };\n } else {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n\n case \"image_url\":\n return {\n type: \"image\" as const,\n image:\n typeof part.image_url === \"string\"\n ? part.image_url\n : part.image_url.url,\n };\n default:\n const unhandledType: never = type;\n throw new Error(\n `Unhandled content part type: ${unhandledType}`,\n );\n }\n }\n });\n }\n\n return {\n role: \"user\",\n id: message.id,\n content,\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text: message.content,\n },\n ...(message.tool_calls?.map(\n (chunk): ToolCallContentPart => ({\n type: \"tool-call\",\n toolCallId: chunk.id,\n toolName: chunk.name,\n args: chunk.args,\n argsText:\n message.tool_call_chunks?.find((c) => c.id === chunk.id)\n ?.args ?? JSON.stringify(chunk.args),\n }),\n ) ?? []),\n ],\n };\n case \"tool\":\n return {\n role: \"tool\",\n toolName: message.name,\n toolCallId: message.tool_call_id,\n result: message.content,\n };\n }\n};\n","import { useState, useCallback } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: (messages: TMessage[]) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n >;\n}) => {\n const [messages, setMessages] = useState<TMessage[]>([]);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[]) => {\n const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\n\n const response = await stream(newMessages);\n\n const completeMessages: TMessage[] = [];\n let partialMessages: Map<string, TMessage> = new Map();\n for await (const chunk of response) {\n if (chunk.event === \"messages/partial\") {\n // bug fix: messages/complete is not sent for some python langchain backends\n if (completeMessages.length === 0) {\n completeMessages.push(...optimisticMessages);\n }\n\n for (const message of chunk.data) {\n if (!message.id) throw new Error(\"Partial message missing id\");\n\n partialMessages.set(message.id, message);\n }\n } else if (chunk.event === \"messages/complete\") {\n for (const message of chunk.data) {\n if (message.id) {\n partialMessages.delete(message.id);\n }\n\n // bug fix: tool results can arrive before the message is complete ?\n // if we have partial messages left, we add complete message to partial messages\n if (partialMessages.size > 0) {\n partialMessages.set(message.id ?? generateId(), message);\n } else {\n completeMessages.push(message);\n }\n }\n } else {\n continue;\n }\n\n setMessages([...completeMessages, ...partialMessages.values()]);\n }\n // if (partialMessages.size > 0) {\n // throw new Error(\"A partial message was not marked as complete\");\n // }\n },\n [stream],\n );\n console.log(\"messages\", messages);\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":"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","names":["useState","useState","messages","threadId"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-langgraph",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "zod": "^3.23.8"
28
28
  },
29
29
  "peerDependencies": {
30
- "@assistant-ui/react": "^0.5.73",
30
+ "@assistant-ui/react": "^0.5.74",
31
31
  "@types/react": "*",
32
32
  "react": "^18",
33
33
  "react-hook-form": "^7.x.x"