@eeacms/volto-eea-chatbot 1.0.9
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/.coverage.babel.config.js +9 -0
- package/.eslintrc.js +68 -0
- package/.husky/pre-commit +2 -0
- package/.release-it.json +17 -0
- package/AGENTS.md +89 -0
- package/CHANGELOG.md +770 -0
- package/DEVELOP.md +124 -0
- package/LICENSE.md +9 -0
- package/README.md +170 -0
- package/RELEASE.md +74 -0
- package/TESTING.md +5 -0
- package/babel.config.js +17 -0
- package/bootstrap +41 -0
- package/cypress.config.js +27 -0
- package/docker-compose.yml +32 -0
- package/jest-addon.config.js +465 -0
- package/jest.setup.js +65 -0
- package/locales/de/LC_MESSAGES/volto.po +14 -0
- package/locales/en/LC_MESSAGES/volto.po +14 -0
- package/locales/it/LC_MESSAGES/volto.po +14 -0
- package/locales/ro/LC_MESSAGES/volto.po +14 -0
- package/locales/volto.pot +16 -0
- package/package.json +98 -0
- package/razzle.extend.js +40 -0
- package/src/ChatBlock/ChatBlockEdit.jsx +46 -0
- package/src/ChatBlock/ChatBlockView.jsx +21 -0
- package/src/ChatBlock/chat/AIMessage.tsx +566 -0
- package/src/ChatBlock/chat/ChatMessage.tsx +35 -0
- package/src/ChatBlock/chat/ChatWindow.tsx +288 -0
- package/src/ChatBlock/chat/UserMessage.tsx +27 -0
- package/src/ChatBlock/chat/index.ts +4 -0
- package/src/ChatBlock/components/AutoResizeTextarea.jsx +67 -0
- package/src/ChatBlock/components/BlinkingDot.tsx +3 -0
- package/src/ChatBlock/components/ChatMessageFeedback.jsx +77 -0
- package/src/ChatBlock/components/EmptyState.jsx +70 -0
- package/src/ChatBlock/components/FeedbackModal.jsx +125 -0
- package/src/ChatBlock/components/HalloumiFeedback.jsx +126 -0
- package/src/ChatBlock/components/Icon.tsx +35 -0
- package/src/ChatBlock/components/QualityCheckToggle.jsx +26 -0
- package/src/ChatBlock/components/RelatedQuestions.jsx +59 -0
- package/src/ChatBlock/components/Source.jsx +93 -0
- package/src/ChatBlock/components/SourceChip.tsx +55 -0
- package/src/ChatBlock/components/Spinner.jsx +3 -0
- package/src/ChatBlock/components/UserActionsToolbar.jsx +44 -0
- package/src/ChatBlock/components/WebResultIcon.tsx +42 -0
- package/src/ChatBlock/components/markdown/Citation.jsx +70 -0
- package/src/ChatBlock/components/markdown/ClaimModal.jsx +98 -0
- package/src/ChatBlock/components/markdown/ClaimSegments.jsx +172 -0
- package/src/ChatBlock/components/markdown/RenderClaimView.jsx +96 -0
- package/src/ChatBlock/components/markdown/colors.js +29 -0
- package/src/ChatBlock/components/markdown/colors.less +52 -0
- package/src/ChatBlock/components/markdown/colors.test.js +69 -0
- package/src/ChatBlock/components/markdown/index.js +115 -0
- package/src/ChatBlock/fonts/DejaVuSans.ttf +0 -0
- package/src/ChatBlock/hocs/withOnyxData.jsx +46 -0
- package/src/ChatBlock/hooks/index.ts +7 -0
- package/src/ChatBlock/hooks/useChatController.ts +333 -0
- package/src/ChatBlock/hooks/useChatStreaming.ts +82 -0
- package/src/ChatBlock/hooks/useDeepCompareMemoize.js +17 -0
- package/src/ChatBlock/hooks/useMarked.js +44 -0
- package/src/ChatBlock/hooks/useQualityMarkers.js +119 -0
- package/src/ChatBlock/hooks/useScrollonStream.ts +131 -0
- package/src/ChatBlock/hooks/useToolDisplayTiming.ts +80 -0
- package/src/ChatBlock/index.js +32 -0
- package/src/ChatBlock/packets/MultiToolRenderer.tsx +235 -0
- package/src/ChatBlock/packets/RendererComponent.tsx +115 -0
- package/src/ChatBlock/packets/index.ts +4 -0
- package/src/ChatBlock/packets/renderers/CustomToolRenderer.tsx +63 -0
- package/src/ChatBlock/packets/renderers/FetchToolRenderer.tsx +59 -0
- package/src/ChatBlock/packets/renderers/ImageToolRenderer.tsx +62 -0
- package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +172 -0
- package/src/ChatBlock/packets/renderers/ReasoningRenderer.tsx +122 -0
- package/src/ChatBlock/packets/renderers/SearchToolRenderer.tsx +323 -0
- package/src/ChatBlock/packets/renderers/index.ts +6 -0
- package/src/ChatBlock/schema.js +403 -0
- package/src/ChatBlock/services/index.ts +3 -0
- package/src/ChatBlock/services/messageProcessor.ts +348 -0
- package/src/ChatBlock/services/packetUtils.ts +48 -0
- package/src/ChatBlock/services/streamingService.ts +342 -0
- package/src/ChatBlock/style.less +1881 -0
- package/src/ChatBlock/tests/AIMessage.test.jsx +95 -0
- package/src/ChatBlock/tests/AutoResizeTextarea.test.jsx +49 -0
- package/src/ChatBlock/tests/BlinkingDot.test.jsx +71 -0
- package/src/ChatBlock/tests/ChatMessageFeedback.test.jsx +73 -0
- package/src/ChatBlock/tests/Citation.test.jsx +107 -0
- package/src/ChatBlock/tests/EmptyState.test.jsx +137 -0
- package/src/ChatBlock/tests/FeedbackModal.test.jsx +138 -0
- package/src/ChatBlock/tests/HalloumiFeedback.test.jsx +94 -0
- package/src/ChatBlock/tests/QualityCheckToggle.test.jsx +105 -0
- package/src/ChatBlock/tests/RelatedQuestions.test.jsx +215 -0
- package/src/ChatBlock/tests/Source.test.jsx +79 -0
- package/src/ChatBlock/tests/Spinner.test.jsx +18 -0
- package/src/ChatBlock/tests/index.test.js +51 -0
- package/src/ChatBlock/tests/messageProcessor.test.jsx +154 -0
- package/src/ChatBlock/tests/schema.test.js +166 -0
- package/src/ChatBlock/tests/useDeepCompareMemoize.test.js +107 -0
- package/src/ChatBlock/tests/useToolDisplayTiming.test.jsx +151 -0
- package/src/ChatBlock/types/cssmodules.d.ts +7 -0
- package/src/ChatBlock/types/interfaces.ts +154 -0
- package/src/ChatBlock/types/slate.d.ts +3 -0
- package/src/ChatBlock/types/streamingModels.ts +267 -0
- package/src/ChatBlock/types/volto.d.ts +3 -0
- package/src/ChatBlock/utils/citations.ts +25 -0
- package/src/ChatBlock/utils/index.tsx +114 -0
- package/src/halloumi/README.md +1 -0
- package/src/halloumi/generative.js +219 -0
- package/src/halloumi/generative.test.js +88 -0
- package/src/halloumi/middleware.js +70 -0
- package/src/halloumi/postprocessing.js +273 -0
- package/src/halloumi/postprocessing.test.js +441 -0
- package/src/halloumi/preprocessing.js +115 -0
- package/src/halloumi/preprocessing.test.js +245 -0
- package/src/icons/bot.svg +1 -0
- package/src/icons/check.svg +1 -0
- package/src/icons/chevron.svg +3 -0
- package/src/icons/clear.svg +1 -0
- package/src/icons/copy.svg +1 -0
- package/src/icons/done.svg +5 -0
- package/src/icons/external-link.svg +1 -0
- package/src/icons/file.svg +1 -0
- package/src/icons/glasses.svg +1 -0
- package/src/icons/globe.svg +1 -0
- package/src/icons/rotate.svg +1 -0
- package/src/icons/search.svg +5 -0
- package/src/icons/send.svg +1 -0
- package/src/icons/square-pen.svg +1 -0
- package/src/icons/stop.svg +9 -0
- package/src/icons/thumbs-down.svg +1 -0
- package/src/icons/thumbs-up.svg +1 -0
- package/src/icons/user.svg +1 -0
- package/src/index.js +58 -0
- package/src/middleware.js +250 -0
- package/tsconfig.json +40 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Packet, OnyxDocument } from './streamingModels';
|
|
2
|
+
import { PacketType } from './streamingModels';
|
|
3
|
+
|
|
4
|
+
export type FeedbackType = 'like' | 'dislike';
|
|
5
|
+
|
|
6
|
+
export enum RetrievalType {
|
|
7
|
+
None = 'none',
|
|
8
|
+
Search = 'search',
|
|
9
|
+
SelectedDocs = 'selectedDocs',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum ResearchType {
|
|
13
|
+
LegacyAgentic = 'LEGACY_AGENTIC',
|
|
14
|
+
Thoughtful = 'THOUGHTFUL',
|
|
15
|
+
Deep = 'DEEP',
|
|
16
|
+
Fast = 'FAST',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum ChatFileType {
|
|
20
|
+
IMAGE = 'image',
|
|
21
|
+
DOCUMENT = 'document',
|
|
22
|
+
PLAIN_TEXT = 'plain_text',
|
|
23
|
+
CSV = 'csv',
|
|
24
|
+
USER_KNOWLEDGE = 'user_knowledge',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Message {
|
|
28
|
+
messageId: number | null;
|
|
29
|
+
nodeId: number;
|
|
30
|
+
message: string; // Unique identifier for tree structure (can be negative for temp messages)
|
|
31
|
+
error?: string;
|
|
32
|
+
type: 'user' | 'assistant' | 'system' | 'error';
|
|
33
|
+
retrievalType?: RetrievalType;
|
|
34
|
+
researchType?: ResearchType;
|
|
35
|
+
query?: string | null;
|
|
36
|
+
files: FileDescriptor[];
|
|
37
|
+
toolCall: ToolCallMetadata | null;
|
|
38
|
+
|
|
39
|
+
// for rebuilding the message tree
|
|
40
|
+
parentNodeId: number | null;
|
|
41
|
+
childrenNodeIds?: number[];
|
|
42
|
+
latestChildNodeId?: number | null;
|
|
43
|
+
overridden_model?: string;
|
|
44
|
+
|
|
45
|
+
// Packet-based data
|
|
46
|
+
packets: Packet[];
|
|
47
|
+
groupedPackets?: { ind: number; packets: Packet[] }[];
|
|
48
|
+
|
|
49
|
+
// Packet indices for tracking
|
|
50
|
+
toolPackets?: number[];
|
|
51
|
+
displayPackets?: number[];
|
|
52
|
+
|
|
53
|
+
// Message status
|
|
54
|
+
isComplete?: boolean;
|
|
55
|
+
isFinalMessageComing?: boolean;
|
|
56
|
+
|
|
57
|
+
// Cached values for easy access
|
|
58
|
+
documents?: OnyxDocument[] | null;
|
|
59
|
+
citations?: Record<string, string>; // citation_num -> document_id
|
|
60
|
+
relatedQuestions?: { question: string }[] | null;
|
|
61
|
+
|
|
62
|
+
// Feedback state
|
|
63
|
+
currentFeedback?: FeedbackType | null;
|
|
64
|
+
|
|
65
|
+
// Timestamps
|
|
66
|
+
time_sent?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RendererResult {
|
|
70
|
+
icon: React.ComponentType<{ size: number }> | null;
|
|
71
|
+
status: string | null;
|
|
72
|
+
content: JSX.Element;
|
|
73
|
+
expandedText?: JSX.Element;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface MessageRendererProps<T extends Packet = Packet> {
|
|
77
|
+
packets: T[];
|
|
78
|
+
message: Message;
|
|
79
|
+
libs: {
|
|
80
|
+
[key: string]: any;
|
|
81
|
+
};
|
|
82
|
+
markers?: any;
|
|
83
|
+
stableContextSources?: any;
|
|
84
|
+
addQualityMarkersPlugin?: any;
|
|
85
|
+
onComplete: () => void;
|
|
86
|
+
animate: boolean;
|
|
87
|
+
stopPacketSeen: boolean;
|
|
88
|
+
children: (result: RendererResult) => JSX.Element;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type MessageRenderer<T extends Packet = Packet> = React.FC<
|
|
92
|
+
MessageRendererProps<T>
|
|
93
|
+
>;
|
|
94
|
+
|
|
95
|
+
export interface GroupedPackets {
|
|
96
|
+
ind: number;
|
|
97
|
+
packets: Packet[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface FileDescriptor {
|
|
101
|
+
id: string;
|
|
102
|
+
type: ChatFileType;
|
|
103
|
+
name?: string | null;
|
|
104
|
+
|
|
105
|
+
user_file_id?: string | null;
|
|
106
|
+
// FE only
|
|
107
|
+
isUploading?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface Filters {
|
|
111
|
+
source_type: string[] | null;
|
|
112
|
+
document_set: string[] | null;
|
|
113
|
+
time_cutoff: Date | null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ToolCallMetadata {
|
|
117
|
+
tool_name: string;
|
|
118
|
+
tool_args: Record<string, any>;
|
|
119
|
+
tool_result?: Record<string, any>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ChatMessageProps {
|
|
123
|
+
message: Message;
|
|
124
|
+
isLoading: boolean;
|
|
125
|
+
isDeepResearchEnabled?: boolean;
|
|
126
|
+
libs?: any;
|
|
127
|
+
onChoice?: (message: string) => void;
|
|
128
|
+
onFetchRelatedQuestions?: () => void;
|
|
129
|
+
enableFeedback?: boolean;
|
|
130
|
+
scrollToInput?: boolean;
|
|
131
|
+
feedbackReasons?: string[];
|
|
132
|
+
qualityCheck?: string;
|
|
133
|
+
qualityCheckStages?: any[];
|
|
134
|
+
qualityCheckContext?: string;
|
|
135
|
+
qualityCheckEnabled?: boolean;
|
|
136
|
+
noSupportDocumentsMessage?: any;
|
|
137
|
+
totalFailMessage?: any;
|
|
138
|
+
isFetchingRelatedQuestions?: boolean;
|
|
139
|
+
enableShowTotalFailMessage?: boolean;
|
|
140
|
+
enableMatomoTracking?: boolean;
|
|
141
|
+
persona?: number;
|
|
142
|
+
maxContextSegments?: number;
|
|
143
|
+
isLastMessage?: boolean;
|
|
144
|
+
className?: string;
|
|
145
|
+
chatWindowRef?: React.RefObject<HTMLDivElement>;
|
|
146
|
+
chatWindowEndRef?: React.RefObject<HTMLDivElement>;
|
|
147
|
+
showTools?: PacketType[];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface Persona {
|
|
151
|
+
id: number;
|
|
152
|
+
name: string;
|
|
153
|
+
description?: string;
|
|
154
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Models for Onyx v2
|
|
3
|
+
* Based on the new packet-based architecture
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface OnyxDocument {
|
|
7
|
+
document_id: string;
|
|
8
|
+
semantic_identifier: string;
|
|
9
|
+
link: string | null;
|
|
10
|
+
blurb: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
source_type: string;
|
|
13
|
+
updated_at: string | null;
|
|
14
|
+
match_highlights: string[];
|
|
15
|
+
db_doc_id?: number;
|
|
16
|
+
score?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface BaseObj {
|
|
20
|
+
type: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum PacketType {
|
|
24
|
+
MESSAGE_START = 'message_start',
|
|
25
|
+
MESSAGE_DELTA = 'message_delta',
|
|
26
|
+
MESSAGE_END = 'message_end',
|
|
27
|
+
|
|
28
|
+
STOP = 'stop',
|
|
29
|
+
SECTION_END = 'section_end',
|
|
30
|
+
|
|
31
|
+
// Specific tool packets
|
|
32
|
+
SEARCH_TOOL_START = 'internal_search_tool_start',
|
|
33
|
+
SEARCH_TOOL_DELTA = 'internal_search_tool_delta',
|
|
34
|
+
IMAGE_GENERATION_TOOL_START = 'image_generation_tool_start',
|
|
35
|
+
IMAGE_GENERATION_TOOL_DELTA = 'image_generation_tool_delta',
|
|
36
|
+
FETCH_TOOL_START = 'fetch_tool_start',
|
|
37
|
+
|
|
38
|
+
// Custom tool packets
|
|
39
|
+
CUSTOM_TOOL_START = 'custom_tool_start',
|
|
40
|
+
CUSTOM_TOOL_DELTA = 'custom_tool_delta',
|
|
41
|
+
|
|
42
|
+
// Reasoning packets
|
|
43
|
+
REASONING_START = 'reasoning_start',
|
|
44
|
+
REASONING_DELTA = 'reasoning_delta',
|
|
45
|
+
REASONING_END = 'reasoning_end',
|
|
46
|
+
|
|
47
|
+
CITATION_START = 'citation_start',
|
|
48
|
+
CITATION_DELTA = 'citation_delta',
|
|
49
|
+
CITATION_END = 'citation_end',
|
|
50
|
+
|
|
51
|
+
MESSAGE_END_ID_INFO = 'message_end_id_info',
|
|
52
|
+
|
|
53
|
+
ERROR = 'error',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Basic Message Packets
|
|
57
|
+
export interface MessageStart extends BaseObj {
|
|
58
|
+
id: string;
|
|
59
|
+
type: PacketType.MESSAGE_START;
|
|
60
|
+
content: string;
|
|
61
|
+
final_documents: OnyxDocument[] | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MessageDelta extends BaseObj {
|
|
65
|
+
content: string;
|
|
66
|
+
type: PacketType.MESSAGE_DELTA;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface MessageEnd extends BaseObj {
|
|
70
|
+
type: PacketType.MESSAGE_END;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Control Packets
|
|
74
|
+
export interface Stop extends BaseObj {
|
|
75
|
+
type: PacketType.STOP;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface SectionEnd extends BaseObj {
|
|
79
|
+
type: PacketType.SECTION_END;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Specific tool packets
|
|
83
|
+
export interface SearchToolStart extends BaseObj {
|
|
84
|
+
type: PacketType.SEARCH_TOOL_START;
|
|
85
|
+
is_internet_search?: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SearchToolDelta extends BaseObj {
|
|
89
|
+
type: PacketType.SEARCH_TOOL_DELTA;
|
|
90
|
+
queries: string[] | null;
|
|
91
|
+
documents: OnyxDocument[] | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type ImageShape = 'square' | 'landscape' | 'portrait';
|
|
95
|
+
|
|
96
|
+
export interface GeneratedImage {
|
|
97
|
+
file_id: string;
|
|
98
|
+
url: string;
|
|
99
|
+
revised_prompt: string;
|
|
100
|
+
shape?: ImageShape;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ImageGenerationToolStart extends BaseObj {
|
|
104
|
+
type: PacketType.IMAGE_GENERATION_TOOL_START;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ImageGenerationToolDelta extends BaseObj {
|
|
108
|
+
type: PacketType.IMAGE_GENERATION_TOOL_DELTA;
|
|
109
|
+
images: GeneratedImage[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface FetchToolStart extends BaseObj {
|
|
113
|
+
type: PacketType.FETCH_TOOL_START;
|
|
114
|
+
queries: string[] | null;
|
|
115
|
+
documents: OnyxDocument[] | null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Custom Tool Packets
|
|
119
|
+
export interface CustomToolStart extends BaseObj {
|
|
120
|
+
type: PacketType.CUSTOM_TOOL_START;
|
|
121
|
+
tool_name: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface CustomToolDelta extends BaseObj {
|
|
125
|
+
type: PacketType.CUSTOM_TOOL_DELTA;
|
|
126
|
+
tool_name: string;
|
|
127
|
+
response_type: string;
|
|
128
|
+
data?: any;
|
|
129
|
+
file_ids?: string[] | null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Reasoning Packets
|
|
133
|
+
export interface ReasoningStart extends BaseObj {
|
|
134
|
+
type: PacketType.REASONING_START;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ReasoningDelta extends BaseObj {
|
|
138
|
+
type: PacketType.REASONING_DELTA;
|
|
139
|
+
reasoning: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ReasoningEnd extends BaseObj {
|
|
143
|
+
type: PacketType.REASONING_END;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Citation Packets
|
|
147
|
+
export interface StreamingCitation {
|
|
148
|
+
citation_num: number;
|
|
149
|
+
document_id: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface CitationStart extends BaseObj {
|
|
153
|
+
type: PacketType.CITATION_START;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface CitationDelta extends BaseObj {
|
|
157
|
+
type: PacketType.CITATION_DELTA;
|
|
158
|
+
citations: StreamingCitation[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface CitationEnd extends BaseObj {
|
|
162
|
+
type: PacketType.CITATION_END;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface MessageEndIdInfo extends BaseObj {
|
|
166
|
+
type: PacketType.MESSAGE_END_ID_INFO;
|
|
167
|
+
user_message_id: number;
|
|
168
|
+
reserved_assistant_message_id: number;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Error packet
|
|
172
|
+
export interface ErrorObj extends BaseObj {
|
|
173
|
+
type: PacketType.ERROR;
|
|
174
|
+
error: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export type ChatObj = MessageStart | MessageDelta | MessageEnd;
|
|
178
|
+
export type StopObj = Stop;
|
|
179
|
+
export type SectionEndObj = SectionEnd;
|
|
180
|
+
|
|
181
|
+
// Specific tool objects
|
|
182
|
+
export type SearchToolObj = SearchToolStart | SearchToolDelta | SectionEnd;
|
|
183
|
+
export type ImageGenerationToolObj =
|
|
184
|
+
| ImageGenerationToolStart
|
|
185
|
+
| ImageGenerationToolDelta
|
|
186
|
+
| SectionEnd;
|
|
187
|
+
export type FetchToolObj = FetchToolStart | SectionEnd;
|
|
188
|
+
export type CustomToolObj = CustomToolStart | CustomToolDelta | SectionEnd;
|
|
189
|
+
export type NewToolObj =
|
|
190
|
+
| SearchToolObj
|
|
191
|
+
| ImageGenerationToolObj
|
|
192
|
+
| FetchToolObj
|
|
193
|
+
| CustomToolObj;
|
|
194
|
+
|
|
195
|
+
export type ReasoningObj =
|
|
196
|
+
| ReasoningStart
|
|
197
|
+
| ReasoningDelta
|
|
198
|
+
| ReasoningEnd
|
|
199
|
+
| SectionEnd;
|
|
200
|
+
export type CitationObj =
|
|
201
|
+
| CitationStart
|
|
202
|
+
| CitationDelta
|
|
203
|
+
| CitationEnd
|
|
204
|
+
| SectionEnd;
|
|
205
|
+
|
|
206
|
+
// Union type for all possible streaming objects
|
|
207
|
+
export type ObjTypes =
|
|
208
|
+
| ChatObj
|
|
209
|
+
| NewToolObj
|
|
210
|
+
| ReasoningObj
|
|
211
|
+
| StopObj
|
|
212
|
+
| SectionEndObj
|
|
213
|
+
| CitationObj
|
|
214
|
+
| ErrorObj
|
|
215
|
+
| MessageEndIdInfo;
|
|
216
|
+
|
|
217
|
+
// Packet wrapper for streaming objects
|
|
218
|
+
export interface Packet {
|
|
219
|
+
ind: number;
|
|
220
|
+
obj: ObjTypes;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface ChatPacket {
|
|
224
|
+
ind: number;
|
|
225
|
+
obj: ChatObj;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface StopPacket {
|
|
229
|
+
ind: number;
|
|
230
|
+
obj: StopObj;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface CitationPacket {
|
|
234
|
+
ind: number;
|
|
235
|
+
obj: CitationObj;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// New specific tool packet types
|
|
239
|
+
export interface SearchToolPacket {
|
|
240
|
+
ind: number;
|
|
241
|
+
obj: SearchToolObj;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface ImageGenerationToolPacket {
|
|
245
|
+
ind: number;
|
|
246
|
+
obj: ImageGenerationToolObj;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface FetchToolPacket {
|
|
250
|
+
ind: number;
|
|
251
|
+
obj: FetchToolObj;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface CustomToolPacket {
|
|
255
|
+
ind: number;
|
|
256
|
+
obj: CustomToolObj;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface ReasoningPacket {
|
|
260
|
+
ind: number;
|
|
261
|
+
obj: ReasoningObj;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface SectionEndPacket {
|
|
265
|
+
ind: number;
|
|
266
|
+
obj: SectionEndObj;
|
|
267
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Message } from '../types/interfaces';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Regex to match citation markers like [1], [2], etc.
|
|
5
|
+
* The negative lookahead (?![[(\])]) ensures we don't match citations
|
|
6
|
+
* that are already formatted as links like [1](1) or [1][ or [1]]
|
|
7
|
+
*/
|
|
8
|
+
const CITATION_MATCH = /\[\d+\](?![[(\])])/gm;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Transforms citation markers [1] into markdown links [1](1)
|
|
12
|
+
* so they can be rendered as clickable citations by the Citation component.
|
|
13
|
+
*
|
|
14
|
+
* @param text - The text containing citation markers
|
|
15
|
+
* @returns The text with citations transformed into markdown links
|
|
16
|
+
*/
|
|
17
|
+
export function addCitations(text: string, message: Message): string {
|
|
18
|
+
return text.replaceAll(CITATION_MATCH, (match) => {
|
|
19
|
+
const number = match.match(/\d+/)?.[0];
|
|
20
|
+
if (!number || !message.citations) {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
return `[${match}](${message.citations[number]})`;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ReactNode, MutableRefObject } from 'react';
|
|
2
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
export const EMAIL_REGEX = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g;
|
|
5
|
+
|
|
6
|
+
interface CreateChatMessageFeedbackArgs {
|
|
7
|
+
chat_message_id: string;
|
|
8
|
+
feedback_text?: string;
|
|
9
|
+
is_positive: boolean;
|
|
10
|
+
predefined_feedback?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Convert text with email addresses to mailto links
|
|
14
|
+
export function transformEmailsToLinks(text: string): (string | ReactNode)[] {
|
|
15
|
+
return text.split(EMAIL_REGEX).map((part, index) => {
|
|
16
|
+
if (EMAIL_REGEX.test(part)) {
|
|
17
|
+
return (
|
|
18
|
+
<a key={index} href={`mailto:${part}`} className="text-email">
|
|
19
|
+
{part}
|
|
20
|
+
</a>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return part;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function debounce(
|
|
28
|
+
callable: () => void,
|
|
29
|
+
click_signal: MutableRefObject<boolean | null>,
|
|
30
|
+
): void {
|
|
31
|
+
if (!click_signal.current) {
|
|
32
|
+
click_signal.current = true;
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
click_signal.current = null;
|
|
35
|
+
}, 1000);
|
|
36
|
+
callable();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const useCopyToClipboard = (text: string): [boolean, () => void] => {
|
|
41
|
+
const [copied, setCopied] = useState(false);
|
|
42
|
+
|
|
43
|
+
const copy = useCallback(() => {
|
|
44
|
+
navigator.clipboard.writeText(text).then(
|
|
45
|
+
() => setCopied(true),
|
|
46
|
+
() => setCopied(false),
|
|
47
|
+
);
|
|
48
|
+
}, [text]);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!copied) return;
|
|
52
|
+
|
|
53
|
+
const timeout = setTimeout(() => setCopied(false), 2000);
|
|
54
|
+
|
|
55
|
+
return () => clearTimeout(timeout);
|
|
56
|
+
}, [copied]);
|
|
57
|
+
|
|
58
|
+
return [copied, copy];
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function convertToPercentage(
|
|
62
|
+
floatValue: number,
|
|
63
|
+
digits: number = 2,
|
|
64
|
+
): string {
|
|
65
|
+
if (floatValue < 0 || floatValue > 1) {
|
|
66
|
+
return '0%';
|
|
67
|
+
}
|
|
68
|
+
return (floatValue * 100).toFixed(digits) + '%';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function createChatMessageFeedback({
|
|
72
|
+
chat_message_id,
|
|
73
|
+
feedback_text = '',
|
|
74
|
+
is_positive,
|
|
75
|
+
predefined_feedback = '',
|
|
76
|
+
}: CreateChatMessageFeedbackArgs): Promise<any> {
|
|
77
|
+
const payload: {
|
|
78
|
+
chat_message_id: string;
|
|
79
|
+
feedback_text: string;
|
|
80
|
+
is_positive: boolean;
|
|
81
|
+
predefined_feedback?: string;
|
|
82
|
+
} = {
|
|
83
|
+
chat_message_id,
|
|
84
|
+
feedback_text,
|
|
85
|
+
is_positive,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (!is_positive) {
|
|
89
|
+
payload.predefined_feedback = predefined_feedback;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const createChatMessageFeedbackResponse = await fetch(
|
|
93
|
+
'/_da/chat/create-chat-message-feedback',
|
|
94
|
+
{
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify(payload),
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (!createChatMessageFeedbackResponse.ok) {
|
|
104
|
+
//eslint-disable-next-line no-console
|
|
105
|
+
console.log(
|
|
106
|
+
`Failed to submit feedback - ${createChatMessageFeedbackResponse.status}`,
|
|
107
|
+
);
|
|
108
|
+
throw Error(`Failed to submit feedback.`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const createChatMessageFeedbackResponseJson =
|
|
112
|
+
await createChatMessageFeedbackResponse.json();
|
|
113
|
+
return await createChatMessageFeedbackResponseJson;
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
code based on Apache2 licensed https://github.com/oumi-ai/halloumi-demo/tree/d088e1f25e7785326a53bc120113e226ee2f54b7
|