@assistant-ui/ai-sdk 0.0.2 → 0.0.4
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/README.md +1 -1
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +23 -2
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.js +6 -2
- package/dist/converters/toCreateMessage.js.map +1 -1
- package/dist/model-context/injectInteractableContext.d.ts +1 -1
- package/dist/model-context/injectInteractableContext.js +1 -1
- package/dist/model-context/injectInteractableContext.js.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +1 -1
- package/dist/model-context/injectQuoteContext.js +1 -1
- package/dist/model-context/injectQuoteContext.js.map +1 -1
- package/dist/runtime/AISDKChat.js +1 -1
- package/dist/runtime/AISDKThreads.d.ts +10 -8
- package/dist/runtime/AISDKThreads.d.ts.map +1 -1
- package/dist/runtime/AISDKThreads.js +34 -27
- package/dist/runtime/AISDKThreads.js.map +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +18 -3
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +199 -73
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.js +1 -1
- package/dist/runtime/useChatThread.d.ts +14 -1
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +11 -4
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +52 -47
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/runtime/useResourceCleanup.js +1 -1
- package/dist/usage.d.ts +1 -2
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +5 -7
- package/dist/usage.js.map +1 -1
- package/package.json +12 -12
- package/src/converters/convertMessage.test.ts +22 -0
- package/src/converters/convertMessage.ts +26 -2
- package/src/converters/toCreateMessage.ts +6 -5
- package/src/model-context/injectInteractableContext.ts +1 -1
- package/src/model-context/injectQuoteContext.ts +1 -1
- package/src/runtime/AISDKThreads.cloud.test.ts +11 -1
- package/src/runtime/AISDKThreads.test.ts +102 -0
- package/src/runtime/AISDKThreads.ts +16 -9
- package/src/runtime/useAISDKRuntime.denied-tool.test.tsx +122 -0
- package/src/runtime/useAISDKRuntime.test.ts +479 -10
- package/src/runtime/useAISDKRuntime.ts +199 -55
- package/src/runtime/useChatRuntime.integration.test.tsx +46 -0
- package/src/runtime/useChatRuntime.test.ts +16 -0
- package/src/runtime/useChatThread.ts +26 -0
- package/src/runtime/useExternalHistory.test.ts +143 -1
- package/src/runtime/useExternalHistory.ts +53 -56
- package/src/usage.test.ts +26 -8
- package/src/usage.ts +4 -9
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm install @assistant-ui/react @assistant-ui/ai-sdk
|
|
|
15
15
|
|
|
16
16
|
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
17
17
|
import { useChatRuntime } from "@assistant-ui/ai-sdk";
|
|
18
|
-
import { Thread } from "@/components/assistant-ui/thread";
|
|
18
|
+
import { Thread } from "@/components/assistant-ui/elements/thread.aui";
|
|
19
19
|
|
|
20
20
|
export function Chat() {
|
|
21
21
|
const runtime = useChatRuntime();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../src/converters/convertMessage.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"convertMessage.d.ts","names":[],"sources":["../../src/converters/convertMessage.ts"],"mappings":";;;;;KAyDY,gCACV,4BAA4B;EAC1B,wBAAwB,YAAY;EACpC,qBAAqB,YAAY;EACjC,sBAAsB,YAAY;;EAElC;;cA+VS;;;;;;;;EA9XmB,qBAAA,oCAAA,2CAAA,6CAAA;EAK9B,oBAAA,oCAAA,2CAAA,6CAAA"}
|
|
@@ -5,6 +5,27 @@ import { isMcpAppUri } from "@assistant-ui/core";
|
|
|
5
5
|
import { stableStringifyToolArgs } from "@assistant-ui/core/internal";
|
|
6
6
|
import { parsePartialJsonObject } from "assistant-stream/utils";
|
|
7
7
|
//#region src/converters/convertMessage.ts
|
|
8
|
+
const THREAD_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
9
|
+
"unstable_state",
|
|
10
|
+
"unstable_annotations",
|
|
11
|
+
"unstable_data",
|
|
12
|
+
"steps",
|
|
13
|
+
"timing",
|
|
14
|
+
"submittedFeedback",
|
|
15
|
+
"isOptimistic",
|
|
16
|
+
"custom"
|
|
17
|
+
]);
|
|
18
|
+
const toThreadMetadata = (metadata) => {
|
|
19
|
+
if (!metadata || typeof metadata !== "object") return void 0;
|
|
20
|
+
const result = {};
|
|
21
|
+
const extra = {};
|
|
22
|
+
for (const [key, value] of Object.entries(metadata)) (THREAD_METADATA_KEYS.has(key) ? result : extra)[key] = value;
|
|
23
|
+
if (Object.keys(extra).length > 0) result.custom = {
|
|
24
|
+
...extra,
|
|
25
|
+
...result.custom
|
|
26
|
+
};
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
8
29
|
function stripClosingDelimiters(json) {
|
|
9
30
|
return json.replace(/[}\]"]+$/, "");
|
|
10
31
|
}
|
|
@@ -220,7 +241,7 @@ const AISDKMessageConverter = createMessageConverter((message, metadata) => {
|
|
|
220
241
|
status: { type: "complete" }
|
|
221
242
|
};
|
|
222
243
|
}),
|
|
223
|
-
metadata: message.metadata
|
|
244
|
+
metadata: toThreadMetadata(message.metadata)
|
|
224
245
|
};
|
|
225
246
|
case "system":
|
|
226
247
|
case "assistant": {
|
|
@@ -232,7 +253,7 @@ const AISDKMessageConverter = createMessageConverter((message, metadata) => {
|
|
|
232
253
|
createdAt,
|
|
233
254
|
content,
|
|
234
255
|
metadata: {
|
|
235
|
-
...message.metadata,
|
|
256
|
+
...toThreadMetadata(message.metadata),
|
|
236
257
|
...timing && { timing },
|
|
237
258
|
...isOptimistic && { isOptimistic: true }
|
|
238
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMessage.js","names":["isToolUIPart","isReasoningFileUIPart","isCustomContentUIPart","getToolName","UIMessage","createMessageConverter","unstable_createMessageConverter","useExternalMessageConverter","isMcpAppUri","ReasoningMessagePart","ToolCallMessagePart","TextMessagePart","DataMessagePart","PartProviderMetadata","SourceMessagePart","SourceProviderMetadata","FileMessagePart","ThreadMessageLike","McpAppMetadata","MessagePartStreamStatus","stableStringifyToolArgs","parsePartialJsonObject","ReadonlyJSONObject","unwrapModelContentEnvelope","MessageMetadata","AISDKMessageConverterMetadata","Metadata","toolArgsKeyOrderCache","Map","toolLastInputCache","mcpAppMetadataCache","optimisticMessageId","stripClosingDelimiters","json","replace","MCP_APP_METADATA_CACHE_MAX","extractMcpAppMetadata","part","cache","undefined","meta","callProviderMetadata","mcp","app","a","Record","output","outMeta","_meta","ui","uiMeta","resourceUri","Array","isArray","visibility","flat","cacheKey","cached","get","delete","set","out","K","mimeType","filter","v","length","serverId","size","oldest","keys","next","value","getToolApprovalAndInterrupt","approval","id","approved","reason","isAutomatic","toolStatus","type","payload","NonNullable","interrupt","MessageContent","Exclude","uiPartStateToStatus","state","convertParts","message","metadata","parts","converted","p","role","map","status","text","providerMetadata","toolName","toolCallId","argsKeyOrderCacheKey","rawInput","input","args","result","modelContent","isError","unwrapped","error","errorText","argsText","toolStatuses","mcpApp","sourceType","sourceId","url","title","data","mediaType","filename","startsWith","name","substring","kind","console","warn","Boolean","seenToolCallIds","Set","has","add","AISDKMessageConverter","createdAt","Date","content","attachments","idx","isImage","toString","image","contentType","const","timing","messageTiming","isOptimistic"],"sources":["../../src/converters/convertMessage.ts"],"sourcesContent":["import {\n isToolUIPart,\n isReasoningFileUIPart,\n isCustomContentUIPart,\n getToolName,\n type UIMessage,\n} 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 PartProviderMetadata,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n type MessagePartStreamStatus,\n} from \"@assistant-ui/core\";\nimport { stableStringifyToolArgs } from \"@assistant-ui/core/internal\";\nimport {\n parsePartialJsonObject,\n type ReadonlyJSONObject,\n} 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 surface the pointer on result._meta: canonical nested\n // `ui.resourceUri`, or the deprecated flat `\"ui/resourceUri\"` key.\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 ui =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui\"]\n : undefined;\n if (\n ui &&\n typeof ui === \"object\" &&\n typeof (ui as Record<string, unknown>)[\"resourceUri\"] === \"string\" &&\n isMcpAppUri((ui as Record<string, unknown>)[\"resourceUri\"] as string)\n ) {\n // Only the spec'd ui fields cross from the result body; serverId is a\n // routing key and stays transport-derived via callProviderMetadata.\n const uiMeta = ui as Record<string, unknown>;\n a = {\n resourceUri: uiMeta[\"resourceUri\"],\n ...(Array.isArray(uiMeta[\"visibility\"])\n ? { visibility: uiMeta[\"visibility\"] }\n : {}),\n };\n } else {\n const flat =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof flat !== \"string\" || !isMcpAppUri(flat)) return undefined;\n a = { resourceUri: flat };\n }\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cacheKey = `${typeof a[\"serverId\"] === \"string\" ? a[\"serverId\"] : \"\"} ${a[\"resourceUri\"]}`;\n const cached = cache?.get(cacheKey);\n if (cached) {\n cache!.delete(cacheKey);\n cache!.set(cacheKey, 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 (typeof a[\"serverId\"] === \"string\" && a[\"serverId\"].length > 0)\n out.serverId = a[\"serverId\"];\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(cacheKey, out);\n }\n return out;\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\nconst uiPartStateToStatus = (\n state: \"streaming\" | \"done\" | undefined,\n): MessagePartStreamStatus | undefined => {\n if (state === \"streaming\") return { type: \"running\" };\n if (state === \"done\") return { type: \"complete\" };\n return undefined;\n};\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 const status = uiPartStateToStatus(part.state);\n return {\n type: \"text\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"reasoning\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\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 // Re-parse so args carries the partial-JSON meta that marks which\n // field is still mid-arrival, like every argsText-based runtime.\n // The key-order cache appends new keys last, so the trailing field\n // of the stripped text is the streaming frontier.\n args = parsePartialJsonObject(argsText) ?? args;\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 ...(part.callProviderMetadata != null\n ? {\n providerMetadata:\n part.callProviderMetadata as PartProviderMetadata,\n }\n : undefined),\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 if (isReasoningFileUIPart(part)) {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n } satisfies FileMessagePart;\n }\n\n if (isCustomContentUIPart(part)) {\n return {\n type: \"data\",\n name: part.kind,\n data: part.providerMetadata ?? null,\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 const mediaType = part.mediaType ?? \"unknown/unknown\";\n const isImage = mediaType.startsWith(\"image/\");\n return {\n id: idx.toString(),\n type: isImage ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n isImage\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: mediaType,\n },\n ],\n contentType: mediaType,\n status: { type: \"complete\" as const },\n };\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":["isToolUIPart","isReasoningFileUIPart","isCustomContentUIPart","getToolName","UIMessage","createMessageConverter","unstable_createMessageConverter","useExternalMessageConverter","isMcpAppUri","ReasoningMessagePart","ToolCallMessagePart","TextMessagePart","DataMessagePart","PartProviderMetadata","SourceMessagePart","SourceProviderMetadata","FileMessagePart","ThreadMessageLike","McpAppMetadata","MessagePartStreamStatus","stableStringifyToolArgs","parsePartialJsonObject","ReadonlyJSONObject","unwrapModelContentEnvelope","MessageMetadata","THREAD_METADATA_KEYS","Set","toThreadMetadata","metadata","undefined","result","Record","extra","key","value","Object","entries","has","keys","length","custom","AISDKMessageConverterMetadata","Metadata","toolArgsKeyOrderCache","Map","toolLastInputCache","mcpAppMetadataCache","optimisticMessageId","stripClosingDelimiters","json","replace","MCP_APP_METADATA_CACHE_MAX","extractMcpAppMetadata","part","cache","meta","callProviderMetadata","mcp","app","a","output","outMeta","_meta","ui","uiMeta","resourceUri","Array","isArray","visibility","flat","cacheKey","cached","get","delete","set","out","K","mimeType","filter","v","serverId","size","oldest","next","getToolApprovalAndInterrupt","approval","id","approved","reason","isAutomatic","toolStatus","type","payload","NonNullable","interrupt","MessageContent","Exclude","uiPartStateToStatus","state","convertParts","message","parts","converted","p","role","map","status","text","providerMetadata","toolName","toolCallId","argsKeyOrderCacheKey","rawInput","input","args","modelContent","isError","unwrapped","error","errorText","argsText","toolStatuses","mcpApp","sourceType","sourceId","url","title","data","mediaType","filename","startsWith","name","substring","kind","console","warn","Boolean","seenToolCallIds","add","AISDKMessageConverter","createdAt","Date","content","attachments","idx","isImage","toString","image","contentType","const","timing","messageTiming","isOptimistic"],"sources":["../../src/converters/convertMessage.ts"],"sourcesContent":["import {\n isToolUIPart,\n isReasoningFileUIPart,\n isCustomContentUIPart,\n getToolName,\n type UIMessage,\n} 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 PartProviderMetadata,\n type SourceMessagePart,\n type SourceProviderMetadata,\n type FileMessagePart,\n type ThreadMessageLike,\n type McpAppMetadata,\n type MessagePartStreamStatus,\n} from \"@assistant-ui/core\";\nimport { stableStringifyToolArgs } from \"@assistant-ui/core/internal\";\nimport {\n parsePartialJsonObject,\n type ReadonlyJSONObject,\n} from \"assistant-stream/utils\";\nimport { unwrapModelContentEnvelope } from \"./modelContentEnvelope\";\n\ntype MessageMetadata = ThreadMessageLike[\"metadata\"];\n\nconst THREAD_METADATA_KEYS = new Set([\n \"unstable_state\",\n \"unstable_annotations\",\n \"unstable_data\",\n \"steps\",\n \"timing\",\n \"submittedFeedback\",\n \"isOptimistic\",\n \"custom\",\n]);\n\nconst toThreadMetadata = (metadata: unknown): MessageMetadata => {\n if (!metadata || typeof metadata !== \"object\") return undefined;\n const result: Record<string, unknown> = {};\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n (THREAD_METADATA_KEYS.has(key) ? result : extra)[key] = value;\n }\n if (Object.keys(extra).length > 0) {\n result.custom = { ...extra, ...(result.custom as object | undefined) };\n }\n return result as MessageMetadata;\n};\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 surface the pointer on result._meta: canonical nested\n // `ui.resourceUri`, or the deprecated flat `\"ui/resourceUri\"` key.\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 ui =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui\"]\n : undefined;\n if (\n ui &&\n typeof ui === \"object\" &&\n typeof (ui as Record<string, unknown>)[\"resourceUri\"] === \"string\" &&\n isMcpAppUri((ui as Record<string, unknown>)[\"resourceUri\"] as string)\n ) {\n // Only the spec'd ui fields cross from the result body; serverId is a\n // routing key and stays transport-derived via callProviderMetadata.\n const uiMeta = ui as Record<string, unknown>;\n a = {\n resourceUri: uiMeta[\"resourceUri\"],\n ...(Array.isArray(uiMeta[\"visibility\"])\n ? { visibility: uiMeta[\"visibility\"] }\n : {}),\n };\n } else {\n const flat =\n outMeta && typeof outMeta === \"object\"\n ? (outMeta as Record<string, unknown>)[\"ui/resourceUri\"]\n : undefined;\n if (typeof flat !== \"string\" || !isMcpAppUri(flat)) return undefined;\n a = { resourceUri: flat };\n }\n }\n if (typeof a[\"resourceUri\"] !== \"string\") return undefined;\n if (!isMcpAppUri(a[\"resourceUri\"])) return undefined;\n const cacheKey = `${typeof a[\"serverId\"] === \"string\" ? a[\"serverId\"] : \"\"} ${a[\"resourceUri\"]}`;\n const cached = cache?.get(cacheKey);\n if (cached) {\n cache!.delete(cacheKey);\n cache!.set(cacheKey, 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 (typeof a[\"serverId\"] === \"string\" && a[\"serverId\"].length > 0)\n out.serverId = a[\"serverId\"];\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(cacheKey, out);\n }\n return out;\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\nconst uiPartStateToStatus = (\n state: \"streaming\" | \"done\" | undefined,\n): MessagePartStreamStatus | undefined => {\n if (state === \"streaming\") return { type: \"running\" };\n if (state === \"done\") return { type: \"complete\" };\n return undefined;\n};\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 const status = uiPartStateToStatus(part.state);\n return {\n type: \"text\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\n } satisfies TextMessagePart;\n }\n\n if (part.type === \"reasoning\") {\n const status = uiPartStateToStatus(part.state);\n return {\n type: \"reasoning\",\n text: part.text,\n ...(status != null ? { status } : undefined),\n ...(part.providerMetadata != null\n ? {\n providerMetadata: part.providerMetadata as PartProviderMetadata,\n }\n : undefined),\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 // Re-parse so args carries the partial-JSON meta that marks which\n // field is still mid-arrival, like every argsText-based runtime.\n // The key-order cache appends new keys last, so the trailing field\n // of the stripped text is the streaming frontier.\n args = parsePartialJsonObject(argsText) ?? args;\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 ...(part.callProviderMetadata != null\n ? {\n providerMetadata:\n part.callProviderMetadata as PartProviderMetadata,\n }\n : undefined),\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 if (isReasoningFileUIPart(part)) {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n } satisfies FileMessagePart;\n }\n\n if (isCustomContentUIPart(part)) {\n return {\n type: \"data\",\n name: part.kind,\n data: part.providerMetadata ?? null,\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 const mediaType = part.mediaType ?? \"unknown/unknown\";\n const isImage = mediaType.startsWith(\"image/\");\n return {\n id: idx.toString(),\n type: isImage ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n isImage\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: mediaType,\n },\n ],\n contentType: mediaType,\n status: { type: \"complete\" as const },\n };\n }),\n metadata: toThreadMetadata(message.metadata),\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 ...toThreadMetadata(message.metadata),\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":"toCreateMessage.d.ts","names":[],"sources":["../../src/converters/toCreateMessage.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"toCreateMessage.d.ts","names":[],"sources":["../../src/converters/toCreateMessage.ts"],"mappings":";;;cAoBa,kBAAmB,mBAAmB,YAAY,WAC7D,SAAS,kBACR,gBAAgB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveFileMediaType, resolveFilePartSource, resolveImageMediaType, toMediaWireUrl } from "@assistant-ui/core/internal";
|
|
2
2
|
//#region src/converters/toCreateMessage.ts
|
|
3
3
|
const toCreateMessage = (message) => {
|
|
4
4
|
const parts = [...message.content, ...message.attachments?.flatMap((a) => a.content.map((c) => ({
|
|
@@ -32,9 +32,13 @@ const toCreateMessage = (message) => {
|
|
|
32
32
|
case "audio": {
|
|
33
33
|
const mediaType = `audio/${part.audio.format}`;
|
|
34
34
|
const data = part.audio.data;
|
|
35
|
+
const source = resolveFilePartSource({
|
|
36
|
+
data,
|
|
37
|
+
mimeType: mediaType
|
|
38
|
+
});
|
|
35
39
|
return {
|
|
36
40
|
type: "file",
|
|
37
|
-
url:
|
|
41
|
+
url: source.kind === "url" ? source.url : `data:${mediaType};base64,${source.data}`,
|
|
38
42
|
mediaType,
|
|
39
43
|
...part.filename && { filename: part.filename }
|
|
40
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toCreateMessage.js","names":["AppendMessage","
|
|
1
|
+
{"version":3,"file":"toCreateMessage.js","names":["AppendMessage","resolveFileMediaType","resolveFilePartSource","resolveImageMediaType","toMediaWireUrl","CreateUIMessage","UIDataTypes","UIMessage","UIMessagePart","UITools","InputPart","contentType","filename","toCreateMessage","message","UI_MESSAGE","inputParts","content","attachments","flatMap","a","map","c","name","parts","part","type","text","mediaType","image","url","data","mimeType","sourceType","audio","format","source","kind","Error","role","metadata"],"sources":["../../src/converters/toCreateMessage.ts"],"sourcesContent":["import type { AppendMessage } from \"@assistant-ui/core\";\nimport {\n resolveFileMediaType,\n resolveFilePartSource,\n resolveImageMediaType,\n toMediaWireUrl,\n} from \"@assistant-ui/core/internal\";\nimport type {\n CreateUIMessage,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\ntype InputPart = AppendMessage[\"content\"][number] & {\n readonly contentType?: string | undefined;\n readonly filename?: string | undefined;\n};\n\nexport const toCreateMessage = <UI_MESSAGE extends UIMessage = UIMessage>(\n message: AppendMessage,\n): CreateUIMessage<UI_MESSAGE> => {\n const inputParts: InputPart[] = [\n ...message.content,\n ...(message.attachments?.flatMap((a) =>\n a.content.map((c) => ({\n ...c,\n filename: a.name,\n contentType: a.contentType,\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 const mediaType = resolveImageMediaType(part.image, part.contentType);\n return {\n type: \"file\",\n url: toMediaWireUrl(part.image, mediaType),\n ...(part.filename && { filename: part.filename }),\n mediaType,\n };\n }\n case \"file\": {\n // `mimeType` is a plain string, and an adapter reading `file.type` on a\n // file the OS cannot type yields \"\". Same ladder as images: declared,\n // then the envelope, then the floor.\n const mediaType = resolveFileMediaType(part.data, part.mimeType);\n return {\n type: \"file\",\n // An `id` reference is an opaque provider handle, not base64, and\n // this adapter has no way to send one. Left unwrapped so it fails\n // loudly upstream rather than shipping a corrupt payload.\n url:\n part.sourceType === \"id\"\n ? part.data\n : toMediaWireUrl(part.data, mediaType),\n mediaType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"audio\": {\n // A data URL's own media type wins over `mediaType` downstream, so the\n // envelope is rebuilt from the typed format rather than forwarded.\n const mediaType = `audio/${part.audio.format}`;\n const data = part.audio.data;\n const source = resolveFilePartSource({ data, mimeType: mediaType });\n return {\n type: \"file\",\n url:\n source.kind === \"url\"\n ? source.url\n : `data:${mediaType};base64,${source.data}`,\n mediaType,\n ...(part.filename && { filename: part.filename }),\n };\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":";;AAoBA,MAAaa,mBACXC,YACgC;CAYhC,MAAMU,QAAQR,CAVZ,GAAGF,QAAQG,SACX,GAAIH,QAAQI,aAAaC,SAASC,MAChCA,EAAEH,QAAQI,KAAKC,OAAO;EACpB,GAAGA;EACHV,UAAUQ,EAAEG;EACZZ,aAAaS,EAAET;CACjB,EAAE,CACJ,KAAK,CAAA,CAGOK,CAAU,CAACK,KAAKI,SAA8C;EAC1E,QAAQA,KAAKC,MAAb;GACE,KAAK,QACH,OAAO;IACLA,MAAM;IACNC,MAAMF,KAAKE;GACb;GACF,KAAK,SAAS;IACZ,MAAMC,YAAYzB,sBAAsBsB,KAAKI,OAAOJ,KAAKd,WAAW;IACpE,OAAO;KACLe,MAAM;KACNI,KAAK1B,eAAeqB,KAAKI,OAAOD,SAAS;KACzC,GAAIH,KAAKb,YAAY,EAAEA,UAAUa,KAAKb,SAAS;KAC/CgB;IACF;GACF;GACA,KAAK,QAAQ;IAIX,MAAMA,YAAY3B,qBAAqBwB,KAAKM,MAAMN,KAAKO,QAAQ;IAC/D,OAAO;KACLN,MAAM;KAINI,KACEL,KAAKQ,eAAe,OAChBR,KAAKM,OACL3B,eAAeqB,KAAKM,MAAMH,SAAS;KACzCA;KACA,GAAIH,KAAKb,YAAY,EAAEA,UAAUa,KAAKb,SAAS;IACjD;GACF;GACA,KAAK,SAAS;IAGZ,MAAMgB,YAAY,SAASH,KAAKS,MAAMC;IACtC,MAAMJ,OAAON,KAAKS,MAAMH;IACxB,MAAMK,SAASlC,sBAAsB;KAAE6B;KAAMC,UAAUJ;IAAU,CAAC;IAClE,OAAO;KACLF,MAAM;KACNI,KACEM,OAAOC,SAAS,QACZD,OAAON,MACP,QAAQF,UAAS,UAAWQ,OAAOL;KACzCH;KACA,GAAIH,KAAKb,YAAY,EAAEA,UAAUa,KAAKb,SAAS;IACjD;GACF;GACA,KAAK,QACH,OAAO;IACLc,MAAM,QAAQD,KAAKF;IACnBQ,MAAMN,KAAKM;GACb;GACF,SACE,MAAM,IAAIO,MAAM,0BAA0Bb,KAAKC,MAAM;EACzD;CACF,CAAC;CAED,OAAO;EACLa,MAAMzB,QAAQyB;EACdf;EACAgB,UAAU1B,QAAQ0B;CACpB;AACF"}
|
|
@@ -19,7 +19,7 @@ import { Unstable_InteractableSnapshotEntry } from "@assistant-ui/core";
|
|
|
19
19
|
* @example
|
|
20
20
|
* ```ts
|
|
21
21
|
* import { convertToModelMessages, streamText } from "ai";
|
|
22
|
-
* import { unstable_injectInteractableContext } from "@assistant-ui/
|
|
22
|
+
* import { unstable_injectInteractableContext } from "@assistant-ui/ai-sdk";
|
|
23
23
|
*
|
|
24
24
|
* export async function POST(req: Request) {
|
|
25
25
|
* const { messages } = await req.json();
|
|
@@ -18,7 +18,7 @@ import { unstable_formatInteractableSnapshot, unstable_getInteractableSnapshots
|
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
20
|
* import { convertToModelMessages, streamText } from "ai";
|
|
21
|
-
* import { unstable_injectInteractableContext } from "@assistant-ui/
|
|
21
|
+
* import { unstable_injectInteractableContext } from "@assistant-ui/ai-sdk";
|
|
22
22
|
*
|
|
23
23
|
* export async function POST(req: Request) {
|
|
24
24
|
* const { messages } = await req.json();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injectInteractableContext.js","names":["UIMessage","unstable_formatInteractableSnapshot","unstable_getInteractableSnapshots","Unstable_InteractableSnapshotEntry","unstable_injectInteractableContext","messages","format","item","map","msg","role","items","length","text","join","alreadyInjected","parts","type","const"],"sources":["../../src/model-context/injectInteractableContext.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\nimport {\n unstable_formatInteractableSnapshot,\n unstable_getInteractableSnapshots,\n type Unstable_InteractableSnapshotEntry,\n} from \"@assistant-ui/core\";\n\n/**\n * Injects interactable state snapshots into messages as model-visible text.\n *\n * Mirrors {@link injectQuoteContext}: reads the frozen snapshot stamped on a user\n * message's `metadata.custom.interactables` (by the interactables scope at send\n * time) and prepends a text part. Run this in your route handler before\n * `convertToModelMessages`, which otherwise ignores `metadata.custom`.\n *\n * Wording is consumer-owned — pass `format` to control how each snapshot reads.\n * A snapshot may originate from a user edit or an agent `update_*` call, so the\n * default phrasing is neutral. Keep the instance id visible in custom wording:\n * the model needs it to address the `update_*` tool's `id` parameter. A custom\n * `format` must also handle entries with `partial: true`, whose `state` carries\n * only the fields that changed since the model's last known state.\n *\n * @example\n * ```ts\n * import { convertToModelMessages, streamText } from \"ai\";\n * import { unstable_injectInteractableContext } from \"@assistant-ui/
|
|
1
|
+
{"version":3,"file":"injectInteractableContext.js","names":["UIMessage","unstable_formatInteractableSnapshot","unstable_getInteractableSnapshots","Unstable_InteractableSnapshotEntry","unstable_injectInteractableContext","messages","format","item","map","msg","role","items","length","text","join","alreadyInjected","parts","type","const"],"sources":["../../src/model-context/injectInteractableContext.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\nimport {\n unstable_formatInteractableSnapshot,\n unstable_getInteractableSnapshots,\n type Unstable_InteractableSnapshotEntry,\n} from \"@assistant-ui/core\";\n\n/**\n * Injects interactable state snapshots into messages as model-visible text.\n *\n * Mirrors {@link injectQuoteContext}: reads the frozen snapshot stamped on a user\n * message's `metadata.custom.interactables` (by the interactables scope at send\n * time) and prepends a text part. Run this in your route handler before\n * `convertToModelMessages`, which otherwise ignores `metadata.custom`.\n *\n * Wording is consumer-owned — pass `format` to control how each snapshot reads.\n * A snapshot may originate from a user edit or an agent `update_*` call, so the\n * default phrasing is neutral. Keep the instance id visible in custom wording:\n * the model needs it to address the `update_*` tool's `id` parameter. A custom\n * `format` must also handle entries with `partial: true`, whose `state` carries\n * only the fields that changed since the model's last known state.\n *\n * @example\n * ```ts\n * import { convertToModelMessages, streamText } from \"ai\";\n * import { unstable_injectInteractableContext } from \"@assistant-ui/ai-sdk\";\n *\n * export async function POST(req: Request) {\n * const { messages } = await req.json();\n * const result = streamText({\n * model: myModel,\n * messages: await convertToModelMessages(unstable_injectInteractableContext(messages)),\n * });\n * return result.toUIMessageStreamResponse();\n * }\n * ```\n */\nexport function unstable_injectInteractableContext(\n messages: UIMessage[],\n format: (\n item: Unstable_InteractableSnapshotEntry,\n ) => string = unstable_formatInteractableSnapshot,\n): UIMessage[] {\n return messages.map((msg) => {\n if (msg.role !== \"user\") return msg;\n\n const items = unstable_getInteractableSnapshots(msg);\n if (!items?.length) return msg;\n\n const text = `${items.map(format).join(\"\\n\")}\\n\\n`;\n\n const alreadyInjected =\n msg.parts?.[0]?.type === \"text\" && msg.parts[0].text === text;\n if (alreadyInjected) return msg;\n\n return {\n ...msg,\n parts: [{ type: \"text\" as const, text }, ...(msg.parts ?? [])],\n };\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgBI,mCACdC,UACAC,SAEcL,qCACD;CACb,OAAOI,SAASG,KAAKC,QAAQ;EAC3B,IAAIA,IAAIC,SAAS,QAAQ,OAAOD;EAEhC,MAAME,QAAQT,kCAAkCO,GAAG;EACnD,IAAI,CAACE,OAAOC,QAAQ,OAAOH;EAE3B,MAAMI,OAAO,GAAGF,MAAMH,IAAIF,MAAM,CAAC,CAACQ,KAAK,IAAI,EAAC;EAI5C,IADEL,IAAIO,QAAQ,EAAE,EAAEC,SAAS,UAAUR,IAAIO,MAAM,EAAE,CAACH,SAASA,MACtC,OAAOJ;EAE5B,OAAO;GACL,GAAGA;GACHO,OAAO,CAAC;IAAEC,MAAM;IAAiBJ;GAAK,GAAG,GAAIJ,IAAIO,SAAS,CAAA,CAAG;EAC/D;CACF,CAAC;AACH"}
|
|
@@ -9,7 +9,7 @@ import { UIMessage } from "ai";
|
|
|
9
9
|
* @example
|
|
10
10
|
* ```ts
|
|
11
11
|
* import { convertToModelMessages, streamText } from "ai";
|
|
12
|
-
* import { injectQuoteContext } from "@assistant-ui/
|
|
12
|
+
* import { injectQuoteContext } from "@assistant-ui/ai-sdk";
|
|
13
13
|
*
|
|
14
14
|
* export async function POST(req: Request) {
|
|
15
15
|
* const { messages } = await req.json();
|
|
@@ -17,7 +17,7 @@ const getQuoteText = (metadata) => {
|
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* import { convertToModelMessages, streamText } from "ai";
|
|
20
|
-
* import { injectQuoteContext } from "@assistant-ui/
|
|
20
|
+
* import { injectQuoteContext } from "@assistant-ui/ai-sdk";
|
|
21
21
|
*
|
|
22
22
|
* export async function POST(req: Request) {
|
|
23
23
|
* const { messages } = await req.json();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injectQuoteContext.js","names":["UIMessage","getQuoteText","metadata","undefined","custom","Record","quote","text","injectQuoteContext","messages","map","msg","role","blockquote","split","line","join","alreadyInjected","parts","type","const"],"sources":["../../src/model-context/injectQuoteContext.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nconst getQuoteText = (metadata: unknown): string | undefined => {\n if (!metadata || typeof metadata !== \"object\") return undefined;\n\n const custom = (metadata as Record<string, unknown>).custom;\n if (!custom || typeof custom !== \"object\") return undefined;\n\n const quote = (custom as Record<string, unknown>).quote;\n if (!quote || typeof quote !== \"object\") return undefined;\n\n const text = (quote as Record<string, unknown>).text;\n return typeof text === \"string\" ? text : undefined;\n};\n\n/**\n * Injects quote context into messages as markdown blockquotes.\n *\n * Use this in your route handler before `convertToModelMessages` so the LLM\n * sees the quoted text that the user is referring to.\n *\n * @example\n * ```ts\n * import { convertToModelMessages, streamText } from \"ai\";\n * import { injectQuoteContext } from \"@assistant-ui/
|
|
1
|
+
{"version":3,"file":"injectQuoteContext.js","names":["UIMessage","getQuoteText","metadata","undefined","custom","Record","quote","text","injectQuoteContext","messages","map","msg","role","blockquote","split","line","join","alreadyInjected","parts","type","const"],"sources":["../../src/model-context/injectQuoteContext.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nconst getQuoteText = (metadata: unknown): string | undefined => {\n if (!metadata || typeof metadata !== \"object\") return undefined;\n\n const custom = (metadata as Record<string, unknown>).custom;\n if (!custom || typeof custom !== \"object\") return undefined;\n\n const quote = (custom as Record<string, unknown>).quote;\n if (!quote || typeof quote !== \"object\") return undefined;\n\n const text = (quote as Record<string, unknown>).text;\n return typeof text === \"string\" ? text : undefined;\n};\n\n/**\n * Injects quote context into messages as markdown blockquotes.\n *\n * Use this in your route handler before `convertToModelMessages` so the LLM\n * sees the quoted text that the user is referring to.\n *\n * @example\n * ```ts\n * import { convertToModelMessages, streamText } from \"ai\";\n * import { injectQuoteContext } from \"@assistant-ui/ai-sdk\";\n *\n * export async function POST(req: Request) {\n * const { messages } = await req.json();\n * const result = streamText({\n * model: myModel,\n * messages: await convertToModelMessages(injectQuoteContext(messages)),\n * });\n * return result.toUIMessageStreamResponse();\n * }\n * ```\n */\nexport function injectQuoteContext(messages: UIMessage[]): UIMessage[] {\n return messages.map((msg) => {\n if (msg.role !== \"user\") return msg;\n\n const text = getQuoteText(msg.metadata);\n if (!text) return msg;\n\n const blockquote = text\n .split(/\\r?\\n/)\n .map((line) => `> ${line}`)\n .join(\"\\n\");\n\n const alreadyInjected =\n msg.parts?.[0]?.type === \"text\" &&\n msg.parts[0].text === `${blockquote}\\n\\n`;\n if (alreadyInjected) return msg;\n\n return {\n ...msg,\n parts: [\n { type: \"text\" as const, text: `${blockquote}\\n\\n` },\n ...(msg.parts ?? []),\n ],\n };\n });\n}\n"],"mappings":";AAEA,MAAMC,gBAAgBC,aAA0C;CAC9D,IAAI,CAACA,YAAY,OAAOA,aAAa,UAAU,OAAOC,KAAAA;CAEtD,MAAMC,SAAUF,SAAqCE;CACrD,IAAI,CAACA,UAAU,OAAOA,WAAW,UAAU,OAAOD,KAAAA;CAElD,MAAMG,QAASF,OAAmCE;CAClD,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU,OAAOH,KAAAA;CAEhD,MAAMI,OAAQD,MAAkCC;CAChD,OAAO,OAAOA,SAAS,WAAWA,OAAOJ,KAAAA;AAC3C;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgBK,mBAAmBC,UAAoC;CACrE,OAAOA,SAASC,KAAKC,QAAQ;EAC3B,IAAIA,IAAIC,SAAS,QAAQ,OAAOD;EAEhC,MAAMJ,OAAON,aAAaU,IAAIT,QAAQ;EACtC,IAAI,CAACK,MAAM,OAAOI;EAElB,MAAME,aAAaN,KAChBO,MAAM,OAAO,CAAC,CACdJ,KAAKK,SAAS,KAAKA,MAAM,CAAC,CAC1BC,KAAK,IAAI;EAKZ,IAFEL,IAAIO,QAAQ,EAAE,EAAEC,SAAS,UACzBR,IAAIO,MAAM,EAAE,CAACX,SAAS,GAAGM,WAAU,OAChB,OAAOF;EAE5B,OAAO;GACL,GAAGA;GACHO,OAAO,CACL;IAAEC,MAAM;IAAiBZ,MAAM,GAAGM,WAAU;GAAO,GACnD,GAAIF,IAAIO,SAAS,CAAA,CAAG;EAExB;CACF,CAAC;AACH"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useChatThread } from "./useChatThread.js";
|
|
3
|
+
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
3
4
|
import { useState } from "@assistant-ui/tap/react-shim";
|
|
4
5
|
import { generateId } from "ai";
|
|
5
|
-
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
6
6
|
import { resource, useResource } from "@assistant-ui/tap";
|
|
7
7
|
import { RuntimeAdapter, runtimeAdapterTransformScopes } from "@assistant-ui/core/store";
|
|
8
8
|
import { attachTransformScopes } from "@assistant-ui/store/client";
|
|
@@ -14,9 +14,9 @@ type AISDKThreadsOptions<UI_MESSAGE extends UIMessage$1 = UIMessage$1> = Omit<Ch
|
|
|
14
14
|
transport?: ChatTransport<UI_MESSAGE> | (() => ChatTransport<UI_MESSAGE>) | undefined;
|
|
15
15
|
/**
|
|
16
16
|
* When set, the thread list is a `RemoteThreadList` backed by this
|
|
17
|
-
* assistant-cloud. Omit it to keep the in-memory list.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* assistant-cloud. Omit it to keep the in-memory list. Every visited
|
|
18
|
+
* cloud thread stays mounted, so an in-flight run continues after a
|
|
19
|
+
* switch and stops on delete; per-thread history loads once per thread.
|
|
20
20
|
*/
|
|
21
21
|
cloud?: AssistantCloud | undefined;
|
|
22
22
|
/**
|
|
@@ -33,11 +33,13 @@ type AISDKThreadsOptions<UI_MESSAGE extends UIMessage$1 = UIMessage$1> = Omit<Ch
|
|
|
33
33
|
* per-thread orchestration as {@link AISDKChat} inside the client's own
|
|
34
34
|
* resource tree, so it works with any `AssistantClient` host, React or not.
|
|
35
35
|
* Without `cloud`, threads live in memory for the client's lifetime and keep
|
|
36
|
-
* their history across switches;
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* their history across switches; only the visible thread is mounted, and a
|
|
37
|
+
* switched-away chat keeps streaming into its stored state until it settles
|
|
38
|
+
* or the thread is deleted. With `cloud`, the list is a
|
|
39
|
+
* `RemoteThreadList` with `backgroundThreads`: every visited thread stays
|
|
40
|
+
* mounted with its own history, a run continues after a switch and stops on
|
|
41
|
+
* delete, and a freshly created thread titles itself. Model context is
|
|
42
|
+
* registered on every mounted thread.
|
|
41
43
|
*/
|
|
42
44
|
declare const AISDKThreads: <UI_MESSAGE extends UIMessage$1 = UIMessage$1<unknown, import("ai").UIDataTypes, import("ai").UITools>>(options?: AISDKThreadsOptions<UI_MESSAGE> | undefined) => import("@assistant-ui/tap").ResourceElement<import("@assistant-ui/store").ClientOutput<"threads">>;
|
|
43
45
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AISDKThreads.d.ts","names":[],"sources":["../../src/runtime/AISDKThreads.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"AISDKThreads.d.ts","names":[],"sources":["../../src/runtime/AISDKThreads.ts"],"mappings":";;;;;KA6BY,oBAAoB,mBAAmB,cAAY,eAC7D,KAAK,kBAAkB;;;;;;;;EAQrB,YACI,cAAc,qBACP,cAAc;;;;;;;EAQzB,QAAQ;;;;EAIR;;;;EAIA,qBAAqB;;;;;;;;;;;;;;;cA2KZ,eA3DY,mBAAmB,cAAS,kCAAA,0BAAA,UAAA,UAAA,oBAAA,wDAAA,8CAAA"}
|
|
@@ -3,9 +3,10 @@ import { AssistantChatTransport } from "../transport/AssistantChatTransport.js";
|
|
|
3
3
|
import { useResourceCleanup } from "./useResourceCleanup.js";
|
|
4
4
|
import { splitChatThreadOptions, useChatThread } from "./useChatThread.js";
|
|
5
5
|
import { useCloudThreadListAdapter } from "@assistant-ui/core/react";
|
|
6
|
+
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
6
7
|
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
8
|
+
import { MessageRepository } from "@assistant-ui/core/internal";
|
|
7
9
|
import { useAui } from "@assistant-ui/store";
|
|
8
|
-
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
9
10
|
import { Chat } from "@ai-sdk/react";
|
|
10
11
|
import { resource, useResource, withKey } from "@assistant-ui/tap";
|
|
11
12
|
import { InMemoryThreadList, RemoteThreadList, inMemoryThreadListTransformScopes } from "@assistant-ui/core/store";
|
|
@@ -21,7 +22,8 @@ const createChatEntry = (threadId, options) => {
|
|
|
21
22
|
id: threadId,
|
|
22
23
|
transport
|
|
23
24
|
}),
|
|
24
|
-
transport
|
|
25
|
+
transport,
|
|
26
|
+
repository: new MessageRepository()
|
|
25
27
|
};
|
|
26
28
|
};
|
|
27
29
|
const getOrCreateChatEntry = (threadId, options, chats) => {
|
|
@@ -32,7 +34,7 @@ const getOrCreateChatEntry = (threadId, options, chats) => {
|
|
|
32
34
|
return created;
|
|
33
35
|
};
|
|
34
36
|
const useAISDKChatThread = (t0) => {
|
|
35
|
-
const $ = c(
|
|
37
|
+
const $ = c(35);
|
|
36
38
|
const { threadId, options, chats, cloud } = t0;
|
|
37
39
|
let t1;
|
|
38
40
|
if ($[0] !== cloud || $[1] !== options || $[2] !== threadId) {
|
|
@@ -52,7 +54,7 @@ const useAISDKChatThread = (t0) => {
|
|
|
52
54
|
$[7] = threadId;
|
|
53
55
|
$[8] = t2;
|
|
54
56
|
} else t2 = $[8];
|
|
55
|
-
const { chat, transport } = t2;
|
|
57
|
+
const { chat, transport, repository } = t2;
|
|
56
58
|
let t3;
|
|
57
59
|
let t4;
|
|
58
60
|
if ($[9] !== chat || $[10] !== cloud) {
|
|
@@ -102,42 +104,44 @@ const useAISDKChatThread = (t0) => {
|
|
|
102
104
|
$[21] = t7;
|
|
103
105
|
} else t7 = $[21];
|
|
104
106
|
let t8;
|
|
105
|
-
if ($[22] !== chat || $[23] !== cloud || $[24] !==
|
|
107
|
+
if ($[22] !== chat || $[23] !== cloud || $[24] !== repository || $[25] !== t7 || $[26] !== threadId) {
|
|
106
108
|
t8 = {
|
|
107
109
|
id: threadId,
|
|
108
110
|
isMainThread: true,
|
|
109
111
|
getThreadListItem: t7,
|
|
110
112
|
chat,
|
|
113
|
+
messageRepositoryInstance: repository,
|
|
111
114
|
stopOnClientDestroy: cloud
|
|
112
115
|
};
|
|
113
116
|
$[22] = chat;
|
|
114
117
|
$[23] = cloud;
|
|
115
|
-
$[24] =
|
|
116
|
-
$[25] =
|
|
117
|
-
$[26] =
|
|
118
|
-
|
|
118
|
+
$[24] = repository;
|
|
119
|
+
$[25] = t7;
|
|
120
|
+
$[26] = threadId;
|
|
121
|
+
$[27] = t8;
|
|
122
|
+
} else t8 = $[27];
|
|
119
123
|
const runtime = useChatThread(t6, t8);
|
|
120
124
|
const clientRef = useAssistantClientRef();
|
|
121
125
|
let t9;
|
|
122
|
-
if ($[
|
|
126
|
+
if ($[28] !== clientRef || $[29] !== runtime) {
|
|
123
127
|
t9 = () => runtime.registerModelContextProvider(clientRef.current.modelContext());
|
|
124
|
-
$[
|
|
125
|
-
$[
|
|
126
|
-
$[
|
|
127
|
-
} else t9 = $[
|
|
128
|
+
$[28] = clientRef;
|
|
129
|
+
$[29] = runtime;
|
|
130
|
+
$[30] = t9;
|
|
131
|
+
} else t9 = $[30];
|
|
128
132
|
let t10;
|
|
129
|
-
if ($[
|
|
133
|
+
if ($[31] !== runtime) {
|
|
130
134
|
t10 = [runtime];
|
|
131
|
-
$[
|
|
132
|
-
$[
|
|
133
|
-
} else t10 = $[
|
|
135
|
+
$[31] = runtime;
|
|
136
|
+
$[32] = t10;
|
|
137
|
+
} else t10 = $[32];
|
|
134
138
|
useAssistantScopeEffect("modelContext", t9, t10);
|
|
135
139
|
let t11;
|
|
136
|
-
if ($[
|
|
140
|
+
if ($[33] !== runtime.thread) {
|
|
137
141
|
t11 = ThreadClient({ runtime: runtime.thread });
|
|
138
|
-
$[
|
|
139
|
-
$[
|
|
140
|
-
} else t11 = $[
|
|
142
|
+
$[33] = runtime.thread;
|
|
143
|
+
$[34] = t11;
|
|
144
|
+
} else t11 = $[34];
|
|
141
145
|
return useResource(t11);
|
|
142
146
|
};
|
|
143
147
|
const AISDKChatThread = resource(useAISDKChatThread);
|
|
@@ -167,6 +171,7 @@ const useAISDKThreads = (options) => {
|
|
|
167
171
|
};
|
|
168
172
|
return useResource(bindCloud ? RemoteThreadList({
|
|
169
173
|
adapter: cloudAdapter,
|
|
174
|
+
backgroundThreads: true,
|
|
170
175
|
thread,
|
|
171
176
|
threadId,
|
|
172
177
|
onThreadIdChange
|
|
@@ -183,11 +188,13 @@ const useAISDKThreads = (options) => {
|
|
|
183
188
|
* per-thread orchestration as {@link AISDKChat} inside the client's own
|
|
184
189
|
* resource tree, so it works with any `AssistantClient` host, React or not.
|
|
185
190
|
* Without `cloud`, threads live in memory for the client's lifetime and keep
|
|
186
|
-
* their history across switches;
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
+
* their history across switches; only the visible thread is mounted, and a
|
|
192
|
+
* switched-away chat keeps streaming into its stored state until it settles
|
|
193
|
+
* or the thread is deleted. With `cloud`, the list is a
|
|
194
|
+
* `RemoteThreadList` with `backgroundThreads`: every visited thread stays
|
|
195
|
+
* mounted with its own history, a run continues after a switch and stops on
|
|
196
|
+
* delete, and a freshly created thread titles itself. Model context is
|
|
197
|
+
* registered on every mounted thread.
|
|
191
198
|
*/
|
|
192
199
|
const AISDKThreads = resource(useAISDKThreads);
|
|
193
200
|
attachTransformScopes(useAISDKThreads, inMemoryThreadListTransformScopes);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AISDKThreads.js","names":["c","_c","resource","useResource","withKey","useEffect","useMemo","useState","Chat","UIMessage","ChatTransport","AssistantCloud","InMemoryThreadList","RemoteThreadList","inMemoryThreadListTransformScopes","ThreadClient","useCloudThreadListAdapter","attachTransformScopes","useAssistantClientRef","useAssistantScopeEffect","useAui","AssistantChatTransport","splitChatThreadOptions","useChatThread","ChatThreadOptions","useResourceCleanup","AISDKThreadsOptions","Omit","UI_MESSAGE","transport","cloud","threadId","onThreadIdChange","AISDKThreadChatOptions","ChatEntry","chat","createChatEntry","options","chatInit","undefined","__internal_clone","id","getOrCreateChatEntry","chats","Map","existing","get","created","set","useAISDKChatThread","t0","$","t1","owned","t2","t3","t4","stop","catch","_temp","aui","t5","initialize","remoteId","externalId","fallbackItem","t6","t7","threadListItem","source","t8","isMainThread","getThreadListItem","stopOnClientDestroy","runtime","clientRef","t9","registerModelContextProvider","current","modelContext","t10","t11","thread","AISDKChatThread","useAISDKThreads","threadOptions","_temp2","bindCloud","values","_temp3","cloudAdapter","element","adapter","onDelete","id_0","_temp4","delete","AISDKThreads"],"sources":["../../src/runtime/AISDKThreads.ts"],"sourcesContent":["\"use client\";\n\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { Chat, type UIMessage } from \"@ai-sdk/react\";\nimport type { ChatTransport } from \"ai\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport {\n InMemoryThreadList,\n RemoteThreadList,\n inMemoryThreadListTransformScopes,\n} from \"@assistant-ui/core/store\";\nimport { ThreadClient } from \"@assistant-ui/core/store/internal\";\nimport { useCloudThreadListAdapter } from \"@assistant-ui/core/react\";\nimport {\n attachTransformScopes,\n useAssistantClientRef,\n useAssistantScopeEffect,\n} from \"@assistant-ui/store/client\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { AssistantChatTransport } from \"../transport/AssistantChatTransport\";\nimport {\n splitChatThreadOptions,\n useChatThread,\n type ChatThreadOptions,\n} from \"./useChatThread\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type AISDKThreadsOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n Omit<ChatThreadOptions<UI_MESSAGE>, \"id\" | \"transport\" | \"messages\"> & {\n /**\n * The transport threads send through. A factory is invoked once per\n * thread so each thread owns its instance. A plain\n * `AssistantChatTransport` instance is cloned per thread (its\n * assistant-ui wiring is per thread); any other transport instance is\n * shared as-is. Defaults to one `AssistantChatTransport` per thread.\n */\n transport?:\n | ChatTransport<UI_MESSAGE>\n | (() => ChatTransport<UI_MESSAGE>)\n | undefined;\n /**\n * When set, the thread list is a `RemoteThreadList` backed by this\n * assistant-cloud. Omit it to keep the in-memory list. The thread\n * factory is keyed so cloud history reloads on a switch, and an\n * in-flight run does not continue in the background.\n */\n cloud?: AssistantCloud | undefined;\n /**\n * Controlled thread id for the cloud list. Ignored without `cloud`.\n */\n threadId?: string | undefined;\n /**\n * Called with the settled remote id when the cloud list changes thread.\n */\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n };\n\ntype AISDKThreadChatOptions<UI_MESSAGE extends UIMessage = UIMessage> = Omit<\n AISDKThreadsOptions<UI_MESSAGE>,\n \"cloud\" | \"threadId\" | \"onThreadIdChange\"\n>;\n\ntype ChatEntry<UI_MESSAGE extends UIMessage> = {\n chat: Chat<UI_MESSAGE>;\n transport: ChatTransport<UI_MESSAGE>;\n};\n\nconst createChatEntry = <UI_MESSAGE extends UIMessage>(\n threadId: string,\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,\n): ChatEntry<UI_MESSAGE> => {\n const { chatInit } = splitChatThreadOptions(\n options as ChatThreadOptions<UI_MESSAGE> | undefined,\n );\n const transport =\n typeof options?.transport === \"function\"\n ? options.transport()\n : options?.transport === undefined\n ? new AssistantChatTransport()\n : options.transport instanceof AssistantChatTransport\n ? options.transport.__internal_clone()\n : options.transport;\n return {\n chat: new Chat<UI_MESSAGE>({ ...chatInit, id: threadId, transport }),\n transport,\n };\n};\n\nconst getOrCreateChatEntry = <UI_MESSAGE extends UIMessage>(\n threadId: string,\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,\n chats: Map<string, ChatEntry<UI_MESSAGE>>,\n): ChatEntry<UI_MESSAGE> => {\n const existing = chats.get(threadId);\n if (existing) return existing;\n const created = createChatEntry(threadId, options);\n chats.set(threadId, created);\n return created;\n};\n\nconst useAISDKChatThread = <UI_MESSAGE extends UIMessage = UIMessage>({\n threadId,\n options,\n chats,\n cloud,\n}: {\n threadId: string;\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined;\n chats: Map<string, ChatEntry<UI_MESSAGE>>;\n cloud: boolean;\n}) => {\n const [owned] = useState(() =>\n cloud ? createChatEntry(threadId, options) : undefined,\n );\n const { chat, transport } =\n owned ?? getOrCreateChatEntry(threadId, options, chats);\n\n useEffect(() => {\n if (!cloud) return undefined;\n return () => {\n void chat.stop().catch(() => {});\n };\n }, [chat, cloud]);\n\n const aui = useAui();\n const fallbackItem = useMemo(\n () => ({\n initialize: async () => ({\n remoteId: threadId,\n externalId: undefined,\n }),\n }),\n [threadId],\n );\n const runtime = useChatThread(\n { ...options, transport } as ChatThreadOptions<UI_MESSAGE>,\n {\n id: threadId,\n isMainThread: true,\n getThreadListItem: () =>\n cloud\n ? aui.threadListItem.source\n ? aui.threadListItem\n : undefined\n : fallbackItem,\n chat,\n stopOnClientDestroy: cloud,\n },\n );\n\n const clientRef = useAssistantClientRef();\n useAssistantScopeEffect(\n \"modelContext\",\n () =>\n runtime.registerModelContextProvider(clientRef.current!.modelContext()),\n [runtime],\n );\n\n return useResource(ThreadClient({ runtime: runtime.thread }));\n};\n\nconst AISDKChatThread = resource(useAISDKChatThread);\n\nconst useAISDKThreads = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: AISDKThreadsOptions<UI_MESSAGE>,\n) => {\n const { cloud, threadId, onThreadIdChange, ...threadOptions } = options ?? {};\n const [chats] = useState(() => new Map<string, ChatEntry<UI_MESSAGE>>());\n const bindCloud = cloud !== undefined;\n\n useResourceCleanup(true, () => {\n for (const { chat } of chats.values()) {\n void chat.stop().catch(() => {});\n }\n });\n\n const cloudAdapter = useCloudThreadListAdapter({ cloud });\n const thread = (id: string) => {\n const element = AISDKChatThread({\n threadId: id,\n options: threadOptions,\n chats,\n cloud: bindCloud,\n });\n return bindCloud ? withKey(id, element) : element;\n };\n\n return useResource(\n bindCloud\n ? RemoteThreadList({\n adapter: cloudAdapter,\n thread,\n threadId,\n onThreadIdChange,\n })\n : InMemoryThreadList({\n thread,\n onDelete: (id) => {\n void chats\n .get(id)\n ?.chat.stop()\n .catch(() => {});\n chats.delete(id);\n },\n }),\n );\n};\n\n/**\n * `AuiConfig` entry that runs one AI SDK chat per thread. Hosts the same\n * per-thread orchestration as {@link AISDKChat} inside the client's own\n * resource tree, so it works with any `AssistantClient` host, React or not.\n * Without `cloud`, threads live in memory for the client's lifetime and keep\n * their history across switches; each thread's chat id is its thread id.\n * With `cloud`, the list is a `RemoteThreadList` and the factory is keyed so\n * cloud history reloads on a switch. The store entry mounts only the visible\n * thread, so a switch cancels an in-flight run. Model context is\n * registered on the visible thread only.\n */\nexport const AISDKThreads = resource(useAISDKThreads);\n\nattachTransformScopes(useAISDKThreads, inMemoryThreadListTransformScopes);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AISDKThreads.js","names":["c","_c","resource","useResource","withKey","useEffect","useMemo","useState","Chat","UIMessage","ChatTransport","AssistantCloud","InMemoryThreadList","RemoteThreadList","inMemoryThreadListTransformScopes","ThreadClient","useCloudThreadListAdapter","attachTransformScopes","useAssistantClientRef","useAssistantScopeEffect","useAui","AssistantChatTransport","splitChatThreadOptions","useChatThread","ChatThreadOptions","MessageRepository","useResourceCleanup","AISDKThreadsOptions","Omit","UI_MESSAGE","transport","cloud","threadId","onThreadIdChange","AISDKThreadChatOptions","ChatEntry","chat","repository","createChatEntry","options","chatInit","undefined","__internal_clone","id","getOrCreateChatEntry","chats","Map","existing","get","created","set","useAISDKChatThread","t0","$","t1","owned","t2","t3","t4","stop","catch","_temp","aui","t5","initialize","remoteId","externalId","fallbackItem","t6","t7","threadListItem","source","t8","isMainThread","getThreadListItem","messageRepositoryInstance","stopOnClientDestroy","runtime","clientRef","t9","registerModelContextProvider","current","modelContext","t10","t11","thread","AISDKChatThread","useAISDKThreads","threadOptions","_temp2","bindCloud","values","_temp3","cloudAdapter","element","adapter","backgroundThreads","onDelete","id_0","_temp4","delete","AISDKThreads"],"sources":["../../src/runtime/AISDKThreads.ts"],"sourcesContent":["\"use client\";\n\nimport { resource, useResource, withKey } from \"@assistant-ui/tap\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { Chat, type UIMessage } from \"@ai-sdk/react\";\nimport type { ChatTransport } from \"ai\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport {\n InMemoryThreadList,\n RemoteThreadList,\n inMemoryThreadListTransformScopes,\n} from \"@assistant-ui/core/store\";\nimport { ThreadClient } from \"@assistant-ui/core/store/internal\";\nimport { useCloudThreadListAdapter } from \"@assistant-ui/core/react\";\nimport {\n attachTransformScopes,\n useAssistantClientRef,\n useAssistantScopeEffect,\n} from \"@assistant-ui/store/client\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { AssistantChatTransport } from \"../transport/AssistantChatTransport\";\nimport {\n splitChatThreadOptions,\n useChatThread,\n type ChatThreadOptions,\n} from \"./useChatThread\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type AISDKThreadsOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n Omit<ChatThreadOptions<UI_MESSAGE>, \"id\" | \"transport\" | \"messages\"> & {\n /**\n * The transport threads send through. A factory is invoked once per\n * thread so each thread owns its instance. A plain\n * `AssistantChatTransport` instance is cloned per thread (its\n * assistant-ui wiring is per thread); any other transport instance is\n * shared as-is. Defaults to one `AssistantChatTransport` per thread.\n */\n transport?:\n | ChatTransport<UI_MESSAGE>\n | (() => ChatTransport<UI_MESSAGE>)\n | undefined;\n /**\n * When set, the thread list is a `RemoteThreadList` backed by this\n * assistant-cloud. Omit it to keep the in-memory list. Every visited\n * cloud thread stays mounted, so an in-flight run continues after a\n * switch and stops on delete; per-thread history loads once per thread.\n */\n cloud?: AssistantCloud | undefined;\n /**\n * Controlled thread id for the cloud list. Ignored without `cloud`.\n */\n threadId?: string | undefined;\n /**\n * Called with the settled remote id when the cloud list changes thread.\n */\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n };\n\ntype AISDKThreadChatOptions<UI_MESSAGE extends UIMessage = UIMessage> = Omit<\n AISDKThreadsOptions<UI_MESSAGE>,\n \"cloud\" | \"threadId\" | \"onThreadIdChange\"\n>;\n\ntype ChatEntry<UI_MESSAGE extends UIMessage> = {\n chat: Chat<UI_MESSAGE>;\n transport: ChatTransport<UI_MESSAGE>;\n repository: MessageRepository;\n};\n\nconst createChatEntry = <UI_MESSAGE extends UIMessage>(\n threadId: string,\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,\n): ChatEntry<UI_MESSAGE> => {\n const { chatInit } = splitChatThreadOptions(\n options as ChatThreadOptions<UI_MESSAGE> | undefined,\n );\n const transport =\n typeof options?.transport === \"function\"\n ? options.transport()\n : options?.transport === undefined\n ? new AssistantChatTransport()\n : options.transport instanceof AssistantChatTransport\n ? options.transport.__internal_clone()\n : options.transport;\n return {\n chat: new Chat<UI_MESSAGE>({ ...chatInit, id: threadId, transport }),\n transport,\n repository: new MessageRepository(),\n };\n};\n\nconst getOrCreateChatEntry = <UI_MESSAGE extends UIMessage>(\n threadId: string,\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined,\n chats: Map<string, ChatEntry<UI_MESSAGE>>,\n): ChatEntry<UI_MESSAGE> => {\n const existing = chats.get(threadId);\n if (existing) return existing;\n const created = createChatEntry(threadId, options);\n chats.set(threadId, created);\n return created;\n};\n\nconst useAISDKChatThread = <UI_MESSAGE extends UIMessage = UIMessage>({\n threadId,\n options,\n chats,\n cloud,\n}: {\n threadId: string;\n options: AISDKThreadChatOptions<UI_MESSAGE> | undefined;\n chats: Map<string, ChatEntry<UI_MESSAGE>>;\n cloud: boolean;\n}) => {\n const [owned] = useState(() =>\n cloud ? createChatEntry(threadId, options) : undefined,\n );\n const { chat, transport, repository } =\n owned ?? getOrCreateChatEntry(threadId, options, chats);\n\n useEffect(() => {\n if (!cloud) return undefined;\n return () => {\n void chat.stop().catch(() => {});\n };\n }, [chat, cloud]);\n\n const aui = useAui();\n const fallbackItem = useMemo(\n () => ({\n initialize: async () => ({\n remoteId: threadId,\n externalId: undefined,\n }),\n }),\n [threadId],\n );\n const runtime = useChatThread(\n { ...options, transport } as ChatThreadOptions<UI_MESSAGE>,\n {\n id: threadId,\n isMainThread: true,\n getThreadListItem: () =>\n cloud\n ? aui.threadListItem.source\n ? aui.threadListItem\n : undefined\n : fallbackItem,\n chat,\n messageRepositoryInstance: repository,\n stopOnClientDestroy: cloud,\n },\n );\n\n const clientRef = useAssistantClientRef();\n useAssistantScopeEffect(\n \"modelContext\",\n () =>\n runtime.registerModelContextProvider(clientRef.current!.modelContext()),\n [runtime],\n );\n\n return useResource(ThreadClient({ runtime: runtime.thread }));\n};\n\nconst AISDKChatThread = resource(useAISDKChatThread);\n\nconst useAISDKThreads = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: AISDKThreadsOptions<UI_MESSAGE>,\n) => {\n const { cloud, threadId, onThreadIdChange, ...threadOptions } = options ?? {};\n const [chats] = useState(() => new Map<string, ChatEntry<UI_MESSAGE>>());\n const bindCloud = cloud !== undefined;\n\n useResourceCleanup(true, () => {\n for (const { chat } of chats.values()) {\n void chat.stop().catch(() => {});\n }\n });\n\n const cloudAdapter = useCloudThreadListAdapter({ cloud });\n const thread = (id: string) => {\n const element = AISDKChatThread({\n threadId: id,\n options: threadOptions,\n chats,\n cloud: bindCloud,\n });\n return bindCloud ? withKey(id, element) : element;\n };\n\n return useResource(\n bindCloud\n ? RemoteThreadList({\n adapter: cloudAdapter,\n backgroundThreads: true,\n thread,\n threadId,\n onThreadIdChange,\n })\n : InMemoryThreadList({\n thread,\n onDelete: (id) => {\n void chats\n .get(id)\n ?.chat.stop()\n .catch(() => {});\n chats.delete(id);\n },\n }),\n );\n};\n\n/**\n * `AuiConfig` entry that runs one AI SDK chat per thread. Hosts the same\n * per-thread orchestration as {@link AISDKChat} inside the client's own\n * resource tree, so it works with any `AssistantClient` host, React or not.\n * Without `cloud`, threads live in memory for the client's lifetime and keep\n * their history across switches; only the visible thread is mounted, and a\n * switched-away chat keeps streaming into its stored state until it settles\n * or the thread is deleted. With `cloud`, the list is a\n * `RemoteThreadList` with `backgroundThreads`: every visited thread stays\n * mounted with its own history, a run continues after a switch and stops on\n * delete, and a freshly created thread titles itself. Model context is\n * registered on every mounted thread.\n */\nexport const AISDKThreads = resource(useAISDKThreads);\n\nattachTransformScopes(useAISDKThreads, inMemoryThreadListTransformScopes);\n"],"mappings":"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"}
|