@eeacms/volto-eea-chatbot 3.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +53 -4
  2. package/README.md +115 -12
  3. package/package.json +1 -1
  4. package/src/ChatBlock/ChatBlockView.jsx +7 -1
  5. package/src/ChatBlock/chat/AIMessage.tsx +79 -45
  6. package/src/ChatBlock/chat/ChatMessage.tsx +17 -10
  7. package/src/ChatBlock/chat/ChatWindow.tsx +11 -0
  8. package/src/ChatBlock/chat/MessageContext.ts +12 -0
  9. package/src/ChatBlock/chat/index.ts +1 -0
  10. package/src/ChatBlock/components/markdown/ClaimModal.jsx +3 -3
  11. package/src/ChatBlock/components/markdown/index.jsx +1 -0
  12. package/src/ChatBlock/hooks/useQualityMarkers.js +21 -4
  13. package/src/ChatBlock/index.js +9 -1
  14. package/src/ChatBlock/packets/RendererComponent.tsx +9 -0
  15. package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +15 -3
  16. package/src/ChatBlock/schema.jsx +13 -0
  17. package/src/ChatBlock/services/messageProcessor.ts +9 -12
  18. package/src/ChatBlock/types/interfaces.ts +25 -0
  19. package/src/ChatBlock/utils/index.tsx +10 -0
  20. package/src/ChatBlock/variations.ts +44 -0
  21. package/src/halloumi/README.md +27 -1
  22. package/src/halloumi/middleware.js +41 -50
  23. package/src/halloumi/middleware.test.js +102 -103
  24. package/src/halloumi/filtering.js +0 -149
  25. package/src/halloumi/filtering.test.js +0 -242
  26. package/src/halloumi/generative.js +0 -324
  27. package/src/halloumi/generative.test.js +0 -386
  28. package/src/halloumi/markdown-splitter.js +0 -172
  29. package/src/halloumi/markdown-splitter.test.js +0 -133
  30. package/src/halloumi/postprocessing.js +0 -247
  31. package/src/halloumi/postprocessing.test.js +0 -441
  32. package/src/halloumi/preprocessing.js +0 -117
  33. package/src/halloumi/preprocessing.test.js +0 -184
