@stack-spot/ai-chat-widget 0.10.0 → 0.11.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/AbortedError.d.ts +5 -0
- package/dist/AbortedError.d.ts.map +1 -0
- package/dist/AbortedError.js +7 -0
- package/dist/AbortedError.js.map +1 -0
- package/dist/StackspotAIWidget.d.ts.map +1 -1
- package/dist/StackspotAIWidget.js +11 -3
- package/dist/StackspotAIWidget.js.map +1 -1
- package/dist/chat-interceptors/CustomInputs.d.ts +19 -0
- package/dist/chat-interceptors/CustomInputs.d.ts.map +1 -0
- package/dist/chat-interceptors/CustomInputs.js +62 -0
- package/dist/chat-interceptors/CustomInputs.js.map +1 -0
- package/dist/chat-interceptors/quick-command-questions.d.ts +4 -0
- package/dist/chat-interceptors/quick-command-questions.d.ts.map +1 -0
- package/dist/chat-interceptors/quick-command-questions.js +18 -0
- package/dist/chat-interceptors/quick-command-questions.js.map +1 -0
- package/dist/chat-interceptors/quick-commands.d.ts +3 -1
- package/dist/chat-interceptors/quick-commands.d.ts.map +1 -1
- package/dist/chat-interceptors/quick-commands.js +249 -8
- package/dist/chat-interceptors/quick-commands.js.map +1 -1
- package/dist/chat-interceptors/send-message.d.ts +1 -1
- package/dist/chat-interceptors/send-message.d.ts.map +1 -1
- package/dist/chat-interceptors/send-message.js +4 -4
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/state/ChatEntry.d.ts +8 -8
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js +4 -16
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/state/ChatState.d.ts +13 -1
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js +38 -3
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/ObservableState.d.ts +1 -1
- package/dist/state/ObservableState.d.ts.map +1 -1
- package/dist/state/ObservableState.js.map +1 -1
- package/dist/utils/chat.d.ts.map +1 -1
- package/dist/utils/chat.js +2 -1
- package/dist/utils/chat.js.map +1 -1
- package/dist/utils/knowledge-source.d.ts +2 -2
- package/dist/utils/knowledge-source.d.ts.map +1 -1
- package/dist/utils/knowledge-source.js +4 -6
- package/dist/utils/knowledge-source.js.map +1 -1
- package/dist/utils/programming-languages.d.ts +1 -0
- package/dist/utils/programming-languages.d.ts.map +1 -1
- package/dist/utils/programming-languages.js +1 -0
- package/dist/utils/programming-languages.js.map +1 -1
- package/dist/utils/string.d.ts +2 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +7 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/views/Chat/ChatMessage.d.ts +2 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +15 -4
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/ChatMessages.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessages.js +1 -1
- package/dist/views/Chat/ChatMessages.js.map +1 -1
- package/dist/views/Chat/styled.d.ts.map +1 -1
- package/dist/views/Chat/styled.js +31 -0
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/Editor.d.ts.map +1 -1
- package/dist/views/Editor.js +3 -4
- package/dist/views/Editor.js.map +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts +1 -1
- package/dist/views/MessageInput/ButtonGroup.d.ts.map +1 -1
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +8 -4
- package/dist/views/MessageInput/index.js.map +1 -1
- package/package.json +2 -2
- package/src/AbortedError.ts +7 -0
- package/src/StackspotAIWidget.tsx +13 -3
- package/src/chat-interceptors/CustomInputs.ts +70 -0
- package/src/chat-interceptors/quick-command-questions.ts +15 -0
- package/src/chat-interceptors/quick-commands.ts +269 -7
- package/src/chat-interceptors/send-message.ts +4 -4
- package/src/state/ChatEntry.ts +7 -20
- package/src/state/ChatState.ts +41 -3
- package/src/state/ObservableState.ts +1 -1
- package/src/utils/chat.ts +2 -1
- package/src/utils/knowledge-source.ts +6 -8
- package/src/utils/programming-languages.ts +2 -0
- package/src/utils/string.ts +6 -0
- package/src/views/Chat/ChatMessage.tsx +38 -8
- package/src/views/Chat/ChatMessages.tsx +4 -1
- package/src/views/Chat/styled.ts +31 -0
- package/src/views/Editor.tsx +3 -4
- package/src/views/MessageInput/ButtonGroup.tsx +1 -1
- package/src/views/MessageInput/index.tsx +9 -5
- package/dist/components/Editor.d.ts +0 -9
- package/dist/components/Editor.d.ts.map +0 -1
- package/dist/components/Editor.js +0 -2
- package/dist/components/Editor.js.map +0 -1
- package/src/components/Editor.tsx +0 -12
|
@@ -5,7 +5,7 @@ interface ButtonGroupProps {
|
|
|
5
5
|
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
|
|
6
6
|
isLoading: boolean;
|
|
7
7
|
onSend: () => void;
|
|
8
|
-
onCancel
|
|
8
|
+
onCancel: () => void;
|
|
9
9
|
}
|
|
10
10
|
export declare const ButtonGroup: ({ features, onSend, onCancel, expanded, setExpanded, isLoading }: ButtonGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonGroup.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/ButtonGroup.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAGrD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,
|
|
1
|
+
{"version":3,"file":"ButtonGroup.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/ButtonGroup.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAGrD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,qEAAsE,gBAAgB,4CAwE7G,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAOrD,UAAU,KAAK;IACb,QAAQ,EAAE,oBAAoB,CAAC;CAChC;AAED,eAAO,MAAM,YAAY,iBAAkB,KAAK,4CAgE/C,CAAA"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { listToClass } from '@stack-spot/portal-theme';
|
|
3
|
-
import {
|
|
4
|
-
import { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
4
|
import { AdaptiveTextArea } from '../../components/AdaptiveTextArea.js';
|
|
6
5
|
import { ProgressBar } from '../../components/ProgressBar.js';
|
|
7
6
|
import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../context/hooks.js';
|
|
@@ -19,13 +18,14 @@ export const MessageInput = ({ features }) => {
|
|
|
19
18
|
const isLoading = useCurrentChatState('isLoading') ?? false;
|
|
20
19
|
const value = useCurrentChatState('nextMessage') ?? '';
|
|
21
20
|
const isMinimized = useWidgetState('isMinimized');
|
|
21
|
+
const elementRef = useRef(null);
|
|
22
22
|
const onSend = useCallback(async () => {
|
|
23
23
|
const message = chat.get('nextMessage');
|
|
24
24
|
if (!message)
|
|
25
25
|
return;
|
|
26
26
|
const code = chat.get('codeSelection');
|
|
27
27
|
const language = chat.get('codeLanguage');
|
|
28
|
-
const prompt = code ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message;
|
|
28
|
+
const prompt = code && !message.startsWith('/') ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message;
|
|
29
29
|
chat.pushMessage(ChatEntry.createUserEntry(prompt, true));
|
|
30
30
|
chat.set('nextMessage', '');
|
|
31
31
|
setFocused(false);
|
|
@@ -36,7 +36,11 @@ export const MessageInput = ({ features }) => {
|
|
|
36
36
|
onSend();
|
|
37
37
|
}
|
|
38
38
|
}, [onSend]);
|
|
39
|
-
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!isLoading)
|
|
41
|
+
elementRef.current?.querySelector('textarea')?.focus();
|
|
42
|
+
}, [isLoading]);
|
|
43
|
+
return (_jsxs(MessageInputBox, { ref: elementRef, "aria-busy": isLoading, className: "message-input", children: [_jsx(ProgressBar, { visible: isLoading, shimmer: true }), _jsx(InfoBar, {}), _jsxs("div", { className: listToClass(['action-box', focused && 'focused', isLoading && 'disabled']), children: [_jsx(AdaptiveTextArea, { disabled: isLoading, placeholder: t.placeholder, onChange: e => chat.set('nextMessage', e.target.value), value: value, onFocus: () => setFocused(true), onBlur: () => setFocused(false), onKeyDown: onKeyDown, onIncreaseSize: () => setExpanded(false), onResetSize: () => !expansionLocked.current && setExpanded(true), maxHeight: isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT }), _jsx(ButtonGroup, { features: features, onSend: onSend, onCancel: () => chat.abort(), expanded: expanded, isLoading: isLoading, setExpanded: (value) => {
|
|
40
44
|
setExpanded(value);
|
|
41
45
|
expansionLocked.current = expanded;
|
|
42
46
|
} })] })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/views/MessageInput/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAM9E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAE,QAAQ,EAAS,EAAE,EAAE;IAClD,MAAM,CAAC,GAAG,yBAAyB,EAAE,CAAA;IACrC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,cAAc,EAAE,CAAA;IAC7B,MAAM,SAAS,GAAG,mBAAmB,CAAC,WAAW,CAAC,IAAI,KAAK,CAAA;IAC3D,MAAM,KAAK,GAAG,mBAAmB,CAAC,aAAa,CAAC,IAAI,EAAE,CAAA;IACtD,MAAM,WAAW,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IAE/C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACzC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,WAAW,QAAQ,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,OAAO,CAAA;QAC5G,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;QACzD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;QAC3B,UAAU,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IAEV,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,KAA+C,EAAE,EAAE;QAChF,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YAC7C,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,MAAM,EAAE,CAAA;QACV,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS;YAAE,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAA;IACxE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;IAEf,OAAO,CACL,MAAC,eAAe,IAAC,GAAG,EAAE,UAAU,eAAa,SAAS,EAAE,SAAS,EAAC,eAAe,aAC/E,KAAC,WAAW,IAAC,OAAO,EAAE,SAAS,EAAE,OAAO,SAAG,EAC3C,KAAC,OAAO,KAAG,EACX,eAAK,SAAS,EAAE,WAAW,CAAC,CAAC,YAAY,EAAE,OAAO,IAAI,SAAS,EAAE,SAAS,IAAI,UAAU,CAAC,CAAC,aACxF,KAAC,gBAAgB,IACf,QAAQ,EAAE,SAAS,EACnB,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACtD,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAC/B,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAC/B,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EACxC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,eAAe,CAAC,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,EAChE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,GAC5D,EACF,KAAC,WAAW,IACV,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAC5B,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;4BACrB,WAAW,CAAC,KAAK,CAAC,CAAA;4BAClB,eAAe,CAAC,OAAO,GAAG,QAAQ,CAAA;wBACpC,CAAC,GACD,IACE,IACU,CACnB,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/ai-chat-widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"@citric/icons": "^5.4.0 || ^6.0.0",
|
|
10
10
|
"@citric/ui": "^5.4.0 || ^6.0.0",
|
|
11
11
|
"@stack-spot/portal-components": "^2.6.1",
|
|
12
|
-
"@stack-spot/portal-network": "^0.
|
|
12
|
+
"@stack-spot/portal-network": "^0.46.0",
|
|
13
13
|
"@stack-spot/portal-theme": "^1.0.0",
|
|
14
14
|
"@stack-spot/portal-translate": "^1.1.0",
|
|
15
15
|
"lodash": "^4.17.0",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { loader } from '@monaco-editor/react'
|
|
1
2
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
3
|
import { useMemo, useRef } from 'react'
|
|
3
|
-
import {
|
|
4
|
+
import { quickCommandQuestionsInterceptor } from './chat-interceptors/quick-command-questions'
|
|
5
|
+
import { createQuickCommandInterceptor } from './chat-interceptors/quick-commands'
|
|
4
6
|
import { sendMessageInterceptor } from './chat-interceptors/send-message'
|
|
5
7
|
import { TooltipProvider } from './components/Tooltip'
|
|
6
|
-
import { useCurrentChatMessages, useFirstChat, useWidgetState } from './context/hooks'
|
|
8
|
+
import { useCurrentChatMessages, useFirstChat, useWidget, useWidgetState } from './context/hooks'
|
|
7
9
|
import { AIWidgetFeatures, defaultFeatures } from './features'
|
|
8
10
|
import './layout.css'
|
|
9
11
|
import { RightPanel } from './right-panel/RightPanel'
|
|
@@ -34,7 +36,15 @@ export interface AIWidgetProps {
|
|
|
34
36
|
export const StackspotAIWidget = (
|
|
35
37
|
{ username, features = defaultFeatures, interceptors: userInterceptors = [], minimizedActions = {}, children }: AIWidgetProps,
|
|
36
38
|
) => {
|
|
37
|
-
const
|
|
39
|
+
const widget = useWidget()
|
|
40
|
+
const interceptors: MessageInterceptor[] = useMemo(
|
|
41
|
+
() => [
|
|
42
|
+
...userInterceptors,
|
|
43
|
+
quickCommandQuestionsInterceptor,
|
|
44
|
+
createQuickCommandInterceptor(widget, () => loader.__getMonacoInstance()?.editor),
|
|
45
|
+
sendMessageInterceptor],
|
|
46
|
+
[],
|
|
47
|
+
)
|
|
38
48
|
useFirstChat(interceptors)
|
|
39
49
|
const rightPanelRef = useRef<HTMLDivElement>(null)
|
|
40
50
|
const chatWindowRef = useRef<HTMLDivElement>(null)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { CustomInputResponse } from '@stack-spot/portal-network/api/ai'
|
|
2
|
+
import { Dictionary, translate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { ChatEntry } from '../state/ChatEntry'
|
|
4
|
+
import { LabeledWithImage } from '../state/types'
|
|
5
|
+
|
|
6
|
+
export const CUSTOM_INPUTS_KEY = 'customInputs'
|
|
7
|
+
|
|
8
|
+
export class CustomInputs {
|
|
9
|
+
private value: Record<string, string> = {}
|
|
10
|
+
private inputs: CustomInputResponse[]
|
|
11
|
+
private index = 0
|
|
12
|
+
private resolve?: (value: Record<string, string>) => void
|
|
13
|
+
private promise: Promise<Record<string, string>>
|
|
14
|
+
private agent?: LabeledWithImage
|
|
15
|
+
|
|
16
|
+
constructor(inputs: CustomInputResponse[], agent?: LabeledWithImage) {
|
|
17
|
+
this.inputs = inputs
|
|
18
|
+
this.agent = agent
|
|
19
|
+
this.promise = new Promise(resolve => {
|
|
20
|
+
this.resolve = resolve
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ask(): ChatEntry {
|
|
25
|
+
if (this.index >= this.inputs.length) throw new Error('No more questions to ask.')
|
|
26
|
+
const t = translate(dictionary)
|
|
27
|
+
const input = this.inputs[this.index]
|
|
28
|
+
return new ChatEntry({
|
|
29
|
+
agentType: 'bot',
|
|
30
|
+
content: input.question,
|
|
31
|
+
type: 'text',
|
|
32
|
+
agent: this.agent,
|
|
33
|
+
badges: [{ label: input.mandatory ? t.required : t.optional }],
|
|
34
|
+
card: true,
|
|
35
|
+
actions: input.mandatory ? undefined : [{ type: 'command', appearance: 'primary', title: t.skip, exec: '/skip' }],
|
|
36
|
+
updated: new Date().toISOString(),
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
answer(value: string) {
|
|
41
|
+
this.value[this.inputs[this.index].slug] = value
|
|
42
|
+
this.skip()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
skip() {
|
|
46
|
+
this.index++
|
|
47
|
+
if (this.hasFinished()) this.resolve?.(this.value)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
hasFinished() {
|
|
51
|
+
return this.index === this.inputs.length
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getValue() {
|
|
55
|
+
return this.promise
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const dictionary = {
|
|
60
|
+
en: {
|
|
61
|
+
required: 'Required information',
|
|
62
|
+
optional: 'Optional information',
|
|
63
|
+
skip: 'Skip',
|
|
64
|
+
},
|
|
65
|
+
pt: {
|
|
66
|
+
required: 'Informação obrigatória',
|
|
67
|
+
optional: 'Informação opcional',
|
|
68
|
+
skip: 'Pular',
|
|
69
|
+
},
|
|
70
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ChatEntry } from '../state/ChatEntry'
|
|
2
|
+
import { ChatState } from '../state/ChatState'
|
|
3
|
+
import { CUSTOM_INPUTS_KEY } from './CustomInputs'
|
|
4
|
+
|
|
5
|
+
export function quickCommandQuestionsInterceptor(entry: ChatEntry, chat: ChatState) {
|
|
6
|
+
const { agentType, content } = entry.getValue()
|
|
7
|
+
if (agentType !== 'user') return
|
|
8
|
+
const customInputs = chat.interceptorMemory.get(CUSTOM_INPUTS_KEY)
|
|
9
|
+
if (!customInputs) return
|
|
10
|
+
if (content === '/skip') customInputs.skip()
|
|
11
|
+
else customInputs.answer(content)
|
|
12
|
+
if (!customInputs.hasFinished()) chat.pushMessage(customInputs.ask())
|
|
13
|
+
// prevents next interceptors from running
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
@@ -1,12 +1,274 @@
|
|
|
1
|
+
import { aiClient, CancelledError, FixedChatRequest, StackspotAPIError } from '@stack-spot/portal-network'
|
|
2
|
+
import { QuickCommandFetchResponseResult, QuickCommandPromptResponse2, QuickCommandResponse } from '@stack-spot/portal-network/api/ai'
|
|
3
|
+
import { Dictionary, interpolate, translate } from '@stack-spot/portal-translate'
|
|
4
|
+
import type { editor } from 'monaco-editor'
|
|
5
|
+
import { ulid } from 'ulid'
|
|
6
|
+
import { AbortedError } from '../AbortedError'
|
|
1
7
|
import { ChatEntry } from '../state/ChatEntry'
|
|
2
8
|
import { ChatState } from '../state/ChatState'
|
|
9
|
+
import { LabeledWithImage } from '../state/types'
|
|
10
|
+
import { WidgetState } from '../state/WidgetState'
|
|
11
|
+
import { buildConversationContext } from '../utils/chat'
|
|
12
|
+
import { getSizeOfString } from '../utils/string'
|
|
13
|
+
import { CUSTOM_INPUTS_KEY, CustomInputs } from './CustomInputs'
|
|
3
14
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
type SlugExecution = Record<string, string | QuickCommandFetchResponseResult | QuickCommandPromptResponse2>
|
|
16
|
+
|
|
17
|
+
interface QCContext {
|
|
18
|
+
qc: QuickCommandResponse,
|
|
19
|
+
context: Required<FixedChatRequest>['context'],
|
|
20
|
+
resultMap: SlugExecution,
|
|
21
|
+
chat: ChatState,
|
|
22
|
+
code?: string,
|
|
23
|
+
executionId: string,
|
|
24
|
+
signal: AbortSignal,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const commandRegex = /^\/[\w\d-_]+$/
|
|
28
|
+
const progressMessageDelayMS = 1000
|
|
29
|
+
|
|
30
|
+
export function createQuickCommandInterceptor(widget: WidgetState, getEditor: () => typeof editor | undefined) {
|
|
31
|
+
async function computeCustomInputs(ctx: QCContext) {
|
|
32
|
+
const { chat, qc: { custom_inputs: inputs, name, slug } } = ctx
|
|
33
|
+
if (!inputs?.length) return
|
|
34
|
+
chat.set('isLoading', false)
|
|
35
|
+
const t = translate(dictionary)
|
|
36
|
+
const customInputs = new CustomInputs(inputs, chat.get('agent'))
|
|
37
|
+
chat.interceptorMemory.set(CUSTOM_INPUTS_KEY, customInputs)
|
|
38
|
+
chat.pushMessage(new ChatEntry({
|
|
39
|
+
agentType: 'bot',
|
|
40
|
+
type: 'text',
|
|
41
|
+
content: interpolate(t.startQuestioning, name || slug),
|
|
42
|
+
}))
|
|
43
|
+
chat.pushMessage(customInputs.ask())
|
|
44
|
+
const inputsValues = await customInputs.getValue()
|
|
45
|
+
chat.set('isLoading', true)
|
|
46
|
+
chat.interceptorMemory.delete(CUSTOM_INPUTS_KEY)
|
|
47
|
+
ctx.resultMap = { ...ctx.resultMap, ...inputsValues }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runFetchStep(ctx: QCContext, stepIndex: number) {
|
|
51
|
+
const { qc: { slug, steps }, code, context, resultMap, executionId, signal } = ctx
|
|
52
|
+
const step = steps![stepIndex]
|
|
53
|
+
const { headers, data, method, url } = await aiClient.fetchStepOfQuickCommand.mutate({
|
|
54
|
+
slug,
|
|
55
|
+
stepSlug: step.slug,
|
|
56
|
+
quickCommandsExecutionRequest: { input_data: code, context, slugs_executions: resultMap, qc_execution_id: executionId },
|
|
57
|
+
}, signal)
|
|
58
|
+
const body = ['get', 'head'].includes(method.toLowerCase()) ? undefined : data
|
|
59
|
+
const response = await fetch(url, { headers: headers || undefined, body, method, signal })
|
|
60
|
+
if (!response.ok) throw new Error(`Failed to execute step "${step.slug}" of quick command "${slug}". Status ${response.status}.`)
|
|
61
|
+
resultMap[step.slug] = {
|
|
62
|
+
status: response.status,
|
|
63
|
+
data: await response.text(),
|
|
64
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function runLLMStep(
|
|
69
|
+
{ qc: { slug, steps }, code, context, executionId, resultMap, signal }: QCContext,
|
|
70
|
+
stepIndex: number,
|
|
71
|
+
) {
|
|
72
|
+
const step = steps![stepIndex]
|
|
73
|
+
resultMap[step.slug] = await aiClient.llmStepOfQuickCommand.mutate({
|
|
74
|
+
slug,
|
|
75
|
+
stepSlug: step.slug,
|
|
76
|
+
quickCommandsExecutionRequest: { input_data: code, context, qc_execution_id: executionId, slugs_executions: resultMap },
|
|
77
|
+
}, signal)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function updateProgressMessageForStep(message: ChatEntry, qc: QuickCommandResponse, stepIndex: number) {
|
|
81
|
+
const t = translate(dictionary)
|
|
82
|
+
message.setValue({
|
|
83
|
+
...message.getValue(),
|
|
84
|
+
content: interpolate(t.progress, qc.name || qc.slug, stepIndex + 1, qc.steps?.[stepIndex]?.slug, qc.steps?.length),
|
|
85
|
+
updated: new Date().toISOString(),
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function showProgressMessage(ctx: QCContext, message: ChatEntry) {
|
|
90
|
+
const result = {
|
|
91
|
+
removeProgressMessage: () => clearTimeout(timeout),
|
|
92
|
+
}
|
|
93
|
+
const timeout = window.setTimeout(() => {
|
|
94
|
+
const chat = ctx.chat
|
|
95
|
+
chat.pushMessage(message)
|
|
96
|
+
result.removeProgressMessage = () => chat.popMessage()
|
|
97
|
+
}, progressMessageDelayMS)
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function createProgressMessage(agent?: LabeledWithImage) {
|
|
102
|
+
return new ChatEntry({
|
|
103
|
+
agentType: 'bot',
|
|
104
|
+
content: '',
|
|
105
|
+
type: 'text',
|
|
106
|
+
agent,
|
|
107
|
+
updated: new Date().toISOString(),
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function runSteps(ctx: QCContext) {
|
|
112
|
+
const { qc, chat } = ctx
|
|
113
|
+
const progress = createProgressMessage(chat.get('agent'))
|
|
114
|
+
const { removeProgressMessage } = showProgressMessage(ctx, progress)
|
|
115
|
+
try {
|
|
116
|
+
for (let i = 0; i < (qc.steps?.length ?? 0); i++) {
|
|
117
|
+
updateProgressMessageForStep(progress, qc, i)
|
|
118
|
+
await (qc.steps?.[i].type === 'FETCH' ? runFetchStep(ctx, i) : runLLMStep(ctx, i))
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
removeProgressMessage()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function formatResult({ qc, code, executionId, context, resultMap, signal }: QCContext) {
|
|
126
|
+
const formatted = await aiClient.formatResultOfQuickCommand.mutate({
|
|
127
|
+
slug: qc.slug,
|
|
128
|
+
quickCommandsExecutionRequest: {
|
|
129
|
+
input_data: code,
|
|
130
|
+
context,
|
|
131
|
+
qc_execution_id: executionId,
|
|
132
|
+
slugs_executions: resultMap,
|
|
133
|
+
},
|
|
134
|
+
}, signal)
|
|
135
|
+
return formatted.result
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// opens a new chat tab if the quick command doesn't preserve the conversation and the current conversation isn't clean.
|
|
139
|
+
function manageConversationContext(ctx: QCContext) {
|
|
140
|
+
if (!ctx.qc.preserve_conversation && ctx.chat.getMessages().length > 1) {
|
|
141
|
+
if (ctx.qc.return_type === 'CHAT') {
|
|
142
|
+
ctx.chat.set('isLoading', false)
|
|
143
|
+
ctx.chat = ctx.chat.transferToNewChat(ctx.qc.name || ctx.qc.slug)
|
|
144
|
+
ctx.chat.set('isLoading', true)
|
|
145
|
+
widget.chatTabs.add(ctx.chat)
|
|
146
|
+
widget.chatTabs.select(ctx.chat.id)
|
|
147
|
+
ctx.context.conversation_id = ctx.chat.id
|
|
148
|
+
} else {
|
|
149
|
+
ctx.context.conversation_id = ulid()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function runQuickCommand(ctx: QCContext) {
|
|
155
|
+
const { qc: { slug }, code } = ctx
|
|
156
|
+
const t = translate(dictionary)
|
|
157
|
+
ctx.signal.addEventListener('abort', () => aiClient.quickCommand.cancel({ slug }))
|
|
158
|
+
await aiClient.quickCommand.invalidate({ slug })
|
|
159
|
+
try {
|
|
160
|
+
ctx.qc = await aiClient.quickCommand.query({ slug })
|
|
161
|
+
} catch (error) {
|
|
162
|
+
throw error instanceof StackspotAPIError && error.status === 404 ? new Error(t.notFound) : error
|
|
163
|
+
}
|
|
164
|
+
if (ctx.qc.use_selected_code && !code) {
|
|
165
|
+
throw new Error(t.requiresSelection)
|
|
166
|
+
}
|
|
167
|
+
manageConversationContext(ctx)
|
|
168
|
+
await computeCustomInputs(ctx)
|
|
169
|
+
await runSteps(ctx)
|
|
170
|
+
return formatResult(ctx)
|
|
11
171
|
}
|
|
172
|
+
|
|
173
|
+
async function registerAnalyticsEvent({ qc, executionId, code = '', context }: QCContext, status: string, start: number) {
|
|
174
|
+
const now = new Date().getTime()
|
|
175
|
+
try {
|
|
176
|
+
await aiClient.createEvent.mutate({
|
|
177
|
+
body: [{
|
|
178
|
+
type: 'custom_quick_command_execution',
|
|
179
|
+
quick_command_event: {
|
|
180
|
+
type: qc.type || '',
|
|
181
|
+
duration_execution: now - start,
|
|
182
|
+
status_execution: status,
|
|
183
|
+
slug: qc.slug,
|
|
184
|
+
qc_execution_id: executionId,
|
|
185
|
+
id: qc.id,
|
|
186
|
+
},
|
|
187
|
+
code,
|
|
188
|
+
context,
|
|
189
|
+
knowledge_sources: [],
|
|
190
|
+
size: getSizeOfString(code),
|
|
191
|
+
generated_at: now,
|
|
192
|
+
}],
|
|
193
|
+
})
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// eslint-disable-next-line no-console
|
|
196
|
+
console.warn('Failed to register event: quick command.')
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function outputResult({ qc, chat, code }: QCContext, result: string) {
|
|
201
|
+
if (qc.return_type === 'CHAT') {
|
|
202
|
+
chat.pushMessage(new ChatEntry({
|
|
203
|
+
agentType: 'bot',
|
|
204
|
+
content: result,
|
|
205
|
+
agent: chat.get('agent'),
|
|
206
|
+
type: 'md',
|
|
207
|
+
}))
|
|
208
|
+
} else {
|
|
209
|
+
const editor = getEditor()?.getEditors()[0]
|
|
210
|
+
let finalCode = result
|
|
211
|
+
if (qc.return_type === 'BEFORE_CODE') finalCode = `${result}\n${code}`
|
|
212
|
+
else if (qc.return_type === 'AFTER_CODE') finalCode = `${code}\n${result}`
|
|
213
|
+
finalCode = finalCode.replace(/(\$[{a-z]|\})/g, '\\$1')
|
|
214
|
+
editor?.trigger('keyboard', 'type', { text: finalCode })
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function quickCommandInterceptor(entry: ChatEntry, chat: ChatState, signal: AbortSignal) {
|
|
219
|
+
const { agentType, content } = entry.getValue()
|
|
220
|
+
if (agentType !== 'user' || !commandRegex.test(content.trim())) return
|
|
221
|
+
const t = translate(dictionary)
|
|
222
|
+
const slug = content.trim().substring(1)
|
|
223
|
+
const ctx: QCContext = {
|
|
224
|
+
qc: { slug } as QuickCommandResponse,
|
|
225
|
+
chat,
|
|
226
|
+
code: chat.get('codeSelection'),
|
|
227
|
+
context: buildConversationContext(chat) ?? {},
|
|
228
|
+
executionId: ulid(),
|
|
229
|
+
resultMap: {},
|
|
230
|
+
signal,
|
|
231
|
+
}
|
|
232
|
+
chat.set('isLoading', true)
|
|
233
|
+
const start = new Date().getTime()
|
|
234
|
+
try {
|
|
235
|
+
const result = await runQuickCommand(ctx)
|
|
236
|
+
outputResult(ctx, result)
|
|
237
|
+
registerAnalyticsEvent(ctx, '200', start)
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
let message = error.message || `${error}`
|
|
240
|
+
if (error instanceof AbortedError || error instanceof CancelledError) message = t.aborted
|
|
241
|
+
else if (error instanceof StackspotAPIError) message = error.translate()
|
|
242
|
+
ctx.chat.pushMessage(new ChatEntry({
|
|
243
|
+
agentType: 'bot',
|
|
244
|
+
content: '',
|
|
245
|
+
error: message,
|
|
246
|
+
agent: chat.get('agent'),
|
|
247
|
+
type: 'text',
|
|
248
|
+
}))
|
|
249
|
+
registerAnalyticsEvent(ctx, message, start)
|
|
250
|
+
}
|
|
251
|
+
ctx.chat.set('isLoading', false)
|
|
252
|
+
// prevents the next interceptors from running
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return quickCommandInterceptor
|
|
12
257
|
}
|
|
258
|
+
|
|
259
|
+
const dictionary = {
|
|
260
|
+
en: {
|
|
261
|
+
requiresSelection: 'This quick command requires some code to be selected in the editor.',
|
|
262
|
+
startQuestioning: 'To execute the Quick Command "$0", I\'ll need you to provide some information. Some may be mandatory, and others optional. Let\'s get started.',
|
|
263
|
+
progress: 'Running quick command "$0". Step $1 ($2) of $3.',
|
|
264
|
+
aborted: 'The quick command execution aborted by the user.',
|
|
265
|
+
notFound: 'There\'s no quick command with the provided name. If you don\'t wish to run a command, prefix the first "/" with a "\\".',
|
|
266
|
+
},
|
|
267
|
+
pt: {
|
|
268
|
+
requiresSelection: 'Este quick command precisa que algum código esteja selecionado no editor.',
|
|
269
|
+
startQuestioning: 'Para executar o Quick Command "$0", vou precisar que você providencie algumas explicações. Algumas são obrigatórias e outras opcionais. Vamos começar.',
|
|
270
|
+
progress: 'Executando quick command "$0". Passo $1 ($2) de $3.',
|
|
271
|
+
aborted: 'A execução do quick command foi abortada pelo usuário.',
|
|
272
|
+
notFound: 'Não existe quick command com o nome providenciado. Se você não quiser rodar um comando, prefixe o primeiro "/" com um "\\".',
|
|
273
|
+
},
|
|
274
|
+
} satisfies Dictionary
|
|
@@ -4,15 +4,16 @@ import { ChatState } from '../state/ChatState'
|
|
|
4
4
|
import { buildConversationContext } from '../utils/chat'
|
|
5
5
|
import { genericSourcesToKnowledgeSources } from '../utils/knowledge-source'
|
|
6
6
|
|
|
7
|
-
export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState) {
|
|
7
|
+
export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState, signal: AbortSignal) {
|
|
8
8
|
const { agentType, content } = entry.getValue()
|
|
9
9
|
if (agentType !== 'user') return
|
|
10
10
|
const context = buildConversationContext(chat)
|
|
11
11
|
chat.set('isLoading', true)
|
|
12
12
|
const isFirstMessage = chat.getMessages().length === 1
|
|
13
13
|
if (isFirstMessage) chat.set('label', content)
|
|
14
|
-
const stream = aiClient.sendChatMessage({ context, user_prompt: content })
|
|
15
|
-
|
|
14
|
+
const stream = aiClient.sendChatMessage({ context, user_prompt: content.replace(/^\s*\\(\\|\/)/, '$1') })
|
|
15
|
+
signal.addEventListener('abort', () => stream.cancel())
|
|
16
|
+
const botEntry = ChatEntry.createStreamedBotEntry()
|
|
16
17
|
chat.pushMessage(botEntry)
|
|
17
18
|
let knowledgeSources: KnowledgeSource[] | undefined
|
|
18
19
|
stream.onChange(value => {
|
|
@@ -39,6 +40,5 @@ export async function sendMessageInterceptor(entry: ChatEntry, chat: ChatState)
|
|
|
39
40
|
error: error instanceof StackspotAPIError ? error.translate() : (error.message ?? `${error}`),
|
|
40
41
|
})
|
|
41
42
|
}
|
|
42
|
-
botEntry.finish()
|
|
43
43
|
chat.set('isLoading', false)
|
|
44
44
|
}
|
package/src/state/ChatEntry.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ColorPaletteName } from '@stack-spot/portal-theme'
|
|
1
2
|
import { pull } from 'lodash'
|
|
2
3
|
import { LabeledWithImage } from './types'
|
|
3
4
|
|
|
@@ -27,16 +28,15 @@ export interface KnowledgeSource {
|
|
|
27
28
|
export interface TextChatEntry {
|
|
28
29
|
type: 'text' | 'md',
|
|
29
30
|
agentType: 'bot' | 'user' | 'system',
|
|
30
|
-
// image?: string,
|
|
31
31
|
actions?: ChatAction[],
|
|
32
|
-
subtitle?: string,
|
|
33
32
|
content: string,
|
|
34
33
|
knowledgeSources?: KnowledgeSource[],
|
|
35
34
|
updated?: string,
|
|
36
35
|
agent?: LabeledWithImage,
|
|
37
36
|
messageId?: string,
|
|
38
37
|
error?: string,
|
|
39
|
-
|
|
38
|
+
badges?: { color?: ColorPaletteName, label: string }[],
|
|
39
|
+
card?: boolean,
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
type ChatEntryListener = (value: TextChatEntry) => void
|
|
@@ -46,20 +46,15 @@ let nextId = 0
|
|
|
46
46
|
export class ChatEntry {
|
|
47
47
|
readonly id: number
|
|
48
48
|
private value: TextChatEntry
|
|
49
|
-
private streamFinished: boolean
|
|
50
49
|
private listeners: ChatEntryListener[] = []
|
|
51
|
-
abort: () => void
|
|
52
50
|
|
|
53
51
|
/**
|
|
54
52
|
* @param value the value of the entry.
|
|
55
53
|
* @param isStreamed whether or not this entry is streamed. Defaults to false.
|
|
56
|
-
* @param abort an abort function to cancel the transmission of this chat entry. Specially useful for canceling streamings.
|
|
57
54
|
*/
|
|
58
|
-
constructor(value: TextChatEntry
|
|
55
|
+
constructor(value: TextChatEntry) {
|
|
59
56
|
this.id = nextId++
|
|
60
57
|
this.value = value
|
|
61
|
-
this.streamFinished = !isStreamed
|
|
62
|
-
this.abort = abort
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
static createUserEntry(content: string, isMd = false) {
|
|
@@ -71,8 +66,8 @@ export class ChatEntry {
|
|
|
71
66
|
})
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
static createStreamedBotEntry(
|
|
75
|
-
return new ChatEntry({ agentType: 'bot', type: 'md', content: '' }
|
|
69
|
+
static createStreamedBotEntry() {
|
|
70
|
+
return new ChatEntry({ agentType: 'bot', type: 'md', content: '' })
|
|
76
71
|
}
|
|
77
72
|
|
|
78
73
|
setValue(value: TextChatEntry) {
|
|
@@ -84,16 +79,8 @@ export class ChatEntry {
|
|
|
84
79
|
return this.value
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
finish() {
|
|
88
|
-
this.streamFinished = true
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
hasFinished() {
|
|
92
|
-
return this.streamFinished
|
|
93
|
-
}
|
|
94
|
-
|
|
95
82
|
onChange(listener: ChatEntryListener) {
|
|
96
|
-
|
|
83
|
+
this.listeners.push(listener)
|
|
97
84
|
return () => {
|
|
98
85
|
pull(this.listeners, listener)
|
|
99
86
|
}
|