@assistant-ui/react-ai-sdk 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,7 +44,7 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
44
44
  ),
45
45
  onCancel: async () => chatHelpers.stop(),
46
46
  onNew: async (message) => {
47
- const createMessage = await toCreateMessage(message);
47
+ const createMessage = toCreateMessage(message);
48
48
  await chatHelpers.sendMessage(createMessage, {
49
49
  metadata: message.runConfig
50
50
  });
@@ -55,7 +55,7 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
55
55
  message.parentId
56
56
  );
57
57
  chatHelpers.setMessages(newMessages);
58
- const createMessage = await toCreateMessage(message);
58
+ const createMessage = toCreateMessage(message);
59
59
  await chatHelpers.sendMessage(createMessage, {
60
60
  metadata: message.runConfig
61
61
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage, useChat } from \"@ai-sdk/react\";\nimport {\n useExternalStoreRuntime,\n ExternalStoreAdapter,\n ThreadHistoryAdapter,\n AssistantRuntime,\n ThreadMessage,\n MessageFormatAdapter,\n} from \"@assistant-ui/react\";\nimport { sliceMessagesUntil } from \"../utils/sliceMessagesUntil\";\nimport { toCreateMessage } from \"../utils/toCreateMessage\";\nimport { vercelAttachmentAdapter } from \"../utils/vercelAttachmentAdapter\";\nimport { getVercelAIMessages } from \"../getVercelAIMessages\";\nimport { AISDKMessageConverter } from \"../utils/convertMessage\";\nimport {\n AISDKStorageFormat,\n aiSDKV5FormatAdapter,\n} from \"../adapters/aiSDKFormatAdapter\";\nimport { useExternalHistory } from \"./useExternalHistory\";\nimport { useMemo } from \"react\";\n\nexport type AISDKRuntimeAdapter = {\n adapters?:\n | (NonNullable<ExternalStoreAdapter[\"adapters\"]> & {\n history?: ThreadHistoryAdapter | undefined;\n })\n | undefined;\n};\n\nexport const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>,\n adapter: AISDKRuntimeAdapter = {},\n) => {\n const messages = AISDKMessageConverter.useThreadMessages({\n isRunning:\n chatHelpers.status === \"submitted\" || chatHelpers.status == \"streaming\",\n messages: chatHelpers.messages,\n });\n\n const isLoading = useExternalHistory(\n useMemo(\n () => ({\n get current(): AssistantRuntime {\n return runtime;\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n ),\n adapter.adapters?.history,\n AISDKMessageConverter.toThreadMessages as (\n messages: UI_MESSAGE[],\n ) => ThreadMessage[],\n aiSDKV5FormatAdapter as MessageFormatAdapter<\n UI_MESSAGE,\n AISDKStorageFormat\n >,\n (messages) => {\n chatHelpers.setMessages(messages);\n },\n );\n\n const runtime = useExternalStoreRuntime({\n isRunning:\n chatHelpers.status === \"submitted\" || chatHelpers.status === \"streaming\",\n messages,\n setMessages: (messages) =>\n chatHelpers.setMessages(\n messages.map(getVercelAIMessages<UI_MESSAGE>).flat(),\n ),\n onCancel: async () => chatHelpers.stop(),\n onNew: async (message) => {\n const createMessage = await toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onEdit: async (message) => {\n const newMessages = sliceMessagesUntil(\n chatHelpers.messages,\n message.parentId,\n );\n chatHelpers.setMessages(newMessages);\n\n const createMessage = await toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onReload: async (parentId: string | null, config) => {\n const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.regenerate({ metadata: config.runConfig });\n },\n onAddToolResult: ({ toolCallId, result }) => {\n chatHelpers.addToolResult({\n tool: toolCallId,\n toolCallId,\n output: result,\n });\n },\n adapters: {\n attachments: vercelAttachmentAdapter,\n ...adapter.adapters,\n },\n isLoading,\n });\n\n return runtime;\n};\n"],"mappings":";;;AAGA;AAAA,EACE;AAAA,OAMK;AACP,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AACtC;AAAA,EAEE;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,eAAe;AAUjB,IAAM,kBAAkB,CAC7B,aACA,UAA+B,CAAC,MAC7B;AACH,QAAM,WAAW,sBAAsB,kBAAkB;AAAA,IACvD,WACE,YAAY,WAAW,eAAe,YAAY,UAAU;AAAA,IAC9D,UAAU,YAAY;AAAA,EACxB,CAAC;AAED,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,OAAO;AAAA,QACL,IAAI,UAA4B;AAC9B,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA,MAEA,CAAC;AAAA,IACH;AAAA,IACA,QAAQ,UAAU;AAAA,IAClB,sBAAsB;AAAA,IAGtB;AAAA,IAIA,CAACA,cAAa;AACZ,kBAAY,YAAYA,SAAQ;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,UAAU,wBAAwB;AAAA,IACtC,WACE,YAAY,WAAW,eAAe,YAAY,WAAW;AAAA,IAC/D;AAAA,IACA,aAAa,CAACA,cACZ,YAAY;AAAA,MACVA,UAAS,IAAI,mBAA+B,EAAE,KAAK;AAAA,IACrD;AAAA,IACF,UAAU,YAAY,YAAY,KAAK;AAAA,IACvC,OAAO,OAAO,YAAY;AACxB,YAAM,gBAAgB,MAAM,gBAA4B,OAAO;AAC/D,YAAM,YAAY,YAAY,eAAe;AAAA,QAC3C,UAAU,QAAQ;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,IACA,QAAQ,OAAO,YAAY;AACzB,YAAM,cAAc;AAAA,QAClB,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV;AACA,kBAAY,YAAY,WAAW;AAEnC,YAAM,gBAAgB,MAAM,gBAA4B,OAAO;AAC/D,YAAM,YAAY,YAAY,eAAe;AAAA,QAC3C,UAAU,QAAQ;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,IACA,UAAU,OAAO,UAAyB,WAAW;AACnD,YAAM,cAAc,mBAAmB,YAAY,UAAU,QAAQ;AACrE,kBAAY,YAAY,WAAW;AAEnC,YAAM,YAAY,WAAW,EAAE,UAAU,OAAO,UAAU,CAAC;AAAA,IAC7D;AAAA,IACA,iBAAiB,CAAC,EAAE,YAAY,OAAO,MAAM;AAC3C,kBAAY,cAAc;AAAA,QACxB,MAAM;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,IACA,UAAU;AAAA,MACR,aAAa;AAAA,MACb,GAAG,QAAQ;AAAA,IACb;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":["messages"]}
1
+ {"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage, useChat } from \"@ai-sdk/react\";\nimport {\n useExternalStoreRuntime,\n ExternalStoreAdapter,\n ThreadHistoryAdapter,\n AssistantRuntime,\n ThreadMessage,\n MessageFormatAdapter,\n} from \"@assistant-ui/react\";\nimport { sliceMessagesUntil } from \"../utils/sliceMessagesUntil\";\nimport { toCreateMessage } from \"../utils/toCreateMessage\";\nimport { vercelAttachmentAdapter } from \"../utils/vercelAttachmentAdapter\";\nimport { getVercelAIMessages } from \"../getVercelAIMessages\";\nimport { AISDKMessageConverter } from \"../utils/convertMessage\";\nimport {\n AISDKStorageFormat,\n aiSDKV5FormatAdapter,\n} from \"../adapters/aiSDKFormatAdapter\";\nimport { useExternalHistory } from \"./useExternalHistory\";\nimport { useMemo } from \"react\";\n\nexport type AISDKRuntimeAdapter = {\n adapters?:\n | (NonNullable<ExternalStoreAdapter[\"adapters\"]> & {\n history?: ThreadHistoryAdapter | undefined;\n })\n | undefined;\n};\n\nexport const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n chatHelpers: ReturnType<typeof useChat<UI_MESSAGE>>,\n adapter: AISDKRuntimeAdapter = {},\n) => {\n const messages = AISDKMessageConverter.useThreadMessages({\n isRunning:\n chatHelpers.status === \"submitted\" || chatHelpers.status == \"streaming\",\n messages: chatHelpers.messages,\n });\n\n const isLoading = useExternalHistory(\n useMemo(\n () => ({\n get current(): AssistantRuntime {\n return runtime;\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n ),\n adapter.adapters?.history,\n AISDKMessageConverter.toThreadMessages as (\n messages: UI_MESSAGE[],\n ) => ThreadMessage[],\n aiSDKV5FormatAdapter as MessageFormatAdapter<\n UI_MESSAGE,\n AISDKStorageFormat\n >,\n (messages) => {\n chatHelpers.setMessages(messages);\n },\n );\n\n const runtime = useExternalStoreRuntime({\n isRunning:\n chatHelpers.status === \"submitted\" || chatHelpers.status === \"streaming\",\n messages,\n setMessages: (messages) =>\n chatHelpers.setMessages(\n messages.map(getVercelAIMessages<UI_MESSAGE>).flat(),\n ),\n onCancel: async () => chatHelpers.stop(),\n onNew: async (message) => {\n const createMessage = toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onEdit: async (message) => {\n const newMessages = sliceMessagesUntil(\n chatHelpers.messages,\n message.parentId,\n );\n chatHelpers.setMessages(newMessages);\n\n const createMessage = toCreateMessage<UI_MESSAGE>(message);\n await chatHelpers.sendMessage(createMessage, {\n metadata: message.runConfig,\n });\n },\n onReload: async (parentId: string | null, config) => {\n const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.regenerate({ metadata: config.runConfig });\n },\n onAddToolResult: ({ toolCallId, result }) => {\n chatHelpers.addToolResult({\n tool: toolCallId,\n toolCallId,\n output: result,\n });\n },\n adapters: {\n attachments: vercelAttachmentAdapter,\n ...adapter.adapters,\n },\n isLoading,\n });\n\n return runtime;\n};\n"],"mappings":";;;AAGA;AAAA,EACE;AAAA,OAMK;AACP,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AACtC;AAAA,EAEE;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,eAAe;AAUjB,IAAM,kBAAkB,CAC7B,aACA,UAA+B,CAAC,MAC7B;AACH,QAAM,WAAW,sBAAsB,kBAAkB;AAAA,IACvD,WACE,YAAY,WAAW,eAAe,YAAY,UAAU;AAAA,IAC9D,UAAU,YAAY;AAAA,EACxB,CAAC;AAED,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,OAAO;AAAA,QACL,IAAI,UAA4B;AAC9B,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA,MAEA,CAAC;AAAA,IACH;AAAA,IACA,QAAQ,UAAU;AAAA,IAClB,sBAAsB;AAAA,IAGtB;AAAA,IAIA,CAACA,cAAa;AACZ,kBAAY,YAAYA,SAAQ;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,UAAU,wBAAwB;AAAA,IACtC,WACE,YAAY,WAAW,eAAe,YAAY,WAAW;AAAA,IAC/D;AAAA,IACA,aAAa,CAACA,cACZ,YAAY;AAAA,MACVA,UAAS,IAAI,mBAA+B,EAAE,KAAK;AAAA,IACrD;AAAA,IACF,UAAU,YAAY,YAAY,KAAK;AAAA,IACvC,OAAO,OAAO,YAAY;AACxB,YAAM,gBAAgB,gBAA4B,OAAO;AACzD,YAAM,YAAY,YAAY,eAAe;AAAA,QAC3C,UAAU,QAAQ;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,IACA,QAAQ,OAAO,YAAY;AACzB,YAAM,cAAc;AAAA,QAClB,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV;AACA,kBAAY,YAAY,WAAW;AAEnC,YAAM,gBAAgB,gBAA4B,OAAO;AACzD,YAAM,YAAY,YAAY,eAAe;AAAA,QAC3C,UAAU,QAAQ;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,IACA,UAAU,OAAO,UAAyB,WAAW;AACnD,YAAM,cAAc,mBAAmB,YAAY,UAAU,QAAQ;AACrE,kBAAY,YAAY,WAAW;AAEnC,YAAM,YAAY,WAAW,EAAE,UAAU,OAAO,UAAU,CAAC;AAAA,IAC7D;AAAA,IACA,iBAAiB,CAAC,EAAE,YAAY,OAAO,MAAM;AAC3C,kBAAY,cAAc;AAAA,QACxB,MAAM;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,IACA,UAAU;AAAA,MACR,aAAa;AAAA,MACb,GAAG,QAAQ;AAAA,IACb;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":["messages"]}
@@ -64,7 +64,7 @@ var useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageF
64
64
  for (let i = 0; i < messages.length; i++) {
65
65
  const message = messages[i];
66
66
  if (message.status === void 0 || message.status.type === "complete" || message.status.type === "incomplete") {
67
- if (historyIds.current.has(message.id)) return;
67
+ if (historyIds.current.has(message.id)) continue;
68
68
  historyIds.current.add(message.id);
69
69
  const parentId = i > 0 ? messages[i - 1].id : null;
70
70
  await historyAdapter?.withFormat?.(storageFormatAdapter).append({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/use-chat/useExternalHistory.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n getExternalStoreMessages,\n MessageFormatRepository,\n ExportedMessageRepository,\n INTERNAL,\n} from \"@assistant-ui/react\";\nimport { useRef, useEffect, useState, RefObject } from \"react\";\n\nconst { MessageRepository } = INTERNAL;\n\nexport const toExportedMessageRepository = <TMessage,>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n return {\n headId: messages.headId!,\n messages: messages.messages.map((m) => {\n const message = toThreadMessages([m.message])[0]!;\n return {\n ...m,\n message,\n };\n }),\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 const [isLoading, setIsLoading] = useState(true);\n const historyIds = useRef(new Set<string>());\n\n const onSetMessagesRef = useRef<typeof onSetMessages>(() => onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n // Load messages from history adapter on mount\n useEffect(() => {\n if (!historyAdapter || loadedRef.current) return;\n\n const loadHistory = async () => {\n setIsLoading(true);\n try {\n const repo = await historyAdapter\n .withFormat?.(storageFormatAdapter)\n .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.map(getExternalStoreMessages<TMessage>).flat(),\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 if (!loadedRef.current) {\n loadedRef.current = true;\n loadHistory();\n }\n }, [historyAdapter, storageFormatAdapter, toThreadMessages, runtimeRef]);\n\n useEffect(() => {\n return runtimeRef.current.thread.subscribe(async () => {\n const { messages, isRunning } = runtimeRef.current.thread.getState();\n if (isRunning) return;\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]!;\n if (\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\"\n ) {\n if (historyIds.current.has(message.id)) return;\n historyIds.current.add(message.id);\n\n const parentId = i > 0 ? messages[i - 1]!.id : null;\n await historyAdapter?.withFormat?.(storageFormatAdapter).append({\n parentId,\n message: getExternalStoreMessages<TMessage>(message)[0]!,\n });\n }\n }\n });\n }, [historyAdapter, storageFormatAdapter, runtimeRef]);\n\n return isLoading;\n};\n"],"mappings":";;;AAEA;AAAA,EAKE;AAAA,EAGA;AAAA,OACK;AACP,SAAS,QAAQ,WAAW,gBAA2B;AAEvD,IAAM,EAAE,kBAAkB,IAAI;AAEvB,IAAM,8BAA8B,CACzC,kBACA,aAC8B;AAC9B,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,UAAU,SAAS,SAAS,IAAI,CAAC,MAAM;AACrC,YAAM,UAAU,iBAAiB,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;AAC/C,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAM,qBAAqB,CAChC,YACA,gBACA,kBACA,sBACA,kBACG;AACH,QAAM,YAAY,OAAO,KAAK;AAC9B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,aAAa,OAAO,oBAAI,IAAY,CAAC;AAE3C,QAAM,mBAAmB,OAA6B,MAAM,aAAa;AACzE,YAAU,MAAM;AACd,qBAAiB,UAAU;AAAA,EAC7B,CAAC;AAGD,YAAU,MAAM;AACd,QAAI,CAAC,kBAAkB,UAAU,QAAS;AAE1C,UAAM,cAAc,YAAY;AAC9B,mBAAa,IAAI;AACjB,UAAI;AACF,cAAM,OAAO,MAAM,eAChB,aAAa,oBAAoB,EACjC,KAAK;AACR,YAAI,QAAQ,KAAK,SAAS,SAAS,GAAG;AACpC,gBAAM,YAAY,4BAA4B,kBAAkB,IAAI;AACpE,qBAAW,QAAQ,OAAO,OAAO,SAAS;AAE1C,gBAAM,WAAW,IAAI,kBAAkB;AACvC,mBAAS,OAAO,SAAS;AACzB,gBAAM,WAAW,SAAS,YAAY;AAEtC,2BAAiB;AAAA,YACf,SAAS,IAAI,wBAAkC,EAAE,KAAK;AAAA,UACxD;AAEA,qBAAW,UAAU,IAAI;AAAA,YACvB,UAAU,SAAS,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE;AAAA,UAC5C;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,mCAAmC,KAAK;AAAA,MACxD,UAAE;AACA,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,SAAS;AACtB,gBAAU,UAAU;AACpB,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,gBAAgB,sBAAsB,kBAAkB,UAAU,CAAC;AAEvE,YAAU,MAAM;AACd,WAAO,WAAW,QAAQ,OAAO,UAAU,YAAY;AACrD,YAAM,EAAE,UAAU,UAAU,IAAI,WAAW,QAAQ,OAAO,SAAS;AACnE,UAAI,UAAW;AAEf,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,cAAM,UAAU,SAAS,CAAC;AAC1B,YACE,QAAQ,WAAW,UACnB,QAAQ,OAAO,SAAS,cACxB,QAAQ,OAAO,SAAS,cACxB;AACA,cAAI,WAAW,QAAQ,IAAI,QAAQ,EAAE,EAAG;AACxC,qBAAW,QAAQ,IAAI,QAAQ,EAAE;AAEjC,gBAAM,WAAW,IAAI,IAAI,SAAS,IAAI,CAAC,EAAG,KAAK;AAC/C,gBAAM,gBAAgB,aAAa,oBAAoB,EAAE,OAAO;AAAA,YAC9D;AAAA,YACA,SAAS,yBAAmC,OAAO,EAAE,CAAC;AAAA,UACxD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,gBAAgB,sBAAsB,UAAU,CAAC;AAErD,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/use-chat/useExternalHistory.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n AssistantRuntime,\n ThreadHistoryAdapter,\n ThreadMessage,\n MessageFormatAdapter,\n getExternalStoreMessages,\n MessageFormatRepository,\n ExportedMessageRepository,\n INTERNAL,\n} from \"@assistant-ui/react\";\nimport { useRef, useEffect, useState, RefObject } from \"react\";\n\nconst { MessageRepository } = INTERNAL;\n\nexport const toExportedMessageRepository = <TMessage,>(\n toThreadMessages: (messages: TMessage[]) => ThreadMessage[],\n messages: MessageFormatRepository<TMessage>,\n): ExportedMessageRepository => {\n return {\n headId: messages.headId!,\n messages: messages.messages.map((m) => {\n const message = toThreadMessages([m.message])[0]!;\n return {\n ...m,\n message,\n };\n }),\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 const [isLoading, setIsLoading] = useState(true);\n const historyIds = useRef(new Set<string>());\n\n const onSetMessagesRef = useRef<typeof onSetMessages>(() => onSetMessages);\n useEffect(() => {\n onSetMessagesRef.current = onSetMessages;\n });\n\n // Load messages from history adapter on mount\n useEffect(() => {\n if (!historyAdapter || loadedRef.current) return;\n\n const loadHistory = async () => {\n setIsLoading(true);\n try {\n const repo = await historyAdapter\n .withFormat?.(storageFormatAdapter)\n .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.map(getExternalStoreMessages<TMessage>).flat(),\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 if (!loadedRef.current) {\n loadedRef.current = true;\n loadHistory();\n }\n }, [historyAdapter, storageFormatAdapter, toThreadMessages, runtimeRef]);\n\n useEffect(() => {\n return runtimeRef.current.thread.subscribe(async () => {\n const { messages, isRunning } = runtimeRef.current.thread.getState();\n if (isRunning) return;\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]!;\n if (\n message.status === undefined ||\n message.status.type === \"complete\" ||\n message.status.type === \"incomplete\"\n ) {\n if (historyIds.current.has(message.id)) continue;\n historyIds.current.add(message.id);\n\n const parentId = i > 0 ? messages[i - 1]!.id : null;\n await historyAdapter?.withFormat?.(storageFormatAdapter).append({\n parentId,\n message: getExternalStoreMessages<TMessage>(message)[0]!,\n });\n }\n }\n });\n }, [historyAdapter, storageFormatAdapter, runtimeRef]);\n\n return isLoading;\n};\n"],"mappings":";;;AAEA;AAAA,EAKE;AAAA,EAGA;AAAA,OACK;AACP,SAAS,QAAQ,WAAW,gBAA2B;AAEvD,IAAM,EAAE,kBAAkB,IAAI;AAEvB,IAAM,8BAA8B,CACzC,kBACA,aAC8B;AAC9B,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,UAAU,SAAS,SAAS,IAAI,CAAC,MAAM;AACrC,YAAM,UAAU,iBAAiB,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;AAC/C,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAM,qBAAqB,CAChC,YACA,gBACA,kBACA,sBACA,kBACG;AACH,QAAM,YAAY,OAAO,KAAK;AAC9B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,aAAa,OAAO,oBAAI,IAAY,CAAC;AAE3C,QAAM,mBAAmB,OAA6B,MAAM,aAAa;AACzE,YAAU,MAAM;AACd,qBAAiB,UAAU;AAAA,EAC7B,CAAC;AAGD,YAAU,MAAM;AACd,QAAI,CAAC,kBAAkB,UAAU,QAAS;AAE1C,UAAM,cAAc,YAAY;AAC9B,mBAAa,IAAI;AACjB,UAAI;AACF,cAAM,OAAO,MAAM,eAChB,aAAa,oBAAoB,EACjC,KAAK;AACR,YAAI,QAAQ,KAAK,SAAS,SAAS,GAAG;AACpC,gBAAM,YAAY,4BAA4B,kBAAkB,IAAI;AACpE,qBAAW,QAAQ,OAAO,OAAO,SAAS;AAE1C,gBAAM,WAAW,IAAI,kBAAkB;AACvC,mBAAS,OAAO,SAAS;AACzB,gBAAM,WAAW,SAAS,YAAY;AAEtC,2BAAiB;AAAA,YACf,SAAS,IAAI,wBAAkC,EAAE,KAAK;AAAA,UACxD;AAEA,qBAAW,UAAU,IAAI;AAAA,YACvB,UAAU,SAAS,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE;AAAA,UAC5C;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,mCAAmC,KAAK;AAAA,MACxD,UAAE;AACA,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,SAAS;AACtB,gBAAU,UAAU;AACpB,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,gBAAgB,sBAAsB,kBAAkB,UAAU,CAAC;AAEvE,YAAU,MAAM;AACd,WAAO,WAAW,QAAQ,OAAO,UAAU,YAAY;AACrD,YAAM,EAAE,UAAU,UAAU,IAAI,WAAW,QAAQ,OAAO,SAAS;AACnE,UAAI,UAAW;AAEf,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,cAAM,UAAU,SAAS,CAAC;AAC1B,YACE,QAAQ,WAAW,UACnB,QAAQ,OAAO,SAAS,cACxB,QAAQ,OAAO,SAAS,cACxB;AACA,cAAI,WAAW,QAAQ,IAAI,QAAQ,EAAE,EAAG;AACxC,qBAAW,QAAQ,IAAI,QAAQ,EAAE;AAEjC,gBAAM,WAAW,IAAI,IAAI,SAAS,IAAI,CAAC,EAAG,KAAK;AAC/C,gBAAM,gBAAgB,aAAa,oBAAoB,EAAE,OAAO;AAAA,YAC9D;AAAA,YACA,SAAS,yBAAmC,OAAO,EAAE,CAAC;AAAA,UACxD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,gBAAgB,sBAAsB,UAAU,CAAC;AAErD,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,SAAS,EAAE,MAAM,IAAI,CAAC;AAwJ7C,eAAO,MAAM,qBAAqB;;;;;;;0HAhIhB,sDACF;yHAIM,sDACT;;;CA+KZ,CAAC"}