@@ -1,184 +0,0 @@
1
- import {
2
- createChunkedHalloumiPrompts,
3
- annotate,
4
- getOffsets,
5
- } from './preprocessing';
6
-
7
- describe('annotate', () => {
8
- it('should annotate multiple sentences correctly', () => {
9
- const sentences = ['Sentence one.', 'Sentence two.'];
10
- const annotationChar = 's';
11
- const expected =
12
- '<|s1|><Sentence one.><end||s><|s2|><Sentence two.><end||s>';
13
- expect(annotate(sentences, annotationChar)).toEqual(expected);
14
- });
15
-
16
- it('should handle an empty array of sentences', () => {
17
- const sentences = [];
18
- const annotationChar = 's';
19
- expect(annotate(sentences, annotationChar)).toEqual('');
20
- });
21
-
22
- it('should annotate a single sentence', () => {
23
- const sentences = ['Single sentence.'];
24
- const annotationChar = 'r';
25
- const expected = '<|r1|><Single sentence.><end||r>';
26
- expect(annotate(sentences, annotationChar)).toEqual(expected);
27
- });
28
-
29
- it('should use different annotation characters', () => {
30
- const sentences = ['Hello.'];
31
- const annotationChar = 'x';
32
- const expected = '<|x1|><Hello.><end||x>';
33
- expect(annotate(sentences, annotationChar)).toEqual(expected);
34
- });
35
- });
36
-
37
- describe('getOffsets', () => {
38
- it('should calculate correct offsets for multiple sentences', () => {
39
- const originalString = 'First sentence. Second sentence. Third sentence.';
40
- const sentences = [
41
- 'First sentence. ',
42
- 'Second sentence. ',
43
- 'Third sentence.',
44
- ];
45
- const expected = new Map([
46
- [1, { startOffset: 0, endOffset: 16 }],
47
- [2, { startOffset: 16, endOffset: 33 }],
48
- [3, { startOffset: 33, endOffset: 48 }],
49
- ]);
50
- expect(getOffsets(originalString, sentences)).toEqual(expected);
51
- });
52
-
53
- it('should handle empty original string and sentences', () => {
54
- const originalString = '';
55
- const sentences = [];
56
- expect(getOffsets(originalString, sentences)).toEqual(new Map());
57
- });
58
-
59
- it('should handle original string matching sentences exactly', () => {
60
- const originalString = 'Hello world.';
61
- const sentences = ['Hello world.'];
62
- const expected = new Map([[1, { startOffset: 0, endOffset: 12 }]]);
63
- expect(getOffsets(originalString, sentences)).toEqual(expected);
64
- });
65
-
66
- it('should handle original string with leading/trailing spaces', () => {
67
- const originalString = ' Sentence one. Sentence two. ';
68
- const sentences = ['Sentence one. ', 'Sentence two. '];
69
- const expected = new Map([
70
- [1, { startOffset: 2, endOffset: 17 }],
71
- [2, { startOffset: 17, endOffset: 32 }],
72
- ]);
73
- expect(getOffsets(originalString, sentences)).toEqual(expected);
74
- });
75
-
76
- it('should handle sentences with special characters', () => {
77
- const originalString = 'Sentence with !@#$%^&*() special characters.';
78
- const sentences = ['Sentence with !@#$%^&*() special characters.'];
79
- const expected = new Map([[1, { startOffset: 0, endOffset: 44 }]]);
80
- expect(getOffsets(originalString, sentences)).toEqual(expected);
81
- });
82
- });
83
-
84
- describe('createChunkedHalloumiPrompts', () => {
85
- it('should create a single chunk for small input', () => {
86
- const indexedContextSentences = [
87
- { sentence: 'Context one.', sourceId: 1, globalId: 1 },
88
- { sentence: 'Context two.', sourceId: 1, globalId: 2 },
89
- ];
90
- const responseSentences = ['Response one.', 'Response two.'];
91
- const responseOffsets = new Map([
92
- [1, { startOffset: 0, endOffset: 13 }],
93
- [2, { startOffset: 14, endOffset: 28 }],
94
- ]);
95
-
96
- const { prompts } = createChunkedHalloumiPrompts({
97
- indexedContextSentences,
98
- responseSentences,
99
- responseOffsets,
100
- });
101
-
102
- expect(prompts).toHaveLength(1);
103
- expect(prompts[0].prompt).toContain('<|s1|><Context one.><end||s>');
104
- expect(prompts[0].prompt).toContain('<|s2|><Context two.><end||s>');
105
- expect(prompts[0].prompt).toContain('<|r1|><Response one.><end||r>');
106
- expect(prompts[0].prompt).toContain('<|r2|><Response two.><end||r>');
107
- });
108
-
109
- it('should handle empty context', () => {
110
- const responseSentences = ['Response.'];
111
- const responseOffsets = new Map([[1, { startOffset: 0, endOffset: 9 }]]);
112
-
113
- const { prompts } = createChunkedHalloumiPrompts({
114
- indexedContextSentences: [],
115
- responseSentences,
116
- responseOffsets,
117
- });
118
-
119
- expect(prompts).toHaveLength(1);
120
- expect(prompts[0].prompt).toContain('<|context|><end||context>');
121
- });
122
-
123
- it('should exclude response sentences based on excludeResponseIndices', () => {
124
- const indexedContextSentences = [
125
- { sentence: 'Context.', sourceId: 1, globalId: 1 },
126
- ];
127
- const responseSentences = ['Keep this.', 'Skip this.', 'Keep too.'];
128
- const responseOffsets = new Map([
129
- [1, { startOffset: 0, endOffset: 10 }],
130
- [2, { startOffset: 11, endOffset: 21 }],
131
- [3, { startOffset: 22, endOffset: 31 }],
132
- ]);
133
- const excludeResponseIndices = new Set([2]);
134
-
135
- const { prompts } = createChunkedHalloumiPrompts({
136
- indexedContextSentences,
137
- responseSentences,
138
- responseOffsets,
139
- excludeResponseIndices,
140
- });
141
-
142
- expect(prompts[0].prompt).toContain('<|r1|><Keep this.><end||r>');
143
- expect(prompts[0].prompt).not.toContain('Skip this.');
144
- expect(prompts[0].prompt).toContain('<|r3|><Keep too.><end||r>');
145
- });
146
-
147
- it('should keep sources together with bin packing', () => {
148
- // 3 sources: 60 + 60 + 30 = 150 sentences
149
- // Should pack into 2 chunks: (60+30) and (60) or similar
150
- const indexed = [];
151
- let gid = 1;
152
- // Source 1: 60 sentences
153
- for (let i = 0; i < 60; i++) {
154
- indexed.push({ sentence: `S1-${i}`, sourceId: 1, globalId: gid++ });
155
- }
156
- // Source 2: 60 sentences
157
- for (let i = 0; i < 60; i++) {
158
- indexed.push({ sentence: `S2-${i}`, sourceId: 2, globalId: gid++ });
159
- }
160
- // Source 3: 30 sentences
161
- for (let i = 0; i < 30; i++) {
162
- indexed.push({ sentence: `S3-${i}`, sourceId: 3, globalId: gid++ });
163
- }
164
-
165
- const { prompts } = createChunkedHalloumiPrompts({
166
- indexedContextSentences: indexed,
167
- responseSentences: ['Claim.'],
168
- responseOffsets: new Map([[1, { startOffset: 0, endOffset: 6 }]]),
169
- });
170
-
171
- expect(prompts).toHaveLength(2);
172
-
173
- // Each source should be entirely within one chunk
174
- for (const prompt of prompts) {
175
- const s1Count = (prompt.prompt.match(/S1-/g) || []).length;
176
- const s2Count = (prompt.prompt.match(/S2-/g) || []).length;
177
- const s3Count = (prompt.prompt.match(/S3-/g) || []).length;
178
-
179
- if (s1Count > 0) expect(s1Count).toBe(60);
180
- if (s2Count > 0) expect(s2Count).toBe(60);
181
- if (s3Count > 0) expect(s3Count).toBe(30);
182
- }
183
- });
184
- });