@eeacms/volto-eea-chatbot 4.1.1 → 4.1.3
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/CHANGELOG.md +18 -0
- package/locales/de/LC_MESSAGES/volto.po +12 -0
- package/locales/en/LC_MESSAGES/volto.po +12 -0
- package/locales/it/LC_MESSAGES/volto.po +12 -0
- package/locales/ro/LC_MESSAGES/volto.po +12 -0
- package/locales/volto.pot +13 -1
- package/package.json +1 -1
- package/src/ChatBlock/chat/AIMessage.tsx +41 -0
- package/src/ChatBlock/chat/ChatWindow.tsx +91 -2
- package/src/ChatBlock/components/AutoResizeTextarea.jsx +71 -18
- package/src/ChatBlock/hooks/useChatController.ts +2 -2
- package/src/ChatBlock/packets/MultiToolRenderer.tsx +7 -3
- package/src/ChatBlock/schema.jsx +14 -1
- package/src/ChatBlock/services/streamingService.ts +16 -0
- package/src/ChatBlock/style.less +131 -1
- package/src/ChatBlock/types/interfaces.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [4.1.3](https://github.com/eea/volto-eea-chatbot/compare/4.1.2...4.1.3) - 21 September 2026
|
|
8
|
+
|
|
9
|
+
#### :bug: Bug Fixes
|
|
10
|
+
|
|
11
|
+
- fix(chat): remove duplicate loader during tool execution and default to Onyx v3 [Tiberiu Ichim - [`127b919`](https://github.com/eea/volto-eea-chatbot/commit/127b9191a3141b9f59d44b0eebb69e11b68a6420)]
|
|
12
|
+
|
|
13
|
+
### [4.1.2](https://github.com/eea/volto-eea-chatbot/compare/4.1.1...4.1.2) - 17 September 2026
|
|
14
|
+
|
|
15
|
+
#### :rocket: New Features
|
|
16
|
+
|
|
17
|
+
- feat: make floating pill the default stop button mode [Tiberiu Ichim - [`c7ce2f0`](https://github.com/eea/volto-eea-chatbot/commit/c7ce2f0eb7e0e9fd843eceadf12f5555b807cc1a)]
|
|
18
|
+
- feat: support stop button modes (input, floating pill, message loader) with pulse animation [Tiberiu Ichim - [`fb29273`](https://github.com/eea/volto-eea-chatbot/commit/fb2927319872a1e39f1fdc0557f1b290d76c42dd)]
|
|
19
|
+
- feat: add stop generation button with block toggle option [Tiberiu Ichim - [`b964ae3`](https://github.com/eea/volto-eea-chatbot/commit/b964ae3dce9abb7f4dd8c4acf2c1374bd5004358)]
|
|
20
|
+
|
|
21
|
+
#### :bug: Bug Fixes
|
|
22
|
+
|
|
23
|
+
- fix: avoid duplicate stop buttons when using message_loader option [Tiberiu Ichim - [`4f0c854`](https://github.com/eea/volto-eea-chatbot/commit/4f0c854bef40172b754db0ca46405426f1a2fd60)]
|
|
24
|
+
|
|
7
25
|
### [4.1.1](https://github.com/eea/volto-eea-chatbot/compare/4.1.0...4.1.1) - 14 September 2026
|
|
8
26
|
|
|
9
27
|
### [4.1.0](https://github.com/eea/volto-eea-chatbot/compare/4.0.0...4.1.0) - 31 August 2026
|
|
@@ -46,6 +46,18 @@ msgstr ""
|
|
|
46
46
|
msgid "Invalid assistant page URL."
|
|
47
47
|
msgstr ""
|
|
48
48
|
|
|
49
|
+
#. Default: "Send"
|
|
50
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
51
|
+
msgid "Send"
|
|
52
|
+
msgstr ""
|
|
53
|
+
|
|
54
|
+
#. Default: "Stop generating"
|
|
55
|
+
#: ChatBlock/chat/AIMessage
|
|
56
|
+
#: ChatBlock/chat/ChatWindow
|
|
57
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
58
|
+
msgid "Stop generating"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
49
61
|
#. Default: "Submit question"
|
|
50
62
|
#: AISearchInput/components/SearchInput
|
|
51
63
|
#: AISearchInput/messages
|
|
@@ -46,6 +46,18 @@ msgstr ""
|
|
|
46
46
|
msgid "Invalid assistant page URL."
|
|
47
47
|
msgstr ""
|
|
48
48
|
|
|
49
|
+
#. Default: "Send"
|
|
50
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
51
|
+
msgid "Send"
|
|
52
|
+
msgstr ""
|
|
53
|
+
|
|
54
|
+
#. Default: "Stop generating"
|
|
55
|
+
#: ChatBlock/chat/AIMessage
|
|
56
|
+
#: ChatBlock/chat/ChatWindow
|
|
57
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
58
|
+
msgid "Stop generating"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
49
61
|
#. Default: "Submit question"
|
|
50
62
|
#: AISearchInput/components/SearchInput
|
|
51
63
|
#: AISearchInput/messages
|
|
@@ -46,6 +46,18 @@ msgstr ""
|
|
|
46
46
|
msgid "Invalid assistant page URL."
|
|
47
47
|
msgstr ""
|
|
48
48
|
|
|
49
|
+
#. Default: "Send"
|
|
50
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
51
|
+
msgid "Send"
|
|
52
|
+
msgstr ""
|
|
53
|
+
|
|
54
|
+
#. Default: "Stop generating"
|
|
55
|
+
#: ChatBlock/chat/AIMessage
|
|
56
|
+
#: ChatBlock/chat/ChatWindow
|
|
57
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
58
|
+
msgid "Stop generating"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
49
61
|
#. Default: "Submit question"
|
|
50
62
|
#: AISearchInput/components/SearchInput
|
|
51
63
|
#: AISearchInput/messages
|
|
@@ -46,6 +46,18 @@ msgstr ""
|
|
|
46
46
|
msgid "Invalid assistant page URL."
|
|
47
47
|
msgstr ""
|
|
48
48
|
|
|
49
|
+
#. Default: "Send"
|
|
50
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
51
|
+
msgid "Send"
|
|
52
|
+
msgstr ""
|
|
53
|
+
|
|
54
|
+
#. Default: "Stop generating"
|
|
55
|
+
#: ChatBlock/chat/AIMessage
|
|
56
|
+
#: ChatBlock/chat/ChatWindow
|
|
57
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
58
|
+
msgid "Stop generating"
|
|
59
|
+
msgstr ""
|
|
60
|
+
|
|
49
61
|
#. Default: "Submit question"
|
|
50
62
|
#: AISearchInput/components/SearchInput
|
|
51
63
|
#: AISearchInput/messages
|
package/locales/volto.pot
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
msgid ""
|
|
2
2
|
msgstr ""
|
|
3
3
|
"Project-Id-Version: Plone\n"
|
|
4
|
-
"POT-Creation-Date: 2026-
|
|
4
|
+
"POT-Creation-Date: 2026-09-17T10:07:41.673Z\n"
|
|
5
5
|
"Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
|
|
6
6
|
"Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
|
|
7
7
|
"Content-Type: text/plain; charset=utf-8\n"
|
|
@@ -48,6 +48,18 @@ msgstr ""
|
|
|
48
48
|
msgid "Invalid assistant page URL."
|
|
49
49
|
msgstr ""
|
|
50
50
|
|
|
51
|
+
#. Default: "Send"
|
|
52
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
53
|
+
msgid "Send"
|
|
54
|
+
msgstr ""
|
|
55
|
+
|
|
56
|
+
#. Default: "Stop generating"
|
|
57
|
+
#: ChatBlock/chat/AIMessage
|
|
58
|
+
#: ChatBlock/chat/ChatWindow
|
|
59
|
+
#: ChatBlock/components/AutoResizeTextarea
|
|
60
|
+
msgid "Stop generating"
|
|
61
|
+
msgstr ""
|
|
62
|
+
|
|
51
63
|
#. Default: "Submit question"
|
|
52
64
|
#: AISearchInput/components/SearchInput
|
|
53
65
|
#: AISearchInput/messages
|
package/package.json
CHANGED
|
@@ -23,6 +23,9 @@ import { addCitations } from '@eeacms/volto-eea-chatbot/ChatBlock/utils/citation
|
|
|
23
23
|
import SVGIcon from '@eeacms/volto-eea-chatbot/ChatBlock/components/Icon';
|
|
24
24
|
import BotIcon from '@eeacms/volto-eea-chatbot/icons/bot.svg';
|
|
25
25
|
import ClearIcon from '@eeacms/volto-eea-chatbot/icons/clear.svg';
|
|
26
|
+
import StopIcon from '@eeacms/volto-eea-chatbot/icons/stop.svg';
|
|
27
|
+
import { trackEvent } from '@eeacms/volto-matomo/utils';
|
|
28
|
+
import { FormattedMessage } from 'react-intl';
|
|
26
29
|
|
|
27
30
|
// Lazy load heavy components
|
|
28
31
|
const SourceDetails: any = loadable(
|
|
@@ -214,6 +217,8 @@ export function AIMessage({
|
|
|
214
217
|
extraRemarkPlugins,
|
|
215
218
|
extraMarkdownComponents,
|
|
216
219
|
extraRehypePlugins,
|
|
220
|
+
stopButton,
|
|
221
|
+
onCancel,
|
|
217
222
|
}: ChatMessageProps) {
|
|
218
223
|
const [allToolsDisplayed, setAllToolsDisplayed] = useState(false);
|
|
219
224
|
const [messageDisplayed, setMessageDisplayed] = useState(false);
|
|
@@ -470,6 +475,42 @@ export function AIMessage({
|
|
|
470
475
|
</RendererComponent>
|
|
471
476
|
</div>
|
|
472
477
|
))}
|
|
478
|
+
|
|
479
|
+
{stopButton === 'message_loader' &&
|
|
480
|
+
Boolean(message.isFinalMessageComing) &&
|
|
481
|
+
isLoading &&
|
|
482
|
+
isLastMessage &&
|
|
483
|
+
onCancel && (
|
|
484
|
+
<div className="inline-message-stop">
|
|
485
|
+
<Button
|
|
486
|
+
className="inline-message-stop-btn"
|
|
487
|
+
type="button"
|
|
488
|
+
aria-label="Stop generating"
|
|
489
|
+
onClick={() => {
|
|
490
|
+
if (enableMatomoTracking) {
|
|
491
|
+
trackEvent({
|
|
492
|
+
category: persona ? `Chatbot - ${persona}` : 'Chatbot',
|
|
493
|
+
action: 'Chatbot: Stop generating',
|
|
494
|
+
name: 'Message generation stopped',
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
onCancel();
|
|
498
|
+
}}
|
|
499
|
+
>
|
|
500
|
+
<div className="stop-btn-inner">
|
|
501
|
+
<div className="stop-icon-circle">
|
|
502
|
+
<SVGIcon name={StopIcon} size={8} color="#fff" />
|
|
503
|
+
</div>
|
|
504
|
+
<span>
|
|
505
|
+
<FormattedMessage
|
|
506
|
+
id="Stop generating"
|
|
507
|
+
defaultMessage="Stop generating"
|
|
508
|
+
/>
|
|
509
|
+
</span>
|
|
510
|
+
</div>
|
|
511
|
+
</Button>
|
|
512
|
+
</div>
|
|
513
|
+
)}
|
|
473
514
|
</div>
|
|
474
515
|
|
|
475
516
|
{/* Total fail message */}
|
|
@@ -10,6 +10,7 @@ import type { Persona } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfac
|
|
|
10
10
|
import { Button, Form, Segment, Checkbox } from 'semantic-ui-react';
|
|
11
11
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable';
|
|
12
12
|
import { trackEvent } from '@eeacms/volto-matomo/utils';
|
|
13
|
+
import { FormattedMessage } from 'react-intl';
|
|
13
14
|
|
|
14
15
|
import { ChatMessage } from '.';
|
|
15
16
|
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
@@ -19,6 +20,7 @@ import EmptyState from '@eeacms/volto-eea-chatbot/ChatBlock/components/EmptyStat
|
|
|
19
20
|
import { useChatController } from '@eeacms/volto-eea-chatbot/ChatBlock/hooks';
|
|
20
21
|
import SVGIcon from '@eeacms/volto-eea-chatbot/ChatBlock/components/Icon';
|
|
21
22
|
import PenIcon from '@eeacms/volto-eea-chatbot/icons/square-pen.svg';
|
|
23
|
+
import StopIcon from '@eeacms/volto-eea-chatbot/icons/stop.svg';
|
|
22
24
|
|
|
23
25
|
import '@eeacms/volto-eea-chatbot/ChatBlock/style.less';
|
|
24
26
|
|
|
@@ -33,6 +35,8 @@ interface ChatWindowProps {
|
|
|
33
35
|
qgenAsistantId?: number;
|
|
34
36
|
enableQgen?: boolean;
|
|
35
37
|
enableFeedback?: boolean;
|
|
38
|
+
stopButton?: 'disabled' | 'input' | 'floating' | 'message_loader';
|
|
39
|
+
enableStopButton?: boolean;
|
|
36
40
|
scrollToInput?: boolean;
|
|
37
41
|
feedbackReasons?: string[];
|
|
38
42
|
qualityCheck?: string;
|
|
@@ -74,6 +78,8 @@ function ChatWindow({
|
|
|
74
78
|
qgenAsistantId,
|
|
75
79
|
enableQgen,
|
|
76
80
|
enableFeedback = true,
|
|
81
|
+
stopButton,
|
|
82
|
+
enableStopButton = true,
|
|
77
83
|
scrollToInput,
|
|
78
84
|
feedbackReasons,
|
|
79
85
|
qualityCheck = 'disabled',
|
|
@@ -91,12 +97,20 @@ function ChatWindow({
|
|
|
91
97
|
onDemandInputToggle = true,
|
|
92
98
|
maxContextSegments = 0,
|
|
93
99
|
batchSize,
|
|
94
|
-
onyxVersion = '
|
|
100
|
+
onyxVersion = '3',
|
|
95
101
|
hideSourcesTab,
|
|
96
102
|
extraRemarkPlugins,
|
|
97
103
|
extraMarkdownComponents,
|
|
98
104
|
extraRehypePlugins,
|
|
99
105
|
} = data;
|
|
106
|
+
|
|
107
|
+
const stopButtonSetting:
|
|
108
|
+
| 'disabled'
|
|
109
|
+
| 'input'
|
|
110
|
+
| 'floating'
|
|
111
|
+
| 'message_loader' =
|
|
112
|
+
stopButton || (enableStopButton === false ? 'disabled' : 'floating');
|
|
113
|
+
|
|
100
114
|
const [qualityCheckEnabled, setQualityCheckEnabled] = useState(
|
|
101
115
|
onDemandInputToggle ?? true,
|
|
102
116
|
);
|
|
@@ -123,6 +137,7 @@ function ChatWindow({
|
|
|
123
137
|
isStreaming,
|
|
124
138
|
isFetchingRelatedQuestions,
|
|
125
139
|
clearChat,
|
|
140
|
+
cancelStreaming,
|
|
126
141
|
setIsDeepResearchEnabled,
|
|
127
142
|
isDeepResearchEnabled,
|
|
128
143
|
} = useChatController({
|
|
@@ -249,18 +264,57 @@ function ChatWindow({
|
|
|
249
264
|
extraRemarkPlugins={extraRemarkPlugins}
|
|
250
265
|
extraMarkdownComponents={extraMarkdownComponents}
|
|
251
266
|
extraRehypePlugins={extraRehypePlugins}
|
|
267
|
+
stopButton={stopButtonSetting}
|
|
268
|
+
onCancel={cancelStreaming}
|
|
252
269
|
/>
|
|
253
270
|
</React.Fragment>
|
|
254
271
|
))}
|
|
255
272
|
|
|
256
273
|
{isStreaming &&
|
|
257
274
|
!isFetchingRelatedQuestions &&
|
|
258
|
-
|
|
275
|
+
messages[messages.length - 1]?.type === 'user' && (
|
|
259
276
|
<div className="comment">
|
|
260
277
|
<div className="circle assistant placeholder"></div>
|
|
261
278
|
<div className="comment-content">
|
|
262
279
|
<div className="loader-container">
|
|
263
280
|
<div className="loader" />
|
|
281
|
+
{stopButtonSetting === 'message_loader' && (
|
|
282
|
+
<div className="message-loader-stop">
|
|
283
|
+
<Button
|
|
284
|
+
className="message-loader-stop-btn"
|
|
285
|
+
type="button"
|
|
286
|
+
aria-label="Stop generating"
|
|
287
|
+
onClick={() => {
|
|
288
|
+
if (enableMatomoTracking) {
|
|
289
|
+
trackEvent({
|
|
290
|
+
category: persona?.name
|
|
291
|
+
? `Chatbot - ${persona.name}`
|
|
292
|
+
: 'Chatbot',
|
|
293
|
+
action: 'Chatbot: Stop generating',
|
|
294
|
+
name: 'Message generation stopped',
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
cancelStreaming();
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
<div className="stop-btn-inner">
|
|
301
|
+
<div className="stop-icon-circle">
|
|
302
|
+
<SVGIcon
|
|
303
|
+
name={StopIcon}
|
|
304
|
+
size={8}
|
|
305
|
+
color="#fff"
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
<span>
|
|
309
|
+
<FormattedMessage
|
|
310
|
+
id="Stop generating"
|
|
311
|
+
defaultMessage="Stop generating"
|
|
312
|
+
/>
|
|
313
|
+
</span>
|
|
314
|
+
</div>
|
|
315
|
+
</Button>
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
264
318
|
</div>
|
|
265
319
|
</div>
|
|
266
320
|
</div>
|
|
@@ -271,6 +325,39 @@ function ChatWindow({
|
|
|
271
325
|
</div>
|
|
272
326
|
|
|
273
327
|
<div className="chat-form">
|
|
328
|
+
{stopButtonSetting === 'floating' && isStreaming && (
|
|
329
|
+
<div className="chat-floating-stop">
|
|
330
|
+
<Button
|
|
331
|
+
className="floating-stop-btn"
|
|
332
|
+
type="button"
|
|
333
|
+
aria-label="Stop generating"
|
|
334
|
+
onClick={() => {
|
|
335
|
+
if (enableMatomoTracking) {
|
|
336
|
+
trackEvent({
|
|
337
|
+
category: persona?.name
|
|
338
|
+
? `Chatbot - ${persona.name}`
|
|
339
|
+
: 'Chatbot',
|
|
340
|
+
action: 'Chatbot: Stop generating',
|
|
341
|
+
name: 'Message generation stopped',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
cancelStreaming();
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
<div className="stop-btn-inner">
|
|
348
|
+
<div className="stop-icon-circle">
|
|
349
|
+
<SVGIcon name={StopIcon} size={8} color="#fff" />
|
|
350
|
+
</div>
|
|
351
|
+
<span>
|
|
352
|
+
<FormattedMessage
|
|
353
|
+
id="Stop generating"
|
|
354
|
+
defaultMessage="Stop generating"
|
|
355
|
+
/>
|
|
356
|
+
</span>
|
|
357
|
+
</div>
|
|
358
|
+
</Button>
|
|
359
|
+
</div>
|
|
360
|
+
)}
|
|
274
361
|
{/* @ts-ignore */}
|
|
275
362
|
<Form>
|
|
276
363
|
<div className="textarea-wrapper">
|
|
@@ -283,6 +370,8 @@ function ChatWindow({
|
|
|
283
370
|
messages.length > 0 ? 'Ask follow-up...' : placeholderPrompt
|
|
284
371
|
}
|
|
285
372
|
isStreaming={isStreaming}
|
|
373
|
+
stopButton={stopButtonSetting}
|
|
374
|
+
onCancel={cancelStreaming}
|
|
286
375
|
enableMatomoTracking={enableMatomoTracking}
|
|
287
376
|
persona={persona}
|
|
288
377
|
onSubmit={onSubmit}
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useIntl, defineMessages } from 'react-intl';
|
|
2
3
|
import { Button } from 'semantic-ui-react';
|
|
3
4
|
import { trackEvent } from '@eeacms/volto-matomo/utils';
|
|
4
5
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
5
6
|
|
|
6
7
|
import SVGIcon from './Icon';
|
|
7
8
|
import SendIcon from '@eeacms/volto-eea-chatbot/icons/send.svg';
|
|
9
|
+
import StopIcon from '@eeacms/volto-eea-chatbot/icons/stop.svg';
|
|
10
|
+
|
|
11
|
+
const messages = defineMessages({
|
|
12
|
+
send: {
|
|
13
|
+
id: 'Send',
|
|
14
|
+
defaultMessage: 'Send',
|
|
15
|
+
},
|
|
16
|
+
stopGenerating: {
|
|
17
|
+
id: 'Stop generating',
|
|
18
|
+
defaultMessage: 'Stop generating',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
8
21
|
|
|
9
22
|
export default React.forwardRef(function AutoResizeTextarea(props, ref) {
|
|
10
|
-
const {
|
|
11
|
-
|
|
23
|
+
const {
|
|
24
|
+
onSubmit,
|
|
25
|
+
isStreaming,
|
|
26
|
+
stopButton,
|
|
27
|
+
enableStopButton = true,
|
|
28
|
+
onCancel,
|
|
29
|
+
enableMatomoTracking,
|
|
30
|
+
persona,
|
|
31
|
+
...rest
|
|
32
|
+
} = props;
|
|
33
|
+
const intl = useIntl();
|
|
12
34
|
const [input, setInput] = React.useState('');
|
|
13
35
|
|
|
14
36
|
const handleSubmit = (e) => {
|
|
@@ -27,6 +49,24 @@ export default React.forwardRef(function AutoResizeTextarea(props, ref) {
|
|
|
27
49
|
}
|
|
28
50
|
};
|
|
29
51
|
|
|
52
|
+
const handleCancel = (e) => {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
if (enableMatomoTracking) {
|
|
55
|
+
trackEvent({
|
|
56
|
+
category: persona?.name ? `Chatbot - ${persona.name}` : 'Chatbot',
|
|
57
|
+
action: 'Chatbot: Stop generating',
|
|
58
|
+
name: 'Message generation stopped',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (onCancel) {
|
|
62
|
+
onCancel();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const resolvedStopButton =
|
|
67
|
+
stopButton || (enableStopButton === false ? 'disabled' : 'floating');
|
|
68
|
+
const showStopButton = Boolean(resolvedStopButton === 'input' && isStreaming);
|
|
69
|
+
|
|
30
70
|
return (
|
|
31
71
|
<>
|
|
32
72
|
<TextareaAutosize
|
|
@@ -46,22 +86,35 @@ export default React.forwardRef(function AutoResizeTextarea(props, ref) {
|
|
|
46
86
|
/>
|
|
47
87
|
|
|
48
88
|
<div className="chat-right-actions">
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
{showStopButton ? (
|
|
90
|
+
<Button
|
|
91
|
+
className="stop-btn"
|
|
92
|
+
type="button"
|
|
93
|
+
aria-label={intl.formatMessage(messages.stopGenerating)}
|
|
94
|
+
onClick={handleCancel}
|
|
95
|
+
>
|
|
96
|
+
<div className="btn-icon">
|
|
97
|
+
<SVGIcon name={StopIcon} size={14} color="#fff" />
|
|
98
|
+
</div>
|
|
99
|
+
</Button>
|
|
100
|
+
) : (
|
|
101
|
+
<Button
|
|
102
|
+
className="submit-btn"
|
|
103
|
+
type="submit"
|
|
104
|
+
aria-label={intl.formatMessage(messages.send)}
|
|
105
|
+
onKeyDown={(e) => {
|
|
106
|
+
handleSubmit(e);
|
|
107
|
+
}}
|
|
108
|
+
disabled={isStreaming || input.trim() === ''}
|
|
109
|
+
onClick={(e) => {
|
|
110
|
+
handleSubmit(e);
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<div className="btn-icon">
|
|
114
|
+
<SVGIcon name={SendIcon} size="28" />
|
|
115
|
+
</div>
|
|
116
|
+
</Button>
|
|
117
|
+
)}
|
|
65
118
|
</div>
|
|
66
119
|
</>
|
|
67
120
|
);
|
|
@@ -65,7 +65,7 @@ async function fetchRelatedQuestions(
|
|
|
65
65
|
query: string,
|
|
66
66
|
answer: string,
|
|
67
67
|
qgenAsistantId: number,
|
|
68
|
-
onyxVersion: '2' | '3' = '
|
|
68
|
+
onyxVersion: '2' | '3' = '3',
|
|
69
69
|
): Promise<RelatedQuestion[]> {
|
|
70
70
|
try {
|
|
71
71
|
console.log(`[RQ] Creating session for assistant ${qgenAsistantId} (Onyx v${onyxVersion})`);
|
|
@@ -122,7 +122,7 @@ export function useChatController({
|
|
|
122
122
|
enableQgen = false,
|
|
123
123
|
qgenAsistantId,
|
|
124
124
|
deepResearch,
|
|
125
|
-
onyxVersion = '
|
|
125
|
+
onyxVersion = '3',
|
|
126
126
|
}: UseChatControllerProps) {
|
|
127
127
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
128
128
|
const [chatSessionId, setChatSessionId] = useState<string | null>(null);
|
|
@@ -29,8 +29,12 @@ export function MultiToolRenderer({
|
|
|
29
29
|
|
|
30
30
|
// Filter tool groups based on allowed tool types
|
|
31
31
|
const filteredToolGroups = useMemo(() => {
|
|
32
|
-
const
|
|
33
|
-
|
|
32
|
+
const activeShowTools =
|
|
33
|
+
Array.isArray(showTools) && showTools.length > 0
|
|
34
|
+
? showTools
|
|
35
|
+
: [PacketType.SEARCH_TOOL_START, PacketType.REASONING_START];
|
|
36
|
+
const expandedShowTools = [...activeShowTools];
|
|
37
|
+
if (activeShowTools.includes(PacketType.SEARCH_TOOL_START)) {
|
|
34
38
|
expandedShowTools.push(
|
|
35
39
|
PacketType.SEARCH_TOOL_START_V3,
|
|
36
40
|
PacketType.SEARCH_TOOL_QUERIES_DELTA,
|
|
@@ -38,7 +42,7 @@ export function MultiToolRenderer({
|
|
|
38
42
|
PacketType.SEARCH_TOOL_DELTA,
|
|
39
43
|
);
|
|
40
44
|
}
|
|
41
|
-
if (
|
|
45
|
+
if (activeShowTools.includes(PacketType.REASONING_START)) {
|
|
42
46
|
expandedShowTools.push(
|
|
43
47
|
PacketType.REASONING_DELTA,
|
|
44
48
|
PacketType.REASONING_DONE,
|
package/src/ChatBlock/schema.jsx
CHANGED
|
@@ -100,6 +100,7 @@ export function ChatBlockSchema({ assistants, data }) {
|
|
|
100
100
|
: []),
|
|
101
101
|
'enableFeedback',
|
|
102
102
|
...(data.enableFeedback ? ['feedbackReasons'] : []),
|
|
103
|
+
'stopButton',
|
|
103
104
|
'showTools',
|
|
104
105
|
'enableMatomoTracking',
|
|
105
106
|
'scrollToInput',
|
|
@@ -175,6 +176,18 @@ export function ChatBlockSchema({ assistants, data }) {
|
|
|
175
176
|
type: 'boolean',
|
|
176
177
|
default: true,
|
|
177
178
|
},
|
|
179
|
+
stopButton: {
|
|
180
|
+
title: 'Stop generation button',
|
|
181
|
+
choices: [
|
|
182
|
+
['disabled', 'Disabled'],
|
|
183
|
+
['input', 'Inline text input'],
|
|
184
|
+
['floating', 'Floating pill'],
|
|
185
|
+
['message_loader', 'Inline message loader'],
|
|
186
|
+
],
|
|
187
|
+
default: 'floating',
|
|
188
|
+
description:
|
|
189
|
+
'Choose where and how the stop generation button appears during response generation',
|
|
190
|
+
},
|
|
178
191
|
qualityCheck: {
|
|
179
192
|
title: 'Quality checks',
|
|
180
193
|
choices: [
|
|
@@ -388,7 +401,7 @@ range is from 0 to 100`,
|
|
|
388
401
|
['2', 'Onyx 2.x'],
|
|
389
402
|
['3', 'Onyx 3.x'],
|
|
390
403
|
],
|
|
391
|
-
default: '
|
|
404
|
+
default: '3',
|
|
392
405
|
description:
|
|
393
406
|
'Select which Onyx API version the backend is running. ' +
|
|
394
407
|
'Onyx 2.x uses the legacy send-message payload; ' +
|
|
@@ -195,6 +195,22 @@ function normaliseV3Chunk(raw: any): Packet | null {
|
|
|
195
195
|
} as Packet;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// Handle Error format (special case without ind/obj)
|
|
199
|
+
if ('error' in raw) {
|
|
200
|
+
return {
|
|
201
|
+
ind: -1,
|
|
202
|
+
obj: {
|
|
203
|
+
type: PacketType.ERROR,
|
|
204
|
+
error: raw.error,
|
|
205
|
+
},
|
|
206
|
+
} as Packet;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Already normalised or v2 format: { ind: number, obj: { type: string, ... } }
|
|
210
|
+
if ('ind' in raw && typeof raw.obj === 'object' && raw.obj !== null) {
|
|
211
|
+
return raw as Packet;
|
|
212
|
+
}
|
|
213
|
+
|
|
198
214
|
if (!raw.placement || typeof raw.obj !== 'object') return null;
|
|
199
215
|
|
|
200
216
|
const ind: number = raw.placement.turn_index ?? 0;
|
package/src/ChatBlock/style.less
CHANGED
|
@@ -123,6 +123,20 @@
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
@keyframes stopButtonPulse {
|
|
127
|
+
0% {
|
|
128
|
+
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.45);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
70% {
|
|
132
|
+
box-shadow: 0 0 0 7px rgba(0, 0, 0, 0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
100% {
|
|
136
|
+
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
126
140
|
@keyframes popIn {
|
|
127
141
|
0% {
|
|
128
142
|
opacity: 0;
|
|
@@ -1353,6 +1367,122 @@ mark {
|
|
|
1353
1367
|
color: @grey;
|
|
1354
1368
|
}
|
|
1355
1369
|
}
|
|
1370
|
+
|
|
1371
|
+
.ui.button.stop-btn {
|
|
1372
|
+
display: flex;
|
|
1373
|
+
width: 28px;
|
|
1374
|
+
height: 28px;
|
|
1375
|
+
align-items: center;
|
|
1376
|
+
justify-content: center;
|
|
1377
|
+
padding: 0;
|
|
1378
|
+
border-radius: 50%;
|
|
1379
|
+
margin: 0 0.5em 0 0.8em;
|
|
1380
|
+
animation: stopButtonPulse 1.8s cubic-bezier(0.66, 0, 0, 1) infinite;
|
|
1381
|
+
background: #000;
|
|
1382
|
+
cursor: pointer;
|
|
1383
|
+
transition:
|
|
1384
|
+
background-color 0.2s ease,
|
|
1385
|
+
transform 0.15s ease;
|
|
1386
|
+
|
|
1387
|
+
&:hover {
|
|
1388
|
+
background: #333;
|
|
1389
|
+
transform: scale(1.1);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
&:focus {
|
|
1393
|
+
background: #000;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
.btn-icon {
|
|
1397
|
+
display: flex;
|
|
1398
|
+
width: 100%;
|
|
1399
|
+
height: 100%;
|
|
1400
|
+
align-items: center;
|
|
1401
|
+
justify-content: center;
|
|
1402
|
+
|
|
1403
|
+
svg {
|
|
1404
|
+
width: 12px;
|
|
1405
|
+
height: 12px;
|
|
1406
|
+
color: #fff;
|
|
1407
|
+
fill: #fff;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
.chat-floating-stop {
|
|
1414
|
+
display: flex;
|
|
1415
|
+
justify-content: center;
|
|
1416
|
+
margin-bottom: 0.75em;
|
|
1417
|
+
animation: fadeInScale 0.2s ease;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
.message-loader-stop {
|
|
1421
|
+
display: inline-flex;
|
|
1422
|
+
align-items: center;
|
|
1423
|
+
margin-left: 20px;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.inline-message-stop {
|
|
1427
|
+
display: flex;
|
|
1428
|
+
margin-top: 12px;
|
|
1429
|
+
animation: fadeIn 0.2s ease;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.ui.button.floating-stop-btn,
|
|
1433
|
+
.ui.button.inline-message-stop-btn,
|
|
1434
|
+
.ui.button.message-loader-stop-btn {
|
|
1435
|
+
display: inline-flex;
|
|
1436
|
+
align-items: center;
|
|
1437
|
+
justify-content: center;
|
|
1438
|
+
padding: 6px 14px;
|
|
1439
|
+
border: 1px solid #d0d5dd;
|
|
1440
|
+
border-radius: 20px;
|
|
1441
|
+
background: #fff;
|
|
1442
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
|
1443
|
+
color: #344054;
|
|
1444
|
+
cursor: pointer;
|
|
1445
|
+
font-size: 13px;
|
|
1446
|
+
font-weight: 500;
|
|
1447
|
+
line-height: 1;
|
|
1448
|
+
transition: all 0.2s ease;
|
|
1449
|
+
|
|
1450
|
+
&:hover {
|
|
1451
|
+
border-color: #98a2b3;
|
|
1452
|
+
background: #f9fafb;
|
|
1453
|
+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
|
|
1454
|
+
color: #1d2939;
|
|
1455
|
+
transform: translateY(-1px);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
&:focus {
|
|
1459
|
+
background: #fff;
|
|
1460
|
+
color: #344054;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
.stop-btn-inner {
|
|
1464
|
+
display: inline-flex;
|
|
1465
|
+
align-items: center;
|
|
1466
|
+
gap: 8px;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.stop-icon-circle {
|
|
1470
|
+
display: flex;
|
|
1471
|
+
width: 18px;
|
|
1472
|
+
height: 18px;
|
|
1473
|
+
align-items: center;
|
|
1474
|
+
justify-content: center;
|
|
1475
|
+
border-radius: 50%;
|
|
1476
|
+
animation: stopButtonPulse 1.8s cubic-bezier(0.66, 0, 0, 1) infinite;
|
|
1477
|
+
background: #000;
|
|
1478
|
+
|
|
1479
|
+
svg {
|
|
1480
|
+
width: 8px;
|
|
1481
|
+
height: 8px;
|
|
1482
|
+
color: #fff;
|
|
1483
|
+
fill: #fff;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1356
1486
|
}
|
|
1357
1487
|
|
|
1358
1488
|
#quality-check-toggle[disabled] ~ label {
|
|
@@ -1757,7 +1887,7 @@ mark {
|
|
|
1757
1887
|
opacity 0.2s;
|
|
1758
1888
|
|
|
1759
1889
|
&.expanded {
|
|
1760
|
-
max-height:
|
|
1890
|
+
max-height: 3000px;
|
|
1761
1891
|
padding: 0.25rem 0.875rem;
|
|
1762
1892
|
opacity: 1;
|
|
1763
1893
|
}
|
|
@@ -171,6 +171,8 @@ export interface ChatMessageProps {
|
|
|
171
171
|
extraMarkdownComponents?: any;
|
|
172
172
|
/** Extra rehype plugins merged after the built-in ones. */
|
|
173
173
|
extraRehypePlugins?: any[];
|
|
174
|
+
stopButton?: 'disabled' | 'input' | 'floating' | 'message_loader';
|
|
175
|
+
onCancel?: () => void;
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
export interface Persona {
|