@assistant-ui/react-ai-sdk 0.11.1 → 0.11.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.
Files changed (36) hide show
  1. package/README.md +55 -0
  2. package/dist/ui/adapters/aiSDKFormatAdapter.d.ts +5 -0
  3. package/dist/ui/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  4. package/dist/ui/adapters/aiSDKFormatAdapter.js +28 -0
  5. package/dist/ui/adapters/aiSDKFormatAdapter.js.map +1 -0
  6. package/dist/ui/index.d.ts +2 -0
  7. package/dist/ui/index.d.ts.map +1 -1
  8. package/dist/ui/index.js +5 -1
  9. package/dist/ui/index.js.map +1 -1
  10. package/dist/ui/use-chat/AssistantChatTransport.d.ts +8 -0
  11. package/dist/ui/use-chat/AssistantChatTransport.d.ts.map +1 -0
  12. package/dist/ui/use-chat/AssistantChatTransport.js +60 -0
  13. package/dist/ui/use-chat/AssistantChatTransport.js.map +1 -0
  14. package/dist/ui/use-chat/useAISDKRuntime.d.ts +5 -3
  15. package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
  16. package/dist/ui/use-chat/useAISDKRuntime.js +24 -4
  17. package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
  18. package/dist/ui/use-chat/useChatRuntime.d.ts +12 -0
  19. package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -0
  20. package/dist/ui/use-chat/useChatRuntime.js +51 -0
  21. package/dist/ui/use-chat/useChatRuntime.js.map +1 -0
  22. package/dist/ui/use-chat/useExternalHistory.d.ts +5 -0
  23. package/dist/ui/use-chat/useExternalHistory.d.ts.map +1 -0
  24. package/dist/ui/use-chat/useExternalHistory.js +84 -0
  25. package/dist/ui/use-chat/useExternalHistory.js.map +1 -0
  26. package/dist/ui/utils/toCreateMessage.d.ts.map +1 -1
  27. package/dist/ui/utils/toCreateMessage.js +4 -0
  28. package/dist/ui/utils/toCreateMessage.js.map +1 -1
  29. package/package.json +8 -4
  30. package/src/ui/adapters/aiSDKFormatAdapter.ts +42 -0
  31. package/src/ui/index.ts +2 -0
  32. package/src/ui/use-chat/AssistantChatTransport.tsx +70 -0
  33. package/src/ui/use-chat/useAISDKRuntime.tsx +32 -5
  34. package/src/ui/use-chat/useChatRuntime.tsx +63 -0
  35. package/src/ui/use-chat/useExternalHistory.tsx +114 -0
  36. package/src/ui/utils/toCreateMessage.ts +2 -0
package/README.md CHANGED
@@ -1,3 +1,58 @@
1
1
  # `@assistant-ui/react-ai-sdk`
2
2
 
3
3
  Vercel AI SDK integration for `@assistant-ui/react`.
4
+
5
+ ## Features
6
+
7
+ - Seamless integration with Vercel AI SDK v5
8
+ - Automatic system message and frontend tools forwarding via `AssistantChatTransport`
9
+ - Support for custom transport configuration
10
+
11
+ ## Usage
12
+
13
+ ### Basic Setup
14
+
15
+ ```typescript
16
+ import { useChatRuntime } from '@assistant-ui/react-ai-sdk';
17
+ import { AssistantRuntimeProvider } from '@assistant-ui/react';
18
+
19
+ function App() {
20
+ // By default, uses AssistantChatTransport which forwards system messages and tools
21
+ const runtime = useChatRuntime();
22
+
23
+ return (
24
+ <AssistantRuntimeProvider runtime={runtime}>
25
+ {/* Your assistant UI components */}
26
+ </AssistantRuntimeProvider>
27
+ );
28
+ }
29
+ ```
30
+
31
+ ### Custom Transport
32
+
33
+ When you need to customize the transport configuration:
34
+
35
+ ```typescript
36
+ import { DefaultChatTransport } from 'ai';
37
+ import { AssistantChatTransport } from '@assistant-ui/react-ai-sdk';
38
+ import { useChatRuntime } from '@assistant-ui/react-ai-sdk';
39
+
40
+ // Custom API URL while keeping system/tools forwarding
41
+ const runtime = useChatRuntime({
42
+ transport: new AssistantChatTransport({
43
+ api: '/my-custom-api/chat'
44
+ })
45
+ });
46
+
47
+ // Or disable system/tools forwarding entirely
48
+ const runtime = useChatRuntime({
49
+ transport: new DefaultChatTransport()
50
+ });
51
+ ```
52
+
53
+ **Important:** When customizing the API URL, you must explicitly use `AssistantChatTransport` to keep frontend system messages and tools forwarding.
54
+
55
+ ## AssistantChatTransport vs DefaultChatTransport
56
+
57
+ - **AssistantChatTransport** (default): Automatically forwards system messages and frontend tools from the Assistant UI context to your backend API
58
+ - **DefaultChatTransport**: Standard AI SDK transport without automatic forwarding
@@ -0,0 +1,5 @@
1
+ import { UIMessage } from "ai";
2
+ import { MessageFormatAdapter } from "@assistant-ui/react";
3
+ export type AISDKStorageFormat = Omit<UIMessage, "id">;
4
+ export declare const aiSDKV5FormatAdapter: MessageFormatAdapter<UIMessage, AISDKStorageFormat>;
5
+ //# sourceMappingURL=aiSDKFormatAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiSDKFormatAdapter.d.ts","sourceRoot":"","sources":["../../../src/ui/adapters/aiSDKFormatAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EACL,oBAAoB,EAGrB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAEvD,eAAO,MAAM,oBAAoB,EAAE,oBAAoB,CACrD,SAAS,EACT,kBAAkB,CA6BnB,CAAC"}
@@ -0,0 +1,28 @@
1
+ // src/ui/adapters/aiSDKFormatAdapter.ts
2
+ var aiSDKV5FormatAdapter = {
3
+ format: "ai-sdk/v5",
4
+ encode({
5
+ message: { id, parts, ...message }
6
+ }) {
7
+ return {
8
+ ...message,
9
+ parts: parts.filter((part) => part.type !== "file")
10
+ };
11
+ },
12
+ decode(stored) {
13
+ return {
14
+ parentId: stored.parent_id,
15
+ message: {
16
+ id: stored.id,
17
+ ...stored.content
18
+ }
19
+ };
20
+ },
21
+ getId(message) {
22
+ return message.id;
23
+ }
24
+ };
25
+ export {
26
+ aiSDKV5FormatAdapter
27
+ };
28
+ //# sourceMappingURL=aiSDKFormatAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ui/adapters/aiSDKFormatAdapter.ts"],"sourcesContent":["import { UIMessage } from \"ai\";\nimport {\n MessageFormatAdapter,\n MessageFormatItem,\n MessageStorageEntry,\n} from \"@assistant-ui/react\";\n\n// Storage format for AI SDK messages - just the UIMessage\nexport type AISDKStorageFormat = Omit<UIMessage, \"id\">;\n\nexport const aiSDKV5FormatAdapter: MessageFormatAdapter<\n UIMessage,\n AISDKStorageFormat\n> = {\n format: \"ai-sdk/v5\",\n\n encode({\n message: { id, parts, ...message },\n }: MessageFormatItem<UIMessage>): AISDKStorageFormat {\n // Filter out FileContentParts until they are supported\n return {\n ...message,\n parts: parts.filter((part) => part.type !== \"file\"),\n };\n },\n\n decode(\n stored: MessageStorageEntry<AISDKStorageFormat>,\n ): MessageFormatItem<UIMessage> {\n return {\n parentId: stored.parent_id,\n message: {\n id: stored.id,\n ...stored.content,\n },\n };\n },\n\n getId(message: UIMessage): string {\n return message.id;\n },\n};\n"],"mappings":";AAUO,IAAM,uBAGT;AAAA,EACF,QAAQ;AAAA,EAER,OAAO;AAAA,IACL,SAAS,EAAE,IAAI,OAAO,GAAG,QAAQ;AAAA,EACnC,GAAqD;AAEnD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,OACE,QAC8B;AAC9B,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB,SAAS;AAAA,QACP,IAAI,OAAO;AAAA,QACX,GAAG,OAAO;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,SAA4B;AAChC,WAAO,QAAQ;AAAA,EACjB;AACF;","names":[]}
@@ -1,2 +1,4 @@
1
1
  export { useAISDKRuntime } from "./use-chat/useAISDKRuntime";