1
+ {"version":3,"file":"convertMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/convertMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,SAAS,EAAE,MAAM,IAAI,CAAC;AA8I7C,eAAO,MAAM,qBAAqB;;;;;;;0HAvHb,sDACF;yHAIM,sDACT;;;CAqLf,CAAC"}
@@ -7,7 +7,7 @@ var convertParts = (message) => {
7
7
  if (!message.parts || message.parts.length === 0) {
8
8
  return [];
9
9
  }
10
- return message.parts.filter((p) => p.type !== "step-start").map((part) => {
10
+ return message.parts.filter((p) => p.type !== "step-start" && p.type !== "file").map((part) => {
11
11
  const type = part.type;
12
12
  if (type === "text") {
13
13
  return {
@@ -88,13 +88,6 @@ var convertParts = (message) => {
88
88
  );
89
89
  return null;
90
90
  }
91
- if (type === "file") {
92
- return {
93
- type: "file",
94
- data: part.url,
95
- mimeType: part.mediaType
96
- };
97
- }
98
91
  if (type.startsWith("data-")) {
99
92
  console.warn(
100
93
  `Data part type ${type} is not yet supported in conversion`
@@ -115,14 +108,27 @@ var AISDKMessageConverter = unstable_createMessageConverter(
115
108
  id: message.id,
116
109
  createdAt,
117
110
  content: convertParts(message),
118
- attachments: message.parts?.filter((p) => p.type === "file").map((part, idx) => ({
119
- id: idx.toString(),
120
- type: "file",
121
- name: part.name ?? part.url ?? "file",
122
- content: [],
123
- contentType: part.mediaType ?? part.mimeType ?? "unknown/unknown",
124
- status: { type: "complete" }
125
- }))
111
+ attachments: message.parts?.filter((p) => p.type === "file").map((part, idx) => {
112
+ return {
113
+ id: idx.toString(),
114
+ type: part.mediaType.startsWith("image/") ? "image" : "file",
115
+ name: part.filename ?? "file",
116
+ content: [
117
+ part.mediaType.startsWith("image/") ? {
118
+ type: "image",
119
+ image: part.url,
120
+ filename: part.filename
121
+ } : {
122
+ type: "file",
123
+ filename: part.filename,
124
+ data: part.url,
125
+ mimeType: part.mediaType
126
+ }
127
+ ],
128
+ contentType: part.mediaType ?? "unknown/unknown",
129
+ status: { type: "complete" }
130
+ };
131
+ })
126
132
  };
127
133
  case "system":
128
134
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, UIMessage } from \"ai\";\nimport {\n unstable_createMessageConverter,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type SourceMessagePart,\n type FileMessagePart,\n} from \"@assistant-ui/react\";\n\nconst convertParts = (message: UIMessage) => {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n return message.parts\n .filter((p) => p.type !== \"step-start\")\n .map((part) => {\n const type = part.type;\n\n // Handle text parts\n if (type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n // Handle reasoning parts\n if (type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n // Handle tool-* parts (AI SDK v5 tool calls)\n if (isToolUIPart(part)) {\n const toolName = type.replace(\"tool-\", \"\");\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle dynamic-tool parts\n if (type === \"dynamic-tool\") {\n const toolName = part.toolName;\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle source-url parts\n if (type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n title: part.title || \"\",\n } satisfies SourceMessagePart;\n }\n\n // Handle source-document parts\n if (type === \"source-document\") {\n console.warn(\n `Source document part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // Handle file parts\n if (type === \"file\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType,\n } satisfies FileMessagePart;\n }\n\n // Handle data-* parts (AI SDK v5 data parts)\n if (type.startsWith(\"data-\")) {\n // For now, we'll skip data parts as they don't have a direct equivalent\n // in the assistant-ui types. They could be converted to a custom message part\n // or handled differently based on the specific use case.\n console.warn(\n `Data part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // For unsupported types, we'll skip them instead of throwing\n console.warn(`Unsupported message part type: ${type}`);\n return null;\n })\n .filter(Boolean) as any[];\n};\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage) => {\n // UIMessage doesn't have createdAt, so we'll use current date or undefined\n const createdAt = new Date();\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n attachments: message.parts\n ?.filter((p: any) => p.type === \"file\")\n .map((part: any, idx) => ({\n id: idx.toString(),\n type: \"file\" as const,\n name: part.name ?? part.url ?? \"file\",\n content: [],\n contentType: part.mediaType ?? part.mimeType ?? \"unknown/unknown\",\n status: { type: \"complete\" as const },\n })),\n };\n\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n };\n\n case \"assistant\":\n return {\n role: \"assistant\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n metadata: {\n unstable_annotations: (message as any).annotations,\n unstable_data: Array.isArray((message as any).data)\n ? (message as any).data\n : (message as any).data\n ? [(message as any).data]\n : undefined,\n custom: {},\n },\n };\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/utils/convertMessage.ts"],"sourcesContent":["import { isToolUIPart, UIMessage } from \"ai\";\nimport {\n unstable_createMessageConverter,\n type ReasoningMessagePart,\n type ToolCallMessagePart,\n type TextMessagePart,\n type SourceMessagePart,\n} from \"@assistant-ui/react\";\n\nconst convertParts = (message: UIMessage) => {\n if (!message.parts || message.parts.length === 0) {\n return [];\n }\n\n return message.parts\n .filter((p) => p.type !== \"step-start\" && p.type !== \"file\")\n .map((part) => {\n const type = part.type;\n\n // Handle text parts\n if (type === \"text\") {\n return {\n type: \"text\",\n text: part.text,\n } satisfies TextMessagePart;\n }\n\n // Handle reasoning parts\n if (type === \"reasoning\") {\n return {\n type: \"reasoning\",\n text: part.text,\n } satisfies ReasoningMessagePart;\n }\n\n // Handle tool-* parts (AI SDK v5 tool calls)\n if (isToolUIPart(part)) {\n const toolName = type.replace(\"tool-\", \"\");\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle dynamic-tool parts\n if (type === \"dynamic-tool\") {\n const toolName = part.toolName;\n const toolCallId = part.toolCallId;\n\n // Extract args and result based on state\n let args: any = {};\n let result: any = undefined;\n let isError = false;\n\n if (\n part.state === \"input-streaming\" ||\n part.state === \"input-available\"\n ) {\n args = part.input || {};\n } else if (part.state === \"output-available\") {\n args = part.input || {};\n result = part.output;\n } else if (part.state === \"output-error\") {\n args = part.input || {};\n isError = true;\n result = { error: part.errorText };\n }\n\n return {\n type: \"tool-call\",\n toolName,\n toolCallId,\n argsText: JSON.stringify(args),\n args,\n result,\n isError,\n } satisfies ToolCallMessagePart;\n }\n\n // Handle source-url parts\n if (type === \"source-url\") {\n return {\n type: \"source\",\n sourceType: \"url\",\n id: part.sourceId,\n url: part.url,\n title: part.title || \"\",\n } satisfies SourceMessagePart;\n }\n\n // Handle source-document parts\n if (type === \"source-document\") {\n console.warn(\n `Source document part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // Handle data-* parts (AI SDK v5 data parts)\n if (type.startsWith(\"data-\")) {\n // For now, we'll skip data parts as they don't have a direct equivalent\n // in the assistant-ui types. They could be converted to a custom message part\n // or handled differently based on the specific use case.\n console.warn(\n `Data part type ${type} is not yet supported in conversion`,\n );\n return null;\n }\n\n // For unsupported types, we'll skip them instead of throwing\n console.warn(`Unsupported message part type: ${type}`);\n return null;\n })\n .filter(Boolean) as any[];\n};\n\nexport const AISDKMessageConverter = unstable_createMessageConverter(\n (message: UIMessage) => {\n // UIMessage doesn't have createdAt, so we'll use current date or undefined\n const createdAt = new Date();\n switch (message.role) {\n case \"user\":\n return {\n role: \"user\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n attachments: message.parts\n ?.filter((p) => p.type === \"file\")\n .map((part, idx) => {\n return {\n id: idx.toString(),\n type: part.mediaType.startsWith(\"image/\") ? \"image\" : \"file\",\n name: part.filename ?? \"file\",\n content: [\n part.mediaType.startsWith(\"image/\")\n ? {\n type: \"image\",\n image: part.url,\n filename: part.filename!,\n }\n : {\n type: \"file\",\n filename: part.filename!,\n data: part.url,\n mimeType: part.mediaType,\n },\n ],\n contentType: part.mediaType ?? \"unknown/unknown\",\n status: { type: \"complete\" as const },\n };\n }),\n };\n\n case \"system\":\n return {\n role: \"system\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n };\n\n case \"assistant\":\n return {\n role: \"assistant\",\n id: message.id,\n createdAt,\n content: convertParts(message),\n metadata: {\n unstable_annotations: (message as any).annotations,\n unstable_data: Array.isArray((message as any).data)\n ? (message as any).data\n : (message as any).data\n ? [(message as any).data]\n : undefined,\n custom: {},\n },\n };\n\n default:\n console.warn(`Unsupported message role: ${message.role}`);\n return [];\n }\n },\n);\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
1
  import { AppendMessage } from "@assistant-ui/react";
2
2
  import { CreateUIMessage, UIMessage } from "ai";
3
- export declare const toCreateMessage: <UI_MESSAGE extends UIMessage = UIMessage>(message: AppendMessage) => Promise<CreateUIMessage<UI_MESSAGE>>;
3
+ export declare const toCreateMessage: <UI_MESSAGE extends UIMessage = UIMessage>(message: AppendMessage) => CreateUIMessage<UI_MESSAGE>;
4
4
  //# sourceMappingURL=toCreateMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toCreateMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/toCreateMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,eAAe,EAIf,SAAS,EAGV,MAAM,IAAI,CAAC;AAEZ,eAAO,MAAM,eAAe,GAAU,UAAU,SAAS,SAAS,GAAG,SAAS,EAC5E,SAAS,aAAa,KACrB,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC,CA+CrC,CAAC"}
1
+ {"version":3,"file":"toCreateMessage.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/toCreateMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,eAAe,EAGf,SAAS,EAGV,MAAM,IAAI,CAAC;AAEZ,eAAO,MAAM,eAAe,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EACtE,SAAS,aAAa,KACrB,eAAe,CAAC,UAAU,CA0C5B,CAAC"}
@@ -2,47 +2,47 @@
2
2
  import {
3
3
  generateId
4
4
  } from "ai";
5
- var toCreateMessage = async (message) => {
6
- const textParts = message.content.filter((part) => part.type === "text").map((t) => t.text).join("\n\n");
7
- const parts = [
8
- {
9
- type: "text",
10
- text: textParts
11
- }
5
+ var toCreateMessage = (message) => {
6
+ const inputParts = [
7
+ ...message.content.filter((c) => c.type !== "file"),
8
+ ...message.attachments?.flatMap(
9
+ (a) => a.content.map((c) => ({
10
+ ...c,
11
+ filename: a.name
12
+ }))
13
+ ) ?? []
12
14
  ];
13
- const imageParts = message.content.filter((part) => part.type === "image").map(
14
- (part) => ({
15
- type: "file",
16
- mediaType: "image/png",
17
- // Default to PNG, could be made more dynamic
18
- url: part.image
19
- })
20
- );
21
- parts.push(...imageParts);
22
- const attachmentParts = await Promise.all(
23
- (message.attachments ?? []).map(async (m) => {
24
- if (m.file == null) throw new Error("Attachment did not contain a file");
25
- return {
26
- type: "file",
27
- mediaType: m.file.type,
28
- filename: m.file.name,
29
- url: await getFileDataURL(m.file)
30
- };
31
- })
32
- );
33
- parts.push(...attachmentParts);
15
+ const parts = inputParts.map((part) => {
16
+ switch (part.type) {
17
+ case "text":
18
+ return {
19
+ type: "text",
20
+ text: part.text
21
+ };
22
+ case "image":
23
+ return {
24
+ type: "file",
25
+ url: part.image,
26
+ ...part.filename && { filename: part.filename },
27
+ mediaType: "image/png"
28
+ };
29
+ case "file":
30
+ return {
31
+ type: "file",
32
+ url: part.data,
33
+ mediaType: part.mimeType,
34
+ ...part.filename && { filename: part.filename }
35
+ };
36
+ default:
37
+ throw new Error(`Unsupported part type: ${part.type}`);
38
+ }
39
+ });
34
40
  return {
35
41
  id: generateId(),
36
42
  role: message.role,
37
43
  parts
38
44
  };
39
45
  };
40
- var getFileDataURL = (file) => new Promise((resolve, reject) => {
41
- const reader = new FileReader();
42
- reader.onload = () => resolve(reader.result);
43
- reader.onerror = (error) => reject(error);
44
- reader.readAsDataURL(file);
45
- });
46
46
  export {
47
47
  toCreateMessage
48
48
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/utils/toCreateMessage.ts"],"sourcesContent":["import { AppendMessage } from \"@assistant-ui/react\";\nimport {\n CreateUIMessage,\n FileUIPart,\n generateId,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\nexport const toCreateMessage = async <UI_MESSAGE extends UIMessage = UIMessage>(\n message: AppendMessage,\n): Promise<CreateUIMessage<UI_MESSAGE>> => {\n const textParts = message.content\n .filter((part) => part.type === \"text\")\n .map((t) => t.text)\n .join(\"\\n\\n\");\n\n const parts: UIMessagePart<UIDataTypes, UITools>[] = [\n {\n type: \"text\",\n text: textParts,\n },\n ];\n\n // Add image parts\n const imageParts = message.content\n .filter((part) => part.type === \"image\")\n .map(\n (part) =>\n ({\n type: \"file\",\n mediaType: \"image/png\", // Default to PNG, could be made more dynamic\n url: part.image,\n }) satisfies FileUIPart,\n );\n\n parts.push(...imageParts);\n\n // Add attachment parts\n const attachmentParts = await Promise.all(\n (message.attachments ?? []).map(async (m) => {\n if (m.file == null) throw new Error(\"Attachment did not contain a file\");\n return {\n type: \"file\",\n mediaType: m.file.type,\n filename: m.file.name,\n url: await getFileDataURL(m.file),\n } satisfies FileUIPart;\n }),\n );\n\n parts.push(...attachmentParts);\n\n return {\n id: generateId(),\n role: message.role,\n parts,\n } satisfies CreateUIMessage<UIMessage> as CreateUIMessage<UI_MESSAGE>;\n};\n\nconst getFileDataURL = (file: File) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = (error) => reject(error);\n\n reader.readAsDataURL(file);\n });\n"],"mappings":";AACA;AAAA,EAGE;AAAA,OAKK;AAEA,IAAM,kBAAkB,OAC7B,YACyC;AACzC,QAAM,YAAY,QAAQ,QACvB,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,EACrC,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,MAAM;AAEd,QAAM,QAA+C;AAAA,IACnD;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AAGA,QAAM,aAAa,QAAQ,QACxB,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,EACtC;AAAA,IACC,CAAC,UACE;AAAA,MACC,MAAM;AAAA,MACN,WAAW;AAAA;AAAA,MACX,KAAK,KAAK;AAAA,IACZ;AAAA,EACJ;AAEF,QAAM,KAAK,GAAG,UAAU;AAGxB,QAAM,kBAAkB,MAAM,QAAQ;AAAA,KACnC,QAAQ,eAAe,CAAC,GAAG,IAAI,OAAO,MAAM;AAC3C,UAAI,EAAE,QAAQ,KAAM,OAAM,IAAI,MAAM,mCAAmC;AACvE,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAW,EAAE,KAAK;AAAA,QAClB,UAAU,EAAE,KAAK;AAAA,QACjB,KAAK,MAAM,eAAe,EAAE,IAAI;AAAA,MAClC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,KAAK,GAAG,eAAe;AAE7B,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,MAAM,QAAQ;AAAA,IACd;AAAA,EACF;AACF;AAEA,IAAM,iBAAiB,CAAC,SACtB,IAAI,QAAgB,CAAC,SAAS,WAAW;AACvC,QAAM,SAAS,IAAI,WAAW;AAE9B,SAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,SAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AAExC,SAAO,cAAc,IAAI;AAC3B,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/utils/toCreateMessage.ts"],"sourcesContent":["import { AppendMessage } from \"@assistant-ui/react\";\nimport {\n CreateUIMessage,\n generateId,\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 default:\n throw new Error(`Unsupported part type: ${part.type}`);\n }\n });\n\n return {\n id: generateId(),\n role: message.role,\n parts,\n } satisfies CreateUIMessage<UIMessage> as CreateUIMessage<UI_MESSAGE>;\n};\n"],"mappings":";AACA;AAAA,EAEE;AAAA,OAKK;AAEA,IAAM,kBAAkB,CAC7B,YACgC;AAChC,QAAM,aAAa;AAAA,IACjB,GAAG,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM;AAAA,IAClD,GAAI,QAAQ,aAAa;AAAA,MAAQ,CAAC,MAChC,EAAE,QAAQ,IAAI,CAAC,OAAO;AAAA,QACpB,GAAG;AAAA,QACH,UAAU,EAAE;AAAA,MACd,EAAE;AAAA,IACJ,KAAK,CAAC;AAAA,EACR;AAEA,QAAM,QAAQ,WAAW,IAAI,CAAC,SAA8C;AAC1E,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,eAAO;AAAA,UACL,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,QACb;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,MAAM;AAAA,UACN,KAAK,KAAK;AAAA,UACV,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;AAAA,UAC/C,WAAW;AAAA,QACb;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,MAAM;AAAA,UACN,KAAK,KAAK;AAAA,UACV,WAAW,KAAK;AAAA,UAChB,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;AAAA,QACjD;AAAA,MACF;AACE,cAAM,IAAI,MAAM,0BAA0B,KAAK,IAAI,EAAE;AAAA,IACzD;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,MAAM,QAAQ;AAAA,IACd;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"vercelAttachmentAdapter.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,eAAO,MAAM,uBAAuB,EAAE,iBAyBrC,CAAC"}
1
+ {"version":3,"file":"vercelAttachmentAdapter.d.ts","sourceRoot":"","sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAaxD,eAAO,MAAM,uBAAuB,EAAE,iBAgCrC,CAAC"}
@@ -1,11 +1,17 @@
1
1
  // src/ui/utils/vercelAttachmentAdapter.ts
2
2
  import { generateId } from "ai";
3
+ var getFileDataURL = (file) => new Promise((resolve, reject) => {
4
+ const reader = new FileReader();
5
+ reader.onload = () => resolve(reader.result);
6
+ reader.onerror = (error) => reject(error);
7
+ reader.readAsDataURL(file);
8
+ });
3
9
  var vercelAttachmentAdapter = {
4
10
  accept: "image/*, text/plain, text/html, text/markdown, text/csv, text/xml, text/json, text/css",
5
11
  async add({ file }) {
6
12
  return {
7
13
  id: generateId(),
8
- type: "file",
14
+ type: file.type.startsWith("image/") ? "image" : "file",
9
15
  name: file.name,
10
16
  file,
11
17
  contentType: file.type,
@@ -17,7 +23,14 @@ var vercelAttachmentAdapter = {
17
23
  return {
18
24
  ...attachment,
19
25
  status: { type: "complete" },
20
- content: []
26
+ content: [
27
+ {
28
+ type: "file",
29
+ mimeType: attachment.contentType,
30
+ filename: attachment.name,
31
+ data: await getFileDataURL(attachment.file)
32
+ }
33
+ ]
21
34
  };
22
35
  },
23
36
  async remove() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"sourcesContent":["import { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { generateId } from \"ai\";\n\nexport const vercelAttachmentAdapter: AttachmentAdapter = {\n accept:\n \"image/*, text/plain, text/html, text/markdown, text/csv, text/xml, text/json, text/css\",\n async add({ file }) {\n return {\n id: generateId(),\n type: \"file\",\n name: file.name,\n file,\n contentType: file.type,\n content: [],\n status: { type: \"requires-action\", reason: \"composer-send\" },\n };\n },\n async send(attachment) {\n // noop\n return {\n ...attachment,\n status: { type: \"complete\" },\n content: [],\n };\n },\n async remove() {\n // noop\n },\n};\n"],"mappings":";AACA,SAAS,kBAAkB;AAEpB,IAAM,0BAA6C;AAAA,EACxD,QACE;AAAA,EACF,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,WAAO;AAAA,MACL,IAAI,WAAW;AAAA,MACf,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX;AAAA,MACA,aAAa,KAAK;AAAA,MAClB,SAAS,CAAC;AAAA,MACV,QAAQ,EAAE,MAAM,mBAAmB,QAAQ,gBAAgB;AAAA,IAC7D;AAAA,EACF;AAAA,EACA,MAAM,KAAK,YAAY;AAErB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,EAAE,MAAM,WAAW;AAAA,MAC3B,SAAS,CAAC;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,SAAS;AAAA,EAEf;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/utils/vercelAttachmentAdapter.ts"],"sourcesContent":["import { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { generateId } from \"ai\";\n\nconst getFileDataURL = (file: File) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = (error) => reject(error);\n\n reader.readAsDataURL(file);\n });\n\nexport const vercelAttachmentAdapter: AttachmentAdapter = {\n accept:\n \"image/*, text/plain, text/html, text/markdown, text/csv, text/xml, text/json, text/css\",\n async add({ file }) {\n return {\n id: generateId(),\n type: file.type.startsWith(\"image/\") ? \"image\" : \"file\",\n name: file.name,\n file,\n contentType: file.type,\n content: [],\n status: { type: \"requires-action\", reason: \"composer-send\" },\n };\n },\n async send(attachment) {\n // noop\n return {\n ...attachment,\n status: { type: \"complete\" },\n content: [\n {\n type: \"file\",\n mimeType: attachment.contentType,\n filename: attachment.name,\n data: await getFileDataURL(attachment.file),\n },\n ],\n };\n },\n async remove() {\n // noop\n },\n};\n"],"mappings":";AACA,SAAS,kBAAkB;AAE3B,IAAM,iBAAiB,CAAC,SACtB,IAAI,QAAgB,CAAC,SAAS,WAAW;AACvC,QAAM,SAAS,IAAI,WAAW;AAE9B,SAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,SAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AAExC,SAAO,cAAc,IAAI;AAC3B,CAAC;AAEI,IAAM,0BAA6C;AAAA,EACxD,QACE;AAAA,EACF,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,WAAO;AAAA,MACL,IAAI,WAAW;AAAA,MACf,MAAM,KAAK,KAAK,WAAW,QAAQ,IAAI,UAAU;AAAA,MACjD,MAAM,KAAK;AAAA,MACX;AAAA,MACA,aAAa,KAAK;AAAA,MAClB,SAAS,CAAC;AAAA,MACV,QAAQ,EAAE,MAAM,mBAAmB,QAAQ,gBAAgB;AAAA,IAC7D;AAAA,EACF;AAAA,EACA,MAAM,KAAK,YAAY;AAErB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,EAAE,MAAM,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,UAAU,WAAW;AAAA,UACrB,UAAU,WAAW;AAAA,UACrB,MAAM,MAAM,eAAe,WAAW,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,SAAS;AAAA,EAEf;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-ai-sdk",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -49,8 +49,8 @@
49
49
  "eslint-config-next": "15.4.6",
50
50
  "react": "19.1.1",
51
51
  "tsx": "^4.20.4",
52
- "@assistant-ui/x-buildutils": "0.0.1",
53
- "@assistant-ui/react": "0.10.43"
52
+ "@assistant-ui/react": "0.10.43",
53
+ "@assistant-ui/x-buildutils": "0.0.1"
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public",
@@ -72,7 +72,7 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
72
72
  ),
73
73
  onCancel: async () => chatHelpers.stop(),
74
74
  onNew: async (message) => {
75
- const createMessage = await toCreateMessage<UI_MESSAGE>(message);
75
+ const createMessage = toCreateMessage<UI_MESSAGE>(message);
76
76
  await chatHelpers.sendMessage(createMessage, {
77
77
  metadata: message.runConfig,
78
78
  });
@@ -84,7 +84,7 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
84
84
  );
85
85
  chatHelpers.setMessages(newMessages);
86
86
 
87
- const createMessage = await toCreateMessage<UI_MESSAGE>(message);
87
+ const createMessage = toCreateMessage<UI_MESSAGE>(message);
88
88
  await chatHelpers.sendMessage(createMessage, {
89
89
  metadata: message.runConfig,
90
90
  });
@@ -97,7 +97,7 @@ export const useExternalHistory = <TMessage,>(
97
97
  message.status.type === "complete" ||
98
98
  message.status.type === "incomplete"
99
99
  ) {
100
- if (historyIds.current.has(message.id)) return;
100
+ if (historyIds.current.has(message.id)) continue;
101
101
  historyIds.current.add(message.id);
102
102
 
103
103
  const parentId = i > 0 ? messages[i - 1]!.id : null;
@@ -5,7 +5,6 @@ import {
5
5
  type ToolCallMessagePart,
6
6
  type TextMessagePart,
7
7
  type SourceMessagePart,
8
- type FileMessagePart,
9
8
  } from "@assistant-ui/react";
10
9
 
11
10
  const convertParts = (message: UIMessage) => {
@@ -14,7 +13,7 @@ const convertParts = (message: UIMessage) => {
14
13
  }
15
14
 
16
15
  return message.parts
17
- .filter((p) => p.type !== "step-start")
16
+ .filter((p) => p.type !== "step-start" && p.type !== "file")
18
17
  .map((part) => {
19
18
  const type = part.type;
20
19
 
@@ -123,15 +122,6 @@ const convertParts = (message: UIMessage) => {
123
122
  return null;
124
123
  }
125
124
 
126
- // Handle file parts
127
- if (type === "file") {
128
- return {
129
- type: "file",
130
- data: part.url,
131
- mimeType: part.mediaType,
132
- } satisfies FileMessagePart;
133
- }
134
-
135
125
  // Handle data-* parts (AI SDK v5 data parts)
136
126
  if (type.startsWith("data-")) {
137
127
  // For now, we'll skip data parts as they don't have a direct equivalent
@@ -162,15 +152,30 @@ export const AISDKMessageConverter = unstable_createMessageConverter(
162
152
  createdAt,
163
153
  content: convertParts(message),
164
154
  attachments: message.parts
165
- ?.filter((p: any) => p.type === "file")
166
- .map((part: any, idx) => ({
167
- id: idx.toString(),
168
- type: "file" as const,
169
- name: part.name ?? part.url ?? "file",
170
- content: [],
171
- contentType: part.mediaType ?? part.mimeType ?? "unknown/unknown",
172
- status: { type: "complete" as const },
173
- })),
155
+ ?.filter((p) => p.type === "file")
156
+ .map((part, idx) => {
157
+ return {
158
+ id: idx.toString(),
159
+ type: part.mediaType.startsWith("image/") ? "image" : "file",
160
+ name: part.filename ?? "file",
161
+ content: [
162
+ part.mediaType.startsWith("image/")
163
+ ? {
164
+ type: "image",
165
+ image: part.url,
166
+ filename: part.filename!,
167
+ }
168
+ : {
169
+ type: "file",
170
+ filename: part.filename!,
171
+ data: part.url,
172
+ mimeType: part.mediaType,
173
+ },
174
+ ],
175
+ contentType: part.mediaType ?? "unknown/unknown",
176
+ status: { type: "complete" as const },
177
+ };
178
+ }),
174
179
  };
175
180
 
176
181
  case "system":
@@ -1,7 +1,6 @@
1
1
  import { AppendMessage } from "@assistant-ui/react";
2
2
  import {
3
3
  CreateUIMessage,
4
- FileUIPart,
5
4
  generateId,
6
5
  UIDataTypes,
7
6
  UIMessage,
@@ -9,49 +8,44 @@ import {
9
8
  UITools,
10
9
  } from "ai";
11
10
 
12
- export const toCreateMessage = async <UI_MESSAGE extends UIMessage = UIMessage>(
11
+ export const toCreateMessage = <UI_MESSAGE extends UIMessage = UIMessage>(
13
12
  message: AppendMessage,
14
- ): Promise<CreateUIMessage<UI_MESSAGE>> => {
15
- const textParts = message.content
16
- .filter((part) => part.type === "text")
17
- .map((t) => t.text)
18
- .join("\n\n");
19
-
20
- const parts: UIMessagePart<UIDataTypes, UITools>[] = [
21
- {
22
- type: "text",
23
- text: textParts,
24
- },
13
+ ): CreateUIMessage<UI_MESSAGE> => {
14
+ const inputParts = [
15
+ ...message.content.filter((c) => c.type !== "file"),
16
+ ...(message.attachments?.flatMap((a) =>
17
+ a.content.map((c) => ({
18
+ ...c,
19
+ filename: a.name,
20
+ })),
21
+ ) ?? []),
25
22
  ];
26
23
 
27
- // Add image parts
28
- const imageParts = message.content
29
- .filter((part) => part.type === "image")
30
- .map(
31
- (part) =>
32
- ({
24
+ const parts = inputParts.map((part): UIMessagePart<UIDataTypes, UITools> => {
25
+ switch (part.type) {
26
+ case "text":
27
+ return {
28
+ type: "text",
29
+ text: part.text,
30
+ };
31
+ case "image":
32
+ return {
33
33
  type: "file",
34
- mediaType: "image/png", // Default to PNG, could be made more dynamic
35
34
  url: part.image,
36
- }) satisfies FileUIPart,
37
- );
38
-
39
- parts.push(...imageParts);
40
-
41
- // Add attachment parts
42
- const attachmentParts = await Promise.all(
43
- (message.attachments ?? []).map(async (m) => {
44
- if (m.file == null) throw new Error("Attachment did not contain a file");
45
- return {
46
- type: "file",
47
- mediaType: m.file.type,
48
- filename: m.file.name,
49
- url: await getFileDataURL(m.file),
50
- } satisfies FileUIPart;
51
- }),
52
- );
53
-
54
- parts.push(...attachmentParts);
35
+ ...(part.filename && { filename: part.filename }),
36
+ mediaType: "image/png",
37
+ };
38
+ case "file":
39
+ return {
40
+ type: "file",
41
+ url: part.data,
42
+ mediaType: part.mimeType,
43
+ ...(part.filename && { filename: part.filename }),
44
+ };
45
+ default:
46
+ throw new Error(`Unsupported part type: ${part.type}`);
47
+ }
48
+ });
55
49
 
56
50
  return {
57
51
  id: generateId(),
@@ -59,13 +53,3 @@ export const toCreateMessage = async <UI_MESSAGE extends UIMessage = UIMessage>(
59
53
  parts,
60
54
  } satisfies CreateUIMessage<UIMessage> as CreateUIMessage<UI_MESSAGE>;
61
55
  };
62
-
63
- const getFileDataURL = (file: File) =>
64
- new Promise<string>((resolve, reject) => {
65
- const reader = new FileReader();
66
-
67
- reader.onload = () => resolve(reader.result as string);
68
- reader.onerror = (error) => reject(error);
69
-
70
- reader.readAsDataURL(file);
71
- });
@@ -1,13 +1,23 @@
1
1
  import { AttachmentAdapter } from "@assistant-ui/react";
2
2
  import { generateId } from "ai";
3
3
 
4
+ const getFileDataURL = (file: File) =>
5
+ new Promise<string>((resolve, reject) => {
6
+ const reader = new FileReader();
7
+
8
+ reader.onload = () => resolve(reader.result as string);
9
+ reader.onerror = (error) => reject(error);
10
+
11
+ reader.readAsDataURL(file);
12
+ });
13
+
4
14
  export const vercelAttachmentAdapter: AttachmentAdapter = {
5
15
  accept:
6
16
  "image/*, text/plain, text/html, text/markdown, text/csv, text/xml, text/json, text/css",
7
17
  async add({ file }) {
8
18
  return {
9
19
  id: generateId(),
10
- type: "file",
20
+ type: file.type.startsWith("image/") ? "image" : "file",
11
21
  name: file.name,
12
22
  file,
13
23
  contentType: file.type,
@@ -20,7 +30,14 @@ export const vercelAttachmentAdapter: AttachmentAdapter = {
20
30
  return {
21
31
  ...attachment,
22
32
  status: { type: "complete" },
23
- content: [],
33
+ content: [
34
+ {
35
+ type: "file",
36
+ mimeType: attachment.contentType,
37
+ filename: attachment.name,
38
+ data: await getFileDataURL(attachment.file),
39
+ },
40
+ ],
24
41
  };
25
42
  },
26
43
  async remove() {