@assistant-ui/react-ai-sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +86 -0
- package/dist/index.js +68 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +468 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -7
- package/.turbo/turbo-build.log +0 -20
- package/src/core/VercelModelAdapter.tsx +0 -54
- package/src/core/index.ts +0 -1
- package/src/index.ts +0 -3
- package/src/rsc/VercelRSCAdapter.tsx +0 -18
- package/src/rsc/VercelRSCMessage.tsx +0 -9
- package/src/rsc/VercelRSCRuntime.tsx +0 -106
- package/src/rsc/getVercelRSCMessage.tsx +0 -11
- package/src/rsc/index.ts +0 -4
- package/src/rsc/useVercelRSCRuntime.tsx +0 -20
- package/src/rsc/useVercelRSCSync.tsx +0 -50
- package/src/ui/VercelAIRuntime.tsx +0 -169
- package/src/ui/getVercelAIMessage.tsx +0 -12
- package/src/ui/index.ts +0 -3
- package/src/ui/use-assistant/useVercelUseAssistantRuntime.tsx +0 -18
- package/src/ui/use-chat/useVercelUseChatRuntime.tsx +0 -16
- package/src/ui/utils/VercelHelpers.tsx +0 -3
- package/src/ui/utils/sliceMessagesUntil.tsx +0 -20
- package/src/ui/utils/useVercelAIComposerSync.tsx +0 -15
- package/src/ui/utils/useVercelAIThreadSync.tsx +0 -142
- package/src/utils/ThreadMessageConverter.ts +0 -24
- package/tsconfig.json +0 -11
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import type { AppendMessage } from "@assistant-ui/react";
|
|
3
|
-
import type { VercelRSCMessage } from "./VercelRSCMessage";
|
|
4
|
-
|
|
5
|
-
type RSCMessageConverter<T> = {
|
|
6
|
-
convertMessage: (message: T) => VercelRSCMessage;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
type VercelRSCAdapterBase<T> = {
|
|
10
|
-
messages: T[];
|
|
11
|
-
append: (message: AppendMessage) => Promise<void>;
|
|
12
|
-
edit?: (message: AppendMessage) => Promise<void>;
|
|
13
|
-
reload?: (parentId: string | null) => Promise<void>;
|
|
14
|
-
convertMessage?: (message: T) => VercelRSCMessage;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type VercelRSCAdapter<T = VercelRSCMessage> = VercelRSCAdapterBase<T> &
|
|
18
|
-
(T extends VercelRSCMessage ? object : RSCMessageConverter<T>);
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
AssistantRuntime,
|
|
5
|
-
ReactThreadRuntime,
|
|
6
|
-
Unsubscribe,
|
|
7
|
-
} from "@assistant-ui/react";
|
|
8
|
-
|
|
9
|
-
import type { AppendMessage, ThreadMessage } from "@assistant-ui/react";
|
|
10
|
-
import { ProxyConfigProvider } from "@assistant-ui/react/internal";
|
|
11
|
-
import { type StoreApi, type UseBoundStore, create } from "zustand";
|
|
12
|
-
import type { VercelRSCAdapter } from "./VercelRSCAdapter";
|
|
13
|
-
import type { VercelRSCMessage } from "./VercelRSCMessage";
|
|
14
|
-
import { useVercelRSCSync } from "./useVercelRSCSync";
|
|
15
|
-
|
|
16
|
-
const EMPTY_BRANCHES: readonly never[] = Object.freeze([]);
|
|
17
|
-
|
|
18
|
-
export class VercelRSCRuntime<T extends WeakKey = VercelRSCMessage>
|
|
19
|
-
extends ProxyConfigProvider
|
|
20
|
-
implements AssistantRuntime, ReactThreadRuntime
|
|
21
|
-
{
|
|
22
|
-
private useAdapter: UseBoundStore<StoreApi<{ adapter: VercelRSCAdapter<T> }>>;
|
|
23
|
-
|
|
24
|
-
private _subscriptions = new Set<() => void>();
|
|
25
|
-
|
|
26
|
-
public isRunning = false;
|
|
27
|
-
public messages: ThreadMessage[] = [];
|
|
28
|
-
|
|
29
|
-
constructor(public adapter: VercelRSCAdapter<T>) {
|
|
30
|
-
super();
|
|
31
|
-
|
|
32
|
-
this.useAdapter = create(() => ({
|
|
33
|
-
adapter,
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private withRunning = (callback: Promise<unknown>) => {
|
|
38
|
-
this.isRunning = true;
|
|
39
|
-
return callback.finally(() => {
|
|
40
|
-
this.isRunning = false;
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
public getBranches(): readonly string[] {
|
|
45
|
-
return EMPTY_BRANCHES;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public switchToBranch(): void {
|
|
49
|
-
throw new Error(
|
|
50
|
-
"Branch switching is not supported by VercelRSCAssistantProvider.",
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public async append(message: AppendMessage): Promise<void> {
|
|
55
|
-
if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
|
|
56
|
-
if (!this.adapter.edit)
|
|
57
|
-
throw new Error(
|
|
58
|
-
"Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider.",
|
|
59
|
-
);
|
|
60
|
-
await this.withRunning(this.adapter.edit(message));
|
|
61
|
-
} else {
|
|
62
|
-
await this.withRunning(this.adapter.append(message));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public async startRun(parentId: string | null): Promise<void> {
|
|
67
|
-
if (!this.adapter.reload)
|
|
68
|
-
throw new Error(
|
|
69
|
-
"Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider.",
|
|
70
|
-
);
|
|
71
|
-
await this.withRunning(this.adapter.reload(parentId));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
cancelRun(): void {
|
|
75
|
-
// in dev mode, log a warning
|
|
76
|
-
if (process.env["NODE_ENV"] === "development") {
|
|
77
|
-
console.warn(
|
|
78
|
-
"Run cancellation is not supported by VercelRSCAssistantProvider.",
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public subscribe(callback: () => void): Unsubscribe {
|
|
84
|
-
this._subscriptions.add(callback);
|
|
85
|
-
return () => this._subscriptions.delete(callback);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
public onAdapterUpdated() {
|
|
89
|
-
if (this.useAdapter.getState().adapter !== this.adapter) {
|
|
90
|
-
this.useAdapter.setState({ adapter: this.adapter });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private updateData = (messages: ThreadMessage[]) => {
|
|
95
|
-
this.messages = messages;
|
|
96
|
-
for (const callback of this._subscriptions) callback();
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
unstable_synchronizer = () => {
|
|
100
|
-
const { adapter } = this.useAdapter();
|
|
101
|
-
|
|
102
|
-
useVercelRSCSync(adapter, this.updateData);
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
};
|
|
106
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ThreadMessage } from "@assistant-ui/react";
|
|
2
|
-
|
|
3
|
-
export const symbolInnerRSCMessage = Symbol("innerVercelRSCMessage");
|
|
4
|
-
|
|
5
|
-
export type VercelRSCThreadMessage<T> = ThreadMessage & {
|
|
6
|
-
[symbolInnerRSCMessage]?: T;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const getVercelRSCMessage = <T,>(message: ThreadMessage) => {
|
|
10
|
-
return (message as VercelRSCThreadMessage<T>)[symbolInnerRSCMessage];
|
|
11
|
-
};
|
package/src/rsc/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useInsertionEffect, useState } from "react";
|
|
4
|
-
import type { VercelRSCAdapter } from "./VercelRSCAdapter";
|
|
5
|
-
import { VercelRSCRuntime } from "./VercelRSCRuntime";
|
|
6
|
-
|
|
7
|
-
export const useVercelRSCRuntime = <T extends WeakKey>(
|
|
8
|
-
adapter: VercelRSCAdapter<T>,
|
|
9
|
-
) => {
|
|
10
|
-
const [runtime] = useState(() => new VercelRSCRuntime(adapter));
|
|
11
|
-
|
|
12
|
-
useInsertionEffect(() => {
|
|
13
|
-
runtime.adapter = adapter;
|
|
14
|
-
});
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
runtime.onAdapterUpdated();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
return runtime;
|
|
20
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { ThreadMessage } from "@assistant-ui/react";
|
|
2
|
-
import { useEffect, useMemo } from "react";
|
|
3
|
-
import {
|
|
4
|
-
type ConverterCallback,
|
|
5
|
-
ThreadMessageConverter,
|
|
6
|
-
} from "../utils/ThreadMessageConverter";
|
|
7
|
-
import type { VercelRSCAdapter } from "./VercelRSCAdapter";
|
|
8
|
-
import type { VercelRSCMessage } from "./VercelRSCMessage";
|
|
9
|
-
import {
|
|
10
|
-
type VercelRSCThreadMessage,
|
|
11
|
-
symbolInnerRSCMessage,
|
|
12
|
-
} from "./getVercelRSCMessage";
|
|
13
|
-
|
|
14
|
-
const vercelToThreadMessage = <T,>(
|
|
15
|
-
converter: (message: T) => VercelRSCMessage,
|
|
16
|
-
rawMessage: T,
|
|
17
|
-
): VercelRSCThreadMessage<T> => {
|
|
18
|
-
const message = converter(rawMessage);
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
id: message.id,
|
|
22
|
-
role: message.role,
|
|
23
|
-
content: [{ type: "ui", display: message.display }],
|
|
24
|
-
createdAt: message.createdAt ?? new Date(),
|
|
25
|
-
...{ status: "done" },
|
|
26
|
-
[symbolInnerRSCMessage]: rawMessage,
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
type UpdateDataCallback = (messages: ThreadMessage[]) => void;
|
|
31
|
-
|
|
32
|
-
export const useVercelRSCSync = <T extends WeakKey>(
|
|
33
|
-
adapter: VercelRSCAdapter<T>,
|
|
34
|
-
updateData: UpdateDataCallback,
|
|
35
|
-
) => {
|
|
36
|
-
// flush the converter cache when the convertMessage prop changes
|
|
37
|
-
const [converter, convertCallback] = useMemo(() => {
|
|
38
|
-
const rscConverter =
|
|
39
|
-
adapter.convertMessage ?? ((m: T) => m as VercelRSCMessage);
|
|
40
|
-
const convertCallback: ConverterCallback<T> = (m, cache) => {
|
|
41
|
-
if (cache) return cache;
|
|
42
|
-
return vercelToThreadMessage(rscConverter, m);
|
|
43
|
-
};
|
|
44
|
-
return [new ThreadMessageConverter(), convertCallback];
|
|
45
|
-
}, [adapter.convertMessage]);
|
|
46
|
-
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
updateData(converter.convertMessages(adapter.messages, convertCallback));
|
|
49
|
-
}, [updateData, converter, convertCallback, adapter.messages]);
|
|
50
|
-
};
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AssistantRuntime,
|
|
3
|
-
ReactThreadRuntime,
|
|
4
|
-
Unsubscribe,
|
|
5
|
-
} from "@assistant-ui/react";
|
|
6
|
-
import type { AppendMessage, ThreadMessage } from "@assistant-ui/react";
|
|
7
|
-
import {
|
|
8
|
-
MessageRepository,
|
|
9
|
-
ProxyConfigProvider,
|
|
10
|
-
} from "@assistant-ui/react/internal";
|
|
11
|
-
import type { Message } from "ai";
|
|
12
|
-
import { type StoreApi, type UseBoundStore, create } from "zustand";
|
|
13
|
-
import { getVercelAIMessage } from "./getVercelAIMessage";
|
|
14
|
-
import type { VercelHelpers } from "./utils/VercelHelpers";
|
|
15
|
-
import { sliceMessagesUntil } from "./utils/sliceMessagesUntil";
|
|
16
|
-
import { useVercelAIComposerSync } from "./utils/useVercelAIComposerSync";
|
|
17
|
-
import { useVercelAIThreadSync } from "./utils/useVercelAIThreadSync";
|
|
18
|
-
|
|
19
|
-
const hasUpcomingMessage = (isRunning: boolean, messages: ThreadMessage[]) => {
|
|
20
|
-
return isRunning && messages[messages.length - 1]?.role !== "assistant";
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export class VercelAIRuntime
|
|
24
|
-
extends ProxyConfigProvider
|
|
25
|
-
implements AssistantRuntime, ReactThreadRuntime
|
|
26
|
-
{
|
|
27
|
-
private _subscriptions = new Set<() => void>();
|
|
28
|
-
private repository = new MessageRepository();
|
|
29
|
-
private assistantOptimisticId: string | null = null;
|
|
30
|
-
|
|
31
|
-
private useVercel: UseBoundStore<StoreApi<{ vercel: VercelHelpers }>>;
|
|
32
|
-
|
|
33
|
-
public messages: ThreadMessage[] = [];
|
|
34
|
-
public isRunning = false;
|
|
35
|
-
|
|
36
|
-
constructor(public vercel: VercelHelpers) {
|
|
37
|
-
super();
|
|
38
|
-
this.useVercel = create(() => ({
|
|
39
|
-
vercel,
|
|
40
|
-
}));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
public getBranches(messageId: string): string[] {
|
|
44
|
-
return this.repository.getBranches(messageId);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public switchToBranch(branchId: string): void {
|
|
48
|
-
this.repository.switchToBranch(branchId);
|
|
49
|
-
this.updateVercelMessages(this.repository.getMessages());
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public async append(message: AppendMessage): Promise<void> {
|
|
53
|
-
// add user message
|
|
54
|
-
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
|
55
|
-
throw new Error("Only text content is supported by Vercel AI SDK.");
|
|
56
|
-
|
|
57
|
-
const newMessages = sliceMessagesUntil(
|
|
58
|
-
this.vercel.messages,
|
|
59
|
-
message.parentId,
|
|
60
|
-
);
|
|
61
|
-
this.vercel.setMessages(newMessages);
|
|
62
|
-
|
|
63
|
-
await this.vercel.append({
|
|
64
|
-
role: "user",
|
|
65
|
-
content: message.content[0].text,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public async startRun(parentId: string | null): Promise<void> {
|
|
70
|
-
const reloadMaybe =
|
|
71
|
-
"reload" in this.vercel ? this.vercel.reload : undefined;
|
|
72
|
-
if (!reloadMaybe)
|
|
73
|
-
throw new Error(
|
|
74
|
-
"Reload is not supported by Vercel AI SDK's useAssistant.",
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const newMessages = sliceMessagesUntil(this.vercel.messages, parentId);
|
|
78
|
-
this.vercel.setMessages(newMessages);
|
|
79
|
-
|
|
80
|
-
await reloadMaybe();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public cancelRun(): void {
|
|
84
|
-
const previousMessage = this.vercel.messages.at(-1);
|
|
85
|
-
|
|
86
|
-
this.vercel.stop();
|
|
87
|
-
|
|
88
|
-
if (this.assistantOptimisticId) {
|
|
89
|
-
this.repository.deleteMessage(this.assistantOptimisticId);
|
|
90
|
-
this.assistantOptimisticId = null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
let messages = this.repository.getMessages();
|
|
94
|
-
if (
|
|
95
|
-
previousMessage?.role === "user" &&
|
|
96
|
-
previousMessage.id === messages.at(-1)?.id // ensure the previous message is a leaf node
|
|
97
|
-
) {
|
|
98
|
-
this.vercel.setInput(previousMessage.content);
|
|
99
|
-
this.repository.deleteMessage(previousMessage.id);
|
|
100
|
-
|
|
101
|
-
messages = this.repository.getMessages();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// resync messages
|
|
105
|
-
setTimeout(() => {
|
|
106
|
-
this.updateVercelMessages(messages);
|
|
107
|
-
}, 0);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
public subscribe(callback: () => void): Unsubscribe {
|
|
111
|
-
this._subscriptions.add(callback);
|
|
112
|
-
return () => this._subscriptions.delete(callback);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private updateVercelMessages = (messages: ThreadMessage[]) => {
|
|
116
|
-
this.vercel.setMessages(
|
|
117
|
-
messages
|
|
118
|
-
.flatMap(getVercelAIMessage)
|
|
119
|
-
.filter((m): m is Message => m != null),
|
|
120
|
-
);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
public onVercelUpdated() {
|
|
124
|
-
if (this.useVercel.getState().vercel !== this.vercel) {
|
|
125
|
-
this.useVercel.setState({ vercel: this.vercel });
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private updateData = (isRunning: boolean, vm: ThreadMessage[]) => {
|
|
130
|
-
for (let i = 0; i < vm.length; i++) {
|
|
131
|
-
const message = vm[i]!;
|
|
132
|
-
const parent = vm[i - 1];
|
|
133
|
-
this.repository.addOrUpdateMessage(parent?.id ?? null, message);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (this.assistantOptimisticId) {
|
|
137
|
-
this.repository.deleteMessage(this.assistantOptimisticId);
|
|
138
|
-
this.assistantOptimisticId = null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (hasUpcomingMessage(isRunning, vm)) {
|
|
142
|
-
this.assistantOptimisticId = this.repository.appendOptimisticMessage(
|
|
143
|
-
vm.at(-1)?.id ?? null,
|
|
144
|
-
{
|
|
145
|
-
role: "assistant",
|
|
146
|
-
content: [{ type: "text", text: "" }],
|
|
147
|
-
},
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
this.repository.resetHead(
|
|
152
|
-
this.assistantOptimisticId ?? vm.at(-1)?.id ?? null,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
this.messages = this.repository.getMessages();
|
|
156
|
-
this.isRunning = isRunning;
|
|
157
|
-
|
|
158
|
-
for (const callback of this._subscriptions) callback();
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
unstable_synchronizer = () => {
|
|
162
|
-
const { vercel } = this.useVercel();
|
|
163
|
-
|
|
164
|
-
useVercelAIThreadSync(vercel, this.updateData);
|
|
165
|
-
useVercelAIComposerSync(vercel);
|
|
166
|
-
|
|
167
|
-
return null;
|
|
168
|
-
};
|
|
169
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ThreadMessage } from "@assistant-ui/react";
|
|
2
|
-
import type { Message } from "ai";
|
|
3
|
-
|
|
4
|
-
export const symbolInnerAIMessage = Symbol("innerVercelAIUIMessage");
|
|
5
|
-
|
|
6
|
-
export type VercelAIThreadMessage = ThreadMessage & {
|
|
7
|
-
[symbolInnerAIMessage]?: Message[];
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const getVercelAIMessage = (message: ThreadMessage) => {
|
|
11
|
-
return (message as VercelAIThreadMessage)[symbolInnerAIMessage];
|
|
12
|
-
};
|
package/src/ui/index.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { UseAssistantHelpers } from "@ai-sdk/react";
|
|
2
|
-
import { useEffect, useInsertionEffect, useState } from "react";
|
|
3
|
-
import { VercelAIRuntime } from "../VercelAIRuntime";
|
|
4
|
-
|
|
5
|
-
export const useVercelUseAssistantRuntime = (
|
|
6
|
-
assistantHelpers: UseAssistantHelpers,
|
|
7
|
-
) => {
|
|
8
|
-
const [runtime] = useState(() => new VercelAIRuntime(assistantHelpers));
|
|
9
|
-
|
|
10
|
-
useInsertionEffect(() => {
|
|
11
|
-
runtime.vercel = assistantHelpers;
|
|
12
|
-
});
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
runtime.onVercelUpdated();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return runtime;
|
|
18
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { UseChatHelpers } from "@ai-sdk/react";
|
|
2
|
-
import { useEffect, useInsertionEffect, useState } from "react";
|
|
3
|
-
import { VercelAIRuntime } from "../VercelAIRuntime";
|
|
4
|
-
|
|
5
|
-
export const useVercelUseChatRuntime = (chatHelpers: UseChatHelpers) => {
|
|
6
|
-
const [runtime] = useState(() => new VercelAIRuntime(chatHelpers));
|
|
7
|
-
|
|
8
|
-
useInsertionEffect(() => {
|
|
9
|
-
runtime.vercel = chatHelpers;
|
|
10
|
-
});
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
runtime.onVercelUpdated();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return runtime;
|
|
16
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Message } from "ai";
|
|
2
|
-
|
|
3
|
-
export const sliceMessagesUntil = (
|
|
4
|
-
messages: Message[],
|
|
5
|
-
messageId: string | null,
|
|
6
|
-
) => {
|
|
7
|
-
if (messageId == null) return [];
|
|
8
|
-
|
|
9
|
-
let messageIdx = messages.findIndex((m) => m.id === messageId);
|
|
10
|
-
if (messageIdx === -1)
|
|
11
|
-
throw new Error(
|
|
12
|
-
"useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui.",
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
while (messages[messageIdx + 1]?.role === "assistant") {
|
|
16
|
-
messageIdx++;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return messages.slice(0, messageIdx + 1);
|
|
20
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { useThreadContext } from "@assistant-ui/react/experimental";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import type { VercelHelpers } from "./VercelHelpers";
|
|
4
|
-
|
|
5
|
-
// two way sync between vercel helpers input state and composer text state
|
|
6
|
-
export const useVercelAIComposerSync = (vercel: VercelHelpers) => {
|
|
7
|
-
const { useComposer } = useThreadContext();
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
useComposer.setState({
|
|
11
|
-
value: vercel.input,
|
|
12
|
-
setValue: vercel.setInput,
|
|
13
|
-
});
|
|
14
|
-
}, [useComposer, vercel.input, vercel.setInput]);
|
|
15
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import type { TextContentPart, ThreadMessage } from "@assistant-ui/react";
|
|
2
|
-
import type { ToolCallContentPart } from "@assistant-ui/react/experimental";
|
|
3
|
-
import type { Message } from "ai";
|
|
4
|
-
import { useEffect, useMemo } from "react";
|
|
5
|
-
import {
|
|
6
|
-
type ConverterCallback,
|
|
7
|
-
ThreadMessageConverter,
|
|
8
|
-
} from "../../utils/ThreadMessageConverter";
|
|
9
|
-
import {
|
|
10
|
-
type VercelAIThreadMessage,
|
|
11
|
-
symbolInnerAIMessage,
|
|
12
|
-
} from "../getVercelAIMessage";
|
|
13
|
-
import type { VercelHelpers } from "./VercelHelpers";
|
|
14
|
-
|
|
15
|
-
const getIsRunning = (vercel: VercelHelpers) => {
|
|
16
|
-
if ("isLoading" in vercel) return vercel.isLoading;
|
|
17
|
-
return vercel.status === "in_progress";
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const vercelToThreadMessage = (
|
|
21
|
-
messages: Message[],
|
|
22
|
-
status: "in_progress" | "done" | "error",
|
|
23
|
-
): VercelAIThreadMessage => {
|
|
24
|
-
const firstMessage = messages[0];
|
|
25
|
-
if (!firstMessage) throw new Error("No messages found");
|
|
26
|
-
|
|
27
|
-
const common = {
|
|
28
|
-
id: firstMessage.id,
|
|
29
|
-
createdAt: firstMessage.createdAt ?? new Date(),
|
|
30
|
-
[symbolInnerAIMessage]: messages,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
switch (firstMessage.role) {
|
|
34
|
-
case "user":
|
|
35
|
-
if (messages.length > 1) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
"Multiple user messages found. This is likely an internal bug in assistant-ui.",
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
...common,
|
|
43
|
-
role: "user",
|
|
44
|
-
content: [{ type: "text", text: firstMessage.content }],
|
|
45
|
-
};
|
|
46
|
-
case "assistant":
|
|
47
|
-
return {
|
|
48
|
-
...common,
|
|
49
|
-
role: "assistant",
|
|
50
|
-
content: messages.flatMap((message) => [
|
|
51
|
-
...(message.content
|
|
52
|
-
? [{ type: "text", text: message.content } as TextContentPart]
|
|
53
|
-
: []),
|
|
54
|
-
...(message.toolInvocations?.map(
|
|
55
|
-
(t) =>
|
|
56
|
-
({
|
|
57
|
-
type: "tool-call",
|
|
58
|
-
name: t.toolName,
|
|
59
|
-
args: t.args,
|
|
60
|
-
result: "result" in t ? t.result : undefined,
|
|
61
|
-
}) as ToolCallContentPart,
|
|
62
|
-
) ?? []),
|
|
63
|
-
]),
|
|
64
|
-
status,
|
|
65
|
-
};
|
|
66
|
-
default:
|
|
67
|
-
throw new Error(
|
|
68
|
-
`You have a message with an unsupported role. The role ${firstMessage.role} is not supported.`,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
type Chunk = [Message, ...Message[]];
|
|
74
|
-
const hasItems = (messages: Message[]): messages is Chunk =>
|
|
75
|
-
messages.length > 0;
|
|
76
|
-
|
|
77
|
-
const chunkedMessages = (messages: Message[]): Chunk[] => {
|
|
78
|
-
const chunks: Chunk[] = [];
|
|
79
|
-
let currentChunk: Message[] = [];
|
|
80
|
-
|
|
81
|
-
for (const message of messages) {
|
|
82
|
-
if (message.role === "assistant") {
|
|
83
|
-
currentChunk.push(message);
|
|
84
|
-
} else {
|
|
85
|
-
if (hasItems(currentChunk)) {
|
|
86
|
-
chunks.push(currentChunk);
|
|
87
|
-
currentChunk = [];
|
|
88
|
-
}
|
|
89
|
-
chunks.push([message]);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (hasItems(currentChunk)) {
|
|
94
|
-
chunks.push(currentChunk);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return chunks;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const shallowArrayEqual = (a: unknown[], b: unknown[]) => {
|
|
101
|
-
if (a.length !== b.length) return false;
|
|
102
|
-
for (let i = 0; i < a.length; i++) {
|
|
103
|
-
if (a[i] !== b[i]) return false;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
type UpdateDataCallback = (isRunning: boolean, vm: ThreadMessage[]) => void;
|
|
109
|
-
|
|
110
|
-
export const useVercelAIThreadSync = (
|
|
111
|
-
vercel: VercelHelpers,
|
|
112
|
-
updateData: UpdateDataCallback,
|
|
113
|
-
) => {
|
|
114
|
-
const isRunning = getIsRunning(vercel);
|
|
115
|
-
|
|
116
|
-
const converter = useMemo(() => new ThreadMessageConverter(), []);
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
const lastMessageId = vercel.messages.at(-1)?.id;
|
|
120
|
-
const convertCallback: ConverterCallback<Chunk> = (messages, cache) => {
|
|
121
|
-
const status =
|
|
122
|
-
lastMessageId === messages[0].id && isRunning ? "in_progress" : "done";
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
cache &&
|
|
126
|
-
shallowArrayEqual(cache.content, messages) &&
|
|
127
|
-
(cache.role === "user" || cache.status === status)
|
|
128
|
-
)
|
|
129
|
-
return cache;
|
|
130
|
-
|
|
131
|
-
return vercelToThreadMessage(messages, status);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const messages = converter.convertMessages(
|
|
135
|
-
chunkedMessages(vercel.messages),
|
|
136
|
-
convertCallback,
|
|
137
|
-
(m) => m[0],
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
updateData(isRunning, messages);
|
|
141
|
-
}, [updateData, isRunning, vercel.messages, converter]);
|
|
142
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ThreadMessage } from "@assistant-ui/react";
|
|
2
|
-
|
|
3
|
-
export type ConverterCallback<TIn> = (
|
|
4
|
-
message: TIn,
|
|
5
|
-
cache: ThreadMessage | undefined,
|
|
6
|
-
) => ThreadMessage;
|
|
7
|
-
|
|
8
|
-
export class ThreadMessageConverter {
|
|
9
|
-
private readonly cache = new WeakMap<WeakKey, ThreadMessage>();
|
|
10
|
-
|
|
11
|
-
convertMessages<TIn extends WeakKey>(
|
|
12
|
-
messages: TIn[],
|
|
13
|
-
converter: ConverterCallback<TIn>,
|
|
14
|
-
keyMapper: (m: TIn) => WeakKey = (key) => key,
|
|
15
|
-
): ThreadMessage[] {
|
|
16
|
-
return messages.map((m) => {
|
|
17
|
-
const key = keyMapper(m);
|
|
18
|
-
const cached = this.cache.get(key);
|
|
19
|
-
const newMessage = converter(m, cached);
|
|
20
|
-
this.cache.set(key, newMessage);
|
|
21
|
-
return newMessage;
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@assistant-ui/tsconfig/base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"paths": {
|
|
5
|
-
"@assistant-ui/*": ["../../packages/*/src"],
|
|
6
|
-
"@assistant-ui/react/*": ["../../packages/react/src/*"]
|
|
7
|
-
}
|
|
8
|
-
},
|
|
9
|
-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
10
|
-
"exclude": ["node_modules"]
|
|
11
|
-
}
|