2
+ export { useChatRuntime } from "./use-chat/useChatRuntime";
3
+ export { AssistantChatTransport } from "./use-chat/AssistantChatTransport";
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC"}
package/dist/ui/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  // src/ui/index.ts
2
2
  import { useAISDKRuntime } from "./use-chat/useAISDKRuntime.js";
3
+ import { useChatRuntime } from "./use-chat/useChatRuntime.js";
4
+ import { AssistantChatTransport } from "./use-chat/AssistantChatTransport.js";
3
5
  export {
4
- useAISDKRuntime
6
+ AssistantChatTransport,
7
+ useAISDKRuntime,
8
+ useChatRuntime
5
9
  };
6
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ui/index.ts"],"sourcesContent":["export { useAISDKRuntime } from \"./use-chat/useAISDKRuntime\";\n"],"mappings":";AAAA,SAAS,uBAAuB;","names":[]}
1
+ {"version":3,"sources":["../../src/ui/index.ts"],"sourcesContent":["export { useAISDKRuntime } from \"./use-chat/useAISDKRuntime\";\nexport { useChatRuntime } from \"./use-chat/useChatRuntime\";\nexport { AssistantChatTransport } from \"./use-chat/AssistantChatTransport\";\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,8BAA8B;","names":[]}
@@ -0,0 +1,8 @@
1
+ import { AssistantRuntime } from "@assistant-ui/react";
2
+ import { DefaultChatTransport, HttpChatTransportInitOptions, UIMessage } from "ai";
3
+ export declare class AssistantChatTransport<UI_MESSAGE extends UIMessage> extends DefaultChatTransport<UI_MESSAGE> {
4
+ private runtime;
5
+ constructor(initOptions?: HttpChatTransportInitOptions<UI_MESSAGE>);
6
+ setRuntime(runtime: AssistantRuntime): void;
7
+ }
8
+ //# sourceMappingURL=AssistantChatTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantChatTransport.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/AssistantChatTransport.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAQ,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAE5B,SAAS,EACV,MAAM,IAAI,CAAC;AAyBZ,qBAAa,sBAAsB,CACjC,UAAU,SAAS,SAAS,CAC5B,SAAQ,oBAAoB,CAAC,UAAU,CAAC;IACxC,OAAO,CAAC,OAAO,CAA+B;gBAClC,WAAW,CAAC,EAAE,4BAA4B,CAAC,UAAU,CAAC;IA+BlE,UAAU,CAAC,OAAO,EAAE,gBAAgB;CAGrC"}
@@ -0,0 +1,60 @@
1
+ // src/ui/use-chat/AssistantChatTransport.tsx
2
+ import {
3
+ DefaultChatTransport
4
+ } from "ai";
5
+ import z from "zod";
6
+ var toAISDKTools = (tools) => {
7
+ return Object.fromEntries(
8
+ Object.entries(tools).map(([name, tool]) => [
9
+ name,
10
+ {
11
+ ...tool.description ? { description: tool.description } : void 0,
12
+ parameters: tool.parameters instanceof z.ZodType ? z.toJSONSchema(tool.parameters) : tool.parameters
13
+ }
14
+ ])
15
+ );
16
+ };
17
+ var getEnabledTools = (tools) => {
18
+ return Object.fromEntries(
19
+ Object.entries(tools).filter(
20
+ ([, tool]) => !tool.disabled && tool.type !== "backend"
21
+ )
22
+ );
23
+ };
24
+ var AssistantChatTransport = class extends DefaultChatTransport {
25
+ runtime;
26
+ constructor(initOptions) {
27
+ super({
28
+ ...initOptions,
29
+ prepareSendMessagesRequest: async (options) => {
30
+ const context = this.runtime?.thread.getModelContext();
31
+ const optionsEx = {
32
+ ...options,
33
+ body: {
34
+ system: context?.system,
35
+ tools: toAISDKTools(getEnabledTools(context?.tools ?? {})),
36
+ ...options?.body
37
+ }
38
+ };
39
+ const preparedRequest = await initOptions?.prepareSendMessagesRequest?.(optionsEx);
40
+ return {
41
+ ...preparedRequest,
42
+ body: preparedRequest?.body ?? {
43
+ ...optionsEx.body,
44
+ id: options.id,
45
+ messages: options.messages,
46
+ trigger: options.trigger,
47
+ messageId: options.messageId
48
+ }
49
+ };
50
+ }
51
+ });
52
+ }
53
+ setRuntime(runtime) {
54
+ this.runtime = runtime;
55
+ }
56
+ };
57
+ export {
58
+ AssistantChatTransport
59
+ };
60
+ //# sourceMappingURL=AssistantChatTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ui/use-chat/AssistantChatTransport.tsx"],"sourcesContent":["import { AssistantRuntime, Tool } from \"@assistant-ui/react\";\nimport {\n DefaultChatTransport,\n HttpChatTransportInitOptions,\n JSONSchema7,\n UIMessage,\n} from \"ai\";\nimport z from \"zod\";\n\nconst toAISDKTools = (tools: Record<string, Tool>) => {\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => [\n name,\n {\n ...(tool.description ? { description: tool.description } : undefined),\n parameters: (tool.parameters instanceof z.ZodType\n ? z.toJSONSchema(tool.parameters)\n : tool.parameters) as JSONSchema7,\n },\n ]),\n );\n};\n\nconst getEnabledTools = (tools: Record<string, Tool>) => {\n return Object.fromEntries(\n Object.entries(tools).filter(\n ([, tool]) => !tool.disabled && tool.type !== \"backend\",\n ),\n );\n};\n\nexport class AssistantChatTransport<\n UI_MESSAGE extends UIMessage,\n> extends DefaultChatTransport<UI_MESSAGE> {\n private runtime: AssistantRuntime | undefined;\n constructor(initOptions?: HttpChatTransportInitOptions<UI_MESSAGE>) {\n super({\n ...initOptions,\n prepareSendMessagesRequest: async (options) => {\n const context = this.runtime?.thread.getModelContext();\n\n const optionsEx = {\n ...options,\n body: {\n system: context?.system,\n tools: toAISDKTools(getEnabledTools(context?.tools ?? {})),\n ...options?.body,\n },\n };\n const preparedRequest =\n await initOptions?.prepareSendMessagesRequest?.(optionsEx);\n\n return {\n ...preparedRequest,\n body: preparedRequest?.body ?? {\n ...optionsEx.body,\n id: options.id,\n messages: options.messages,\n trigger: options.trigger,\n messageId: options.messageId,\n },\n };\n },\n });\n }\n\n setRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n}\n"],"mappings":";AACA;AAAA,EACE;AAAA,OAIK;AACP,OAAO,OAAO;AAEd,IAAM,eAAe,CAAC,UAAgC;AACpD,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;AAAA,MAC1C;AAAA,MACA;AAAA,QACE,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI;AAAA,QAC3D,YAAa,KAAK,sBAAsB,EAAE,UACtC,EAAE,aAAa,KAAK,UAAU,IAC9B,KAAK;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,IAAM,kBAAkB,CAAC,UAAgC;AACvD,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAK,EAAE;AAAA,MACpB,CAAC,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,YAAY,KAAK,SAAS;AAAA,IAChD;AAAA,EACF;AACF;AAEO,IAAM,yBAAN,cAEG,qBAAiC;AAAA,EACjC;AAAA,EACR,YAAY,aAAwD;AAClE,UAAM;AAAA,MACJ,GAAG;AAAA,MACH,4BAA4B,OAAO,YAAY;AAC7C,cAAM,UAAU,KAAK,SAAS,OAAO,gBAAgB;AAErD,cAAM,YAAY;AAAA,UAChB,GAAG;AAAA,UACH,MAAM;AAAA,YACJ,QAAQ,SAAS;AAAA,YACjB,OAAO,aAAa,gBAAgB,SAAS,SAAS,CAAC,CAAC,CAAC;AAAA,YACzD,GAAG,SAAS;AAAA,UACd;AAAA,QACF;AACA,cAAM,kBACJ,MAAM,aAAa,6BAA6B,SAAS;AAE3D,eAAO;AAAA,UACL,GAAG;AAAA,UACH,MAAM,iBAAiB,QAAQ;AAAA,YAC7B,GAAG,UAAU;AAAA,YACb,IAAI,QAAQ;AAAA,YACZ,UAAU,QAAQ;AAAA,YAClB,SAAS,QAAQ;AAAA,YACjB,WAAW,QAAQ;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,WAAW,SAA2B;AACpC,SAAK,UAAU;AAAA,EACjB;AACF;","names":[]}
@@ -1,7 +1,9 @@
1
1
  import type { useChat } from "@ai-sdk/react";
2
- import { ExternalStoreAdapter } from "@assistant-ui/react";
2
+ import { ExternalStoreAdapter, ThreadHistoryAdapter, AssistantRuntime } from "@assistant-ui/react";
3
3
  export type AISDKRuntimeAdapter = {
4
- adapters?: NonNullable<ExternalStoreAdapter["adapters"]> | undefined;
4
+ adapters?: (NonNullable<ExternalStoreAdapter["adapters"]> & {
5
+ history?: ThreadHistoryAdapter | undefined;
6
+ }) | undefined;
5
7
  };
6
- export declare const useAISDKRuntime: (chatHelpers: ReturnType<typeof useChat>, adapter?: AISDKRuntimeAdapter) => import("@assistant-ui/react").AssistantRuntime;
8
+ export declare const useAISDKRuntime: (chatHelpers: ReturnType<typeof useChat>, adapter?: AISDKRuntimeAdapter) => AssistantRuntime;
7
9
  //# sourceMappingURL=useAISDKRuntime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAISDKRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAM7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC;CACtE,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,aAAa,UAAU,CAAC,OAAO,OAAO,CAAC,EACvC,UAAS,mBAAwB,mDA+ClC,CAAC"}
1
+ {"version":3,"file":"useAISDKRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAEL,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAS7B,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EACL,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,GAAG;QAC/C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;KAC5C,CAAC,GACF,SAAS,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,aAAa,UAAU,CAAC,OAAO,OAAO,CAAC,EACvC,UAAS,mBAAwB,qBAgElC,CAAC"}
@@ -1,24 +1,42 @@
1
1
  "use client";
2
2
 
3
3
  // src/ui/use-chat/useAISDKRuntime.tsx
4
- import { useExternalStoreRuntime } from "@assistant-ui/react";
4
+ import {
5
+ useExternalStoreRuntime
6
+ } from "@assistant-ui/react";
5
7
  import { sliceMessagesUntil } from "../utils/sliceMessagesUntil.js";
6
8
  import { toCreateMessage } from "../utils/toCreateMessage.js";
7
9
  import { vercelAttachmentAdapter } from "../utils/vercelAttachmentAdapter.js";
8
10
  import { getVercelAIMessages } from "../getVercelAIMessages.js";
9
11
  import { AISDKMessageConverter } from "../utils/convertMessage.js";
12
+ import { aiSDKV5FormatAdapter } from "../adapters/aiSDKFormatAdapter.js";
13
+ import { useExternalHistory } from "./useExternalHistory.js";
10
14
  var useAISDKRuntime = (chatHelpers, adapter = {}) => {
11
15
  const messages = AISDKMessageConverter.useThreadMessages({
12
16
  isRunning: chatHelpers.status === "submitted" || chatHelpers.status == "streaming",
13
17
  messages: chatHelpers.messages
14
18
  });
19
+ const isLoading = useExternalHistory(
20
+ {
21
+ get current() {
22
+ return runtime;
23
+ }
24
+ },
25
+ adapter.adapters?.history,
26
+ AISDKMessageConverter.toThreadMessages,
27
+ aiSDKV5FormatAdapter,
28
+ (messages2) => {
29
+ chatHelpers.setMessages(messages2);
30
+ }
31
+ );
15
32
  const runtime = useExternalStoreRuntime({
16
33
  isRunning: chatHelpers.status === "submitted" || chatHelpers.status === "streaming",
17
34
  messages,
18
35
  setMessages: (messages2) => chatHelpers.setMessages(messages2.map(getVercelAIMessages).flat()),
19
36
  onCancel: async () => chatHelpers.stop(),
20
37
  onNew: async (message) => {
21
- await chatHelpers.sendMessage(await toCreateMessage(message));
38
+ const createMessage = await toCreateMessage(message);
39
+ await chatHelpers.sendMessage(createMessage);
22
40
  },
23
41
  onEdit: async (message) => {
24
42
  const newMessages = sliceMessagesUntil(
@@ -26,7 +44,8 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
26
44
  message.parentId
27
45
  );
28
46
  chatHelpers.setMessages(newMessages);
29
- await chatHelpers.sendMessage(await toCreateMessage(message));
47
+ const createMessage = await toCreateMessage(message);
48
+ await chatHelpers.sendMessage(createMessage);
30
49
  },
31
50
  onReload: async (parentId) => {
32
51
  const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);
@@ -43,7 +62,8 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
43
62
  adapters: {
44
63
  attachments: vercelAttachmentAdapter,
45
64
  ...adapter.adapters
46
- }
65
+ },
66
+ isLoading
47
67
  });
48
68
  return runtime;
49
69
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { useChat } from \"@ai-sdk/react\";\nimport { useExternalStoreRuntime } from \"@assistant-ui/react\";\nimport { sliceMessagesUntil } from \"../utils/sliceMessagesUntil\";\nimport { toCreateMessage } from \"../utils/toCreateMessage\";\nimport { vercelAttachmentAdapter } from \"../utils/vercelAttachmentAdapter\";\nimport { getVercelAIMessages } from \"../getVercelAIMessages\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { AISDKMessageConverter } from \"../utils/convertMessage\";\n\nexport type AISDKRuntimeAdapter = {\n adapters?: NonNullable<ExternalStoreAdapter[\"adapters\"]> | undefined;\n};\n\nexport const useAISDKRuntime = (\n chatHelpers: ReturnType<typeof useChat>,\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 runtime = useExternalStoreRuntime({\n isRunning:\n chatHelpers.status === \"submitted\" || chatHelpers.status === \"streaming\",\n messages,\n setMessages: (messages) =>\n chatHelpers.setMessages(messages.map(getVercelAIMessages).flat()),\n onCancel: async () => chatHelpers.stop(),\n onNew: async (message) => {\n await chatHelpers.sendMessage(await toCreateMessage(message));\n },\n onEdit: async (message) => {\n const newMessages = sliceMessagesUntil(\n chatHelpers.messages,\n message.parentId,\n );\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.sendMessage(await toCreateMessage(message));\n },\n onReload: async (parentId: string | null) => {\n const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.regenerate();\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 });\n\n return runtime;\n};\n"],"mappings":";;;AAGA,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,SAAS,2BAA2B;AAEpC,SAAS,6BAA6B;AAM/B,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,UAAU,wBAAwB;AAAA,IACtC,WACE,YAAY,WAAW,eAAe,YAAY,WAAW;AAAA,IAC/D;AAAA,IACA,aAAa,CAACA,cACZ,YAAY,YAAYA,UAAS,IAAI,mBAAmB,EAAE,KAAK,CAAC;AAAA,IAClE,UAAU,YAAY,YAAY,KAAK;AAAA,IACvC,OAAO,OAAO,YAAY;AACxB,YAAM,YAAY,YAAY,MAAM,gBAAgB,OAAO,CAAC;AAAA,IAC9D;AAAA,IACA,QAAQ,OAAO,YAAY;AACzB,YAAM,cAAc;AAAA,QAClB,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV;AACA,kBAAY,YAAY,WAAW;AAEnC,YAAM,YAAY,YAAY,MAAM,gBAAgB,OAAO,CAAC;AAAA,IAC9D;AAAA,IACA,UAAU,OAAO,aAA4B;AAC3C,YAAM,cAAc,mBAAmB,YAAY,UAAU,QAAQ;AACrE,kBAAY,YAAY,WAAW;AAEnC,YAAM,YAAY,WAAW;AAAA,IAC/B;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,EACF,CAAC;AAED,SAAO;AACT;","names":["messages"]}
1
+ {"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { useChat } from \"@ai-sdk/react\";\nimport {\n useExternalStoreRuntime,\n ExternalStoreAdapter,\n ThreadHistoryAdapter,\n AssistantRuntime,\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 { aiSDKV5FormatAdapter } from \"../adapters/aiSDKFormatAdapter\";\nimport { useExternalHistory } from \"./useExternalHistory\";\n\nexport type AISDKRuntimeAdapter = {\n adapters?:\n | (NonNullable<ExternalStoreAdapter[\"adapters\"]> & {\n history?: ThreadHistoryAdapter | undefined;\n })\n | undefined;\n};\n\nexport const useAISDKRuntime = (\n chatHelpers: ReturnType<typeof useChat>,\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 {\n get current(): AssistantRuntime {\n return runtime;\n },\n },\n adapter.adapters?.history,\n AISDKMessageConverter.toThreadMessages,\n aiSDKV5FormatAdapter,\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(messages.map(getVercelAIMessages).flat()),\n onCancel: async () => chatHelpers.stop(),\n onNew: async (message) => {\n const createMessage = await toCreateMessage(message);\n await chatHelpers.sendMessage(createMessage);\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(message);\n await chatHelpers.sendMessage(createMessage);\n },\n onReload: async (parentId: string | null) => {\n const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);\n chatHelpers.setMessages(newMessages);\n\n await chatHelpers.regenerate();\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,OAIK;AACP,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AACtC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AAU5B,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,IAAI,UAA4B;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,QAAQ,UAAU;AAAA,IAClB,sBAAsB;AAAA,IACtB;AAAA,IACA,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,YAAYA,UAAS,IAAI,mBAAmB,EAAE,KAAK,CAAC;AAAA,IAClE,UAAU,YAAY,YAAY,KAAK;AAAA,IACvC,OAAO,OAAO,YAAY;AACxB,YAAM,gBAAgB,MAAM,gBAAgB,OAAO;AACnD,YAAM,YAAY,YAAY,aAAa;AAAA,IAC7C;AAAA,IACA,QAAQ,OAAO,YAAY;AACzB,YAAM,cAAc;AAAA,QAClB,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV;AACA,kBAAY,YAAY,WAAW;AAEnC,YAAM,gBAAgB,MAAM,gBAAgB,OAAO;AACnD,YAAM,YAAY,YAAY,aAAa;AAAA,IAC7C;AAAA,IACA,UAAU,OAAO,aAA4B;AAC3C,YAAM,cAAc,mBAAmB,YAAY,UAAU,QAAQ;AACrE,kBAAY,YAAY,WAAW;AAEnC,YAAM,YAAY,WAAW;AAAA,IAC/B;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"]}
@@ -0,0 +1,12 @@
1
+ import { type UIMessage } from "@ai-sdk/react";
2
+ import type { AssistantCloud } from "assistant-cloud";
3
+ import { AssistantRuntime } from "@assistant-ui/react";
4
+ import { type AISDKRuntimeAdapter } from "./useAISDKRuntime";
5
+ import { ChatInit } from "ai";
6
+ export type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> = ChatInit<UI_MESSAGE> & {
7
+ cloud?: AssistantCloud | undefined;
8
+ adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
9
+ };
10
+ export declare const useChatThreadRuntime: <UI_MESSAGE extends UIMessage = UIMessage>(options?: UseChatRuntimeOptions<UI_MESSAGE>) => AssistantRuntime;
11
+ export declare const useChatRuntime: <UI_MESSAGE extends UIMessage = UIMessage>({ cloud, ...options }?: UseChatRuntimeOptions<UI_MESSAGE>) => AssistantRuntime;
12
+ //# sourceMappingURL=useChatRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,gBAAgB,EAIjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAG9B,MAAM,MAAM,qBAAqB,CAAC,UAAU,SAAS,SAAS,GAAG,SAAS,IACxE,QAAQ,CAAC,UAAU,CAAC,GAAG;IACrB,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;CACxD,CAAC;AAEJ,eAAO,MAAM,oBAAoB,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EAC3E,UAAU,qBAAqB,CAAC,UAAU,CAAC,KAC1C,gBA2BF,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAU,SAAS,SAAS,GAAG,SAAS,EAAE,wBAGtE,qBAAqB,CAAC,UAAU,CAAM,KAAG,gBAQ3C,CAAC"}
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ // src/ui/use-chat/useChatRuntime.tsx
4
+ import { useChat } from "@ai-sdk/react";
5
+ import {
6
+ unstable_useCloudThreadListAdapter,
7
+ unstable_useRemoteThreadListRuntime,
8
+ useRuntimeAdapters
9
+ } from "@assistant-ui/react";
10
+ import { useAISDKRuntime } from "./useAISDKRuntime.js";
11
+ import { AssistantChatTransport } from "./AssistantChatTransport.js";
12
+ var useChatThreadRuntime = (options) => {
13
+ const {
14
+ adapters,
15
+ transport: transportOptions,
16
+ ...chatOptions
17
+ } = options ?? {};
18
+ const transport = transportOptions ?? new AssistantChatTransport();
19
+ const contextAdapters = useRuntimeAdapters();
20
+ const chat = useChat({
21
+ ...chatOptions,
22
+ transport
23
+ });
24
+ const runtime = useAISDKRuntime(chat, {
25
+ adapters: {
26
+ ...contextAdapters,
27
+ ...adapters
28
+ }
29
+ });
30
+ if (transport instanceof AssistantChatTransport) {
31
+ transport.setRuntime(runtime);
32
+ }
33
+ return runtime;
34
+ };
35
+ var useChatRuntime = ({
36
+ cloud,
37
+ ...options
38
+ } = {}) => {
39
+ const cloudAdapter = unstable_useCloudThreadListAdapter({ cloud });
40
+ return unstable_useRemoteThreadListRuntime({
41
+ runtimeHook: function RuntimeHook() {
42
+ return useChatThreadRuntime(options);
43
+ },
44
+ adapter: cloudAdapter
45
+ });
46
+ };
47
+ export {
48
+ useChatRuntime,
49
+ useChatThreadRuntime
50
+ };
51
+ //# sourceMappingURL=useChatRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport { useChat, type UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport {\n AssistantRuntime,\n unstable_useCloudThreadListAdapter,\n unstable_useRemoteThreadListRuntime,\n useRuntimeAdapters,\n} from \"@assistant-ui/react\";\nimport { useAISDKRuntime, type AISDKRuntimeAdapter } from \"./useAISDKRuntime\";\nimport { ChatInit } from \"ai\";\nimport { AssistantChatTransport } from \"./AssistantChatTransport\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n };\n\nexport const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: UseChatRuntimeOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n ...chatOptions\n } = options ?? {};\n const transport = transportOptions ?? new AssistantChatTransport();\n\n // Get adapters from context (including history adapter from cloud)\n const contextAdapters = useRuntimeAdapters();\n\n const chat = useChat({\n ...chatOptions,\n transport,\n });\n\n const runtime = useAISDKRuntime(chat as any, {\n adapters: {\n ...contextAdapters,\n ...adapters,\n },\n });\n if (transport instanceof AssistantChatTransport) {\n transport.setRuntime(runtime);\n }\n\n return runtime;\n};\n\nexport const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({\n cloud,\n ...options\n}: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {\n const cloudAdapter = unstable_useCloudThreadListAdapter({ cloud });\n return unstable_useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useChatThreadRuntime(options);\n },\n adapter: cloudAdapter,\n });\n};\n"],"mappings":";;;AAEA,SAAS,eAA+B;AAExC;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAiD;AAE1D,SAAS,8BAA8B;AAQhC,IAAM,uBAAuB,CAClC,YACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EACL,IAAI,WAAW,CAAC;AAChB,QAAM,YAAY,oBAAoB,IAAI,uBAAuB;AAGjE,QAAM,kBAAkB,mBAAmB;AAE3C,QAAM,OAAO,QAAQ;AAAA,IACnB,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,UAAU,gBAAgB,MAAa;AAAA,IAC3C,UAAU;AAAA,MACR,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EACF,CAAC;AACD,MAAI,qBAAqB,wBAAwB;AAC/C,cAAU,WAAW,OAAO;AAAA,EAC9B;AAEA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAA2C;AAAA,EACvE;AAAA,EACA,GAAG;AACL,IAAuC,CAAC,MAAwB;AAC9D,QAAM,eAAe,mCAAmC,EAAE,MAAM,CAAC;AACjE,SAAO,oCAAoC;AAAA,IACzC,aAAa,SAAS,cAAc;AAClC,aAAO,qBAAqB,OAAO;AAAA,IACrC;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
@@ -0,0 +1,5 @@
1
+ import { AssistantRuntime, ThreadHistoryAdapter, ThreadMessage, MessageFormatAdapter, MessageFormatRepository, ExportedMessageRepository } from "@assistant-ui/react";
2
+ import { RefObject } from "react";
3
+ export declare const toExportedMessageRepository: <TMessage>(toThreadMessages: (messages: TMessage[]) => ThreadMessage[], messages: MessageFormatRepository<TMessage>) => ExportedMessageRepository;
4
+ export declare const useExternalHistory: <TMessage>(runtimeRef: RefObject<AssistantRuntime>, historyAdapter: ThreadHistoryAdapter | undefined, toThreadMessages: (messages: TMessage[]) => ThreadMessage[], storageFormatAdapter: MessageFormatAdapter<TMessage, any>, onSetMessages: (messages: TMessage[]) => void) => boolean;
5
+ //# sourceMappingURL=useExternalHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useExternalHistory.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useExternalHistory.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EAEpB,uBAAuB,EACvB,yBAAyB,EAE1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA+B,SAAS,EAAE,MAAM,OAAO,CAAC;AAI/D,eAAO,MAAM,2BAA2B,GAAI,QAAQ,EAClD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,UAAU,uBAAuB,CAAC,QAAQ,CAAC,KAC1C,yBAWF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,EACzC,YAAY,SAAS,CAAC,gBAAgB,CAAC,EACvC,gBAAgB,oBAAoB,GAAG,SAAS,EAChD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,EAC3D,sBAAsB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,EACzD,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,YA4E9C,CAAC"}
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ // src/ui/use-chat/useExternalHistory.tsx
4
+ import {
5
+ getExternalStoreMessages,
6
+ INTERNAL
7
+ } from "@assistant-ui/react";
8
+ import { useRef, useEffect, useState } from "react";
9
+ var { MessageRepository } = INTERNAL;
10
+ var toExportedMessageRepository = (toThreadMessages, messages) => {
11
+ return {
12
+ headId: messages.headId,
13
+ messages: messages.messages.map((m) => {
14
+ const message = toThreadMessages([m.message])[0];
15
+ return {
16
+ ...m,
17
+ message
18
+ };
19
+ })
20
+ };
21
+ };
22
+ var useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageFormatAdapter, onSetMessages) => {
23
+ const loadedRef = useRef(false);
24
+ const [isLoading, setIsLoading] = useState(true);
25
+ const historyIds = useRef(/* @__PURE__ */ new Set());
26
+ const onSetMessagesRef = useRef(onSetMessages);
27
+ useEffect(() => {
28
+ onSetMessagesRef.current = onSetMessages;
29
+ });
30
+ useEffect(() => {
31
+ if (!historyAdapter || loadedRef.current) return;
32
+ const loadHistory = async () => {
33
+ setIsLoading(true);
34
+ try {
35
+ const repo = await historyAdapter.withFormat?.(storageFormatAdapter).load();
36
+ if (repo && repo.messages.length > 0) {
37
+ const converted = toExportedMessageRepository(toThreadMessages, repo);
38
+ runtimeRef.current.thread.import(converted);
39
+ const tempRepo = new MessageRepository();
40
+ tempRepo.import(converted);
41
+ const messages = tempRepo.getMessages();
42
+ onSetMessagesRef.current(
43
+ messages.map(getExternalStoreMessages).flat()
44
+ );
45
+ historyIds.current = new Set(
46
+ converted.messages.map((m) => m.message.id)
47
+ );
48
+ }
49
+ } catch (error) {
50
+ console.error("Failed to load message history:", error);
51
+ } finally {
52
+ setIsLoading(false);
53
+ }
54
+ };
55
+ if (!loadedRef.current) {
56
+ loadedRef.current = true;
57
+ loadHistory();
58
+ }
59
+ }, [historyAdapter, storageFormatAdapter, toThreadMessages]);
60
+ useEffect(() => {
61
+ return runtimeRef.current.thread.subscribe(async () => {
62
+ const { messages, isRunning } = runtimeRef.current.thread.getState();
63
+ if (isRunning) return;
64
+ for (let i = 0; i < messages.length; i++) {
65
+ const message = messages[i];
66
+ if (message.status === void 0 || message.status.type === "complete" || message.status.type === "incomplete") {
67
+ if (historyIds.current.has(message.id)) return;
68
+ historyIds.current.add(message.id);
69
+ const parentId = i > 0 ? messages[i - 1].id : null;
70
+ await historyAdapter?.withFormat?.(storageFormatAdapter).append({
71
+ parentId,
72
+ message: getExternalStoreMessages(message)[0]
73
+ });
74
+ }
75
+ }
76
+ });
77
+ }, []);
78
+ return isLoading;
79
+ };
80
+ export {
81
+ toExportedMessageRepository,
82
+ useExternalHistory
83
+ };
84
+ //# sourceMappingURL=useExternalHistory.js.map
@@ -0,0 +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(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]);\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 }, []);\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,OAAO,aAAa;AAC7C,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,gBAAgB,CAAC;AAE3D,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,CAAC;AAEL,SAAO;AACT;","names":[]}
@@ -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,EAGf,SAAS,EAGV,MAAM,IAAI,CAAC;AAEZ,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,KACrB,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CA8CpC,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,EAIf,SAAS,EAGV,MAAM,IAAI,CAAC;AAEZ,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,KACrB,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CA+CpC,CAAC"}
@@ -1,4 +1,7 @@
1
1
  // src/ui/utils/toCreateMessage.ts
