@assistant-ui/react-langgraph 0.1.16 → 0.1.18

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
@@ -62,13 +62,16 @@ type LangGraphSendMessageConfig = {
62
62
  command?: LangGraphCommand;
63
63
  runConfig?: unknown;
64
64
  };
65
+ type LangGraphMessagesEvent<TMessage> = {
66
+ event: "messages/partial" | "messages/complete" | string;
67
+ data: TMessage[] | any;
68
+ };
65
69
  type LangGraphStreamCallback<TMessage> = (messages: TMessage[], config: LangGraphSendMessageConfig & {
66
70
  abortSignal: AbortSignal;
67
- }) => Promise<AsyncGenerator<{
68
- event: string;
69
- data: any;
70
- }>>;
71
- declare const useLangGraphMessages: <TMessage>({ stream, }: {
71
+ }) => Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>> | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;
72
+ declare const useLangGraphMessages: <TMessage extends {
73
+ id?: string;
74
+ }>({ stream, }: {
72
75
  stream: LangGraphStreamCallback<TMessage>;
73
76
  }) => {
74
77
  messages: TMessage[];
package/dist/index.d.ts CHANGED
@@ -62,13 +62,16 @@ type LangGraphSendMessageConfig = {
62
62
  command?: LangGraphCommand;
63
63
  runConfig?: unknown;
64
64
  };
65
+ type LangGraphMessagesEvent<TMessage> = {
66
+ event: "messages/partial" | "messages/complete" | string;
67
+ data: TMessage[] | any;
68
+ };
65
69
  type LangGraphStreamCallback<TMessage> = (messages: TMessage[], config: LangGraphSendMessageConfig & {
66
70
  abortSignal: AbortSignal;
67
- }) => Promise<AsyncGenerator<{
68
- event: string;
69
- data: any;
70
- }>>;
71
- declare const useLangGraphMessages: <TMessage>({ stream, }: {
71
+ }) => Promise<AsyncGenerator<LangGraphMessagesEvent<TMessage>>> | AsyncGenerator<LangGraphMessagesEvent<TMessage>>;
72
+ declare const useLangGraphMessages: <TMessage extends {
73
+ id?: string;
74
+ }>({ stream, }: {
72
75
  stream: LangGraphStreamCallback<TMessage>;
73
76
  }) => {
74
77
  messages: TMessage[];
package/dist/index.js CHANGED
@@ -29,8 +29,8 @@ __export(index_exports, {
29
29
  module.exports = __toCommonJS(index_exports);
30
30
 
31
31
  // src/useLangGraphRuntime.ts
32
- var import_react3 = require("react");
33
- var import_react4 = require("@assistant-ui/react");
32
+ var import_react2 = require("react");
33
+ var import_react3 = require("@assistant-ui/react");
34
34
 
35
35
  // src/convertLangchainMessages.ts
36
36
  var contentToParts = (content) => {
@@ -101,8 +101,7 @@ var convertLangchainMessages = (message) => {
101
101
 
102
102
  // src/useLangGraphMessages.ts
103
103
  var import_react = require("react");
104
- var import_react2 = require("@assistant-ui/react");
105
- var { generateId } = import_react2.INTERNAL;
104
+ var import_uuid = require("uuid");
106
105
  var useLangGraphMessages = ({
107
106
  stream
108
107
  }) => {
@@ -110,42 +109,26 @@ var useLangGraphMessages = ({
110
109
  const abortControllerRef = (0, import_react.useRef)(null);
111
110
  const sendMessage = (0, import_react.useCallback)(
112
111
  async (newMessages, config) => {
113
- const optimisticMessages = [...messages, ...newMessages];
114
- if (newMessages.length > 0) {
115
- setMessages(optimisticMessages);
116
- }
112
+ newMessages = newMessages.map((m) => m.id ? m : { ...m, id: (0, import_uuid.v4)() });
113
+ const messagesMap = /* @__PURE__ */ new Map();
114
+ const addMessages = (newMessages2) => {
115
+ if (newMessages2.length === 0) return;
116
+ for (const message of newMessages2) {
117
+ messagesMap.set(message.id ?? (0, import_uuid.v4)(), message);
118
+ }
119
+ setMessages([...messagesMap.values()]);
120
+ };
121
+ addMessages([...messages, ...newMessages]);
117
122
  const abortController = new AbortController();
118
123
  abortControllerRef.current = abortController;
119
124
  const response = await stream(newMessages, {
120
125
  ...config,
121
126
  abortSignal: abortController.signal
122
127
  });
123
- const completeMessages = [];
124
- let partialMessages = /* @__PURE__ */ new Map();
125
128
  for await (const chunk of response) {
126
- if (chunk.event === "messages/partial") {
127
- if (completeMessages.length === 0) {
128
- completeMessages.push(...optimisticMessages);
129
- }
130
- for (const message of chunk.data) {
131
- if (!message.id) throw new Error("Partial message missing id");
132
- partialMessages.set(message.id, message);
133
- }
134
- } else if (chunk.event === "messages/complete") {
135
- for (const message of chunk.data) {
136
- if (message.id) {
137
- partialMessages.delete(message.id);
138
- }
139
- if (partialMessages.size > 0) {
140
- partialMessages.set(message.id ?? generateId(), message);
141
- } else {
142
- completeMessages.push(message);
143
- }
144
- }
145
- } else {
146
- continue;
129
+ if (chunk.event === "messages/partial" || chunk.event === "messages/complete") {
130
+ addMessages(chunk.data);
147
131
  }
148
- setMessages([...completeMessages, ...partialMessages.values()]);
149
132
  }
150
133
  },
151
134
  [messages, stream]
@@ -159,7 +142,7 @@ var useLangGraphMessages = ({
159
142
  };
160
143
 
161
144
  // src/useLangGraphRuntime.ts
162
- var import_react5 = require("@assistant-ui/react");
145
+ var import_react4 = require("@assistant-ui/react");
163
146
  var getPendingToolCalls = (messages) => {
164
147
  const pendingToolCalls = /* @__PURE__ */ new Map();
165
148
  for (const message of messages) {
@@ -186,13 +169,13 @@ var getMessageContent = (msg) => {
186
169
  return { type: "text", text: part.text };
187
170
  case "image":
188
171
  return { type: "image_url", image_url: { url: part.image } };
189
- case "audio":
190
- throw new Error("Audio appends are not supported yet.");
191
172
  case "tool-call":
192
- throw new Error("Tool call appends are not supported yet.");
173
+ throw new Error("Tool call appends are not supported.");
193
174
  default:
194
175
  const _exhaustiveCheck = type;
195
- throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);
176
+ throw new Error(
177
+ `Unsupported append content part type: ${_exhaustiveCheck}`
178
+ );
196
179
  }
197
180
  });
198
181
  if (content.length === 1 && content[0]?.type === "text") {
@@ -209,7 +192,7 @@ var asLangGraphRuntimeExtras = (extras) => {
209
192
  return extras;
210
193
  };
211
194
  var useLangGraphRuntimeSend = () => {
212
- const { send } = (0, import_react4.useThread)((t) => asLangGraphRuntimeExtras(t.extras));
195
+ const { send } = (0, import_react3.useThread)((t) => asLangGraphRuntimeExtras(t.extras));
213
196
  return send;
214
197
  };
215
198
  var useLangGraphRuntimeSendCommand = (command) => {
@@ -229,7 +212,7 @@ var useLangGraphRuntime = ({
229
212
  const { messages, sendMessage, cancel, setMessages } = useLangGraphMessages({
230
213
  stream
231
214
  });
232
- const [isRunning, setIsRunning] = (0, import_react3.useState)(false);
215
+ const [isRunning, setIsRunning] = (0, import_react2.useState)(false);
233
216
  const handleSendMessage = async (messages2, config) => {
234
217
  try {
235
218
  setIsRunning(true);
@@ -240,7 +223,7 @@ var useLangGraphRuntime = ({
240
223
  setIsRunning(false);
241
224
  }
242
225
  };
243
- const threadMessages = (0, import_react4.useExternalMessageConverter)({
226
+ const threadMessages = (0, import_react3.useExternalMessageConverter)({
244
227
  callback: convertLangchainMessages,
245
228
  messages,
246
229
  isRunning
@@ -250,9 +233,9 @@ var useLangGraphRuntime = ({
250
233
  "Replace unstable_allowImageAttachments with `adapters: { attachments: new SimpleImageAttachmentAdapter() }`."
251
234
  );
252
235
  if (unstable_allowImageAttachments)
253
- attachments = new import_react5.SimpleImageAttachmentAdapter();
254
- const switchToThread = !onSwitchToThread ? void 0 : async (threadId2) => {
255
- const { messages: messages2 } = await onSwitchToThread(threadId2);
236
+ attachments = new import_react4.SimpleImageAttachmentAdapter();
237
+ const switchToThread = !onSwitchToThread ? void 0 : async (externalId) => {
238
+ const { messages: messages2 } = await onSwitchToThread(externalId);
256
239
  setMessages(messages2);
257
240
  };
258
241
  const threadList = {
@@ -263,9 +246,9 @@ var useLangGraphRuntime = ({
263
246
  },
264
247
  onSwitchToThread: switchToThread
265
248
  };
266
- const loadingRef = (0, import_react3.useRef)(false);
267
- const threadListItemRuntime = (0, import_react4.useThreadListItemRuntime)({ optional: true });
268
- (0, import_react3.useEffect)(() => {
249
+ const loadingRef = (0, import_react2.useRef)(false);
250
+ const threadListItemRuntime = (0, import_react3.useThreadListItemRuntime)({ optional: true });
251
+ (0, import_react2.useEffect)(() => {
269
252
  if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;
270
253
  const externalId = threadListItemRuntime.getState().externalId;
271
254
  if (externalId) {
@@ -275,7 +258,7 @@ var useLangGraphRuntime = ({
275
258
  });
276
259
  }
277
260
  }, []);
278
- return (0, import_react4.useExternalStoreRuntime)({
261
+ return (0, import_react3.useExternalStoreRuntime)({
279
262
  isRunning,
280
263
  messages: threadMessages,
281
264
  adapters: {
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 {\n useLangGraphRuntime,\n useLangGraphRuntimeSend,\n useLangGraphRuntimeSendCommand,\n} 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 { useEffect, useRef, useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n useThread,\n useThreadListItemRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport {\n LangGraphCommand,\n LangGraphSendMessageConfig,\n LangGraphStreamCallback,\n useLangGraphMessages,\n} from \"./useLangGraphMessages\";\nimport { SimpleImageAttachmentAdapter } from \"@assistant-ui/react\";\nimport { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { AppendMessage } from \"@assistant-ui/react\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { FeedbackAdapter } from \"@assistant-ui/react\";\nimport { SpeechSynthesisAdapter } from \"@assistant-ui/react\";\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\nconst getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...msg.attachments.flatMap((a) => a.content),\n ];\n const content = allContent.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\":\n return { type: \"image_url\" as const, image_url: { url: part.image } };\n\n case \"audio\":\n throw new Error(\"Audio appends are not supported yet.\");\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported yet.\");\n\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\nconst symbolLangGraphRuntimeExtras = Symbol(\"langgraph-runtime-extras\");\ntype LangGraphRuntimeExtras = {\n [symbolLangGraphRuntimeExtras]: true;\n send: (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => Promise<void>;\n};\n\nconst asLangGraphRuntimeExtras = (extras: unknown): LangGraphRuntimeExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolLangGraphRuntimeExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useLangGraphRuntime\",\n );\n\n return extras as LangGraphRuntimeExtras;\n};\n\nexport const useLangGraphRuntimeSend = () => {\n const { send } = useThread((t) => asLangGraphRuntimeExtras(t.extras));\n return send;\n};\n\nexport const useLangGraphRuntimeSendCommand = (command: LangGraphCommand) => {\n const send = useLangGraphRuntimeSend();\n return () => send([], { command });\n};\n\nexport const useLangGraphRuntime = ({\n autoCancelPendingToolCalls,\n adapters: { attachments, feedback, speech } = {},\n unstable_allowImageAttachments,\n unstable_allowCancellation,\n stream,\n threadId,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n /**\n * @deprecated Use `adapters: { attachments: new SimpleImageAttachmentAdapter() }` instead. This option will be removed in a future version.\n */\n unstable_allowImageAttachments?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n stream: LangGraphStreamCallback<LangChainMessage>;\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n}) => {\n const { messages, sendMessage, cancel, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(messages, config);\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 if (attachments && unstable_allowImageAttachments)\n throw new Error(\n \"Replace unstable_allowImageAttachments with `adapters: { attachments: new SimpleImageAttachmentAdapter() }`.\",\n );\n if (unstable_allowImageAttachments)\n attachments = new SimpleImageAttachmentAdapter();\n\n const switchToThread = !onSwitchToThread\n ? undefined\n : async (threadId: string) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n };\n\n const threadList: NonNullable<\n ExternalStoreAdapter[\"adapters\"]\n >[\"threadList\"] = {\n threadId,\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: switchToThread,\n };\n\n const loadingRef = useRef(false);\n const threadListItemRuntime = useThreadListItemRuntime({ optional: true });\n useEffect(() => {\n if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;\n\n const externalId = threadListItemRuntime.getState().externalId;\n if (externalId) {\n loadingRef.current = true;\n switchToThread(externalId).finally(() => {\n loadingRef.current = false;\n });\n }\n }, []);\n\n return useExternalStoreRuntime({\n isRunning,\n messages: threadMessages,\n adapters: {\n attachments,\n feedback,\n speech,\n threadList,\n },\n extras: {\n [symbolLangGraphRuntimeExtras]: true,\n send: handleSendMessage,\n } satisfies LangGraphRuntimeExtras,\n onNew: (msg) => {\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 [\n ...cancellations,\n {\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n },\n );\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n // TODO parallel human in the loop calls\n await handleSendMessage(\n [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ],\n // TODO reuse runconfig here!\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\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\nconst contentToParts = (\n content: LangChainMessage[\"content\"],\n) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n return content\n .map((part): ThreadUserMessage[\"content\"][number] | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n case \"image_url\":\n if (typeof part.image_url === \"string\") {\n return { type: \"image\", image: part.image_url };\n } else {\n return {\n type: \"image\",\n image: part.image_url.url,\n };\n }\n\n case \"tool_use\":\n return null;\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n })\n .filter((a) => a !== null);\n};\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: contentToParts(message.content),\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\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, useRef } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport type LangGraphCommand = {\n resume: string;\n};\n\nexport type LangGraphSendMessageConfig = {\n command?: LangGraphCommand;\n runConfig?: unknown;\n};\n\nexport type LangGraphStreamCallback<TMessage> = (\n messages: TMessage[],\n config: LangGraphSendMessageConfig & { abortSignal: AbortSignal },\n) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n>;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\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 const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\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 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 [messages, stream],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return { messages, sendMessage, cancel, setMessages };\n};\n"],"mappings":"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","names":["import_react","import_react","import_react","messages","threadId"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["export {\n useLangGraphRuntime,\n useLangGraphRuntimeSend,\n useLangGraphRuntimeSendCommand,\n} 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 { useEffect, useRef, useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n useThread,\n useThreadListItemRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport {\n LangGraphCommand,\n LangGraphSendMessageConfig,\n LangGraphStreamCallback,\n useLangGraphMessages,\n} from \"./useLangGraphMessages\";\nimport { SimpleImageAttachmentAdapter } from \"@assistant-ui/react\";\nimport { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { AppendMessage } from \"@assistant-ui/react\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { FeedbackAdapter } from \"@assistant-ui/react\";\nimport { SpeechSynthesisAdapter } from \"@assistant-ui/react\";\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\nconst getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...msg.attachments.flatMap((a) => a.content),\n ];\n const content = allContent.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\":\n return { type: \"image_url\" as const, image_url: { url: part.image } };\n\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n\n default:\n const _exhaustiveCheck: \"file\" | \"audio\" = type;\n throw new Error(\n `Unsupported append content part type: ${_exhaustiveCheck}`,\n );\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\nconst symbolLangGraphRuntimeExtras = Symbol(\"langgraph-runtime-extras\");\ntype LangGraphRuntimeExtras = {\n [symbolLangGraphRuntimeExtras]: true;\n send: (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => Promise<void>;\n};\n\nconst asLangGraphRuntimeExtras = (extras: unknown): LangGraphRuntimeExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolLangGraphRuntimeExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useLangGraphRuntime\",\n );\n\n return extras as LangGraphRuntimeExtras;\n};\n\nexport const useLangGraphRuntimeSend = () => {\n const { send } = useThread((t) => asLangGraphRuntimeExtras(t.extras));\n return send;\n};\n\nexport const useLangGraphRuntimeSendCommand = (command: LangGraphCommand) => {\n const send = useLangGraphRuntimeSend();\n return () => send([], { command });\n};\n\nexport const useLangGraphRuntime = ({\n autoCancelPendingToolCalls,\n adapters: { attachments, feedback, speech } = {},\n unstable_allowImageAttachments,\n unstable_allowCancellation,\n stream,\n threadId,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n /**\n * @deprecated Use `adapters: { attachments: new SimpleImageAttachmentAdapter() }` instead. This option will be removed in a future version.\n */\n unstable_allowImageAttachments?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n stream: LangGraphStreamCallback<LangChainMessage>;\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n}) => {\n const { messages, sendMessage, cancel, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(messages, config);\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 if (attachments && unstable_allowImageAttachments)\n throw new Error(\n \"Replace unstable_allowImageAttachments with `adapters: { attachments: new SimpleImageAttachmentAdapter() }`.\",\n );\n if (unstable_allowImageAttachments)\n attachments = new SimpleImageAttachmentAdapter();\n\n const switchToThread = !onSwitchToThread\n ? undefined\n : async (externalId: string) => {\n const { messages } = await onSwitchToThread(externalId);\n setMessages(messages);\n };\n\n const threadList: NonNullable<\n ExternalStoreAdapter[\"adapters\"]\n >[\"threadList\"] = {\n threadId,\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: switchToThread,\n };\n\n const loadingRef = useRef(false);\n const threadListItemRuntime = useThreadListItemRuntime({ optional: true });\n useEffect(() => {\n if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;\n\n const externalId = threadListItemRuntime.getState().externalId;\n if (externalId) {\n loadingRef.current = true;\n switchToThread(externalId).finally(() => {\n loadingRef.current = false;\n });\n }\n }, []);\n\n return useExternalStoreRuntime({\n isRunning,\n messages: threadMessages,\n adapters: {\n attachments,\n feedback,\n speech,\n threadList,\n },\n extras: {\n [symbolLangGraphRuntimeExtras]: true,\n send: handleSendMessage,\n } satisfies LangGraphRuntimeExtras,\n onNew: (msg) => {\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 [\n ...cancellations,\n {\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n },\n );\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n // TODO parallel human in the loop calls\n await handleSendMessage(\n [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ],\n // TODO reuse runconfig here!\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\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\nconst contentToParts = (\n content: LangChainMessage[\"content\"],\n) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n return content\n .map((part): ThreadUserMessage[\"content\"][number] | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n case \"image_url\":\n if (typeof part.image_url === \"string\") {\n return { type: \"image\", image: part.image_url };\n } else {\n return {\n type: \"image\",\n image: part.image_url.url,\n };\n }\n\n case \"tool_use\":\n return null;\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n })\n .filter((a) => a !== null);\n};\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: contentToParts(message.content),\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\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, useRef } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\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: \"messages/partial\" | \"messages/complete\" | 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 const useLangGraphMessages = <TMessage extends { id?: string }>({\n stream,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\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 messagesMap = new Map<string, TMessage>();\n const addMessages = (newMessages: TMessage[]) => {\n if (newMessages.length === 0) return;\n for (const message of newMessages) {\n messagesMap.set(message.id ?? uuidv4(), message);\n }\n setMessages([...messagesMap.values()]);\n };\n addMessages([...messages, ...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 // TODO verify bugfix - if there are messages without IDs, they appear duplicated\n addMessages(chunk.data);\n }\n }\n },\n [messages, stream],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return { messages, sendMessage, cancel, setMessages };\n};\n"],"mappings":"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","names":["import_react","uuidv4","newMessages","import_react","messages"]}
package/dist/index.mjs CHANGED
@@ -76,8 +76,7 @@ var convertLangchainMessages = (message) => {
76
76
 
77
77
  // src/useLangGraphMessages.ts
78
78
  import { useState, useCallback, useRef } from "react";
79
- import { INTERNAL } from "@assistant-ui/react";
80
- var { generateId } = INTERNAL;
79
+ import { v4 as uuidv4 } from "uuid";
81
80
  var useLangGraphMessages = ({
82
81
  stream
83
82
  }) => {
@@ -85,42 +84,26 @@ var useLangGraphMessages = ({
85
84
  const abortControllerRef = useRef(null);
86
85
  const sendMessage = useCallback(
87
86
  async (newMessages, config) => {
88
- const optimisticMessages = [...messages, ...newMessages];
89
- if (newMessages.length > 0) {
90
- setMessages(optimisticMessages);
91
- }
87
+ newMessages = newMessages.map((m) => m.id ? m : { ...m, id: uuidv4() });
88
+ const messagesMap = /* @__PURE__ */ new Map();
89
+ const addMessages = (newMessages2) => {
90
+ if (newMessages2.length === 0) return;
91
+ for (const message of newMessages2) {
92
+ messagesMap.set(message.id ?? uuidv4(), message);
93
+ }
94
+ setMessages([...messagesMap.values()]);
95
+ };
96
+ addMessages([...messages, ...newMessages]);
92
97
  const abortController = new AbortController();
93
98
  abortControllerRef.current = abortController;
94
99
  const response = await stream(newMessages, {
95
100
  ...config,
96
101
  abortSignal: abortController.signal
97
102
  });
98
- const completeMessages = [];
99
- let partialMessages = /* @__PURE__ */ new Map();
100
103
  for await (const chunk of response) {
101
- if (chunk.event === "messages/partial") {
102
- if (completeMessages.length === 0) {
103
- completeMessages.push(...optimisticMessages);
104
- }
105
- for (const message of chunk.data) {
106
- if (!message.id) throw new Error("Partial message missing id");
107
- partialMessages.set(message.id, message);
108
- }
109
- } else if (chunk.event === "messages/complete") {
110
- for (const message of chunk.data) {
111
- if (message.id) {
112
- partialMessages.delete(message.id);
113
- }
114
- if (partialMessages.size > 0) {
115
- partialMessages.set(message.id ?? generateId(), message);
116
- } else {
117
- completeMessages.push(message);
118
- }
119
- }
120
- } else {
121
- continue;
104
+ if (chunk.event === "messages/partial" || chunk.event === "messages/complete") {
105
+ addMessages(chunk.data);
122
106
  }
123
- setMessages([...completeMessages, ...partialMessages.values()]);
124
107
  }
125
108
  },
126
109
  [messages, stream]
@@ -161,13 +144,13 @@ var getMessageContent = (msg) => {
161
144
  return { type: "text", text: part.text };
162
145
  case "image":
163
146
  return { type: "image_url", image_url: { url: part.image } };
164
- case "audio":
165
- throw new Error("Audio appends are not supported yet.");
166
147
  case "tool-call":
167
- throw new Error("Tool call appends are not supported yet.");
148
+ throw new Error("Tool call appends are not supported.");
168
149
  default:
169
150
  const _exhaustiveCheck = type;
170
- throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);
151
+ throw new Error(
152
+ `Unsupported append content part type: ${_exhaustiveCheck}`
153
+ );
171
154
  }
172
155
  });
173
156
  if (content.length === 1 && content[0]?.type === "text") {
@@ -226,8 +209,8 @@ var useLangGraphRuntime = ({
226
209
  );
227
210
  if (unstable_allowImageAttachments)
228
211
  attachments = new SimpleImageAttachmentAdapter();
229
- const switchToThread = !onSwitchToThread ? void 0 : async (threadId2) => {
230
- const { messages: messages2 } = await onSwitchToThread(threadId2);
212
+ const switchToThread = !onSwitchToThread ? void 0 : async (externalId) => {
213
+ const { messages: messages2 } = await onSwitchToThread(externalId);
231
214
  setMessages(messages2);
232
215
  };
233
216
  const threadList = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n useThread,\n useThreadListItemRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport {\n LangGraphCommand,\n LangGraphSendMessageConfig,\n LangGraphStreamCallback,\n useLangGraphMessages,\n} from \"./useLangGraphMessages\";\nimport { SimpleImageAttachmentAdapter } from \"@assistant-ui/react\";\nimport { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { AppendMessage } from \"@assistant-ui/react\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { FeedbackAdapter } from \"@assistant-ui/react\";\nimport { SpeechSynthesisAdapter } from \"@assistant-ui/react\";\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\nconst getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...msg.attachments.flatMap((a) => a.content),\n ];\n const content = allContent.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\":\n return { type: \"image_url\" as const, image_url: { url: part.image } };\n\n case \"audio\":\n throw new Error(\"Audio appends are not supported yet.\");\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported yet.\");\n\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\nconst symbolLangGraphRuntimeExtras = Symbol(\"langgraph-runtime-extras\");\ntype LangGraphRuntimeExtras = {\n [symbolLangGraphRuntimeExtras]: true;\n send: (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => Promise<void>;\n};\n\nconst asLangGraphRuntimeExtras = (extras: unknown): LangGraphRuntimeExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolLangGraphRuntimeExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useLangGraphRuntime\",\n );\n\n return extras as LangGraphRuntimeExtras;\n};\n\nexport const useLangGraphRuntimeSend = () => {\n const { send } = useThread((t) => asLangGraphRuntimeExtras(t.extras));\n return send;\n};\n\nexport const useLangGraphRuntimeSendCommand = (command: LangGraphCommand) => {\n const send = useLangGraphRuntimeSend();\n return () => send([], { command });\n};\n\nexport const useLangGraphRuntime = ({\n autoCancelPendingToolCalls,\n adapters: { attachments, feedback, speech } = {},\n unstable_allowImageAttachments,\n unstable_allowCancellation,\n stream,\n threadId,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n /**\n * @deprecated Use `adapters: { attachments: new SimpleImageAttachmentAdapter() }` instead. This option will be removed in a future version.\n */\n unstable_allowImageAttachments?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n stream: LangGraphStreamCallback<LangChainMessage>;\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n}) => {\n const { messages, sendMessage, cancel, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(messages, config);\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 if (attachments && unstable_allowImageAttachments)\n throw new Error(\n \"Replace unstable_allowImageAttachments with `adapters: { attachments: new SimpleImageAttachmentAdapter() }`.\",\n );\n if (unstable_allowImageAttachments)\n attachments = new SimpleImageAttachmentAdapter();\n\n const switchToThread = !onSwitchToThread\n ? undefined\n : async (threadId: string) => {\n const { messages } = await onSwitchToThread(threadId);\n setMessages(messages);\n };\n\n const threadList: NonNullable<\n ExternalStoreAdapter[\"adapters\"]\n >[\"threadList\"] = {\n threadId,\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: switchToThread,\n };\n\n const loadingRef = useRef(false);\n const threadListItemRuntime = useThreadListItemRuntime({ optional: true });\n useEffect(() => {\n if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;\n\n const externalId = threadListItemRuntime.getState().externalId;\n if (externalId) {\n loadingRef.current = true;\n switchToThread(externalId).finally(() => {\n loadingRef.current = false;\n });\n }\n }, []);\n\n return useExternalStoreRuntime({\n isRunning,\n messages: threadMessages,\n adapters: {\n attachments,\n feedback,\n speech,\n threadList,\n },\n extras: {\n [symbolLangGraphRuntimeExtras]: true,\n send: handleSendMessage,\n } satisfies LangGraphRuntimeExtras,\n onNew: (msg) => {\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 [\n ...cancellations,\n {\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n },\n );\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n // TODO parallel human in the loop calls\n await handleSendMessage(\n [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ],\n // TODO reuse runconfig here!\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\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\nconst contentToParts = (\n content: LangChainMessage[\"content\"],\n) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n return content\n .map((part): ThreadUserMessage[\"content\"][number] | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n case \"image_url\":\n if (typeof part.image_url === \"string\") {\n return { type: \"image\", image: part.image_url };\n } else {\n return {\n type: \"image\",\n image: part.image_url.url,\n };\n }\n\n case \"tool_use\":\n return null;\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n })\n .filter((a) => a !== null);\n};\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: contentToParts(message.content),\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\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, useRef } from \"react\";\nimport { INTERNAL } from \"@assistant-ui/react\";\n\nconst { generateId } = INTERNAL;\n\nexport type LangGraphCommand = {\n resume: string;\n};\n\nexport type LangGraphSendMessageConfig = {\n command?: LangGraphCommand;\n runConfig?: unknown;\n};\n\nexport type LangGraphStreamCallback<TMessage> = (\n messages: TMessage[],\n config: LangGraphSendMessageConfig & { abortSignal: AbortSignal },\n) => Promise<\n AsyncGenerator<{\n event: string;\n data: any;\n }>\n>;\n\nexport const useLangGraphMessages = <TMessage>({\n stream,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\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 const optimisticMessages = [...messages, ...newMessages];\n if (newMessages.length > 0) {\n setMessages(optimisticMessages);\n }\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 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 [messages, stream],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return { messages, sendMessage, cancel, setMessages };\n};\n"],"mappings":";AAAA,SAAS,WAAW,UAAAA,SAAQ,YAAAC,iBAAgB;AAE5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACAP,IAAM,iBAAiB,CACrB,YACG;AACH,MAAI,OAAO,YAAY;AACrB,WAAO,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC;AAClD,SAAO,QACJ,IAAI,CAAC,SAAsD;AAC1D,UAAM,OAAO,KAAK;AAClB,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK;AAAA,MACzC,KAAK;AACH,YAAI,OAAO,KAAK,cAAc,UAAU;AACtC,iBAAO,EAAE,MAAM,SAAS,OAAO,KAAK,UAAU;AAAA,QAChD,OAAO;AACL,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO,KAAK,UAAU;AAAA,UACxB;AAAA,QACF;AAAA,MAEF,KAAK;AACH,eAAO;AAAA,MACT;AACE,cAAM,mBAA0B;AAChC,cAAM,IAAI,MAAM,8BAA8B,gBAAgB,EAAE;AAAA,IACpE;AAAA,EACF,CAAC,EACA,OAAO,CAAC,MAAM,MAAM,IAAI;AAC7B;AAEO,IAAM,2BAET,CAAC,YAAY;AACf,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,IAAI,QAAQ;AAAA,QACZ,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAAA,MACnD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,IAAI,QAAQ;AAAA,QACZ,SAAS,eAAe,QAAQ,OAAO;AAAA,MACzC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,IAAI,QAAQ;AAAA,QACZ,SAAS;AAAA,UACP,GAAG,eAAe,QAAQ,OAAO;AAAA,UACjC,GAAI,QAAQ,YAAY;AAAA,YACtB,CAAC,WAAgC;AAAA,cAC/B,MAAM;AAAA,cACN,YAAY,MAAM;AAAA,cAClB,UAAU,MAAM;AAAA,cAChB,MAAM,MAAM;AAAA,cACZ,UACE,QAAQ,kBAAkB,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,GACnD,QAAQ,KAAK,UAAU,MAAM,IAAI;AAAA,YACzC;AAAA,UACF,KAAK,CAAC;AAAA,QACR;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,YAAY,QAAQ;AAAA,QACpB,QAAQ,QAAQ;AAAA,MAClB;AAAA,EACJ;AACF;;;ACjFA,SAAS,UAAU,aAAa,cAAc;AAC9C,SAAS,gBAAgB;AAEzB,IAAM,EAAE,WAAW,IAAI;AAqBhB,IAAM,uBAAuB,CAAW;AAAA,EAC7C;AACF,MAEM;AACJ,QAAM,CAAC,UAAU,WAAW,IAAI,SAAqB,CAAC,CAAC;AACvD,QAAM,qBAAqB,OAA+B,IAAI;AAE9D,QAAM,cAAc;AAAA,IAClB,OAAO,aAAyB,WAAuC;AACrE,YAAM,qBAAqB,CAAC,GAAG,UAAU,GAAG,WAAW;AACvD,UAAI,YAAY,SAAS,GAAG;AAC1B,oBAAY,kBAAkB;AAAA,MAChC;AAEA,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,YAAM,mBAA+B,CAAC;AACtC,UAAI,kBAAyC,oBAAI,IAAI;AACrD,uBAAiB,SAAS,UAAU;AAClC,YAAI,MAAM,UAAU,oBAAoB;AAEtC,cAAI,iBAAiB,WAAW,GAAG;AACjC,6BAAiB,KAAK,GAAG,kBAAkB;AAAA,UAC7C;AAEA,qBAAW,WAAW,MAAM,MAAM;AAChC,gBAAI,CAAC,QAAQ,GAAI,OAAM,IAAI,MAAM,4BAA4B;AAE7D,4BAAgB,IAAI,QAAQ,IAAI,OAAO;AAAA,UACzC;AAAA,QACF,WAAW,MAAM,UAAU,qBAAqB;AAC9C,qBAAW,WAAW,MAAM,MAAM;AAChC,gBAAI,QAAQ,IAAI;AACd,8BAAgB,OAAO,QAAQ,EAAE;AAAA,YACnC;AAIA,gBAAI,gBAAgB,OAAO,GAAG;AAC5B,8BAAgB,IAAI,QAAQ,MAAM,WAAW,GAAG,OAAO;AAAA,YACzD,OAAO;AACL,+BAAiB,KAAK,OAAO;AAAA,YAC/B;AAAA,UACF;AAAA,QACF,OAAO;AACL;AAAA,QACF;AAEA,oBAAY,CAAC,GAAG,kBAAkB,GAAG,gBAAgB,OAAO,CAAC,CAAC;AAAA,MAChE;AAAA,IAIF;AAAA,IACA,CAAC,UAAU,MAAM;AAAA,EACnB;AAEA,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,mBAAmB,SAAS;AAC9B,yBAAmB,QAAQ,MAAM;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,kBAAkB,CAAC;AAEvB,SAAO,EAAE,UAAU,aAAa,QAAQ,YAAY;AACtD;;;AF/EA,SAAS,oCAAoC;AAO7C,IAAM,sBAAsB,CAAC,aAAiC;AAC5D,QAAM,mBAAmB,oBAAI,IAA+B;AAC5D,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,MAAM;AACzB,iBAAW,YAAY,QAAQ,cAAc,CAAC,GAAG;AAC/C,yBAAiB,IAAI,SAAS,IAAI,QAAQ;AAAA,MAC5C;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,QAAQ;AAC3B,uBAAiB,OAAO,QAAQ,YAAY;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,iBAAiB,OAAO,CAAC;AACtC;AAEA,IAAM,oBAAoB,CAAC,QAAuB;AAChD,QAAM,aAAa;AAAA,IACjB,GAAG,IAAI;AAAA,IACP,GAAG,IAAI,YAAY,QAAQ,CAAC,MAAM,EAAE,OAAO;AAAA,EAC7C;AACA,QAAM,UAAU,WAAW,IAAI,CAAC,SAAS;AACvC,UAAM,OAAO,KAAK;AAClB,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO,EAAE,MAAM,QAAiB,MAAM,KAAK,KAAK;AAAA,MAClD,KAAK;AACH,eAAO,EAAE,MAAM,aAAsB,WAAW,EAAE,KAAK,KAAK,MAAM,EAAE;AAAA,MAEtE,KAAK;AACH,cAAM,IAAI,MAAM,sCAAsC;AAAA,MACxD,KAAK;AACH,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAE5D;AACE,cAAM,mBAA0B;AAChC,cAAM,IAAI,MAAM,8BAA8B,gBAAgB,EAAE;AAAA,IACpE;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,WAAW,KAAK,QAAQ,CAAC,GAAG,SAAS,QAAQ;AACvD,WAAO,QAAQ,CAAC,EAAE,QAAQ;AAAA,EAC5B;AAEA,SAAO;AACT;AAEA,IAAM,+BAA+B,OAAO,0BAA0B;AAStE,IAAM,2BAA2B,CAAC,WAA4C;AAC5E,MACE,OAAO,WAAW,YAClB,UAAU,QACV,EAAE,gCAAgC;AAElC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAEF,SAAO;AACT;AAEO,IAAM,0BAA0B,MAAM;AAC3C,QAAM,EAAE,KAAK,IAAI,UAAU,CAAC,MAAM,yBAAyB,EAAE,MAAM,CAAC;AACpE,SAAO;AACT;AAEO,IAAM,iCAAiC,CAAC,YAA8B;AAC3E,QAAM,OAAO,wBAAwB;AACrC,SAAO,MAAM,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC;AACnC;AAEO,IAAM,sBAAsB,CAAC;AAAA,EAClC;AAAA,EACA,UAAU,EAAE,aAAa,UAAU,OAAO,IAAI,CAAC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MA0BM;AACJ,QAAM,EAAE,UAAU,aAAa,QAAQ,YAAY,IAAI,qBAAqB;AAAA,IAC1E;AAAA,EACF,CAAC;AAED,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,QAAM,oBAAoB,OACxBC,WACA,WACG;AACH,QAAI;AACF,mBAAa,IAAI;AACjB,YAAM,YAAYA,WAAU,MAAM;AAAA,IACpC,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,iBAAiB,4BAA4B;AAAA,IACjD,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,eAAe;AACjB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AACF,MAAI;AACF,kBAAc,IAAI,6BAA6B;AAEjD,QAAM,iBAAiB,CAAC,mBACpB,SACA,OAAOC,cAAqB;AAC1B,UAAM,EAAE,UAAAD,UAAS,IAAI,MAAM,iBAAiBC,SAAQ;AACpD,gBAAYD,SAAQ;AAAA,EACtB;AAEJ,QAAM,aAEY;AAAA,IAChB;AAAA,IACA,qBAAqB,CAAC,sBAClB,SACA,YAAY;AACV,YAAM,oBAAoB;AAC1B,kBAAY,CAAC,CAAC;AAAA,IAChB;AAAA,IACJ,kBAAkB;AAAA,EACpB;AAEA,QAAM,aAAaE,QAAO,KAAK;AAC/B,QAAM,wBAAwB,yBAAyB,EAAE,UAAU,KAAK,CAAC;AACzE,YAAU,MAAM;AACd,QAAI,CAAC,yBAAyB,CAAC,kBAAkB,WAAW,QAAS;AAErE,UAAM,aAAa,sBAAsB,SAAS,EAAE;AACpD,QAAI,YAAY;AACd,iBAAW,UAAU;AACrB,qBAAe,UAAU,EAAE,QAAQ,MAAM;AACvC,mBAAW,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,wBAAwB;AAAA,IAC7B;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,CAAC,4BAA4B,GAAG;AAAA,MAChC,MAAM;AAAA,IACR;AAAA,IACA,OAAO,CAAC,QAAQ;AACd,YAAM,gBACJ,+BAA+B,QAC3B,oBAAoB,QAAQ,EAAE;AAAA,QAC5B,CAAC,OACE;AAAA,UACC,MAAM;AAAA,UACN,MAAM,EAAE;AAAA,UACR,cAAc,EAAE;AAAA,UAChB,SAAS,KAAK,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,QAC7C;AAAA,MACJ,IACA,CAAC;AAEP,aAAO;AAAA,QACL;AAAA,UACE,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS,kBAAkB,GAAG;AAAA,UAChC;AAAA,QACF;AAAA,QACA;AAAA,UACE,WAAW,IAAI;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,IACA,iBAAiB,OAAO,EAAE,YAAY,UAAU,OAAO,MAAM;AAE3D,YAAM;AAAA,QACJ;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,cAAc;AAAA,YACd,SAAS,KAAK,UAAU,MAAM;AAAA,UAChC;AAAA,QACF;AAAA;AAAA,QAEA,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,UAAU,6BACN,YAAY;AACV,aAAO;AAAA,IACT,IACA;AAAA,EACN,CAAC;AACH;","names":["useRef","useState","useState","messages","threadId","useRef"]}
1
+ {"version":3,"sources":["../src/useLangGraphRuntime.ts","../src/convertLangchainMessages.ts","../src/useLangGraphMessages.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { LangChainMessage, LangChainToolCall } from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n useThread,\n useThreadListItemRuntime,\n} from \"@assistant-ui/react\";\nimport { convertLangchainMessages } from \"./convertLangchainMessages\";\nimport {\n LangGraphCommand,\n LangGraphSendMessageConfig,\n LangGraphStreamCallback,\n useLangGraphMessages,\n} from \"./useLangGraphMessages\";\nimport { SimpleImageAttachmentAdapter } from \"@assistant-ui/react\";\nimport { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { AppendMessage } from \"@assistant-ui/react\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { FeedbackAdapter } from \"@assistant-ui/react\";\nimport { SpeechSynthesisAdapter } from \"@assistant-ui/react\";\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\nconst getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...msg.attachments.flatMap((a) => a.content),\n ];\n const content = allContent.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\":\n return { type: \"image_url\" as const, image_url: { url: part.image } };\n\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n\n default:\n const _exhaustiveCheck: \"file\" | \"audio\" = type;\n throw new Error(\n `Unsupported append content part type: ${_exhaustiveCheck}`,\n );\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\nconst symbolLangGraphRuntimeExtras = Symbol(\"langgraph-runtime-extras\");\ntype LangGraphRuntimeExtras = {\n [symbolLangGraphRuntimeExtras]: true;\n send: (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => Promise<void>;\n};\n\nconst asLangGraphRuntimeExtras = (extras: unknown): LangGraphRuntimeExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolLangGraphRuntimeExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useLangGraphRuntime\",\n );\n\n return extras as LangGraphRuntimeExtras;\n};\n\nexport const useLangGraphRuntimeSend = () => {\n const { send } = useThread((t) => asLangGraphRuntimeExtras(t.extras));\n return send;\n};\n\nexport const useLangGraphRuntimeSendCommand = (command: LangGraphCommand) => {\n const send = useLangGraphRuntimeSend();\n return () => send([], { command });\n};\n\nexport const useLangGraphRuntime = ({\n autoCancelPendingToolCalls,\n adapters: { attachments, feedback, speech } = {},\n unstable_allowImageAttachments,\n unstable_allowCancellation,\n stream,\n threadId,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n threadId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n /**\n * @deprecated Use `adapters: { attachments: new SimpleImageAttachmentAdapter() }` instead. This option will be removed in a future version.\n */\n unstable_allowImageAttachments?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n stream: LangGraphStreamCallback<LangChainMessage>;\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (\n threadId: string,\n ) => Promise<{ messages: LangChainMessage[] }>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n}) => {\n const { messages, sendMessage, cancel, setMessages } = useLangGraphMessages({\n stream,\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (\n messages: LangChainMessage[],\n config: LangGraphSendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(messages, config);\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 if (attachments && unstable_allowImageAttachments)\n throw new Error(\n \"Replace unstable_allowImageAttachments with `adapters: { attachments: new SimpleImageAttachmentAdapter() }`.\",\n );\n if (unstable_allowImageAttachments)\n attachments = new SimpleImageAttachmentAdapter();\n\n const switchToThread = !onSwitchToThread\n ? undefined\n : async (externalId: string) => {\n const { messages } = await onSwitchToThread(externalId);\n setMessages(messages);\n };\n\n const threadList: NonNullable<\n ExternalStoreAdapter[\"adapters\"]\n >[\"threadList\"] = {\n threadId,\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n },\n onSwitchToThread: switchToThread,\n };\n\n const loadingRef = useRef(false);\n const threadListItemRuntime = useThreadListItemRuntime({ optional: true });\n useEffect(() => {\n if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;\n\n const externalId = threadListItemRuntime.getState().externalId;\n if (externalId) {\n loadingRef.current = true;\n switchToThread(externalId).finally(() => {\n loadingRef.current = false;\n });\n }\n }, []);\n\n return useExternalStoreRuntime({\n isRunning,\n messages: threadMessages,\n adapters: {\n attachments,\n feedback,\n speech,\n threadList,\n },\n extras: {\n [symbolLangGraphRuntimeExtras]: true,\n send: handleSendMessage,\n } satisfies LangGraphRuntimeExtras,\n onNew: (msg) => {\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 [\n ...cancellations,\n {\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n },\n );\n },\n onAddToolResult: async ({ toolCallId, toolName, result }) => {\n // TODO parallel human in the loop calls\n await handleSendMessage(\n [\n {\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n },\n ],\n // TODO reuse runconfig here!\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\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\nconst contentToParts = (\n content: LangChainMessage[\"content\"],\n) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n return content\n .map((part): ThreadUserMessage[\"content\"][number] | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n case \"image_url\":\n if (typeof part.image_url === \"string\") {\n return { type: \"image\", image: part.image_url };\n } else {\n return {\n type: \"image\",\n image: part.image_url.url,\n };\n }\n\n case \"tool_use\":\n return null;\n default:\n const _exhaustiveCheck: never = type;\n throw new Error(`Unknown content part type: ${_exhaustiveCheck}`);\n }\n })\n .filter((a) => a !== null);\n};\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: contentToParts(message.content),\n };\n case \"ai\":\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\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, useRef } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\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: \"messages/partial\" | \"messages/complete\" | 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 const useLangGraphMessages = <TMessage extends { id?: string }>({\n stream,\n}: {\n stream: LangGraphStreamCallback<TMessage>;\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 messagesMap = new Map<string, TMessage>();\n const addMessages = (newMessages: TMessage[]) => {\n if (newMessages.length === 0) return;\n for (const message of newMessages) {\n messagesMap.set(message.id ?? uuidv4(), message);\n }\n setMessages([...messagesMap.values()]);\n };\n addMessages([...messages, ...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 // TODO verify bugfix - if there are messages without IDs, they appear duplicated\n addMessages(chunk.data);\n }\n }\n },\n [messages, stream],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return { messages, sendMessage, cancel, setMessages };\n};\n"],"mappings":"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","names":["useRef","useState","newMessages","useState","messages","useRef"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-langgraph",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {
@@ -24,10 +24,11 @@
24
24
  ],
25
25
  "sideEffects": false,
26
26
  "dependencies": {
27
+ "uuid": "^11.0.5",
27
28
  "zod": "^3.24.1"
28
29
  },
29
30
  "peerDependencies": {
30
- "@assistant-ui/react": "^0.7.35",
31
+ "@assistant-ui/react": "^0.7.39",
31
32
  "@types/react": "*",
32
33
  "react": "^18 || ^19 || ^19.0.0-rc"
33
34
  },
@@ -37,10 +38,10 @@
37
38
  }
38
39
  },
39
40
  "devDependencies": {
40
- "eslint-config-next": "15.1.4",
41
+ "eslint-config-next": "15.1.5",
41
42
  "tsup": "8.3.5",
42
- "@assistant-ui/react": "0.7.35",
43
- "@assistant-ui/tsconfig": "0.0.0"
43
+ "@assistant-ui/tsconfig": "0.0.0",
44
+ "@assistant-ui/react": "0.7.39"
44
45
  },
45
46
  "publishConfig": {
46
47
  "access": "public",