@assistant-ui/react 0.15.3 → 0.15.4
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/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +6 -0
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +20 -15
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/package.json +4 -4
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +6 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +31 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +26 -17
|
@@ -70,6 +70,12 @@ type AssistantTransportOptions<T> = {
|
|
|
70
70
|
api: string;
|
|
71
71
|
resumeApi?: string;
|
|
72
72
|
protocol?: AssistantTransportProtocol;
|
|
73
|
+
/**
|
|
74
|
+
* When `false`, stream decoding and state reconciliation tolerate malformed
|
|
75
|
+
* input (invalid chunks are dropped with a console log) instead of throwing.
|
|
76
|
+
* Resume runs always decode leniently. Defaults to `true`.
|
|
77
|
+
*/
|
|
78
|
+
strict?: boolean;
|
|
73
79
|
converter: AssistantTransportStateConverter<T>;
|
|
74
80
|
headers: HeadersValue | (() => Promise<HeadersValue>);
|
|
75
81
|
body?: object | (() => Promise<object | undefined>);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/types.ts"],"mappings":";;;;;KAaY;WACD;WACA;;KAGC;WACD;WACA;;KAGC,kBAAkB,WAAW;KAE7B;WACD;WACA,gBAAgB;;KAGf;WACD;WACA,gBAAgB;;KAIf;WACD;WACA,SAAS,cAAc;WACvB;WACA;;KAGC;WACD;WACA;WACA;WACA,QAAQ;WACR;WACA,WAAW;WACX,wBAAwB;;KAGvB,4BACR,oBACA,uBACA;KAGQ;WACD,mBAAmB;WACnB,QAAQ;WACR;;KAGC;EACV,iBAAiB;EACjB;EACA,cAAc,eAAe;;KAGnB,iCAAiC,MAC3C,OAAO,GACP,oBAAoB,yCACjB;KAGO;EACV,QAAQ;EACR,WAAW;;KAID,gBAAgB;KAKhB,eAAe,yBAAyB;KAExC;KAEA;EACV,UAAU;EACV;EACA;EACA,OAAO;EACP,cAAc;EACd,QAAQ;EACR;EACA;GAIC;;KAGS,0BAA0B;EACpC,cAAc;EACd;EACA;EACA,WAAW;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/types.ts"],"mappings":";;;;;KAaY;WACD;WACA;;KAGC;WACD;WACA;;KAGC,kBAAkB,WAAW;KAE7B;WACD;WACA,gBAAgB;;KAGf;WACD;WACA,gBAAgB;;KAIf;WACD;WACA,SAAS,cAAc;WACvB;WACA;;KAGC;WACD;WACA;WACA;WACA,QAAQ;WACR;WACA,WAAW;WACX,wBAAwB;;KAGvB,4BACR,oBACA,uBACA;KAGQ;WACD,mBAAmB;WACnB,QAAQ;WACR;;KAGC;EACV,iBAAiB;EACjB;EACA,cAAc,eAAe;;KAGnB,iCAAiC,MAC3C,OAAO,GACP,oBAAoB,yCACjB;KAGO;EACV,QAAQ;EACR,WAAW;;KAID,gBAAgB;KAKhB,eAAe,yBAAyB;KAExC;KAEA;EACV,UAAU;EACV;EACA;EACA,OAAO;EACP,cAAc;EACd,QAAQ;EACR;EACA;GAIC;;KAGS,0BAA0B;EACpC,cAAc;EACd;EACA;EACA,WAAW;;;;;;EAMX;EACA,WAAW,iCAAiC;EAC5C,SAAS,sBAAsB,QAAQ;EACvC,uBAAuB;;;;;;;;;;;;;EAavB,8BACE,MAAM,4BACH,0BAA0B,QAAQ;EACvC,cAAc,UAAU;EACxB;EACA,WACE,OAAO,OACP;IACE,UAAU;IACV,cAAc,UAAU,OAAO,MAAM;eAE7B;;;;;;;EAOZ,YAAY;IACV,UAAU;IACV,cAAc,UAAU,OAAO,MAAM;IACrC,QAAQ;;EAEV;IACE;;EAEF;IACE,cAAc;IACd,UAAU"}
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssistantTransportRuntime.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"useAssistantTransportRuntime.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"mappings":";;;;cAgGa,yCAGH,SAAS;iBAOH,8BAA8B;iBAC9B,2BAA2B,GACzC,WAAW,OAAO,sBAAsB,IACvC;;;;cAoSU,+BAAgC,GAC3C,SAAS,0BAA0B,OAClC"}
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js
CHANGED
|
@@ -24,6 +24,7 @@ const convertAppendMessageToCommand = (message) => {
|
|
|
24
24
|
type: "image",
|
|
25
25
|
image: contentPart.image
|
|
26
26
|
});
|
|
27
|
+
if (parts.length === 0) return null;
|
|
27
28
|
return {
|
|
28
29
|
type: "add-message",
|
|
29
30
|
message: {
|
|
@@ -64,6 +65,15 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
64
65
|
const waitForReplayRender = useReplayRenderWait();
|
|
65
66
|
const parentIdRef = useRef(void 0);
|
|
66
67
|
const commandQueue = useCommandQueue({ onQueue: () => runManager.schedule() });
|
|
68
|
+
const enqueueAppendMessage = (message) => {
|
|
69
|
+
const command = convertAppendMessageToCommand(message);
|
|
70
|
+
if (!command) {
|
|
71
|
+
console.warn("[assistant-ui] Skipped add-message command with no supported parts");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
parentIdRef.current = message.parentId;
|
|
75
|
+
commandQueue.enqueue(command, { schedule: message.startRun ?? message.role === "user" });
|
|
76
|
+
};
|
|
67
77
|
const threadId = useAuiState((s) => s.threadListItem.remoteId);
|
|
68
78
|
const runManager = useRunManager({
|
|
69
79
|
onRun: async (signal) => {
|
|
@@ -104,11 +114,14 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
104
114
|
setReplaying: setIsReplaying,
|
|
105
115
|
waitForRender: waitForReplayRender
|
|
106
116
|
});
|
|
107
|
-
const
|
|
117
|
+
const protocol = options.protocol ?? "data-stream";
|
|
118
|
+
const strict = isResume ? false : options.strict ?? true;
|
|
119
|
+
const decoder = protocol === "assistant-transport" ? new AssistantTransportDecoder({ strict }) : new DataStreamDecoder({ strict });
|
|
108
120
|
let err;
|
|
109
121
|
const stream = body.pipeThrough(decoder).pipeThrough(new AssistantMessageAccumulator({
|
|
110
122
|
initialMessage: unstable_createInitialMessage({ unstable_state: agentStateRef.current ?? null }),
|
|
111
123
|
throttle: isResume,
|
|
124
|
+
strict,
|
|
112
125
|
onError: (error) => {
|
|
113
126
|
err = error;
|
|
114
127
|
}
|
|
@@ -178,21 +191,13 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
178
191
|
setToolStatuses,
|
|
179
192
|
extras: {
|
|
180
193
|
[symbolAssistantTransportExtras]: true,
|
|
181
|
-
sendCommand: (
|
|
182
|
-
commandQueue.enqueue(
|
|
194
|
+
sendCommand: (command_0) => {
|
|
195
|
+
commandQueue.enqueue(command_0);
|
|
183
196
|
},
|
|
184
197
|
state: agentStateRef.current
|
|
185
198
|
},
|
|
186
|
-
onNew: async (
|
|
187
|
-
|
|
188
|
-
const command_0 = convertAppendMessageToCommand(message);
|
|
189
|
-
commandQueue.enqueue(command_0, { schedule: message.startRun ?? message.role === "user" });
|
|
190
|
-
},
|
|
191
|
-
...options.capabilities?.edit && { onEdit: async (message_0) => {
|
|
192
|
-
parentIdRef.current = message_0.parentId;
|
|
193
|
-
const command_1 = convertAppendMessageToCommand(message_0);
|
|
194
|
-
commandQueue.enqueue(command_1, { schedule: message_0.startRun ?? message_0.role === "user" });
|
|
195
|
-
} },
|
|
199
|
+
onNew: async (message_0) => enqueueAppendMessage(message_0),
|
|
200
|
+
...options.capabilities?.edit && { onEdit: async (message_1) => enqueueAppendMessage(message_1) },
|
|
196
201
|
...commandQueue.state.queued.length > 0 && { onReload: async (parentId_0) => {
|
|
197
202
|
parentIdRef.current = parentId_0;
|
|
198
203
|
runManager.schedule();
|
|
@@ -206,7 +211,7 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
206
211
|
runManager.schedule();
|
|
207
212
|
},
|
|
208
213
|
onAddToolResult: async (toolOptions) => {
|
|
209
|
-
const
|
|
214
|
+
const command_1 = {
|
|
210
215
|
type: "add-tool-result",
|
|
211
216
|
toolCallId: toolOptions.toolCallId,
|
|
212
217
|
result: toolOptions.result,
|
|
@@ -215,7 +220,7 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
215
220
|
...toolOptions.artifact && { artifact: toolOptions.artifact },
|
|
216
221
|
...toolOptions.modelContent !== void 0 && { modelContent: toolOptions.modelContent }
|
|
217
222
|
};
|
|
218
|
-
commandQueue.enqueue(
|
|
223
|
+
commandQueue.enqueue(command_1);
|
|
219
224
|
},
|
|
220
225
|
onLoadExternalState: async (state) => {
|
|
221
226
|
agentStateRef.current = state;
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssistantTransportRuntime.js","names":["c","_c","AppendMessage","ReadonlyJSONObject","ReadonlyJSONValue","asAsyncIterableStream","useExternalStoreRuntime","AssistantRuntime","AddToolResultOptions","useMemo","useRef","useState","AssistantMessageAccumulator","DataStreamDecoder","AssistantTransportDecoder","unstable_createInitialMessage","createInitialMessage","toToolsJSONSchema","AssistantTransportOptions","AddMessageCommand","AddToolResultCommand","UserMessagePart","QueuedCommand","AssistantTransportCommand","SendCommandsRequestBody","useCommandQueue","createReplayBoundaryStream","useReplayRenderWait","useRunManager","useConvertedState","ToolExecutionStatus","createRequestHeaders","useRemoteThreadListRuntime","InMemoryThreadListAdapter","useAui","useAuiState","UserExternalState","convertAppendMessageToCommand","message","role","Error","parts","content","attachments","flatMap","a","contentPart","type","push","text","image","parentId","sourceId","symbolAssistantTransportExtras","Symbol","AssistantTransportExtras","sendCommand","command","state","asAssistantTransportExtras","extras","useAssistantTransportSendCommand","$","aui","t0","thread","getState","transportExtras","useAssistantTransportState","selector","T","t","s","useAssistantTransportThreadRuntime","options","agentStateRef","initialState","rerender","resumeFlagRef","isReplaying","setIsReplaying","waitForReplayRender","parentIdRef","undefined","commandQueue","onQueue","runManager","schedule","threadId","threadListItem","remoteId","onRun","signal","AbortSignal","isResume","current","commands","flush","length","headers","bodyValue","body","context","runtime","getModelContext","requestBody","Record","system","tools","callSettings","config","prepareSendCommandsRequest","response","fetch","resumeApi","api","method","JSON","stringify","onResponse","ok","status","setReplaying","waitForRender","protocol","decoder","err","stream","pipeThrough","initialMessage","unstable_state","throttle","onError","error","markedDelivered","chunk","metadata","markDelivered","prev","queued","onFinish","onCancel","cmds","inTransit","reset","updateState","updater","inTransitCmds","queuedCmds","toolStatuses","setToolStatuses","pendingCommands","converted","converter","isRunning","messages","isLoading","adapters","unstable_enableToolInvocations","enqueue","onNew","Promise","startRun","capabilities","edit","onEdit","onReload","cancel","onResume","onAddToolResult","toolOptions","toolCallId","result","toolName","isError","artifact","modelContent","onLoadExternalState","useAssistantTransportRuntime","runtimeHook","RuntimeHook","adapter","allowNesting"],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { AppendMessage } from \"@assistant-ui/core\";\nimport {\n type ReadonlyJSONObject,\n type ReadonlyJSONValue,\n asAsyncIterableStream,\n} from \"assistant-stream/utils\";\nimport { useExternalStoreRuntime } from \"../external-store/useExternalStoreRuntime\";\nimport type { AssistantRuntime } from \"../../runtime/AssistantRuntime\";\nimport type { AddToolResultOptions } from \"@assistant-ui/core\";\nimport { useMemo, useRef, useState } from \"react\";\nimport {\n AssistantMessageAccumulator,\n DataStreamDecoder,\n AssistantTransportDecoder,\n unstable_createInitialMessage as createInitialMessage,\n toToolsJSONSchema,\n} from \"assistant-stream\";\nimport type {\n AssistantTransportOptions,\n AddMessageCommand,\n AddToolResultCommand,\n UserMessagePart,\n QueuedCommand,\n AssistantTransportCommand,\n SendCommandsRequestBody,\n} from \"./types\";\nimport { useCommandQueue } from \"./commandQueue\";\nimport {\n createReplayBoundaryStream,\n useReplayRenderWait,\n} from \"./replayBoundaryStream\";\nimport { useRunManager } from \"./runManager\";\nimport { useConvertedState } from \"./useConvertedState\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport { createRequestHeaders } from \"@assistant-ui/core\";\nimport { useRemoteThreadListRuntime } from \"../remote-thread-list/useRemoteThreadListRuntime\";\nimport { InMemoryThreadListAdapter } from \"@assistant-ui/core\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type { UserExternalState } from \"../../../augmentations\";\n\nconst convertAppendMessageToCommand = (\n message: AppendMessage,\n): AddMessageCommand => {\n if (message.role !== \"user\")\n throw new Error(\"Only user messages are supported\");\n\n const parts: UserMessagePart[] = [];\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((a) => a.content) ?? []),\n ];\n for (const contentPart of content) {\n if (contentPart.type === \"text\") {\n parts.push({ type: \"text\", text: contentPart.text });\n } else if (contentPart.type === \"image\") {\n parts.push({ type: \"image\", image: contentPart.image });\n }\n }\n\n return {\n type: \"add-message\",\n message: {\n role: \"user\",\n parts,\n },\n parentId: message.parentId,\n sourceId: message.sourceId,\n };\n};\n\nconst symbolAssistantTransportExtras = Symbol(\"assistant-transport-extras\");\ntype AssistantTransportExtras = {\n [symbolAssistantTransportExtras]: true;\n sendCommand: (command: AssistantTransportCommand) => void;\n state: UserExternalState;\n};\n\nconst asAssistantTransportExtras = (\n extras: unknown,\n): AssistantTransportExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolAssistantTransportExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useAssistantTransportRuntime\",\n );\n\n return extras as AssistantTransportExtras;\n};\n\nexport const useAssistantTransportSendCommand = () => {\n const aui = useAui();\n\n return (command: AssistantTransportCommand) => {\n const extras = aui.thread.getState().extras;\n const transportExtras = asAssistantTransportExtras(extras);\n transportExtras.sendCommand(command);\n };\n};\n\nexport function useAssistantTransportState(): UserExternalState;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T,\n): T;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T = (t) => t as T,\n): T | UserExternalState {\n return useAuiState((s) =>\n selector(asAssistantTransportExtras(s.thread.extras).state),\n );\n}\n\nconst useAssistantTransportThreadRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const agentStateRef = useRef(options.initialState);\n const [, rerender] = useState(0);\n const resumeFlagRef = useRef(false);\n const [isReplaying, setIsReplaying] = useState(false);\n const waitForReplayRender = useReplayRenderWait();\n const parentIdRef = useRef<string | null | undefined>(undefined);\n const commandQueue = useCommandQueue({\n onQueue: () => runManager.schedule(),\n });\n\n const threadId = useAuiState((s) => s.threadListItem.remoteId);\n\n const runManager = useRunManager({\n onRun: async (signal: AbortSignal) => {\n const isResume = resumeFlagRef.current;\n resumeFlagRef.current = false;\n setIsReplaying(false);\n const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();\n if (commands.length === 0 && !isResume) return;\n\n // The flushed batch consumes the parentId; read it alongside the flush\n // (before any awaits) so a mid-run append keeps its own value. Resume\n // runs send no commands, so they neither send nor consume it.\n const parentId = isResume ? undefined : parentIdRef.current;\n if (!isResume) parentIdRef.current = undefined;\n\n const headers = await createRequestHeaders(options.headers);\n const bodyValue =\n typeof options.body === \"function\"\n ? await options.body()\n : options.body;\n const context = runtime.thread.getModelContext();\n\n let requestBody: Record<string, unknown> = {\n commands,\n state: agentStateRef.current,\n system: context.system,\n tools: context.tools ? toToolsJSONSchema(context.tools) : undefined,\n threadId,\n ...(parentId !== undefined && {\n parentId,\n }),\n // nested (new format, aligned with AssistantChatTransport)\n callSettings: context.callSettings,\n config: context.config,\n // @deprecated spread at top level — use nested `callSettings`/`config` instead. Will be removed in a future version.\n ...context.callSettings,\n ...context.config,\n ...(bodyValue ?? {}),\n };\n\n if (options.prepareSendCommandsRequest) {\n requestBody = await options.prepareSendCommandsRequest(\n requestBody as SendCommandsRequestBody,\n );\n }\n\n const response = await fetch(\n isResume ? options.resumeApi! : options.api,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(requestBody),\n signal,\n },\n );\n\n options.onResponse?.(response);\n\n if (!response.ok) {\n throw new Error(`Status ${response.status}: ${await response.text()}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n const body = await createReplayBoundaryStream(response, {\n setReplaying: setIsReplaying,\n waitForRender: waitForReplayRender,\n });\n\n // Select decoder based on protocol option\n const protocol = options.protocol ?? \"data-stream\";\n const decoder =\n protocol === \"assistant-transport\"\n ? new AssistantTransportDecoder()\n : new DataStreamDecoder();\n\n let err: string | undefined;\n const stream = body.pipeThrough(decoder).pipeThrough(\n new AssistantMessageAccumulator({\n initialMessage: createInitialMessage({\n unstable_state:\n (agentStateRef.current as ReadonlyJSONValue) ?? null,\n }),\n throttle: isResume,\n onError: (error) => {\n err = error;\n },\n }),\n );\n\n let markedDelivered = false;\n\n for await (const chunk of asAsyncIterableStream(stream)) {\n if (chunk.metadata.unstable_state === agentStateRef.current) continue;\n\n if (!markedDelivered) {\n commandQueue.markDelivered();\n markedDelivered = true;\n }\n\n agentStateRef.current = chunk.metadata.unstable_state as T;\n rerender((prev) => prev + 1);\n }\n\n if (err) {\n throw new Error(err);\n }\n\n // A successful run confirms delivery even when no state-changing\n // chunk was observed.\n if (!markedDelivered) {\n commandQueue.markDelivered();\n }\n\n // commands that coalesced into this resume run must not starve\n if (isResume && commandQueue.state.queued.length > 0) {\n runManager.schedule();\n }\n },\n onFinish: options.onFinish,\n onCancel: () => {\n setIsReplaying(false);\n const cmds = [\n ...commandQueue.state.inTransit,\n ...commandQueue.state.queued,\n ];\n\n commandQueue.reset();\n\n options.onCancel?.({\n commands: cmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n },\n onError: async (error) => {\n setIsReplaying(false);\n const inTransitCmds = [...commandQueue.state.inTransit];\n const queuedCmds = [...commandQueue.state.queued];\n\n commandQueue.reset();\n\n try {\n await options.onError?.(error as Error, {\n commands: inTransitCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n } finally {\n options.onCancel?.({\n commands: queuedCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n error: error as Error,\n });\n }\n },\n });\n\n // Tool execution status state\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n // Reactive conversion of agent state + connection metadata → UI state\n const pendingCommands = useMemo(\n () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],\n [commandQueue.state],\n );\n const converted = useConvertedState(\n options.converter,\n agentStateRef.current,\n pendingCommands,\n runManager.isRunning,\n toolStatuses,\n );\n\n // Create runtime\n const runtime = useExternalStoreRuntime({\n messages: converted.messages,\n state: converted.state,\n isRunning: converted.isRunning,\n isLoading: isReplaying,\n adapters: options.adapters,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n extras: {\n [symbolAssistantTransportExtras]: true,\n sendCommand: (command: AssistantTransportCommand) => {\n commandQueue.enqueue(command);\n },\n state: agentStateRef.current as UserExternalState,\n } satisfies AssistantTransportExtras,\n onNew: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command, {\n schedule: message.startRun ?? message.role === \"user\",\n });\n },\n ...(options.capabilities?.edit && {\n onEdit: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command, {\n schedule: message.startRun ?? message.role === \"user\",\n });\n },\n }),\n ...(commandQueue.state.queued.length > 0 && {\n onReload: async (parentId: string | null) => {\n parentIdRef.current = parentId;\n runManager.schedule();\n },\n }),\n onCancel: async () => {\n runManager.cancel();\n },\n onResume: async () => {\n if (!options.resumeApi)\n throw new Error(\"Must pass resumeApi to options to resume runs\");\n\n resumeFlagRef.current = true;\n runManager.schedule();\n },\n onAddToolResult: async (\n toolOptions: AddToolResultOptions,\n ): Promise<void> => {\n const command: AddToolResultCommand = {\n type: \"add-tool-result\",\n toolCallId: toolOptions.toolCallId,\n result: toolOptions.result as ReadonlyJSONObject,\n toolName: toolOptions.toolName,\n isError: toolOptions.isError,\n ...(toolOptions.artifact && { artifact: toolOptions.artifact }),\n ...(toolOptions.modelContent !== undefined && {\n modelContent: toolOptions.modelContent,\n }),\n };\n\n commandQueue.enqueue(command);\n },\n onLoadExternalState: async (state) => {\n agentStateRef.current = state as T;\n rerender((prev) => prev + 1);\n },\n });\n\n return runtime;\n};\n\n/**\n * @alpha This is an experimental API that is subject to change.\n */\nexport const useAssistantTransportRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const runtime = useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useAssistantTransportThreadRuntime(options);\n },\n adapter: new InMemoryThreadListAdapter(),\n allowNesting: true,\n });\n return runtime;\n};\n"],"mappings":";;;;;;;;;;;;;;AA0CA,MAAMqC,iCACJC,YACsB;CACtB,IAAIA,QAAQC,SAAS,QACnB,MAAM,IAAIC,MAAM,kCAAkC;CAEpD,MAAMC,QAA2B,CAAA;CACjC,MAAMC,UAAU,CACd,GAAGJ,QAAQI,SACX,GAAIJ,QAAQK,aAAaC,SAASC,MAAMA,EAAEH,OAAO,KAAK,CAAA,CAAG;CAE3D,KAAK,MAAMI,eAAeJ,SACxB,IAAII,YAAYC,SAAS,QACvBN,MAAMO,KAAK;EAAED,MAAM;EAAQE,MAAMH,YAAYG;CAAK,CAAC;MAC9C,IAAIH,YAAYC,SAAS,SAC9BN,MAAMO,KAAK;EAAED,MAAM;EAASG,OAAOJ,YAAYI;CAAM,CAAC;CAI1D,OAAO;EACLH,MAAM;EACNT,SAAS;GACPC,MAAM;GACNE;EACF;EACAU,UAAUb,QAAQa;EAClBC,UAAUd,QAAQc;CACpB;AACF;AAEA,MAAMC,iCAAiCC,OAAO,4BAA4B;AAO1E,MAAMK,8BACJC,WAC6B;CAC7B,IACE,OAAOA,WAAW,YAClBA,UAAU,QACV,EAAEP,kCAAkCO,SAEpC,MAAM,IAAIpB,MACR,gFACF;CAEF,OAAOoB;AACT;AAEA,MAAaC,yCAAmC;CAAA,MAAAC,IAAA7D,EAAA,CAAA;CAC9C,MAAA8D,MAAY7B,OAAO;CAAE,IAAA8B;CAAA,IAAAF,EAAA,OAAAC,IAAAE,QAAA;EAEdD,MAAAP,YAAA;GACL,MAAAG,SAAeG,IAAGE,OAAOC,SAAU,CAAC,CAAAN;GAEpCO,2BADmDP,MACnDO,CAAe,CAAAX,YAAaC,OAAO;EAAC;EACrCK,EAAA,KAAAC,IAAAE;EAAAH,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAA,OAJME;AAIN;AAOH,SAAgBI,2BACdC,YAA6CE,MAAMA,GAC5B;CACvB,OAAOpC,aAAaqC,MAClBH,SAASV,2BAA2Ba,EAAEP,OAAOL,MAAM,CAAC,CAACF,KAAK,CAC5D;AACF;AAEA,MAAMe,sCACJC,YACqB;CACrB,MAAMC,gBAAgBjE,OAAOgE,QAAQE,YAAY;CACjD,MAAM,GAAGC,YAAYlE,SAAS,CAAC;CAC/B,MAAMmE,gBAAgBpE,OAAO,KAAK;CAClC,MAAM,CAACqE,aAAaC,kBAAkBrE,SAAS,KAAK;CACpD,MAAMsE,sBAAsBtD,oBAAoB;CAChD,MAAMuD,cAAcxE,OAAkCyE,KAAAA,CAAS;CAC/D,MAAMC,eAAe3D,gBAAgB,EACnC4D,eAAeC,WAAWC,SAAS,EACrC,CAAC;CAED,MAAMC,WAAWrD,aAAaqC,MAAMA,EAAEiB,eAAeC,QAAQ;CAE7D,MAAMJ,aAAa1D,cAAc;EAC/B+D,OAAO,OAAOC,WAAwB;GACpC,MAAME,WAAWhB,cAAciB;GAC/BjB,cAAciB,UAAU;GACxBf,eAAe,KAAK;GACpB,MAAMgB,WAA4BF,WAAW,CAAA,IAAKV,aAAaa,MAAM;GACrE,IAAID,SAASE,WAAW,KAAK,CAACJ,UAAU;GAKxC,MAAM3C,WAAW2C,WAAWX,KAAAA,IAAYD,YAAYa;GACpD,IAAI,CAACD,UAAUZ,YAAYa,UAAUZ,KAAAA;GAErC,MAAMgB,UAAU,MAAMpE,qBAAqB2C,QAAQyB,OAAO;GAC1D,MAAMC,YACJ,OAAO1B,QAAQ2B,SAAS,aACpB,MAAM3B,QAAQ2B,KAAK,IACnB3B,QAAQ2B;GACd,MAAMC,UAAUC,QAAQtC,OAAOuC,gBAAgB;GAE/C,IAAIC,cAAuC;IACzCT;IACAtC,OAAOiB,cAAcoB;IACrBY,QAAQL,QAAQK;IAChBC,OAAON,QAAQM,QAAQ3F,kBAAkBqF,QAAQM,KAAK,IAAIzB,KAAAA;IAC1DK;IACA,GAAIrC,aAAagC,KAAAA,KAAa,EAC5BhC,SACF;IAEA0D,cAAcP,QAAQO;IACtBC,QAAQR,QAAQQ;IAEhB,GAAGR,QAAQO;IACX,GAAGP,QAAQQ;IACX,GAAIV,aAAa,CAAC;GACpB;GAEA,IAAI1B,QAAQqC,4BACVN,cAAc,MAAM/B,QAAQqC,2BAC1BN,WACF;GAGF,MAAMO,WAAW,MAAMC,MACrBnB,WAAWpB,QAAQwC,YAAaxC,QAAQyC,KACxC;IACEC,QAAQ;IACRjB;IACAE,MAAMgB,KAAKC,UAAUb,WAAW;IAChCb;GACF,CACF;GAEAlB,QAAQ6C,aAAaP,QAAQ;GAE7B,IAAI,CAACA,SAASQ,IACZ,MAAM,IAAIhF,MAAM,UAAUwE,SAASS,OAAM,IAAK,MAAMT,SAAS/D,KAAK,GAAG;GAGvE,IAAI,CAAC+D,SAASX,MACZ,MAAM,IAAI7D,MAAM,uBAAuB;GAGzC,MAAM6D,OAAO,MAAM3E,2BAA2BsF,UAAU;IACtDU,cAAc1C;IACd2C,eAAe1C;GACjB,CAAC;GAID,MAAM4C,WADWnD,QAAQkD,YAAY,mBAEtB,wBACT,IAAI9G,0BAA0B,IAC9B,IAAID,kBAAkB;GAE5B,IAAIiH;GACJ,MAAMC,SAAS1B,KAAK2B,YAAYH,OAAO,CAAC,CAACG,YACvC,IAAIpH,4BAA4B;IAC9BqH,gBAAgBjH,8BAAqB,EACnCkH,gBACGvD,cAAcoB,WAAiC,KACpD,CAAC;IACDoC,UAAUrC;IACVsC,UAAUC,UAAU;KAClBP,MAAMO;IACR;GACF,CAAC,CACH;GAEA,IAAIC,kBAAkB;GAEtB,WAAW,MAAMC,SAASlI,sBAAsB0H,MAAM,GAAG;IACvD,IAAIQ,MAAMC,SAASN,mBAAmBvD,cAAcoB,SAAS;IAE7D,IAAI,CAACuC,iBAAiB;KACpBlD,aAAaqD,cAAc;KAC3BH,kBAAkB;IACpB;IAEA3D,cAAcoB,UAAUwC,MAAMC,SAASN;IACvCrD,UAAU6D,SAASA,OAAO,CAAC;GAC7B;GAEA,IAAIZ,KACF,MAAM,IAAItF,MAAMsF,GAAG;GAKrB,IAAI,CAACQ,iBACHlD,aAAaqD,cAAc;GAI7B,IAAI3C,YAAYV,aAAa1B,MAAMiF,OAAOzC,SAAS,GACjDZ,WAAWC,SAAS;EAExB;EACAqD,UAAUlE,QAAQkE;EAClBC,gBAAgB;GACd7D,eAAe,KAAK;GACpB,MAAM8D,OAAO,CACX,GAAG1D,aAAa1B,MAAMqF,WACtB,GAAG3D,aAAa1B,MAAMiF,MAAM;GAG9BvD,aAAa4D,MAAM;GAEnBtE,QAAQmE,WAAW;IACjB7C,UAAU8C;IACVG,cAAcC,YAAY;KACxBvE,cAAcoB,UAAUmD,QAAQvE,cAAcoB,OAAO;KACrDlB,UAAU6D,SAASA,OAAO,CAAC;IAC7B;GACF,CAAC;EACH;EACAN,SAAS,OAAOC,YAAU;GACxBrD,eAAe,KAAK;GACpB,MAAMmE,gBAAgB,CAAC,GAAG/D,aAAa1B,MAAMqF,SAAS;GACtD,MAAMK,aAAa,CAAC,GAAGhE,aAAa1B,MAAMiF,MAAM;GAEhDvD,aAAa4D,MAAM;GAEnB,IAAI;IACF,MAAMtE,QAAQ0D,UAAUC,SAAgB;KACtCrC,UAAUmD;KACVF,cAAcC,YAAY;MACxBvE,cAAcoB,UAAUmD,QAAQvE,cAAcoB,OAAO;MACrDlB,UAAU6D,SAASA,OAAO,CAAC;KAC7B;IACF,CAAC;GACH,UAAU;IACRhE,QAAQmE,WAAW;KACjB7C,UAAUoD;KACVH,cAAcC,YAAY;MACxBvE,cAAcoB,UAAUmD,QAAQvE,cAAcoB,OAAO;MACrDlB,UAAU6D,SAASA,OAAO,CAAC;KAC7B;KACAL,OAAOA;IACT,CAAC;GACH;EACF;CACF,CAAC;CAGD,MAAM,CAACgB,cAAcC,mBAAmB3I,SAEtC,CAAC,CAAC;CAGJ,MAAM4I,kBAAkB9I,cAChB,CAAC,GAAG2E,aAAa1B,MAAMqF,WAAW,GAAG3D,aAAa1B,MAAMiF,MAAM,GACpE,CAACvD,aAAa1B,KAAK,CACrB;CACA,MAAM8F,YAAY3H,kBAChB6C,QAAQ+E,WACR9E,cAAcoB,SACdwD,iBACAjE,WAAWoE,WACXL,YACF;CAGA,MAAM9C,UAAUjG,wBAAwB;EACtCqJ,UAAUH,UAAUG;EACpBjG,OAAO8F,UAAU9F;EACjBgG,WAAWF,UAAUE;EACrBE,WAAW7E;EACX8E,UAAUnF,QAAQmF;EAClBC,gCAAgC;EAChCR;EACA1F,QAAQ;IACLP,iCAAiC;GAClCG,cAAcC,YAAuC;IACnD2B,aAAa2E,QAAQtG,OAAO;GAC9B;GACAC,OAAOiB,cAAcoB;EACvB;EACAiE,OAAO,OAAO1H,YAA0C;GACtD4C,YAAYa,UAAUzD,QAAQa;GAC9B,MAAMM,YAAUpB,8BAA8BC,OAAO;GACrD8C,aAAa2E,QAAQtG,WAAS,EAC5B8B,UAAUjD,QAAQ4H,YAAY5H,QAAQC,SAAS,OACjD,CAAC;EACH;EACA,GAAImC,QAAQyF,cAAcC,QAAQ,EAChCC,QAAQ,OAAO/H,cAA0C;GACvD4C,YAAYa,UAAUzD,UAAQa;GAC9B,MAAMM,YAAUpB,8BAA8BC,SAAO;GACrD8C,aAAa2E,QAAQtG,WAAS,EAC5B8B,UAAUjD,UAAQ4H,YAAY5H,UAAQC,SAAS,OACjD,CAAC;EACH,EACF;EACA,GAAI6C,aAAa1B,MAAMiF,OAAOzC,SAAS,KAAK,EAC1CoE,UAAU,OAAOnH,eAA4B;GAC3C+B,YAAYa,UAAU5C;GACtBmC,WAAWC,SAAS;EACtB,EACF;EACAsD,UAAU,YAAY;GACpBvD,WAAWiF,OAAO;EACpB;EACAC,UAAU,YAAY;GACpB,IAAI,CAAC9F,QAAQwC,WACX,MAAM,IAAI1E,MAAM,+CAA+C;GAEjEsC,cAAciB,UAAU;GACxBT,WAAWC,SAAS;EACtB;EACAkF,iBAAiB,OACfC,gBACkB;GAClB,MAAMjH,YAAgC;IACpCV,MAAM;IACN4H,YAAYD,YAAYC;IACxBC,QAAQF,YAAYE;IACpBC,UAAUH,YAAYG;IACtBC,SAASJ,YAAYI;IACrB,GAAIJ,YAAYK,YAAY,EAAEA,UAAUL,YAAYK,SAAS;IAC7D,GAAIL,YAAYM,iBAAiB7F,KAAAA,KAAa,EAC5C6F,cAAcN,YAAYM,aAC5B;GACF;GAEA5F,aAAa2E,QAAQtG,SAAO;EAC9B;EACAwH,qBAAqB,OAAOvH,UAAU;GACpCiB,cAAcoB,UAAUrC;GACxBmB,UAAU6D,WAASA,SAAO,CAAC;EAC7B;CACF,CAAC;CAED,OAAOnC;AACT;;;;AAKA,MAAa2E,gCACXxG,YACqB;CAQrB,OAPgB1C,2BAA2B;EACzCmJ,aAAa,SAASC,cAAc;GAClC,OAAO3G,mCAAmCC,OAAO;EACnD;EACA2G,SAAS,IAAIpJ,0BAA0B;EACvCqJ,cAAc;CAChB,CACO/E;AACT"}
|
|
1
|
+
{"version":3,"file":"useAssistantTransportRuntime.js","names":["c","_c","AppendMessage","ReadonlyJSONObject","ReadonlyJSONValue","asAsyncIterableStream","useExternalStoreRuntime","AssistantRuntime","AddToolResultOptions","useMemo","useRef","useState","AssistantMessageAccumulator","DataStreamDecoder","AssistantTransportDecoder","unstable_createInitialMessage","createInitialMessage","toToolsJSONSchema","AssistantTransportOptions","AddMessageCommand","AddToolResultCommand","UserMessagePart","QueuedCommand","AssistantTransportCommand","SendCommandsRequestBody","useCommandQueue","createReplayBoundaryStream","useReplayRenderWait","useRunManager","useConvertedState","ToolExecutionStatus","createRequestHeaders","useRemoteThreadListRuntime","InMemoryThreadListAdapter","useAui","useAuiState","UserExternalState","convertAppendMessageToCommand","message","role","Error","parts","content","attachments","flatMap","a","contentPart","type","push","text","image","length","parentId","sourceId","symbolAssistantTransportExtras","Symbol","AssistantTransportExtras","sendCommand","command","state","asAssistantTransportExtras","extras","useAssistantTransportSendCommand","$","aui","t0","thread","getState","transportExtras","useAssistantTransportState","selector","T","t","s","useAssistantTransportThreadRuntime","options","agentStateRef","initialState","rerender","resumeFlagRef","isReplaying","setIsReplaying","waitForReplayRender","parentIdRef","undefined","commandQueue","onQueue","runManager","schedule","enqueueAppendMessage","console","warn","current","enqueue","startRun","threadId","threadListItem","remoteId","onRun","signal","AbortSignal","isResume","commands","flush","headers","bodyValue","body","context","runtime","getModelContext","requestBody","Record","system","tools","callSettings","config","prepareSendCommandsRequest","response","fetch","resumeApi","api","method","JSON","stringify","onResponse","ok","status","setReplaying","waitForRender","protocol","strict","decoder","err","stream","pipeThrough","initialMessage","unstable_state","throttle","onError","error","markedDelivered","chunk","metadata","markDelivered","prev","queued","onFinish","onCancel","cmds","inTransit","reset","updateState","updater","inTransitCmds","queuedCmds","toolStatuses","setToolStatuses","pendingCommands","converted","converter","isRunning","messages","isLoading","adapters","unstable_enableToolInvocations","onNew","Promise","capabilities","edit","onEdit","onReload","cancel","onResume","onAddToolResult","toolOptions","toolCallId","result","toolName","isError","artifact","modelContent","onLoadExternalState","useAssistantTransportRuntime","runtimeHook","RuntimeHook","adapter","allowNesting"],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { AppendMessage } from \"@assistant-ui/core\";\nimport {\n type ReadonlyJSONObject,\n type ReadonlyJSONValue,\n asAsyncIterableStream,\n} from \"assistant-stream/utils\";\nimport { useExternalStoreRuntime } from \"../external-store/useExternalStoreRuntime\";\nimport type { AssistantRuntime } from \"../../runtime/AssistantRuntime\";\nimport type { AddToolResultOptions } from \"@assistant-ui/core\";\nimport { useMemo, useRef, useState } from \"react\";\nimport {\n AssistantMessageAccumulator,\n DataStreamDecoder,\n AssistantTransportDecoder,\n unstable_createInitialMessage as createInitialMessage,\n toToolsJSONSchema,\n} from \"assistant-stream\";\nimport type {\n AssistantTransportOptions,\n AddMessageCommand,\n AddToolResultCommand,\n UserMessagePart,\n QueuedCommand,\n AssistantTransportCommand,\n SendCommandsRequestBody,\n} from \"./types\";\nimport { useCommandQueue } from \"./commandQueue\";\nimport {\n createReplayBoundaryStream,\n useReplayRenderWait,\n} from \"./replayBoundaryStream\";\nimport { useRunManager } from \"./runManager\";\nimport { useConvertedState } from \"./useConvertedState\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport { createRequestHeaders } from \"@assistant-ui/core\";\nimport { useRemoteThreadListRuntime } from \"../remote-thread-list/useRemoteThreadListRuntime\";\nimport { InMemoryThreadListAdapter } from \"@assistant-ui/core\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type { UserExternalState } from \"../../../augmentations\";\n\nconst convertAppendMessageToCommand = (\n message: AppendMessage,\n): AddMessageCommand | null => {\n if (message.role !== \"user\")\n throw new Error(\"Only user messages are supported\");\n\n const parts: UserMessagePart[] = [];\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((a) => a.content) ?? []),\n ];\n for (const contentPart of content) {\n if (contentPart.type === \"text\") {\n parts.push({ type: \"text\", text: contentPart.text });\n } else if (contentPart.type === \"image\") {\n parts.push({ type: \"image\", image: contentPart.image });\n }\n }\n\n if (parts.length === 0) return null;\n\n return {\n type: \"add-message\",\n message: {\n role: \"user\",\n parts,\n },\n parentId: message.parentId,\n sourceId: message.sourceId,\n };\n};\n\nconst symbolAssistantTransportExtras = Symbol(\"assistant-transport-extras\");\ntype AssistantTransportExtras = {\n [symbolAssistantTransportExtras]: true;\n sendCommand: (command: AssistantTransportCommand) => void;\n state: UserExternalState;\n};\n\nconst asAssistantTransportExtras = (\n extras: unknown,\n): AssistantTransportExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolAssistantTransportExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useAssistantTransportRuntime\",\n );\n\n return extras as AssistantTransportExtras;\n};\n\nexport const useAssistantTransportSendCommand = () => {\n const aui = useAui();\n\n return (command: AssistantTransportCommand) => {\n const extras = aui.thread.getState().extras;\n const transportExtras = asAssistantTransportExtras(extras);\n transportExtras.sendCommand(command);\n };\n};\n\nexport function useAssistantTransportState(): UserExternalState;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T,\n): T;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T = (t) => t as T,\n): T | UserExternalState {\n return useAuiState((s) =>\n selector(asAssistantTransportExtras(s.thread.extras).state),\n );\n}\n\nconst useAssistantTransportThreadRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const agentStateRef = useRef(options.initialState);\n const [, rerender] = useState(0);\n const resumeFlagRef = useRef(false);\n const [isReplaying, setIsReplaying] = useState(false);\n const waitForReplayRender = useReplayRenderWait();\n const parentIdRef = useRef<string | null | undefined>(undefined);\n const commandQueue = useCommandQueue({\n onQueue: () => runManager.schedule(),\n });\n\n const enqueueAppendMessage = (message: AppendMessage) => {\n const command = convertAppendMessageToCommand(message);\n if (!command) {\n console.warn(\n \"[assistant-ui] Skipped add-message command with no supported parts\",\n );\n return;\n }\n parentIdRef.current = message.parentId;\n commandQueue.enqueue(command, {\n schedule: message.startRun ?? message.role === \"user\",\n });\n };\n\n const threadId = useAuiState((s) => s.threadListItem.remoteId);\n\n const runManager = useRunManager({\n onRun: async (signal: AbortSignal) => {\n const isResume = resumeFlagRef.current;\n resumeFlagRef.current = false;\n setIsReplaying(false);\n const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();\n if (commands.length === 0 && !isResume) return;\n\n // The flushed batch consumes the parentId; read it alongside the flush\n // (before any awaits) so a mid-run append keeps its own value. Resume\n // runs send no commands, so they neither send nor consume it.\n const parentId = isResume ? undefined : parentIdRef.current;\n if (!isResume) parentIdRef.current = undefined;\n\n const headers = await createRequestHeaders(options.headers);\n const bodyValue =\n typeof options.body === \"function\"\n ? await options.body()\n : options.body;\n const context = runtime.thread.getModelContext();\n\n let requestBody: Record<string, unknown> = {\n commands,\n state: agentStateRef.current,\n system: context.system,\n tools: context.tools ? toToolsJSONSchema(context.tools) : undefined,\n threadId,\n ...(parentId !== undefined && {\n parentId,\n }),\n // nested (new format, aligned with AssistantChatTransport)\n callSettings: context.callSettings,\n config: context.config,\n // @deprecated spread at top level — use nested `callSettings`/`config` instead. Will be removed in a future version.\n ...context.callSettings,\n ...context.config,\n ...(bodyValue ?? {}),\n };\n\n if (options.prepareSendCommandsRequest) {\n requestBody = await options.prepareSendCommandsRequest(\n requestBody as SendCommandsRequestBody,\n );\n }\n\n const response = await fetch(\n isResume ? options.resumeApi! : options.api,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(requestBody),\n signal,\n },\n );\n\n options.onResponse?.(response);\n\n if (!response.ok) {\n throw new Error(`Status ${response.status}: ${await response.text()}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n const body = await createReplayBoundaryStream(response, {\n setReplaying: setIsReplaying,\n waitForRender: waitForReplayRender,\n });\n\n // Select decoder based on protocol option\n const protocol = options.protocol ?? \"data-stream\";\n // Resume replays a best-effort buffer; always reconcile leniently.\n const strict = isResume ? false : (options.strict ?? true);\n const decoder =\n protocol === \"assistant-transport\"\n ? new AssistantTransportDecoder({ strict })\n : new DataStreamDecoder({ strict });\n\n let err: string | undefined;\n const stream = body.pipeThrough(decoder).pipeThrough(\n new AssistantMessageAccumulator({\n initialMessage: createInitialMessage({\n unstable_state:\n (agentStateRef.current as ReadonlyJSONValue) ?? null,\n }),\n throttle: isResume,\n strict,\n onError: (error) => {\n err = error;\n },\n }),\n );\n\n let markedDelivered = false;\n\n for await (const chunk of asAsyncIterableStream(stream)) {\n if (chunk.metadata.unstable_state === agentStateRef.current) continue;\n\n if (!markedDelivered) {\n commandQueue.markDelivered();\n markedDelivered = true;\n }\n\n agentStateRef.current = chunk.metadata.unstable_state as T;\n rerender((prev) => prev + 1);\n }\n\n if (err) {\n throw new Error(err);\n }\n\n // A successful run confirms delivery even when no state-changing\n // chunk was observed.\n if (!markedDelivered) {\n commandQueue.markDelivered();\n }\n\n // commands that coalesced into this resume run must not starve\n if (isResume && commandQueue.state.queued.length > 0) {\n runManager.schedule();\n }\n },\n onFinish: options.onFinish,\n onCancel: () => {\n setIsReplaying(false);\n const cmds = [\n ...commandQueue.state.inTransit,\n ...commandQueue.state.queued,\n ];\n\n commandQueue.reset();\n\n options.onCancel?.({\n commands: cmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n },\n onError: async (error) => {\n setIsReplaying(false);\n const inTransitCmds = [...commandQueue.state.inTransit];\n const queuedCmds = [...commandQueue.state.queued];\n\n commandQueue.reset();\n\n try {\n await options.onError?.(error as Error, {\n commands: inTransitCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n } finally {\n options.onCancel?.({\n commands: queuedCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n error: error as Error,\n });\n }\n },\n });\n\n // Tool execution status state\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n // Reactive conversion of agent state + connection metadata → UI state\n const pendingCommands = useMemo(\n () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],\n [commandQueue.state],\n );\n const converted = useConvertedState(\n options.converter,\n agentStateRef.current,\n pendingCommands,\n runManager.isRunning,\n toolStatuses,\n );\n\n // Create runtime\n const runtime = useExternalStoreRuntime({\n messages: converted.messages,\n state: converted.state,\n isRunning: converted.isRunning,\n isLoading: isReplaying,\n adapters: options.adapters,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n extras: {\n [symbolAssistantTransportExtras]: true,\n sendCommand: (command: AssistantTransportCommand) => {\n commandQueue.enqueue(command);\n },\n state: agentStateRef.current as UserExternalState,\n } satisfies AssistantTransportExtras,\n onNew: async (message: AppendMessage): Promise<void> =>\n enqueueAppendMessage(message),\n ...(options.capabilities?.edit && {\n onEdit: async (message: AppendMessage): Promise<void> =>\n enqueueAppendMessage(message),\n }),\n ...(commandQueue.state.queued.length > 0 && {\n onReload: async (parentId: string | null) => {\n parentIdRef.current = parentId;\n runManager.schedule();\n },\n }),\n onCancel: async () => {\n runManager.cancel();\n },\n onResume: async () => {\n if (!options.resumeApi)\n throw new Error(\"Must pass resumeApi to options to resume runs\");\n\n resumeFlagRef.current = true;\n runManager.schedule();\n },\n onAddToolResult: async (\n toolOptions: AddToolResultOptions,\n ): Promise<void> => {\n const command: AddToolResultCommand = {\n type: \"add-tool-result\",\n toolCallId: toolOptions.toolCallId,\n result: toolOptions.result as ReadonlyJSONObject,\n toolName: toolOptions.toolName,\n isError: toolOptions.isError,\n ...(toolOptions.artifact && { artifact: toolOptions.artifact }),\n ...(toolOptions.modelContent !== undefined && {\n modelContent: toolOptions.modelContent,\n }),\n };\n\n commandQueue.enqueue(command);\n },\n onLoadExternalState: async (state) => {\n agentStateRef.current = state as T;\n rerender((prev) => prev + 1);\n },\n });\n\n return runtime;\n};\n\n/**\n * @alpha This is an experimental API that is subject to change.\n */\nexport const useAssistantTransportRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const runtime = useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useAssistantTransportThreadRuntime(options);\n },\n adapter: new InMemoryThreadListAdapter(),\n allowNesting: true,\n });\n return runtime;\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.3.
|
|
58
|
+
"@assistant-ui/core": "^0.3.5",
|
|
59
59
|
"@assistant-ui/store": "^0.3.3",
|
|
60
60
|
"@assistant-ui/tap": "^0.9.9",
|
|
61
61
|
"@radix-ui/primitive": "^1.1.7",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"@radix-ui/react-use-callback-ref": "^1.1.4",
|
|
67
67
|
"@radix-ui/react-use-controllable-state": "^1.2.6",
|
|
68
68
|
"@radix-ui/react-use-escape-keydown": "^1.1.5",
|
|
69
|
-
"assistant-cloud": "^0.1.
|
|
70
|
-
"assistant-stream": "^0.3.
|
|
69
|
+
"assistant-cloud": "^0.1.38",
|
|
70
|
+
"assistant-stream": "^0.3.33",
|
|
71
71
|
"nanoid": "^6.0.0",
|
|
72
72
|
"radix-ui": "^1.6.7",
|
|
73
73
|
"react-textarea-autosize": "^8.5.9",
|
|
@@ -110,6 +110,12 @@ export type AssistantTransportOptions<T> = {
|
|
|
110
110
|
api: string;
|
|
111
111
|
resumeApi?: string;
|
|
112
112
|
protocol?: AssistantTransportProtocol;
|
|
113
|
+
/**
|
|
114
|
+
* When `false`, stream decoding and state reconciliation tolerate malformed
|
|
115
|
+
* input (invalid chunks are dropped with a console log) instead of throwing.
|
|
116
|
+
* Resume runs always decode leniently. Defaults to `true`.
|
|
117
|
+
*/
|
|
118
|
+
strict?: boolean;
|
|
113
119
|
converter: AssistantTransportStateConverter<T>;
|
|
114
120
|
headers: HeadersValue | (() => Promise<HeadersValue>);
|
|
115
121
|
body?: object | (() => Promise<object | undefined>);
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx
CHANGED
|
@@ -112,6 +112,7 @@ const mountRuntime = (
|
|
|
112
112
|
|
|
113
113
|
afterEach(() => {
|
|
114
114
|
vi.unstubAllGlobals();
|
|
115
|
+
vi.restoreAllMocks();
|
|
115
116
|
});
|
|
116
117
|
|
|
117
118
|
describe("useAssistantTransportRuntime", () => {
|
|
@@ -145,6 +146,36 @@ describe("useAssistantTransportRuntime", () => {
|
|
|
145
146
|
expect(fetchMock.requests).toHaveLength(1);
|
|
146
147
|
});
|
|
147
148
|
|
|
149
|
+
it("skips add-message commands with no supported parts", async () => {
|
|
150
|
+
const fetchMock = installFetch();
|
|
151
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
152
|
+
const { aui, sendCommand } = mountRuntime();
|
|
153
|
+
await waitFor(() =>
|
|
154
|
+
expect(
|
|
155
|
+
(aui().thread.getState().extras as { sendCommand?: unknown })
|
|
156
|
+
?.sendCommand,
|
|
157
|
+
).toBeTypeOf("function"),
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
act(() =>
|
|
161
|
+
aui().thread.append({
|
|
162
|
+
role: "user",
|
|
163
|
+
content: [{ type: "audio", audio: { data: "", format: "mp3" } }],
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
await act(async () => {});
|
|
168
|
+
expect(warn).toHaveBeenCalledWith(
|
|
169
|
+
"[assistant-ui] Skipped add-message command with no supported parts",
|
|
170
|
+
);
|
|
171
|
+
expect(fetchMock.requests).toHaveLength(0);
|
|
172
|
+
|
|
173
|
+
// The skipped message must not leak its parentId into later batches.
|
|
174
|
+
act(() => sendCommand(createMessageCommand("follow-up")));
|
|
175
|
+
await waitFor(() => expect(fetchMock.requests).toHaveLength(1));
|
|
176
|
+
expect(fetchMock.requests[0]!.body).not.toHaveProperty("parentId");
|
|
177
|
+
});
|
|
178
|
+
|
|
148
179
|
it("flushes commands enqueued during a resume run in a follow-up run", async () => {
|
|
149
180
|
const fetchMock = installFetch();
|
|
150
181
|
const { aui, sendCommand } = mountRuntime({
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts
CHANGED
|
@@ -42,7 +42,7 @@ import type { UserExternalState } from "../../../augmentations";
|
|
|
42
42
|
|
|
43
43
|
const convertAppendMessageToCommand = (
|
|
44
44
|
message: AppendMessage,
|
|
45
|
-
): AddMessageCommand => {
|
|
45
|
+
): AddMessageCommand | null => {
|
|
46
46
|
if (message.role !== "user")
|
|
47
47
|
throw new Error("Only user messages are supported");
|
|
48
48
|
|
|
@@ -59,6 +59,8 @@ const convertAppendMessageToCommand = (
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
if (parts.length === 0) return null;
|
|
63
|
+
|
|
62
64
|
return {
|
|
63
65
|
type: "add-message",
|
|
64
66
|
message: {
|
|
@@ -127,6 +129,20 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
127
129
|
onQueue: () => runManager.schedule(),
|
|
128
130
|
});
|
|
129
131
|
|
|
132
|
+
const enqueueAppendMessage = (message: AppendMessage) => {
|
|
133
|
+
const command = convertAppendMessageToCommand(message);
|
|
134
|
+
if (!command) {
|
|
135
|
+
console.warn(
|
|
136
|
+
"[assistant-ui] Skipped add-message command with no supported parts",
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
parentIdRef.current = message.parentId;
|
|
141
|
+
commandQueue.enqueue(command, {
|
|
142
|
+
schedule: message.startRun ?? message.role === "user",
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
130
146
|
const threadId = useAuiState((s) => s.threadListItem.remoteId);
|
|
131
147
|
|
|
132
148
|
const runManager = useRunManager({
|
|
@@ -201,10 +217,12 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
201
217
|
|
|
202
218
|
// Select decoder based on protocol option
|
|
203
219
|
const protocol = options.protocol ?? "data-stream";
|
|
220
|
+
// Resume replays a best-effort buffer; always reconcile leniently.
|
|
221
|
+
const strict = isResume ? false : (options.strict ?? true);
|
|
204
222
|
const decoder =
|
|
205
223
|
protocol === "assistant-transport"
|
|
206
|
-
? new AssistantTransportDecoder()
|
|
207
|
-
: new DataStreamDecoder();
|
|
224
|
+
? new AssistantTransportDecoder({ strict })
|
|
225
|
+
: new DataStreamDecoder({ strict });
|
|
208
226
|
|
|
209
227
|
let err: string | undefined;
|
|
210
228
|
const stream = body.pipeThrough(decoder).pipeThrough(
|
|
@@ -214,6 +232,7 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
214
232
|
(agentStateRef.current as ReadonlyJSONValue) ?? null,
|
|
215
233
|
}),
|
|
216
234
|
throttle: isResume,
|
|
235
|
+
strict,
|
|
217
236
|
onError: (error) => {
|
|
218
237
|
err = error;
|
|
219
238
|
},
|
|
@@ -329,21 +348,11 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
329
348
|
},
|
|
330
349
|
state: agentStateRef.current as UserExternalState,
|
|
331
350
|
} satisfies AssistantTransportExtras,
|
|
332
|
-
onNew: async (message: AppendMessage): Promise<void> =>
|
|
333
|
-
|
|
334
|
-
const command = convertAppendMessageToCommand(message);
|
|
335
|
-
commandQueue.enqueue(command, {
|
|
336
|
-
schedule: message.startRun ?? message.role === "user",
|
|
337
|
-
});
|
|
338
|
-
},
|
|
351
|
+
onNew: async (message: AppendMessage): Promise<void> =>
|
|
352
|
+
enqueueAppendMessage(message),
|
|
339
353
|
...(options.capabilities?.edit && {
|
|
340
|
-
onEdit: async (message: AppendMessage): Promise<void> =>
|
|
341
|
-
|
|
342
|
-
const command = convertAppendMessageToCommand(message);
|
|
343
|
-
commandQueue.enqueue(command, {
|
|
344
|
-
schedule: message.startRun ?? message.role === "user",
|
|
345
|
-
});
|
|
346
|
-
},
|
|
354
|
+
onEdit: async (message: AppendMessage): Promise<void> =>
|
|
355
|
+
enqueueAppendMessage(message),
|
|
347
356
|
}),
|
|
348
357
|
...(commandQueue.state.queued.length > 0 && {
|
|
349
358
|
onReload: async (parentId: string | null) => {
|