@eeacms/volto-eea-chatbot 2.0.1 → 2.0.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/.eslintrc.js +6 -6
- package/CHANGELOG.md +20 -0
- package/artifacts/ONYX_V3_INTEGRATION.md +34 -0
- package/jest-addon.config.js +2 -1
- package/package.json +1 -1
- package/src/ChatBlock/ChatBlockEdit.jsx +2 -1
- package/src/ChatBlock/chat/AIMessage.tsx +36 -16
- package/src/ChatBlock/chat/ChatMessage.tsx +1 -1
- package/src/ChatBlock/chat/ChatWindow.tsx +13 -11
- package/src/ChatBlock/chat/UserMessage.tsx +4 -4
- package/src/ChatBlock/components/AutoResizeTextarea.jsx +1 -1
- package/src/ChatBlock/components/ChatMessageFeedback.jsx +2 -2
- package/src/ChatBlock/components/EmptyState.jsx +1 -1
- package/src/ChatBlock/components/FeedbackModal.jsx +1 -1
- package/src/ChatBlock/components/HalloumiFeedback.jsx +2 -2
- package/src/ChatBlock/components/Source.jsx +2 -2
- package/src/ChatBlock/components/UserActionsToolbar.jsx +3 -3
- package/src/ChatBlock/components/WebResultIcon.tsx +2 -2
- package/src/ChatBlock/components/markdown/ClaimModal.jsx +3 -3
- package/src/ChatBlock/components/markdown/ClaimSegments.jsx +4 -4
- package/src/ChatBlock/components/markdown/{index.js → index.jsx} +1 -1
- package/src/ChatBlock/hooks/useChatController.ts +67 -14
- package/src/ChatBlock/hooks/useChatStreaming.ts +4 -4
- package/src/ChatBlock/hooks/useToolDisplayTiming.ts +2 -1
- package/src/ChatBlock/packets/MultiToolRenderer.tsx +86 -56
- package/src/ChatBlock/packets/RendererComponent.tsx +13 -5
- package/src/ChatBlock/packets/renderers/CustomToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/FetchToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/ImageToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +14 -9
- package/src/ChatBlock/packets/renderers/ReasoningRenderer.tsx +6 -5
- package/src/ChatBlock/packets/renderers/SearchToolRenderer.tsx +30 -21
- package/src/ChatBlock/{schema.js → schema.jsx} +13 -0
- package/src/ChatBlock/services/messageProcessor.ts +72 -17
- package/src/ChatBlock/services/packetUtils.ts +13 -3
- package/src/ChatBlock/services/streamingService.ts +155 -68
- package/src/ChatBlock/types/streamingModels.ts +47 -2
- package/src/ChatBlock/utils/citations.ts +1 -1
- package/src/halloumi/filtering.test.js +199 -1
- package/src/middleware.js +18 -1
- package/src/middleware.test.js +14 -0
- package/src/ChatBlock/tests/AIMessage.test.jsx +0 -95
- package/src/ChatBlock/tests/AutoResizeTextarea.test.jsx +0 -49
- package/src/ChatBlock/tests/BlinkingDot.test.jsx +0 -71
- package/src/ChatBlock/tests/ChatMessage.test.jsx +0 -75
- package/src/ChatBlock/tests/ChatMessageFeedback.test.jsx +0 -73
- package/src/ChatBlock/tests/Citation.test.jsx +0 -107
- package/src/ChatBlock/tests/ClaimModal.test.jsx +0 -136
- package/src/ChatBlock/tests/ClaimSegments.test.jsx +0 -206
- package/src/ChatBlock/tests/CustomToolRenderer.test.jsx +0 -241
- package/src/ChatBlock/tests/EmptyState.test.jsx +0 -137
- package/src/ChatBlock/tests/FeedbackModal.test.jsx +0 -138
- package/src/ChatBlock/tests/FetchToolRenderer.test.jsx +0 -161
- package/src/ChatBlock/tests/HalloumiFeedback.test.jsx +0 -94
- package/src/ChatBlock/tests/ImageToolRenderer.test.jsx +0 -178
- package/src/ChatBlock/tests/MessageTextRenderer.test.jsx +0 -227
- package/src/ChatBlock/tests/MultiToolRenderer.test.jsx +0 -134
- package/src/ChatBlock/tests/QualityCheckToggle.test.jsx +0 -105
- package/src/ChatBlock/tests/ReasoningRenderer.test.jsx +0 -163
- package/src/ChatBlock/tests/RelatedQuestions.test.jsx +0 -215
- package/src/ChatBlock/tests/RenderClaimView.test.jsx +0 -191
- package/src/ChatBlock/tests/RendererComponent.test.jsx +0 -139
- package/src/ChatBlock/tests/SearchToolRenderer.test.jsx +0 -295
- package/src/ChatBlock/tests/Source.test.jsx +0 -79
- package/src/ChatBlock/tests/SourceChip.test.jsx +0 -108
- package/src/ChatBlock/tests/Spinner.test.jsx +0 -18
- package/src/ChatBlock/tests/UserActionsToolbar.test.jsx +0 -135
- package/src/ChatBlock/tests/UserMessage.test.jsx +0 -83
- package/src/ChatBlock/tests/WebResultIcon.test.jsx +0 -61
- package/src/ChatBlock/tests/citations.test.js +0 -114
- package/src/ChatBlock/tests/index.test.js +0 -51
- package/src/ChatBlock/tests/messageProcessor.test.jsx +0 -438
- package/src/ChatBlock/tests/packetUtils.test.js +0 -158
- package/src/ChatBlock/tests/schema.test.js +0 -166
- package/src/ChatBlock/tests/streamingService.test.js +0 -467
- package/src/ChatBlock/tests/useChatController.test.jsx +0 -268
- package/src/ChatBlock/tests/useChatStreaming.test.jsx +0 -163
- package/src/ChatBlock/tests/useDeepCompareMemoize.test.js +0 -107
- package/src/ChatBlock/tests/useMarked.test.jsx +0 -107
- package/src/ChatBlock/tests/useQualityMarkers.test.jsx +0 -150
- package/src/ChatBlock/tests/useScrollonStream.test.jsx +0 -121
- package/src/ChatBlock/tests/useToolDisplayTiming.test.jsx +0 -151
- package/src/ChatBlock/tests/utils.test.jsx +0 -241
- package/src/ChatBlock/tests/withOnyxData.test.jsx +0 -81
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { Packet } from '
|
|
2
|
-
import type { Message } from '
|
|
1
|
+
import type { Packet } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
2
|
+
import type { Message } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
3
3
|
import React, { useState, useEffect, useMemo } from 'react';
|
|
4
4
|
import cx from 'classnames';
|
|
5
|
-
import { PacketType } from '
|
|
5
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
6
6
|
import { RendererComponent } from './RendererComponent';
|
|
7
|
-
import { useToolDisplayTiming } from '
|
|
8
|
-
import SVGIcon from '
|
|
9
|
-
import DoneIcon from '
|
|
10
|
-
import ChevronIcon from '
|
|
7
|
+
import { useToolDisplayTiming } from '@eeacms/volto-eea-chatbot/ChatBlock/hooks/useToolDisplayTiming';
|
|
8
|
+
import SVGIcon from '@eeacms/volto-eea-chatbot/ChatBlock/components/Icon';
|
|
9
|
+
import DoneIcon from '@eeacms/volto-eea-chatbot/icons/done.svg';
|
|
10
|
+
import ChevronIcon from '@eeacms/volto-eea-chatbot/icons/chevron.svg';
|
|
11
11
|
|
|
12
12
|
interface MultiToolRendererProps {
|
|
13
13
|
toolGroups: { ind: number; packets: Packet[] }[];
|
|
@@ -19,7 +19,7 @@ interface MultiToolRendererProps {
|
|
|
19
19
|
|
|
20
20
|
export function MultiToolRenderer({
|
|
21
21
|
toolGroups,
|
|
22
|
-
showTools = [PacketType.SEARCH_TOOL_START],
|
|
22
|
+
showTools = [PacketType.SEARCH_TOOL_START, PacketType.REASONING_START],
|
|
23
23
|
message,
|
|
24
24
|
libs,
|
|
25
25
|
onAllToolsDisplayed,
|
|
@@ -28,23 +28,33 @@ export function MultiToolRenderer({
|
|
|
28
28
|
const { isFinalMessageComing = false, isComplete = false } = message;
|
|
29
29
|
|
|
30
30
|
// Filter tool groups based on allowed tool types
|
|
31
|
-
const filteredToolGroups = useMemo(
|
|
32
|
-
()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
const filteredToolGroups = useMemo(() => {
|
|
32
|
+
const expandedShowTools = [...(showTools || [])];
|
|
33
|
+
if (showTools?.includes(PacketType.SEARCH_TOOL_START)) {
|
|
34
|
+
expandedShowTools.push(
|
|
35
|
+
PacketType.SEARCH_TOOL_START_V3,
|
|
36
|
+
PacketType.SEARCH_TOOL_QUERIES_DELTA,
|
|
37
|
+
PacketType.SEARCH_TOOL_DOCUMENTS_DELTA,
|
|
38
|
+
PacketType.SEARCH_TOOL_DELTA,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (showTools?.includes(PacketType.REASONING_START)) {
|
|
42
|
+
expandedShowTools.push(
|
|
43
|
+
PacketType.REASONING_DELTA,
|
|
44
|
+
PacketType.REASONING_DONE,
|
|
45
|
+
PacketType.REASONING_END as any,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return toolGroups.filter((group) =>
|
|
49
|
+
group.packets?.some((packet) =>
|
|
50
|
+
expandedShowTools.includes(packet.obj.type as PacketType),
|
|
38
51
|
),
|
|
39
|
-
|
|
40
|
-
);
|
|
52
|
+
);
|
|
53
|
+
}, [toolGroups, showTools]);
|
|
41
54
|
|
|
42
55
|
// Manage tool display timing
|
|
43
|
-
const { allToolsDisplayed, handleToolComplete } =
|
|
44
|
-
filteredToolGroups,
|
|
45
|
-
isFinalMessageComing,
|
|
46
|
-
isComplete,
|
|
47
|
-
);
|
|
56
|
+
const { allToolsDisplayed, handleToolComplete, toolStates } =
|
|
57
|
+
useToolDisplayTiming(filteredToolGroups, isFinalMessageComing, isComplete);
|
|
48
58
|
|
|
49
59
|
// Notify parent when all tools are displayed
|
|
50
60
|
useEffect(() => {
|
|
@@ -65,27 +75,29 @@ export function MultiToolRenderer({
|
|
|
65
75
|
|
|
66
76
|
if (filteredToolGroups.length === 0) return null;
|
|
67
77
|
|
|
68
|
-
const
|
|
78
|
+
const isOverallStreaming = !allToolsDisplayed;
|
|
69
79
|
|
|
70
80
|
const count = filteredToolGroups.length;
|
|
71
81
|
|
|
72
|
-
const ariaLabel = `${count} ${
|
|
73
|
-
|
|
74
|
-
}, ${isExpanded ? 'expanded' : 'collapsed'}`;
|
|
82
|
+
const ariaLabel = `${count} ${
|
|
83
|
+
isOverallStreaming ? 'processing' : 'completed'
|
|
84
|
+
} ${count === 1 ? 'step' : 'steps'}, ${isExpanded ? 'expanded' : 'collapsed'}`;
|
|
75
85
|
|
|
76
86
|
return (
|
|
77
87
|
<div
|
|
78
88
|
className={cx('multi-tool-renderer', {
|
|
79
|
-
streaming:
|
|
80
|
-
complete: !
|
|
89
|
+
streaming: isOverallStreaming,
|
|
90
|
+
complete: !isOverallStreaming,
|
|
81
91
|
})}
|
|
82
92
|
>
|
|
83
93
|
{/* Header */}
|
|
84
|
-
<div
|
|
94
|
+
<div
|
|
95
|
+
className={cx({ 'tools-container collapsed-view': isOverallStreaming })}
|
|
96
|
+
>
|
|
85
97
|
<div
|
|
86
98
|
className={cx({
|
|
87
|
-
'tools-collapsed-header':
|
|
88
|
-
'tools-summary-header': !
|
|
99
|
+
'tools-collapsed-header': isOverallStreaming,
|
|
100
|
+
'tools-summary-header': !isOverallStreaming,
|
|
89
101
|
})}
|
|
90
102
|
onClick={toggleExpanded}
|
|
91
103
|
role="button"
|
|
@@ -110,21 +122,25 @@ export function MultiToolRenderer({
|
|
|
110
122
|
{/* Tools List */}
|
|
111
123
|
<div
|
|
112
124
|
className={cx({
|
|
113
|
-
'tools-collapsed-list':
|
|
114
|
-
'tools-expanded-content': !
|
|
115
|
-
expanded: isExpanded &&
|
|
116
|
-
visible: isExpanded && !
|
|
125
|
+
'tools-collapsed-list': isOverallStreaming,
|
|
126
|
+
'tools-expanded-content': !isOverallStreaming,
|
|
127
|
+
expanded: isExpanded && isOverallStreaming,
|
|
128
|
+
visible: isExpanded && !isOverallStreaming,
|
|
117
129
|
})}
|
|
118
130
|
>
|
|
119
|
-
<div className={cx({ 'tools-list':
|
|
131
|
+
<div className={cx({ 'tools-list': isOverallStreaming })}>
|
|
120
132
|
<div>
|
|
121
133
|
{filteredToolGroups.map((toolGroup, index) => {
|
|
122
134
|
const isLastItem = index === filteredToolGroups.length - 1;
|
|
135
|
+
const toolState = toolStates.get(toolGroup.ind);
|
|
136
|
+
const isToolCompleted = toolState?.isCompleted;
|
|
123
137
|
|
|
124
138
|
return (
|
|
125
139
|
<div
|
|
126
140
|
key={toolGroup.ind}
|
|
127
|
-
className={cx({
|
|
141
|
+
className={cx({
|
|
142
|
+
'tool-collapsed-wrapper': isOverallStreaming,
|
|
143
|
+
})}
|
|
128
144
|
>
|
|
129
145
|
<RendererComponent
|
|
130
146
|
packets={toolGroup.packets}
|
|
@@ -144,32 +160,46 @@ export function MultiToolRenderer({
|
|
|
144
160
|
) : (
|
|
145
161
|
<span
|
|
146
162
|
className={cx({
|
|
147
|
-
'tool-icon-dot':
|
|
148
|
-
'tool-icon-default': !
|
|
163
|
+
'tool-icon-dot': isOverallStreaming,
|
|
164
|
+
'tool-icon-default': !isOverallStreaming,
|
|
149
165
|
})}
|
|
150
166
|
/>
|
|
151
167
|
);
|
|
152
168
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<span className="tool-collapsed-status">
|
|
166
|
-
{status}
|
|
167
|
-
</span>
|
|
168
|
-
</div>
|
|
169
|
+
// If tool is not completed and we are overall streaming, show collapsed view
|
|
170
|
+
// EXCEPT for reasoning and search which we want to see while they stream/progress
|
|
171
|
+
if (isOverallStreaming && !isToolCompleted) {
|
|
172
|
+
const isDetailedTool = toolGroup.packets.some(
|
|
173
|
+
(p) =>
|
|
174
|
+
p.obj.type === PacketType.REASONING_START ||
|
|
175
|
+
p.obj.type === PacketType.REASONING_DELTA ||
|
|
176
|
+
p.obj.type === PacketType.SEARCH_TOOL_START ||
|
|
177
|
+
p.obj.type === PacketType.SEARCH_TOOL_START_V3 ||
|
|
178
|
+
p.obj.type === PacketType.SEARCH_TOOL_DELTA ||
|
|
179
|
+
p.obj.type === PacketType.SEARCH_TOOL_QUERIES_DELTA ||
|
|
180
|
+
p.obj.type === PacketType.SEARCH_TOOL_DOCUMENTS_DELTA,
|
|
169
181
|
);
|
|
182
|
+
|
|
183
|
+
if (!isDetailedTool || !content) {
|
|
184
|
+
return (
|
|
185
|
+
<div
|
|
186
|
+
className={cx('tool-item-collapsed', {
|
|
187
|
+
active: isLastItem,
|
|
188
|
+
completed: isToolCompleted,
|
|
189
|
+
})}
|
|
190
|
+
>
|
|
191
|
+
<div className="tool-collapsed-icon">
|
|
192
|
+
{finalIcon}
|
|
193
|
+
</div>
|
|
194
|
+
<span className="tool-collapsed-status">
|
|
195
|
+
{status}
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
170
200
|
}
|
|
171
201
|
|
|
172
|
-
//
|
|
202
|
+
// Expanded view (full content) - used for completed tools or when overall complete
|
|
173
203
|
return (
|
|
174
204
|
<div className="tool-item-expanded">
|
|
175
205
|
<div className="tool-connector-line" />
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { Packet } from '
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import type { Packet } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
2
|
+
import type {
|
|
3
|
+
RendererResult,
|
|
4
|
+
Message,
|
|
5
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
6
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
4
7
|
import {
|
|
5
8
|
MessageTextRenderer,
|
|
6
9
|
SearchToolRenderer,
|
|
@@ -23,7 +26,10 @@ function isChatPacket(packet: Packet): boolean {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
function isSearchToolPacket(packet: Packet): boolean {
|
|
26
|
-
return
|
|
29
|
+
return (
|
|
30
|
+
packet.obj.type === PacketType.SEARCH_TOOL_START ||
|
|
31
|
+
packet.obj.type === PacketType.SEARCH_TOOL_START_V3
|
|
32
|
+
);
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
function isImageToolPacket(packet: Packet): boolean {
|
|
@@ -41,7 +47,9 @@ function isFetchToolPacket(packet: Packet): boolean {
|
|
|
41
47
|
function isReasoningPacket(packet: Packet): boolean {
|
|
42
48
|
return (
|
|
43
49
|
packet.obj.type === PacketType.REASONING_START ||
|
|
44
|
-
packet.obj.type === PacketType.REASONING_DELTA
|
|
50
|
+
packet.obj.type === PacketType.REASONING_DELTA ||
|
|
51
|
+
packet.obj.type === PacketType.REASONING_DONE ||
|
|
52
|
+
packet.obj.type === PacketType.REASONING_END
|
|
45
53
|
);
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -2,10 +2,10 @@ import type {
|
|
|
2
2
|
CustomToolPacket,
|
|
3
3
|
CustomToolStart,
|
|
4
4
|
CustomToolDelta,
|
|
5
|
-
} from '
|
|
6
|
-
import type { MessageRenderer } from '
|
|
5
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
6
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
7
7
|
import { useEffect } from 'react';
|
|
8
|
-
import { PacketType } from '
|
|
8
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
9
9
|
|
|
10
10
|
export const CustomToolRenderer: MessageRenderer<CustomToolPacket> = ({
|
|
11
11
|
packets,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
FetchToolPacket,
|
|
3
3
|
FetchToolStart,
|
|
4
|
-
} from '
|
|
5
|
-
import type { MessageRenderer } from '
|
|
4
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
5
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
|
-
import { PacketType } from '
|
|
7
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
8
8
|
|
|
9
9
|
export const FetchToolRenderer: MessageRenderer<FetchToolPacket> = ({
|
|
10
10
|
packets,
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ImageGenerationToolPacket,
|
|
4
4
|
ImageGenerationToolDelta,
|
|
5
5
|
GeneratedImage,
|
|
6
|
-
} from '
|
|
7
|
-
import type { MessageRenderer } from '
|
|
8
|
-
import { PacketType } from '
|
|
6
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
7
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
8
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
9
9
|
|
|
10
10
|
export const ImageToolRenderer: MessageRenderer<ImageGenerationToolPacket> = ({
|
|
11
11
|
packets,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { ChatPacket } from '
|
|
2
|
-
import type { MessageRenderer } from '
|
|
1
|
+
import type { ChatPacket } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
2
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
3
3
|
import { useEffect, useMemo, useState } from 'react';
|
|
4
4
|
import loadable from '@loadable/component';
|
|
5
|
-
import { components } from '
|
|
6
|
-
import { isFinalAnswerComplete } from '
|
|
7
|
-
import { PacketType } from '
|
|
8
|
-
import { BlinkingDot } from '
|
|
5
|
+
import { components } from '@eeacms/volto-eea-chatbot/ChatBlock/components/markdown';
|
|
6
|
+
import { isFinalAnswerComplete } from '@eeacms/volto-eea-chatbot/ChatBlock/services/packetUtils';
|
|
7
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
8
|
+
import { BlinkingDot } from '@eeacms/volto-eea-chatbot/ChatBlock/components/BlinkingDot';
|
|
9
9
|
|
|
10
10
|
const Markdown: any = loadable(() => import('react-markdown'));
|
|
11
11
|
|
|
@@ -90,7 +90,11 @@ export const MessageTextRenderer: MessageRenderer<ChatPacket> = ({
|
|
|
90
90
|
// If we're far behind, catch up faster
|
|
91
91
|
const increment =
|
|
92
92
|
remaining > CATCH_UP_THRESHOLD ? PACKETS_PER_TICK : 1;
|
|
93
|
-
|
|
93
|
+
const next = Math.min(prev + increment, packets.length);
|
|
94
|
+
if (isStreamFinished && next === packets.length) {
|
|
95
|
+
console.log(`[MessageTextRenderer] Animation finished: ${next}/${packets.length}`);
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
94
98
|
});
|
|
95
99
|
}, PACKET_DELAY_MS);
|
|
96
100
|
|
|
@@ -104,8 +108,8 @@ export const MessageTextRenderer: MessageRenderer<ChatPacket> = ({
|
|
|
104
108
|
const resetCount = isStreamFinished
|
|
105
109
|
? packets.length // Show all if stream is finished
|
|
106
110
|
: packets.length > 0
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
? 1
|
|
112
|
+
: 0;
|
|
109
113
|
setDisplayedPacketCount(resetCount);
|
|
110
114
|
}
|
|
111
115
|
}, [animate, packets.length, displayedPacketCount, isStreamFinished]);
|
|
@@ -121,6 +125,7 @@ export const MessageTextRenderer: MessageRenderer<ChatPacket> = ({
|
|
|
121
125
|
) {
|
|
122
126
|
return;
|
|
123
127
|
}
|
|
128
|
+
console.log(`[MessageTextRenderer] Calling onComplete: packets=${packets.length}, finished=${isStreamFinished}`);
|
|
124
129
|
onComplete();
|
|
125
130
|
}
|
|
126
131
|
}, [
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ReasoningPacket,
|
|
3
3
|
ReasoningDelta,
|
|
4
|
-
} from '
|
|
5
|
-
import type { MessageRenderer } from '
|
|
4
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
5
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
6
6
|
import { useEffect, useState, useRef, useMemo } from 'react';
|
|
7
7
|
import loadable from '@loadable/component';
|
|
8
|
-
import { PacketType } from '
|
|
9
|
-
import { components } from '
|
|
10
|
-
import { addCitations } from '
|
|
8
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
9
|
+
import { components } from '@eeacms/volto-eea-chatbot/ChatBlock/components/markdown';
|
|
10
|
+
import { addCitations } from '@eeacms/volto-eea-chatbot/ChatBlock/utils/citations';
|
|
11
11
|
|
|
12
12
|
const Markdown: any = loadable(() => import('react-markdown'));
|
|
13
13
|
|
|
@@ -21,6 +21,7 @@ function constructCurrentReasoningState(packets: ReasoningPacket[]) {
|
|
|
21
21
|
const hasEnd = packets.some(
|
|
22
22
|
(p) =>
|
|
23
23
|
p.obj.type === PacketType.SECTION_END ||
|
|
24
|
+
p.obj.type === PacketType.REASONING_DONE ||
|
|
24
25
|
// Support either convention for reasoning completion
|
|
25
26
|
(p.obj as any).type === PacketType.REASONING_END,
|
|
26
27
|
);
|
|
@@ -4,17 +4,17 @@ import type {
|
|
|
4
4
|
SearchToolDelta,
|
|
5
5
|
SectionEnd,
|
|
6
6
|
OnyxDocument,
|
|
7
|
-
} from '
|
|
8
|
-
import type { MessageRenderer } from '
|
|
7
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
8
|
+
import type { MessageRenderer } from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
9
9
|
import { useEffect, useState, useRef, useMemo } from 'react';
|
|
10
|
-
import { PacketType } from '
|
|
11
|
-
import { SourceChip } from '
|
|
12
|
-
import { BlinkingDot } from '
|
|
13
|
-
import SVGIcon from '
|
|
14
|
-
import { WebResultIcon } from '
|
|
15
|
-
import SearchIcon from '
|
|
16
|
-
import GlobeIcon from '
|
|
17
|
-
import FileIcon from '
|
|
10
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
11
|
+
import { SourceChip } from '@eeacms/volto-eea-chatbot/ChatBlock/components/SourceChip';
|
|
12
|
+
import { BlinkingDot } from '@eeacms/volto-eea-chatbot/ChatBlock/components/BlinkingDot';
|
|
13
|
+
import SVGIcon from '@eeacms/volto-eea-chatbot/ChatBlock/components/Icon';
|
|
14
|
+
import { WebResultIcon } from '@eeacms/volto-eea-chatbot/ChatBlock/components/WebResultIcon';
|
|
15
|
+
import SearchIcon from '@eeacms/volto-eea-chatbot/icons/search.svg';
|
|
16
|
+
import GlobeIcon from '@eeacms/volto-eea-chatbot/icons/globe.svg';
|
|
17
|
+
import FileIcon from '@eeacms/volto-eea-chatbot/icons/file.svg';
|
|
18
18
|
|
|
19
19
|
const INITIAL_RESULTS_TO_SHOW = 3;
|
|
20
20
|
const RESULTS_PER_EXPANSION = 10;
|
|
@@ -55,26 +55,33 @@ const constructCurrentSearchState = (
|
|
|
55
55
|
isInternetSearch: boolean;
|
|
56
56
|
} => {
|
|
57
57
|
const searchStart = packets.find(
|
|
58
|
-
(packet) =>
|
|
58
|
+
(packet) =>
|
|
59
|
+
packet.obj.type === PacketType.SEARCH_TOOL_START ||
|
|
60
|
+
packet.obj.type === PacketType.SEARCH_TOOL_START_V3,
|
|
59
61
|
)?.obj as SearchToolStart | null;
|
|
60
62
|
|
|
61
63
|
const searchDeltas = packets
|
|
62
|
-
.filter(
|
|
63
|
-
|
|
64
|
+
.filter(
|
|
65
|
+
(packet) =>
|
|
66
|
+
packet.obj.type === PacketType.SEARCH_TOOL_DELTA ||
|
|
67
|
+
packet.obj.type === PacketType.SEARCH_TOOL_QUERIES_DELTA ||
|
|
68
|
+
packet.obj.type === PacketType.SEARCH_TOOL_DOCUMENTS_DELTA,
|
|
69
|
+
)
|
|
70
|
+
.map((packet) => packet.obj);
|
|
64
71
|
|
|
65
72
|
const searchEnd = packets.find(
|
|
66
73
|
(packet) => packet.obj.type === PacketType.SECTION_END,
|
|
67
74
|
)?.obj as SectionEnd | null;
|
|
68
75
|
|
|
69
|
-
// Extract queries from
|
|
76
|
+
// Extract queries from various delta packets
|
|
70
77
|
const queries = searchDeltas
|
|
71
|
-
.flatMap((delta) => delta?.queries || [])
|
|
78
|
+
.flatMap((delta: any) => delta?.queries || [])
|
|
72
79
|
.filter((query, index, arr) => arr.indexOf(query) === index); // Remove duplicates
|
|
73
80
|
|
|
74
81
|
const seenDocIds = new Set<string>();
|
|
75
82
|
const results = searchDeltas
|
|
76
|
-
.flatMap((delta) => delta?.documents || [])
|
|
77
|
-
.filter((doc) => {
|
|
83
|
+
.flatMap((delta: any) => delta?.documents || [])
|
|
84
|
+
.filter((doc: OnyxDocument) => {
|
|
78
85
|
if (!doc || !doc.document_id) return false;
|
|
79
86
|
if (seenDocIds.has(doc.document_id)) return false;
|
|
80
87
|
seenDocIds.add(doc.document_id);
|
|
@@ -202,8 +209,8 @@ export const SearchToolRenderer: MessageRenderer<SearchToolPacket> = ({
|
|
|
202
209
|
<SVGIcon name={isInternetSearch ? GlobeIcon : SearchIcon} size={size} />
|
|
203
210
|
);
|
|
204
211
|
|
|
205
|
-
// Don't render anything if search hasn't started
|
|
206
|
-
if (queries.length === 0) {
|
|
212
|
+
// Don't render anything if search hasn't started or has no data yet
|
|
213
|
+
if (queries.length === 0 && results.length === 0) {
|
|
207
214
|
return children({
|
|
208
215
|
icon: IconComponent,
|
|
209
216
|
status: status,
|
|
@@ -218,7 +225,9 @@ export const SearchToolRenderer: MessageRenderer<SearchToolPacket> = ({
|
|
|
218
225
|
<div className="search-tool-renderer">
|
|
219
226
|
<div className="queries-section">
|
|
220
227
|
<div className="queries-header">
|
|
221
|
-
<strong>
|
|
228
|
+
<strong>
|
|
229
|
+
{isInternetSearch ? 'Web Queries' : 'Internal Search Queries'}
|
|
230
|
+
</strong>
|
|
222
231
|
</div>
|
|
223
232
|
<div className="queries-list">
|
|
224
233
|
{queries.slice(0, queriesToShow).map((query, index) => (
|
|
@@ -262,7 +271,7 @@ export const SearchToolRenderer: MessageRenderer<SearchToolPacket> = ({
|
|
|
262
271
|
|
|
263
272
|
<div className="results-section">
|
|
264
273
|
<div className="results-header">
|
|
265
|
-
<strong>{isInternetSearch ? 'Results' : 'Documents'}</strong>
|
|
274
|
+
<strong>{isInternetSearch ? 'Web Results' : 'Documents'}</strong>
|
|
266
275
|
</div>
|
|
267
276
|
|
|
268
277
|
<div className="results-list">
|
|
@@ -104,6 +104,7 @@ export function ChatBlockSchema({ assistants, data }) {
|
|
|
104
104
|
'scrollToInput',
|
|
105
105
|
'showAssistantTitle',
|
|
106
106
|
'showAssistantDescription',
|
|
107
|
+
'onyxVersion',
|
|
107
108
|
],
|
|
108
109
|
},
|
|
109
110
|
],
|
|
@@ -368,6 +369,18 @@ range is from 0 to 100`,
|
|
|
368
369
|
type: 'boolean',
|
|
369
370
|
default: true,
|
|
370
371
|
},
|
|
372
|
+
onyxVersion: {
|
|
373
|
+
title: 'Onyx API version',
|
|
374
|
+
choices: [
|
|
375
|
+
['2', 'Onyx 2.x'],
|
|
376
|
+
['3', 'Onyx 3.x'],
|
|
377
|
+
],
|
|
378
|
+
default: '2',
|
|
379
|
+
description:
|
|
380
|
+
'Select which Onyx API version the backend is running. ' +
|
|
381
|
+
'Onyx 2.x uses the legacy send-message payload; ' +
|
|
382
|
+
'Onyx 3.x uses the new send-message payload with placement-based streaming.',
|
|
383
|
+
},
|
|
371
384
|
showAssistantPrompts: {
|
|
372
385
|
title: 'Show predefined prompts',
|
|
373
386
|
type: 'boolean',
|
|
@@ -5,14 +5,17 @@ import type {
|
|
|
5
5
|
OnyxDocument,
|
|
6
6
|
Packet,
|
|
7
7
|
StreamingCitation,
|
|
8
|
-
} from '
|
|
9
|
-
import type {
|
|
8
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
9
|
+
import type {
|
|
10
|
+
Message,
|
|
11
|
+
ToolCallMetadata,
|
|
12
|
+
} from '@eeacms/volto-eea-chatbot/ChatBlock/types/interfaces';
|
|
10
13
|
import {
|
|
11
14
|
getSynteticPacket,
|
|
12
15
|
isToolPacket,
|
|
13
16
|
isDisplayPacket,
|
|
14
17
|
} from './packetUtils';
|
|
15
|
-
import { PacketType } from '
|
|
18
|
+
import { PacketType } from '@eeacms/volto-eea-chatbot/ChatBlock/types/streamingModels';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Process streaming packets into a message object
|
|
@@ -108,17 +111,26 @@ export class MessageProcessor {
|
|
|
108
111
|
this.indicesStarted.push(packet.ind);
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
//
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.indicesStarted.shift()
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
// Handle SECTION_END (v2/v3 compatible)
|
|
115
|
+
if (packet.obj.type === PacketType.SECTION_END) {
|
|
116
|
+
let targetInd = packet.ind;
|
|
117
|
+
|
|
118
|
+
// If ind is -1 (common in v2), use synthetic logic
|
|
119
|
+
if (targetInd === -1 && this.indicesStarted.length > 0) {
|
|
120
|
+
targetInd = this.indicesStarted.shift()!;
|
|
121
|
+
} else if (targetInd !== -1) {
|
|
122
|
+
// If ind is provided (v3), remove it from indicesStarted if present
|
|
123
|
+
const startIdx = this.indicesStarted.indexOf(targetInd);
|
|
124
|
+
if (startIdx > -1) {
|
|
125
|
+
this.indicesStarted.splice(startIdx, 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (targetInd === -1) {
|
|
130
|
+
return; // Skip if we can't map it to a turn
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
processedPacket = getSynteticPacket(targetInd, PacketType.SECTION_END);
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
const { ind } = processedPacket;
|
|
@@ -126,6 +138,10 @@ export class MessageProcessor {
|
|
|
126
138
|
// Store processed packet for later aggregation
|
|
127
139
|
this.packets.push(processedPacket);
|
|
128
140
|
|
|
141
|
+
if (processedPacket.obj.type === PacketType.MESSAGE_START || processedPacket.obj.type === PacketType.SECTION_END) {
|
|
142
|
+
console.log(`[MessageProcessor] Processed ${processedPacket.obj.type} for ind=${processedPacket.ind}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
// Group packets by index for later processing
|
|
130
146
|
if (!this.groupedPackets.has(ind)) {
|
|
131
147
|
this.groupedPackets.set(ind, []);
|
|
@@ -171,6 +187,7 @@ export class MessageProcessor {
|
|
|
171
187
|
PacketType.MESSAGE_START,
|
|
172
188
|
PacketType.SEARCH_TOOL_DELTA,
|
|
173
189
|
PacketType.FETCH_TOOL_START,
|
|
190
|
+
PacketType.SEARCH_TOOL_DOCUMENTS_DELTA,
|
|
174
191
|
].includes(packet.obj.type)
|
|
175
192
|
) {
|
|
176
193
|
return;
|
|
@@ -178,7 +195,8 @@ export class MessageProcessor {
|
|
|
178
195
|
let newDocuments = false;
|
|
179
196
|
const data = packet.obj as any;
|
|
180
197
|
const documents = data.final_documents || data.documents;
|
|
181
|
-
|
|
198
|
+
|
|
199
|
+
if (documents && Array.isArray(documents)) {
|
|
182
200
|
documents.forEach((doc: OnyxDocument) => {
|
|
183
201
|
const docId = doc.document_id;
|
|
184
202
|
if (docId && !this.documentMap.has(docId)) {
|
|
@@ -187,8 +205,23 @@ export class MessageProcessor {
|
|
|
187
205
|
}
|
|
188
206
|
});
|
|
189
207
|
}
|
|
208
|
+
|
|
190
209
|
if (newDocuments) {
|
|
191
210
|
this._documents = Array.from(this.documentMap.values());
|
|
211
|
+
|
|
212
|
+
// If we have final_documents and no citations yet, create a fallback mapping
|
|
213
|
+
// This ensures the Sources tab shows up in v3 when citation_info is missing
|
|
214
|
+
if (
|
|
215
|
+
packet.obj.type === PacketType.MESSAGE_START &&
|
|
216
|
+
data.final_documents &&
|
|
217
|
+
this._citations.size === 0
|
|
218
|
+
) {
|
|
219
|
+
data.final_documents.forEach((doc: OnyxDocument, index: number) => {
|
|
220
|
+
if (doc.document_id) {
|
|
221
|
+
this._citations.set(index + 1, doc.document_id);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
192
225
|
}
|
|
193
226
|
}
|
|
194
227
|
|
|
@@ -197,6 +230,16 @@ export class MessageProcessor {
|
|
|
197
230
|
* Updates the internal citation collection and notifies when new citations are added
|
|
198
231
|
*/
|
|
199
232
|
private processCitations(packet: Packet) {
|
|
233
|
+
if (packet.obj.type === PacketType.CITATION_INFO) {
|
|
234
|
+
const citationInfo = packet.obj as any;
|
|
235
|
+
if (citationInfo.citation_number && citationInfo.document_id) {
|
|
236
|
+
this._citations.set(
|
|
237
|
+
citationInfo.citation_number,
|
|
238
|
+
citationInfo.document_id,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
200
243
|
if (packet.obj.type !== PacketType.CITATION_DELTA) {
|
|
201
244
|
return;
|
|
202
245
|
}
|
|
@@ -234,6 +277,13 @@ export class MessageProcessor {
|
|
|
234
277
|
*/
|
|
235
278
|
private processStreamEnd(packet: Packet) {
|
|
236
279
|
if ([PacketType.STOP, PacketType.ERROR].includes(packet.obj.type)) {
|
|
280
|
+
// Close any remaining open sections (especially the last one)
|
|
281
|
+
while (this.indicesStarted.length > 0) {
|
|
282
|
+
const ind = this.indicesStarted.shift()!;
|
|
283
|
+
console.log(`[MessageProcessor] Stream ended. Synthesizing section_end for ind=${ind}`);
|
|
284
|
+
const synthetic = getSynteticPacket(ind, PacketType.SECTION_END);
|
|
285
|
+
this.processPacket(synthetic);
|
|
286
|
+
}
|
|
237
287
|
this._isComplete = true;
|
|
238
288
|
}
|
|
239
289
|
}
|
|
@@ -245,7 +295,9 @@ export class MessageProcessor {
|
|
|
245
295
|
private extractToolCall(packets: Packet[]): ToolCallMetadata | null {
|
|
246
296
|
// Look for search tool packets
|
|
247
297
|
const searchToolStart = packets.find(
|
|
248
|
-
(p) =>
|
|
298
|
+
(p) =>
|
|
299
|
+
p.obj.type === PacketType.SEARCH_TOOL_START ||
|
|
300
|
+
p.obj.type === PacketType.SEARCH_TOOL_START_V3,
|
|
249
301
|
);
|
|
250
302
|
|
|
251
303
|
if (!searchToolStart) {
|
|
@@ -257,7 +309,10 @@ export class MessageProcessor {
|
|
|
257
309
|
const processedDocs: Record<string, any> = {};
|
|
258
310
|
|
|
259
311
|
for (const packet of packets) {
|
|
260
|
-
if (
|
|
312
|
+
if (
|
|
313
|
+
packet.obj.type === PacketType.SEARCH_TOOL_DELTA ||
|
|
314
|
+
packet.obj.type === PacketType.SEARCH_TOOL_DOCUMENTS_DELTA
|
|
315
|
+
) {
|
|
261
316
|
const delta = packet.obj as any;
|
|
262
317
|
if (delta.documents && Array.isArray(delta.documents)) {
|
|
263
318
|
delta.documents.forEach((doc: any) => {
|