@assistant-ui/react-ai-sdk 0.11.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/dist/ui/adapters/aiSDKFormatAdapter.d.ts +5 -0
- package/dist/ui/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/ui/adapters/aiSDKFormatAdapter.js +28 -0
- package/dist/ui/adapters/aiSDKFormatAdapter.js.map +1 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/use-chat/AssistantChatTransport.d.ts +8 -0
- package/dist/ui/use-chat/AssistantChatTransport.d.ts.map +1 -0
- package/dist/ui/use-chat/AssistantChatTransport.js +60 -0
- package/dist/ui/use-chat/AssistantChatTransport.js.map +1 -0
- package/dist/ui/use-chat/useAISDKRuntime.d.ts +5 -3
- package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useAISDKRuntime.js +29 -4
- package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.d.ts +11 -2
- package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
- package/dist/ui/use-chat/useChatRuntime.js +41 -4
- package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
- package/dist/ui/use-chat/useExternalHistory.d.ts +5 -0
- package/dist/ui/use-chat/useExternalHistory.d.ts.map +1 -0
- package/dist/ui/use-chat/useExternalHistory.js +84 -0
- package/dist/ui/use-chat/useExternalHistory.js.map +1 -0
- package/dist/ui/utils/toCreateMessage.d.ts.map +1 -1
- package/dist/ui/utils/toCreateMessage.js +4 -0
- package/dist/ui/utils/toCreateMessage.js.map +1 -1
- package/package.json +7 -3
- package/src/ui/adapters/aiSDKFormatAdapter.ts +42 -0
- package/src/ui/index.ts +1 -0
- package/src/ui/use-chat/AssistantChatTransport.tsx +70 -0
- package/src/ui/use-chat/useAISDKRuntime.tsx +37 -5
- package/src/ui/use-chat/useChatRuntime.tsx +57 -7
- package/src/ui/use-chat/useExternalHistory.tsx +114 -0
- 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":[]}
|
package/dist/ui/index.d.ts
CHANGED
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -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;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,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,7 +1,9 @@
|
|
|
1
1
|
// src/ui/index.ts
|
|
2
2
|
import { useAISDKRuntime } from "./use-chat/useAISDKRuntime.js";
|
|
3
3
|
import { useChatRuntime } from "./use-chat/useChatRuntime.js";
|
|
4
|
+
import { AssistantChatTransport } from "./use-chat/AssistantChatTransport.js";
|
|
4
5
|
export {
|
|
6
|
+
AssistantChatTransport,
|
|
5
7
|
useAISDKRuntime,
|
|
6
8
|
useChatRuntime
|
|
7
9
|
};
|
package/dist/ui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ui/index.ts"],"sourcesContent":["export { useAISDKRuntime } from \"./use-chat/useAISDKRuntime\";\nexport { useChatRuntime } from \"./use-chat/useChatRuntime\";\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;","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"]>
|
|
4
|
+
adapters?: (NonNullable<ExternalStoreAdapter["adapters"]> & {
|
|
5
|
+
history?: ThreadHistoryAdapter | undefined;
|
|
6
|
+
}) | undefined;
|
|
5
7
|
};
|
|
6
|
-
export declare const useAISDKRuntime: (chatHelpers: ReturnType<typeof useChat>, adapter?: AISDKRuntimeAdapter) =>
|
|
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;
|
|
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;AAU7B,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,qBAoElC,CAAC"}
|
|
@@ -1,24 +1,47 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/ui/use-chat/useAISDKRuntime.tsx
|
|
4
|
-
import {
|
|
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";
|
|
14
|
+
import { useMemo } from "react";
|
|
10
15
|
var useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
11
16
|
const messages = AISDKMessageConverter.useThreadMessages({
|
|
12
17
|
isRunning: chatHelpers.status === "submitted" || chatHelpers.status == "streaming",
|
|
13
18
|
messages: chatHelpers.messages
|
|
14
19
|
});
|
|
20
|
+
const isLoading = useExternalHistory(
|
|
21
|
+
useMemo(
|
|
22
|
+
() => ({
|
|
23
|
+
get current() {
|
|
24
|
+
return runtime;
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
[]
|
|
29
|
+
),
|
|
30
|
+
adapter.adapters?.history,
|
|
31
|
+
AISDKMessageConverter.toThreadMessages,
|
|
32
|
+
aiSDKV5FormatAdapter,
|
|
33
|
+
(messages2) => {
|
|
34
|
+
chatHelpers.setMessages(messages2);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
15
37
|
const runtime = useExternalStoreRuntime({
|
|
16
38
|
isRunning: chatHelpers.status === "submitted" || chatHelpers.status === "streaming",
|
|
17
39
|
messages,
|
|
18
40
|
setMessages: (messages2) => chatHelpers.setMessages(messages2.map(getVercelAIMessages).flat()),
|
|
19
41
|
onCancel: async () => chatHelpers.stop(),
|
|
20
42
|
onNew: async (message) => {
|
|
21
|
-
|
|
43
|
+
const createMessage = await toCreateMessage(message);
|
|
44
|
+
await chatHelpers.sendMessage(createMessage);
|
|
22
45
|
},
|
|
23
46
|
onEdit: async (message) => {
|
|
24
47
|
const newMessages = sliceMessagesUntil(
|
|
@@ -26,7 +49,8 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
26
49
|
message.parentId
|
|
27
50
|
);
|
|
28
51
|
chatHelpers.setMessages(newMessages);
|
|
29
|
-
|
|
52
|
+
const createMessage = await toCreateMessage(message);
|
|
53
|
+
await chatHelpers.sendMessage(createMessage);
|
|
30
54
|
},
|
|
31
55
|
onReload: async (parentId) => {
|
|
32
56
|
const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);
|
|
@@ -43,7 +67,8 @@ var useAISDKRuntime = (chatHelpers, adapter = {}) => {
|
|
|
43
67
|
adapters: {
|
|
44
68
|
attachments: vercelAttachmentAdapter,
|
|
45
69
|
...adapter.adapters
|
|
46
|
-
}
|
|
70
|
+
},
|
|
71
|
+
isLoading
|
|
47
72
|
});
|
|
48
73
|
return runtime;
|
|
49
74
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/use-chat/useAISDKRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport type { useChat } from \"@ai-sdk/react\";\nimport {
|
|
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\";\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 = (\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 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,\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;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,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"]}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
import { type UIMessage
|
|
2
|
-
|
|
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;
|
|
3
12
|
//# sourceMappingURL=useChatRuntime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,
|
|
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"}
|
|
@@ -2,13 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
// src/ui/use-chat/useChatRuntime.tsx
|
|
4
4
|
import { useChat } from "@ai-sdk/react";
|
|
5
|
+
import {
|
|
6
|
+
unstable_useCloudThreadListAdapter,
|
|
7
|
+
unstable_useRemoteThreadListRuntime,
|
|
8
|
+
useRuntimeAdapters
|
|
9
|
+
} from "@assistant-ui/react";
|
|
5
10
|
import { useAISDKRuntime } from "./useAISDKRuntime.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
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
|
+
}
|
|
9
33
|
return runtime;
|
|
10
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
|
+
};
|
|
11
47
|
export {
|
|
12
|
-
useChatRuntime
|
|
48
|
+
useChatRuntime,
|
|
49
|
+
useChatThreadRuntime
|
|
13
50
|
};
|
|
14
51
|
//# sourceMappingURL=useChatRuntime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/use-chat/useChatRuntime.tsx"],"sourcesContent":["\"use client\";\n\nimport { useChat, type UIMessage
|
|
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
|
+
}, [historyAdapter, storageFormatAdapter]);
|
|
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<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]);\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]);\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,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,gBAAgB,oBAAoB,CAAC;AAEzC,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,
|
|
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":";
|
|
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.
|
|
3
|
+
"version": "1.0.0",
|
|
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.
|
|
32
|
+
"@assistant-ui/react": "^0.10.40",
|
|
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,7 +49,7 @@
|
|
|
45
49
|
"eslint-config-next": "15.4.5",
|
|
46
50
|
"react": "19.1.1",
|
|
47
51
|
"tsx": "^4.20.3",
|
|
48
|
-
"@assistant-ui/react": "0.10.
|
|
52
|
+
"@assistant-ui/react": "0.10.40",
|
|
49
53
|
"@assistant-ui/x-buildutils": "0.0.1"
|
|
50
54
|
},
|
|
51
55
|
"publishConfig": {
|
|
@@ -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
|
@@ -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,27 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { useChat } from "@ai-sdk/react";
|
|
4
|
-
import {
|
|
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";
|
|
17
|
+
import { useMemo } from "react";
|
|
11
18
|
|
|
12
19
|
export type AISDKRuntimeAdapter = {
|
|
13
|
-
adapters?:
|
|
20
|
+
adapters?:
|
|
21
|
+
| (NonNullable<ExternalStoreAdapter["adapters"]> & {
|
|
22
|
+
history?: ThreadHistoryAdapter | undefined;
|
|
23
|
+
})
|
|
24
|
+
| undefined;
|
|
14
25
|
};
|
|
15
26
|
|
|
16
27
|
export const useAISDKRuntime = (
|
|
@@ -23,6 +34,24 @@ export const useAISDKRuntime = (
|
|
|
23
34
|
messages: chatHelpers.messages,
|
|
24
35
|
});
|
|
25
36
|
|
|
37
|
+
const isLoading = useExternalHistory(
|
|
38
|
+
useMemo(
|
|
39
|
+
() => ({
|
|
40
|
+
get current(): AssistantRuntime {
|
|
41
|
+
return runtime;
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
+
[],
|
|
46
|
+
),
|
|
47
|
+
adapter.adapters?.history,
|
|
48
|
+
AISDKMessageConverter.toThreadMessages,
|
|
49
|
+
aiSDKV5FormatAdapter,
|
|
50
|
+
(messages) => {
|
|
51
|
+
chatHelpers.setMessages(messages);
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
26
55
|
const runtime = useExternalStoreRuntime({
|
|
27
56
|
isRunning:
|
|
28
57
|
chatHelpers.status === "submitted" || chatHelpers.status === "streaming",
|
|
@@ -31,7 +60,8 @@ export const useAISDKRuntime = (
|
|
|
31
60
|
chatHelpers.setMessages(messages.map(getVercelAIMessages).flat()),
|
|
32
61
|
onCancel: async () => chatHelpers.stop(),
|
|
33
62
|
onNew: async (message) => {
|
|
34
|
-
|
|
63
|
+
const createMessage = await toCreateMessage(message);
|
|
64
|
+
await chatHelpers.sendMessage(createMessage);
|
|
35
65
|
},
|
|
36
66
|
onEdit: async (message) => {
|
|
37
67
|
const newMessages = sliceMessagesUntil(
|
|
@@ -40,7 +70,8 @@ export const useAISDKRuntime = (
|
|
|
40
70
|
);
|
|
41
71
|
chatHelpers.setMessages(newMessages);
|
|
42
72
|
|
|
43
|
-
|
|
73
|
+
const createMessage = await toCreateMessage(message);
|
|
74
|
+
await chatHelpers.sendMessage(createMessage);
|
|
44
75
|
},
|
|
45
76
|
onReload: async (parentId: string | null) => {
|
|
46
77
|
const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);
|
|
@@ -59,6 +90,7 @@ export const useAISDKRuntime = (
|
|
|
59
90
|
attachments: vercelAttachmentAdapter,
|
|
60
91
|
...adapter.adapters,
|
|
61
92
|
},
|
|
93
|
+
isLoading,
|
|
62
94
|
});
|
|
63
95
|
|
|
64
96
|
return runtime;
|
|
@@ -1,13 +1,63 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useChat, type UIMessage
|
|
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";
|
|
4
14
|
|
|
5
|
-
|
|
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
|
+
}
|
|
6
48
|
|
|
7
|
-
export const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
|
|
8
|
-
options?: UseChatOptions<UI_MESSAGE>,
|
|
9
|
-
) => {
|
|
10
|
-
const chat = useChat(options);
|
|
11
|
-
const runtime = useAISDKRuntime(chat as any);
|
|
12
49
|
return runtime;
|
|
13
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<typeof onSetMessages>(() => 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
|
+
}, [historyAdapter, storageFormatAdapter]);
|
|
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
|
};
|