@assistant-ui/core 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/internal.d.ts +1 -2
- package/dist/internal.js +1 -2
- 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 +2 -2
- 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.js +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/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/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.js +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.js +2 -2
- 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/utils/data-url.js +2 -2
- package/dist/utils/data-url.js.map +1 -1
- package/package.json +4 -4
- package/src/internal.ts +0 -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/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/useRemoteThreadListRuntime.ts +9 -15
- package/src/runtime/utils/thread-message-like.ts +2 -2
- package/src/runtimes/remote-thread-list/types.ts +5 -4
- package/src/store/scopes/thread.ts +1 -1
- 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/utils/data-url.test.ts +26 -2
- package/src/utils/data-url.ts +2 -2
package/dist/utils/data-url.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//#region src/utils/data-url.ts
|
|
2
2
|
const httpUrlPattern = /^https?:\/\//i;
|
|
3
3
|
function parseDataUrl(value) {
|
|
4
|
-
const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(
|
|
4
|
+
const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(.*)$/i);
|
|
5
5
|
if (!match) return null;
|
|
6
6
|
return {
|
|
7
|
-
mimeType: match[1],
|
|
7
|
+
mimeType: match[1].toLowerCase(),
|
|
8
8
|
data: match[2]
|
|
9
9
|
};
|
|
10
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-url.js","names":["httpUrlPattern","parseDataUrl","value","mimeType","data","match"],"sources":["../../src/utils/data-url.ts"],"sourcesContent":["export const httpUrlPattern = /^https?:\\/\\//i;\n\nexport function parseDataUrl(\n value: string,\n): { mimeType: string; data: string } | null {\n const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(
|
|
1
|
+
{"version":3,"file":"data-url.js","names":["httpUrlPattern","parseDataUrl","value","mimeType","data","match","toLowerCase"],"sources":["../../src/utils/data-url.ts"],"sourcesContent":["export const httpUrlPattern = /^https?:\\/\\//i;\n\nexport function parseDataUrl(\n value: string,\n): { mimeType: string; data: string } | null {\n const match = value.match(/^data:([^;,]+)(?:;[^;,]+)*;base64,(.*)$/i);\n if (!match) return null;\n return { mimeType: match[1]!.toLowerCase(), data: match[2]! };\n}\n"],"mappings":";AAAA,MAAaA,iBAAiB;AAE9B,SAAgBC,aACdC,OAC2C;CAC3C,MAAMG,QAAQH,MAAMG,MAAM,0CAA0C;CACpE,IAAI,CAACA,OAAO,OAAO;CACnB,OAAO;EAAEF,UAAUE,MAAM,EAAE,CAAEC,YAAY;EAAGF,MAAMC,MAAM;CAAI;AAC9D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Framework-agnostic core runtime for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
],
|
|
60
60
|
"sideEffects": false,
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
62
|
+
"assistant-stream": "^0.3.31",
|
|
63
63
|
"nanoid": "^6.0.0"
|
|
64
64
|
},
|
|
65
65
|
"optionalDevDependencies": {
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
"react-dom": "^19.2.8",
|
|
96
96
|
"vitest": "^4.1.10",
|
|
97
97
|
"zustand": "^5.0.14",
|
|
98
|
-
"@assistant-ui/store": "0.3.
|
|
99
|
-
"@assistant-ui/tap": "0.9.
|
|
98
|
+
"@assistant-ui/store": "0.3.2",
|
|
99
|
+
"@assistant-ui/tap": "0.9.8",
|
|
100
100
|
"@assistant-ui/vite": "0.0.11",
|
|
101
101
|
"@assistant-ui/x-buildutils": "0.0.20",
|
|
102
102
|
"assistant-cloud": "0.1.37"
|
package/src/internal.ts
CHANGED
|
@@ -58,12 +58,5 @@ export { isJSONValue, isRecord } from "./utils/json/is-json";
|
|
|
58
58
|
// outbound part conversion lives in one place.
|
|
59
59
|
export { httpUrlPattern, parseDataUrl } from "./utils/data-url";
|
|
60
60
|
|
|
61
|
-
// Runtime extras helper for external-store adapters. Internal because the
|
|
62
|
-
// tap-native runtime path replaces the `thread.extras` side-channel it wraps.
|
|
63
|
-
export {
|
|
64
|
-
createRuntimeExtras,
|
|
65
|
-
type RuntimeExtras,
|
|
66
|
-
} from "./react/runtimes/createRuntimeExtras";
|
|
67
|
-
|
|
68
61
|
export * from "./runtime/internal";
|
|
69
62
|
export * from "./runtimes/internal";
|
|
@@ -19,24 +19,37 @@ export type AssistantProviderBaseProps = PropsWithChildren<{
|
|
|
19
19
|
aui?: AssistantClient | null;
|
|
20
20
|
}>;
|
|
21
21
|
|
|
22
|
+
const AssistantProviderInner: FC<
|
|
23
|
+
PropsWithChildren<{ runtime: AssistantRuntime }>
|
|
24
|
+
> = ({ runtime, children }) => {
|
|
25
|
+
// The runtime has a stable identity but mutates in place: its options are
|
|
26
|
+
// pushed in by an unconditional effect inside <RenderComponent />, so that
|
|
27
|
+
// element must be re-created every commit for React to re-render it and
|
|
28
|
+
// re-run the effect. React Compiler caches <RenderComponent /> on the
|
|
29
|
+
// stable RenderComponent type, which silences the effect and stops option
|
|
30
|
+
// changes (e.g. unstable_enableMessageQueue) from reaching the runtime.
|
|
31
|
+
"use no memo";
|
|
32
|
+
const aui = useAui({ threads: RuntimeAdapter(runtime) });
|
|
33
|
+
const RenderComponent = getRenderComponent(runtime);
|
|
34
|
+
return (
|
|
35
|
+
<AuiProvider value={aui}>
|
|
36
|
+
{RenderComponent && <RenderComponent />}
|
|
37
|
+
{children}
|
|
38
|
+
</AuiProvider>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
22
42
|
export const AssistantProviderBase: FC<AssistantProviderBaseProps> = memo(
|
|
23
|
-
({ runtime, aui
|
|
24
|
-
// The runtime has a stable identity but mutates in place: its options are
|
|
25
|
-
// pushed in by an unconditional effect inside <RenderComponent />, so that
|
|
26
|
-
// element must be re-created every commit for React to re-render it and
|
|
27
|
-
// re-run the effect. React Compiler caches <RenderComponent /> on the
|
|
28
|
-
// stable RenderComponent type, which silences the effect and stops option
|
|
29
|
-
// changes (e.g. unstable_enableMessageQueue) from reaching the runtime.
|
|
30
|
-
"use no memo";
|
|
31
|
-
const aui = useAui({ threads: RuntimeAdapter(runtime) }, { parent });
|
|
32
|
-
const RenderComponent = getRenderComponent(runtime);
|
|
43
|
+
({ runtime, aui = null, children }) => {
|
|
33
44
|
const inner = (
|
|
34
|
-
<
|
|
35
|
-
{RenderComponent && <RenderComponent />}
|
|
45
|
+
<AssistantProviderInner runtime={runtime}>
|
|
36
46
|
{children}
|
|
37
|
-
</
|
|
47
|
+
</AssistantProviderInner>
|
|
48
|
+
);
|
|
49
|
+
return aui ? (
|
|
50
|
+
<AuiProvider value={aui}>{inner}</AuiProvider>
|
|
51
|
+
) : (
|
|
52
|
+
<AuiProvider value={null}>{inner}</AuiProvider>
|
|
38
53
|
);
|
|
39
|
-
if (!parent) return inner;
|
|
40
|
-
return <AuiProvider value={parent}>{inner}</AuiProvider>;
|
|
41
54
|
},
|
|
42
55
|
);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { cleanup, render } from "@testing-library/react";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
import { resource } from "@assistant-ui/tap";
|
|
8
|
+
import { AuiProvider, useAui, type AssistantClient } from "@assistant-ui/store";
|
|
9
|
+
import type { ChatModelAdapter } from "../runtime/utils/chat-model-adapter";
|
|
10
|
+
import { AssistantRuntimeProvider } from "./AssistantRuntimeProvider";
|
|
11
|
+
import { useLocalRuntime } from "./runtimes/useLocalRuntime";
|
|
12
|
+
|
|
13
|
+
type AnyClient = Record<string, any>;
|
|
14
|
+
|
|
15
|
+
const chatModel: ChatModelAdapter = {
|
|
16
|
+
run: async () => ({ content: [] }),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const makeCounterClient = (log?: string[]) => {
|
|
20
|
+
const useCounterClient = () => {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
log?.push("parent tap effect");
|
|
23
|
+
}, []);
|
|
24
|
+
return { getState: () => ({ count: 0 }) };
|
|
25
|
+
};
|
|
26
|
+
return resource(useCounterClient);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
cleanup();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("AssistantRuntimeProvider aui composition", () => {
|
|
34
|
+
it("isolates from ambient context by default", () => {
|
|
35
|
+
let aui!: AnyClient;
|
|
36
|
+
const Consumer = () => {
|
|
37
|
+
aui = useAui();
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const Ambient = ({ children }: { children: ReactNode }) => {
|
|
42
|
+
const ambient = useAui({
|
|
43
|
+
parentScope: makeCounterClient()(),
|
|
44
|
+
} as unknown as useAui.Props);
|
|
45
|
+
return <AuiProvider value={ambient}>{children}</AuiProvider>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const App = () => {
|
|
49
|
+
const runtime = useLocalRuntime(chatModel);
|
|
50
|
+
return (
|
|
51
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
52
|
+
<Consumer />
|
|
53
|
+
</AssistantRuntimeProvider>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
render(
|
|
58
|
+
<Ambient>
|
|
59
|
+
<App />
|
|
60
|
+
</Ambient>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(aui.threads).toBeDefined();
|
|
64
|
+
expect(() => aui.parentScope.getState()).toThrow(
|
|
65
|
+
/The current scope does not have a "parentScope" property/,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("inherits a prop-passed parent and mounts its effects", () => {
|
|
70
|
+
const log: string[] = [];
|
|
71
|
+
let aui!: AnyClient;
|
|
72
|
+
const Consumer = () => {
|
|
73
|
+
aui = useAui();
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
log.push("consumer effect");
|
|
76
|
+
}, []);
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const App = ({ parent }: { parent: AssistantClient }) => {
|
|
81
|
+
const runtime = useLocalRuntime(chatModel);
|
|
82
|
+
return (
|
|
83
|
+
<AssistantRuntimeProvider runtime={runtime} aui={parent}>
|
|
84
|
+
<Consumer />
|
|
85
|
+
</AssistantRuntimeProvider>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const Root = () => {
|
|
90
|
+
const parent = useAui({
|
|
91
|
+
parentScope: makeCounterClient(log)(),
|
|
92
|
+
} as unknown as useAui.Props);
|
|
93
|
+
return <App parent={parent} />;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
render(<Root />);
|
|
97
|
+
|
|
98
|
+
expect(aui.parentScope.getState()).toEqual({ count: 0 });
|
|
99
|
+
expect(log).toEqual(["parent tap effect", "consumer effect"]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { type AssistantStream, createAssistantStream } from "assistant-stream";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type FC,
|
|
4
|
+
type PropsWithChildren,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from "react";
|
|
3
10
|
import { useAui } from "@assistant-ui/store";
|
|
4
11
|
import type {
|
|
5
12
|
RemoteThreadInitializeResponse,
|
|
@@ -276,22 +283,26 @@ export const parseStoredMessageRepository = (
|
|
|
276
283
|
|
|
277
284
|
class AsyncStorageHistoryAdapter implements ThreadHistoryAdapter {
|
|
278
285
|
private storage: AsyncStorageLike;
|
|
279
|
-
private
|
|
286
|
+
private getAui: () => ReturnType<typeof useAui>;
|
|
280
287
|
private prefix: string;
|
|
281
288
|
private mutationQueue: KeyedMutationQueue;
|
|
282
289
|
|
|
283
290
|
constructor(
|
|
284
291
|
storage: AsyncStorageLike,
|
|
285
|
-
|
|
292
|
+
getAui: () => ReturnType<typeof useAui>,
|
|
286
293
|
prefix: string,
|
|
287
294
|
mutationQueue: KeyedMutationQueue,
|
|
288
295
|
) {
|
|
289
296
|
this.storage = storage;
|
|
290
|
-
this.
|
|
297
|
+
this.getAui = getAui;
|
|
291
298
|
this.prefix = prefix;
|
|
292
299
|
this.mutationQueue = mutationQueue;
|
|
293
300
|
}
|
|
294
301
|
|
|
302
|
+
private get aui(): ReturnType<typeof useAui> {
|
|
303
|
+
return this.getAui();
|
|
304
|
+
}
|
|
305
|
+
|
|
295
306
|
private _messagesKey(remoteId: string) {
|
|
296
307
|
return `${this.prefix}messages:${remoteId}`;
|
|
297
308
|
}
|
|
@@ -334,9 +345,19 @@ const createHistoryProvider = (
|
|
|
334
345
|
): FC<PropsWithChildren> => {
|
|
335
346
|
const Provider: FC<PropsWithChildren> = ({ children }) => {
|
|
336
347
|
const aui = useAui();
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
348
|
+
// Not useEffectEvent: history adapter methods run during render (SSR load).
|
|
349
|
+
const auiRef = useRef(aui);
|
|
350
|
+
useEffect(() => {
|
|
351
|
+
auiRef.current = aui;
|
|
352
|
+
});
|
|
353
|
+
const [history] = useState(
|
|
354
|
+
() =>
|
|
355
|
+
new AsyncStorageHistoryAdapter(
|
|
356
|
+
storage,
|
|
357
|
+
() => auiRef.current,
|
|
358
|
+
prefix,
|
|
359
|
+
mutationQueue,
|
|
360
|
+
),
|
|
340
361
|
);
|
|
341
362
|
const adapters = useMemo(() => ({ history }), [history]);
|
|
342
363
|
|
package/src/react/index.ts
CHANGED
|
@@ -226,6 +226,10 @@ export {
|
|
|
226
226
|
type StreamingTimingOptions,
|
|
227
227
|
type StreamingTimingState,
|
|
228
228
|
} from "./runtimes/useStreamingTiming";
|
|
229
|
+
export {
|
|
230
|
+
createRuntimeExtras,
|
|
231
|
+
type RuntimeExtras,
|
|
232
|
+
} from "./runtimes/createRuntimeExtras";
|
|
229
233
|
export { RemoteThreadListHookInstanceManager } from "./runtimes/RemoteThreadListHookInstanceManager";
|
|
230
234
|
export { RemoteThreadListThreadListRuntimeCore } from "./runtimes/RemoteThreadListThreadListRuntimeCore";
|
|
231
235
|
export { useRemoteThreadListRuntime } from "./runtimes/useRemoteThreadListRuntime";
|
|
@@ -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
|
}
|
|
@@ -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
|
};
|
|
@@ -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
|
|
@@ -108,7 +108,7 @@ export type ThreadMethods = {
|
|
|
108
108
|
* @param initialMessages - Optional array of initial messages to populate the thread
|
|
109
109
|
*/
|
|
110
110
|
reset(initialMessages?: readonly ThreadMessageLike[]): void;
|
|
111
|
-
importExternalState
|
|
111
|
+
importExternalState(state: unknown): void;
|
|
112
112
|
message(selector: { id: string } | { index: number }): MessageMethods;
|
|
113
113
|
/** @deprecated This API is still under active development and might change without notice. */
|
|
114
114
|
stopSpeaking(): void;
|