@assistant-ui/react-ai-sdk 1.3.32 → 1.3.33
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/assistant-stream/dist/core/tool/schema-utils.js.map +1 -1
- package/dist/client.d.ts +10 -0
- package/dist/client.js +9 -0
- package/dist/frontendTools.js.map +1 -1
- package/dist/generativeTools.d.ts +17 -3
- package/dist/generativeTools.d.ts.map +1 -1
- package/dist/generativeTools.js +14 -5
- package/dist/generativeTools.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.native.d.ts +10 -0
- package/dist/index.native.js +9 -0
- package/dist/injectQuoteContext.js.map +1 -1
- package/dist/mcp-stdio.node.d.ts +2 -0
- package/dist/mcp-stdio.node.js +2 -0
- package/dist/mcp-stdio.unsupported.d.ts +7 -0
- package/dist/mcp-stdio.unsupported.d.ts.map +1 -0
- package/dist/mcp-stdio.unsupported.js +11 -0
- package/dist/mcp-stdio.unsupported.js.map +1 -0
- package/dist/ui/use-chat/AssistantChatTransport.js.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.d.ts +10 -0
- package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.js +4 -3
- package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.d.ts +1 -0
- package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.js +3 -2
- package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
- package/dist/ui/use-chat/useExternalHistory.js.map +1 -1
- package/dist/ui/use-chat/useStreamingTiming.js +1 -1
- package/dist/ui/utils/convertMessage.d.ts +1 -1
- package/dist/ui/utils/convertMessage.d.ts.map +1 -1
- package/dist/ui/utils/convertMessage.js.map +1 -1
- package/dist/ui/utils/sliceMessagesUntil.js.map +1 -1
- package/dist/ui/utils/toCreateMessage.js.map +1 -1
- package/dist/usage.js.map +1 -1
- package/package.json +27 -11
- package/src/client.ts +18 -0
- package/src/generativeTools.test.ts +29 -47
- package/src/generativeTools.ts +18 -4
- package/src/index.native.ts +3 -0
- package/src/index.ts +1 -16
- package/src/mcp-stdio.node.ts +1 -0
- package/src/mcp-stdio.unsupported.ts +12 -0
- package/src/ui/use-chat/useAISDKRuntime.test.ts +56 -0
- package/src/ui/use-chat/useAISDKRuntime.ts +12 -0
- package/src/ui/use-chat/useChatRuntime.ts +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExternalHistory.js","names":[],"sources":["../../../src/ui/use-chat/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem() : null),\n [aui],\n );\n\n const [isLoading, setIsLoading] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return;\n\n const loadHistory = async () => {\n setIsLoading(true);\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set(\n converted.messages.map((m) => m.message.id),\n );\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n loadedRef.current = true;\n\n if (!optionalThreadListItem()?.getState().remoteId) {\n setIsLoading(false);\n return;\n }\n\n loadHistory();\n }, [formatAdapter, toThreadMessages, runtimeRef, optionalThreadListItem]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const { isRunning } = runtimeRef.current.thread.getState();\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = runtimeRef.current.thread.getState().messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(async () => {\n persistTimerRef.current = null;\n\n // Re-read latest state — may have changed since the timeout was scheduled\n const latest = runtimeRef.current.thread.getState();\n if (latest.isRunning) return; // was just a flicker\n\n // Derive durationMs from the last boundary (covers all steps)\n const boundaries = stepBoundariesRef.current;\n const durationMs =\n boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isReady =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (historyIds.current.has(message.id)) {\n if (durationMs !== undefined) {\n let parentId = lastInnerMessageId;\n for (const innerMessage of innerMessages) {\n try {\n await formatAdapter.update?.(\n { parentId, message: innerMessage },\n storageFormatAdapter.getId(innerMessage),\n );\n } catch {\n // ignore update failures to avoid breaking the message processing loop\n }\n parentId = storageFormatAdapter.getId(innerMessage);\n }\n }\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n historyIds.current.add(message.id);\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n await formatAdapter.append(item);\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n formatAdapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }, 0);\n });\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n return isLoading;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useExternalHistory.js","names":[],"sources":["../../../src/ui/use-chat/useExternalHistory.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n MessageFormatRepository,\n ExportedMessageRepository,\n} from \"@assistant-ui/core\";\nimport { getExternalStoreMessages } from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n useRef,\n useEffect,\n useState,\n type RefObject,\n useCallback,\n useMemo,\n} from \"react\";\n\nexport const toExportedMessageRepository = <TMessage>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n const survivingIds = new Set<string>();\n const survivors = messages.messages.flatMap((m) => {\n const message = toThreadMessages([m.message])[0];\n if (!message) {\n console.warn(\"Skipping a stored message that could not be loaded.\");\n return [];\n }\n if (m.parentId && !survivingIds.has(m.parentId)) return [];\n survivingIds.add(message.id);\n return [{ ...m, message }];\n });\n\n return {\n headId:\n messages.headId && survivingIds.has(messages.headId)\n ? messages.headId\n : null,\n messages: survivors,\n };\n};\n\nexport const useExternalHistory = <TMessage>(\n runtimeRef: RefObject<AssistantRuntime>,\n historyAdapter: ThreadHistoryAdapter | undefined,\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n storageFormatAdapter: MessageFormatAdapter<TMessage, any>,\n onSetMessages: (messages: TMessage[]) => void,\n) => {\n const loadedRef = useRef(false);\n\n const aui = useAui();\n const optionalThreadListItem = useCallback(\n () => (aui.threadListItem.source ? aui.threadListItem() : null),\n [aui],\n );\n\n const [isLoading, setIsLoading] = useState(false);\n\n const historyIds = useRef(new Set<string>());\n\n const onSetMessagesRef = useRef(onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n const formatAdapter = useMemo(() => {\n if (!historyAdapter) return undefined;\n if (!historyAdapter.withFormat) {\n throw new Error(\n \"useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.\",\n );\n }\n return historyAdapter.withFormat<TMessage, any>(storageFormatAdapter);\n }, [historyAdapter, storageFormatAdapter]);\n\n useEffect(() => {\n if (!formatAdapter || loadedRef.current) return;\n\n const loadHistory = async () => {\n setIsLoading(true);\n try {\n const repo = await formatAdapter.load();\n if (repo && repo.messages.length > 0) {\n const converted = toExportedMessageRepository(toThreadMessages, repo);\n runtimeRef.current.thread.import(converted);\n\n const tempRepo = new MessageRepository();\n tempRepo.import(converted);\n const messages = tempRepo.getMessages();\n\n onSetMessagesRef.current(\n messages.flatMap(getExternalStoreMessages<TMessage>),\n );\n\n historyIds.current = new Set(\n converted.messages.map((m) => m.message.id),\n );\n }\n } catch (error) {\n console.error(\"Failed to load message history:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n loadedRef.current = true;\n\n if (!optionalThreadListItem()?.getState().remoteId) {\n setIsLoading(false);\n return;\n }\n\n loadHistory();\n }, [formatAdapter, toThreadMessages, runtimeRef, optionalThreadListItem]);\n\n const runStartRef = useRef<number | null>(null);\n const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const stepBoundariesRef = useRef<number[]>([]);\n const wasRunningRef = useRef(false);\n const toolCallCountRef = useRef(0);\n\n useEffect(() => {\n if (!formatAdapter) return;\n\n const unsubscribe = runtimeRef.current.thread.subscribe(() => {\n const { isRunning } = runtimeRef.current.thread.getState();\n const wasRunning = wasRunningRef.current;\n wasRunningRef.current = isRunning;\n\n // Track step boundaries by content changes (more reliable than isRunning)\n if (runStartRef.current != null) {\n const lastMsg = runtimeRef.current.thread.getState().messages.at(-1);\n if (lastMsg?.role === \"assistant\") {\n const currentToolCallCount = lastMsg.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n while (toolCallCountRef.current < currentToolCallCount) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n toolCallCountRef.current++;\n }\n }\n }\n\n if (isRunning) {\n if (runStartRef.current == null) {\n runStartRef.current = Date.now();\n stepBoundariesRef.current = [];\n toolCallCountRef.current = 0;\n }\n // Cancel any pending persist — isRunning went back to true\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n return;\n }\n\n // Only act on the true→false transition\n if (!wasRunning) return;\n\n // Record step boundary offset (synchronous for accuracy)\n if (runStartRef.current != null) {\n stepBoundariesRef.current.push(Date.now() - runStartRef.current);\n }\n\n // Debounce: wait one macrotask so agentic step flickers are absorbed\n if (persistTimerRef.current) clearTimeout(persistTimerRef.current);\n persistTimerRef.current = setTimeout(async () => {\n persistTimerRef.current = null;\n\n // Re-read latest state — may have changed since the timeout was scheduled\n const latest = runtimeRef.current.thread.getState();\n if (latest.isRunning) return; // was just a flicker\n\n // Derive durationMs from the last boundary (covers all steps)\n const boundaries = stepBoundariesRef.current;\n const durationMs =\n boundaries.length > 0 ? boundaries.at(-1) : undefined;\n\n // Fallback: if only 1 boundary but message has multiple steps, distribute evenly\n if (boundaries.length === 1 && durationMs != null) {\n const lastAssistant = latest.messages.findLast(\n (m) => m.role === \"assistant\",\n );\n if (lastAssistant) {\n const tcCount = lastAssistant.content.filter(\n (p) => p.type === \"tool-call\",\n ).length;\n if (tcCount > 0) {\n const totalSteps = tcCount + 1;\n const stepDur = durationMs / totalSteps;\n boundaries.length = 0;\n for (let i = 0; i < totalSteps; i++) {\n boundaries.push(Math.round((i + 1) * stepDur));\n }\n }\n }\n }\n\n // Build per-step timestamps when there are multiple steps\n const stepTimestamps =\n boundaries.length > 1\n ? boundaries.map((endMs, i) => ({\n start_ms: i === 0 ? 0 : boundaries[i - 1]!,\n end_ms: endMs,\n }))\n : undefined;\n\n runStartRef.current = null;\n stepBoundariesRef.current = [];\n\n const telemetryOptions = {\n ...(durationMs != null ? { durationMs } : undefined),\n ...(stepTimestamps != null ? { stepTimestamps } : undefined),\n };\n\n const { messages } = latest;\n let lastInnerMessageId: string | null = null;\n\n const getLastInnerId = (msgs: TMessage[]): string | null =>\n msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;\n\n const toBatchItems = (msgs: TMessage[]) =>\n msgs.map((msg, idx) => ({\n parentId:\n idx === 0\n ? lastInnerMessageId\n : storageFormatAdapter.getId(msgs[idx - 1]!),\n message: msg,\n }));\n\n for (const message of messages) {\n const innerMessages = getExternalStoreMessages<TMessage>(message);\n\n const isReady =\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\";\n\n if (!isReady) {\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n\n if (historyIds.current.has(message.id)) {\n if (durationMs !== undefined) {\n let parentId = lastInnerMessageId;\n for (const innerMessage of innerMessages) {\n try {\n await formatAdapter.update?.(\n { parentId, message: innerMessage },\n storageFormatAdapter.getId(innerMessage),\n );\n } catch {\n // ignore update failures to avoid breaking the message processing loop\n }\n parentId = storageFormatAdapter.getId(innerMessage);\n }\n }\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n continue;\n }\n historyIds.current.add(message.id);\n\n const batchItems = toBatchItems(innerMessages);\n for (const item of batchItems) {\n await formatAdapter.append(item);\n }\n\n lastInnerMessageId =\n getLastInnerId(innerMessages) ?? lastInnerMessageId;\n\n formatAdapter.reportTelemetry?.(batchItems, telemetryOptions);\n }\n }, 0);\n });\n\n return () => {\n unsubscribe();\n if (persistTimerRef.current) {\n clearTimeout(persistTimerRef.current);\n persistTimerRef.current = null;\n }\n };\n }, [formatAdapter, storageFormatAdapter, runtimeRef]);\n\n return isLoading;\n};\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ declare const AISDKMessageConverter: {
|
|
|
19
19
|
}: {
|
|
20
20
|
messages: UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[];
|
|
21
21
|
isRunning: boolean;
|
|
22
|
-
joinStrategy?: "
|
|
22
|
+
joinStrategy?: import("@assistant-ui/core/react").JoinStrategy | undefined;
|
|
23
23
|
metadata?: useExternalMessageConverter.Metadata;
|
|
24
24
|
}) => import("@assistant-ui/core").ThreadMessage[];
|
|
25
25
|
toThreadMessages: (messages: UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], isRunning?: boolean, metadata?: useExternalMessageConverter.Metadata) => import("@assistant-ui/core").ThreadMessage[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../../src/ui/utils/convertMessage.ts"],"mappings":";;;;;;KAqBY,6BAAA,GACV,2BAAA,CAA4B,QAAA;EAC1B,qBAAA,GAAwB,GAAA,SAAY,GAAA;EACpC,kBAAA,GAAqB,GAAA,SAAY,kBAAA;EACjC,mBAAA,GAAsB,GAAA,SAAY,cAAA,GAJG;EAMrC,mBAAA;AAAA;AAAA,cAyUS,qBAAA;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../../src/ui/utils/convertMessage.ts"],"mappings":";;;;;;KAqBY,6BAAA,GACV,2BAAA,CAA4B,QAAA;EAC1B,qBAAA,GAAwB,GAAA,SAAY,GAAA;EACpC,kBAAA,GAAqB,GAAA,SAAY,kBAAA;EACjC,mBAAA,GAAsB,GAAA,SAAY,cAAA,GAJG;EAMrC,mBAAA;AAAA;AAAA,cAyUS,qBAAA;;;;;;;;;;;;;mJA7U0C,aAAA;kJAEf,aAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.js","names":["unstable_createMessageConverter"],"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, getToolName, type UIMessage } from \"ai\";\nimport {\n createMessageConverter as unstable_createMessageConverter,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport {\n isMcpAppUri,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type DataMessagePart,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport { unwrapModelContentEnvelope } from \"../../modelContentEnvelope\";\n\ntype MessageMetadata = ThreadMessageLike[\"metadata\"];\nexport type AISDKMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n toolArgsKeyOrderCache?: Map<string, Map<string, string[]>>;\n toolLastInputCache?: Map<string, ReadonlyJSONObject>;\n mcpAppMetadataCache?: Map<string, McpAppMetadata>;\n /** Id of the currently-streaming message, flagged optimistic (#4037). */\n optimisticMessageId?: string | undefined;\n };\n\nfunction stripClosingDelimiters(json: string): string {\n return json.replace(/[}\\]\"]+$/, \"\");\n}\n\nconst MCP_APP_METADATA_CACHE_MAX = 100;\n\nfunction extractMcpAppMetadata(\n part: unknown,\n cache: Map<string, McpAppMetadata> | undefined,\n): McpAppMetadata | undefined {\n if (!part || typeof part !== \"object\") return undefined;\n const meta = (part as { callProviderMetadata?: unknown })\n .callProviderMetadata;\n const mcp =\n meta && typeof meta === \"object\"\n ? (meta as { mcp?: unknown }).mcp\n : undefined;\n const app =\n mcp && typeof mcp === \"object\" ? (mcp as { app?: unknown }).app : undefined;\n let a: Record<string, unknown>;\n if (app && typeof app === \"object\") {\n a = app as Record<string, unknown>;\n } else {\n // MCP-UI tools (e.g. xmcp) surface the UI pointer as\n // result._meta[\"ui/resourceUri\"] rather than in callProviderMetadata.\n const output = (part as { output?: unknown }).output;\n const outMeta =\n output && typeof output === \"object\"\n ? (output as { _meta?: unknown })._meta\n : undefined;\n const uiResourceUri =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof uiResourceUri !== \"string\") return undefined;\n a = { resourceUri: uiResourceUri };\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cached = cache?.get(a[\"resourceUri\"]);\n if (cached) {\n cache!.delete(a[\"resourceUri\"]);\n cache!.set(a[\"resourceUri\"], cached);\n return cached;\n }\n const out: { -readonly [K in keyof McpAppMetadata]: McpAppMetadata[K] } = {\n resourceUri: a[\"resourceUri\"],\n };\n if (typeof a[\"mimeType\"] === \"string\") out.mimeType = a[\"mimeType\"];\n if (Array.isArray(a[\"visibility\"])) {\n out.visibility = a[\"visibility\"].filter(\n (v): v is \"model\" | \"app\" => v === \"model\" || v === \"app\",\n );\n }\n if (cache) {\n if (cache.size >= MCP_APP_METADATA_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n cache.set(a[\"resourceUri\"], out);\n }\n return out;\n}\n\nconst hasOwn = (value: object, key: string) => Object.hasOwn(value, key);\n\nconst stabilizeToolArgsValue = (\n value: unknown,\n path: string,\n keyOrderByPath: Map<string, string[]>,\n): unknown => {\n if (Array.isArray(value)) {\n return value.map((item, idx) =>\n stabilizeToolArgsValue(item, `${path}[${idx}]`, keyOrderByPath),\n );\n }\n\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const currentKeys = Object.keys(record);\n const previousOrder = keyOrderByPath.get(path) ?? [];\n const previousOrderSet = new Set(previousOrder);\n const nextOrder = [\n ...previousOrder.filter((key) => hasOwn(record, key)),\n ...currentKeys.filter((key) => !previousOrderSet.has(key)),\n ];\n keyOrderByPath.set(path, nextOrder);\n\n return Object.fromEntries(\n nextOrder.map((key) => [\n key,\n stabilizeToolArgsValue(record[key], `${path}.${key}`, keyOrderByPath),\n ]),\n );\n }\n\n return value;\n};\n\nfunction stableStringifyToolArgs(\n keyOrderCache: Map<string, Map<string, string[]>> | undefined,\n cacheKey: string,\n args: ReadonlyJSONObject,\n): string {\n const keyOrderByPath = keyOrderCache?.get(cacheKey) ?? new Map();\n keyOrderCache?.set(cacheKey, keyOrderByPath);\n\n const stableArgs = stabilizeToolArgsValue(\n args,\n \"$\",\n keyOrderByPath,\n ) as ReadonlyJSONObject;\n return JSON.stringify(stableArgs);\n}\n\nfunction getToolApprovalAndInterrupt(\n part: {\n approval?:\n | {\n id: string;\n approved?: boolean;\n reason?: string;\n isAutomatic?: boolean;\n }\n | undefined;\n },\n toolStatus: { type: string; payload?: unknown } | undefined,\n): {\n approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n interrupt?: NonNullable<ToolCallMessagePart[\"interrupt\"]>;\n} {\n if (part.approval && typeof part.approval.id === \"string\") {\n const { id, approved, reason, isAutomatic } = part.approval;\n return {\n approval: {\n id,\n ...(typeof approved === \"boolean\" && { approved }),\n ...(typeof reason === \"string\" && { reason }),\n ...(isAutomatic === true && { isAutomatic: true }),\n },\n };\n }\n\n if (toolStatus?.type === \"interrupt\") {\n return {\n interrupt: toolStatus.payload as NonNullable<\n ToolCallMessagePart[\"interrupt\"]\n >,\n };\n }\n\n return {};\n}\n\ntype MessageContent = Exclude<ThreadMessageLike[\"content\"], string>;\n\nfunction convertParts(\n message: UIMessage,\n metadata: AISDKMessageConverterMetadata,\n): MessageContent {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n const converted = message.parts\n .filter(\n (p) =>\n p.type !== \"step-start\" &&\n (message.role !== \"user\" || p.type !== \"file\"),\n )\n .map((part) => {\n if (part.type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n if (isToolUIPart(part)) {\n const toolName = getToolName(part);\n const toolCallId = part.toolCallId;\n const argsKeyOrderCacheKey = `${message.id}:${toolCallId}`;\n\n const rawInput = part.input as ReadonlyJSONObject | null | undefined;\n let args: ReadonlyJSONObject;\n if (\n rawInput != null &&\n typeof rawInput === \"object\" &&\n !Array.isArray(rawInput)\n ) {\n args = rawInput;\n metadata.toolLastInputCache?.set(argsKeyOrderCacheKey, args);\n } else {\n args = metadata.toolLastInputCache?.get(argsKeyOrderCacheKey) ?? {};\n }\n\n let result: unknown;\n let modelContent: ToolCallMessagePart[\"modelContent\"];\n let isError = false;\n\n if (part.state === \"output-available\") {\n const unwrapped = unwrapModelContentEnvelope(part.output);\n result = unwrapped.result;\n modelContent = unwrapped.modelContent;\n } else if (part.state === \"output-error\") {\n isError = true;\n result = { error: part.errorText };\n } else if (part.state === \"output-denied\") {\n isError = true;\n result = {\n error:\n (part as { approval?: { reason?: string } }).approval?.reason ||\n \"Tool approval denied\",\n };\n }\n\n let argsText = stableStringifyToolArgs(\n metadata.toolArgsKeyOrderCache,\n argsKeyOrderCacheKey,\n args,\n );\n if (part.state === \"input-streaming\") {\n // strip closing delimiters added by the AI SDK's fix-json\n argsText = stripClosingDelimiters(argsText);\n } else {\n metadata.toolArgsKeyOrderCache?.delete(argsKeyOrderCacheKey);\n if (\n part.state === \"output-available\" ||\n part.state === \"output-error\" ||\n part.state === \"output-denied\"\n ) {\n metadata.toolLastInputCache?.delete(argsKeyOrderCacheKey);\n }\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n const mcpApp = extractMcpAppMetadata(\n part,\n metadata.mcpAppMetadataCache,\n );\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText,\n args,\n result,\n isError,\n ...(modelContent !== undefined && { modelContent }),\n ...(mcpApp && { mcp: { app: mcpApp } }),\n ...getToolApprovalAndInterrupt(part, toolStatus),\n } satisfies ToolCallMessagePart;\n }\n\n if (part.type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n ...(part.title != null ? { title: part.title } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type === \"file\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n ...(part.filename != null && { filename: part.filename }),\n } satisfies FileMessagePart;\n }\n\n if (part.type === \"source-document\") {\n return {\n type: \"source\",\n sourceType: \"document\",\n id: part.sourceId,\n title: part.title,\n mediaType: part.mediaType,\n ...(part.filename != null ? { filename: part.filename } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type.startsWith(\"data-\")) {\n return {\n type: \"data\",\n name: part.type.substring(5),\n data: (part as any).data,\n } satisfies DataMessagePart;\n }\n\n console.warn(`Unsupported message part type: ${part.type}`);\n return null;\n })\n .filter(Boolean) as MessageContent[number][];\n\n const seenToolCallIds = new Set<string>();\n return converted.filter((part) => {\n if (part.type === \"tool-call\" && part.toolCallId != null) {\n if (seenToolCallIds.has(part.toolCallId)) return false;\n seenToolCallIds.add(part.toolCallId);\n }\n return true;\n });\n}\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage, metadata: AISDKMessageConverterMetadata) => {\n const createdAt = new Date();\n const content = convertParts(message, metadata);\n\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content,\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => ({\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 metadata: message.metadata as MessageMetadata,\n };\n\n case \"system\":\n case \"assistant\": {\n const timing = metadata.messageTiming?.[message.id];\n const isOptimistic =\n message.role === \"assistant\" &&\n message.id === metadata.optimisticMessageId;\n return {\n role: message.role,\n id: message.id,\n createdAt,\n content,\n metadata: {\n ...(message.metadata as MessageMetadata),\n ...(timing && { timing }),\n ...(isOptimistic && { isOptimistic: true }),\n },\n };\n }\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"convertMessage.js","names":["unstable_createMessageConverter"],"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, getToolName, type UIMessage } from \"ai\";\nimport {\n createMessageConverter as unstable_createMessageConverter,\n type useExternalMessageConverter,\n} from \"@assistant-ui/core/react\";\nimport {\n isMcpAppUri,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type DataMessagePart,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport { unwrapModelContentEnvelope } from \"../../modelContentEnvelope\";\n\ntype MessageMetadata = ThreadMessageLike[\"metadata\"];\nexport type AISDKMessageConverterMetadata =\n useExternalMessageConverter.Metadata & {\n toolArgsKeyOrderCache?: Map<string, Map<string, string[]>>;\n toolLastInputCache?: Map<string, ReadonlyJSONObject>;\n mcpAppMetadataCache?: Map<string, McpAppMetadata>;\n /** Id of the currently-streaming message, flagged optimistic (#4037). */\n optimisticMessageId?: string | undefined;\n };\n\nfunction stripClosingDelimiters(json: string): string {\n return json.replace(/[}\\]\"]+$/, \"\");\n}\n\nconst MCP_APP_METADATA_CACHE_MAX = 100;\n\nfunction extractMcpAppMetadata(\n part: unknown,\n cache: Map<string, McpAppMetadata> | undefined,\n): McpAppMetadata | undefined {\n if (!part || typeof part !== \"object\") return undefined;\n const meta = (part as { callProviderMetadata?: unknown })\n .callProviderMetadata;\n const mcp =\n meta && typeof meta === \"object\"\n ? (meta as { mcp?: unknown }).mcp\n : undefined;\n const app =\n mcp && typeof mcp === \"object\" ? (mcp as { app?: unknown }).app : undefined;\n let a: Record<string, unknown>;\n if (app && typeof app === \"object\") {\n a = app as Record<string, unknown>;\n } else {\n // MCP-UI tools (e.g. xmcp) surface the UI pointer as\n // result._meta[\"ui/resourceUri\"] rather than in callProviderMetadata.\n const output = (part as { output?: unknown }).output;\n const outMeta =\n output && typeof output === \"object\"\n ? (output as { _meta?: unknown })._meta\n : undefined;\n const uiResourceUri =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof uiResourceUri !== \"string\") return undefined;\n a = { resourceUri: uiResourceUri };\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cached = cache?.get(a[\"resourceUri\"]);\n if (cached) {\n cache!.delete(a[\"resourceUri\"]);\n cache!.set(a[\"resourceUri\"], cached);\n return cached;\n }\n const out: { -readonly [K in keyof McpAppMetadata]: McpAppMetadata[K] } = {\n resourceUri: a[\"resourceUri\"],\n };\n if (typeof a[\"mimeType\"] === \"string\") out.mimeType = a[\"mimeType\"];\n if (Array.isArray(a[\"visibility\"])) {\n out.visibility = a[\"visibility\"].filter(\n (v): v is \"model\" | \"app\" => v === \"model\" || v === \"app\",\n );\n }\n if (cache) {\n if (cache.size >= MCP_APP_METADATA_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n cache.set(a[\"resourceUri\"], out);\n }\n return out;\n}\n\nconst hasOwn = (value: object, key: string) => Object.hasOwn(value, key);\n\nconst stabilizeToolArgsValue = (\n value: unknown,\n path: string,\n keyOrderByPath: Map<string, string[]>,\n): unknown => {\n if (Array.isArray(value)) {\n return value.map((item, idx) =>\n stabilizeToolArgsValue(item, `${path}[${idx}]`, keyOrderByPath),\n );\n }\n\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const currentKeys = Object.keys(record);\n const previousOrder = keyOrderByPath.get(path) ?? [];\n const previousOrderSet = new Set(previousOrder);\n const nextOrder = [\n ...previousOrder.filter((key) => hasOwn(record, key)),\n ...currentKeys.filter((key) => !previousOrderSet.has(key)),\n ];\n keyOrderByPath.set(path, nextOrder);\n\n return Object.fromEntries(\n nextOrder.map((key) => [\n key,\n stabilizeToolArgsValue(record[key], `${path}.${key}`, keyOrderByPath),\n ]),\n );\n }\n\n return value;\n};\n\nfunction stableStringifyToolArgs(\n keyOrderCache: Map<string, Map<string, string[]>> | undefined,\n cacheKey: string,\n args: ReadonlyJSONObject,\n): string {\n const keyOrderByPath = keyOrderCache?.get(cacheKey) ?? new Map();\n keyOrderCache?.set(cacheKey, keyOrderByPath);\n\n const stableArgs = stabilizeToolArgsValue(\n args,\n \"$\",\n keyOrderByPath,\n ) as ReadonlyJSONObject;\n return JSON.stringify(stableArgs);\n}\n\nfunction getToolApprovalAndInterrupt(\n part: {\n approval?:\n | {\n id: string;\n approved?: boolean;\n reason?: string;\n isAutomatic?: boolean;\n }\n | undefined;\n },\n toolStatus: { type: string; payload?: unknown } | undefined,\n): {\n approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n interrupt?: NonNullable<ToolCallMessagePart[\"interrupt\"]>;\n} {\n if (part.approval && typeof part.approval.id === \"string\") {\n const { id, approved, reason, isAutomatic } = part.approval;\n return {\n approval: {\n id,\n ...(typeof approved === \"boolean\" && { approved }),\n ...(typeof reason === \"string\" && { reason }),\n ...(isAutomatic === true && { isAutomatic: true }),\n },\n };\n }\n\n if (toolStatus?.type === \"interrupt\") {\n return {\n interrupt: toolStatus.payload as NonNullable<\n ToolCallMessagePart[\"interrupt\"]\n >,\n };\n }\n\n return {};\n}\n\ntype MessageContent = Exclude<ThreadMessageLike[\"content\"], string>;\n\nfunction convertParts(\n message: UIMessage,\n metadata: AISDKMessageConverterMetadata,\n): MessageContent {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n const converted = message.parts\n .filter(\n (p) =>\n p.type !== \"step-start\" &&\n (message.role !== \"user\" || p.type !== \"file\"),\n )\n .map((part) => {\n if (part.type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n if (isToolUIPart(part)) {\n const toolName = getToolName(part);\n const toolCallId = part.toolCallId;\n const argsKeyOrderCacheKey = `${message.id}:${toolCallId}`;\n\n const rawInput = part.input as ReadonlyJSONObject | null | undefined;\n let args: ReadonlyJSONObject;\n if (\n rawInput != null &&\n typeof rawInput === \"object\" &&\n !Array.isArray(rawInput)\n ) {\n args = rawInput;\n metadata.toolLastInputCache?.set(argsKeyOrderCacheKey, args);\n } else {\n args = metadata.toolLastInputCache?.get(argsKeyOrderCacheKey) ?? {};\n }\n\n let result: unknown;\n let modelContent: ToolCallMessagePart[\"modelContent\"];\n let isError = false;\n\n if (part.state === \"output-available\") {\n const unwrapped = unwrapModelContentEnvelope(part.output);\n result = unwrapped.result;\n modelContent = unwrapped.modelContent;\n } else if (part.state === \"output-error\") {\n isError = true;\n result = { error: part.errorText };\n } else if (part.state === \"output-denied\") {\n isError = true;\n result = {\n error:\n (part as { approval?: { reason?: string } }).approval?.reason ||\n \"Tool approval denied\",\n };\n }\n\n let argsText = stableStringifyToolArgs(\n metadata.toolArgsKeyOrderCache,\n argsKeyOrderCacheKey,\n args,\n );\n if (part.state === \"input-streaming\") {\n // strip closing delimiters added by the AI SDK's fix-json\n argsText = stripClosingDelimiters(argsText);\n } else {\n metadata.toolArgsKeyOrderCache?.delete(argsKeyOrderCacheKey);\n if (\n part.state === \"output-available\" ||\n part.state === \"output-error\" ||\n part.state === \"output-denied\"\n ) {\n metadata.toolLastInputCache?.delete(argsKeyOrderCacheKey);\n }\n }\n\n const toolStatus = metadata.toolStatuses?.[toolCallId];\n const mcpApp = extractMcpAppMetadata(\n part,\n metadata.mcpAppMetadataCache,\n );\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText,\n args,\n result,\n isError,\n ...(modelContent !== undefined && { modelContent }),\n ...(mcpApp && { mcp: { app: mcpApp } }),\n ...getToolApprovalAndInterrupt(part, toolStatus),\n } satisfies ToolCallMessagePart;\n }\n\n if (part.type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n ...(part.title != null ? { title: part.title } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type === \"file\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n ...(part.filename != null && { filename: part.filename }),\n } satisfies FileMessagePart;\n }\n\n if (part.type === \"source-document\") {\n return {\n type: \"source\",\n sourceType: \"document\",\n id: part.sourceId,\n title: part.title,\n mediaType: part.mediaType,\n ...(part.filename != null ? { filename: part.filename } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata:\n part.providerMetadata as SourceProviderMetadata,\n }\n : undefined),\n } satisfies SourceMessagePart;\n }\n\n if (part.type.startsWith(\"data-\")) {\n return {\n type: \"data\",\n name: part.type.substring(5),\n data: (part as any).data,\n } satisfies DataMessagePart;\n }\n\n console.warn(`Unsupported message part type: ${part.type}`);\n return null;\n })\n .filter(Boolean) as MessageContent[number][];\n\n const seenToolCallIds = new Set<string>();\n return converted.filter((part) => {\n if (part.type === \"tool-call\" && part.toolCallId != null) {\n if (seenToolCallIds.has(part.toolCallId)) return false;\n seenToolCallIds.add(part.toolCallId);\n }\n return true;\n });\n}\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage, metadata: AISDKMessageConverterMetadata) => {\n const createdAt = new Date();\n const content = convertParts(message, metadata);\n\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content,\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => ({\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 metadata: message.metadata as MessageMetadata,\n };\n\n case \"system\":\n case \"assistant\": {\n const timing = metadata.messageTiming?.[message.id];\n const isOptimistic =\n message.role === \"assistant\" &&\n message.id === metadata.optimisticMessageId;\n return {\n role: message.role,\n id: message.id,\n createdAt,\n content,\n metadata: {\n ...(message.metadata as MessageMetadata),\n ...(timing && { timing }),\n ...(isOptimistic && { isOptimistic: true }),\n },\n };\n }\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sliceMessagesUntil.js","names":[],"sources":["../../../src/ui/utils/sliceMessagesUntil.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport const sliceMessagesUntil = <UI_MESSAGE extends UIMessage = UIMessage>(\n messages: UI_MESSAGE[],\n messageId: string | null,\n) => {\n if (messageId == null) return [];\n\n let messageIdx = messages.findIndex((m) => m.id === messageId);\n if (messageIdx === -1)\n throw new Error(\n \"useVercelAIThreadState: Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n while (messages[messageIdx + 1]?.role === \"assistant\") {\n messageIdx++;\n }\n\n return messages.slice(0, messageIdx + 1);\n};\n"],"mappings":";AAEA,MAAa,sBACX,UACA,cACG;CACH,IAAI,aAAa,MAAM,OAAO,CAAC;CAE/B,IAAI,aAAa,SAAS,WAAW,MAAM,EAAE,OAAO,SAAS;CAC7D,IAAI,eAAe,IACjB,MAAM,IAAI,MACR,4FACF;CAEF,OAAO,SAAS,aAAa,
|
|
1
|
+
{"version":3,"file":"sliceMessagesUntil.js","names":[],"sources":["../../../src/ui/utils/sliceMessagesUntil.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport const sliceMessagesUntil = <UI_MESSAGE extends UIMessage = UIMessage>(\n messages: UI_MESSAGE[],\n messageId: string | null,\n) => {\n if (messageId == null) return [];\n\n let messageIdx = messages.findIndex((m) => m.id === messageId);\n if (messageIdx === -1)\n throw new Error(\n \"useVercelAIThreadState: Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n while (messages[messageIdx + 1]?.role === \"assistant\") {\n messageIdx++;\n }\n\n return messages.slice(0, messageIdx + 1);\n};\n"],"mappings":";AAEA,MAAa,sBACX,UACA,cACG;CACH,IAAI,aAAa,MAAM,OAAO,CAAC;CAE/B,IAAI,aAAa,SAAS,WAAW,MAAM,EAAE,OAAO,SAAS;CAC7D,IAAI,eAAe,IACjB,MAAM,IAAI,MACR,4FACF;CAEF,OAAO,SAAS,aAAa,EAAE,EAAE,SAAS,aACxC;CAGF,OAAO,SAAS,MAAM,GAAG,aAAa,CAAC;AACzC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toCreateMessage.js","names":[],"sources":["../../../src/ui/utils/toCreateMessage.ts"],"sourcesContent":["import type { AppendMessage } from \"@assistant-ui/core\";\nimport type {\n CreateUIMessage,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\nexport const toCreateMessage = <UI_MESSAGE extends UIMessage = UIMessage>(\n message: AppendMessage,\n): CreateUIMessage<UI_MESSAGE> => {\n const inputParts = [\n ...message.content.filter((c) => c.type !== \"file\"),\n ...(message.attachments?.flatMap((a) =>\n a.content.map((c) => ({\n ...c,\n filename: a.name,\n })),\n ) ?? []),\n ];\n\n const parts = inputParts.map((part): UIMessagePart<UIDataTypes, UITools> => {\n switch (part.type) {\n case \"text\":\n return {\n type: \"text\",\n text: part.text,\n };\n case \"image\":\n return {\n type: \"file\",\n url: part.image,\n ...(part.filename && { filename: part.filename }),\n mediaType: \"image/png\",\n };\n case \"file\":\n return {\n type: \"file\",\n url: part.data,\n mediaType: part.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n case \"data\":\n return {\n type: `data-${part.name}`,\n data: part.data,\n };\n default:\n throw new Error(`Unsupported part type: ${part.type}`);\n }\n });\n\n return {\n role: message.role,\n parts,\n metadata: message.metadata,\n } satisfies CreateUIMessage<UIMessage> as CreateUIMessage<UI_MESSAGE>;\n};\n"],"mappings":";AASA,MAAa,mBACX,YACgC;CAWhC,MAAM,QAAQ,CATZ,GAAG,QAAQ,QAAQ,QAAQ,MAAM,EAAE,SAAS,MAAM,GAClD,GAAI,QAAQ,aAAa,SAAS,MAChC,EAAE,QAAQ,KAAK,OAAO;EACpB,GAAG;EACH,UAAU,EAAE;CACd,EAAE,CACJ,KAAK,CAAC,CAGe,
|
|
1
|
+
{"version":3,"file":"toCreateMessage.js","names":[],"sources":["../../../src/ui/utils/toCreateMessage.ts"],"sourcesContent":["import type { AppendMessage } from \"@assistant-ui/core\";\nimport type {\n CreateUIMessage,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\nexport const toCreateMessage = <UI_MESSAGE extends UIMessage = UIMessage>(\n message: AppendMessage,\n): CreateUIMessage<UI_MESSAGE> => {\n const inputParts = [\n ...message.content.filter((c) => c.type !== \"file\"),\n ...(message.attachments?.flatMap((a) =>\n a.content.map((c) => ({\n ...c,\n filename: a.name,\n })),\n ) ?? []),\n ];\n\n const parts = inputParts.map((part): UIMessagePart<UIDataTypes, UITools> => {\n switch (part.type) {\n case \"text\":\n return {\n type: \"text\",\n text: part.text,\n };\n case \"image\":\n return {\n type: \"file\",\n url: part.image,\n ...(part.filename && { filename: part.filename }),\n mediaType: \"image/png\",\n };\n case \"file\":\n return {\n type: \"file\",\n url: part.data,\n mediaType: part.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n case \"data\":\n return {\n type: `data-${part.name}`,\n data: part.data,\n };\n default:\n throw new Error(`Unsupported part type: ${part.type}`);\n }\n });\n\n return {\n role: message.role,\n parts,\n metadata: message.metadata,\n } satisfies CreateUIMessage<UIMessage> as CreateUIMessage<UI_MESSAGE>;\n};\n"],"mappings":";AASA,MAAa,mBACX,YACgC;CAWhC,MAAM,QAAQ,CATZ,GAAG,QAAQ,QAAQ,QAAQ,MAAM,EAAE,SAAS,MAAM,GAClD,GAAI,QAAQ,aAAa,SAAS,MAChC,EAAE,QAAQ,KAAK,OAAO;EACpB,GAAG;EACH,UAAU,EAAE;CACd,EAAE,CACJ,KAAK,CAAC,CAGe,CAAC,CAAC,KAAK,SAA8C;EAC1E,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM,KAAK;GACb;GACF,KAAK,SACH,OAAO;IACL,MAAM;IACN,KAAK,KAAK;IACV,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IAC/C,WAAW;GACb;GACF,KAAK,QACH,OAAO;IACL,MAAM;IACN,KAAK,KAAK;IACV,WAAW,KAAK;IAChB,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;GACjD;GACF,KAAK,QACH,OAAO;IACL,MAAM,QAAQ,KAAK;IACnB,MAAM,KAAK;GACb;GACF,SACE,MAAM,IAAI,MAAM,0BAA0B,KAAK,MAAM;EACzD;CACF,CAAC;CAED,OAAO;EACL,MAAM,QAAQ;EACd;EACA,UAAU,QAAQ;CACpB;AACF"}
|
package/dist/usage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.js","names":[],"sources":["../src/usage.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/react\" />\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadTokenUsage = {\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport interface TokenUsageExtractableMessage {\n role?: string;\n metadata?: unknown;\n}\n\ntype UsageRecord = Record<string, unknown>;\n\nconst USAGE_KEYS = [\n \"inputTokens\",\n \"outputTokens\",\n \"reasoningTokens\",\n \"cachedInputTokens\",\n \"totalTokens\",\n] as const satisfies (keyof ThreadTokenUsage)[];\n\nfunction asRecord(value: unknown): UsageRecord | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return undefined;\n return value as UsageRecord;\n}\n\nfunction asPositiveTokenCount(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n return value;\n}\n\nfunction computeTotalTokens(usage: ThreadTokenUsage): number | undefined {\n if (usage.totalTokens !== undefined) return usage.totalTokens;\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {\n return usage.inputTokens + usage.outputTokens;\n }\n return undefined;\n}\n\nfunction normalizeUsage(value: unknown): ThreadTokenUsage | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const result: ThreadTokenUsage = {};\n let hasFields = false;\n for (const key of USAGE_KEYS) {\n const count = asPositiveTokenCount(record[key]);\n if (count !== undefined) {\n result[key] = count;\n hasFields = true;\n }\n }\n return hasFields ? result : undefined;\n}\n\nfunction withComputedTotal(\n usage: ThreadTokenUsage,\n): ThreadTokenUsage | undefined {\n const totalTokens = computeTotalTokens(usage);\n return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };\n}\n\nfunction usageFromSteps(value: unknown): ThreadTokenUsage | undefined {\n const steps = Array.isArray(value) ? value : [];\n\n const sums: Record<string, number> = {};\n const present: Record<string, boolean> = {};\n let stepsWithUsage = 0;\n let stepsWithComputableTotal = 0;\n\n for (const step of steps) {\n const usage = normalizeUsage(asRecord(step)?.usage);\n if (!usage) continue;\n stepsWithUsage++;\n\n const stepTotal = computeTotalTokens(usage);\n if (stepTotal !== undefined) {\n sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;\n stepsWithComputableTotal++;\n }\n\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (usage[key] !== undefined) {\n sums[key] = (sums[key] ?? 0) + usage[key];\n present[key] = true;\n }\n }\n }\n\n if (stepsWithUsage === 0) return undefined;\n\n const result: ThreadTokenUsage = {};\n if (stepsWithComputableTotal === stepsWithUsage) {\n result.totalTokens = sums.totalTokens!;\n }\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (present[key]) {\n result[key] = sums[key]!;\n }\n }\n return result;\n}\n\nexport function getThreadMessageTokenUsage(\n message: TokenUsageExtractableMessage | undefined,\n): ThreadTokenUsage | undefined {\n if (!message || message.role !== \"assistant\") return undefined;\n\n const metadata = asRecord(message.metadata);\n if (!metadata) return undefined;\n\n const topLevelUsage = normalizeUsage(metadata.usage);\n if (topLevelUsage) return withComputedTotal(topLevelUsage);\n\n const legacyUsage = normalizeUsage(asRecord(metadata.custom)?.usage);\n if (legacyUsage) return withComputedTotal(legacyUsage);\n\n return usageFromSteps(metadata.steps);\n}\n\nexport function getLatestThreadTokenUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): ThreadTokenUsage | undefined {\n return getThreadMessageTokenUsage(findLatestMessageWithUsage(messages));\n}\n\nfunction findLatestMessageWithUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): TokenUsageExtractableMessage | undefined {\n if (!messages) return undefined;\n\n for (let idx = messages.length - 1; idx >= 0; idx -= 1) {\n const message = messages[idx];\n if (getThreadMessageTokenUsage(message)) {\n return message;\n }\n }\n\n return undefined;\n}\n\nexport function useThreadTokenUsage(): ThreadTokenUsage | undefined {\n const msg = useAuiState((s) => findLatestMessageWithUsage(s.thread.messages));\n return getThreadMessageTokenUsage(msg);\n}\n"],"mappings":";;AAkBA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,SAAS,OAAyC;CACzD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D,OAAO,KAAA;CACT,OAAO;AACT;AAEA,SAAS,qBAAqB,OAAoC;CAChE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAClE;CAEF,OAAO;AACT;AAEA,SAAS,mBAAmB,OAA6C;CACvE,IAAI,MAAM,gBAAgB,KAAA,GAAW,OAAO,MAAM;CAClD,IAAI,MAAM,gBAAgB,KAAA,KAAa,MAAM,iBAAiB,KAAA,GAC5D,OAAO,MAAM,cAAc,MAAM;AAGrC;AAEA,SAAS,eAAe,OAA8C;CACpE,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,QAAQ,OAAO,KAAA;CAEpB,MAAM,SAA2B,CAAC;CAClC,IAAI,YAAY;CAChB,KAAK,MAAM,OAAO,YAAY;EAC5B,MAAM,QAAQ,qBAAqB,OAAO,IAAI;EAC9C,IAAI,UAAU,KAAA,GAAW;GACvB,OAAO,OAAO;GACd,YAAY;EACd;CACF;CACA,OAAO,YAAY,SAAS,KAAA;AAC9B;AAEA,SAAS,kBACP,OAC8B;CAC9B,MAAM,cAAc,mBAAmB,KAAK;CAC5C,OAAO;EAAE,GAAG;EAAO,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;CAAG;AACvE;AAEA,SAAS,eAAe,OAA8C;CACpE,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;CAE9C,MAAM,OAA+B,CAAC;CACtC,MAAM,UAAmC,CAAC;CAC1C,IAAI,iBAAiB;CACrB,IAAI,2BAA2B;CAE/B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,eAAe,SAAS,IAAI,
|
|
1
|
+
{"version":3,"file":"usage.js","names":[],"sources":["../src/usage.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/react\" />\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadTokenUsage = {\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport interface TokenUsageExtractableMessage {\n role?: string;\n metadata?: unknown;\n}\n\ntype UsageRecord = Record<string, unknown>;\n\nconst USAGE_KEYS = [\n \"inputTokens\",\n \"outputTokens\",\n \"reasoningTokens\",\n \"cachedInputTokens\",\n \"totalTokens\",\n] as const satisfies (keyof ThreadTokenUsage)[];\n\nfunction asRecord(value: unknown): UsageRecord | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return undefined;\n return value as UsageRecord;\n}\n\nfunction asPositiveTokenCount(value: unknown): number | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n return value;\n}\n\nfunction computeTotalTokens(usage: ThreadTokenUsage): number | undefined {\n if (usage.totalTokens !== undefined) return usage.totalTokens;\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {\n return usage.inputTokens + usage.outputTokens;\n }\n return undefined;\n}\n\nfunction normalizeUsage(value: unknown): ThreadTokenUsage | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n\n const result: ThreadTokenUsage = {};\n let hasFields = false;\n for (const key of USAGE_KEYS) {\n const count = asPositiveTokenCount(record[key]);\n if (count !== undefined) {\n result[key] = count;\n hasFields = true;\n }\n }\n return hasFields ? result : undefined;\n}\n\nfunction withComputedTotal(\n usage: ThreadTokenUsage,\n): ThreadTokenUsage | undefined {\n const totalTokens = computeTotalTokens(usage);\n return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };\n}\n\nfunction usageFromSteps(value: unknown): ThreadTokenUsage | undefined {\n const steps = Array.isArray(value) ? value : [];\n\n const sums: Record<string, number> = {};\n const present: Record<string, boolean> = {};\n let stepsWithUsage = 0;\n let stepsWithComputableTotal = 0;\n\n for (const step of steps) {\n const usage = normalizeUsage(asRecord(step)?.usage);\n if (!usage) continue;\n stepsWithUsage++;\n\n const stepTotal = computeTotalTokens(usage);\n if (stepTotal !== undefined) {\n sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;\n stepsWithComputableTotal++;\n }\n\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (usage[key] !== undefined) {\n sums[key] = (sums[key] ?? 0) + usage[key];\n present[key] = true;\n }\n }\n }\n\n if (stepsWithUsage === 0) return undefined;\n\n const result: ThreadTokenUsage = {};\n if (stepsWithComputableTotal === stepsWithUsage) {\n result.totalTokens = sums.totalTokens!;\n }\n for (const key of USAGE_KEYS) {\n if (key === \"totalTokens\") continue;\n if (present[key]) {\n result[key] = sums[key]!;\n }\n }\n return result;\n}\n\nexport function getThreadMessageTokenUsage(\n message: TokenUsageExtractableMessage | undefined,\n): ThreadTokenUsage | undefined {\n if (!message || message.role !== \"assistant\") return undefined;\n\n const metadata = asRecord(message.metadata);\n if (!metadata) return undefined;\n\n const topLevelUsage = normalizeUsage(metadata.usage);\n if (topLevelUsage) return withComputedTotal(topLevelUsage);\n\n const legacyUsage = normalizeUsage(asRecord(metadata.custom)?.usage);\n if (legacyUsage) return withComputedTotal(legacyUsage);\n\n return usageFromSteps(metadata.steps);\n}\n\nexport function getLatestThreadTokenUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): ThreadTokenUsage | undefined {\n return getThreadMessageTokenUsage(findLatestMessageWithUsage(messages));\n}\n\nfunction findLatestMessageWithUsage(\n messages: readonly TokenUsageExtractableMessage[] | undefined,\n): TokenUsageExtractableMessage | undefined {\n if (!messages) return undefined;\n\n for (let idx = messages.length - 1; idx >= 0; idx -= 1) {\n const message = messages[idx];\n if (getThreadMessageTokenUsage(message)) {\n return message;\n }\n }\n\n return undefined;\n}\n\nexport function useThreadTokenUsage(): ThreadTokenUsage | undefined {\n const msg = useAuiState((s) => findLatestMessageWithUsage(s.thread.messages));\n return getThreadMessageTokenUsage(msg);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-ai-sdk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.33",
|
|
4
4
|
"description": "Vercel AI SDK adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -15,13 +15,29 @@
|
|
|
15
15
|
"author": "AgentbaseAI Inc.",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"type": "module",
|
|
18
|
+
"imports": {
|
|
19
|
+
"#mcp-stdio": {
|
|
20
|
+
"types": "./src/mcp-stdio.node.ts",
|
|
21
|
+
"react-native": "./dist/mcp-stdio.unsupported.js",
|
|
22
|
+
"edge-light": "./dist/mcp-stdio.unsupported.js",
|
|
23
|
+
"workerd": "./dist/mcp-stdio.unsupported.js",
|
|
24
|
+
"browser": "./dist/mcp-stdio.unsupported.js",
|
|
25
|
+
"node": "./dist/mcp-stdio.node.js",
|
|
26
|
+
"default": "./dist/mcp-stdio.unsupported.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
18
29
|
"exports": {
|
|
19
30
|
".": {
|
|
31
|
+
"react-native": {
|
|
32
|
+
"types": "./dist/index.native.d.ts",
|
|
33
|
+
"default": "./dist/index.native.js"
|
|
34
|
+
},
|
|
20
35
|
"types": "./dist/index.d.ts",
|
|
21
36
|
"default": "./dist/index.js"
|
|
22
37
|
}
|
|
23
38
|
},
|
|
24
39
|
"main": "./dist/index.js",
|
|
40
|
+
"react-native": "./dist/index.native.js",
|
|
25
41
|
"types": "./dist/index.d.ts",
|
|
26
42
|
"files": [
|
|
27
43
|
"dist",
|
|
@@ -30,11 +46,11 @@
|
|
|
30
46
|
],
|
|
31
47
|
"sideEffects": false,
|
|
32
48
|
"dependencies": {
|
|
33
|
-
"@ai-sdk/mcp": "^1.0.
|
|
34
|
-
"@ai-sdk/react": "^3.0.
|
|
35
|
-
"@assistant-ui/core": "^0.2.
|
|
36
|
-
"@assistant-ui/store": "^0.2.
|
|
37
|
-
"ai": "^6.0.
|
|
49
|
+
"@ai-sdk/mcp": "^1.0.46",
|
|
50
|
+
"@ai-sdk/react": "^3.0.199",
|
|
51
|
+
"@assistant-ui/core": "^0.2.11",
|
|
52
|
+
"@assistant-ui/store": "^0.2.14",
|
|
53
|
+
"ai": "^6.0.197",
|
|
38
54
|
"assistant-cloud": "*"
|
|
39
55
|
},
|
|
40
56
|
"peerDependencies": {
|
|
@@ -49,13 +65,13 @@
|
|
|
49
65
|
"devDependencies": {
|
|
50
66
|
"@testing-library/react": "^16.3.2",
|
|
51
67
|
"@types/json-schema": "^7.0.15",
|
|
52
|
-
"@types/react": "^19.2.
|
|
68
|
+
"@types/react": "^19.2.17",
|
|
53
69
|
"@types/react-dom": "^19.2.3",
|
|
54
70
|
"jsdom": "^29.1.1",
|
|
55
|
-
"react": "^19.2.
|
|
56
|
-
"vitest": "^4.1.
|
|
57
|
-
"assistant-
|
|
58
|
-
"
|
|
71
|
+
"react": "^19.2.7",
|
|
72
|
+
"vitest": "^4.1.8",
|
|
73
|
+
"@assistant-ui/x-buildutils": "0.0.12",
|
|
74
|
+
"assistant-stream": "0.3.21"
|
|
59
75
|
},
|
|
60
76
|
"publishConfig": {
|
|
61
77
|
"access": "public",
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="@assistant-ui/core/react" />
|
|
2
|
+
|
|
3
|
+
export { useAISDKRuntime } from "./ui/use-chat/useAISDKRuntime";
|
|
4
|
+
export { useChatRuntime } from "./ui/use-chat/useChatRuntime";
|
|
5
|
+
export type { UseChatRuntimeOptions } from "./ui/use-chat/useChatRuntime";
|
|
6
|
+
export { AssistantChatTransport } from "./ui/use-chat/AssistantChatTransport";
|
|
7
|
+
export {
|
|
8
|
+
RESUMABLE_STREAM_ID_HEADER,
|
|
9
|
+
createResumableSessionStorage,
|
|
10
|
+
} from "./ui/resumable";
|
|
11
|
+
export type {
|
|
12
|
+
AssistantChatResumableOptions,
|
|
13
|
+
ResumableClientStorage,
|
|
14
|
+
} from "./ui/resumable";
|
|
15
|
+
export { frontendTools } from "./frontendTools";
|
|
16
|
+
export { injectQuoteContext } from "./injectQuoteContext";
|
|
17
|
+
export type { ThreadTokenUsage, TokenUsageExtractableMessage } from "./usage";
|
|
18
|
+
export { getThreadMessageTokenUsage, useThreadTokenUsage } from "./usage";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { AISDKToolkit
|
|
2
|
+
import { AISDKToolkit } from "./generativeTools";
|
|
3
3
|
import { wrapModelContentEnvelope } from "./modelContentEnvelope";
|
|
4
4
|
|
|
5
5
|
const mocks = vi.hoisted(() => ({
|
|
@@ -19,20 +19,9 @@ vi.mock("@ai-sdk/mcp/mcp-stdio", () => ({
|
|
|
19
19
|
})),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
describe("
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
mocks.tools.mockReset();
|
|
26
|
-
mocks.createMCPClient.mockReset();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("merges frontend tools with toolkit tools", () => {
|
|
30
|
-
const toolSet = generativeTools({
|
|
31
|
-
frontendTools: {
|
|
32
|
-
clientTool: {
|
|
33
|
-
parameters: { type: "object", properties: {} },
|
|
34
|
-
},
|
|
35
|
-
},
|
|
22
|
+
describe("AISDKToolkit.tools()", () => {
|
|
23
|
+
it("merges frontend tools with toolkit tools", async () => {
|
|
24
|
+
const toolSet = await new AISDKToolkit({
|
|
36
25
|
toolkit: {
|
|
37
26
|
serverTool: {
|
|
38
27
|
type: "backend",
|
|
@@ -41,6 +30,12 @@ describe("generativeTools", () => {
|
|
|
41
30
|
execute: async () => "ok",
|
|
42
31
|
} as never,
|
|
43
32
|
},
|
|
33
|
+
}).tools({
|
|
34
|
+
frontend: {
|
|
35
|
+
clientTool: {
|
|
36
|
+
parameters: { type: "object", properties: {} },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
44
39
|
});
|
|
45
40
|
|
|
46
41
|
expect(toolSet.clientTool).toBeDefined();
|
|
@@ -48,8 +43,8 @@ describe("generativeTools", () => {
|
|
|
48
43
|
expect(toolSet.serverTool?.execute).toBeTypeOf("function");
|
|
49
44
|
});
|
|
50
45
|
|
|
51
|
-
it("keeps a flat toolkit tool named tools", () => {
|
|
52
|
-
const toolSet =
|
|
46
|
+
it("keeps a flat toolkit tool named tools", async () => {
|
|
47
|
+
const toolSet = await new AISDKToolkit({
|
|
53
48
|
toolkit: {
|
|
54
49
|
tools: {
|
|
55
50
|
type: "backend",
|
|
@@ -58,27 +53,14 @@ describe("generativeTools", () => {
|
|
|
58
53
|
execute: async () => "ok",
|
|
59
54
|
} as never,
|
|
60
55
|
},
|
|
61
|
-
});
|
|
56
|
+
}).tools();
|
|
62
57
|
|
|
63
58
|
expect(toolSet.tools?.description).toBe("Actually a tool, not config");
|
|
64
59
|
expect(toolSet.tools?.execute).toBeTypeOf("function");
|
|
65
60
|
});
|
|
66
61
|
|
|
67
|
-
it("
|
|
68
|
-
|
|
69
|
-
generativeTools({
|
|
70
|
-
toolkit: {
|
|
71
|
-
docs: {
|
|
72
|
-
type: "mcp",
|
|
73
|
-
server: { type: "http", url: "http://localhost:3001/mcp" },
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
}),
|
|
77
|
-
).toThrow(/requires AISDKToolkit/);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("converts provider tools without an execute function", () => {
|
|
81
|
-
const toolSet = generativeTools({
|
|
62
|
+
it("converts provider tools without an execute function", async () => {
|
|
63
|
+
const toolSet = await new AISDKToolkit({
|
|
82
64
|
toolkit: {
|
|
83
65
|
web_search: {
|
|
84
66
|
type: "provider",
|
|
@@ -86,7 +68,7 @@ describe("generativeTools", () => {
|
|
|
86
68
|
args: { searchContextSize: "low" },
|
|
87
69
|
},
|
|
88
70
|
},
|
|
89
|
-
});
|
|
71
|
+
}).tools();
|
|
90
72
|
|
|
91
73
|
expect(toolSet.web_search).toMatchObject({
|
|
92
74
|
type: "provider",
|
|
@@ -97,8 +79,8 @@ describe("generativeTools", () => {
|
|
|
97
79
|
expect(toolSet.web_search).not.toHaveProperty("execute");
|
|
98
80
|
});
|
|
99
81
|
|
|
100
|
-
it("forwards provider tool parameters and providerOptions when present", () => {
|
|
101
|
-
const toolSet =
|
|
82
|
+
it("forwards provider tool parameters and providerOptions when present", async () => {
|
|
83
|
+
const toolSet = await new AISDKToolkit({
|
|
102
84
|
toolkit: {
|
|
103
85
|
web_search: {
|
|
104
86
|
type: "provider",
|
|
@@ -116,7 +98,7 @@ describe("generativeTools", () => {
|
|
|
116
98
|
},
|
|
117
99
|
},
|
|
118
100
|
},
|
|
119
|
-
});
|
|
101
|
+
}).tools();
|
|
120
102
|
|
|
121
103
|
expect(toolSet.web_search).toMatchObject({
|
|
122
104
|
type: "provider",
|
|
@@ -129,8 +111,8 @@ describe("generativeTools", () => {
|
|
|
129
111
|
expect(toolSet.web_search).toHaveProperty("inputSchema");
|
|
130
112
|
});
|
|
131
113
|
|
|
132
|
-
it("forwards explicit false supportsDeferredResults", () => {
|
|
133
|
-
const toolSet =
|
|
114
|
+
it("forwards explicit false supportsDeferredResults", async () => {
|
|
115
|
+
const toolSet = await new AISDKToolkit({
|
|
134
116
|
toolkit: {
|
|
135
117
|
web_search: {
|
|
136
118
|
type: "provider",
|
|
@@ -139,7 +121,7 @@ describe("generativeTools", () => {
|
|
|
139
121
|
supportsDeferredResults: false,
|
|
140
122
|
},
|
|
141
123
|
},
|
|
142
|
-
});
|
|
124
|
+
}).tools();
|
|
143
125
|
|
|
144
126
|
expect(toolSet.web_search).toMatchObject({
|
|
145
127
|
supportsDeferredResults: false,
|
|
@@ -324,18 +306,18 @@ describe("AISDKToolkit", () => {
|
|
|
324
306
|
});
|
|
325
307
|
});
|
|
326
308
|
|
|
327
|
-
describe("
|
|
309
|
+
describe("AISDKToolkit toModelOutput", () => {
|
|
328
310
|
const createWeatherTools = (toModelOutput?: any) =>
|
|
329
|
-
|
|
311
|
+
new AISDKToolkit({
|
|
330
312
|
toolkit: {
|
|
331
313
|
get_weather: {
|
|
332
314
|
...(toModelOutput && { toModelOutput }),
|
|
333
315
|
},
|
|
334
316
|
} as any,
|
|
335
|
-
});
|
|
317
|
+
}).tools();
|
|
336
318
|
|
|
337
319
|
it("adapts assistant-ui model content parts to the AI SDK tool output shape", async () => {
|
|
338
|
-
const tools = createWeatherTools(({ output }: any) => [
|
|
320
|
+
const tools = await createWeatherTools(({ output }: any) => [
|
|
339
321
|
{ type: "text", text: `Weather card displayed: ${output.location}` },
|
|
340
322
|
]);
|
|
341
323
|
|
|
@@ -353,7 +335,7 @@ describe("generativeTools toModelOutput", () => {
|
|
|
353
335
|
|
|
354
336
|
it("uses stored model content envelopes without re-running the custom projector", async () => {
|
|
355
337
|
let called = false;
|
|
356
|
-
const tools = createWeatherTools(() => {
|
|
338
|
+
const tools = await createWeatherTools(() => {
|
|
357
339
|
called = true;
|
|
358
340
|
return [{ type: "text", text: "recomputed" }];
|
|
359
341
|
});
|
|
@@ -374,7 +356,7 @@ describe("generativeTools toModelOutput", () => {
|
|
|
374
356
|
});
|
|
375
357
|
|
|
376
358
|
it("falls back to default model output when no custom projector is defined", async () => {
|
|
377
|
-
const tools = createWeatherTools();
|
|
359
|
+
const tools = await createWeatherTools();
|
|
378
360
|
|
|
379
361
|
const output = await tools.get_weather!.toModelOutput!({
|
|
380
362
|
toolCallId: "tc-weather",
|
|
@@ -389,7 +371,7 @@ describe("generativeTools toModelOutput", () => {
|
|
|
389
371
|
});
|
|
390
372
|
|
|
391
373
|
it("uses stored model content envelopes when no custom projector is defined", async () => {
|
|
392
|
-
const tools = createWeatherTools();
|
|
374
|
+
const tools = await createWeatherTools();
|
|
393
375
|
|
|
394
376
|
const output = await tools.get_weather!.toModelOutput!({
|
|
395
377
|
toolCallId: "tc-weather",
|
package/src/generativeTools.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsonSchema, type ToolSet } from "ai";
|
|
2
2
|
import type { MCPClient, MCPClientConfig } from "@ai-sdk/mcp";
|
|
3
3
|
import { createMCPClient } from "@ai-sdk/mcp";
|
|
4
|
-
import { Experimental_StdioMCPTransport } from "
|
|
4
|
+
import { Experimental_StdioMCPTransport } from "#mcp-stdio";
|
|
5
5
|
import {
|
|
6
6
|
toJSONSchema,
|
|
7
7
|
type Tool,
|
|
@@ -31,6 +31,11 @@ const neverAbort = new AbortController().signal;
|
|
|
31
31
|
const parametersToInputSchema = (parameters: Tool["parameters"] | undefined) =>
|
|
32
32
|
jsonSchema(parameters ? toJSONSchema(parameters) : EMPTY_SCHEMA);
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Options for the deprecated {@link generativeTools}. Use
|
|
36
|
+
* {@link AISDKToolkit} with {@link AISDKToolkitOptions} /
|
|
37
|
+
* {@link AISDKToolkitToolsOptions} instead.
|
|
38
|
+
*/
|
|
34
39
|
export interface GenerativeToolsOptions {
|
|
35
40
|
/**
|
|
36
41
|
* The server build of a generative toolkit (schema + server `execute`). Typed
|
|
@@ -66,16 +71,25 @@ export type AISDKToolkitToolsOptions = {
|
|
|
66
71
|
* resolves to the server build — schema + `execute`, with `render` stripped) and
|
|
67
72
|
* pass it here. Tools without an `execute` are still exposed to the model but
|
|
68
73
|
* left for the client to fulfill. `frontendTools` lets the client contribute
|
|
69
|
-
* tools that aren't in the static toolkit.
|
|
70
|
-
*
|
|
74
|
+
* tools that aren't in the static toolkit.
|
|
75
|
+
*
|
|
76
|
+
* @deprecated Use {@link AISDKToolkit} instead:
|
|
77
|
+
* `new AISDKToolkit({ toolkit }).tools({ frontend })`. It is a strict superset
|
|
78
|
+
* (it also opens MCP server connections), so it replaces `generativeTools`
|
|
79
|
+
* everywhere. The `frontendTools` option is named `frontend` on `.tools()`, and
|
|
80
|
+
* `.tools()` is async. `generativeTools` will be removed in a future version.
|
|
71
81
|
*
|
|
72
82
|
* @example
|
|
73
83
|
* ```ts
|
|
84
|
+
* // Define once at module scope so any MCP connections pool across requests.
|
|
85
|
+
* const aiToolkit = new AISDKToolkit({ toolkit: docsToolkit });
|
|
86
|
+
*
|
|
87
|
+
* // In your route handler:
|
|
74
88
|
* const { tools } = await req.json();
|
|
75
89
|
* streamText({
|
|
76
90
|
* model,
|
|
77
91
|
* messages,
|
|
78
|
-
* tools:
|
|
92
|
+
* tools: await aiToolkit.tools({ frontend: tools }),
|
|
79
93
|
* });
|
|
80
94
|
* ```
|
|
81
95
|
*/
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
/// <reference types="@assistant-ui/core/react" />
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
export { useChatRuntime } from "./ui/use-chat/useChatRuntime";
|
|
5
|
-
export type { UseChatRuntimeOptions } from "./ui/use-chat/useChatRuntime";
|
|
6
|
-
export { AssistantChatTransport } from "./ui/use-chat/AssistantChatTransport";
|
|
7
|
-
export {
|
|
8
|
-
RESUMABLE_STREAM_ID_HEADER,
|
|
9
|
-
createResumableSessionStorage,
|
|
10
|
-
} from "./ui/resumable";
|
|
11
|
-
export type {
|
|
12
|
-
AssistantChatResumableOptions,
|
|
13
|
-
ResumableClientStorage,
|
|
14
|
-
} from "./ui/resumable";
|
|
15
|
-
export { frontendTools } from "./frontendTools";
|
|
3
|
+
export * from "./client";
|
|
16
4
|
export {
|
|
17
5
|
AISDKToolkit,
|
|
18
6
|
generativeTools,
|
|
@@ -20,6 +8,3 @@ export {
|
|
|
20
8
|
type AISDKToolkitToolsOptions,
|
|
21
9
|
type GenerativeToolsOptions,
|
|
22
10
|
} from "./generativeTools";
|
|
23
|
-
export { injectQuoteContext } from "./injectQuoteContext";
|
|
24
|
-
export type { ThreadTokenUsage, TokenUsageExtractableMessage } from "./usage";
|
|
25
|
-
export { getThreadMessageTokenUsage, useThreadTokenUsage } from "./usage";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Experimental_StdioMCPTransport as NodeStdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";
|
|
2
|
+
|
|
3
|
+
class UnsupportedStdioMCPTransport {
|
|
4
|
+
constructor() {
|
|
5
|
+
throw new Error(
|
|
6
|
+
"stdio MCP transport requires a runtime that can spawn a subprocess, such as Node, Bun, or Deno (with --allow-run). Use an HTTP or SSE MCP server config in browser, React Native, edge, or worker runtimes.",
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Experimental_StdioMCPTransport =
|
|
12
|
+
UnsupportedStdioMCPTransport as unknown as typeof NodeStdioMCPTransport;
|