@assistant-ui/react-ai-sdk 1.1.3 → 1.1.5
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/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.js +41 -15
- package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.js +1 -28
- package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
- package/dist/ui/utils/convertMessage.d.ts +4 -2
- package/dist/ui/utils/convertMessage.d.ts.map +1 -1
- package/dist/ui/utils/convertMessage.js +31 -8
- package/dist/ui/utils/convertMessage.js.map +1 -1
- package/package.json +9 -9
- package/src/ui/use-chat/useAISDKRuntime.tsx +48 -15
- package/src/ui/use-chat/useChatRuntime.tsx +2 -28
- package/src/ui/utils/convertMessage.ts +37 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAISDKRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAISDKRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAEL,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAMjB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EACL,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,GAAG;QAC/C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;KAC5C,CAAC,GACF,SAAS,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EACtE,aAAa,UAAU,CAAC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC,EACnD,eAAc,mBAAwB,qBA0HvC,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/ui/use-chat/useAISDKRuntime.tsx
|
|
4
|
+
import { useState, useMemo } from "react";
|
|
4
5
|
import {
|
|
5
6
|
useExternalStoreRuntime,
|
|
6
|
-
useRuntimeAdapters
|
|
7
|
+
useRuntimeAdapters,
|
|
8
|
+
INTERNAL
|
|
7
9
|
} from "@assistant-ui/react";
|
|
8
10
|
import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
|
|
9
11
|
import { toCreateMessage } from "../utils/toCreateMessage.js";
|
|
@@ -14,23 +16,43 @@ import {
|
|
|
14
16
|
aiSDKV5FormatAdapter
|
|
15
17
|
} from "../adapters/aiSDKFormatAdapter.js";
|
|
16
18
|
import { useExternalHistory } from "./useExternalHistory.js";
|
|
17
|
-
import { useMemo } from "react";
|
|
18
19
|
var useAISDKRuntime = (chatHelpers, { adapters } = {}) => {
|
|
19
20
|
const contextAdapters = useRuntimeAdapters();
|
|
21
|
+
const isRunning = chatHelpers.status === "submitted" || chatHelpers.status == "streaming";
|
|
22
|
+
const [toolStatuses, setToolStatuses] = useState({});
|
|
20
23
|
const messages = AISDKMessageConverter.useThreadMessages({
|
|
21
|
-
isRunning
|
|
22
|
-
messages: chatHelpers.messages
|
|
24
|
+
isRunning,
|
|
25
|
+
messages: chatHelpers.messages,
|
|
26
|
+
metadata: useMemo(() => ({ toolStatuses }), [toolStatuses])
|
|
27
|
+
});
|
|
28
|
+
const runtimeRef = useMemo(
|
|
29
|
+
() => ({
|
|
30
|
+
get current() {
|
|
31
|
+
return runtime;
|
|
32
|
+
}
|
|
33
|
+
}),
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
[]
|
|
36
|
+
);
|
|
37
|
+
const toolInvocations = INTERNAL.useToolInvocations({
|
|
38
|
+
state: {
|
|
39
|
+
messages,
|
|
40
|
+
isRunning
|
|
41
|
+
},
|
|
42
|
+
getTools: () => runtimeRef.current.thread.getModelContext().tools,
|
|
43
|
+
onResult: (command) => {
|
|
44
|
+
if (command.type === "add-tool-result") {
|
|
45
|
+
chatHelpers.addToolResult({
|
|
46
|
+
tool: command.toolName,
|
|
47
|
+
toolCallId: command.toolCallId,
|
|
48
|
+
output: command.result
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
setToolStatuses
|
|
23
53
|
});
|
|
24
54
|
const isLoading = useExternalHistory(
|
|
25
|
-
|
|
26
|
-
() => ({
|
|
27
|
-
get current() {
|
|
28
|
-
return runtime;
|
|
29
|
-
}
|
|
30
|
-
}),
|
|
31
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
32
|
-
[]
|
|
33
|
-
),
|
|
55
|
+
runtimeRef,
|
|
34
56
|
adapters?.history ?? contextAdapters?.history,
|
|
35
57
|
AISDKMessageConverter.toThreadMessages,
|
|
36
58
|
aiSDKV5FormatAdapter,
|
|
@@ -39,7 +61,7 @@ var useAISDKRuntime = (chatHelpers, { adapters } = {}) => {
|
|
|
39
61
|
}
|
|
40
62
|
);
|
|
41
63
|
const runtime = useExternalStoreRuntime({
|
|
42
|
-
isRunning
|
|
64
|
+
isRunning,
|
|
43
65
|
messages,
|
|
44
66
|
setMessages: (messages2) => chatHelpers.setMessages(
|
|
45
67
|
messages2.map(getVercelAIMessages).filter(Boolean).flat()
|
|
@@ -47,7 +69,10 @@ var useAISDKRuntime = (chatHelpers, { adapters } = {}) => {
|
|
|
47
69
|
onImport: (messages2) => chatHelpers.setMessages(
|
|
48
70
|
messages2.map(getVercelAIMessages).filter(Boolean).flat()
|
|
49
71
|
),
|
|
50
|
-
onCancel: async () =>
|
|
72
|
+
onCancel: async () => {
|
|
73
|
+
chatHelpers.stop();
|
|
74
|
+
toolInvocations.abort();
|
|
75
|
+
},
|
|
51
76
|
onNew: async (message) => {
|
|
52
77
|
const createMessage = toCreateMessage(message);
|
|
53
78
|
await chatHelpers.sendMessage(createMessage, {
|
|
@@ -77,6 +102,7 @@ var useAISDKRuntime = (chatHelpers, { adapters } = {}) => {
|
|
|
77
102
|
output: result
|
|
78
103
|
});
|
|
79
104
|
},
|
|
105
|
+
onResumeToolCall: (options) => toolInvocations.resume(options.toolCallId, options.payload),
|
|
80
106
|
adapters: {
|
|
81
107
|
attachments: vercelAttachmentAdapter,
|
|
82
108
|
...contextAdapters,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage, useChat } from \"@ai-sdk/react\";\nimport {\n useExternalStoreRuntime,\n ExternalStoreAdapter,\n ThreadHistoryAdapter,\n AssistantRuntime,\n ThreadMessage,\n MessageFormatAdapter,\n useRuntimeAdapters,\n} from \"@assistant-ui/react\";\nimport { sliceMessagesUntil } from \"../utils/sliceMessagesUntil\";\nimport { toCreateMessage } from \"../utils/toCreateMessage\";\nimport { vercelAttachmentAdapter } from \"../utils/vercelAttachmentAdapter\";\nimport { getVercelAIMessages } from \"../getVercelAIMessages\";\nimport { AISDKMessageConverter } from \"../utils/convertMessage\";\nimport {\n AISDKStorageFormat,\n aiSDKV5FormatAdapter,\n} from \"../adapters/aiSDKFormatAdapter\";\nimport { useExternalHistory } from \"./useExternalHistory\";\
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo } from \"react\";\nimport type { UIMessage, useChat } from \"@ai-sdk/react\";\nimport {\n useExternalStoreRuntime,\n ExternalStoreAdapter,\n ThreadHistoryAdapter,\n AssistantRuntime,\n ThreadMessage,\n MessageFormatAdapter,\n useRuntimeAdapters,\n INTERNAL,\n type ToolExecutionStatus,\n} from \"@assistant-ui/react\";\nimport { sliceMessagesUntil } from \"../utils/sliceMessagesUntil\";\nimport { toCreateMessage } from \"../utils/toCreateMessage\";\nimport { vercelAttachmentAdapter } from \"../utils/vercelAttachmentAdapter\";\nimport { getVercelAIMessages } from \"../getVercelAIMessages\";\nimport { AISDKMessageConverter } from \"../utils/convertMessage\";\nimport {\n AISDKStorageFormat,\n aiSDKV5FormatAdapter,\n} from \"../adapters/aiSDKFormatAdapter\";\nimport { useExternalHistory } from \"./useExternalHistory\";\n\nexport type AISDKRuntimeAdapter = {\n adapters?:\n | (NonNullable<ExternalStoreAdapter[\"adapters\"]> & {\n history?: ThreadHistoryAdapter | undefined;\n })\n | undefined;\n};\n\nexport const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>,\n { adapters }: AISDKRuntimeAdapter = {},\n) => {\n const contextAdapters = useRuntimeAdapters();\n const isRunning =\n chatHelpers.status === \"submitted\" || chatHelpers.status == \"streaming\";\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n const messages = AISDKMessageConverter.useThreadMessages({\n isRunning,\n messages: chatHelpers.messages,\n metadata: useMemo(() => ({ toolStatuses }), [toolStatuses]),\n });\n\n const runtimeRef = useMemo(\n () => ({\n get current(): AssistantRuntime {\n return runtime;\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n const toolInvocations = INTERNAL.useToolInvocations({\n state: {\n messages,\n isRunning,\n },\n getTools: () => runtimeRef.current.thread.getModelContext().tools,\n onResult: (command: any) => {\n if (command.type === \"add-tool-result\") {\n chatHelpers.addToolResult({\n tool: command.toolName,\n toolCallId: command.toolCallId,\n output: command.result,\n });\n }\n },\n setToolStatuses,\n });\n\n const isLoading = useExternalHistory(\n runtimeRef,\n adapters?.history ?? contextAdapters?.history,\n AISDKMessageConverter.toThreadMessages as (\n messages: UI_MESSAGE[],\n ) => ThreadMessage[],\n aiSDKV5FormatAdapter as MessageFormatAdapter<\n UI_MESSAGE,\n AISDKStorageFormat\n >,\n (messages) => {\n chatHelpers.setMessages(messages);\n },\n );\n\n const runtime = useExternalStoreRuntime({\n isRunning,\n messages,\n setMessages: (messages) =>\n chatHelpers.setMessages(\n messages\n .map(getVercelAIMessages<UI_MESSAGE>)\n .filter(Boolean)\n .flat(),\n ),\n onImport: (messages) =>\n chatHelpers.setMessages(\n messages\n .map(getVercelAIMessages<UI_MESSAGE>)\n .filter(Boolean)\n .flat(),\n ),\n onCancel: async () => {\n chatHelpers.stop();\n toolInvocations.abort();\n },\n onNew: async (message) => {\n const createMessage = toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onEdit: async (message) => {\n const newMessages = sliceMessagesUntil(\n chatHelpers.messages,\n message.parentId,\n );\n chatHelpers.setMessages(newMessages);\n\n const createMessage = toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onReload: async (parentId: string | null, config) => {\n const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.regenerate({ metadata: config.runConfig });\n },\n onAddToolResult: ({ toolCallId, result }) => {\n chatHelpers.addToolResult({\n tool: toolCallId,\n toolCallId,\n output: result,\n });\n },\n onResumeToolCall: (options) =>\n toolInvocations.resume(options.toolCallId, options.payload),\n adapters: {\n attachments: vercelAttachmentAdapter,\n ...contextAdapters,\n ...adapters,\n },\n isLoading,\n });\n\n return runtime;\n};\n"],"mappings":"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","names":["messages"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,gBAAgB,EAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAG9B,MAAM,MAAM,qBAAqB,CAAC,UAAU,SAAS,SAAS,GAAG,SAAS,IACxE,QAAQ,CAAC,UAAU,CAAC,GAAG;IACrB,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;CACxD,CAAC;AAEJ,eAAO,MAAM,oBAAoB,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EAC3E,UAAU,qBAAqB,CAAC,UAAU,CAAC,KAC1C,
|
|
1
|
+
{"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,gBAAgB,EAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAG9B,MAAM,MAAM,qBAAqB,CAAC,UAAU,SAAS,SAAS,GAAG,SAAS,IACxE,QAAQ,CAAC,UAAU,CAAC,GAAG;IACrB,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;CACxD,CAAC;AAEJ,eAAO,MAAM,oBAAoB,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EAC3E,UAAU,qBAAqB,CAAC,UAAU,CAAC,KAC1C,gBAsBF,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EAAE,wBAGtE,qBAAqB,CAAC,UAAU,CAAM,KAAG,gBAQ3C,CAAC"}
|
|
@@ -17,34 +17,7 @@ var useChatThreadRuntime = (options) => {
|
|
|
17
17
|
const transport = transportOptions ?? new AssistantChatTransport();
|
|
18
18
|
const chat = useChat({
|
|
19
19
|
...chatOptions,
|
|
20
|
-
transport
|
|
21
|
-
onToolCall: async ({ toolCall }) => {
|
|
22
|
-
await chatOptions.onToolCall?.({ toolCall });
|
|
23
|
-
const tools = runtime.thread.getModelContext().tools;
|
|
24
|
-
const tool = tools?.[toolCall.toolName];
|
|
25
|
-
if (tool) {
|
|
26
|
-
try {
|
|
27
|
-
const result = await tool.execute?.(toolCall.input, {
|
|
28
|
-
toolCallId: toolCall.toolCallId,
|
|
29
|
-
abortSignal: new AbortController().signal
|
|
30
|
-
// dummy signal for now
|
|
31
|
-
});
|
|
32
|
-
chat.addToolResult({
|
|
33
|
-
tool: toolCall.toolName,
|
|
34
|
-
toolCallId: toolCall.toolCallId,
|
|
35
|
-
output: result
|
|
36
|
-
});
|
|
37
|
-
} catch (error) {
|
|
38
|
-
chat.addToolResult({
|
|
39
|
-
tool: toolCall.toolName,
|
|
40
|
-
toolCallId: toolCall.toolCallId,
|
|
41
|
-
output: {
|
|
42
|
-
error: error instanceof Error ? error.message : String(error)
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
20
|
+
transport
|
|
48
21
|
});
|
|
49
22
|
const runtime = useAISDKRuntime(chat, {
|
|
50
23
|
adapters
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport { useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport {\n AssistantRuntime,\n unstable_useCloudThreadListAdapter,\n unstable_useRemoteThreadListRuntime,\n} from \"@assistant-ui/react\";\nimport { useAISDKRuntime, type AISDKRuntimeAdapter } from \"./useAISDKRuntime\";\nimport { ChatInit } from \"ai\";\nimport { AssistantChatTransport } from \"./AssistantChatTransport\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n };\n\nexport const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: UseChatRuntimeOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n ...chatOptions\n } = options ?? {};\n const transport = transportOptions ?? new AssistantChatTransport();\n\n const chat = useChat({\n ...chatOptions,\n transport,\n
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport { useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport {\n AssistantRuntime,\n unstable_useCloudThreadListAdapter,\n unstable_useRemoteThreadListRuntime,\n} from \"@assistant-ui/react\";\nimport { useAISDKRuntime, type AISDKRuntimeAdapter } from \"./useAISDKRuntime\";\nimport { ChatInit } from \"ai\";\nimport { AssistantChatTransport } from \"./AssistantChatTransport\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n };\n\nexport const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: UseChatRuntimeOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n ...chatOptions\n } = options ?? {};\n const transport = transportOptions ?? new AssistantChatTransport();\n\n const chat = useChat({\n ...chatOptions,\n transport,\n });\n\n const runtime = useAISDKRuntime(chat, {\n adapters,\n });\n\n if (transport instanceof AssistantChatTransport) {\n transport.setRuntime(runtime);\n }\n\n return runtime;\n};\n\nexport const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({\n cloud,\n ...options\n}: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {\n const cloudAdapter = unstable_useCloudThreadListAdapter({ cloud });\n return unstable_useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useChatThreadRuntime(options);\n },\n adapter: cloudAdapter,\n });\n};\n"],"mappings":";;;AAEA,SAAS,eAA+B;AAExC;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAiD;AAE1D,SAAS,8BAA8B;AAQhC,IAAM,uBAAuB,CAClC,YACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EACL,IAAI,WAAW,CAAC;AAChB,QAAM,YAAY,oBAAoB,IAAI,uBAAuB;AAEjE,QAAM,OAAO,QAAQ;AAAA,IACnB,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,UAAU,gBAAgB,MAAM;AAAA,IACpC;AAAA,EACF,CAAC;AAED,MAAI,qBAAqB,wBAAwB;AAC/C,cAAU,WAAW,OAAO;AAAA,EAC9B;AAEA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAA2C;AAAA,EACvE;AAAA,EACA,GAAG;AACL,IAAuC,CAAC,MAAwB;AAC9D,QAAM,eAAe,mCAAmC,EAAE,MAAM,CAAC;AACjE,SAAO,oCAAoC;AAAA,IACzC,aAAa,SAAS,cAAc;AAClC,aAAO,qBAAqB,OAAO;AAAA,IACrC;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { UIMessage } from "ai";
|
|
2
|
+
import { type useExternalMessageConverter } from "@assistant-ui/react";
|
|
2
3
|
export declare const AISDKMessageConverter: {
|
|
3
|
-
useThreadMessages: ({ messages, isRunning, joinStrategy, }: {
|
|
4
|
+
useThreadMessages: ({ messages, isRunning, joinStrategy, metadata, }: {
|
|
4
5
|
messages: UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[];
|
|
5
6
|
isRunning: boolean;
|
|
6
7
|
joinStrategy?: "concat-content" | "none" | undefined;
|
|
8
|
+
metadata?: useExternalMessageConverter.Metadata;
|
|
7
9
|
}) => import("@assistant-ui/react").ThreadMessage[];
|
|
8
|
-
toThreadMessages: (messages: UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], isRunning?: boolean) => import("@assistant-ui/react").ThreadMessage[];
|
|
10
|
+
toThreadMessages: (messages: UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], isRunning?: boolean, metadata?: useExternalMessageConverter.Metadata) => import("@assistant-ui/react").ThreadMessage[];
|
|
9
11
|
toOriginalMessages: (input: import("@assistant-ui/react").ThreadState | import("@assistant-ui/react").ThreadMessage | import("@assistant-ui/react").ThreadMessage["content"][number]) => unknown[];
|
|
10
12
|
toOriginalMessage: (input: import("@assistant-ui/react").ThreadState | import("@assistant-ui/react").ThreadMessage | import("@assistant-ui/react").ThreadMessage["content"][number]) => {};
|
|
11
13
|
useOriginalMessage: () => {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,SAAS,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"convertMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,SAAS,EAAE,MAAM,IAAI,CAAC;AAC7C,OAAO,EAML,KAAK,2BAA2B,EACjC,MAAM,qBAAqB,CAAC;AAqK7B,eAAO,MAAM,qBAAqB;;;;;;;;0HA/IxB,sDACD;yHAIW,sDACP;;;CA6MZ,CAAC"}
|
|
@@ -3,7 +3,10 @@ import { isToolUIPart } from "ai";
|
|
|
3
3
|
import {
|
|
4
4
|
unstable_createMessageConverter
|
|
5
5
|
} from "@assistant-ui/react";
|
|
6
|
-
|
|
6
|
+
function stripClosingDelimiters(json) {
|
|
7
|
+
return json.replace(/[}\]"]+$/, "");
|
|
8
|
+
}
|
|
9
|
+
var convertParts = (message, metadata) => {
|
|
7
10
|
if (!message.parts || message.parts.length === 0) {
|
|
8
11
|
return [];
|
|
9
12
|
}
|
|
@@ -37,14 +40,26 @@ var convertParts = (message) => {
|
|
|
37
40
|
isError = true;
|
|
38
41
|
result = { error: part.errorText };
|
|
39
42
|
}
|
|
43
|
+
let argsText = JSON.stringify(args);
|
|
44
|
+
if (part.state === "input-streaming") {
|
|
45
|
+
argsText = stripClosingDelimiters(argsText);
|
|
46
|
+
}
|
|
47
|
+
const toolStatus = metadata.toolStatuses?.[toolCallId];
|
|
40
48
|
return {
|
|
41
49
|
type: "tool-call",
|
|
42
50
|
toolName,
|
|
43
51
|
toolCallId,
|
|
44
|
-
argsText
|
|
52
|
+
argsText,
|
|
45
53
|
args,
|
|
46
54
|
result,
|
|
47
|
-
isError
|
|
55
|
+
isError,
|
|
56
|
+
...toolStatus?.type === "interrupt" && {
|
|
57
|
+
interrupt: toolStatus.payload,
|
|
58
|
+
status: {
|
|
59
|
+
type: "requires-action",
|
|
60
|
+
reason: "interrupt"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
48
63
|
};
|
|
49
64
|
}
|
|
50
65
|
if (type === "dynamic-tool") {
|
|
@@ -63,6 +78,7 @@ var convertParts = (message) => {
|
|
|
63
78
|
isError = true;
|
|
64
79
|
result = { error: part.errorText };
|
|
65
80
|
}
|
|
81
|
+
const toolStatus = metadata.toolStatuses?.[toolCallId];
|
|
66
82
|
return {
|
|
67
83
|
type: "tool-call",
|
|
68
84
|
toolName,
|
|
@@ -70,7 +86,14 @@ var convertParts = (message) => {
|
|
|
70
86
|
argsText: JSON.stringify(args),
|
|
71
87
|
args,
|
|
72
88
|
result,
|
|
73
|
-
isError
|
|
89
|
+
isError,
|
|
90
|
+
...toolStatus?.type === "interrupt" && {
|
|
91
|
+
interrupt: toolStatus.payload,
|
|
92
|
+
status: {
|
|
93
|
+
type: "requires-action",
|
|
94
|
+
reason: "interrupt"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
74
97
|
};
|
|
75
98
|
}
|
|
76
99
|
if (type === "source-url") {
|
|
@@ -99,7 +122,7 @@ var convertParts = (message) => {
|
|
|
99
122
|
}).filter(Boolean);
|
|
100
123
|
};
|
|
101
124
|
var AISDKMessageConverter = unstable_createMessageConverter(
|
|
102
|
-
(message) => {
|
|
125
|
+
(message, metadata) => {
|
|
103
126
|
const createdAt = /* @__PURE__ */ new Date();
|
|
104
127
|
switch (message.role) {
|
|
105
128
|
case "user":
|
|
@@ -107,7 +130,7 @@ var AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
107
130
|
role: "user",
|
|
108
131
|
id: message.id,
|
|
109
132
|
createdAt,
|
|
110
|
-
content: convertParts(message),
|
|
133
|
+
content: convertParts(message, metadata),
|
|
111
134
|
attachments: message.parts?.filter((p) => p.type === "file").map((part, idx) => {
|
|
112
135
|
return {
|
|
113
136
|
id: idx.toString(),
|
|
@@ -135,14 +158,14 @@ var AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
135
158
|
role: "system",
|
|
136
159
|
id: message.id,
|
|
137
160
|
createdAt,
|
|
138
|
-
content: convertParts(message)
|
|
161
|
+
content: convertParts(message, metadata)
|
|
139
162
|
};
|
|
140
163
|
case "assistant":
|
|
141
164
|
return {
|
|
142
165
|
role: "assistant",
|
|
143
166
|
id: message.id,
|
|
144
167
|
createdAt,
|
|
145
|
-
content: convertParts(message),
|
|
168
|
+
content: convertParts(message, metadata),
|
|
146
169
|
metadata: {
|
|
147
170
|
unstable_annotations: message.annotations,
|
|
148
171
|
unstable_data: Array.isArray(message.data) ? message.data : message.data ? [message.data] : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, UIMessage } from \"ai\";\nimport {\n unstable_createMessageConverter,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type SourceMessagePart,\n} from \"@assistant-ui/react\";\n\nconst convertParts = (message: UIMessage) => {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n return message.parts\n .filter((p) => p.type !== \"step-start\" && p.type !== \"file\")\n .map((part) => {\n const type = part.type;\n\n // Handle text parts\n if (type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n // Handle reasoning parts\n if (type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n // Handle tool-* parts (AI SDK v5 tool calls)\n if (isToolUIPart(part)) {\n const toolName = type.replace(\"tool-\", \"\");\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle dynamic-tool parts\n if (type === \"dynamic-tool\") {\n const toolName = part.toolName;\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle source-url parts\n if (type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n title: part.title || \"\",\n } satisfies SourceMessagePart;\n }\n\n // Handle source-document parts\n if (type === \"source-document\") {\n console.warn(\n `Source document part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // Handle data-* parts (AI SDK v5 data parts)\n if (type.startsWith(\"data-\")) {\n // For now, we'll skip data parts as they don't have a direct equivalent\n // in the assistant-ui types. They could be converted to a custom message part\n // or handled differently based on the specific use case.\n console.warn(\n `Data part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // For unsupported types, we'll skip them instead of throwing\n console.warn(`Unsupported message part type: ${type}`);\n return null;\n })\n .filter(Boolean) as any[];\n};\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage) => {\n // UIMessage doesn't have createdAt, so we'll use current date or undefined\n const createdAt = new Date();\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => {\n return {\n id: idx.toString(),\n type: part.mediaType.startsWith(\"image/\") ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n part.mediaType.startsWith(\"image/\")\n ? {\n type: \"image\",\n image: part.url,\n filename: part.filename!,\n }\n : {\n type: \"file\",\n filename: part.filename!,\n data: part.url,\n mimeType: part.mediaType,\n },\n ],\n contentType: part.mediaType ?? \"unknown/unknown\",\n status: { type: \"complete\" as const },\n };\n }),\n };\n\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n };\n\n case \"assistant\":\n return {\n role: \"assistant\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n metadata: {\n unstable_annotations: (message as any).annotations,\n unstable_data: Array.isArray((message as any).data)\n ? (message as any).data\n : (message as any).data\n ? [(message as any).data]\n : undefined,\n custom: {},\n },\n };\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, UIMessage } from \"ai\";\nimport {\n unstable_createMessageConverter,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type SourceMessagePart,\n type useExternalMessageConverter,\n} from \"@assistant-ui/react\";\n\nfunction stripClosingDelimiters(json: string) {\n return json.replace(/[}\\]\"]+$/, \"\");\n}\n\nconst convertParts = (\n message: UIMessage,\n metadata: useExternalMessageConverter.Metadata,\n) => {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n return message.parts\n .filter((p) => p.type !== \"step-start\" && p.type !== \"file\")\n .map((part) => {\n const type = part.type;\n\n // Handle text parts\n if (type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n // Handle reasoning parts\n if (type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n // Handle tool-* parts (AI SDK v5 tool calls)\n if (isToolUIPart(part)) {\n const toolName = type.replace(\"tool-\", \"\");\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n let argsText = JSON.stringify(args);\n if (part.state === \"input-streaming\") {\n // the argsText is not complete, so we need to strip the closing delimiters\n // these are added by the AI SDK in fix-json\n argsText = stripClosingDelimiters(argsText);\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText,\n args,\n result,\n isError,\n ...(toolStatus?.type === \"interrupt\" && {\n interrupt: toolStatus.payload,\n status: {\n type: \"requires-action\" as const,\n reason: \"interrupt\",\n },\n }),\n } satisfies ToolCallMessagePart;\n }\n\n // Handle dynamic-tool parts\n if (type === \"dynamic-tool\") {\n const toolName = part.toolName;\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n ...(toolStatus?.type === \"interrupt\" && {\n interrupt: toolStatus.payload,\n status: {\n type: \"requires-action\" as const,\n reason: \"interrupt\",\n },\n }),\n } satisfies ToolCallMessagePart;\n }\n\n // Handle source-url parts\n if (type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n title: part.title || \"\",\n } satisfies SourceMessagePart;\n }\n\n // Handle source-document parts\n if (type === \"source-document\") {\n console.warn(\n `Source document part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // Handle data-* parts (AI SDK v5 data parts)\n if (type.startsWith(\"data-\")) {\n // For now, we'll skip data parts as they don't have a direct equivalent\n // in the assistant-ui types. They could be converted to a custom message part\n // or handled differently based on the specific use case.\n console.warn(\n `Data part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // For unsupported types, we'll skip them instead of throwing\n console.warn(`Unsupported message part type: ${type}`);\n return null;\n })\n .filter(Boolean) as any[];\n};\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage, metadata: useExternalMessageConverter.Metadata) => {\n // UIMessage doesn't have createdAt, so we'll use current date or undefined\n const createdAt = new Date();\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content: convertParts(message, metadata),\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => {\n return {\n id: idx.toString(),\n type: part.mediaType.startsWith(\"image/\") ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n part.mediaType.startsWith(\"image/\")\n ? {\n type: \"image\",\n image: part.url,\n filename: part.filename!,\n }\n : {\n type: \"file\",\n filename: part.filename!,\n data: part.url,\n mimeType: part.mediaType,\n },\n ],\n contentType: part.mediaType ?? \"unknown/unknown\",\n status: { type: \"complete\" as const },\n };\n }),\n };\n\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n createdAt,\n content: convertParts(message, metadata),\n };\n\n case \"assistant\":\n return {\n role: \"assistant\",\n id: message.id,\n createdAt,\n content: convertParts(message, metadata),\n metadata: {\n unstable_annotations: (message as any).annotations,\n unstable_data: Array.isArray((message as any).data)\n ? (message as any).data\n : (message as any).data\n ? [(message as any).data]\n : undefined,\n custom: {},\n },\n };\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-ai-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
"sideEffects": false,
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@ai-sdk/provider": "^2.0.0",
|
|
22
|
-
"@ai-sdk/react": "^2.0.
|
|
23
|
-
"ai": "^5.0.
|
|
22
|
+
"@ai-sdk/react": "^2.0.59",
|
|
23
|
+
"ai": "^5.0.59",
|
|
24
24
|
"@radix-ui/react-use-callback-ref": "^1.1.1",
|
|
25
25
|
"@types/json-schema": "^7.0.15",
|
|
26
|
-
"assistant-stream": "^0.2.
|
|
26
|
+
"assistant-stream": "^0.2.34",
|
|
27
27
|
"json-schema": "^0.4.0",
|
|
28
28
|
"zod": "^4.1.11",
|
|
29
29
|
"zustand": "^5.0.8"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@assistant-ui/react": "^0.11.
|
|
32
|
+
"@assistant-ui/react": "^0.11.27",
|
|
33
33
|
"@types/react": "*",
|
|
34
34
|
"assistant-cloud": "*",
|
|
35
35
|
"react": "^18 || ^19 || ^19.0.0-rc"
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^24.
|
|
47
|
-
"@types/react": "^19.
|
|
46
|
+
"@types/node": "^24.6.2",
|
|
47
|
+
"@types/react": "^19.2.0",
|
|
48
48
|
"eslint": "^9",
|
|
49
49
|
"eslint-config-next": "15.5.4",
|
|
50
|
-
"react": "19.
|
|
50
|
+
"react": "19.2.0",
|
|
51
51
|
"tsx": "^4.20.6",
|
|
52
|
-
"@assistant-ui/react": "0.11.
|
|
52
|
+
"@assistant-ui/react": "0.11.27",
|
|
53
53
|
"@assistant-ui/x-buildutils": "0.0.1"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useState, useMemo } from "react";
|
|
3
4
|
import type { UIMessage, useChat } from "@ai-sdk/react";
|
|
4
5
|
import {
|
|
5
6
|
useExternalStoreRuntime,
|
|
@@ -9,6 +10,8 @@ import {
|
|
|
9
10
|
ThreadMessage,
|
|
10
11
|
MessageFormatAdapter,
|
|
11
12
|
useRuntimeAdapters,
|
|
13
|
+
INTERNAL,
|
|
14
|
+
type ToolExecutionStatus,
|
|
12
15
|
} from "@assistant-ui/react";
|
|
13
16
|
import { sliceMessagesUntil } from "../utils/sliceMessagesUntil";
|
|
14
17
|
import { toCreateMessage } from "../utils/toCreateMessage";
|
|
@@ -20,7 +23,6 @@ import {
|
|
|
20
23
|
aiSDKV5FormatAdapter,
|
|
21
24
|
} from "../adapters/aiSDKFormatAdapter";
|
|
22
25
|
import { useExternalHistory } from "./useExternalHistory";
|
|
23
|
-
import { useMemo } from "react";
|
|
24
26
|
|
|
25
27
|
export type AISDKRuntimeAdapter = {
|
|
26
28
|
adapters?:
|
|
@@ -35,22 +37,49 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
35
37
|
{ adapters }: AISDKRuntimeAdapter = {},
|
|
36
38
|
) => {
|
|
37
39
|
const contextAdapters = useRuntimeAdapters();
|
|
40
|
+
const isRunning =
|
|
41
|
+
chatHelpers.status === "submitted" || chatHelpers.status == "streaming";
|
|
42
|
+
|
|
43
|
+
const [toolStatuses, setToolStatuses] = useState<
|
|
44
|
+
Record<string, ToolExecutionStatus>
|
|
45
|
+
>({});
|
|
46
|
+
|
|
38
47
|
const messages = AISDKMessageConverter.useThreadMessages({
|
|
39
|
-
isRunning
|
|
40
|
-
chatHelpers.status === "submitted" || chatHelpers.status == "streaming",
|
|
48
|
+
isRunning,
|
|
41
49
|
messages: chatHelpers.messages,
|
|
50
|
+
metadata: useMemo(() => ({ toolStatuses }), [toolStatuses]),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const runtimeRef = useMemo(
|
|
54
|
+
() => ({
|
|
55
|
+
get current(): AssistantRuntime {
|
|
56
|
+
return runtime;
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
[],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const toolInvocations = INTERNAL.useToolInvocations({
|
|
64
|
+
state: {
|
|
65
|
+
messages,
|
|
66
|
+
isRunning,
|
|
67
|
+
},
|
|
68
|
+
getTools: () => runtimeRef.current.thread.getModelContext().tools,
|
|
69
|
+
onResult: (command: any) => {
|
|
70
|
+
if (command.type === "add-tool-result") {
|
|
71
|
+
chatHelpers.addToolResult({
|
|
72
|
+
tool: command.toolName,
|
|
73
|
+
toolCallId: command.toolCallId,
|
|
74
|
+
output: command.result,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
setToolStatuses,
|
|
42
79
|
});
|
|
43
80
|
|
|
44
81
|
const isLoading = useExternalHistory(
|
|
45
|
-
|
|
46
|
-
() => ({
|
|
47
|
-
get current(): AssistantRuntime {
|
|
48
|
-
return runtime;
|
|
49
|
-
},
|
|
50
|
-
}),
|
|
51
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
-
[],
|
|
53
|
-
),
|
|
82
|
+
runtimeRef,
|
|
54
83
|
adapters?.history ?? contextAdapters?.history,
|
|
55
84
|
AISDKMessageConverter.toThreadMessages as (
|
|
56
85
|
messages: UI_MESSAGE[],
|
|
@@ -65,8 +94,7 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
65
94
|
);
|
|
66
95
|
|
|
67
96
|
const runtime = useExternalStoreRuntime({
|
|
68
|
-
isRunning
|
|
69
|
-
chatHelpers.status === "submitted" || chatHelpers.status === "streaming",
|
|
97
|
+
isRunning,
|
|
70
98
|
messages,
|
|
71
99
|
setMessages: (messages) =>
|
|
72
100
|
chatHelpers.setMessages(
|
|
@@ -82,7 +110,10 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
82
110
|
.filter(Boolean)
|
|
83
111
|
.flat(),
|
|
84
112
|
),
|
|
85
|
-
onCancel: async () =>
|
|
113
|
+
onCancel: async () => {
|
|
114
|
+
chatHelpers.stop();
|
|
115
|
+
toolInvocations.abort();
|
|
116
|
+
},
|
|
86
117
|
onNew: async (message) => {
|
|
87
118
|
const createMessage = toCreateMessage<UI_MESSAGE>(message);
|
|
88
119
|
await chatHelpers.sendMessage(createMessage, {
|
|
@@ -114,6 +145,8 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
114
145
|
output: result,
|
|
115
146
|
});
|
|
116
147
|
},
|
|
148
|
+
onResumeToolCall: (options) =>
|
|
149
|
+
toolInvocations.resume(options.toolCallId, options.payload),
|
|
117
150
|
adapters: {
|
|
118
151
|
attachments: vercelAttachmentAdapter,
|
|
119
152
|
...contextAdapters,
|
|
@@ -30,38 +30,12 @@ export const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
|
30
30
|
const chat = useChat({
|
|
31
31
|
...chatOptions,
|
|
32
32
|
transport,
|
|
33
|
-
onToolCall: async ({ toolCall }) => {
|
|
34
|
-
await chatOptions.onToolCall?.({ toolCall });
|
|
35
|
-
|
|
36
|
-
const tools = runtime.thread.getModelContext().tools;
|
|
37
|
-
const tool = tools?.[toolCall.toolName];
|
|
38
|
-
if (tool) {
|
|
39
|
-
try {
|
|
40
|
-
const result = await tool.execute?.(toolCall.input, {
|
|
41
|
-
toolCallId: toolCall.toolCallId,
|
|
42
|
-
abortSignal: new AbortController().signal, // dummy signal for now
|
|
43
|
-
});
|
|
44
|
-
chat.addToolResult({
|
|
45
|
-
tool: toolCall.toolName,
|
|
46
|
-
toolCallId: toolCall.toolCallId,
|
|
47
|
-
output: result,
|
|
48
|
-
});
|
|
49
|
-
} catch (error) {
|
|
50
|
-
chat.addToolResult({
|
|
51
|
-
tool: toolCall.toolName,
|
|
52
|
-
toolCallId: toolCall.toolCallId,
|
|
53
|
-
output: {
|
|
54
|
-
error: error instanceof Error ? error.message : String(error),
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
33
|
});
|
|
61
34
|
|
|
62
|
-
const runtime = useAISDKRuntime(chat
|
|
35
|
+
const runtime = useAISDKRuntime(chat, {
|
|
63
36
|
adapters,
|
|
64
37
|
});
|
|
38
|
+
|
|
65
39
|
if (transport instanceof AssistantChatTransport) {
|
|
66
40
|
transport.setRuntime(runtime);
|
|
67
41
|
}
|
|
@@ -5,9 +5,17 @@ import {
|
|
|
5
5
|
type ToolCallMessagePart,
|
|
6
6
|
type TextMessagePart,
|
|
7
7
|
type SourceMessagePart,
|
|
8
|
+
type useExternalMessageConverter,
|
|
8
9
|
} from "@assistant-ui/react";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
function stripClosingDelimiters(json: string) {
|
|
12
|
+
return json.replace(/[}\]"]+$/, "");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const convertParts = (
|
|
16
|
+
message: UIMessage,
|
|
17
|
+
metadata: useExternalMessageConverter.Metadata,
|
|
18
|
+
) => {
|
|
11
19
|
if (!message.parts || message.parts.length === 0) {
|
|
12
20
|
return [];
|
|
13
21
|
}
|
|
@@ -57,14 +65,29 @@ const convertParts = (message: UIMessage) => {
|
|
|
57
65
|
result = { error: part.errorText };
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
let argsText = JSON.stringify(args);
|
|
69
|
+
if (part.state === "input-streaming") {
|
|
70
|
+
// the argsText is not complete, so we need to strip the closing delimiters
|
|
71
|
+
// these are added by the AI SDK in fix-json
|
|
72
|
+
argsText = stripClosingDelimiters(argsText);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const toolStatus = metadata.toolStatuses?.[toolCallId];
|
|
60
76
|
return {
|
|
61
77
|
type: "tool-call",
|
|
62
78
|
toolName,
|
|
63
79
|
toolCallId,
|
|
64
|
-
argsText
|
|
80
|
+
argsText,
|
|
65
81
|
args,
|
|
66
82
|
result,
|
|
67
83
|
isError,
|
|
84
|
+
...(toolStatus?.type === "interrupt" && {
|
|
85
|
+
interrupt: toolStatus.payload,
|
|
86
|
+
status: {
|
|
87
|
+
type: "requires-action" as const,
|
|
88
|
+
reason: "interrupt",
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
68
91
|
} satisfies ToolCallMessagePart;
|
|
69
92
|
}
|
|
70
93
|
|
|
@@ -92,6 +115,7 @@ const convertParts = (message: UIMessage) => {
|
|
|
92
115
|
result = { error: part.errorText };
|
|
93
116
|
}
|
|
94
117
|
|
|
118
|
+
const toolStatus = metadata.toolStatuses?.[toolCallId];
|
|
95
119
|
return {
|
|
96
120
|
type: "tool-call",
|
|
97
121
|
toolName,
|
|
@@ -100,6 +124,13 @@ const convertParts = (message: UIMessage) => {
|
|
|
100
124
|
args,
|
|
101
125
|
result,
|
|
102
126
|
isError,
|
|
127
|
+
...(toolStatus?.type === "interrupt" && {
|
|
128
|
+
interrupt: toolStatus.payload,
|
|
129
|
+
status: {
|
|
130
|
+
type: "requires-action" as const,
|
|
131
|
+
reason: "interrupt",
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
103
134
|
} satisfies ToolCallMessagePart;
|
|
104
135
|
}
|
|
105
136
|
|
|
@@ -141,7 +172,7 @@ const convertParts = (message: UIMessage) => {
|
|
|
141
172
|
};
|
|
142
173
|
|
|
143
174
|
export const AISDKMessageConverter = unstable_createMessageConverter(
|
|
144
|
-
(message: UIMessage) => {
|
|
175
|
+
(message: UIMessage, metadata: useExternalMessageConverter.Metadata) => {
|
|
145
176
|
// UIMessage doesn't have createdAt, so we'll use current date or undefined
|
|
146
177
|
const createdAt = new Date();
|
|
147
178
|
switch (message.role) {
|
|
@@ -150,7 +181,7 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
150
181
|
role: "user",
|
|
151
182
|
id: message.id,
|
|
152
183
|
createdAt,
|
|
153
|
-
content: convertParts(message),
|
|
184
|
+
content: convertParts(message, metadata),
|
|
154
185
|
attachments: message.parts
|
|
155
186
|
?.filter((p) => p.type === "file")
|
|
156
187
|
.map((part, idx) => {
|
|
@@ -183,7 +214,7 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
183
214
|
role: "system",
|
|
184
215
|
id: message.id,
|
|
185
216
|
createdAt,
|
|
186
|
-
content: convertParts(message),
|
|
217
|
+
content: convertParts(message, metadata),
|
|
187
218
|
};
|
|
188
219
|
|
|
189
220
|
case "assistant":
|
|
@@ -191,7 +222,7 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
|
|
|
191
222
|
role: "assistant",
|
|
192
223
|
id: message.id,
|
|
193
224
|
createdAt,
|
|
194
|
-
content: convertParts(message),
|
|
225
|
+
content: convertParts(message, metadata),
|
|
195
226
|
metadata: {
|
|
196
227
|
unstable_annotations: (message as any).annotations,
|
|
197
228
|
unstable_data: Array.isArray((message as any).data)
|