@assistant-ui/react 0.11.5 → 0.11.7

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 (168) hide show
  1. package/dist/client/AssistantClient.d.ts.map +1 -1
  2. package/dist/client/AssistantClient.js +1 -2
  3. package/dist/client/AssistantClient.js.map +1 -1
  4. package/dist/client/ToolUIClient.d.ts +3 -2
  5. package/dist/client/ToolUIClient.d.ts.map +1 -1
  6. package/dist/client/ToolUIClient.js +1 -5
  7. package/dist/client/ToolUIClient.js.map +1 -1
  8. package/dist/context/providers/AttachmentProvider.js +3 -3
  9. package/dist/context/providers/AttachmentProvider.js.map +1 -1
  10. package/dist/context/providers/MessageProvider.js +2 -2
  11. package/dist/context/providers/MessageProvider.js.map +1 -1
  12. package/dist/context/providers/PartProvider.js +2 -2
  13. package/dist/context/providers/PartProvider.js.map +1 -1
  14. package/dist/context/providers/TextMessagePartProvider.d.ts.map +1 -1
  15. package/dist/context/providers/TextMessagePartProvider.js +6 -7
  16. package/dist/context/providers/TextMessagePartProvider.js.map +1 -1
  17. package/dist/context/providers/ThreadListItemProvider.js +3 -3
  18. package/dist/context/providers/ThreadListItemProvider.js.map +1 -1
  19. package/dist/context/react/AssistantApiContext.d.ts +1 -1
  20. package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
  21. package/dist/context/react/AssistantApiContext.js +4 -3
  22. package/dist/context/react/AssistantApiContext.js.map +1 -1
  23. package/dist/context/react/index.d.ts +1 -0
  24. package/dist/context/react/index.d.ts.map +1 -1
  25. package/dist/context/react/index.js +2 -0
  26. package/dist/context/react/index.js.map +1 -1
  27. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
  28. package/dist/legacy-runtime/AssistantRuntimeProvider.js +1 -5
  29. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  30. package/dist/legacy-runtime/client/AttachmentRuntimeClient.d.ts +2 -4
  31. package/dist/legacy-runtime/client/AttachmentRuntimeClient.d.ts.map +1 -1
  32. package/dist/legacy-runtime/client/AttachmentRuntimeClient.js +10 -10
  33. package/dist/legacy-runtime/client/AttachmentRuntimeClient.js.map +1 -1
  34. package/dist/legacy-runtime/client/ComposerRuntimeClient.d.ts +3 -2
  35. package/dist/legacy-runtime/client/ComposerRuntimeClient.d.ts.map +1 -1
  36. package/dist/legacy-runtime/client/ComposerRuntimeClient.js +1 -5
  37. package/dist/legacy-runtime/client/ComposerRuntimeClient.js.map +1 -1
  38. package/dist/legacy-runtime/client/MessagePartRuntimeClient.d.ts +1 -1
  39. package/dist/legacy-runtime/client/MessagePartRuntimeClient.d.ts.map +1 -1
  40. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js +10 -10
  41. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js.map +1 -1
  42. package/dist/legacy-runtime/client/MessageRuntimeClient.d.ts +3 -3
  43. package/dist/legacy-runtime/client/MessageRuntimeClient.d.ts.map +1 -1
  44. package/dist/legacy-runtime/client/MessageRuntimeClient.js +31 -31
  45. package/dist/legacy-runtime/client/MessageRuntimeClient.js.map +1 -1
  46. package/dist/legacy-runtime/client/ThreadListItemRuntimeClient.d.ts +2 -2
  47. package/dist/legacy-runtime/client/ThreadListItemRuntimeClient.d.ts.map +1 -1
  48. package/dist/legacy-runtime/client/ThreadListItemRuntimeClient.js +17 -17
  49. package/dist/legacy-runtime/client/ThreadListItemRuntimeClient.js.map +1 -1
  50. package/dist/legacy-runtime/client/ThreadListRuntimeClient.d.ts +3 -2
  51. package/dist/legacy-runtime/client/ThreadListRuntimeClient.d.ts.map +1 -1
  52. package/dist/legacy-runtime/client/ThreadListRuntimeClient.js +1 -5
  53. package/dist/legacy-runtime/client/ThreadListRuntimeClient.js.map +1 -1
  54. package/dist/legacy-runtime/client/ThreadRuntimeClient.d.ts +3 -2
  55. package/dist/legacy-runtime/client/ThreadRuntimeClient.d.ts.map +1 -1
  56. package/dist/legacy-runtime/client/ThreadRuntimeClient.js +1 -5
  57. package/dist/legacy-runtime/client/ThreadRuntimeClient.js.map +1 -1
  58. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts +8 -1
  59. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts.map +1 -1
  60. package/dist/legacy-runtime/runtime/ThreadRuntime.js +5 -1
  61. package/dist/legacy-runtime/runtime/ThreadRuntime.js.map +1 -1
  62. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +12 -0
  63. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -0
  64. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +52 -0
  65. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -0
  66. package/dist/legacy-runtime/runtime-cores/assistant-transport/index.d.ts +3 -0
  67. package/dist/legacy-runtime/runtime-cores/assistant-transport/index.d.ts.map +1 -0
  68. package/dist/legacy-runtime/runtime-cores/assistant-transport/index.js +6 -0
  69. package/dist/legacy-runtime/runtime-cores/assistant-transport/index.js.map +1 -0
  70. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts +11 -0
  71. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -0
  72. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +55 -0
  73. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -0
  74. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +66 -0
  75. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -0
  76. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.js +1 -0
  77. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.js.map +1 -0
  78. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts +7 -0
  79. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  80. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +185 -0
  81. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  82. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +3 -0
  83. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts.map +1 -0
  84. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +12 -0
  85. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -0
  86. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts +4 -0
  87. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts.map +1 -0
  88. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +13 -0
  89. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js.map +1 -0
  90. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +13 -0
  91. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts.map +1 -0
  92. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +138 -0
  93. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js.map +1 -0
  94. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts +13 -0
  95. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.d.ts.map +1 -0
  96. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js +32 -0
  97. package/dist/legacy-runtime/runtime-cores/assistant-transport/utils.js.map +1 -0
  98. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts +1 -0
  99. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts.map +1 -1
  100. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.js.map +1 -1
  101. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts +2 -1
  102. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts.map +1 -1
  103. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts +3 -1
  104. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts.map +1 -1
  105. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts +3 -2
  106. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts.map +1 -1
  107. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js +9 -2
  108. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js.map +1 -1
  109. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.d.ts.map +1 -1
  110. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.js +2 -2
  111. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.js.map +1 -1
  112. package/dist/legacy-runtime/runtime-cores/index.d.ts +1 -0
  113. package/dist/legacy-runtime/runtime-cores/index.d.ts.map +1 -1
  114. package/dist/legacy-runtime/runtime-cores/index.js +1 -0
  115. package/dist/legacy-runtime/runtime-cores/index.js.map +1 -1
  116. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts +1 -0
  117. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts.map +1 -1
  118. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.js +5 -0
  119. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.js.map +1 -1
  120. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.d.ts.map +1 -1
  121. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.js +3 -0
  122. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.js.map +1 -1
  123. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts +2 -0
  124. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  125. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts +2 -0
  126. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  127. package/dist/utils/tap-store/tap-api.d.ts +9 -1
  128. package/dist/utils/tap-store/tap-api.d.ts.map +1 -1
  129. package/dist/utils/tap-store/tap-api.js +12 -2
  130. package/dist/utils/tap-store/tap-api.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/client/AssistantClient.ts +1 -4
  133. package/src/client/ToolUIClient.ts +1 -6
  134. package/src/context/providers/AttachmentProvider.tsx +3 -3
  135. package/src/context/providers/MessageProvider.tsx +2 -2
  136. package/src/context/providers/PartProvider.tsx +2 -2
  137. package/src/context/providers/TextMessagePartProvider.tsx +5 -7
  138. package/src/context/providers/ThreadListItemProvider.tsx +3 -3
  139. package/src/context/react/AssistantApiContext.tsx +7 -2
  140. package/src/context/react/index.ts +2 -0
  141. package/src/legacy-runtime/AssistantRuntimeProvider.tsx +1 -15
  142. package/src/legacy-runtime/client/AttachmentRuntimeClient.ts +10 -11
  143. package/src/legacy-runtime/client/ComposerRuntimeClient.ts +1 -6
  144. package/src/legacy-runtime/client/MessagePartRuntimeClient.ts +13 -14
  145. package/src/legacy-runtime/client/MessageRuntimeClient.ts +35 -36
  146. package/src/legacy-runtime/client/ThreadListItemRuntimeClient.ts +17 -18
  147. package/src/legacy-runtime/client/ThreadListRuntimeClient.ts +1 -6
  148. package/src/legacy-runtime/client/ThreadRuntimeClient.ts +1 -6
  149. package/src/legacy-runtime/runtime/ThreadRuntime.ts +14 -2
  150. package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +62 -0
  151. package/src/legacy-runtime/runtime-cores/assistant-transport/index.ts +2 -0
  152. package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +67 -0
  153. package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +96 -0
  154. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +125 -0
  155. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +232 -0
  156. package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +18 -0
  157. package/src/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.ts +9 -0
  158. package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +173 -0
  159. package/src/legacy-runtime/runtime-cores/assistant-transport/utils.ts +42 -0
  160. package/src/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.tsx +1 -0
  161. package/src/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.tsx +2 -1
  162. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.tsx +3 -0
  163. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.tsx +13 -2
  164. package/src/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.tsx +2 -5
  165. package/src/legacy-runtime/runtime-cores/index.ts +1 -0
  166. package/src/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.tsx +6 -0
  167. package/src/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.tsx +4 -0
  168. package/src/utils/tap-store/tap-api.ts +19 -2