2
+ import {
3
+ generateId
4
+ } from "ai";
2
5
  var toCreateMessage = async (message) => {
3
6
  const textParts = message.content.filter((part) => part.type === "text").map((t) => t.text).join("\n\n");
4
7
  const parts = [
@@ -29,6 +32,7 @@ var toCreateMessage = async (message) => {
29
32
  );
30
33
  parts.push(...attachmentParts);
31
34
  return {
35
+ id: generateId(),
32
36
  role: message.role,
33
37
  parts
34
38
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/utils/toCreateMessage.ts"],"sourcesContent":["import { AppendMessage } from \"@assistant-ui/react\";\nimport {\n CreateUIMessage,\n FileUIPart,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\nexport const toCreateMessage = async (\n message: AppendMessage,\n): Promise<CreateUIMessage<UIMessage>> => {\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 role: message.role,\n parts,\n };\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":";AAUO,IAAM,kBAAkB,OAC7B,YACwC;AACxC,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,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 FileUIPart,\n generateId,\n UIDataTypes,\n UIMessage,\n UIMessagePart,\n UITools,\n} from \"ai\";\n\nexport const toCreateMessage = async (\n message: AppendMessage,\n): Promise<CreateUIMessage<UIMessage>> => {\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 };\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,YACwC;AACxC,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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-ai-sdk",
3
- "version": "0.11.1",
3
+ "version": "0.11.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,13 +29,17 @@
29
29
  "zustand": "^5.0.7"
30
30
  },
31
31
  "peerDependencies": {
32
- "@assistant-ui/react": "^0.10.36",
32
+ "@assistant-ui/react": "^0.10.39",
33
33
  "@types/react": "*",
34
+ "assistant-cloud": "*",
34
35
  "react": "^18 || ^19 || ^19.0.0-rc"
35
36
  },
36
37
  "peerDependenciesMeta": {
37
38
  "@types/react": {
38
39
  "optional": true
40
+ },
41
+ "assistant-cloud": {
42
+ "optional": true
39
43
  }
40
44
  },
41
45
  "devDependencies": {
@@ -45,8 +49,8 @@
45
49
  "eslint-config-next": "15.4.5",
46
50
  "react": "19.1.1",
47
51
  "tsx": "^4.20.3",
48
- "@assistant-ui/x-buildutils": "0.0.1",
49
- "@assistant-ui/react": "0.10.36"
52
+ "@assistant-ui/react": "0.10.39",
53
+ "@assistant-ui/x-buildutils": "0.0.1"
50
54
  },
51
55
  "publishConfig": {
52
56
  "access": "public",
@@ -0,0 +1,42 @@
1
+ import { UIMessage } from "ai";
2
+ import {
3
+ MessageFormatAdapter,
4
+ MessageFormatItem,
5
+ MessageStorageEntry,
6
+ } from "@assistant-ui/react";
7
+
8
+ // Storage format for AI SDK messages - just the UIMessage
9
+ export type AISDKStorageFormat = Omit<UIMessage, "id">;
10
+
11
+ export const aiSDKV5FormatAdapter: MessageFormatAdapter<
12
+ UIMessage,
13
+ AISDKStorageFormat
14
+ > = {
15
+ format: "ai-sdk/v5",
16
+
17
+ encode({
18
+ message: { id, parts, ...message },
19
+ }: MessageFormatItem<UIMessage>): AISDKStorageFormat {
20
+ // Filter out FileContentParts until they are supported
21
+ return {
22
+ ...message,
23
+ parts: parts.filter((part) => part.type !== "file"),
24
+ };
25
+ },
26
+
27
+ decode(
28
+ stored: MessageStorageEntry<AISDKStorageFormat>,
29
+ ): MessageFormatItem<UIMessage> {
30
+ return {
31
+ parentId: stored.parent_id,
32
+ message: {
33
+ id: stored.id,
34
+ ...stored.content,
35
+ },
36
+ };
37
+ },
38
+
39
+ getId(message: UIMessage): string {
40
+ return message.id;
41
+ },
42
+ };
package/src/ui/index.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { useAISDKRuntime } from "./use-chat/useAISDKRuntime";
2
+ export { useChatRuntime } from "./use-chat/useChatRuntime";
3
+ export { AssistantChatTransport } from "./use-chat/AssistantChatTransport";
@@ -0,0 +1,70 @@
1
+ import { AssistantRuntime, Tool } from "@assistant-ui/react";
2
+ import {
3
+ DefaultChatTransport,
4
+ HttpChatTransportInitOptions,
5
+ JSONSchema7,
6
+ UIMessage,
7
+ } from "ai";
8
+ import z from "zod";
9
+
10
+ const toAISDKTools = (tools: Record<string, Tool>) => {
11
+ return Object.fromEntries(
12
+ Object.entries(tools).map(([name, tool]) => [
13
+ name,
14
+ {
15
+ ...(tool.description ? { description: tool.description } : undefined),
16
+ parameters: (tool.parameters instanceof z.ZodType
17
+ ? z.toJSONSchema(tool.parameters)
18
+ : tool.parameters) as JSONSchema7,
19
+ },
20
+ ]),
21
+ );
22
+ };
23
+
24
+ const getEnabledTools = (tools: Record<string, Tool>) => {
25
+ return Object.fromEntries(
26
+ Object.entries(tools).filter(
27
+ ([, tool]) => !tool.disabled && tool.type !== "backend",
28
+ ),
29
+ );
30
+ };
31
+
32
+ export class AssistantChatTransport<
33
+ UI_MESSAGE extends UIMessage,
34
+ > extends DefaultChatTransport<UI_MESSAGE> {
35
+ private runtime: AssistantRuntime | undefined;
36
+ constructor(initOptions?: HttpChatTransportInitOptions<UI_MESSAGE>) {
37
+ super({
38
+ ...initOptions,
39
+ prepareSendMessagesRequest: async (options) => {
40
+ const context = this.runtime?.thread.getModelContext();
41
+
42
+ const optionsEx = {
43
+ ...options,
44
+ body: {
45
+ system: context?.system,
46
+ tools: toAISDKTools(getEnabledTools(context?.tools ?? {})),
47
+ ...options?.body,
48
+ },
49
+ };
50
+ const preparedRequest =
51
+ await initOptions?.prepareSendMessagesRequest?.(optionsEx);
52
+
53
+ return {
54
+ ...preparedRequest,
55
+ body: preparedRequest?.body ?? {
56
+ ...optionsEx.body,
57
+ id: options.id,
58
+ messages: options.messages,
59
+ trigger: options.trigger,
60
+ messageId: options.messageId,
61
+ },
62
+ };
63
+ },
64
+ });
65
+ }
66
+
67
+ setRuntime(runtime: AssistantRuntime) {
68
+ this.runtime = runtime;
69
+ }
70
+ }
@@ -1,16 +1,26 @@
1
1
  "use client";
2
2
 
3
3
  import type { useChat } from "@ai-sdk/react";
4
- import { useExternalStoreRuntime } from "@assistant-ui/react";
4
+ import {
5
+ useExternalStoreRuntime,
6
+ ExternalStoreAdapter,
7
+ ThreadHistoryAdapter,
8
+ AssistantRuntime,
9
+ } from "@assistant-ui/react";
5
10
  import { sliceMessagesUntil } from "../utils/sliceMessagesUntil";
6
11
  import { toCreateMessage } from "../utils/toCreateMessage";
7
12
  import { vercelAttachmentAdapter } from "../utils/vercelAttachmentAdapter";
8
13
  import { getVercelAIMessages } from "../getVercelAIMessages";
9
- import { ExternalStoreAdapter } from "@assistant-ui/react";
10
14
  import { AISDKMessageConverter } from "../utils/convertMessage";
15
+ import { aiSDKV5FormatAdapter } from "../adapters/aiSDKFormatAdapter";
16
+ import { useExternalHistory } from "./useExternalHistory";
11
17
 
12
18
  export type AISDKRuntimeAdapter = {
13
- adapters?: NonNullable<ExternalStoreAdapter["adapters"]> | undefined;
19
+ adapters?:
20
+ | (NonNullable<ExternalStoreAdapter["adapters"]> & {
21
+ history?: ThreadHistoryAdapter | undefined;
22
+ })
23
+ | undefined;
14
24
  };
15
25
 
16
26
  export const useAISDKRuntime = (
@@ -23,6 +33,20 @@ export const useAISDKRuntime = (
23
33
  messages: chatHelpers.messages,
24
34
  });
25
35
 
36
+ const isLoading = useExternalHistory(
37
+ {
38
+ get current(): AssistantRuntime {
39
+ return runtime;
40
+ },
41
+ },
42
+ adapter.adapters?.history,
43
+ AISDKMessageConverter.toThreadMessages,
44
+ aiSDKV5FormatAdapter,
45
+ (messages) => {
46
+ chatHelpers.setMessages(messages);
47
+ },
48
+ );
49
+
26
50
  const runtime = useExternalStoreRuntime({
27
51
  isRunning:
28
52
  chatHelpers.status === "submitted" || chatHelpers.status === "streaming",
@@ -31,7 +55,8 @@ export const useAISDKRuntime = (
31
55
  chatHelpers.setMessages(messages.map(getVercelAIMessages).flat()),
32
56
  onCancel: async () => chatHelpers.stop(),
33
57
  onNew: async (message) => {
34
- await chatHelpers.sendMessage(await toCreateMessage(message));
58
+ const createMessage = await toCreateMessage(message);
59
+ await chatHelpers.sendMessage(createMessage);
35
60
  },
36
61
  onEdit: async (message) => {
37
62
  const newMessages = sliceMessagesUntil(
@@ -40,7 +65,8 @@ export const useAISDKRuntime = (
40
65
  );
41
66
  chatHelpers.setMessages(newMessages);
42
67
 
43
- await chatHelpers.sendMessage(await toCreateMessage(message));
68
+ const createMessage = await toCreateMessage(message);
69
+ await chatHelpers.sendMessage(createMessage);
44
70
  },
45
71
  onReload: async (parentId: string | null) => {
46
72
  const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);
@@ -59,6 +85,7 @@ export const useAISDKRuntime = (
59
85
  attachments: vercelAttachmentAdapter,
60
86
  ...adapter.adapters,
61
87
  },
88
+ isLoading,
62
89
  });
63
90
 
64
91
  return runtime;
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import { useChat, type UIMessage } from "@ai-sdk/react";
4
+ import type { AssistantCloud } from "assistant-cloud";
5
+ import {
6
+ AssistantRuntime,
7
+ unstable_useCloudThreadListAdapter,
8
+ unstable_useRemoteThreadListRuntime,
9
+ useRuntimeAdapters,
10
+ } from "@assistant-ui/react";
11
+ import { useAISDKRuntime, type AISDKRuntimeAdapter } from "./useAISDKRuntime";
12
+ import { ChatInit } from "ai";
13
+ import { AssistantChatTransport } from "./AssistantChatTransport";
14
+
15
+ export type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =
16
+ ChatInit<UI_MESSAGE> & {
17
+ cloud?: AssistantCloud | undefined;
18
+ adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
19
+ };
20
+
21
+ export const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
22
+ options?: UseChatRuntimeOptions<UI_MESSAGE>,
23
+ ): AssistantRuntime => {
24
+ const {
25
+ adapters,
26
+ transport: transportOptions,
27
+ ...chatOptions
28
+ } = options ?? {};
29
+ const transport = transportOptions ?? new AssistantChatTransport();
30
+
31
+ // Get adapters from context (including history adapter from cloud)
32
+ const contextAdapters = useRuntimeAdapters();
33
+
34
+ const chat = useChat({
35
+ ...chatOptions,
36
+ transport,
37
+ });
38
+
39
+ const runtime = useAISDKRuntime(chat as any, {
40
+ adapters: {
41
+ ...contextAdapters,
42
+ ...adapters,
43
+ },
44
+ });
45
+ if (transport instanceof AssistantChatTransport) {
46
+ transport.setRuntime(runtime);
47
+ }
48
+
49
+ return runtime;
50
+ };
51
+
52
+ export const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({
53
+ cloud,
54
+ ...options
55
+ }: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {
56
+ const cloudAdapter = unstable_useCloudThreadListAdapter({ cloud });
57
+ return unstable_useRemoteThreadListRuntime({
58
+ runtimeHook: function RuntimeHook() {
59
+ return useChatThreadRuntime(options);
60
+ },
61
+ adapter: cloudAdapter,
62
+ });
63
+ };
@@ -0,0 +1,114 @@
1
+ "use client";
2
+
3
+ import {
4
+ AssistantRuntime,
5
+ ThreadHistoryAdapter,
6
+ ThreadMessage,
7
+ MessageFormatAdapter,
8
+ getExternalStoreMessages,
9
+ MessageFormatRepository,
10
+ ExportedMessageRepository,
11
+ INTERNAL,
12
+ } from "@assistant-ui/react";
13
+ import { useRef, useEffect, useState, RefObject } from "react";
14
+
15
+ const { MessageRepository } = INTERNAL;
16
+
17
+ export const toExportedMessageRepository = <TMessage,>(
18
+ toThreadMessages: (messages: TMessage[]) => ThreadMessage[],
19
+ messages: MessageFormatRepository<TMessage>,
20
+ ): ExportedMessageRepository => {
21
+ return {
22
+ headId: messages.headId!,
23
+ messages: messages.messages.map((m) => {
24
+ const message = toThreadMessages([m.message])[0]!;
25
+ return {
26
+ ...m,
27
+ message,
28
+ };
29
+ }),
30
+ };
31
+ };
32
+
33
+ export const useExternalHistory = <TMessage,>(
34
+ runtimeRef: RefObject<AssistantRuntime>,
35
+ historyAdapter: ThreadHistoryAdapter | undefined,
36
+ toThreadMessages: (messages: TMessage[]) => ThreadMessage[],
37
+ storageFormatAdapter: MessageFormatAdapter<TMessage, any>,
38
+ onSetMessages: (messages: TMessage[]) => void,
39
+ ) => {
40
+ const loadedRef = useRef(false);
41
+ const [isLoading, setIsLoading] = useState(true);
42
+ const historyIds = useRef(new Set<string>());
43
+
44
+ const onSetMessagesRef = useRef(onSetMessages);
45
+ useEffect(() => {
46
+ onSetMessagesRef.current = onSetMessages;
47
+ });
48
+
49
+ // Load messages from history adapter on mount
50
+ useEffect(() => {
51
+ if (!historyAdapter || loadedRef.current) return;
52
+
53
+ const loadHistory = async () => {
54
+ setIsLoading(true);
55
+ try {
56
+ const repo = await historyAdapter
57
+ .withFormat?.(storageFormatAdapter)
58
+ .load();
59
+ if (repo && repo.messages.length > 0) {
60
+ const converted = toExportedMessageRepository(toThreadMessages, repo);
61
+ runtimeRef.current.thread.import(converted);
62
+
63
+ const tempRepo = new MessageRepository();
64
+ tempRepo.import(converted);
65
+ const messages = tempRepo.getMessages();
66
+
67
+ onSetMessagesRef.current(
68
+ messages.map(getExternalStoreMessages<TMessage>).flat(),
69
+ );
70
+
71
+ historyIds.current = new Set(
72
+ converted.messages.map((m) => m.message.id),
73
+ );
74
+ }
75
+ } catch (error) {
76
+ console.error("Failed to load message history:", error);
77
+ } finally {
78
+ setIsLoading(false);
79
+ }
80
+ };
81
+
82
+ if (!loadedRef.current) {
83
+ loadedRef.current = true;
84
+ loadHistory();
85
+ }
86
+ }, [historyAdapter, storageFormatAdapter, toThreadMessages]);
87
+
88
+ useEffect(() => {
89
+ return runtimeRef.current.thread.subscribe(async () => {
90
+ const { messages, isRunning } = runtimeRef.current.thread.getState();
91
+ if (isRunning) return;
92
+
93
+ for (let i = 0; i < messages.length; i++) {
94
+ const message = messages[i]!;
95
+ if (
96
+ message.status === undefined ||
97
+ message.status.type === "complete" ||
98
+ message.status.type === "incomplete"
99
+ ) {
100
+ if (historyIds.current.has(message.id)) return;
101
+ historyIds.current.add(message.id);
102
+
103
+ const parentId = i > 0 ? messages[i - 1]!.id : null;
104
+ await historyAdapter?.withFormat?.(storageFormatAdapter).append({
105
+ parentId,
106
+ message: getExternalStoreMessages<TMessage>(message)[0]!,
107
+ });
108
+ }
109
+ }
110
+ });
111
+ }, []);
112
+
113
+ return isLoading;
114
+ };
@@ -2,6 +2,7 @@ import { AppendMessage } from "@assistant-ui/react";
2
2
  import {
3
3
  CreateUIMessage,
4
4
  FileUIPart,
5
+ generateId,
5
6
  UIDataTypes,
6
7
  UIMessage,
7
8
  UIMessagePart,
@@ -53,6 +54,7 @@ export const toCreateMessage = async (
53
54
  parts.push(...attachmentParts);
54
55
 
55
56
  return {
57
+ id: generateId(),
56
58
  role: message.role,
57
59
  parts,
58
60
  };