@assistant-ui/core 0.3.1 → 0.3.3
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.ts +4 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +6 -7
- package/dist/internal.js +5 -6
- package/dist/react/AssistantProvider.d.ts +1 -1
- package/dist/react/AssistantProvider.d.ts.map +1 -1
- package/dist/react/AssistantProvider.js +35 -9
- package/dist/react/AssistantProvider.js.map +1 -1
- package/dist/react/AssistantRuntimeProvider.d.ts +1 -1
- package/dist/react/AssistantRuntimeProvider.js +1 -1
- package/dist/react/RuntimeAdapter.js +2 -2
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +17 -33
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/DataRenderers.js +1 -1
- package/dist/react/client/Interactables.js +1 -1
- package/dist/react/client/Tools.js +2 -2
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/interactables-legacy/Interactables.js +1 -1
- package/dist/react/interactables-legacy/useAssistantInteractable.js +1 -1
- package/dist/react/interactables-legacy/useInteractableState.js +2 -2
- package/dist/react/model-context/useAssistantContext.js +1 -1
- package/dist/react/model-context/useAssistantDataUI.js +2 -2
- package/dist/react/model-context/useAssistantInstructions.js +2 -2
- package/dist/react/model-context/useAssistantTool.js +2 -2
- package/dist/react/model-context/useAssistantToolUI.js +2 -2
- package/dist/react/model-context/useAuiToolOverrides.js +1 -1
- package/dist/react/model-context/useInlineRender.js +1 -1
- package/dist/react/model-context/useInteractable.js +1 -1
- package/dist/react/model-context/useInteractableState.js +2 -2
- package/dist/react/model-context/useInteractableVersions.js +2 -2
- package/dist/react/model-context/useToolArgsStatus.js +2 -2
- package/dist/react/primitive-hooks/useActionBarCopy.js +52 -25
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
- package/dist/react/primitive-hooks/useActionBarEdit.js +2 -2
- package/dist/react/primitive-hooks/useActionBarFeedback.js +2 -2
- package/dist/react/primitive-hooks/useActionBarReload.js +2 -2
- package/dist/react/primitive-hooks/useActionBarSpeak.js +2 -2
- package/dist/react/primitive-hooks/useActionBarStopSpeaking.js +2 -2
- package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -2
- package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -2
- package/dist/react/primitive-hooks/useComposerAddAttachment.js +2 -2
- package/dist/react/primitive-hooks/useComposerCancel.js +2 -2
- package/dist/react/primitive-hooks/useComposerDictate.js +2 -2
- package/dist/react/primitive-hooks/useComposerSend.js +2 -2
- package/dist/react/primitive-hooks/useEditComposerCancel.js +2 -2
- package/dist/react/primitive-hooks/useEditComposerSend.js +2 -2
- package/dist/react/primitive-hooks/useMessageBranching.js +2 -2
- package/dist/react/primitive-hooks/useMessageReload.js +2 -2
- package/dist/react/primitive-hooks/useSuggestionTrigger.js +2 -2
- package/dist/react/primitive-hooks/useThreadListItemArchive.js +2 -2
- package/dist/react/primitive-hooks/useThreadListItemDelete.js +2 -2
- package/dist/react/primitive-hooks/useThreadListItemTrigger.js +2 -2
- package/dist/react/primitive-hooks/useThreadListItemUnarchive.js +2 -2
- package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -2
- package/dist/react/primitive-hooks/useThreadListNew.js +2 -2
- package/dist/react/primitive-hooks/useThreadMessageIds.js +1 -1
- package/dist/react/primitive-hooks/useVoice.js +2 -2
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js +2 -2
- package/dist/react/primitives/composer/ComposerAttachments.js +2 -2
- package/dist/react/primitives/composer/ComposerIf.js +1 -1
- package/dist/react/primitives/composer/ComposerQueue.js +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.js +2 -2
- package/dist/react/primitives/message/MessageAttachments.js +2 -2
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +2 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +3 -3
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +2 -2
- package/dist/react/primitives/message/MessageQuote.js +2 -2
- package/dist/react/primitives/part/PartMessages.js +2 -2
- package/dist/react/primitives/thread/ThreadMessages.js +2 -2
- package/dist/react/primitives/thread/ThreadSuggestions.js +2 -2
- package/dist/react/primitives/threadList/ThreadListItems.js +2 -2
- package/dist/react/primitives/threadListItem/ThreadListItemTitle.js +1 -1
- package/dist/react/providers/AttachmentByIndexProvider.js +1 -1
- package/dist/react/providers/ChainOfThoughtByIndicesProvider.js +1 -1
- package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js +1 -1
- package/dist/react/providers/MessageByIdProvider.js +1 -1
- package/dist/react/providers/MessageByIndexProvider.js +1 -1
- package/dist/react/providers/PartByIndexProvider.js +1 -1
- package/dist/react/providers/QueueItemByIndexProvider.js +1 -1
- package/dist/react/providers/ReadonlyThreadProvider.js +2 -2
- package/dist/react/providers/SuggestionByIndexProvider.js +1 -1
- package/dist/react/providers/TextMessagePartProvider.js +2 -2
- package/dist/react/providers/ThreadListItemByIndexProvider.js +1 -1
- package/dist/react/providers/ThreadListItemRuntimeProvider.js +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +14 -13
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +3 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +22 -12
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RuntimeAdapterProvider.js +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +9 -23
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +24 -2
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +24 -5
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +1 -1
- package/dist/react/runtimes/useExternalStoreSharedOptions.js +1 -1
- package/dist/react/runtimes/useLocalRuntime.js +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +7 -13
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/utils/useJSONEqual.js +1 -1
- package/dist/runtime/api/message-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-runtime.js +5 -1
- package/dist/runtime/api/message-runtime.js.map +1 -1
- package/dist/runtime/internal.d.ts +4 -4
- package/dist/runtime/internal.js +4 -4
- package/dist/runtime/utils/thread-message-like.js +2 -2
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +5 -4
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
- package/dist/store/clients/chain-of-thought-client.js +30 -24
- package/dist/store/clients/chain-of-thought-client.js.map +1 -1
- package/dist/store/clients/model-context-client.js +1 -1
- package/dist/store/clients/no-op-composer-client.js +1 -1
- package/dist/store/clients/suggestions.js +2 -2
- package/dist/store/clients/thread-message-client.d.ts.map +1 -1
- package/dist/store/clients/thread-message-client.js +103 -91
- package/dist/store/clients/thread-message-client.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +2 -2
- package/dist/store/runtime-clients/message-runtime-client.js +2 -2
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +2 -2
- package/dist/store/runtime-clients/thread-list-runtime-client.js +2 -2
- package/dist/store/runtime-clients/thread-runtime-client.js +2 -2
- package/dist/store/scopes/thread.d.ts +1 -1
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/message.d.ts +11 -1
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/data-url.js +2 -2
- package/dist/utils/data-url.js.map +1 -1
- package/dist/utils/getGroupStatus.d.ts +9 -0
- package/dist/utils/getGroupStatus.d.ts.map +1 -0
- package/dist/utils/getGroupStatus.js +15 -0
- package/dist/utils/getGroupStatus.js.map +1 -0
- package/dist/utils/normalizePartStatus.d.ts +8 -0
- package/dist/utils/normalizePartStatus.d.ts.map +1 -0
- package/dist/utils/normalizePartStatus.js +39 -0
- package/dist/utils/normalizePartStatus.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/internal.ts +1 -7
- package/src/react/AssistantProvider.tsx +28 -15
- package/src/react/AssistantRuntimeProvider.test.tsx +101 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +28 -7
- package/src/react/index.ts +4 -0
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +109 -14
- package/src/react/primitive-hooks/useActionBarCopy.ts +27 -2
- package/src/react/primitives/message/MessageGroupedParts.tsx +4 -4
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +16 -13
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +53 -10
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +7 -5
- package/src/react/runtimes/cloud/auiV0.ts +51 -12
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +9 -15
- package/src/runtime/api/message-runtime.test.ts +140 -0
- package/src/runtime/api/message-runtime.ts +10 -5
- package/src/runtime/utils/thread-message-like.ts +2 -2
- package/src/runtimes/remote-thread-list/types.ts +5 -4
- package/src/store/clients/chain-of-thought-client.ts +2 -7
- package/src/store/clients/thread-message-client.test.ts +84 -0
- package/src/store/clients/thread-message-client.ts +14 -4
- package/src/store/scopes/thread.ts +1 -1
- package/src/tests/auiV0Encode.test.ts +93 -0
- package/src/tests/remote-thread-list-reactive-threadId.test.ts +0 -92
- package/src/tests/thread-message-like.test.ts +15 -0
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +293 -0
- package/src/types/index.ts +1 -0
- package/src/types/message.ts +19 -0
- package/src/utils/data-url.test.ts +26 -2
- package/src/utils/data-url.ts +2 -2
- package/src/utils/getGroupStatus.test.ts +36 -0
- package/src/utils/getGroupStatus.ts +31 -0
- package/src/utils/normalizePartStatus.test.ts +59 -0
- package/src/utils/normalizePartStatus.ts +55 -0
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
useCallback,
|
|
5
5
|
useRef,
|
|
6
6
|
useEffect,
|
|
7
|
+
useEffectEvent,
|
|
7
8
|
useLayoutEffect,
|
|
8
9
|
memo,
|
|
9
10
|
type PropsWithChildren,
|
|
@@ -142,23 +143,25 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
|
|
|
142
143
|
}
|
|
143
144
|
}, [threadBinding]);
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
hasInitializedRef.current
|
|
147
|
-
return runtime.threads.main.unstable_on("initialize", () => {
|
|
148
|
-
if (hasInitializedRef.current) return;
|
|
146
|
+
const handleInitialize = useEffectEvent(() => {
|
|
147
|
+
if (hasInitializedRef.current) return;
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
const state = aui.threadListItem.getState();
|
|
150
|
+
if (state.status !== "new") return;
|
|
151
|
+
hasInitializedRef.current = true;
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
initPromiseRef.current = aui.threadListItem.initialize();
|
|
155
154
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
});
|
|
155
|
+
const dispose = runtime.thread.unstable_on("runEnd", () => {
|
|
156
|
+
dispose();
|
|
157
|
+
aui.threadListItem.generateTitle();
|
|
160
158
|
});
|
|
161
|
-
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
hasInitializedRef.current = false;
|
|
163
|
+
return runtime.threads.main.unstable_on("initialize", handleInitialize);
|
|
164
|
+
}, [runtime]);
|
|
162
165
|
|
|
163
166
|
return <>{children}</>;
|
|
164
167
|
};
|
|
@@ -200,6 +200,10 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
200
200
|
|
|
201
201
|
const adapterChanged =
|
|
202
202
|
this._options !== undefined && this._options.adapter !== options.adapter;
|
|
203
|
+
const controlledThreadIdChanged =
|
|
204
|
+
this._initialThreadLoaded &&
|
|
205
|
+
this._options !== undefined &&
|
|
206
|
+
this._options.threadId !== options.threadId;
|
|
203
207
|
|
|
204
208
|
this._options = options;
|
|
205
209
|
|
|
@@ -219,15 +223,25 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
219
223
|
cursor: undefined,
|
|
220
224
|
});
|
|
221
225
|
}
|
|
226
|
+
|
|
227
|
+
if (controlledThreadIdChanged) {
|
|
228
|
+
this._switchToThreadFromProp(options.threadId).catch(() => {});
|
|
229
|
+
}
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
public __internal_load() {
|
|
225
233
|
this.getLoadThreadsPromise(); // begin loading on initial bind
|
|
234
|
+
if (this._initialThreadLoaded) return;
|
|
235
|
+
this._initialThreadLoaded = true;
|
|
236
|
+
|
|
226
237
|
const startThreadId =
|
|
227
238
|
this._options.threadId ?? this._options.initialThreadId;
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
if (startThreadId !== undefined) {
|
|
240
|
+
const switchTask =
|
|
241
|
+
this._options.threadId !== undefined
|
|
242
|
+
? this._switchToThreadFromProp(startThreadId)
|
|
243
|
+
: this.switchToThread(startThreadId);
|
|
244
|
+
switchTask.catch(() => {});
|
|
231
245
|
}
|
|
232
246
|
}
|
|
233
247
|
|
|
@@ -279,11 +293,13 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
279
293
|
|
|
280
294
|
private _lastNotifiedThreadId: string | undefined = undefined;
|
|
281
295
|
|
|
282
|
-
private _notifyThreadIdChange() {
|
|
296
|
+
private _notifyThreadIdChange(emit = true) {
|
|
283
297
|
const threadId = this._mainThreadRemoteId;
|
|
284
298
|
if (this._lastNotifiedThreadId === threadId) return;
|
|
285
299
|
this._lastNotifiedThreadId = threadId;
|
|
286
|
-
|
|
300
|
+
if (emit) {
|
|
301
|
+
this._options.onThreadIdChange?.(threadId);
|
|
302
|
+
}
|
|
287
303
|
}
|
|
288
304
|
|
|
289
305
|
public getMainThreadRuntimeCore() {
|
|
@@ -312,9 +328,22 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
312
328
|
public switchToThread(
|
|
313
329
|
threadIdOrRemoteId: string,
|
|
314
330
|
options?: { unarchive?: boolean },
|
|
331
|
+
): Promise<void> {
|
|
332
|
+
return this._startSwitchToThread(threadIdOrRemoteId, options, true);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private _startSwitchToThread(
|
|
336
|
+
threadIdOrRemoteId: string,
|
|
337
|
+
options: { unarchive?: boolean } | undefined,
|
|
338
|
+
emitThreadIdChange: boolean,
|
|
315
339
|
): Promise<void> {
|
|
316
340
|
const generation = ++this._switchGeneration;
|
|
317
|
-
const task = this._switchToThread(
|
|
341
|
+
const task = this._switchToThread(
|
|
342
|
+
threadIdOrRemoteId,
|
|
343
|
+
options,
|
|
344
|
+
generation,
|
|
345
|
+
emitThreadIdChange,
|
|
346
|
+
);
|
|
318
347
|
this._switchTask = task;
|
|
319
348
|
return task;
|
|
320
349
|
}
|
|
@@ -323,6 +352,7 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
323
352
|
threadIdOrRemoteId: string,
|
|
324
353
|
options: { unarchive?: boolean } | undefined,
|
|
325
354
|
generation: number,
|
|
355
|
+
emitThreadIdChange: boolean,
|
|
326
356
|
): Promise<void> {
|
|
327
357
|
let data = this.getItemById(threadIdOrRemoteId);
|
|
328
358
|
|
|
@@ -418,17 +448,30 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
418
448
|
this._mainThreadId = data.id;
|
|
419
449
|
|
|
420
450
|
this._notifySubscribers();
|
|
421
|
-
this._notifyThreadIdChange();
|
|
451
|
+
this._notifyThreadIdChange(emitThreadIdChange);
|
|
422
452
|
}
|
|
423
453
|
|
|
424
454
|
public switchToNewThread(): Promise<void> {
|
|
455
|
+
return this._startSwitchToNewThread(true);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private _switchToThreadFromProp(threadId: string | undefined): Promise<void> {
|
|
459
|
+
return threadId !== undefined
|
|
460
|
+
? this._startSwitchToThread(threadId, undefined, false)
|
|
461
|
+
: this._startSwitchToNewThread(false);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private _startSwitchToNewThread(emitThreadIdChange: boolean): Promise<void> {
|
|
425
465
|
const generation = ++this._switchGeneration;
|
|
426
|
-
const task = this._switchToNewThread(generation);
|
|
466
|
+
const task = this._switchToNewThread(generation, emitThreadIdChange);
|
|
427
467
|
this._switchTask = task;
|
|
428
468
|
return task;
|
|
429
469
|
}
|
|
430
470
|
|
|
431
|
-
private async _switchToNewThread(
|
|
471
|
+
private async _switchToNewThread(
|
|
472
|
+
generation: number,
|
|
473
|
+
emitThreadIdChange: boolean,
|
|
474
|
+
): Promise<void> {
|
|
432
475
|
// an initialization transaction is in progress, wait for it to settle
|
|
433
476
|
while (
|
|
434
477
|
this._state.baseValue.newThreadId !== undefined &&
|
|
@@ -467,7 +510,7 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
467
510
|
});
|
|
468
511
|
}
|
|
469
512
|
|
|
470
|
-
return this._switchToThread(id, undefined, generation);
|
|
513
|
+
return this._switchToThread(id, undefined, generation, emitThreadIdChange);
|
|
471
514
|
}
|
|
472
515
|
|
|
473
516
|
public initialize = async (threadId: string) => {
|
|
@@ -22,18 +22,18 @@ const globalPersistence = new WeakMap<
|
|
|
22
22
|
|
|
23
23
|
class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
|
|
24
24
|
private cloudRef: RefObject<AssistantCloud>;
|
|
25
|
-
private
|
|
25
|
+
private getAui: () => AssistantClient;
|
|
26
26
|
|
|
27
27
|
constructor(
|
|
28
28
|
cloudRef: RefObject<AssistantCloud>,
|
|
29
|
-
|
|
29
|
+
getAui: () => AssistantClient,
|
|
30
30
|
) {
|
|
31
31
|
this.cloudRef = cloudRef;
|
|
32
|
-
this.
|
|
32
|
+
this.getAui = getAui;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
private get aui(): AssistantClient {
|
|
36
|
-
return this.
|
|
36
|
+
return this.getAui();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
private get _persistence(): CloudMessagePersistence {
|
|
@@ -816,12 +816,14 @@ export function useAssistantCloudThreadHistoryAdapter(
|
|
|
816
816
|
cloudRef: RefObject<AssistantCloud>,
|
|
817
817
|
): ThreadHistoryAdapter {
|
|
818
818
|
const aui = useAui();
|
|
819
|
+
// Not useEffectEvent: history adapter methods run during render (SSR load).
|
|
819
820
|
const auiRef = useRef(aui);
|
|
820
821
|
useEffect(() => {
|
|
821
822
|
auiRef.current = aui;
|
|
822
823
|
});
|
|
823
824
|
const [adapter] = useState(
|
|
824
|
-
() =>
|
|
825
|
+
() =>
|
|
826
|
+
new AssistantCloudThreadHistoryAdapter(cloudRef, () => auiRef.current),
|
|
825
827
|
);
|
|
826
828
|
return adapter;
|
|
827
829
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
DataMessagePart,
|
|
3
|
-
FileMessagePart,
|
|
4
|
-
ImageMessagePart,
|
|
5
2
|
MessageStatus,
|
|
6
3
|
SourceProviderMetadata,
|
|
7
4
|
ThreadMessage,
|
|
8
|
-
TextMessagePart,
|
|
9
5
|
ToolApprovalOption,
|
|
10
|
-
Unstable_AudioMessagePart,
|
|
11
6
|
} from "../../../types/message";
|
|
12
7
|
import type { CompleteAttachment } from "../../../types/attachment";
|
|
13
8
|
import { fromThreadMessageLike } from "../../../runtime/utils/thread-message-like";
|
|
@@ -85,11 +80,33 @@ type AuiV0MessagePart =
|
|
|
85
80
|
};
|
|
86
81
|
|
|
87
82
|
type AuiV0AttachmentPart =
|
|
88
|
-
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
|
83
|
+
| {
|
|
84
|
+
readonly type: "text";
|
|
85
|
+
readonly text: string;
|
|
86
|
+
}
|
|
87
|
+
| {
|
|
88
|
+
readonly type: "image";
|
|
89
|
+
readonly image: string;
|
|
90
|
+
readonly filename?: string;
|
|
91
|
+
}
|
|
92
|
+
| {
|
|
93
|
+
readonly type: "file";
|
|
94
|
+
readonly data: string;
|
|
95
|
+
readonly mimeType: string;
|
|
96
|
+
readonly filename?: string;
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
readonly type: "audio";
|
|
100
|
+
readonly audio: {
|
|
101
|
+
readonly data: string;
|
|
102
|
+
readonly format: "mp3" | "wav";
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
readonly type: "data";
|
|
107
|
+
readonly name: string;
|
|
108
|
+
readonly data: ReadonlyJSONValue;
|
|
109
|
+
};
|
|
93
110
|
|
|
94
111
|
type AuiV0Attachment = {
|
|
95
112
|
readonly id: string;
|
|
@@ -125,16 +142,38 @@ const encodeAttachmentPart = (
|
|
|
125
142
|
const type = part.type;
|
|
126
143
|
switch (type) {
|
|
127
144
|
case "text":
|
|
145
|
+
return { type: "text", text: part.text };
|
|
146
|
+
|
|
128
147
|
case "image":
|
|
148
|
+
return {
|
|
149
|
+
type: "image",
|
|
150
|
+
image: part.image,
|
|
151
|
+
...(part.filename != null ? { filename: part.filename } : undefined),
|
|
152
|
+
};
|
|
153
|
+
|
|
129
154
|
case "file":
|
|
155
|
+
return {
|
|
156
|
+
type: "file",
|
|
157
|
+
data: part.data,
|
|
158
|
+
mimeType: part.mimeType,
|
|
159
|
+
...(part.filename != null ? { filename: part.filename } : undefined),
|
|
160
|
+
};
|
|
161
|
+
|
|
130
162
|
case "audio":
|
|
131
|
-
return
|
|
163
|
+
return {
|
|
164
|
+
type: "audio",
|
|
165
|
+
audio: { data: part.audio.data, format: part.audio.format },
|
|
166
|
+
};
|
|
132
167
|
|
|
133
168
|
case "data": {
|
|
134
169
|
if (!isJSONValue(part.data)) {
|
|
135
170
|
console.warn(`attachment data is not JSON! ${JSON.stringify(part)}`);
|
|
136
171
|
}
|
|
137
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
type: "data",
|
|
174
|
+
name: part.name,
|
|
175
|
+
data: part.data as ReadonlyJSONValue,
|
|
176
|
+
};
|
|
138
177
|
}
|
|
139
178
|
|
|
140
179
|
default: {
|
|
@@ -51,8 +51,7 @@ export const useRemoteThreadListRuntime = (
|
|
|
51
51
|
const runtimeHookRef = useRef(options.runtimeHook);
|
|
52
52
|
runtimeHookRef.current = options.runtimeHook;
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
const startThreadIdRef = useRef(options.threadId ?? options.initialThreadId);
|
|
54
|
+
const initialThreadIdRef = useRef(options.initialThreadId);
|
|
56
55
|
|
|
57
56
|
const stableRuntimeHook = useCallback(() => {
|
|
58
57
|
return runtimeHookRef.current();
|
|
@@ -66,11 +65,17 @@ export const useRemoteThreadListRuntime = (
|
|
|
66
65
|
() => ({
|
|
67
66
|
adapter: options.adapter,
|
|
68
67
|
allowNesting: options.allowNesting,
|
|
69
|
-
|
|
68
|
+
threadId: options.threadId,
|
|
69
|
+
initialThreadId: initialThreadIdRef.current,
|
|
70
70
|
runtimeHook: stableRuntimeHook,
|
|
71
71
|
onThreadIdChange,
|
|
72
72
|
}),
|
|
73
|
-
[
|
|
73
|
+
[
|
|
74
|
+
options.adapter,
|
|
75
|
+
options.allowNesting,
|
|
76
|
+
options.threadId,
|
|
77
|
+
stableRuntimeHook,
|
|
78
|
+
],
|
|
74
79
|
);
|
|
75
80
|
|
|
76
81
|
const aui = useAui();
|
|
@@ -91,16 +96,5 @@ export const useRemoteThreadListRuntime = (
|
|
|
91
96
|
|
|
92
97
|
const runtime = useRemoteThreadListRuntimeImpl(stableOptions);
|
|
93
98
|
|
|
94
|
-
const prevThreadIdRef = useRef(options.threadId);
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
if (options.threadId === prevThreadIdRef.current) return;
|
|
97
|
-
prevThreadIdRef.current = options.threadId;
|
|
98
|
-
if (options.threadId) {
|
|
99
|
-
runtime.threads.switchToThread(options.threadId).catch(() => {});
|
|
100
|
-
} else {
|
|
101
|
-
runtime.threads.switchToNewThread().catch(() => {});
|
|
102
|
-
}
|
|
103
|
-
}, [runtime, options.threadId]);
|
|
104
|
-
|
|
105
99
|
return runtime;
|
|
106
100
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import type { CompleteAttachment } from "../../types/attachment";
|
|
3
|
+
import type { ThreadAssistantMessage } from "../../types/message";
|
|
3
4
|
import type { ThreadRuntimeCoreBinding } from "./thread-runtime";
|
|
4
5
|
import {
|
|
5
6
|
MessageRuntimeImpl,
|
|
7
|
+
toMessagePartStatus,
|
|
6
8
|
type MessageState,
|
|
7
9
|
type MessageStateBinding,
|
|
8
10
|
} from "./message-runtime";
|
|
@@ -63,6 +65,144 @@ const threadBinding = {
|
|
|
63
65
|
subscribe: () => () => {},
|
|
64
66
|
} as unknown as ThreadRuntimeCoreBinding;
|
|
65
67
|
|
|
68
|
+
const createAssistantMessage = (
|
|
69
|
+
content: ThreadAssistantMessage["content"],
|
|
70
|
+
status: ThreadAssistantMessage["status"],
|
|
71
|
+
): ThreadAssistantMessage => ({
|
|
72
|
+
id: "message-1",
|
|
73
|
+
role: "assistant",
|
|
74
|
+
createdAt: new Date(0),
|
|
75
|
+
content,
|
|
76
|
+
status,
|
|
77
|
+
metadata: {
|
|
78
|
+
unstable_state: null,
|
|
79
|
+
unstable_annotations: [],
|
|
80
|
+
unstable_data: [],
|
|
81
|
+
steps: [],
|
|
82
|
+
custom: {},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("toMessagePartStatus", () => {
|
|
87
|
+
it("honours a supplied running status on a non-last part", () => {
|
|
88
|
+
const message = createAssistantMessage(
|
|
89
|
+
[
|
|
90
|
+
{ type: "text", text: "first", status: { type: "running" } },
|
|
91
|
+
{ type: "text", text: "last" },
|
|
92
|
+
],
|
|
93
|
+
{ type: "running" },
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(toMessagePartStatus(message, 0, message.content[0]!)).toEqual({
|
|
97
|
+
type: "running",
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("honours a supplied complete status on the last part", () => {
|
|
102
|
+
const message = createAssistantMessage(
|
|
103
|
+
[{ type: "reasoning", text: "done", status: { type: "complete" } }],
|
|
104
|
+
{ type: "running" },
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(toMessagePartStatus(message, 0, message.content[0]!)).toEqual({
|
|
108
|
+
type: "complete",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("ignores supplied statuses after the message completes", () => {
|
|
113
|
+
const message = createAssistantMessage(
|
|
114
|
+
[{ type: "text", text: "truncated", status: { type: "running" } }],
|
|
115
|
+
{ type: "complete", reason: "stop" },
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(toMessagePartStatus(message, 0, message.content[0]!)).toEqual({
|
|
119
|
+
type: "complete",
|
|
120
|
+
reason: "stop",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("falls back to positional statuses for statusless running parts", () => {
|
|
125
|
+
const message = createAssistantMessage(
|
|
126
|
+
[
|
|
127
|
+
{ type: "text", text: "first" },
|
|
128
|
+
{ type: "reasoning", text: "last" },
|
|
129
|
+
],
|
|
130
|
+
{ type: "running" },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(toMessagePartStatus(message, 0, message.content[0]!)).toEqual({
|
|
134
|
+
type: "complete",
|
|
135
|
+
});
|
|
136
|
+
expect(toMessagePartStatus(message, 1, message.content[1]!)).toEqual({
|
|
137
|
+
type: "running",
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("preserves tool-call status derivation", () => {
|
|
142
|
+
const unresolved = createAssistantMessage(
|
|
143
|
+
[
|
|
144
|
+
{
|
|
145
|
+
type: "tool-call",
|
|
146
|
+
toolCallId: "call-1",
|
|
147
|
+
toolName: "weather",
|
|
148
|
+
args: {},
|
|
149
|
+
argsText: "{}",
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
{ type: "running" },
|
|
153
|
+
);
|
|
154
|
+
const resolved = createAssistantMessage(
|
|
155
|
+
[
|
|
156
|
+
{
|
|
157
|
+
type: "tool-call",
|
|
158
|
+
toolCallId: "call-1",
|
|
159
|
+
toolName: "weather",
|
|
160
|
+
args: {},
|
|
161
|
+
argsText: "{}",
|
|
162
|
+
result: "sunny",
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
{ type: "running" },
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(toMessagePartStatus(unresolved, 0, unresolved.content[0]!)).toEqual({
|
|
169
|
+
type: "running",
|
|
170
|
+
});
|
|
171
|
+
expect(toMessagePartStatus(resolved, 0, resolved.content[0]!)).toEqual({
|
|
172
|
+
type: "complete",
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("normalizes supplied upstream statuses", () => {
|
|
177
|
+
const upstreamComplete = {
|
|
178
|
+
type: "text",
|
|
179
|
+
text: "done",
|
|
180
|
+
status: { type: "complete", reason: "unknown" },
|
|
181
|
+
} as unknown as ThreadAssistantMessage["content"][number];
|
|
182
|
+
const upstreamIncomplete = {
|
|
183
|
+
type: "reasoning",
|
|
184
|
+
text: "interrupted",
|
|
185
|
+
status: {
|
|
186
|
+
type: "incomplete",
|
|
187
|
+
reason: "unknown",
|
|
188
|
+
error: "upstream error",
|
|
189
|
+
},
|
|
190
|
+
} as unknown as ThreadAssistantMessage["content"][number];
|
|
191
|
+
const message = createAssistantMessage(
|
|
192
|
+
[upstreamComplete, upstreamIncomplete],
|
|
193
|
+
{ type: "running" },
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
expect(toMessagePartStatus(message, 0, message.content[0]!)).toEqual({
|
|
197
|
+
type: "complete",
|
|
198
|
+
});
|
|
199
|
+
expect(toMessagePartStatus(message, 1, message.content[1]!)).toEqual({
|
|
200
|
+
type: "incomplete",
|
|
201
|
+
reason: "other",
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
66
206
|
describe("MessageRuntimeImpl paths", () => {
|
|
67
207
|
it("appends nested selectors to the message path", () => {
|
|
68
208
|
const runtime = new MessageRuntimeImpl(messageBinding, threadBinding);
|
|
@@ -8,6 +8,10 @@ import type {
|
|
|
8
8
|
} from "../../types/message";
|
|
9
9
|
import type { Unsubscribe } from "../../types/unsubscribe";
|
|
10
10
|
import type { MessagePartStatus, RunConfig } from "../../types/message";
|
|
11
|
+
import {
|
|
12
|
+
COMPLETE_STATUS,
|
|
13
|
+
normalizePartStatus,
|
|
14
|
+
} from "../../utils/normalizePartStatus";
|
|
11
15
|
import { getThreadMessageText } from "../../utils/text";
|
|
12
16
|
import { NestedSubscriptionSubject } from "../../subscribable/subscribable";
|
|
13
17
|
import {
|
|
@@ -32,10 +36,6 @@ import type { MessageRuntimePath } from "./paths";
|
|
|
32
36
|
import type { ThreadRuntimeCoreBinding } from "./thread-runtime";
|
|
33
37
|
import type { MessageStateBinding } from "./bindings";
|
|
34
38
|
|
|
35
|
-
const COMPLETE_STATUS: MessagePartStatus = Object.freeze({
|
|
36
|
-
type: "complete",
|
|
37
|
-
});
|
|
38
|
-
|
|
39
39
|
export const toMessagePartStatus = (
|
|
40
40
|
message: ThreadMessage,
|
|
41
41
|
partIndex: number,
|
|
@@ -51,6 +51,11 @@ export const toMessagePartStatus = (
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
if (message.status.type === "running") {
|
|
55
|
+
const status = normalizePartStatus(part);
|
|
56
|
+
if (status) return status;
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
const isLastPart = partIndex === Math.max(0, message.content.length - 1);
|
|
55
60
|
if (message.status.type === "requires-action") return COMPLETE_STATUS;
|
|
56
61
|
return isLastPart ? (message.status as MessagePartStatus) : COMPLETE_STATUS;
|
|
@@ -70,7 +75,7 @@ const getMessagePartState = (
|
|
|
70
75
|
return Object.freeze({
|
|
71
76
|
...part,
|
|
72
77
|
...{ [symbolInnerMessage]: (part as any)[symbolInnerMessage] },
|
|
73
|
-
status,
|
|
78
|
+
status: status as MessagePartStatus,
|
|
74
79
|
});
|
|
75
80
|
};
|
|
76
81
|
|
|
@@ -131,12 +131,12 @@ export const fromThreadMessageLike = (
|
|
|
131
131
|
}: ImageMessagePart): ImageMessagePart | null => {
|
|
132
132
|
if (typeof image !== "string") return null;
|
|
133
133
|
const dataUri = image.match(
|
|
134
|
-
/^data:image\/(png|jpeg|jpg|gif|webp|svg\+xml);base64,(.*)
|
|
134
|
+
/^data:image\/(png|jpeg|jpg|gif|webp|svg\+xml);base64,(.*)$/i,
|
|
135
135
|
);
|
|
136
136
|
if (dataUri) {
|
|
137
137
|
return { ...rest, image };
|
|
138
138
|
}
|
|
139
|
-
if (/^(https:\/\/|blob:)
|
|
139
|
+
if (/^(https:\/\/|blob:)/i.test(image)) {
|
|
140
140
|
return { ...rest, image };
|
|
141
141
|
}
|
|
142
142
|
console.warn(`Invalid image data format detected`);
|
|
@@ -78,10 +78,11 @@ export type RemoteThreadListOptions = {
|
|
|
78
78
|
threadId?: string | undefined;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Called whenever the active thread's canonical (remote)
|
|
82
|
-
* value can be treated as a managed/controlled variable (e.g.
|
|
83
|
-
* URL query param).
|
|
84
|
-
*
|
|
81
|
+
* Called whenever the runtime changes the active thread's canonical (remote)
|
|
82
|
+
* ID, so the value can be treated as a managed/controlled variable (e.g.
|
|
83
|
+
* synced to a URL query param). Changes initiated by the controlled
|
|
84
|
+
* `threadId` option are not echoed back. Together these options form the
|
|
85
|
+
* controlled pattern: `threadId` in, `onThreadIdChange` out.
|
|
85
86
|
*
|
|
86
87
|
* Only the settled remote ID is emitted: while a freshly created thread is
|
|
87
88
|
* still optimistic (no remote ID yet) the value is `undefined`, and the real
|
|
@@ -5,12 +5,8 @@ import type {
|
|
|
5
5
|
ChainOfThoughtState,
|
|
6
6
|
ChainOfThoughtPart,
|
|
7
7
|
} from "../scopes/chain-of-thought";
|
|
8
|
-
import type { MessagePartStatus } from "../../types/message";
|
|
9
8
|
import type { PartMethods } from "../scopes/part";
|
|
10
|
-
|
|
11
|
-
const COMPLETE_STATUS: MessagePartStatus = Object.freeze({
|
|
12
|
-
type: "complete",
|
|
13
|
-
});
|
|
9
|
+
import { getGroupStatus } from "../../utils/getGroupStatus";
|
|
14
10
|
|
|
15
11
|
const useChainOfThoughtClient = ({
|
|
16
12
|
parts,
|
|
@@ -22,8 +18,7 @@ const useChainOfThoughtClient = ({
|
|
|
22
18
|
const [collapsed, setCollapsed] = useState(true);
|
|
23
19
|
|
|
24
20
|
const status = useMemo(() => {
|
|
25
|
-
|
|
26
|
-
return lastPart?.status ?? COMPLETE_STATUS;
|
|
21
|
+
return getGroupStatus(parts);
|
|
27
22
|
}, [parts]);
|
|
28
23
|
|
|
29
24
|
const state = useMemo<ChainOfThoughtState>(
|