@assistant-ui/react-langchain 0.0.19 → 0.0.21

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.
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessages.d.ts","names":[],"sources":["../src/convertMessages.ts"],"mappings":";;;;KAeK,oCACH,4BAA4B;EAC1B,qBAAqB,YAAY;EACjC,gBAAgB,eAAe;;cAStB,iBAAkB,SAAS;cAyE3B,8BACX,SAAS,sBACT,WAAU,sCACT,4BAA4B;cAmGlB,oBAAqB,KAAK"}
1
+ {"version":3,"file":"convertMessages.d.ts","names":[],"sources":["../src/convertMessages.ts"],"mappings":";;;;KAgBK,oCACH,4BAA4B;EAC1B,qBAAqB,YAAY;EACjC,gBAAgB,eAAe;;cAStB,iBAAkB,SAAS;cAyE3B,8BACX,SAAS,sBACT,WAAU,sCACT,4BAA4B;cAyFlB,oBAAqB,KAAK"}
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { httpUrlPattern, parseDataUrl } from "@assistant-ui/core/internal";
2
3
  //#region src/convertMessages.ts
3
4
  const uiMessageToDataPart = (ui) => ({
4
5
  type: "data",
@@ -116,15 +117,6 @@ const convertLangChainBaseMessage = (message, metadata = {}) => {
116
117
  };
117
118
  }
118
119
  };
