@assistant-ui/react-langgraph 0.0.9 → 0.0.11

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
@@ -53,7 +53,7 @@ declare const useLangGraphMessages: <TMessage>({ stream, }: {
53
53
  }>>;
54
54
  }) => {
55
55
  messages: TMessage[];
56
- sendMessage: (messages: TMessage[]) => Promise<void>;
56
+ sendMessage: (newMessages: TMessage[]) => Promise<void>;
57
57
  setMessages: react.Dispatch<react.SetStateAction<TMessage[]>>;
58
58
  };
59
59
 
package/dist/index.d.ts CHANGED
@@ -53,7 +53,7 @@ declare const useLangGraphMessages: <TMessage>({ stream, }: {
53
53
  }>>;
54
54
  }) => {
55
55
  messages: TMessage[];
56
- sendMessage: (messages: TMessage[]) => Promise<void>;
56
+ sendMessage: (newMessages: TMessage[]) => Promise<void>;
57
57
  setMessages: react.Dispatch<react.SetStateAction<TMessage[]>>;
58
58
  };
59
59
 
package/dist/index.js CHANGED
@@ -27,8 +27,8 @@ __export(src_exports, {
27
27
  module.exports = __toCommonJS(src_exports);
28
28
 
29
29
  // src/useLangGraphRuntime.ts
30
- var import_react2 = require("react");
31
- var import_react3 = require("@assistant-ui/react");
30
+ var import_react3 = require("react");
31
+ var import_react4 = require("@assistant-ui/react");
32
32
 
33
33
  // src/convertLangchainMessages.ts
34
34
  var convertLangchainMessages = (message) => {
@@ -77,41 +77,50 @@ var convertLangchainMessages = (message) => {
77
77
 
78
78
  // src/useLangGraphMessages.ts
79
79
  var import_react = require("react");
80
+ var import_react2 = require("@assistant-ui/react");
81
+ var { generateId } = import_react2.INTERNAL;
80
82
  var useLangGraphMessages = ({
81
83
  stream
82
84
  }) => {
83
85
  const [messages, setMessages] = (0, import_react.useState)([]);
84
86
  const sendMessage = (0, import_react.useCallback)(
85
- async (messages2) => {
86
- if (messages2.length > 0) {
87
- setMessages((currentMessages) => [...currentMessages, ...messages2]);
87
+ async (newMessages) => {
88
+ const optimisticMessages = [...messages, ...newMessages];
89
+ if (newMessages.length > 0) {
90
+ setMessages(optimisticMessages);
88
91
  }
89
- const response = await stream(messages2);
92
+ const response = await stream(newMessages);
90
93
  const completeMessages = [];
91
94
  let partialMessages = /* @__PURE__ */ new Map();
92
95
  for await (const chunk of response) {
93
96
  if (chunk.event === "messages/partial") {
97
+ if (completeMessages.length === 0) {
98
+ completeMessages.push(...optimisticMessages);
99
+ }
94
100
  for (const message of chunk.data) {
95
101
  if (!message.id) throw new Error("Partial message missing id");
96
102
  partialMessages.set(message.id, message);
97
103
  }
98
104
  } else if (chunk.event === "messages/complete") {
99
105
  for (const message of chunk.data) {
100
- if (!message.id) continue;
101
- partialMessages.delete(message.id);
106
+ if (message.id) {
107
+ partialMessages.delete(message.id);
108
+ }
109
+ if (partialMessages.size > 0) {
110
+ partialMessages.set(message.id ?? generateId(), message);
111
+ } else {
112
+ completeMessages.push(message);
113
+ }
102
114
  }
103
- completeMessages.push(...chunk.data);
104
115
  } else {
105
116
  continue;
106
117
  }
107
118
  setMessages([...completeMessages, ...partialMessages.values()]);
108
119
  }
109
- if (partialMessages.size > 0) {
110
- throw new Error("A partial message was not marked as complete");
111
- }
112
120
  },
113
121
  [stream]
114
122
  );
123
+ console.log("messages", messages);
115
124
  return { messages, sendMessage, setMessages };
116
125
  };
117
126
 
@@ -139,7 +148,7 @@ var useLangGraphRuntime = ({
139
148
  const { messages, sendMessage, setMessages } = useLangGraphMessages({
140
149
  stream
141
150
  });
142
- const [isRunning, setIsRunning] = (0, import_react2.useState)(false);
151
+ const [isRunning, setIsRunning] = (0, import_react3.useState)(false);
143
152
  const handleSendMessage = async (messages2) => {
144
153
  try {
145
154
  setIsRunning(true);
@@ -150,12 +159,12 @@ var useLangGraphRuntime = ({
150
159
  setIsRunning(false);
151
160
  }
152
161
  };
153
- const threadMessages = (0, import_react3.useExternalMessageConverter)({
162
+ const threadMessages = (0, import_react4.useExternalMessageConverter)({
154
163
  callback: convertLangchainMessages,
155
164
  messages,
156
165
  isRunning
157
166
  });
158
- return (0, import_react3.useExternalStoreRuntime)({
167
+ return (0, import_react4.useExternalStoreRuntime)({
159
168
  threadId,
160
169
  isRunning,
161
170
  messages: threadMessages,
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 stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | 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 = 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 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\";\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 (messages: TMessage[]) => {\n if (messages.length > 0) {\n setMessages((currentMessages) => [...currentMessages, ...messages]);\n }\n\n const response = await stream(messages);\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 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) continue;\n partialMessages.delete(message.id);\n }\n\n completeMessages.push(...chunk.data);\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\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAyB;AAEzB,IAAAA,gBAGO;;;ACCA,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,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;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,QAAQ;AAAA,UAChB;AAAA,UACA,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;;;ACpDA,mBAAsC;AAE/B,IAAM,uBAAuB,CAAW;AAAA,EAC7C;AACF,MAOM;AACJ,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAqB,CAAC,CAAC;AAEvD,QAAM,kBAAc;AAAA,IAClB,OAAOC,cAAyB;AAC9B,UAAIA,UAAS,SAAS,GAAG;AACvB,oBAAY,CAAC,oBAAoB,CAAC,GAAG,iBAAiB,GAAGA,SAAQ,CAAC;AAAA,MACpE;AAEA,YAAM,WAAW,MAAM,OAAOA,SAAQ;AAEtC,YAAM,mBAA+B,CAAC;AACtC,UAAI,kBAAyC,oBAAI,IAAI;AACrD,uBAAiB,SAAS,UAAU;AAClC,YAAI,MAAM,UAAU,oBAAoB;AACtC,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,CAAC,QAAQ,GAAI;AACjB,4BAAgB,OAAO,QAAQ,EAAE;AAAA,UACnC;AAEA,2BAAiB,KAAK,GAAG,MAAM,IAAI;AAAA,QACrC,OAAO;AACL;AAAA,QACF;AAEA,oBAAY,CAAC,GAAG,kBAAkB,GAAG,gBAAgB,OAAO,CAAC,CAAC;AAAA,MAChE;AACA,UAAI,gBAAgB,OAAO,GAAG;AAC5B,cAAM,IAAI,MAAM,8CAA8C;AAAA,MAChE;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,EAAE,UAAU,aAAa,YAAY;AAC9C;;;AF3CA,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;AAEO,IAAM,sBAAsB,CAAC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAYM;AACJ,QAAM,EAAE,UAAU,aAAa,YAAY,IAAI,qBAAqB;AAAA,IAClE;AAAA,EACF,CAAC;AAED,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,oBAAoB,OAAOC,cAAiC;AAChE,QAAI;AACF,mBAAa,IAAI;AACjB,YAAM,YAAYA,SAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,qBAAiB,2CAA4B;AAAA,IACjD,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF,CAAC;AAED,aAAO,uCAAwB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,OAAO,CAAC,QAAQ;AACd,UAAI,IAAI,QAAQ,WAAW,KAAK,IAAI,QAAQ,CAAC,GAAG,SAAS;AACvD,cAAM,IAAI,MAAM,kCAAkC;AAEpD,YAAM,gBAAgB,oBAAoB,QAAQ,EAAE;AAAA,QAClD,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;AACA,aAAO,kBAAkB;AAAA,QACvB,GAAG;AAAA,QACH;AAAA,UACE,MAAM;AAAA,UACN,SAAS,IAAI,QAAQ,CAAC,EAAE;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,iBAAiB,OAAO,EAAE,YAAY,UAAU,OAAO,MAAM;AAC3D,YAAM,kBAAkB;AAAA,QACtB;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,cAAc;AAAA,UACd,SAAS,KAAK,UAAU,MAAM;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,qBAAqB,CAAC,sBAClB,SACA,YAAY;AACV,YAAM,oBAAoB;AAC1B,kBAAY,CAAC,CAAC;AAAA,IAChB;AAAA,IACJ,kBAAkB,CAAC,mBACf,SACA,OAAOC,cAAa;AAClB,YAAM,EAAE,UAAAD,UAAS,IAAI,MAAM,iBAAiBC,SAAQ;AACpD,kBAAYD,SAAQ;AAAA,IACtB;AAAA,EACN,CAAC;AACH;","names":["import_react","messages","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 stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | 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 = 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 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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAyB;AAEzB,IAAAA,gBAGO;;;ACCA,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,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;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,QAAQ;AAAA,UAChB;AAAA,UACA,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;;;ACpDA,mBAAsC;AACtC,IAAAC,gBAAyB;AAEzB,IAAM,EAAE,WAAW,IAAI;AAEhB,IAAM,uBAAuB,CAAW;AAAA,EAC7C;AACF,MAOM;AACJ,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAqB,CAAC,CAAC;AAEvD,QAAM,kBAAc;AAAA,IAClB,OAAO,gBAA4B;AACjC,YAAM,qBAAqB,CAAC,GAAG,UAAU,GAAG,WAAW;AACvD,UAAI,YAAY,SAAS,GAAG;AAC1B,oBAAY,kBAAkB;AAAA,MAChC;AAEA,YAAM,WAAW,MAAM,OAAO,WAAW;AAEzC,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,MAAM;AAAA,EACT;AACA,UAAQ,IAAI,YAAY,QAAQ;AAChC,SAAO,EAAE,UAAU,aAAa,YAAY;AAC9C;;;AF3DA,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;AAEO,IAAM,sBAAsB,CAAC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAYM;AACJ,QAAM,EAAE,UAAU,aAAa,YAAY,IAAI,qBAAqB;AAAA,IAClE;AAAA,EACF,CAAC;AAED,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,oBAAoB,OAAOC,cAAiC;AAChE,QAAI;AACF,mBAAa,IAAI;AACjB,YAAM,YAAYA,SAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,qBAAiB,2CAA4B;AAAA,IACjD,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF,CAAC;AAED,aAAO,uCAAwB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,OAAO,CAAC,QAAQ;AACd,UAAI,IAAI,QAAQ,WAAW,KAAK,IAAI,QAAQ,CAAC,GAAG,SAAS;AACvD,cAAM,IAAI,MAAM,kCAAkC;AAEpD,YAAM,gBAAgB,oBAAoB,QAAQ,EAAE;AAAA,QAClD,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;AACA,aAAO,kBAAkB;AAAA,QACvB,GAAG;AAAA,QACH;AAAA,UACE,MAAM;AAAA,UACN,SAAS,IAAI,QAAQ,CAAC,EAAE;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,iBAAiB,OAAO,EAAE,YAAY,UAAU,OAAO,MAAM;AAC3D,YAAM,kBAAkB;AAAA,QACtB;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,cAAc;AAAA,UACd,SAAS,KAAK,UAAU,MAAM;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,qBAAqB,CAAC,sBAClB,SACA,YAAY;AACV,YAAM,oBAAoB;AAC1B,kBAAY,CAAC,CAAC;AAAA,IAChB;AAAA,IACJ,kBAAkB,CAAC,mBACf,SACA,OAAOC,cAAa;AAClB,YAAM,EAAE,UAAAD,UAAS,IAAI,MAAM,iBAAiBC,SAAQ;AACpD,kBAAYD,SAAQ;AAAA,IACtB;AAAA,EACN,CAAC;AACH;","names":["import_react","import_react","messages","threadId"]}
package/dist/index.mjs CHANGED
@@ -52,41 +52,50 @@ var convertLangchainMessages = (message) => {
52
52
 
53
53
  // src/useLangGraphMessages.ts
54
54
  import { useState, useCallback } from "react";
55
+ import { INTERNAL } from "@assistant-ui/react";
56
+ var { generateId } = INTERNAL;
55
57
  var useLangGraphMessages = ({
56
58
  stream
57
59
  }) => {
58
60
  const [messages, setMessages] = useState([]);
59
61
  const sendMessage = useCallback(
60
- async (messages2) => {
61
- if (messages2.length > 0) {
62
- setMessages((currentMessages) => [...currentMessages, ...messages2]);
62
+ async (newMessages) => {
63
+ const optimisticMessages = [...messages, ...newMessages];
64
+ if (newMessages.length > 0) {
65
+ setMessages(optimisticMessages);
63
66
  }
64
- const response = await stream(messages2);
67
+ const response = await stream(newMessages);
65
68
  const completeMessages = [];
66
69
  let partialMessages = /* @__PURE__ */ new Map();
67
70
  for await (const chunk of response) {
68
71
  if (chunk.event === "messages/partial") {
72
+ if (completeMessages.length === 0) {
73
+ completeMessages.push(...optimisticMessages);
74
+ }
69
75
  for (const message of chunk.data) {
70
76
  if (!message.id) throw new Error("Partial message missing id");
71
77
  partialMessages.set(message.id, message);
72
78
  }
73
79
  } else if (chunk.event === "messages/complete") {
74
80
  for (const message of chunk.data) {
75
- if (!message.id) continue;
76
- partialMessages.delete(message.id);
81
+ if (message.id) {
82
+ partialMessages.delete(message.id);
83
+ }
84
+ if (partialMessages.size > 0) {
85
+ partialMessages.set(message.id ?? generateId(), message);
86
+ } else {
87
+ completeMessages.push(message);
88
+ }
77
89
  }
78
- completeMessages.push(...chunk.data);
79
90
  } else {
80
91
  continue;
81
92
  }
82
93
  setMessages([...completeMessages, ...partialMessages.values()]);
83
94
  }
84
- if (partialMessages.size > 0) {
85
- throw new Error("A partial message was not marked as complete");
86
- }
87
95
  },
88
96
  [stream]
89
97
  );
98
+ console.log("messages", messages);
90
99
  return { messages, sendMessage, setMessages };
91
100
  };
92
101
 
@@ -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 stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | 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 = 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 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\";\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 (messages: TMessage[]) => {\n if (messages.length > 0) {\n setMessages((currentMessages) => [...currentMessages, ...messages]);\n }\n\n const response = await stream(messages);\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 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) continue;\n partialMessages.delete(message.id);\n }\n\n completeMessages.push(...chunk.data);\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\n return { messages, sendMessage, setMessages };\n};\n"],"mappings":"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","names":["useState","messages","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 stream,\n onSwitchToNewThread,\n onSwitchToThread,\n}: {\n threadId?: string | 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 = 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 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-langgraph",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {