@autobe/ui 0.22.0 → 0.23.0
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/lib/components/AutoBeChatMain.d.ts +6 -4
- package/lib/components/AutoBeChatMain.js +204 -56
- package/lib/components/AutoBeChatMain.js.map +1 -1
- package/lib/components/AutoBeChatSidebar.d.ts +36 -0
- package/lib/components/AutoBeChatSidebar.js +227 -0
- package/lib/components/AutoBeChatSidebar.js.map +1 -0
- package/lib/components/AutoBeConfigButton.d.ts +15 -0
- package/lib/components/AutoBeConfigButton.js +33 -0
- package/lib/components/AutoBeConfigButton.js.map +1 -0
- package/lib/components/AutoBeConfigModal.d.ts +59 -0
- package/lib/components/AutoBeConfigModal.js +294 -0
- package/lib/components/AutoBeConfigModal.js.map +1 -0
- package/lib/components/AutoBeStatusButton.d.ts +12 -0
- package/lib/components/AutoBeStatusButton.js +29 -0
- package/lib/components/AutoBeStatusButton.js.map +1 -0
- package/lib/components/AutoBeStatusModal.js +35 -15
- package/lib/components/AutoBeStatusModal.js.map +1 -1
- package/lib/components/common/ActionButton.d.ts +16 -0
- package/lib/components/common/ActionButton.js +115 -0
- package/lib/components/common/ActionButton.js.map +1 -0
- package/lib/components/common/ActionButtonGroup.d.ts +13 -0
- package/lib/components/common/ActionButtonGroup.js +37 -0
- package/lib/components/common/ActionButtonGroup.js.map +1 -0
- package/lib/components/common/AutoBeConfigInput.d.ts +24 -0
- package/lib/components/common/AutoBeConfigInput.js +90 -0
- package/lib/components/common/AutoBeConfigInput.js.map +1 -0
- package/lib/components/common/CompactSessionIndicator.d.ts +16 -0
- package/lib/components/common/CompactSessionIndicator.js +46 -0
- package/lib/components/common/CompactSessionIndicator.js.map +1 -0
- package/lib/components/common/CompactSessionList.d.ts +22 -0
- package/lib/components/common/CompactSessionList.js +40 -0
- package/lib/components/common/CompactSessionList.js.map +1 -0
- package/lib/components/common/index.d.ts +6 -0
- package/lib/components/common/index.js +6 -0
- package/lib/components/common/index.js.map +1 -1
- package/lib/components/events/AutoBeEventGroupMovie.d.ts +6 -0
- package/lib/components/events/AutoBeEventGroupMovie.js +11 -0
- package/lib/components/events/AutoBeEventGroupMovie.js.map +1 -0
- package/lib/components/events/AutoBeEventMovie.js +5 -0
- package/lib/components/events/AutoBeEventMovie.js.map +1 -1
- package/lib/components/events/AutoBeValidateEventMovie.js +1 -3
- package/lib/components/events/AutoBeValidateEventMovie.js.map +1 -1
- package/lib/components/events/common/CollapsibleEventGroup.d.ts +1 -1
- package/lib/components/events/groups/ValidateEventGroup.d.ts +1 -1
- package/lib/components/events/utils/eventGrouper.js.map +1 -1
- package/lib/components/index.d.ts +6 -0
- package/lib/components/index.js +7 -0
- package/lib/components/index.js.map +1 -1
- package/lib/components/upload/AutoBeChatUploadBox.js +75 -33
- package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -1
- package/lib/context/AutoBeAgentContext.d.ts +22 -11
- package/lib/context/AutoBeAgentContext.js +127 -11
- package/lib/context/AutoBeAgentContext.js.map +1 -1
- package/lib/context/AutoBeAgentSessionList.d.ts +12 -0
- package/lib/context/AutoBeAgentSessionList.js +37 -0
- package/lib/context/AutoBeAgentSessionList.js.map +1 -0
- package/lib/context/SearchParamsContext.d.ts +10 -0
- package/lib/context/SearchParamsContext.js +29 -0
- package/lib/context/SearchParamsContext.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/structure/AutoBeListener.d.ts +6 -0
- package/lib/structure/AutoBeListener.js +21 -4
- package/lib/structure/AutoBeListener.js.map +1 -1
- package/lib/structure/IAutoBeAgentSessionStorageStrategy.d.ts +35 -0
- package/lib/structure/IAutoBeAgentSessionStorageStrategy.js +30 -0
- package/lib/structure/IAutoBeAgentSessionStorageStrategy.js.map +1 -0
- package/lib/structure/index.d.ts +1 -0
- package/lib/structure/index.js +1 -0
- package/lib/structure/index.js.map +1 -1
- package/lib/types/config.d.ts +26 -0
- package/lib/types/config.js +14 -0
- package/lib/types/config.js.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +18 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/__tests__/crypto.test.d.ts +1 -0
- package/lib/utils/__tests__/crypto.test.js +222 -0
- package/lib/utils/__tests__/crypto.test.js.map +1 -0
- package/lib/utils/__tests__/storage.test.d.ts +1 -0
- package/lib/utils/__tests__/storage.test.js +174 -0
- package/lib/utils/__tests__/storage.test.js.map +1 -0
- package/lib/utils/crypto.d.ts +18 -0
- package/lib/utils/crypto.js +84 -0
- package/lib/utils/crypto.js.map +1 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +2 -0
- package/lib/utils/index.js.map +1 -1
- package/lib/utils/storage.d.ts +29 -0
- package/lib/utils/storage.js +93 -0
- package/lib/utils/storage.js.map +1 -0
- package/package.json +11 -3
- package/src/components/AutoBeChatMain.tsx +329 -131
- package/src/components/AutoBeChatSidebar.tsx +414 -0
- package/src/components/AutoBeConfigButton.tsx +83 -0
- package/src/components/AutoBeConfigModal.tsx +444 -0
- package/src/components/AutoBeStatusButton.tsx +75 -0
- package/src/components/AutoBeStatusModal.tsx +55 -54
- package/src/components/common/ActionButton.tsx +205 -0
- package/src/components/common/ActionButtonGroup.tsx +80 -0
- package/src/components/common/AutoBeConfigInput.tsx +185 -0
- package/src/components/common/CompactSessionIndicator.tsx +73 -0
- package/src/components/common/CompactSessionList.tsx +82 -0
- package/src/components/common/index.ts +6 -0
- package/src/components/events/AutoBeEventGroupMovie.tsx +18 -0
- package/src/components/events/AutoBeEventMovie.tsx +5 -0
- package/src/components/events/AutoBeValidateEventMovie.tsx +7 -9
- package/src/components/events/common/CollapsibleEventGroup.tsx +1 -1
- package/src/components/events/groups/ValidateEventGroup.tsx +1 -1
- package/src/components/events/utils/eventGrouper.tsx +2 -1
- package/src/components/index.ts +6 -0
- package/src/components/upload/AutoBeChatUploadBox.tsx +94 -44
- package/src/context/AutoBeAgentContext.tsx +201 -22
- package/src/context/AutoBeAgentSessionList.tsx +58 -0
- package/src/context/SearchParamsContext.tsx +49 -0
- package/src/index.ts +4 -0
- package/src/structure/AutoBeListener.ts +32 -6
- package/src/structure/IAutoBeAgentSessionStorageStrategy.ts +87 -0
- package/src/structure/index.ts +1 -0
- package/src/types/config.ts +44 -0
- package/src/types/index.ts +1 -0
- package/src/utils/__tests__/crypto.test.ts +286 -0
- package/src/utils/__tests__/storage.test.ts +229 -0
- package/src/utils/crypto.ts +95 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/storage.ts +96 -0
- package/vitest.config.ts +15 -0
|
@@ -78,13 +78,11 @@ function getState(event: IAutoBeValidateEventMovieProps["event"]): IState {
|
|
|
78
78
|
<br />
|
|
79
79
|
{event.result.errors.length > 0 && (
|
|
80
80
|
<>
|
|
81
|
-
{event.result.errors
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</div>
|
|
87
|
-
))}
|
|
81
|
+
{event.result.errors.slice(0, 3).map((error, idx) => (
|
|
82
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
83
|
+
• {error.message}
|
|
84
|
+
</div>
|
|
85
|
+
))}
|
|
88
86
|
</>
|
|
89
87
|
)}
|
|
90
88
|
<br />
|
|
@@ -119,7 +117,7 @@ function getState(event: IAutoBeValidateEventMovieProps["event"]): IState {
|
|
|
119
117
|
{event.result.type === "failure" &&
|
|
120
118
|
event.result.diagnostics
|
|
121
119
|
.slice(0, 3)
|
|
122
|
-
.map((diagnostic
|
|
120
|
+
.map((diagnostic, idx) => (
|
|
123
121
|
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
124
122
|
• {diagnostic.messageText}
|
|
125
123
|
</div>
|
|
@@ -194,7 +192,7 @@ function getState(event: IAutoBeValidateEventMovieProps["event"]): IState {
|
|
|
194
192
|
{event.result.type === "failure" &&
|
|
195
193
|
event.result.diagnostics
|
|
196
194
|
.slice(0, 2)
|
|
197
|
-
.map((diagnostic
|
|
195
|
+
.map((diagnostic, idx) => (
|
|
198
196
|
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
199
197
|
• {diagnostic.messageText}
|
|
200
198
|
</div>
|
|
@@ -4,7 +4,7 @@ import { EventCard } from "./EventCard";
|
|
|
4
4
|
import { EventContent } from "./EventContent";
|
|
5
5
|
import { EventIcon, EventIconType } from "./EventIcon";
|
|
6
6
|
|
|
7
|
-
export interface ICollapsibleEventGroupProps<T =
|
|
7
|
+
export interface ICollapsibleEventGroupProps<T = unknown> {
|
|
8
8
|
/** Array of events of the same type */
|
|
9
9
|
events: T[];
|
|
10
10
|
/** Title for the group */
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { AutoBeValidateEventMovie } from "../AutoBeValidateEventMovie";
|
|
12
12
|
import { CollapsibleEventGroup } from "../common/CollapsibleEventGroup";
|
|
13
13
|
|
|
14
|
-
type ValidateEvent =
|
|
14
|
+
export type ValidateEvent =
|
|
15
15
|
| AutoBePrismaInsufficientEvent
|
|
16
16
|
| AutoBePrismaValidateEvent
|
|
17
17
|
| AutoBeInterfaceOperationsReviewEvent
|
|
@@ -2,6 +2,7 @@ import { AutoBeEvent } from "@autobe/interface";
|
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
import { ValidateEventGroup } from "../groups";
|
|
5
|
+
import { ValidateEvent } from "../groups/ValidateEventGroup";
|
|
5
6
|
|
|
6
7
|
/** Configuration for event grouping */
|
|
7
8
|
export interface IEventGrouperConfig {
|
|
@@ -71,7 +72,7 @@ export function groupEvents(
|
|
|
71
72
|
/** Groups events by their category */
|
|
72
73
|
function groupEventsByCategory(events: AutoBeEvent[]) {
|
|
73
74
|
const grouped = {
|
|
74
|
-
validate: [] as
|
|
75
|
+
validate: [] as ValidateEvent[],
|
|
75
76
|
other: [] as AutoBeEvent[],
|
|
76
77
|
};
|
|
77
78
|
|
package/src/components/index.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export * from "./AutoBeConfigButton";
|
|
2
|
+
export * from "./AutoBeConfigModal";
|
|
3
|
+
export { createAutoBeConfigFields } from "./AutoBeConfigModal";
|
|
4
|
+
export type { AutoBeConfigKey } from "./AutoBeConfigModal";
|
|
5
|
+
export * from "./AutoBeStatusButton";
|
|
1
6
|
export * from "./AutoBeStatusModal";
|
|
2
7
|
export * from "./AutoBeChatMain";
|
|
8
|
+
export * from "./AutoBeChatSidebar";
|
|
3
9
|
export * from "./AutoBeUserMessageMovie";
|
|
4
10
|
export * from "./AutoBeAssistantMessageMovie";
|
|
5
11
|
export * from "./common";
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AutoBeFileUploadBox,
|
|
12
12
|
AutoBeVoiceRecoderButton,
|
|
13
13
|
} from ".";
|
|
14
|
+
import { useAutoBeAgent } from "../../context/AutoBeAgentContext";
|
|
14
15
|
import { useMediaQuery } from "../../hooks/useMediaQuery";
|
|
15
16
|
import { AutoBeFileUploader } from "../../utils";
|
|
16
17
|
|
|
@@ -29,7 +30,8 @@ export interface IAutoBeChatUploadConfig {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
|
|
32
|
-
const
|
|
33
|
+
const { listener } = useAutoBeAgent();
|
|
34
|
+
const inputRef = useRef<HTMLDivElement>(null);
|
|
33
35
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
34
36
|
|
|
35
37
|
const [dragging, setDragging] = useState(false);
|
|
@@ -39,11 +41,29 @@ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
|
|
|
39
41
|
const [extensionError, setExtensionError] = useState<ReactNode | null>(null);
|
|
40
42
|
|
|
41
43
|
const [emptyText, setEmptyText] = useState(false);
|
|
44
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
42
45
|
|
|
43
46
|
const removeFile = (index: number) => {
|
|
44
47
|
setBuckets(buckets.filter((_, i) => i !== index));
|
|
45
48
|
};
|
|
46
49
|
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
async function trackEnable(value: boolean) {
|
|
52
|
+
setEnabled(value);
|
|
53
|
+
}
|
|
54
|
+
listener?.onEnable(trackEnable);
|
|
55
|
+
return () => {
|
|
56
|
+
listener?.offEnable(trackEnable);
|
|
57
|
+
};
|
|
58
|
+
}, [listener]);
|
|
59
|
+
|
|
60
|
+
// Sync text state when cleared programmatically (like after sending message)
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (inputRef.current && text === "" && inputRef.current.innerText !== "") {
|
|
63
|
+
inputRef.current.innerText = "";
|
|
64
|
+
}
|
|
65
|
+
}, [text]);
|
|
66
|
+
|
|
47
67
|
const conversate = async () => {
|
|
48
68
|
if (enabled === false) return;
|
|
49
69
|
|
|
@@ -60,7 +80,6 @@ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
|
|
|
60
80
|
...buckets.map(({ content }) => content),
|
|
61
81
|
] as AutoBeUserMessageContent[];
|
|
62
82
|
|
|
63
|
-
setEnabled(false);
|
|
64
83
|
setEmptyText(false);
|
|
65
84
|
setText("");
|
|
66
85
|
setBuckets([]);
|
|
@@ -72,7 +91,6 @@ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
|
|
|
72
91
|
error instanceof Error ? error : new Error("Unknown error"),
|
|
73
92
|
);
|
|
74
93
|
}
|
|
75
|
-
setEnabled(true);
|
|
76
94
|
};
|
|
77
95
|
|
|
78
96
|
const handleFileSelect = async (fileList: FileList | null) => {
|
|
@@ -272,47 +290,79 @@ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
|
|
|
272
290
|
</div>
|
|
273
291
|
)}
|
|
274
292
|
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
293
|
+
<div style={{ position: "relative" }}>
|
|
294
|
+
<div
|
|
295
|
+
ref={inputRef}
|
|
296
|
+
contentEditable={true}
|
|
297
|
+
style={{
|
|
298
|
+
width: "97%",
|
|
299
|
+
minHeight: "40px",
|
|
300
|
+
maxHeight: "192px",
|
|
301
|
+
padding: "8px 12px",
|
|
302
|
+
border: "none",
|
|
303
|
+
borderRadius: "8px",
|
|
304
|
+
fontSize: "0.95rem",
|
|
305
|
+
fontFamily: "inherit",
|
|
306
|
+
outline: "none",
|
|
307
|
+
boxShadow: "none",
|
|
308
|
+
backgroundColor: "transparent",
|
|
309
|
+
transition: "color 0.2s, opacity 0.2s",
|
|
310
|
+
overflowY: "auto",
|
|
311
|
+
wordBreak: "break-word",
|
|
312
|
+
whiteSpace: "pre-wrap",
|
|
313
|
+
lineHeight: "1.4",
|
|
314
|
+
// Custom scrollbar styles
|
|
315
|
+
scrollbarWidth: "thin",
|
|
316
|
+
scrollbarColor: "rgba(0, 0, 0, 0.3) transparent",
|
|
317
|
+
}}
|
|
318
|
+
onKeyDown={(e) => {
|
|
319
|
+
if (e.key === "Enter" && !e.shiftKey && !isComposing) {
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
if (enabled) {
|
|
322
|
+
void conversate();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}}
|
|
326
|
+
onCompositionStart={() => {
|
|
327
|
+
setIsComposing(true);
|
|
328
|
+
}}
|
|
329
|
+
onCompositionEnd={() => {
|
|
330
|
+
setIsComposing(false);
|
|
331
|
+
}}
|
|
332
|
+
onInput={(e) => {
|
|
333
|
+
const target = e.target as HTMLDivElement;
|
|
334
|
+
const newText = target.innerText || "";
|
|
335
|
+
// Only update state if text actually changed to prevent cursor issues
|
|
336
|
+
if (newText !== text) {
|
|
337
|
+
setText(newText);
|
|
338
|
+
}
|
|
339
|
+
}}
|
|
340
|
+
suppressContentEditableWarning={true}
|
|
341
|
+
/>
|
|
342
|
+
{!text && (
|
|
343
|
+
<div
|
|
344
|
+
style={{
|
|
345
|
+
position: "absolute",
|
|
346
|
+
top: "8px",
|
|
347
|
+
left: "12px",
|
|
348
|
+
right: "12px",
|
|
349
|
+
bottom: "8px",
|
|
350
|
+
color: "rgba(153, 153, 153, 0.6)",
|
|
351
|
+
fontSize: "0.95rem",
|
|
352
|
+
fontFamily: "inherit",
|
|
353
|
+
pointerEvents: "none",
|
|
354
|
+
lineHeight: "1.4",
|
|
355
|
+
padding: "2px 0",
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
{emptyText
|
|
359
|
+
? "Cannot send empty message"
|
|
360
|
+
: dragging
|
|
361
|
+
? "Drop files here..."
|
|
362
|
+
: "Conversate with AI Chatbot"}
|
|
363
|
+
</div>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
316
366
|
|
|
317
367
|
<input
|
|
318
368
|
ref={fileInputRef}
|
|
@@ -1,68 +1,247 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
IAutoBeRpcListener,
|
|
3
3
|
IAutoBeRpcService,
|
|
4
4
|
IAutoBeTokenUsageJson,
|
|
5
5
|
} from "@autobe/interface";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
ReactNode,
|
|
8
|
+
createContext,
|
|
9
|
+
useCallback,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
useState,
|
|
13
|
+
} from "react";
|
|
14
|
+
import { Communicator } from "tgrid";
|
|
9
15
|
|
|
10
16
|
import {
|
|
11
17
|
AutoBeListener,
|
|
12
18
|
AutoBeListenerState,
|
|
19
|
+
IAutoBeAgentSessionStorageStrategy,
|
|
13
20
|
IAutoBeEventGroup,
|
|
14
21
|
} from "../structure";
|
|
22
|
+
import { IAutoBeConfig } from "../types/config";
|
|
23
|
+
import { useAutoBeAgentSessionList } from "./AutoBeAgentSessionList";
|
|
24
|
+
import { useSearchParams } from "./SearchParamsContext";
|
|
25
|
+
|
|
26
|
+
export interface IAutoBeServiceData {
|
|
27
|
+
service: IAutoBeRpcService;
|
|
28
|
+
listener: AutoBeListener;
|
|
29
|
+
connector: Communicator<IAutoBeRpcListener, IAutoBeRpcService>;
|
|
30
|
+
close: () => void | Promise<void>;
|
|
31
|
+
sessionId: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type AutoBeServiceFactory = (
|
|
35
|
+
config: IAutoBeConfig,
|
|
36
|
+
) => Promise<IAutoBeServiceData>;
|
|
37
|
+
|
|
38
|
+
export type AutoBeConnectionStatus =
|
|
39
|
+
| "disconnected" // 연결되지 않음
|
|
40
|
+
| "connecting" // 연결 중
|
|
41
|
+
| "connected"; // 연결 완료 및 활성 상태
|
|
15
42
|
|
|
16
43
|
interface AutoBeAgentContextType {
|
|
44
|
+
// Service state
|
|
45
|
+
connectionStatus: AutoBeConnectionStatus;
|
|
46
|
+
|
|
47
|
+
// Service data (available when ready)
|
|
17
48
|
eventGroups: IAutoBeEventGroup[];
|
|
18
49
|
tokenUsage: IAutoBeTokenUsageJson | null;
|
|
19
|
-
state: AutoBeListenerState;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
50
|
+
state: AutoBeListenerState | null;
|
|
51
|
+
service: IAutoBeRpcService | null;
|
|
52
|
+
listener: AutoBeListener | null;
|
|
53
|
+
|
|
54
|
+
// Service management
|
|
55
|
+
getAutoBeService: (config?: IAutoBeConfig) => Promise<IAutoBeServiceData>;
|
|
56
|
+
resetService: () => void;
|
|
23
57
|
}
|
|
24
58
|
|
|
25
59
|
const AutoBeAgentContext = createContext<AutoBeAgentContextType | null>(null);
|
|
26
60
|
|
|
27
61
|
export function AutoBeAgentProvider({
|
|
28
62
|
children,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
header,
|
|
63
|
+
serviceFactory,
|
|
64
|
+
storageStrategy,
|
|
32
65
|
}: {
|
|
33
|
-
|
|
34
|
-
service: IAutoBeRpcService;
|
|
35
|
-
header: IAutoBePlaygroundHeader<ILlmSchema.Model>;
|
|
66
|
+
serviceFactory: AutoBeServiceFactory;
|
|
36
67
|
children: ReactNode;
|
|
68
|
+
storageStrategy: IAutoBeAgentSessionStorageStrategy;
|
|
37
69
|
}) {
|
|
70
|
+
// Service state
|
|
71
|
+
const [connectionStatus, setConnectionStatus] =
|
|
72
|
+
useState<AutoBeConnectionStatus>("disconnected");
|
|
73
|
+
|
|
74
|
+
// Service data
|
|
75
|
+
const { searchParams, setSearchParams } = useSearchParams();
|
|
76
|
+
// Use URL parameter for conversation ID - enables bookmark/share support
|
|
77
|
+
const activeConversationId = searchParams.get("session-id") ?? null;
|
|
78
|
+
|
|
38
79
|
const [tokenUsage, setTokenUsage] = useState<IAutoBeTokenUsageJson | null>(
|
|
39
80
|
null,
|
|
40
81
|
);
|
|
41
82
|
const [eventGroups, setEventGroups] = useState<IAutoBeEventGroup[]>([]);
|
|
42
83
|
|
|
84
|
+
// Context-scoped service instance (contains service, listener, header)
|
|
85
|
+
const [serviceInstance, setServiceInstance] =
|
|
86
|
+
useState<IAutoBeServiceData | null>(null);
|
|
87
|
+
|
|
88
|
+
const { refreshSessionList } = useAutoBeAgentSessionList();
|
|
89
|
+
// Context-scoped service getter
|
|
90
|
+
const getAutoBeService = useCallback(
|
|
91
|
+
async (
|
|
92
|
+
config: IAutoBeConfig = {} as IAutoBeConfig,
|
|
93
|
+
): Promise<IAutoBeServiceData> => {
|
|
94
|
+
// Return existing instance if available
|
|
95
|
+
if (serviceInstance && connectionStatus === "connected") {
|
|
96
|
+
return serviceInstance;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Prevent multiple concurrent creations
|
|
100
|
+
if (connectionStatus === "connecting") {
|
|
101
|
+
throw new Error("Service is already connecting. Please wait.");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!serviceFactory) {
|
|
105
|
+
throw new Error("No service factory provided. Cannot create service.");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
setConnectionStatus("connecting");
|
|
110
|
+
|
|
111
|
+
// Create new service instance
|
|
112
|
+
const newServiceData = await serviceFactory({
|
|
113
|
+
...config,
|
|
114
|
+
sessionId: activeConversationId,
|
|
115
|
+
});
|
|
116
|
+
newServiceData.connector.join().then(async () => {
|
|
117
|
+
const res = await serviceFactory({
|
|
118
|
+
...config,
|
|
119
|
+
sessionId: activeConversationId,
|
|
120
|
+
});
|
|
121
|
+
setServiceInstance(res);
|
|
122
|
+
});
|
|
123
|
+
setServiceInstance(newServiceData);
|
|
124
|
+
|
|
125
|
+
setSearchParams((sp) => {
|
|
126
|
+
const newSp = new URLSearchParams(sp);
|
|
127
|
+
newSp.set("session-id", newServiceData.sessionId);
|
|
128
|
+
return newSp;
|
|
129
|
+
});
|
|
130
|
+
setConnectionStatus("connected");
|
|
131
|
+
|
|
132
|
+
return newServiceData;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
setConnectionStatus("disconnected");
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
[
|
|
139
|
+
serviceFactory,
|
|
140
|
+
serviceInstance,
|
|
141
|
+
connectionStatus,
|
|
142
|
+
activeConversationId,
|
|
143
|
+
searchParams,
|
|
144
|
+
],
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Reset service (for reconnection, etc.)
|
|
148
|
+
const resetService = useCallback(() => {
|
|
149
|
+
setServiceInstance(null);
|
|
150
|
+
setConnectionStatus("disconnected");
|
|
151
|
+
setEventGroups([]);
|
|
152
|
+
setTokenUsage(null);
|
|
153
|
+
}, []);
|
|
154
|
+
|
|
43
155
|
useEffect(() => {
|
|
44
|
-
|
|
45
|
-
|
|
156
|
+
if (activeConversationId === null) {
|
|
157
|
+
setEventGroups([]);
|
|
158
|
+
setTokenUsage(null);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
storageStrategy
|
|
163
|
+
.getSession({
|
|
164
|
+
id: activeConversationId,
|
|
165
|
+
})
|
|
166
|
+
.then((v) => {
|
|
167
|
+
if (v === null) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
refreshSessionList();
|
|
171
|
+
setEventGroups(v.events);
|
|
172
|
+
setTokenUsage(v.tokenUsage);
|
|
173
|
+
})
|
|
174
|
+
.catch(console.error);
|
|
175
|
+
}, [activeConversationId]);
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (serviceInstance === null) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
serviceInstance.listener.on(async (e) => {
|
|
183
|
+
serviceInstance.service
|
|
46
184
|
.getTokenUsage()
|
|
47
185
|
.then(setTokenUsage)
|
|
48
186
|
.catch(() => {});
|
|
49
187
|
setEventGroups(e);
|
|
50
188
|
});
|
|
51
|
-
|
|
189
|
+
|
|
190
|
+
serviceInstance.service
|
|
52
191
|
.getTokenUsage()
|
|
53
192
|
.then(setTokenUsage)
|
|
54
193
|
.catch(() => {});
|
|
55
|
-
}, []);
|
|
194
|
+
}, [serviceInstance]);
|
|
195
|
+
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (activeConversationId === null || serviceInstance === null) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const originConversate = serviceInstance.service.conversate;
|
|
202
|
+
serviceInstance.service.conversate = async (content) => {
|
|
203
|
+
const result = await originConversate(content);
|
|
204
|
+
await storageStrategy.appendHistory({
|
|
205
|
+
id: activeConversationId,
|
|
206
|
+
history: result,
|
|
207
|
+
});
|
|
208
|
+
return result;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const registerEvent = async (e: IAutoBeEventGroup[]) => {
|
|
212
|
+
await storageStrategy.appendEvent({
|
|
213
|
+
id: activeConversationId,
|
|
214
|
+
events: e,
|
|
215
|
+
});
|
|
216
|
+
await storageStrategy.setTokenUsage({
|
|
217
|
+
id: activeConversationId,
|
|
218
|
+
tokenUsage: await serviceInstance.service.getTokenUsage(),
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
serviceInstance.listener.on(registerEvent);
|
|
223
|
+
return () => {
|
|
224
|
+
serviceInstance.service.conversate = originConversate;
|
|
225
|
+
serviceInstance.listener.off(registerEvent);
|
|
226
|
+
};
|
|
227
|
+
}, [activeConversationId, serviceInstance]);
|
|
56
228
|
|
|
57
229
|
return (
|
|
58
230
|
<AutoBeAgentContext.Provider
|
|
59
231
|
value={{
|
|
232
|
+
// Service state
|
|
233
|
+
connectionStatus,
|
|
234
|
+
|
|
235
|
+
// Service data
|
|
60
236
|
eventGroups,
|
|
61
237
|
tokenUsage,
|
|
62
|
-
state: listener
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
238
|
+
state: serviceInstance?.listener?.getState() ?? null,
|
|
239
|
+
service: serviceInstance?.service ?? null,
|
|
240
|
+
listener: serviceInstance?.listener ?? null,
|
|
241
|
+
|
|
242
|
+
// Service management
|
|
243
|
+
getAutoBeService,
|
|
244
|
+
resetService,
|
|
66
245
|
}}
|
|
67
246
|
>
|
|
68
247
|
{children}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReactNode,
|
|
3
|
+
createContext,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
IAutoBeAgentSession,
|
|
12
|
+
IAutoBeAgentSessionStorageStrategy,
|
|
13
|
+
} from "../structure";
|
|
14
|
+
|
|
15
|
+
interface AutoBeAgentSessionListContextType {
|
|
16
|
+
sessionList: IAutoBeAgentSession[];
|
|
17
|
+
refreshSessionList: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const AutoBeAgentSessionListContext =
|
|
21
|
+
createContext<AutoBeAgentSessionListContextType | null>(null);
|
|
22
|
+
|
|
23
|
+
export function AutoBeAgentSessionListProvider({
|
|
24
|
+
children,
|
|
25
|
+
storageStrategy,
|
|
26
|
+
}: {
|
|
27
|
+
storageStrategy: IAutoBeAgentSessionStorageStrategy;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}) {
|
|
30
|
+
const [sessionList, setSessionList] = useState<IAutoBeAgentSession[]>([]);
|
|
31
|
+
|
|
32
|
+
const refreshSessionList = useCallback(async () => {
|
|
33
|
+
await storageStrategy.getSessionList().then(setSessionList);
|
|
34
|
+
}, [storageStrategy]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
refreshSessionList();
|
|
38
|
+
}, [storageStrategy]);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<AutoBeAgentSessionListContext.Provider
|
|
42
|
+
value={{
|
|
43
|
+
sessionList,
|
|
44
|
+
refreshSessionList,
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</AutoBeAgentSessionListContext.Provider>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function useAutoBeAgentSessionList() {
|
|
53
|
+
const context = useContext(AutoBeAgentSessionListContext);
|
|
54
|
+
if (!context) {
|
|
55
|
+
throw new Error("useAutoBeAgent must be used within a AutoBeAgentProvider");
|
|
56
|
+
}
|
|
57
|
+
return context;
|
|
58
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReactNode,
|
|
3
|
+
createContext,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
interface SearchParamsContextType {
|
|
10
|
+
searchParams: URLSearchParams;
|
|
11
|
+
setSearchParams: React.Dispatch<React.SetStateAction<URLSearchParams>>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SearchParamsContext = createContext<SearchParamsContextType | null>(null);
|
|
15
|
+
|
|
16
|
+
export function SearchParamsProvider({ children }: { children: ReactNode }) {
|
|
17
|
+
const [searchParams, setSearchParams] = useState<URLSearchParams>(
|
|
18
|
+
new URLSearchParams(window.location.search),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const url = new URL(`${window.location.origin}${window.location.pathname}`);
|
|
23
|
+
searchParams.forEach((value, key) => {
|
|
24
|
+
url.searchParams.set(key, value);
|
|
25
|
+
});
|
|
26
|
+
window.history.pushState({}, "", url);
|
|
27
|
+
}, [searchParams]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<SearchParamsContext
|
|
31
|
+
value={{
|
|
32
|
+
searchParams,
|
|
33
|
+
setSearchParams,
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</SearchParamsContext>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useSearchParams() {
|
|
42
|
+
const context = useContext(SearchParamsContext);
|
|
43
|
+
if (!context) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"useSearchParams must be used within a SearchParamsProvider",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return context;
|
|
49
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from "./components";
|
|
2
|
+
export * from "./hooks";
|
|
2
3
|
export * from "./utils";
|
|
3
4
|
export * from "./structure";
|
|
5
|
+
export * from "./types";
|
|
4
6
|
export * from "./context/AutoBeAgentContext";
|
|
7
|
+
export * from "./context/AutoBeAgentSessionList";
|
|
8
|
+
export * from "./context/SearchParamsContext";
|