119
- const parseDataUrl = (value) => {
120
- const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(.+)$/);
121
- if (!match) return null;
122
- return {
123
- mimeType: match[1],
124
- data: match[2]
125
- };
126
- };
127
- const httpUrlPattern = /^https?:\/\//i;
128
120
  const getMessageContent = (msg) => {
129
121
  const allContent = [...msg.content, ...msg.attachments?.flatMap((a) => a.content) ?? []];
130
122
  const hasNonText = allContent.some((part) => part.type === "file" || part.type === "image");
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessages.js","names":["parts","_exhaustiveCheck"],"sources":["../src/convertMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport type {\n AppendMessage,\n DataMessagePart,\n MessageTiming,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport type {\n LangChainBaseMessage,\n LangChainContentBlock,\n UIMessage,\n} from \"./types\";\n\ntype LangChainMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n uiMessagesByParent?: Map<string, UIMessage[]>;\n messageTiming?: Record<string, MessageTiming>;\n };\n\nconst uiMessageToDataPart = (ui: UIMessage): DataMessagePart => ({\n type: \"data\",\n name: ui.name,\n data: ui.props,\n});\n\nexport const getMessageType = (message: LangChainBaseMessage): string => {\n if (typeof message._getType === \"function\") return message._getType();\n if (\"type\" in message)\n return (message as Record<string, unknown>).type as string;\n throw new Error(\"Cannot determine message type\");\n};\n\nconst contentToParts = (content: unknown) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n\n const parts = content as readonly LangChainContentBlock[];\n return parts\n .map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"text_delta\":\n return { type: \"text\" as const, text: part.text };\n case \"image_url\": {\n const image =\n typeof part.image_url === \"string\"\n ? part.image_url\n : part.image_url?.url;\n if (!image) return null;\n return { type: \"image\" as const, image };\n }\n case \"file\":\n return {\n type: \"file\" as const,\n filename: part.metadata?.filename ?? \"file\",\n data:\n part.source_type === \"url\"\n ? part.url\n : part.source_type === \"id\"\n ? part.id\n : part.data,\n mimeType: part.mime_type ?? \"application/octet-stream\",\n };\n case \"thinking\":\n return { type: \"reasoning\" as const, text: part.thinking };\n case \"reasoning\":\n return {\n type: \"reasoning\" as const,\n text:\n part.summary?.map((s) => s?.text ?? \"\").join(\"\\n\\n\\n\") ??\n part.reasoning ??\n \"\",\n };\n case \"tool_use\":\n case \"input_json_delta\":\n return null;\n default:\n return null;\n }\n })\n .filter((p) => p !== null);\n};\n\nconst getCustomMetadata = (\n additionalKwargs: Record<string, unknown> | undefined,\n): Record<string, unknown> =>\n (additionalKwargs?.metadata as Record<string, unknown>) ?? {};\n\nconst getStringContent = (content: unknown): string => {\n if (typeof content === \"string\") return content;\n const parts = content as readonly LangChainContentBlock[];\n return parts\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n};\n\nexport const convertLangChainBaseMessage = (\n message: LangChainBaseMessage,\n metadata: LangChainMessageConverterMetadata = {},\n): useExternalMessageConverter.Message => {\n const type = getMessageType(message);\n\n switch (type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: getStringContent(message.content) }],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content),\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"ai\": {\n const toolCallParts =\n message.tool_calls?.map((tc) => ({\n type: \"tool-call\" as const,\n toolCallId: tc.id,\n toolName: tc.name,\n args: tc.args as ReadonlyJSONObject,\n argsText: JSON.stringify(tc.args),\n })) ?? [];\n\n const assistantStatus =\n typeof message.status === \"object\" ? message.status : undefined;\n\n const uiDataParts =\n (message.id\n ? metadata.uiMessagesByParent\n ?.get(message.id)\n ?.map(uiMessageToDataPart)\n : undefined) ?? [];\n\n const timing = metadata.messageTiming?.[message.id ?? \"\"];\n\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\n ...toolCallParts,\n ...uiDataParts,\n ],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n ...(timing && { timing }),\n },\n ...(assistantStatus && { status: assistantStatus }),\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 artifact: message.artifact,\n isError: message.status === \"error\",\n };\n\n default:\n return {\n role: \"system\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text:\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content),\n },\n ],\n };\n }\n};\n\nconst parseDataUrl = (\n value: string,\n): { mimeType: string; data: string } | null => {\n const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(.+)$/);\n if (!match) return null;\n return { mimeType: match[1]!, data: match[2]! };\n};\n\nconst httpUrlPattern = /^https?:\\/\\//i;\n\nexport const getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...(msg.attachments?.flatMap((a) => a.content) ?? []),\n ];\n\n const hasNonText = allContent.some(\n (part) => part.type === \"file\" || part.type === \"image\",\n );\n const hasText = allContent.some((part) => part.type === \"text\");\n if (hasNonText && !hasText) {\n allContent.unshift({ type: \"text\", text: \" \" });\n }\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 case \"file\": {\n const metadata = { filename: part.filename ?? \"file\" };\n if (httpUrlPattern.test(part.data)) {\n return {\n type: \"file\" as const,\n url: part.data,\n mime_type: part.mimeType,\n metadata,\n source_type: \"url\" as const,\n };\n }\n const parsed = parseDataUrl(part.data);\n return {\n type: \"file\" as const,\n data: parsed?.data ?? part.data,\n mime_type: parsed?.mimeType ?? part.mimeType,\n metadata,\n source_type: \"base64\" as const,\n };\n }\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n default: {\n const _exhaustiveCheck:\n | \"reasoning\"\n | \"source\"\n | \"audio\"\n | \"data\"\n | \"generative-ui\" = type;\n throw new Error(\n `Unsupported append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n return content;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"convertMessages.js","names":["parts","_exhaustiveCheck"],"sources":["../src/convertMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport type {\n AppendMessage,\n DataMessagePart,\n MessageTiming,\n} from \"@assistant-ui/core\";\nimport { httpUrlPattern, parseDataUrl } from \"@assistant-ui/core/internal\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport type {\n LangChainBaseMessage,\n LangChainContentBlock,\n UIMessage,\n} from \"./types\";\n\ntype LangChainMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n uiMessagesByParent?: Map<string, UIMessage[]>;\n messageTiming?: Record<string, MessageTiming>;\n };\n\nconst uiMessageToDataPart = (ui: UIMessage): DataMessagePart => ({\n type: \"data\",\n name: ui.name,\n data: ui.props,\n});\n\nexport const getMessageType = (message: LangChainBaseMessage): string => {\n if (typeof message._getType === \"function\") return message._getType();\n if (\"type\" in message)\n return (message as Record<string, unknown>).type as string;\n throw new Error(\"Cannot determine message type\");\n};\n\nconst contentToParts = (content: unknown) => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n\n const parts = content as readonly LangChainContentBlock[];\n return parts\n .map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"text_delta\":\n return { type: \"text\" as const, text: part.text };\n case \"image_url\": {\n const image =\n typeof part.image_url === \"string\"\n ? part.image_url\n : part.image_url?.url;\n if (!image) return null;\n return { type: \"image\" as const, image };\n }\n case \"file\":\n return {\n type: \"file\" as const,\n filename: part.metadata?.filename ?? \"file\",\n data:\n part.source_type === \"url\"\n ? part.url\n : part.source_type === \"id\"\n ? part.id\n : part.data,\n mimeType: part.mime_type ?? \"application/octet-stream\",\n };\n case \"thinking\":\n return { type: \"reasoning\" as const, text: part.thinking };\n case \"reasoning\":\n return {\n type: \"reasoning\" as const,\n text:\n part.summary?.map((s) => s?.text ?? \"\").join(\"\\n\\n\\n\") ??\n part.reasoning ??\n \"\",\n };\n case \"tool_use\":\n case \"input_json_delta\":\n return null;\n default:\n return null;\n }\n })\n .filter((p) => p !== null);\n};\n\nconst getCustomMetadata = (\n additionalKwargs: Record<string, unknown> | undefined,\n): Record<string, unknown> =>\n (additionalKwargs?.metadata as Record<string, unknown>) ?? {};\n\nconst getStringContent = (content: unknown): string => {\n if (typeof content === \"string\") return content;\n const parts = content as readonly LangChainContentBlock[];\n return parts\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n};\n\nexport const convertLangChainBaseMessage = (\n message: LangChainBaseMessage,\n metadata: LangChainMessageConverterMetadata = {},\n): useExternalMessageConverter.Message => {\n const type = getMessageType(message);\n\n switch (type) {\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n content: [{ type: \"text\", text: getStringContent(message.content) }],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content),\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n },\n };\n\n case \"ai\": {\n const toolCallParts =\n message.tool_calls?.map((tc) => ({\n type: \"tool-call\" as const,\n toolCallId: tc.id,\n toolName: tc.name,\n args: tc.args as ReadonlyJSONObject,\n argsText: JSON.stringify(tc.args),\n })) ?? [];\n\n const assistantStatus =\n typeof message.status === \"object\" ? message.status : undefined;\n\n const uiDataParts =\n (message.id\n ? metadata.uiMessagesByParent\n ?.get(message.id)\n ?.map(uiMessageToDataPart)\n : undefined) ?? [];\n\n const timing = metadata.messageTiming?.[message.id ?? \"\"];\n\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content),\n ...toolCallParts,\n ...uiDataParts,\n ],\n metadata: {\n custom: getCustomMetadata(message.additional_kwargs),\n ...(timing && { timing }),\n },\n ...(assistantStatus && { status: assistantStatus }),\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 artifact: message.artifact,\n isError: message.status === \"error\",\n };\n\n default:\n return {\n role: \"system\",\n id: message.id,\n content: [\n {\n type: \"text\",\n text:\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content),\n },\n ],\n };\n }\n};\n\nexport const getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...(msg.attachments?.flatMap((a) => a.content) ?? []),\n ];\n\n const hasNonText = allContent.some(\n (part) => part.type === \"file\" || part.type === \"image\",\n );\n const hasText = allContent.some((part) => part.type === \"text\");\n if (hasNonText && !hasText) {\n allContent.unshift({ type: \"text\", text: \" \" });\n }\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 case \"file\": {\n const metadata = { filename: part.filename ?? \"file\" };\n if (httpUrlPattern.test(part.data)) {\n return {\n type: \"file\" as const,\n url: part.data,\n mime_type: part.mimeType,\n metadata,\n source_type: \"url\" as const,\n };\n }\n const parsed = parseDataUrl(part.data);\n return {\n type: \"file\" as const,\n data: parsed?.data ?? part.data,\n mime_type: parsed?.mimeType ?? part.mimeType,\n metadata,\n source_type: \"base64\" as const,\n };\n }\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n default: {\n const _exhaustiveCheck:\n | \"reasoning\"\n | \"source\"\n | \"audio\"\n | \"data\"\n | \"generative-ui\" = type;\n throw new Error(\n `Unsupported append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n return content;\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-langchain",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "LangChain useStream adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "langchain",
@@ -29,10 +29,10 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
- "@assistant-ui/core": "^0.2.21",
33
- "@assistant-ui/store": "^0.2.20",
32
+ "@assistant-ui/core": "^0.3.0",
33
+ "@assistant-ui/store": "^0.3.0",
34
34
  "assistant-cloud": "*",
35
- "assistant-stream": "^0.3.26"
35
+ "assistant-stream": "^0.3.29"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@langchain/react": "^1.0.2",
@@ -46,14 +46,14 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@langchain/core": "^1.2.3",
49
- "@langchain/langgraph-sdk": "^1.9.25",
50
- "@langchain/react": "^1.0.26",
49
+ "@langchain/langgraph-sdk": "^1.9.28",
50
+ "@langchain/react": "^1.0.29",
51
51
  "@testing-library/react": "^16.3.2",
52
52
  "@types/react": "^19.2.17",
53
53
  "jsdom": "^29.1.1",
54
- "react": "^19.2.7",
54
+ "react": "^19.2.8",
55
55
  "vitest": "^4.1.10",
56
- "@assistant-ui/x-buildutils": "0.0.18"
56
+ "@assistant-ui/x-buildutils": "0.0.20"
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public",
@@ -6,6 +6,7 @@ import type {
6
6
  DataMessagePart,
7
7
  MessageTiming,
8
8
  } from "@assistant-ui/core";
9
+ import { httpUrlPattern, parseDataUrl } from "@assistant-ui/core/internal";
9
10
  import type { ReadonlyJSONObject } from "assistant-stream/utils";
10
11
  import type {
11
12
  LangChainBaseMessage,
@@ -190,16 +191,6 @@ export const convertLangChainBaseMessage = (
190
191
  }
191
192
  };
192
193
 
193
- const parseDataUrl = (
194
- value: string,
195
- ): { mimeType: string; data: string } | null => {
196
- const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(.+)$/);
197
- if (!match) return null;
198
- return { mimeType: match[1]!, data: match[2]! };
199
- };
200
-
201
- const httpUrlPattern = /^https?:\/\//i;
202
-
203
194
  export const getMessageContent = (msg: AppendMessage) => {
204
195
  const allContent = [
205
196
  ...msg.content,
@@ -103,15 +103,12 @@ const renderAui = (stream: MockStream) => {
103
103
  };
104
104
 
105
105
  const getText = (aui: ReturnType<typeof useAui>) =>
106
- aui
107
- .thread()
108
- .getState()
109
- .messages.map((m) =>
110
- m.content
111
- .filter((part) => part.type === "text")
112
- .map((part) => part.text)
113
- .join(""),
114
- );
106
+ aui.thread.getState().messages.map((m) =>
107
+ m.content
108
+ .filter((part) => part.type === "text")
109
+ .map((part) => part.text)
110
+ .join(""),
111
+ );
115
112
 
116
113
  describe("useStreamRuntime staged messages", () => {
117
114
  it("stages a new user message without submitting when startRun is false", async () => {
@@ -119,7 +116,7 @@ describe("useStreamRuntime staged messages", () => {
119
116
  const { auiResult } = renderAui(stream);
120
117
 
121
118
  await act(async () => {
122
- auiResult.current.thread().append({
119
+ auiResult.current.thread.append({
123
120
  role: "user",
124
121
  content: [{ type: "text", text: "draft" }],
125
122
  startRun: false,
@@ -141,7 +138,7 @@ describe("useStreamRuntime staged messages", () => {
141
138
  const { auiResult, rerender } = renderAui(stream);
142
139
 
143
140
  await act(async () => {
144
- auiResult.current.thread().append({
141
+ auiResult.current.thread.append({
145
142
  role: "user",
146
143
  parentId: "u1",
147
144
  content: [{ type: "text", text: "edited" }],
@@ -171,12 +168,12 @@ describe("useStreamRuntime staged messages", () => {
171
168
  const { auiResult, rerender } = renderAui(stream);
172
169
 
173
170
  await act(async () => {
174
- auiResult.current.thread().append({
171
+ auiResult.current.thread.append({
175
172
  role: "user",
176
173
  content: [{ type: "text", text: "first staged" }],
177
174
  startRun: false,
178
175
  });
179
- auiResult.current.thread().append({
176
+ auiResult.current.thread.append({
180
177
  role: "user",
181
178
  content: [{ type: "text", text: "second staged" }],
182
179
  startRun: false,
@@ -191,9 +188,9 @@ describe("useStreamRuntime staged messages", () => {
191
188
  ]);
192
189
  });
193
190
 
194
- const firstStagedId = auiResult.current.thread().getState().messages[1]!.id;
191
+ const firstStagedId = auiResult.current.thread.getState().messages[1]!.id;
195
192
  await act(async () => {
196
- await auiResult.current.thread().startRun({
193
+ await auiResult.current.thread.startRun({
197
194
  parentId: firstStagedId,
198
195
  sourceId: null,
199
196
  runConfig: {},
@@ -1,24 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { AppendMessage } from "@assistant-ui/core";
3
- import { runConfigToSubmitOptions } from "./useStreamRuntime";
4
-
5
- type RunConfig = AppendMessage["runConfig"];
6
-
7
- describe("runConfigToSubmitOptions", () => {
8
- it("returns undefined when runConfig is undefined", () => {
9
- expect(runConfigToSubmitOptions(undefined)).toBeUndefined();
10
- });
11
-
12
- it("returns undefined when custom is undefined", () => {
13
- expect(
14
- runConfigToSubmitOptions({ custom: undefined } as unknown as RunConfig),
15
- ).toBeUndefined();
16
- });
17
-
18
- it("maps custom to config.configurable", () => {
19
- const runConfig = { custom: { mode: "plan" } } as RunConfig;
20
- expect(runConfigToSubmitOptions(runConfig)).toEqual({
21
- config: { configurable: { mode: "plan" } },
22
- });
23
- });
24
- });
@@ -1,118 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { LangChainBaseMessage } from "./types";
3
- import { langChainStreamingTimingAccessors } from "./streamingTiming";
4
-
5
- const ai = (
6
- fields: Partial<LangChainBaseMessage> & { id: string; content: unknown },
7
- ): LangChainBaseMessage => ({
8
- _getType: () => "ai",
9
- ...fields,
10
- });
11
-
12
- const human = (id: string, content: unknown): LangChainBaseMessage => ({
13
- _getType: () => "human",
14
- id,
15
- content,
16
- });
17
-
18
- const { getAssistantMessageId, getTextLength, getToolCallCount } =
19
- langChainStreamingTimingAccessors;
20
-
21
- describe("langChainStreamingTimingAccessors", () => {
22
- it("resolves the last assistant message id via _getType()", () => {
23
- expect(
24
- getAssistantMessageId([human("u1", "hi"), ai({ id: "a1", content: "" })]),
25
- ).toBe("a1");
26
- expect(
27
- getAssistantMessageId([
28
- ai({ id: "a1", content: "" }),
29
- human("u2", "hi"),
30
- ai({ id: "a2", content: "" }),
31
- ]),
32
- ).toBe("a2");
33
- });
34
-
35
- it("returns undefined when there is no assistant message", () => {
36
- expect(getAssistantMessageId([human("u1", "hi")])).toBeUndefined();
37
- });
38
-
39
- it("measures string content length", () => {
40
- expect(getTextLength([ai({ id: "a1", content: "hello" })], "a1")).toBe(5);
41
- });
42
-
43
- it("sums text and thinking lengths across content blocks", () => {
44
- expect(
45
- getTextLength(
46
- [
47
- ai({
48
- id: "a1",
49
- content: [
50
- { type: "thinking", thinking: "hmm" },
51
- { type: "text", text: "answer" },
52
- ],
53
- }),
54
- ],
55
- "a1",
56
- ),
57
- ).toBe("hmm".length + "answer".length);
58
- });
59
-
60
- it("measures reasoning blocks (summary and reasoning fields)", () => {
61
- // reasoning via summary_text entries, joined like contentToParts does.
62
- expect(
63
- getTextLength(
64
- [
65
- ai({
66
- id: "a1",
67
- content: [
68
- {
69
- type: "reasoning",
70
- summary: [
71
- { type: "summary_text", text: "step one" },
72
- { type: "summary_text", text: "step two" },
73
- ],
74
- },
75
- ],
76
- }),
77
- ],
78
- "a1",
79
- ),
80
- ).toBe("step one\n\n\nstep two".length);
81
-
82
- // reasoning via the bare `reasoning` field when no summary is present.
83
- expect(
84
- getTextLength(
85
- [
86
- ai({
87
- id: "a2",
88
- content: [{ type: "reasoning", reasoning: "deduced" }],
89
- }),
90
- ],
91
- "a2",
92
- ),
93
- ).toBe("deduced".length);
94
- });
95
-
96
- it("counts tool_calls on the assistant message", () => {
97
- expect(
98
- getToolCallCount(
99
- [
100
- ai({
101
- id: "a1",
102
- content: "",
103
- tool_calls: [
104
- { id: "t1", name: "search", args: {} },
105
- { id: "t2", name: "fetch", args: {} },
106
- ],
107
- }),
108
- ],
109
- "a1",
110
- ),
111
- ).toBe(2);
112
- });
113
-
114
- it("returns 0 when the message is missing or not assistant", () => {
115
- expect(getTextLength([human("u1", "hi")], "a1")).toBe(0);
116
- expect(getToolCallCount([human("u1", "hi")], "a1")).toBe(0);
117
- });
118
- });
@@ -1,48 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainError } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainError", () => {
30
- it("returns undefined when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainError());
33
- expect(result.current).toBeUndefined();
34
- });
35
-
36
- it("returns the error value when extras carry error", () => {
37
- const error = new Error("boom");
38
- runSelectorAgainst(makeExtras({ error }));
39
- const { result } = renderHook(() => useLangChainError());
40
- expect(result.current).toBe(error);
41
- });
42
-
43
- it("returns undefined when extras carry no error", () => {
44
- runSelectorAgainst(makeExtras({ error: undefined }));
45
- const { result } = renderHook(() => useLangChainError());
46
- expect(result.current).toBeUndefined();
47
- });
48
- });
@@ -1,48 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainInterrupts } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainInterrupts", () => {
30
- it("returns an empty array when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainInterrupts());
33
- expect(result.current).toEqual([]);
34
- });
35
-
36
- it("returns the pending interrupts from extras", () => {
37
- const interrupts = [{ id: "i1", value: { q: "ok?" } }];
38
- runSelectorAgainst(makeExtras({ interrupts }));
39
- const { result } = renderHook(() => useLangChainInterrupts());
40
- expect(result.current).toBe(interrupts);
41
- });
42
-
43
- it("returns an empty array when extras carry no interrupts", () => {
44
- runSelectorAgainst(makeExtras({ interrupts: undefined }));
45
- const { result } = renderHook(() => useLangChainInterrupts());
46
- expect(result.current).toEqual([]);
47
- });
48
- });
@@ -1,49 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import { makeExtras } from "./__tests__/langChainTestUtils";
6
-
7
- const { mockUseAui } = vi.hoisted(() => ({
8
- mockUseAui: vi.fn(),
9
- }));
10
-
11
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
12
- const actual = await importOriginal();
13
- return {
14
- ...actual,
15
- useAui: (() => mockUseAui()) as unknown as typeof actual.useAui,
16
- };
17
- });
18
-
19
- import { useLangChainRespond } from "./hooks";
20
-
21
- describe("useLangChainRespond", () => {
22
- it("forwards the response and options to stream.respond", async () => {
23
- const respond = vi.fn().mockResolvedValue(undefined);
24
- mockUseAui.mockReturnValue({
25
- thread: () => ({
26
- getState: () => ({ extras: makeExtras({ respond }) }),
27
- }),
28
- });
29
-
30
- const { result } = renderHook(() => useLangChainRespond());
31
- await result.current({ approved: true }, { interruptId: "x" });
32
-
33
- expect(respond).toHaveBeenCalledWith(
34
- { approved: true },
35
- { interruptId: "x" },
36
- );
37
- });
38
-
39
- it("throws when extras are absent or unbranded", () => {
40
- mockUseAui.mockReturnValue({
41
- thread: () => ({ getState: () => ({ extras: { respond: vi.fn() } }) }),
42
- });
43
-
44
- const { result } = renderHook(() => useLangChainRespond());
45
- expect(() => result.current({ approved: true })).toThrow(
46
- "useStreamRuntime",
47
- );
48
- });
49
- });
@@ -1,52 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import { makeExtras } from "./__tests__/langChainTestUtils";
6
-
7
- const { mockUseAui } = vi.hoisted(() => ({
8
- mockUseAui: vi.fn(),
9
- }));
10
-
11
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
12
- const actual = await importOriginal();
13
- return {
14
- ...actual,
15
- useAui: (() => mockUseAui()) as unknown as typeof actual.useAui,
16
- };
17
- });
18
-
19
- import { useLangChainRespondAll } from "./hooks";
20
-
21
- describe("useLangChainRespondAll", () => {
22
- it("forwards the responses and options to stream.respondAll", async () => {
23
- const respondAll = vi.fn().mockResolvedValue(undefined);
24
- mockUseAui.mockReturnValue({
25
- thread: () => ({
26
- getState: () => ({ extras: makeExtras({ respondAll }) }),
27
- }),
28
- });
29
-
30
- const { result } = renderHook(() => useLangChainRespondAll());
31
- await result.current(
32
- { a: { approved: true }, b: { approved: false } },
33
- { metadata: { src: "ui" } },
34
- );
35
-
36
- expect(respondAll).toHaveBeenCalledWith(
37
- { a: { approved: true }, b: { approved: false } },
38
- { metadata: { src: "ui" } },
39
- );
40
- });
41
-
42
- it("throws when extras are absent or unbranded", () => {
43
- mockUseAui.mockReturnValue({
44
- thread: () => ({ getState: () => ({ extras: { respondAll: vi.fn() } }) }),
45
- });
46
-
47
- const { result } = renderHook(() => useLangChainRespondAll());
48
- expect(() => result.current({ a: { approved: true } })).toThrow(
49
- "useStreamRuntime",
50
- );
51
- });
52
- });
@@ -1,42 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainStream } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainStream", () => {
30
- it("returns undefined when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainStream());
33
- expect(result.current).toBeUndefined();
34
- });
35
-
36
- it("returns the stream handle from extras", () => {
37
- const stream = { submit: () => {} };
38
- runSelectorAgainst(makeExtras({ stream }));
39
- const { result } = renderHook(() => useLangChainStream());
40
- expect(result.current).toBe(stream);
41
- });
42
- });
@@ -1,48 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainSubagents } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainSubagents", () => {
30
- it("returns the stable empty map when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainSubagents());
33
- expect(result.current.size).toBe(0);
34
- });
35
-
36
- it("returns the subagents map from extras", () => {
37
- const subagents = new Map([["researcher", { namespace: "researcher" }]]);
38
- runSelectorAgainst(makeExtras({ subagents }));
39
- const { result } = renderHook(() => useLangChainSubagents());
40
- expect(result.current).toBe(subagents);
41
- });
42
-
43
- it("returns the stable empty map when extras carry no subagents", () => {
44
- runSelectorAgainst(makeExtras({ subagents: undefined }));
45
- const { result } = renderHook(() => useLangChainSubagents());
46
- expect(result.current.size).toBe(0);
47
- });
48
- });
@@ -1,48 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainSubgraphs } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainSubgraphs", () => {
30
- it("returns the stable empty map when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainSubgraphs());
33
- expect(result.current.size).toBe(0);
34
- });
35
-
36
- it("returns the subgraphs map from extras", () => {
37
- const subgraphs = new Map([["planner", { namespace: "planner" }]]);
38
- runSelectorAgainst(makeExtras({ subgraphs }));
39
- const { result } = renderHook(() => useLangChainSubgraphs());
40
- expect(result.current).toBe(subgraphs);
41
- });
42
-
43
- it("returns the stable empty map when extras carry no subgraphs", () => {
44
- runSelectorAgainst(makeExtras({ subgraphs: undefined }));
45
- const { result } = renderHook(() => useLangChainSubgraphs());
46
- expect(result.current.size).toBe(0);
47
- });
48
- });
@@ -1,48 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { renderHook } from "@testing-library/react";
5
- import {
6
- createRunSelectorAgainst,
7
- makeExtras,
8
- type Selector,
9
- } from "./__tests__/langChainTestUtils";
10
-
11
- const { mockUseAuiState } = vi.hoisted(() => ({
12
- mockUseAuiState: vi.fn(),
13
- }));
14
-
15
- vi.mock(import("@assistant-ui/store"), async (importOriginal) => {
16
- const actual = await importOriginal();
17
- return {
18
- ...actual,
19
- useAuiState: ((selector: Selector) =>
20
- mockUseAuiState(selector)) as typeof actual.useAuiState,
21
- useAui: (() => ({})) as unknown as typeof actual.useAui,
22
- };
23
- });
24
-
25
- import { useLangChainToolCalls } from "./hooks";
26
-
27
- const runSelectorAgainst = createRunSelectorAgainst(mockUseAuiState);
28
-
29
- describe("useLangChainToolCalls", () => {
30
- it("returns an empty array when extras are absent", () => {
31
- runSelectorAgainst(undefined);
32
- const { result } = renderHook(() => useLangChainToolCalls());
33
- expect(result.current).toEqual([]);
34
- });
35
-
36
- it("returns the assembled tool calls from extras", () => {
37
- const toolCalls = [{ id: "a", name: "search", args: { q: "x" } }];
38
- runSelectorAgainst(makeExtras({ toolCalls }));
39
- const { result } = renderHook(() => useLangChainToolCalls());
40
- expect(result.current).toBe(toolCalls);
41
- });
42
-
43
- it("returns an empty array when extras carry no tool calls", () => {
44
- runSelectorAgainst(makeExtras({ toolCalls: undefined }));
45
- const { result } = renderHook(() => useLangChainToolCalls());
46
- expect(result.current).toEqual([]);
47
- });
48
- });