@@ -0,0 +1,232 @@
1
+ "use client";
2
+
3
+ import {
4
+ ReadonlyJSONObject,
5
+ ReadonlyJSONValue,
6
+ asAsyncIterableStream,
7
+ } from "assistant-stream/utils";
8
+ import { AppendMessage } from "../../../types";
9
+ import { useExternalStoreRuntime } from "../external-store/useExternalStoreRuntime";
10
+ import { AssistantRuntime } from "../../runtime/AssistantRuntime";
11
+ import { AddToolResultOptions } from "../core";
12
+ import { useState, useRef, useMemo } from "react";
13
+ import {
14
+ AssistantMessageAccumulator,
15
+ DataStreamDecoder,
16
+ unstable_createInitialMessage as createInitialMessage,
17
+ } from "assistant-stream";
18
+ import {
19
+ AssistantTransportOptions,
20
+ AddMessageCommand,
21
+ AddToolResultCommand,
22
+ UserMessagePart,
23
+ QueuedCommand,
24
+ } from "./types";
25
+ import { useCommandQueue } from "./commandQueue";
26
+ import { useRunManager } from "./runManager";
27
+ import { useConvertedState } from "./useConvertedState";
28
+ import { useToolInvocations } from "./useToolInvocations";
29
+ import { toAISDKTools, getEnabledTools, createRequestHeaders } from "./utils";
30
+ import { useRemoteThreadListRuntime } from "../remote-thread-list/useRemoteThreadListRuntime";
31
+ import { InMemoryThreadListAdapter } from "../remote-thread-list/adapter/in-memory";
32
+
33
+ const useAssistantTransportThreadRuntime = <T,>(
34
+ options: AssistantTransportOptions<T>,
35
+ ): AssistantRuntime => {
36
+ const agentStateRef = useRef(options.initialState);
37
+ const [, rerender] = useState(0);
38
+ const resumeFlagRef = useRef(false);
39
+ const commandQueue = useCommandQueue({
40
+ onQueue: () => runManager.schedule(),
41
+ });
42
+
43
+ const runManager = useRunManager({
44
+ onRun: async (signal: AbortSignal) => {
45
+ const isResume = resumeFlagRef.current;
46
+ resumeFlagRef.current = false;
47
+ const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();
48
+ if (commands.length === 0 && !isResume)
49
+ throw new Error("No commands to send");
50
+
51
+ const headers = await createRequestHeaders(options.headers);
52
+ const context = runtime.thread.getModelContext();
53
+
54
+ const response = await fetch(
55
+ isResume ? options.resumeApi! : options.api,
56
+ {
57
+ method: "POST",
58
+ headers,
59
+ body: JSON.stringify({
60
+ commands,
61
+ state: agentStateRef.current,
62
+ system: context.system,
63
+ tools: context.tools
64
+ ? toAISDKTools(getEnabledTools(context.tools))
65
+ : undefined,
66
+ ...context.callSettings,
67
+ ...context.config,
68
+ ...options.body,
69
+ }),
70
+ signal,
71
+ },
72
+ );
73
+
74
+ options.onResponse?.(response);
75
+
76
+ if (!response.ok) {
77
+ throw new Error(`Status ${response.status}: ${await response.text()}`);
78
+ }
79
+
80
+ if (!response.body) {
81
+ throw new Error("Response body is null");
82
+ }
83
+
84
+ const stream = response.body
85
+ .pipeThrough(new DataStreamDecoder())
86
+ .pipeThrough(
87
+ new AssistantMessageAccumulator({
88
+ initialMessage: createInitialMessage({
89
+ unstable_state:
90
+ (agentStateRef.current as ReadonlyJSONValue) ?? null,
91
+ }),
92
+ }),
93
+ );
94
+
95
+ let markedDelivered = false;
96
+
97
+ for await (const chunk of asAsyncIterableStream(stream)) {
98
+ if (chunk.metadata.unstable_state === agentStateRef.current) continue;
99
+
100
+ if (!markedDelivered) {
101
+ commandQueue.markDelivered();
102
+ markedDelivered = true;
103
+ }
104
+
105
+ agentStateRef.current = chunk.metadata.unstable_state as T;
106
+ rerender((prev) => prev + 1);
107
+ }
108
+ },
109
+ onFinish: options.onFinish,
110
+ onError: (error) => {
111
+ if (error instanceof Error && error.name === "AbortError") {
112
+ const cmds = [
113
+ ...commandQueue.state.inTransit,
114
+ ...commandQueue.state.queued,
115
+ ];
116
+ options.onCancel?.({
117
+ commands: cmds,
118
+ updateState: (updater) => {
119
+ agentStateRef.current = updater(agentStateRef.current);
120
+ rerender((prev) => prev + 1);
121
+ },
122
+ });
123
+
124
+ commandQueue.reset();
125
+ } else {
126
+ const cmds = [...commandQueue.state.inTransit];
127
+ options.onError?.(error as Error, {
128
+ commands: cmds,
129
+ updateState: (updater) => {
130
+ agentStateRef.current = updater(agentStateRef.current);
131
+ rerender((prev) => prev + 1);
132
+ },
133
+ });
134
+ commandQueue.markDelivered();
135
+ }
136
+ },
137
+ });
138
+
139
+ // Reactive conversion of agent state + connection metadata → UI state
140
+ const pendingCommands = useMemo(
141
+ () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],
142
+ [commandQueue.state],
143
+ );
144
+ const converted = useConvertedState(
145
+ options.converter,
146
+ agentStateRef.current,
147
+ pendingCommands,
148
+ runManager.isRunning,
149
+ );
150
+
151
+ // Create runtime
152
+ const runtime = useExternalStoreRuntime({
153
+ isRunning: converted.isRunning,
154
+ messages: converted.messages,
155
+ adapters: options.adapters,
156
+ onNew: async (message: AppendMessage): Promise<void> => {
157
+ // Convert AppendMessage to AddMessageCommand
158
+ const parts: UserMessagePart[] = [];
159
+
160
+ for (const content of message.content) {
161
+ if (content.type === "text") {
162
+ parts.push({ type: "text", text: content.text });
163
+ } else if (content.type === "image") {
164
+ parts.push({ type: "image", image: content.image });
165
+ }
166
+ }
167
+
168
+ const command: AddMessageCommand = {
169
+ type: "add-message",
170
+ message: {
171
+ role: "user",
172
+ parts,
173
+ },
174
+ };
175
+
176
+ commandQueue.enqueue(command);
177
+ },
178
+ onCancel: async () => {
179
+ runManager.cancel();
180
+ toolInvocations.abort();
181
+ },
182
+ onResume: async () => {
183
+ if (!options.resumeApi)
184
+ throw new Error("Must pass resumeApi to options to resume runs");
185
+
186
+ resumeFlagRef.current = true;
187
+ runManager.schedule();
188
+ },
189
+ onAddToolResult: async (
190
+ toolOptions: AddToolResultOptions,
191
+ ): Promise<void> => {
192
+ const command: AddToolResultCommand = {
193
+ type: "add-tool-result",
194
+ toolCallId: toolOptions.toolCallId,
195
+ result: toolOptions.result as ReadonlyJSONObject,
196
+ toolName: toolOptions.toolName,
197
+ isError: toolOptions.isError,
198
+ ...(toolOptions.artifact && { artifact: toolOptions.artifact }),
199
+ };
200
+
201
+ commandQueue.enqueue(command);
202
+ },
203
+ onLoadExternalState: async (state) => {
204
+ agentStateRef.current = state as T;
205
+ toolInvocations.reset();
206
+ rerender((prev) => prev + 1);
207
+ },
208
+ });
209
+
210
+ const toolInvocations = useToolInvocations({
211
+ state: converted,
212
+ getTools: () => runtime.thread.getModelContext().tools,
213
+ onResult: commandQueue.enqueue,
214
+ });
215
+
216
+ return runtime;
217
+ };
218
+
219
+ /**
220
+ * @alpha This is an experimental API that is subject to change.
221
+ */
222
+ export const useAssistantTransportRuntime = <T,>(
223
+ options: AssistantTransportOptions<T>,
224
+ ): AssistantRuntime => {
225
+ const runtime = useRemoteThreadListRuntime({
226
+ runtimeHook: function RuntimeHook() {
227
+ return useAssistantTransportThreadRuntime(options);
228
+ },
229
+ adapter: new InMemoryThreadListAdapter(),
230
+ });
231
+ return runtime;
232
+ };
@@ -0,0 +1,18 @@
1
+ import { useMemo } from "react";
2
+ import type {
3
+ AssistantTransportCommand,
4
+ AssistantTransportState,
5
+ AssistantTransportStateConverter,
6
+ } from "./types";
7
+
8
+ export function useConvertedState<T>(
9
+ converter: AssistantTransportStateConverter<T>,
10
+ agentState: T,
11
+ pendingCommands: AssistantTransportCommand[],
12
+ isSending: boolean,
13
+ ): AssistantTransportState {
14
+ return useMemo(
15
+ () => converter(agentState, { pendingCommands, isSending }),
16
+ [converter, agentState, pendingCommands, isSending],
17
+ );
18
+ }
@@ -0,0 +1,9 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ export function useLatestRef<T>(value: T) {
4
+ const ref = useRef(value);
5
+ useEffect(() => {
6
+ ref.current = value;
7
+ }, [value]);
8
+ return ref as { current: T };
9
+ }
@@ -0,0 +1,173 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import {
3
+ createAssistantStreamController,
4
+ ToolCallStreamController,
5
+ ToolResponse,
6
+ unstable_toolResultStream,
7
+ type Tool,
8
+ } from "assistant-stream";
9
+ import type {
10
+ AssistantTransportCommand,
11
+ AssistantTransportState,
12
+ } from "./types";
13
+ import {
14
+ AssistantMetaTransformStream,
15
+ ReadonlyJSONValue,
16
+ } from "assistant-stream/utils";
17
+
18
+ const isArgsTextComplete = (argsText: string) => {
19
+ try {
20
+ JSON.parse(argsText);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ };
26
+
27
+ type UseToolInvocationsParams = {
28
+ state: AssistantTransportState;
29
+ getTools: () => Record<string, Tool> | undefined;
30
+ onResult: (command: AssistantTransportCommand) => void;
31
+ };
32
+
33
+ export function useToolInvocations({
34
+ state,
35
+ getTools,
36
+ onResult,
37
+ }: UseToolInvocationsParams) {
38
+ const lastToolStates = useRef<
39
+ Record<
40
+ string,
41
+ {
42
+ argsText: string;
43
+ hasResult: boolean;
44
+ controller: ToolCallStreamController;
45
+ }
46
+ >
47
+ >({});
48
+
49
+ const acRef = useRef<AbortController>(new AbortController());
50
+ const [controller] = useState(() => {
51
+ const [stream, controller] = createAssistantStreamController();
52
+ const transform = unstable_toolResultStream(
53
+ getTools,
54
+ () => acRef.current?.signal ?? new AbortController().signal,
55
+ );
56
+ stream
57
+ .pipeThrough(transform)
58
+ .pipeThrough(new AssistantMetaTransformStream())
59
+ .pipeTo(
60
+ new WritableStream({
61
+ write(chunk) {
62
+ if (chunk.type === "result") {
63
+ // the tool call result was already set by the backend
64
+ if (lastToolStates.current[chunk.meta.toolCallId]?.hasResult)
65
+ return;
66
+
67
+ onResult({
68
+ type: "add-tool-result",
69
+ toolCallId: chunk.meta.toolCallId,
70
+ toolName: chunk.meta.toolName,
71
+ result: chunk.result,
72
+ isError: chunk.isError,
73
+ ...(chunk.artifact && { artifact: chunk.artifact }),
74
+ });
75
+ }
76
+ },
77
+ }),
78
+ );
79
+
80
+ return controller;
81
+ });
82
+
83
+ const ignoredToolIds = useRef<Set<string>>(new Set());
84
+ const isInititialState = useRef(true);
85
+
86
+ useEffect(() => {
87
+ if (isInititialState.current) {
88
+ state.messages.forEach((message) => {
89
+ message.content.forEach((content) => {
90
+ if (content.type === "tool-call") {
91
+ ignoredToolIds.current.add(content.toolCallId);
92
+ }
93
+ });
94
+ });
95
+ isInititialState.current = false;
96
+ } else {
97
+ state.messages.forEach((message) => {
98
+ message.content.forEach((content) => {
99
+ if (content.type === "tool-call") {
100
+ if (ignoredToolIds.current.has(content.toolCallId)) {
101
+ return;
102
+ }
103
+ let lastState = lastToolStates.current[content.toolCallId];
104
+ if (!lastState) {
105
+ const toolCallController = controller.addToolCallPart({
106
+ toolName: content.toolName,
107
+ toolCallId: content.toolCallId,
108
+ });
109
+ lastState = {
110
+ argsText: "",
111
+ hasResult: false,
112
+ controller: toolCallController,
113
+ };
114
+ lastToolStates.current[content.toolCallId] = lastState;
115
+ }
116
+
117
+ if (content.argsText !== lastState.argsText) {
118
+ if (!content.argsText.startsWith(lastState.argsText)) {
119
+ throw new Error(
120
+ `Tool call argsText can only be appended, not updated: ${content.argsText} does not start with ${lastState.argsText}`,
121
+ );
122
+ }
123
+
124
+ const argsTextDelta = content.argsText.slice(
125
+ lastState.argsText.length,
126
+ );
127
+ lastState.controller.argsText.append(argsTextDelta);
128
+
129
+ if (isArgsTextComplete(content.argsText)) {
130
+ lastState.controller.argsText.close();
131
+ }
132
+
133
+ lastToolStates.current[content.toolCallId] = {
134
+ argsText: content.argsText,
135
+ hasResult: lastState.hasResult,
136
+ controller: lastState.controller,
137
+ };
138
+ }
139
+
140
+ if (content.result !== undefined && !lastState.hasResult) {
141
+ lastState.controller.setResponse(
142
+ new ToolResponse({
143
+ result: content.result as ReadonlyJSONValue,
144
+ artifact: content.artifact as ReadonlyJSONValue | undefined,
145
+ isError: content.isError,
146
+ }),
147
+ );
148
+ lastState.controller.close();
149
+
150
+ lastToolStates.current[content.toolCallId] = {
151
+ hasResult: true,
152
+ argsText: lastState.argsText,
153
+ controller: lastState.controller,
154
+ };
155
+ }
156
+ }
157
+ });
158
+ });
159
+ }
160
+ }, [state]);
161
+
162
+ return {
163
+ reset: () => {
164
+ acRef.current.abort();
165
+ acRef.current = new AbortController();
166
+ isInititialState.current = true;
167
+ },
168
+ abort: () => {
169
+ acRef.current.abort();
170
+ acRef.current = new AbortController();
171
+ },
172
+ };
173
+ }
@@ -0,0 +1,42 @@
1
+ import { Tool } from "assistant-stream";
2
+ import { JSONSchema7 } from "json-schema";
3
+ import { z } from "zod";
4
+
5
+ // Convert tools to AI SDK format
6
+ export const toAISDKTools = (tools: Record<string, Tool>) => {
7
+ return Object.fromEntries(
8
+ Object.entries(tools).map(([name, tool]) => [
9
+ name,
10
+ {
11
+ ...(tool.description ? { description: tool.description } : undefined),
12
+ parameters: (tool.parameters instanceof z.ZodType
13
+ ? z.toJSONSchema(tool.parameters)
14
+ : tool.parameters) as JSONSchema7,
15
+ },
16
+ ]),
17
+ );
18
+ };
19
+
20
+ // Filter enabled tools
21
+ export const getEnabledTools = (tools: Record<string, Tool>) => {
22
+ return Object.fromEntries(
23
+ Object.entries(tools).filter(
24
+ ([, tool]) => !tool.disabled && tool.type !== "backend",
25
+ ),
26
+ );
27
+ };
28
+
29
+ // Create headers for fetch request
30
+ export const createRequestHeaders = async (
31
+ headersValue:
32
+ | Record<string, string>
33
+ | Headers
34
+ | (() => Promise<Record<string, string> | Headers>),
35
+ ): Promise<Headers> => {
36
+ const resolvedHeaders =
37
+ typeof headersValue === "function" ? await headersValue() : headersValue;
38
+
39
+ const headers = new Headers(resolvedHeaders);
40
+ headers.set("Content-Type", "application/json");
41
+ return headers;
42
+ };
@@ -47,6 +47,7 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
47
47
  public abstract resumeRun(config: ResumeRunConfig): void;
48
48
  public abstract addToolResult(options: AddToolResultOptions): void;
49
49
  public abstract cancelRun(): void;
50
+ public abstract unstable_loadExternalState(state: any): void;
50
51
 
51
52
  public get messages() {
52
53
  return this.repository.getMessages();
@@ -63,7 +63,7 @@ export type StartRunConfig = {
63
63
  };
64
64
 
65
65
  export type ResumeRunConfig = StartRunConfig & {
66
- stream: (
66
+ stream?: (
67
67
  options: ChatModelRunOptions,
68
68
  ) => AsyncGenerator<ChatModelRunResult, void, unknown>;
69
69
  };
@@ -122,4 +122,5 @@ export type ThreadRuntimeCore = Readonly<{
122
122
  reset(initialMessages?: readonly ThreadMessageLike[]): void;
123
123
 
124
124
  unstable_on(event: ThreadRuntimeEventType, callback: () => void): Unsubscribe;
125
+ unstable_loadExternalState: (state: any) => void;
125
126
  }>;
@@ -2,6 +2,7 @@ import { AppendMessage, ThreadMessage } from "../../../types";
2
2
  import { AttachmentAdapter } from "../adapters/attachment";
3
3
  import {
4
4
  AddToolResultOptions,
5
+ ResumeRunConfig,
5
6
  StartRunConfig,
6
7
  ThreadSuggestion,
7
8
  } from "../core/ThreadRuntimeCore";
@@ -63,11 +64,13 @@ type ExternalStoreAdapterBase<T> = {
63
64
 
64
65
  setMessages?: ((messages: readonly T[]) => void) | undefined;
65
66
  onImport?: ((messages: readonly ThreadMessage[]) => void) | undefined;
67
+ onLoadExternalState?: ((state: any) => void) | undefined;
66
68
  onNew: (message: AppendMessage) => Promise<void>;
67
69
  onEdit?: ((message: AppendMessage) => Promise<void>) | undefined;
68
70
  onReload?: // TODO: remove parentId in 0.8.0
69
71
  | ((parentId: string | null, config: StartRunConfig) => Promise<void>)
70
72
  | undefined;
73
+ onResume?: ((config: ResumeRunConfig) => Promise<void>) | undefined;
71
74
  onCancel?: (() => Promise<void>) | undefined;
72
75
  onAddToolResult?:
73
76
  | ((options: AddToolResultOptions) => Promise<void> | void)
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AddToolResultOptions,
3
+ ResumeRunConfig,
3
4
  StartRunConfig,
4
5
  ThreadSuggestion,
5
6
  } from "../core/ThreadRuntimeCore";
@@ -244,8 +245,18 @@ export class ExternalStoreThreadRuntimeCore
244
245
  await this._store.onReload(config.parentId, config);
245
246
  }
246
247
 
247
- public async resumeRun(): Promise<void> {
248
- throw new Error("Runtime does not support resuming runs.");
248
+ public async resumeRun(config: ResumeRunConfig): Promise<void> {
249
+ if (!this._store.onResume)
250
+ throw new Error("Runtime does not support resuming runs.");
251
+
252
+ await this._store.onResume(config);
253
+ }
254
+
255
+ public unstable_loadExternalState(state: any): void {
256
+ if (!this._store.onLoadExternalState)
257
+ throw new Error("Runtime does not support importing states.");
258
+
259
+ this._store.onLoadExternalState(state);
249
260
  }
250
261
 
251
262
  public cancelRun(): void {
@@ -133,15 +133,12 @@ export const fromThreadMessageLike = (
133
133
  return {
134
134
  ...commonProps,
135
135
  args: part.args,
136
- argsText: JSON.stringify(part.args),
136
+ argsText: part.argsText ?? JSON.stringify(part.args),
137
137
  };
138
138
  }
139
139
  return {
140
140
  ...commonProps,
141
- args:
142
- part.args ??
143
- parsePartialJsonObject(part.argsText ?? "") ??
144
- {},
141
+ args: parsePartialJsonObject(part.argsText ?? "") ?? {},
145
142
  argsText: part.argsText ?? "",
146
143
  };
147
144
  }
@@ -1,4 +1,5 @@
1
1
  export * from "./adapters";
2
+ export * from "./assistant-transport";
2
3
  export * from "./core";
3
4
  export * from "./external-store";
4
5
  export * from "./local";
@@ -166,9 +166,15 @@ export class LocalThreadRuntimeCore
166
166
  }
167
167
 
168
168
  public resumeRun({ stream, ...startConfig }: ResumeRunConfig): Promise<void> {
169
+ if (!stream)
170
+ throw new Error("You must pass a stream parameter to resume runs.");
169
171
  return this.startRun(startConfig, stream);
170
172
  }
171
173
 
174
+ public unstable_loadExternalState(): void {
175
+ throw new Error("Runtime does not support importing external states.");
176
+ }
177
+
172
178
  public async startRun(
173
179
  { parentId, runConfig }: StartRunConfig,
174
180
  runCallback?: ChatModelAdapter["run"],
@@ -56,6 +56,10 @@ export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
56
56
  return {};
57
57
  },
58
58
 
59
+ unstable_loadExternalState() {
60
+ throw EMPTY_THREAD_ERROR;
61
+ },
62
+
59
63
  composer: {
60
64
  attachments: [],
61
65
  attachmentAccept: "*",
@@ -34,15 +34,32 @@ class ReadonlyApiHandler<TApi extends ApiObject> implements ProxyHandler<TApi> {
34
34
  }
35
35
  }
36
36
 
37
- export const tapApi = <TApi extends ApiObject>(api: TApi) => {
37
+ export const tapApi = <TApi extends ApiObject & { getState: () => any }>(
38
+ api: TApi,
39
+ options?: {
40
+ key?: string | undefined;
41
+ },
42
+ ) => {
38
43
  const ref = tapRef(() => api);
39
44
  tapEffect(() => {
40
45
  ref.current = api;
41
46
  });
42
47
 
43
- return tapMemo(
48
+ const apiProxy = tapMemo(
44
49
  () =>
45
50
  new Proxy<TApi>({} as TApi, new ReadonlyApiHandler(() => ref.current)),
46
51
  [],
47
52
  );
53
+
54
+ const key = options?.key;
55
+ const state = api.getState();
56
+
57
+ return tapMemo(
58
+ () => ({
59
+ key,
60
+ state,
61
+ api: apiProxy,
62
+ }),
63
+ [state, key],
64
+ );
48
65
  };