@assistant-ui/core 0.1.3 → 0.1.5
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/react/AssistantRuntimeProvider.d.ts +10 -0
- package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -0
- package/dist/react/AssistantRuntimeProvider.js +16 -0
- package/dist/react/AssistantRuntimeProvider.js.map +1 -0
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts +15 -0
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -0
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +154 -0
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -0
- package/dist/react/adapters/TitleGenerationAdapter.d.ts +6 -0
- package/dist/react/adapters/TitleGenerationAdapter.d.ts.map +1 -0
- package/dist/react/adapters/TitleGenerationAdapter.js +15 -0
- package/dist/react/adapters/TitleGenerationAdapter.js.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/primitive-hooks/index.d.ts +15 -0
- package/dist/react/primitive-hooks/index.d.ts.map +1 -0
- package/dist/react/primitive-hooks/index.js +15 -0
- package/dist/react/primitive-hooks/index.js.map +1 -0
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts +10 -0
- package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useActionBarCopy.js +26 -0
- package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -0
- package/dist/react/primitive-hooks/useActionBarEdit.d.ts +5 -0
- package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useActionBarEdit.js +11 -0
- package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -0
- package/dist/react/primitive-hooks/useActionBarFeedback.d.ts +9 -0
- package/dist/react/primitive-hooks/useActionBarFeedback.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useActionBarFeedback.js +19 -0
- package/dist/react/primitive-hooks/useActionBarFeedback.js.map +1 -0
- package/dist/react/primitive-hooks/useActionBarReload.d.ts +5 -0
- package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useActionBarReload.js +13 -0
- package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -0
- package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts +6 -0
- package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useComposerAddAttachment.js +11 -0
- package/dist/react/primitive-hooks/useComposerAddAttachment.js.map +1 -0
- package/dist/react/primitive-hooks/useComposerCancel.d.ts +5 -0
- package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useComposerCancel.js +11 -0
- package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -0
- package/dist/react/primitive-hooks/useComposerSend.d.ts +5 -0
- package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useComposerSend.js +11 -0
- package/dist/react/primitive-hooks/useComposerSend.js.map +1 -0
- package/dist/react/primitive-hooks/useEditComposerCancel.d.ts +4 -0
- package/dist/react/primitive-hooks/useEditComposerCancel.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useEditComposerCancel.js +10 -0
- package/dist/react/primitive-hooks/useEditComposerCancel.js.map +1 -0
- package/dist/react/primitive-hooks/useEditComposerSend.d.ts +5 -0
- package/dist/react/primitive-hooks/useEditComposerSend.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useEditComposerSend.js +11 -0
- package/dist/react/primitive-hooks/useEditComposerSend.js.map +1 -0
- package/dist/react/primitive-hooks/useMessageBranching.d.ts +7 -0
- package/dist/react/primitive-hooks/useMessageBranching.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useMessageBranching.js +15 -0
- package/dist/react/primitive-hooks/useMessageBranching.js.map +1 -0
- package/dist/react/primitive-hooks/useMessageReload.d.ts +5 -0
- package/dist/react/primitive-hooks/useMessageReload.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useMessageReload.js +11 -0
- package/dist/react/primitive-hooks/useMessageReload.js.map +1 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +2 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.js +5 -0
- package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +2 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.js +5 -0
- package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -0
- package/dist/react/primitive-hooks/useThreadMessages.d.ts +3 -0
- package/dist/react/primitive-hooks/useThreadMessages.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useThreadMessages.js +5 -0
- package/dist/react/primitive-hooks/useThreadMessages.js.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +2 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/index.d.ts +1 -0
- package/dist/react/runtimes/index.d.ts.map +1 -1
- package/dist/react/runtimes/index.js +1 -0
- package/dist/react/runtimes/index.js.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.d.ts +28 -0
- package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -0
- package/dist/react/runtimes/useLocalRuntime.js +64 -0
- package/dist/react/runtimes/useLocalRuntime.js.map +1 -0
- package/package.json +2 -2
- package/src/react/AssistantRuntimeProvider.tsx +33 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +227 -0
- package/src/react/adapters/TitleGenerationAdapter.ts +20 -0
- package/src/react/index.ts +15 -0
- package/src/react/primitive-hooks/index.ts +20 -0
- package/src/react/primitive-hooks/useActionBarCopy.ts +38 -0
- package/src/react/primitive-hooks/useActionBarEdit.ts +13 -0
- package/src/react/primitive-hooks/useActionBarFeedback.ts +28 -0
- package/src/react/primitive-hooks/useActionBarReload.ts +18 -0
- package/src/react/primitive-hooks/useComposerAddAttachment.ts +17 -0
- package/src/react/primitive-hooks/useComposerCancel.ts +13 -0
- package/src/react/primitive-hooks/useComposerSend.ts +15 -0
- package/src/react/primitive-hooks/useEditComposerCancel.ts +12 -0
- package/src/react/primitive-hooks/useEditComposerSend.ts +13 -0
- package/src/react/primitive-hooks/useMessageBranching.ts +18 -0
- package/src/react/primitive-hooks/useMessageReload.ts +13 -0
- package/src/react/primitive-hooks/useThreadIsEmpty.ts +5 -0
- package/src/react/primitive-hooks/useThreadIsRunning.ts +5 -0
- package/src/react/primitive-hooks/useThreadMessages.ts +6 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +3 -1
- package/src/react/runtimes/index.ts +6 -0
- package/src/react/runtimes/useLocalRuntime.ts +101 -0
- package/src/tests/no-unsafe-process-env.test.ts +58 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { useThreadMessages } from "./useThreadMessages";
|
|
2
|
+
export { useThreadIsRunning } from "./useThreadIsRunning";
|
|
3
|
+
export { useThreadIsEmpty } from "./useThreadIsEmpty";
|
|
4
|
+
export { useComposerSend } from "./useComposerSend";
|
|
5
|
+
export { useComposerCancel } from "./useComposerCancel";
|
|
6
|
+
export { useMessageReload } from "./useMessageReload";
|
|
7
|
+
export { useMessageBranching } from "./useMessageBranching";
|
|
8
|
+
export {
|
|
9
|
+
useActionBarCopy,
|
|
10
|
+
type UseActionBarCopyOptions,
|
|
11
|
+
} from "./useActionBarCopy";
|
|
12
|
+
export { useActionBarEdit } from "./useActionBarEdit";
|
|
13
|
+
export { useActionBarReload } from "./useActionBarReload";
|
|
14
|
+
export {
|
|
15
|
+
useActionBarFeedbackPositive,
|
|
16
|
+
useActionBarFeedbackNegative,
|
|
17
|
+
} from "./useActionBarFeedback";
|
|
18
|
+
export { useComposerAddAttachment } from "./useComposerAddAttachment";
|
|
19
|
+
export { useEditComposerCancel } from "./useEditComposerCancel";
|
|
20
|
+
export { useEditComposerSend } from "./useEditComposerSend";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export type UseActionBarCopyOptions = {
|
|
5
|
+
copiedDuration?: number | undefined;
|
|
6
|
+
copyToClipboard?: ((text: string) => void | Promise<void>) | undefined;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const useActionBarCopy = ({
|
|
10
|
+
copiedDuration = 3000,
|
|
11
|
+
copyToClipboard,
|
|
12
|
+
}: UseActionBarCopyOptions = {}) => {
|
|
13
|
+
const aui = useAui();
|
|
14
|
+
const disabled = useAuiState((s) => {
|
|
15
|
+
return !(
|
|
16
|
+
(s.message.role !== "assistant" ||
|
|
17
|
+
s.message.status?.type !== "running") &&
|
|
18
|
+
s.message.parts.some((c) => c.type === "text" && c.text.length > 0)
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
const isCopied = useAuiState((s) => s.message.isCopied);
|
|
22
|
+
const isEditing = useAuiState((s) => s.composer.isEditing);
|
|
23
|
+
const composerValue = useAuiState((s) => s.composer.text);
|
|
24
|
+
|
|
25
|
+
const copy = useCallback(() => {
|
|
26
|
+
const valueToCopy = isEditing ? composerValue : aui.message().getCopyText();
|
|
27
|
+
if (!valueToCopy) return;
|
|
28
|
+
|
|
29
|
+
const write = copyToClipboard ?? (() => {});
|
|
30
|
+
const result = write(valueToCopy);
|
|
31
|
+
Promise.resolve(result).then(() => {
|
|
32
|
+
aui.message().setIsCopied(true);
|
|
33
|
+
setTimeout(() => aui.message().setIsCopied(false), copiedDuration);
|
|
34
|
+
});
|
|
35
|
+
}, [aui, isEditing, composerValue, copiedDuration, copyToClipboard]);
|
|
36
|
+
|
|
37
|
+
return { copy, disabled, isCopied };
|
|
38
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useActionBarEdit = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState((s) => s.composer.isEditing);
|
|
7
|
+
|
|
8
|
+
const edit = useCallback(() => {
|
|
9
|
+
aui.composer().beginEdit();
|
|
10
|
+
}, [aui]);
|
|
11
|
+
|
|
12
|
+
return { edit, disabled };
|
|
13
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useActionBarFeedbackPositive = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const isSubmitted = useAuiState(
|
|
7
|
+
(s) => s.message.metadata.submittedFeedback?.type === "positive",
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const submit = useCallback(() => {
|
|
11
|
+
aui.message().submitFeedback({ type: "positive" });
|
|
12
|
+
}, [aui]);
|
|
13
|
+
|
|
14
|
+
return { submit, isSubmitted };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const useActionBarFeedbackNegative = () => {
|
|
18
|
+
const aui = useAui();
|
|
19
|
+
const isSubmitted = useAuiState(
|
|
20
|
+
(s) => s.message.metadata.submittedFeedback?.type === "negative",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const submit = useCallback(() => {
|
|
24
|
+
aui.message().submitFeedback({ type: "negative" });
|
|
25
|
+
}, [aui]);
|
|
26
|
+
|
|
27
|
+
return { submit, isSubmitted };
|
|
28
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useActionBarReload = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState(
|
|
7
|
+
(s) =>
|
|
8
|
+
s.thread.isRunning ||
|
|
9
|
+
s.thread.isDisabled ||
|
|
10
|
+
s.message.role !== "assistant",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const reload = useCallback(() => {
|
|
14
|
+
aui.message().reload();
|
|
15
|
+
}, [aui]);
|
|
16
|
+
|
|
17
|
+
return { reload, disabled };
|
|
18
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
import type { CreateAttachment } from "../../types/attachment";
|
|
4
|
+
|
|
5
|
+
export const useComposerAddAttachment = () => {
|
|
6
|
+
const aui = useAui();
|
|
7
|
+
const disabled = useAuiState((s) => !s.composer.isEditing);
|
|
8
|
+
|
|
9
|
+
const addAttachment = useCallback(
|
|
10
|
+
(file: File | CreateAttachment) => {
|
|
11
|
+
return aui.composer().addAttachment(file);
|
|
12
|
+
},
|
|
13
|
+
[aui],
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
return { addAttachment, disabled };
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useComposerCancel = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState((s) => !s.composer.canCancel);
|
|
7
|
+
|
|
8
|
+
const cancel = useCallback(() => {
|
|
9
|
+
aui.composer().cancel();
|
|
10
|
+
}, [aui]);
|
|
11
|
+
|
|
12
|
+
return { cancel, disabled };
|
|
13
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useComposerSend = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState(
|
|
7
|
+
(s) => s.thread.isRunning || !s.composer.isEditing || s.composer.isEmpty,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const send = useCallback(() => {
|
|
11
|
+
aui.composer().send();
|
|
12
|
+
}, [aui]);
|
|
13
|
+
|
|
14
|
+
return { send, disabled };
|
|
15
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useEditComposerCancel = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
|
|
7
|
+
const cancel = useCallback(() => {
|
|
8
|
+
aui.composer().cancel();
|
|
9
|
+
}, [aui]);
|
|
10
|
+
|
|
11
|
+
return { cancel };
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useEditComposerSend = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState((s) => s.composer.isEmpty);
|
|
7
|
+
|
|
8
|
+
const send = useCallback(() => {
|
|
9
|
+
aui.composer().send();
|
|
10
|
+
}, [aui]);
|
|
11
|
+
|
|
12
|
+
return { send, disabled };
|
|
13
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useMessageBranching = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const branchNumber = useAuiState((s) => s.message.branchNumber);
|
|
7
|
+
const branchCount = useAuiState((s) => s.message.branchCount);
|
|
8
|
+
|
|
9
|
+
const goToPrev = useCallback(() => {
|
|
10
|
+
aui.message().switchToBranch({ position: "previous" });
|
|
11
|
+
}, [aui]);
|
|
12
|
+
|
|
13
|
+
const goToNext = useCallback(() => {
|
|
14
|
+
aui.message().switchToBranch({ position: "next" });
|
|
15
|
+
}, [aui]);
|
|
16
|
+
|
|
17
|
+
return { branchNumber, branchCount, goToPrev, goToNext };
|
|
18
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useMessageReload = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const canReload = useAuiState((s) => s.message.role === "assistant");
|
|
7
|
+
|
|
8
|
+
const reload = useCallback(() => {
|
|
9
|
+
aui.message().reload();
|
|
10
|
+
}, [aui]);
|
|
11
|
+
|
|
12
|
+
return { reload, canReload };
|
|
13
|
+
};
|
|
@@ -26,7 +26,9 @@ type CloudThreadListAdapterOptions = {
|
|
|
26
26
|
delete?: ((threadId: string) => Promise<void>) | undefined;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const baseUrl =
|
|
29
|
+
const baseUrl =
|
|
30
|
+
typeof process !== "undefined" &&
|
|
31
|
+
process?.env?.["NEXT_PUBLIC_ASSISTANT_BASE_URL"];
|
|
30
32
|
const autoCloud = baseUrl
|
|
31
33
|
? new AssistantCloud({ baseUrl, anonymous: true })
|
|
32
34
|
: undefined;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import type {
|
|
3
|
+
AssistantRuntime,
|
|
4
|
+
ChatModelAdapter,
|
|
5
|
+
ThreadMessageLike,
|
|
6
|
+
} from "../../index";
|
|
7
|
+
import type { LocalRuntimeOptionsBase } from "../../runtimes/local/local-runtime-options";
|
|
8
|
+
import { AssistantRuntimeImpl, LocalRuntimeCore } from "../../internal";
|
|
9
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
10
|
+
import { useRemoteThreadListRuntime } from "./useRemoteThreadListRuntime";
|
|
11
|
+
import { useCloudThreadListAdapter } from "./cloud";
|
|
12
|
+
import { useRuntimeAdapters } from "./RuntimeAdapterProvider";
|
|
13
|
+
import type { AssistantCloud } from "assistant-cloud";
|
|
14
|
+
|
|
15
|
+
export type LocalRuntimeOptions = Omit<LocalRuntimeOptionsBase, "adapters"> & {
|
|
16
|
+
cloud?: AssistantCloud | undefined;
|
|
17
|
+
initialMessages?: readonly ThreadMessageLike[] | undefined;
|
|
18
|
+
adapters?: Omit<LocalRuntimeOptionsBase["adapters"], "chatModel"> | undefined;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const useLocalThreadRuntime = (
|
|
22
|
+
chatModel: ChatModelAdapter,
|
|
23
|
+
{ initialMessages, ...options }: LocalRuntimeOptions,
|
|
24
|
+
): AssistantRuntime => {
|
|
25
|
+
const { modelContext, ...threadListAdapters } = useRuntimeAdapters() ?? {};
|
|
26
|
+
const opt = {
|
|
27
|
+
...options,
|
|
28
|
+
adapters: {
|
|
29
|
+
...threadListAdapters,
|
|
30
|
+
...options.adapters,
|
|
31
|
+
chatModel,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const [runtime] = useState(() => new LocalRuntimeCore(opt, initialMessages));
|
|
36
|
+
|
|
37
|
+
const threadIdRef = useRef<string | undefined>(undefined);
|
|
38
|
+
threadIdRef.current = useAuiState((s) => s.threadListItem.remoteId);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
runtime.threads
|
|
42
|
+
.getMainThreadRuntimeCore()
|
|
43
|
+
.__internal_setGetThreadId(() => threadIdRef.current);
|
|
44
|
+
}, [runtime]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
return () => {
|
|
48
|
+
runtime.threads.getMainThreadRuntimeCore().detach();
|
|
49
|
+
};
|
|
50
|
+
}, [runtime]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
runtime.threads.getMainThreadRuntimeCore().__internal_setOptions(opt);
|
|
54
|
+
runtime.threads.getMainThreadRuntimeCore().__internal_load();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!modelContext) return undefined;
|
|
59
|
+
return runtime.registerModelContextProvider(modelContext);
|
|
60
|
+
}, [modelContext, runtime]);
|
|
61
|
+
|
|
62
|
+
return useMemo(() => new AssistantRuntimeImpl(runtime), [runtime]);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const splitLocalRuntimeOptions = <T extends LocalRuntimeOptions>(
|
|
66
|
+
options: T,
|
|
67
|
+
) => {
|
|
68
|
+
const {
|
|
69
|
+
cloud,
|
|
70
|
+
initialMessages,
|
|
71
|
+
maxSteps,
|
|
72
|
+
adapters,
|
|
73
|
+
unstable_humanToolNames,
|
|
74
|
+
...rest
|
|
75
|
+
} = options;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
localRuntimeOptions: {
|
|
79
|
+
cloud,
|
|
80
|
+
initialMessages,
|
|
81
|
+
maxSteps,
|
|
82
|
+
adapters,
|
|
83
|
+
unstable_humanToolNames,
|
|
84
|
+
},
|
|
85
|
+
otherOptions: rest,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const useLocalRuntime = (
|
|
90
|
+
chatModel: ChatModelAdapter,
|
|
91
|
+
{ cloud, ...options }: LocalRuntimeOptions = {},
|
|
92
|
+
): AssistantRuntime => {
|
|
93
|
+
const cloudAdapter = useCloudThreadListAdapter({ cloud });
|
|
94
|
+
return useRemoteThreadListRuntime({
|
|
95
|
+
runtimeHook: function RuntimeHook() {
|
|
96
|
+
return useLocalThreadRuntime(chatModel, options);
|
|
97
|
+
},
|
|
98
|
+
adapter: cloudAdapter,
|
|
99
|
+
allowNesting: true,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { resolve, relative, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const SRC_DIR = resolve(__dirname, "..");
|
|
6
|
+
|
|
7
|
+
function findFiles(dir: string, ext: string[]): string[] {
|
|
8
|
+
const results: string[] = [];
|
|
9
|
+
for (const entry of readdirSync(dir)) {
|
|
10
|
+
const full = join(dir, entry);
|
|
11
|
+
if (statSync(full).isDirectory()) {
|
|
12
|
+
results.push(...findFiles(full, ext));
|
|
13
|
+
} else if (ext.some((e) => full.endsWith(e))) {
|
|
14
|
+
results.push(full);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return results;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("no unsafe process.env access", () => {
|
|
21
|
+
it("all process.env access for non-NODE_ENV vars must have a typeof process guard", () => {
|
|
22
|
+
const files = findFiles(SRC_DIR, [".ts", ".tsx"]).filter(
|
|
23
|
+
(f) => !f.includes("/tests/") && !f.includes(".test."),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const violations: string[] = [];
|
|
27
|
+
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
const content = readFileSync(file, "utf-8");
|
|
30
|
+
|
|
31
|
+
// Check if file has process.env access beyond NODE_ENV
|
|
32
|
+
// Match process.env, process?.env, process.env?, process?.env?
|
|
33
|
+
const hasNonNodeEnvProcessAccess =
|
|
34
|
+
/process\??\.env\??(?!\.NODE_ENV\b)/.test(
|
|
35
|
+
content.replace(/process\??\.env\??\.NODE_ENV/g, ""),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!hasNonNodeEnvProcessAccess) continue;
|
|
39
|
+
|
|
40
|
+
// File accesses process.env for non-NODE_ENV vars — must have typeof guard
|
|
41
|
+
const hasTypeofGuard = /typeof process\s*!==\s*["']undefined["']/.test(
|
|
42
|
+
content,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (!hasTypeofGuard) {
|
|
46
|
+
violations.push(relative(SRC_DIR, file));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expect(
|
|
51
|
+
violations,
|
|
52
|
+
`These files access process.env (non-NODE_ENV) without a typeof process !== "undefined" guard.\n` +
|
|
53
|
+
`@assistant-ui/core lacks @types/node, so bare process.env access crashes in Vite and other bundlers.\n` +
|
|
54
|
+
`Add: typeof process !== "undefined" ? process.env?.["VAR_NAME"] : undefined\n\n` +
|
|
55
|
+
`Files:\n${violations.map((f) => ` - ${f}`).join("\n")}`,
|
|
56
|
+
).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
});
|