@chatbotkit/react 1.5.3 → 1.7.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/dist/cjs/actions/complete.cjs +112 -0
- package/dist/cjs/actions/complete.d.ts +24 -0
- package/dist/cjs/components/AutoTextarea.cjs +26 -22
- package/dist/cjs/components/AutoTextarea.d.ts +0 -7
- package/dist/cjs/components/ConversationManager.cjs +41 -0
- package/dist/cjs/components/ConversationManager.d.ts +5 -0
- package/dist/cjs/hooks/useConversationManager.cjs +90 -27
- package/dist/cjs/hooks/useConversationManager.d.ts +45 -37
- package/dist/cjs/index.cjs +4 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/utils/stream.cjs +47 -0
- package/dist/cjs/utils/stream.d.ts +6 -0
- package/dist/esm/actions/complete.d.ts +24 -0
- package/dist/esm/actions/complete.js +107 -0
- package/dist/esm/components/AutoScroller.js +1 -1
- package/dist/esm/components/AutoTextarea.d.ts +0 -7
- package/dist/esm/components/AutoTextarea.js +26 -22
- package/dist/esm/components/ConversationManager.d.ts +5 -0
- package/dist/esm/components/ConversationManager.js +36 -0
- package/dist/esm/hooks/useConversationManager.d.ts +45 -37
- package/dist/esm/hooks/useConversationManager.js +90 -27
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/utils/stream.d.ts +6 -0
- package/dist/esm/utils/stream.js +42 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +62 -2
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { isValidElement } from 'react';
|
|
3
|
+
import { stream } from '../utils/stream.js';
|
|
4
|
+
async function* complete({ client, messages, functions, maxRecusion = 3, ...options }) {
|
|
5
|
+
if (maxRecusion <= 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
messages = messages.slice(0);
|
|
9
|
+
const it = client
|
|
10
|
+
.complete(null, {
|
|
11
|
+
...options,
|
|
12
|
+
messages: messages.map(({ type, text, meta }) => {
|
|
13
|
+
return {
|
|
14
|
+
type,
|
|
15
|
+
text,
|
|
16
|
+
meta,
|
|
17
|
+
};
|
|
18
|
+
}),
|
|
19
|
+
functions: functions?.map(({ name, description, parameters }) => {
|
|
20
|
+
return {
|
|
21
|
+
name,
|
|
22
|
+
description,
|
|
23
|
+
parameters,
|
|
24
|
+
};
|
|
25
|
+
}),
|
|
26
|
+
})
|
|
27
|
+
.stream();
|
|
28
|
+
for await (const item of it) {
|
|
29
|
+
yield item;
|
|
30
|
+
const { type, data } = item;
|
|
31
|
+
if (type === 'message') {
|
|
32
|
+
const message = data;
|
|
33
|
+
messages.push(message);
|
|
34
|
+
if (message.meta?.activity?.type === 'request') {
|
|
35
|
+
const name = message.meta.activity.function?.name;
|
|
36
|
+
const args = message.meta.activity.function?.arguments;
|
|
37
|
+
const fn = functions?.find((fn) => fn.name === name);
|
|
38
|
+
if (fn && typeof fn.handler === 'function') {
|
|
39
|
+
const output = await fn.handler(args);
|
|
40
|
+
let text;
|
|
41
|
+
let children;
|
|
42
|
+
let result;
|
|
43
|
+
if (typeof output === 'string') {
|
|
44
|
+
text = undefined;
|
|
45
|
+
children = undefined;
|
|
46
|
+
result = output;
|
|
47
|
+
}
|
|
48
|
+
else if (isValidElement(output)) {
|
|
49
|
+
text = '';
|
|
50
|
+
children = output;
|
|
51
|
+
result = undefined;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (typeof output?.text === 'string') {
|
|
55
|
+
text = output.text;
|
|
56
|
+
}
|
|
57
|
+
if (isValidElement(output?.children)) {
|
|
58
|
+
children = output.children;
|
|
59
|
+
}
|
|
60
|
+
if (output?.result) {
|
|
61
|
+
result = output.result;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (text || children) {
|
|
65
|
+
yield {
|
|
66
|
+
type: 'message',
|
|
67
|
+
data: {
|
|
68
|
+
type: 'bot',
|
|
69
|
+
text: text ? text : '',
|
|
70
|
+
children: children ? _jsx(_Fragment, { children: children }) : undefined,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (result) {
|
|
75
|
+
const activityMessage = {
|
|
76
|
+
type: 'activity',
|
|
77
|
+
text: '',
|
|
78
|
+
meta: {
|
|
79
|
+
activity: {
|
|
80
|
+
type: 'response',
|
|
81
|
+
function: {
|
|
82
|
+
name,
|
|
83
|
+
arguments: args,
|
|
84
|
+
result: JSON.stringify(result),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
yield { type: 'message', data: activityMessage };
|
|
90
|
+
messages.push(activityMessage);
|
|
91
|
+
yield* complete({
|
|
92
|
+
...options,
|
|
93
|
+
client,
|
|
94
|
+
messages,
|
|
95
|
+
functions,
|
|
96
|
+
maxRecusion: maxRecusion - 1,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function streamComplete(options) {
|
|
105
|
+
return stream(complete(options));
|
|
106
|
+
}
|
|
107
|
+
export default complete;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useEffect, useRef } from 'react';
|
|
3
3
|
function AutoScrollAnchor() {
|
|
4
4
|
return _jsx("div", { className: "auto-scroll-anchor", style: { height: '1px' } });
|
|
5
5
|
}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
export function AutoTextarea(props?: {
|
|
2
2
|
[name: string]: any;
|
|
3
|
-
onInput?: onInputFn | null | undefined;
|
|
4
|
-
onFocus?: onFocusFn | null | undefined;
|
|
5
|
-
adjustOnInput?: boolean | null | undefined;
|
|
6
|
-
adjustOnFocus?: boolean | null | undefined;
|
|
7
3
|
} | undefined): import("react/jsx-runtime").JSX.Element;
|
|
8
4
|
export default AutoTextarea;
|
|
9
|
-
export type onInputFn = (event: React.ChangeEvent<HTMLTextAreaElement>) => any;
|
|
10
|
-
export type onFocusFn = (event: React.ChangeEvent<HTMLTextAreaElement>) => any;
|
|
11
|
-
import React from 'react';
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { useEffect, useRef } from 'react';
|
|
3
3
|
export function AutoTextarea(props) {
|
|
4
|
-
const
|
|
5
|
-
function
|
|
6
|
-
const adjustment = `calc(${[
|
|
4
|
+
const ref = useRef(null);
|
|
5
|
+
function recalibrate(textarea) {
|
|
6
|
+
const adjustment = `calc(${[textarea.style.paddingTop, textarea.style.paddingBottom]
|
|
7
7
|
.filter((f) => f)
|
|
8
8
|
.join(' + ') || '0px'})`;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
textarea.style.height = 'auto';
|
|
10
|
+
textarea.style.height = `calc(${textarea.scrollHeight}px - ${adjustment})`;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const textarea = ref.current;
|
|
14
|
+
if (textarea) {
|
|
15
|
+
recalibrate(textarea);
|
|
16
|
+
const observer = new MutationObserver((mutationsList) => {
|
|
17
|
+
for (const mutation of mutationsList) {
|
|
18
|
+
if (mutation.type === 'childList' ||
|
|
19
|
+
mutation.type === 'characterData') {
|
|
20
|
+
recalibrate(textarea);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
observer.observe(textarea, {
|
|
25
|
+
childList: true,
|
|
26
|
+
subtree: true,
|
|
27
|
+
characterData: true,
|
|
28
|
+
});
|
|
29
|
+
return () => observer.disconnect();
|
|
15
30
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function handleOnFocus(event) {
|
|
21
|
-
if (adjustOnFocus) {
|
|
22
|
-
handleEvent(event);
|
|
23
|
-
}
|
|
24
|
-
if (onFocus) {
|
|
25
|
-
onFocus(event);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return (_jsx("textarea", { ...rest, rows: 1, onInput: handleOnInput, onFocus: handleOnFocus }));
|
|
31
|
+
}, []);
|
|
32
|
+
return _jsx("textarea", { ref: ref, rows: 1, ...props });
|
|
29
33
|
}
|
|
30
34
|
export default AutoTextarea;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function ConversationManager({ children, ...options }: import('../hooks/useConversationManager.js').UseConversationManagerOptions & {
|
|
2
|
+
children: import('react').ReactNode;
|
|
3
|
+
}): import('react').ReactElement;
|
|
4
|
+
export const ConversationContext: import("react").Context<import("../hooks/useConversationManager.js").UseConversationManagerResult>;
|
|
5
|
+
export default ConversationManager;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext } from 'react';
|
|
3
|
+
import useConversationManager from '../hooks/useConversationManager.js';
|
|
4
|
+
export const ConversationContext = createContext(({
|
|
5
|
+
token: undefined,
|
|
6
|
+
setToken: () => { },
|
|
7
|
+
conversationId: undefined,
|
|
8
|
+
setConversationId: () => { },
|
|
9
|
+
botId: undefined,
|
|
10
|
+
setBotId: () => { },
|
|
11
|
+
backstory: undefined,
|
|
12
|
+
setBackstory: () => { },
|
|
13
|
+
model: undefined,
|
|
14
|
+
setModel: () => { },
|
|
15
|
+
datasetId: undefined,
|
|
16
|
+
setDatasetId: () => { },
|
|
17
|
+
skillsetId: undefined,
|
|
18
|
+
setSkillsetId: () => { },
|
|
19
|
+
text: '',
|
|
20
|
+
setText: () => { },
|
|
21
|
+
messages: [],
|
|
22
|
+
setMessages: () => { },
|
|
23
|
+
thinking: false,
|
|
24
|
+
setThinking: () => { },
|
|
25
|
+
typing: false,
|
|
26
|
+
setTyping: () => { },
|
|
27
|
+
error: null,
|
|
28
|
+
setError: () => { },
|
|
29
|
+
submit: () => { },
|
|
30
|
+
trigger: () => { },
|
|
31
|
+
}));
|
|
32
|
+
export function ConversationManager({ children, ...options }) {
|
|
33
|
+
const manager = useConversationManager(options);
|
|
34
|
+
return (_jsx(ConversationContext.Provider, { value: manager, children: children }));
|
|
35
|
+
}
|
|
36
|
+
export default ConversationManager;
|
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
export function useConversationManager(options:
|
|
2
|
-
[key: string]: any;
|
|
3
|
-
client?: ConversationClient | undefined;
|
|
4
|
-
endpoint?: string | undefined;
|
|
5
|
-
token?: string | undefined;
|
|
6
|
-
conversationId?: string | undefined;
|
|
7
|
-
backstory?: string | undefined;
|
|
8
|
-
Model?: string | undefined;
|
|
9
|
-
datasetId?: string | undefined;
|
|
10
|
-
skillsetId?: string | undefined;
|
|
11
|
-
}): {
|
|
12
|
-
token: string | undefined;
|
|
13
|
-
setToken: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
|
|
14
|
-
conversationId: string | undefined;
|
|
15
|
-
setConversationId: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
|
|
16
|
-
backstory: string | undefined;
|
|
17
|
-
setBackstory: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
|
|
18
|
-
model: any;
|
|
19
|
-
setModel: import("react").Dispatch<any>;
|
|
20
|
-
datasetId: string | undefined;
|
|
21
|
-
setDatasetId: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
|
|
22
|
-
skillsetId: string | undefined;
|
|
23
|
-
setSkillsetId: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
|
|
24
|
-
text: string;
|
|
25
|
-
setText: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
26
|
-
messages: Message[];
|
|
27
|
-
setMessages: import("react").Dispatch<import("react").SetStateAction<Message[]>>;
|
|
28
|
-
thinking: boolean;
|
|
29
|
-
setThinking: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
30
|
-
typing: boolean;
|
|
31
|
-
setTyping: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
32
|
-
error: any;
|
|
33
|
-
setError: import("react").Dispatch<any>;
|
|
34
|
-
submit: () => Promise<void>;
|
|
35
|
-
};
|
|
1
|
+
export function useConversationManager(options: UseConversationManagerOptions): UseConversationManagerResult;
|
|
36
2
|
export default useConversationManager;
|
|
37
3
|
export type ModelConfig = {
|
|
38
4
|
maxTokens?: number;
|
|
@@ -48,8 +14,50 @@ export type Model = string | {
|
|
|
48
14
|
config?: ModelConfig;
|
|
49
15
|
};
|
|
50
16
|
export type Message = {
|
|
51
|
-
id
|
|
52
|
-
type: 'bot' | 'user';
|
|
17
|
+
id?: string;
|
|
18
|
+
type: 'bot' | 'user' | 'context' | 'instruction' | 'backstory' | 'activity';
|
|
19
|
+
text: string;
|
|
20
|
+
meta?: Record<string, any>;
|
|
21
|
+
};
|
|
22
|
+
export type EndpointURL = string;
|
|
23
|
+
export type EndpointFunction = (conversationId: any, request: any) => AsyncGenerator<any>;
|
|
24
|
+
export type UseConversationManagerOptions = {
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
client?: ConversationClient | undefined;
|
|
27
|
+
endpoint?: string | EndpointFunction | undefined;
|
|
28
|
+
token?: string | undefined;
|
|
29
|
+
conversationId?: string | undefined;
|
|
30
|
+
backstory?: string | undefined;
|
|
31
|
+
Model?: string | undefined;
|
|
32
|
+
datasetId?: string | undefined;
|
|
33
|
+
skillsetId?: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
export type UseConversationManagerResult = {
|
|
36
|
+
token?: string | undefined;
|
|
37
|
+
setToken: (token: string) => void;
|
|
38
|
+
conversationId?: string | undefined;
|
|
39
|
+
setConversationId: (conversationId: string) => void;
|
|
40
|
+
botId?: string | undefined;
|
|
41
|
+
setBotId: (botId: string) => void;
|
|
42
|
+
backstory?: string | undefined;
|
|
43
|
+
setBackstory: (backstory: string) => void;
|
|
44
|
+
model?: Model | undefined;
|
|
45
|
+
setModel: (model: Model) => void;
|
|
46
|
+
datasetId?: string | undefined;
|
|
47
|
+
setDatasetId: (datasetId: string) => void;
|
|
48
|
+
skillsetId?: string | undefined;
|
|
49
|
+
setSkillsetId: (skillsetId: string) => void;
|
|
53
50
|
text: string;
|
|
51
|
+
setText: (text: string) => void;
|
|
52
|
+
messages: Message[];
|
|
53
|
+
setMessages: (messages: Message[]) => void;
|
|
54
|
+
thinking: boolean;
|
|
55
|
+
setThinking: (thinking: boolean) => void;
|
|
56
|
+
typing: boolean;
|
|
57
|
+
setTyping: (typing: boolean) => void;
|
|
58
|
+
error: any;
|
|
59
|
+
setError: (error: any) => void;
|
|
60
|
+
submit: () => void;
|
|
61
|
+
trigger: (name: string, ...args: any) => void;
|
|
54
62
|
};
|
|
55
63
|
import { ConversationClient } from '@chatbotkit/sdk';
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
|
-
import { ConversationClient } from '@chatbotkit/sdk';
|
|
3
|
-
import { getRandomId } from '../utils/string.js';
|
|
4
2
|
import { cloneAndExtend } from '../utils/object.js';
|
|
3
|
+
import { consume } from '../utils/stream.js';
|
|
4
|
+
import { getRandomId } from '../utils/string.js';
|
|
5
|
+
import { ConversationClient } from '@chatbotkit/sdk';
|
|
5
6
|
export function useConversationManager(options) {
|
|
6
|
-
const { client: _client, endpoint, token: _token, conversationId: _conversationId, backstory: _backstory, model: _model, datasetId: _datasetId, skillsetId: _skillsetId, ...rest } = options;
|
|
7
|
+
const { client: _client, endpoint, token: _token, conversationId: _conversationId, botId: _botId, backstory: _backstory, model: _model, datasetId: _datasetId, skillsetId: _skillsetId, ...rest } = options;
|
|
7
8
|
const [token, setToken] = useState(_token);
|
|
8
9
|
const [conversationId, setConversationId] = useState(_conversationId);
|
|
10
|
+
const [botId, setBotId] = useState(_botId);
|
|
9
11
|
const [backstory, setBackstory] = useState(_backstory);
|
|
10
12
|
const [model, setModel] = useState(_model);
|
|
11
13
|
const [datasetId, setDatasetId] = useState(_datasetId);
|
|
12
14
|
const [skillsetId, setSkillsetId] = useState(_skillsetId);
|
|
13
15
|
const client = useMemo(() => {
|
|
16
|
+
if (typeof endpoint === 'function') {
|
|
17
|
+
return {
|
|
18
|
+
complete(conversationId, options) {
|
|
19
|
+
return {
|
|
20
|
+
async *stream() {
|
|
21
|
+
yield* consume(endpoint(conversationId, options));
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
14
27
|
const options = { ...rest, secret: token || '' };
|
|
15
28
|
let thisClient = _client || new ConversationClient(options);
|
|
16
29
|
const extension = {};
|
|
17
|
-
if (endpoint) {
|
|
30
|
+
if (typeof endpoint === 'string') {
|
|
18
31
|
extension.url = new URL(globalThis.window?.location?.origin || 'about:blank');
|
|
19
32
|
extension.endpoints = {
|
|
20
33
|
'/api/v1/conversation/complete': endpoint,
|
|
@@ -26,26 +39,16 @@ export function useConversationManager(options) {
|
|
|
26
39
|
if (Object.keys(extension).length === 0) {
|
|
27
40
|
return thisClient;
|
|
28
41
|
}
|
|
29
|
-
|
|
42
|
+
else {
|
|
43
|
+
return cloneAndExtend(thisClient, extension);
|
|
44
|
+
}
|
|
30
45
|
}, [_client, endpoint, token]);
|
|
31
46
|
const [text, setText] = useState((''));
|
|
32
47
|
const [messages, setMessages] = useState(([]));
|
|
33
48
|
const [thinking, setThinking] = useState(false);
|
|
34
49
|
const [typing, setTyping] = useState(false);
|
|
35
50
|
const [error, setError] = useState((null));
|
|
36
|
-
async function
|
|
37
|
-
if (!text) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
setText('');
|
|
41
|
-
const userMessage = {
|
|
42
|
-
id: getRandomId('message-'),
|
|
43
|
-
type: 'user',
|
|
44
|
-
text: text,
|
|
45
|
-
};
|
|
46
|
-
let newMessages = messages.slice(0);
|
|
47
|
-
newMessages = [...newMessages, userMessage];
|
|
48
|
-
setMessages([...newMessages]);
|
|
51
|
+
async function stream(newMessages) {
|
|
49
52
|
setThinking(true);
|
|
50
53
|
let it;
|
|
51
54
|
try {
|
|
@@ -58,7 +61,9 @@ export function useConversationManager(options) {
|
|
|
58
61
|
model: model,
|
|
59
62
|
datasetId: datasetId,
|
|
60
63
|
skillsetId: skillsetId,
|
|
61
|
-
messages: newMessages
|
|
64
|
+
messages: newMessages
|
|
65
|
+
.slice(-100)
|
|
66
|
+
.map(({ type, text, meta }) => ({ type, text, meta })),
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
}
|
|
@@ -75,16 +80,37 @@ export function useConversationManager(options) {
|
|
|
75
80
|
let alreadyStreaming = false;
|
|
76
81
|
try {
|
|
77
82
|
for await (const event of it.stream()) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
alreadyStreaming
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
switch (event.type) {
|
|
84
|
+
case 'token': {
|
|
85
|
+
if (!alreadyStreaming) {
|
|
86
|
+
alreadyStreaming = true;
|
|
87
|
+
newMessages = [...newMessages, botMessage];
|
|
88
|
+
setMessages(newMessages);
|
|
89
|
+
setThinking(false);
|
|
90
|
+
setTyping(true);
|
|
91
|
+
}
|
|
92
|
+
botMessage.text += event.data.token;
|
|
93
|
+
setMessages([...newMessages]);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'message': {
|
|
97
|
+
const message = event.data;
|
|
98
|
+
if (botMessage.text !== message.text ||
|
|
99
|
+
message.type === 'activity' ||
|
|
100
|
+
typeof message.children !== 'undefined') {
|
|
101
|
+
const newMessage = {
|
|
102
|
+
id: getRandomId('message-'),
|
|
103
|
+
...event.data,
|
|
104
|
+
};
|
|
105
|
+
newMessages = [...newMessages, newMessage];
|
|
106
|
+
setMessages([...newMessages]);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'result': {
|
|
83
111
|
setThinking(false);
|
|
84
|
-
setTyping(
|
|
112
|
+
setTyping(false);
|
|
85
113
|
}
|
|
86
|
-
botMessage.text += event.data.token;
|
|
87
|
-
setMessages([...newMessages]);
|
|
88
114
|
}
|
|
89
115
|
}
|
|
90
116
|
}
|
|
@@ -95,11 +121,47 @@ export function useConversationManager(options) {
|
|
|
95
121
|
setTyping(false);
|
|
96
122
|
}
|
|
97
123
|
}
|
|
124
|
+
async function submit() {
|
|
125
|
+
if (!text) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
setText('');
|
|
129
|
+
const userMessage = {
|
|
130
|
+
id: getRandomId('message-'),
|
|
131
|
+
type: 'user',
|
|
132
|
+
text: text,
|
|
133
|
+
};
|
|
134
|
+
let newMessages = messages.slice(0);
|
|
135
|
+
newMessages = [...newMessages, userMessage];
|
|
136
|
+
setMessages([...newMessages]);
|
|
137
|
+
await stream(newMessages);
|
|
138
|
+
}
|
|
139
|
+
async function trigger(name, ...args) {
|
|
140
|
+
const newMessages = [
|
|
141
|
+
...messages,
|
|
142
|
+
({
|
|
143
|
+
type: 'activity',
|
|
144
|
+
text: '',
|
|
145
|
+
meta: {
|
|
146
|
+
activity: {
|
|
147
|
+
type: 'trigger',
|
|
148
|
+
function: {
|
|
149
|
+
name: name,
|
|
150
|
+
arguments: args,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
];
|
|
156
|
+
await stream(newMessages);
|
|
157
|
+
}
|
|
98
158
|
return {
|
|
99
159
|
token,
|
|
100
160
|
setToken,
|
|
101
161
|
conversationId,
|
|
102
162
|
setConversationId,
|
|
163
|
+
botId,
|
|
164
|
+
setBotId,
|
|
103
165
|
backstory,
|
|
104
166
|
setBackstory,
|
|
105
167
|
model,
|
|
@@ -119,6 +181,7 @@ export function useConversationManager(options) {
|
|
|
119
181
|
error,
|
|
120
182
|
setError,
|
|
121
183
|
submit,
|
|
184
|
+
trigger,
|
|
122
185
|
};
|
|
123
186
|
}
|
|
124
187
|
export default useConversationManager;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { ChatMessage } from "./components/ChatMessage.js";
|
|
|
3
3
|
export { ChatMessages } from "./components/ChatMessages.js";
|
|
4
4
|
export { AutoTextarea } from "./components/AutoTextarea.js";
|
|
5
5
|
export { useConversationManager } from "./hooks/useConversationManager.js";
|
|
6
|
+
export { ConversationManager, ConversationContext } from "./components/ConversationManager.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { ChatInput } from './components/ChatInput.js';
|
|
|
2
2
|
export { ChatMessage } from './components/ChatMessage.js';
|
|
3
3
|
export { ChatMessages } from './components/ChatMessages.js';
|
|
4
4
|
export { AutoTextarea } from './components/AutoTextarea.js';
|
|
5
|
+
export { ConversationManager, ConversationContext, } from './components/ConversationManager.js';
|
|
5
6
|
export { useConversationManager } from './hooks/useConversationManager.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function stream(source) {
|
|
2
|
+
let it;
|
|
3
|
+
if ('next' in source && typeof source.next === 'function') {
|
|
4
|
+
it = source.next();
|
|
5
|
+
}
|
|
6
|
+
else if ('stream' in source && typeof source.stream === 'function') {
|
|
7
|
+
return stream(source.stream());
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
throw new Error('Invalid source');
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
it.then((res) => {
|
|
14
|
+
if (res.done) {
|
|
15
|
+
resolve({ iteratorResult: res });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
resolve({ iteratorResult: res, next: stream(source) });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
it.catch((error) => reject(error));
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function consume(source) {
|
|
25
|
+
return {
|
|
26
|
+
[Symbol.asyncIterator]: function () {
|
|
27
|
+
return {
|
|
28
|
+
current: source,
|
|
29
|
+
async next() {
|
|
30
|
+
const { iteratorResult, next } = await this.current;
|
|
31
|
+
if (next) {
|
|
32
|
+
this.current = next;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
iteratorResult.done = true;
|
|
36
|
+
}
|
|
37
|
+
return iteratorResult;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|