@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.
Files changed (133) hide show
  1. package/.coverage.babel.config.js +9 -0
  2. package/.eslintrc.js +68 -0
  3. package/.husky/pre-commit +2 -0
  4. package/.release-it.json +17 -0
  5. package/AGENTS.md +89 -0
  6. package/CHANGELOG.md +770 -0
  7. package/DEVELOP.md +124 -0
  8. package/LICENSE.md +9 -0
  9. package/README.md +170 -0
  10. package/RELEASE.md +74 -0
  11. package/TESTING.md +5 -0
  12. package/babel.config.js +17 -0
  13. package/bootstrap +41 -0
  14. package/cypress.config.js +27 -0
  15. package/docker-compose.yml +32 -0
  16. package/jest-addon.config.js +465 -0
  17. package/jest.setup.js +65 -0
  18. package/locales/de/LC_MESSAGES/volto.po +14 -0
  19. package/locales/en/LC_MESSAGES/volto.po +14 -0
  20. package/locales/it/LC_MESSAGES/volto.po +14 -0
  21. package/locales/ro/LC_MESSAGES/volto.po +14 -0
  22. package/locales/volto.pot +16 -0
  23. package/package.json +98 -0
  24. package/razzle.extend.js +40 -0
  25. package/src/ChatBlock/ChatBlockEdit.jsx +46 -0
  26. package/src/ChatBlock/ChatBlockView.jsx +21 -0
  27. package/src/ChatBlock/chat/AIMessage.tsx +566 -0
  28. package/src/ChatBlock/chat/ChatMessage.tsx +35 -0
  29. package/src/ChatBlock/chat/ChatWindow.tsx +288 -0
  30. package/src/ChatBlock/chat/UserMessage.tsx +27 -0
  31. package/src/ChatBlock/chat/index.ts +4 -0
  32. package/src/ChatBlock/components/AutoResizeTextarea.jsx +67 -0
  33. package/src/ChatBlock/components/BlinkingDot.tsx +3 -0
  34. package/src/ChatBlock/components/ChatMessageFeedback.jsx +77 -0
  35. package/src/ChatBlock/components/EmptyState.jsx +70 -0
  36. package/src/ChatBlock/components/FeedbackModal.jsx +125 -0
  37. package/src/ChatBlock/components/HalloumiFeedback.jsx +126 -0
  38. package/src/ChatBlock/components/Icon.tsx +35 -0
  39. package/src/ChatBlock/components/QualityCheckToggle.jsx +26 -0
  40. package/src/ChatBlock/components/RelatedQuestions.jsx +59 -0
  41. package/src/ChatBlock/components/Source.jsx +93 -0
  42. package/src/ChatBlock/components/SourceChip.tsx +55 -0
  43. package/src/ChatBlock/components/Spinner.jsx +3 -0
  44. package/src/ChatBlock/components/UserActionsToolbar.jsx +44 -0
  45. package/src/ChatBlock/components/WebResultIcon.tsx +42 -0
  46. package/src/ChatBlock/components/markdown/Citation.jsx +70 -0
  47. package/src/ChatBlock/components/markdown/ClaimModal.jsx +98 -0
  48. package/src/ChatBlock/components/markdown/ClaimSegments.jsx +172 -0
  49. package/src/ChatBlock/components/markdown/RenderClaimView.jsx +96 -0
  50. package/src/ChatBlock/components/markdown/colors.js +29 -0
  51. package/src/ChatBlock/components/markdown/colors.less +52 -0
  52. package/src/ChatBlock/components/markdown/colors.test.js +69 -0
  53. package/src/ChatBlock/components/markdown/index.js +115 -0
  54. package/src/ChatBlock/fonts/DejaVuSans.ttf +0 -0
  55. package/src/ChatBlock/hocs/withOnyxData.jsx +46 -0
  56. package/src/ChatBlock/hooks/index.ts +7 -0
  57. package/src/ChatBlock/hooks/useChatController.ts +333 -0
  58. package/src/ChatBlock/hooks/useChatStreaming.ts +82 -0
  59. package/src/ChatBlock/hooks/useDeepCompareMemoize.js +17 -0
  60. package/src/ChatBlock/hooks/useMarked.js +44 -0
  61. package/src/ChatBlock/hooks/useQualityMarkers.js +119 -0
  62. package/src/ChatBlock/hooks/useScrollonStream.ts +131 -0
  63. package/src/ChatBlock/hooks/useToolDisplayTiming.ts +80 -0
  64. package/src/ChatBlock/index.js +32 -0
  65. package/src/ChatBlock/packets/MultiToolRenderer.tsx +235 -0
  66. package/src/ChatBlock/packets/RendererComponent.tsx +115 -0
  67. package/src/ChatBlock/packets/index.ts +4 -0
  68. package/src/ChatBlock/packets/renderers/CustomToolRenderer.tsx +63 -0
  69. package/src/ChatBlock/packets/renderers/FetchToolRenderer.tsx +59 -0
  70. package/src/ChatBlock/packets/renderers/ImageToolRenderer.tsx +62 -0
  71. package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +172 -0
  72. package/src/ChatBlock/packets/renderers/ReasoningRenderer.tsx +122 -0
  73. package/src/ChatBlock/packets/renderers/SearchToolRenderer.tsx +323 -0
  74. package/src/ChatBlock/packets/renderers/index.ts +6 -0
  75. package/src/ChatBlock/schema.js +403 -0
  76. package/src/ChatBlock/services/index.ts +3 -0
  77. package/src/ChatBlock/services/messageProcessor.ts +348 -0
  78. package/src/ChatBlock/services/packetUtils.ts +48 -0
  79. package/src/ChatBlock/services/streamingService.ts +342 -0
  80. package/src/ChatBlock/style.less +1881 -0
  81. package/src/ChatBlock/tests/AIMessage.test.jsx +95 -0
  82. package/src/ChatBlock/tests/AutoResizeTextarea.test.jsx +49 -0
  83. package/src/ChatBlock/tests/BlinkingDot.test.jsx +71 -0
  84. package/src/ChatBlock/tests/ChatMessageFeedback.test.jsx +73 -0
  85. package/src/ChatBlock/tests/Citation.test.jsx +107 -0
  86. package/src/ChatBlock/tests/EmptyState.test.jsx +137 -0
  87. package/src/ChatBlock/tests/FeedbackModal.test.jsx +138 -0
  88. package/src/ChatBlock/tests/HalloumiFeedback.test.jsx +94 -0
  89. package/src/ChatBlock/tests/QualityCheckToggle.test.jsx +105 -0
  90. package/src/ChatBlock/tests/RelatedQuestions.test.jsx +215 -0
  91. package/src/ChatBlock/tests/Source.test.jsx +79 -0
  92. package/src/ChatBlock/tests/Spinner.test.jsx +18 -0
  93. package/src/ChatBlock/tests/index.test.js +51 -0
  94. package/src/ChatBlock/tests/messageProcessor.test.jsx +154 -0
  95. package/src/ChatBlock/tests/schema.test.js +166 -0
  96. package/src/ChatBlock/tests/useDeepCompareMemoize.test.js +107 -0
  97. package/src/ChatBlock/tests/useToolDisplayTiming.test.jsx +151 -0
  98. package/src/ChatBlock/types/cssmodules.d.ts +7 -0
  99. package/src/ChatBlock/types/interfaces.ts +154 -0
  100. package/src/ChatBlock/types/slate.d.ts +3 -0
  101. package/src/ChatBlock/types/streamingModels.ts +267 -0
  102. package/src/ChatBlock/types/volto.d.ts +3 -0
  103. package/src/ChatBlock/utils/citations.ts +25 -0
  104. package/src/ChatBlock/utils/index.tsx +114 -0
  105. package/src/halloumi/README.md +1 -0
  106. package/src/halloumi/generative.js +219 -0
  107. package/src/halloumi/generative.test.js +88 -0
  108. package/src/halloumi/middleware.js +70 -0
  109. package/src/halloumi/postprocessing.js +273 -0
  110. package/src/halloumi/postprocessing.test.js +441 -0
  111. package/src/halloumi/preprocessing.js +115 -0
  112. package/src/halloumi/preprocessing.test.js +245 -0
  113. package/src/icons/bot.svg +1 -0
  114. package/src/icons/check.svg +1 -0
  115. package/src/icons/chevron.svg +3 -0
  116. package/src/icons/clear.svg +1 -0
  117. package/src/icons/copy.svg +1 -0
  118. package/src/icons/done.svg +5 -0
  119. package/src/icons/external-link.svg +1 -0
  120. package/src/icons/file.svg +1 -0
  121. package/src/icons/glasses.svg +1 -0
  122. package/src/icons/globe.svg +1 -0
  123. package/src/icons/rotate.svg +1 -0
  124. package/src/icons/search.svg +5 -0
  125. package/src/icons/send.svg +1 -0
  126. package/src/icons/square-pen.svg +1 -0
  127. package/src/icons/stop.svg +9 -0
  128. package/src/icons/thumbs-down.svg +1 -0
  129. package/src/icons/thumbs-up.svg +1 -0
  130. package/src/icons/user.svg +1 -0
  131. package/src/index.js +58 -0
  132. package/src/middleware.js +250 -0
  133. 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,3 @@
1
+ declare module '@plone/volto-slate/editor/render' {
2
+ export const serializeNodes: any;
3
+ }
@@ -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,3 @@
1
+ declare module '@plone/volto/helpers/Loadable' {
2
+ export const injectLazyLibs: any;
3
+ }
@@ -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