@memori.ai/memori-react 8.29.0 → 8.30.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.
- package/CHANGELOG.md +30 -0
- package/README.md +1 -1
- package/dist/components/Chat/Chat.css +110 -9
- package/dist/components/Chat/Chat.d.ts +1 -0
- package/dist/components/Chat/Chat.js +72 -5
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.d.ts +1 -0
- package/dist/components/ChatBubble/ChatBubble.js +6 -6
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/ChatInputs/ChatInputs.js +2 -11
- package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
- package/dist/components/DrawerFooter/DrawerFooter.css +0 -1
- package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +13 -3
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/MicrophoneButton/MicrophoneButton.css +4 -14
- package/dist/components/MicrophoneButton/MicrophoneButton.js +1 -1
- package/dist/components/MicrophoneButton/MicrophoneButton.js.map +1 -1
- package/dist/components/StartPanel/StartPanel.js +1 -1
- package/dist/components/StartPanel/StartPanel.js.map +1 -1
- package/dist/components/UploadButton/UploadButton.js +8 -14
- package/dist/components/UploadButton/UploadButton.js.map +1 -1
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +0 -1
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +1 -20
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/dist/components/layouts/fullpage.css +3 -1
- package/dist/components/layouts/website-assistant.css +1 -0
- package/dist/components/ui/Tooltip.css +9 -1
- package/dist/components/ui/Tooltip.js +80 -4
- package/dist/components/ui/Tooltip.js.map +1 -1
- package/dist/helpers/constants.d.ts +1 -2
- package/dist/helpers/constants.js +2 -3
- package/dist/helpers/constants.js.map +1 -1
- package/dist/helpers/llmUsage.d.ts +60 -0
- package/dist/helpers/llmUsage.js +223 -0
- package/dist/helpers/llmUsage.js.map +1 -0
- package/dist/helpers/message.d.ts +1 -0
- package/dist/helpers/message.js +3 -1
- package/dist/helpers/message.js.map +1 -1
- package/dist/helpers/userMessage.d.ts +2 -0
- package/dist/helpers/userMessage.js +23 -0
- package/dist/helpers/userMessage.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/locales/de.json +22 -0
- package/dist/locales/en.json +22 -0
- package/dist/locales/es.json +22 -0
- package/dist/locales/fr.json +22 -0
- package/dist/locales/it.json +22 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/components/Chat/Chat.css +110 -9
- package/esm/components/Chat/Chat.d.ts +1 -0
- package/esm/components/Chat/Chat.js +74 -7
- package/esm/components/Chat/Chat.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.d.ts +1 -0
- package/esm/components/ChatBubble/ChatBubble.js +7 -7
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/ChatInputs/ChatInputs.js +2 -11
- package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
- package/esm/components/DrawerFooter/DrawerFooter.css +0 -1
- package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +13 -3
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/MicrophoneButton/MicrophoneButton.css +4 -14
- package/esm/components/MicrophoneButton/MicrophoneButton.js +1 -1
- package/esm/components/MicrophoneButton/MicrophoneButton.js.map +1 -1
- package/esm/components/StartPanel/StartPanel.js +1 -1
- package/esm/components/StartPanel/StartPanel.js.map +1 -1
- package/esm/components/UploadButton/UploadButton.js +8 -14
- package/esm/components/UploadButton/UploadButton.js.map +1 -1
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +0 -1
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +1 -20
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/esm/components/layouts/fullpage.css +3 -1
- package/esm/components/layouts/website-assistant.css +1 -0
- package/esm/components/ui/Tooltip.css +9 -1
- package/esm/components/ui/Tooltip.js +81 -5
- package/esm/components/ui/Tooltip.js.map +1 -1
- package/esm/helpers/constants.d.ts +1 -2
- package/esm/helpers/constants.js +1 -2
- package/esm/helpers/constants.js.map +1 -1
- package/esm/helpers/llmUsage.d.ts +60 -0
- package/esm/helpers/llmUsage.js +212 -0
- package/esm/helpers/llmUsage.js.map +1 -0
- package/esm/helpers/message.d.ts +1 -0
- package/esm/helpers/message.js +1 -0
- package/esm/helpers/message.js.map +1 -1
- package/esm/helpers/userMessage.d.ts +2 -0
- package/esm/helpers/userMessage.js +18 -0
- package/esm/helpers/userMessage.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +3 -1
- package/esm/index.js.map +1 -1
- package/esm/locales/de.json +22 -0
- package/esm/locales/en.json +22 -0
- package/esm/locales/es.json +22 -0
- package/esm/locales/fr.json +22 -0
- package/esm/locales/it.json +22 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +1 -1
- package/src/components/BlockedMemoriBadge/__snapshots__/BlockedMemoriBadge.test.tsx.snap +0 -29
- package/src/components/Chat/Chat.css +110 -9
- package/src/components/Chat/Chat.stories.tsx +42 -0
- package/src/components/Chat/Chat.test.tsx +47 -0
- package/src/components/Chat/Chat.tsx +238 -5
- package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +745 -0
- package/src/components/ChatBubble/ChatBubble.tsx +20 -7
- package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +0 -20
- package/src/components/ChatInputs/ChatInputs.tsx +4 -15
- package/src/components/CompletionProviderStatus/__snapshots__/CompletionProviderStatus.test.tsx.snap +0 -54
- package/src/components/FeedbackButtons/__snapshots__/FeedbackButtons.test.tsx.snap +0 -5
- package/src/components/MemoriWidget/MemoriWidget.tsx +20 -2
- package/src/components/MicrophoneButton/MicrophoneButton.css +4 -14
- package/src/components/MicrophoneButton/MicrophoneButton.tsx +0 -1
- package/src/components/StartPanel/StartPanel.tsx +1 -1
- package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +11 -368
- package/src/components/UploadButton/UploadButton.stories.tsx +3 -3
- package/src/components/UploadButton/UploadButton.tsx +8 -23
- package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +1 -27
- package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +1 -30
- package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +2 -60
- package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +1 -30
- package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +1 -30
- package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +1 -30
- package/src/components/layouts/website-assistant.css +1 -0
- package/src/components/ui/Tooltip.css +9 -1
- package/src/components/ui/Tooltip.tsx +133 -17
- package/src/components/ui/__snapshots__/Tooltip.test.tsx.snap +5 -55
- package/src/helpers/constants.ts +1 -2
- package/src/helpers/llmUsage.ts +328 -0
- package/src/helpers/message.ts +3 -0
- package/src/index.stories.tsx +2 -3
- package/src/index.tsx +5 -1
- package/src/locales/de.json +22 -0
- package/src/locales/en.json +22 -0
- package/src/locales/es.json +22 -0
- package/src/locales/fr.json +22 -0
- package/src/locales/it.json +22 -0
- package/src/version.ts +1 -1
|
@@ -5,11 +5,6 @@ exports[`renders BlockedMemoriBadge for not enough credits unchanged 1`] = `
|
|
|
5
5
|
<div
|
|
6
6
|
class="memori-tooltip memori-tooltip--align-right blocked-memori-badge--tooltip"
|
|
7
7
|
>
|
|
8
|
-
<div
|
|
9
|
-
class="memori-tooltip--content"
|
|
10
|
-
>
|
|
11
|
-
notEnoughCredits
|
|
12
|
-
</div>
|
|
13
8
|
<div
|
|
14
9
|
class="memori-tooltip--trigger"
|
|
15
10
|
>
|
|
@@ -45,11 +40,6 @@ exports[`renders BlockedMemoriBadge unchanged 1`] = `
|
|
|
45
40
|
<div
|
|
46
41
|
class="memori-tooltip memori-tooltip--align-right blocked-memori-badge--tooltip"
|
|
47
42
|
>
|
|
48
|
-
<div
|
|
49
|
-
class="memori-tooltip--content"
|
|
50
|
-
>
|
|
51
|
-
memoriBlockedAnon
|
|
52
|
-
</div>
|
|
53
43
|
<div
|
|
54
44
|
class="memori-tooltip--trigger"
|
|
55
45
|
>
|
|
@@ -85,15 +75,6 @@ exports[`renders BlockedMemoriBadge with giver info unchanged 1`] = `
|
|
|
85
75
|
<div
|
|
86
76
|
class="memori-tooltip memori-tooltip--align-right blocked-memori-badge--tooltip"
|
|
87
77
|
>
|
|
88
|
-
<div
|
|
89
|
-
class="memori-tooltip--content"
|
|
90
|
-
>
|
|
91
|
-
memoriBlockedUntil
|
|
92
|
-
memoriBlockedReasonExceedChats
|
|
93
|
-
<br />
|
|
94
|
-
|
|
95
|
-
memoriBlockedGiverHelper
|
|
96
|
-
</div>
|
|
97
78
|
<div
|
|
98
79
|
class="memori-tooltip--trigger"
|
|
99
80
|
>
|
|
@@ -129,11 +110,6 @@ exports[`renders BlockedMemoriBadge with margin left unchanged 1`] = `
|
|
|
129
110
|
<div
|
|
130
111
|
class="memori-tooltip memori-tooltip--align-right blocked-memori-badge--tooltip"
|
|
131
112
|
>
|
|
132
|
-
<div
|
|
133
|
-
class="memori-tooltip--content"
|
|
134
|
-
>
|
|
135
|
-
memoriBlockedAnon
|
|
136
|
-
</div>
|
|
137
113
|
<div
|
|
138
114
|
class="memori-tooltip--trigger"
|
|
139
115
|
>
|
|
@@ -169,11 +145,6 @@ exports[`renders BlockedMemoriBadge with title unchanged 1`] = `
|
|
|
169
145
|
<div
|
|
170
146
|
class="memori-tooltip memori-tooltip--align-right blocked-memori-badge--tooltip"
|
|
171
147
|
>
|
|
172
|
-
<div
|
|
173
|
-
class="memori-tooltip--content"
|
|
174
|
-
>
|
|
175
|
-
memoriBlockedAnon
|
|
176
|
-
</div>
|
|
177
148
|
<div
|
|
178
149
|
class="memori-tooltip--trigger"
|
|
179
150
|
>
|
|
@@ -6,15 +6,6 @@
|
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
.memori-chat-wrapper--expanded {
|
|
10
|
-
/* Ensure proper spacing when textarea is expanded */
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/* Handle the focused chat state on mobile */
|
|
14
|
-
.memori-chat--wrapper.chat-focused {
|
|
15
|
-
/* Mobile keyboard adjustment */
|
|
16
|
-
}
|
|
17
|
-
|
|
18
9
|
@media (max-width: 768px) {
|
|
19
10
|
.memori-chat--wrapper.chat-focused {
|
|
20
11
|
padding-bottom: 0;
|
|
@@ -181,4 +172,114 @@
|
|
|
181
172
|
font-size: 1.2rem;
|
|
182
173
|
font-weight: 500;
|
|
183
174
|
text-align: center;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.memori-chat--llm-usage {
|
|
178
|
+
margin-top: 0.625rem;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.memori-chat--usage-inside-bubble {
|
|
182
|
+
margin-top: 0.25rem;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.memori-chat--llm-usage-hr {
|
|
186
|
+
border: 0;
|
|
187
|
+
border-top: 1px solid rgba(148, 163, 184, 0.35);
|
|
188
|
+
margin: 0 0 0.5rem;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.memori-chat--llm-usage-hint {
|
|
192
|
+
margin: 0 0 0.5rem;
|
|
193
|
+
color: #64748b;
|
|
194
|
+
font-size: 0.75rem;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.memori-chat--llm-usage-badges {
|
|
198
|
+
display: flex;
|
|
199
|
+
flex-wrap: wrap;
|
|
200
|
+
margin-top: 0.5rem;
|
|
201
|
+
gap: 0.375rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.memori-chat--usage-badge {
|
|
205
|
+
display: inline-flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
padding: 0.35rem 0.5rem;
|
|
208
|
+
border: 1px solid #d4d8dd;
|
|
209
|
+
border-radius: 999px;
|
|
210
|
+
background: #fff;
|
|
211
|
+
color: #1f2937;
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
font-size: 0.75rem;
|
|
214
|
+
gap: 0.25rem;
|
|
215
|
+
line-height: 1;
|
|
216
|
+
transition: border-color 0.2s ease, background-color 0.2s ease;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.memori-chat--usage-badge:hover {
|
|
220
|
+
border-color: var(--memori-primary,#1890ff);
|
|
221
|
+
background-color: #f8fafc;
|
|
222
|
+
color: var(--memori-primary,#1890ff);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.memori-chat--usage-badge:focus-visible {
|
|
226
|
+
outline: 2px solid var(--memori-primary);
|
|
227
|
+
outline-offset: 2px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.memori-chat--usage-badge-value {
|
|
231
|
+
font-weight: 600;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.memori-chat--usage-modal .memori-modal--panel {
|
|
235
|
+
min-width: 20rem;
|
|
236
|
+
max-width: 30rem;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.memori-chat--usage-details {
|
|
240
|
+
margin: 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.memori-chat--usage-details div {
|
|
244
|
+
display: flex;
|
|
245
|
+
justify-content: space-between;
|
|
246
|
+
padding: 0.375rem 0;
|
|
247
|
+
gap: 1rem;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.memori-chat--usage-details dt {
|
|
251
|
+
color: #64748b;
|
|
252
|
+
font-size: 0.875rem;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.memori-chat--usage-details dd {
|
|
256
|
+
margin: 0;
|
|
257
|
+
color: #111827;
|
|
258
|
+
font-size: 0.875rem;
|
|
259
|
+
font-weight: 600;
|
|
260
|
+
text-align: right;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.memori-chat--usage-educational-content {
|
|
264
|
+
display: flex;
|
|
265
|
+
flex-direction: column;
|
|
266
|
+
padding-top: 0.25rem;
|
|
267
|
+
gap: 0.5rem;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.memori-chat--usage-metric-value {
|
|
271
|
+
display: block;
|
|
272
|
+
margin: 0;
|
|
273
|
+
color: #111827;
|
|
274
|
+
font-size: 1.125rem;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.memori-chat--usage-comparable {
|
|
278
|
+
margin: 0;
|
|
279
|
+
color: #374151;
|
|
280
|
+
font-size: 0.875rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.memori-chat--usage-educational-content p {
|
|
284
|
+
margin: 0;
|
|
184
285
|
}
|
|
@@ -100,6 +100,48 @@ WithHints.args = {
|
|
|
100
100
|
setSendOnEnter: () => {},
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
+
export const WithMessageConsumption = Template.bind({});
|
|
104
|
+
WithMessageConsumption.args = {
|
|
105
|
+
memori,
|
|
106
|
+
tenant,
|
|
107
|
+
sessionID,
|
|
108
|
+
dialogState,
|
|
109
|
+
layout: 'DEFAULT',
|
|
110
|
+
simulateUserPrompt: () => {},
|
|
111
|
+
sendMessage: (msg: string) => console.log(msg),
|
|
112
|
+
stopListening: () => {},
|
|
113
|
+
resetTranscript: () => {},
|
|
114
|
+
setAttachmentsMenuOpen: () => {},
|
|
115
|
+
setSendOnEnter: () => {},
|
|
116
|
+
showMessageConsumption: true,
|
|
117
|
+
history: [
|
|
118
|
+
...history.slice(0, Math.max(0, history.length - 2)),
|
|
119
|
+
// Add one AI message with llmUsage for the badges
|
|
120
|
+
{
|
|
121
|
+
...(history.find(m => !m.fromUser) ?? history[history.length - 1]),
|
|
122
|
+
fromUser: false,
|
|
123
|
+
text: 'Risposta di esempio con consumo LLM.',
|
|
124
|
+
llmUsage: {
|
|
125
|
+
provider: 'openai',
|
|
126
|
+
model: 'gpt-4.1-mini',
|
|
127
|
+
totalInputTokens: 123,
|
|
128
|
+
inputCacheReadTokens: 10,
|
|
129
|
+
inputCacheWriteTokens: 5,
|
|
130
|
+
outputTokens: 456,
|
|
131
|
+
durationMs: 2345,
|
|
132
|
+
energyImpact: {
|
|
133
|
+
energy: 0.00012,
|
|
134
|
+
energyUnit: 'kWh',
|
|
135
|
+
gwp: 0.00009,
|
|
136
|
+
gwpUnit: 'kgCO2eq',
|
|
137
|
+
wcf: 0.02,
|
|
138
|
+
wcfUnit: 'L',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
} as any,
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
|
|
103
145
|
export const WithArtifacts = Template.bind({});
|
|
104
146
|
WithArtifacts.args = {
|
|
105
147
|
memori,
|
|
@@ -225,6 +225,53 @@ it('renders Chat with context vars unchanged', () => {
|
|
|
225
225
|
expect(container).toMatchSnapshot();
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
+
it('renders Chat with message consumption unchanged', () => {
|
|
229
|
+
const { container } = render(
|
|
230
|
+
<ArtifactProvider>
|
|
231
|
+
<Chat
|
|
232
|
+
memori={memori}
|
|
233
|
+
tenant={tenant}
|
|
234
|
+
dialogState={dialogState}
|
|
235
|
+
layout="DEFAULT"
|
|
236
|
+
client={client}
|
|
237
|
+
history={[
|
|
238
|
+
...(history as any),
|
|
239
|
+
{
|
|
240
|
+
text: 'AI message with usage',
|
|
241
|
+
fromUser: false,
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
llmUsage: {
|
|
244
|
+
provider: 'openai',
|
|
245
|
+
model: 'gpt-4.1-mini',
|
|
246
|
+
totalInputTokens: 10,
|
|
247
|
+
outputTokens: 20,
|
|
248
|
+
durationMs: 1000,
|
|
249
|
+
energyImpact: { energy: 0.001, energyUnit: 'kWh' },
|
|
250
|
+
},
|
|
251
|
+
} as any,
|
|
252
|
+
]}
|
|
253
|
+
pushMessage={jest.fn()}
|
|
254
|
+
sessionID={sessionID}
|
|
255
|
+
simulateUserPrompt={jest.fn()}
|
|
256
|
+
setAttachmentsMenuOpen={jest.fn()}
|
|
257
|
+
setSendOnEnter={jest.fn()}
|
|
258
|
+
userMessage=""
|
|
259
|
+
onChangeUserMessage={jest.fn()}
|
|
260
|
+
sendMessage={jest.fn()}
|
|
261
|
+
stopListening={jest.fn()}
|
|
262
|
+
isPlayingAudio={false}
|
|
263
|
+
stopAudio={jest.fn()}
|
|
264
|
+
showMicrophone={false}
|
|
265
|
+
listening={false}
|
|
266
|
+
startListening={jest.fn()}
|
|
267
|
+
setEnableFocusChatInput={jest.fn()}
|
|
268
|
+
showMessageConsumption
|
|
269
|
+
/>
|
|
270
|
+
</ArtifactProvider>
|
|
271
|
+
);
|
|
272
|
+
expect(container).toMatchSnapshot();
|
|
273
|
+
});
|
|
274
|
+
|
|
228
275
|
it('renders Chat with user unchanged', () => {
|
|
229
276
|
const { container } = render(
|
|
230
277
|
<ArtifactProvider
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
memo,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
2
9
|
import cx from 'classnames';
|
|
3
10
|
import {
|
|
4
11
|
DialogState,
|
|
@@ -23,7 +30,21 @@ import { boardOfExpertsLoadingSentences } from '../../helpers/constants';
|
|
|
23
30
|
import ArtifactHandler from '../MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler';
|
|
24
31
|
import { DocumentIcon } from '../icons/Document';
|
|
25
32
|
import { useTranslation } from 'react-i18next';
|
|
26
|
-
import { maxDocumentsPerMessage,
|
|
33
|
+
import { maxDocumentsPerMessage, maxDocumentContentLength, pasteAsCardLineThreshold, pasteAsCardCharThreshold } from '../../helpers/constants';
|
|
34
|
+
import Modal from '../ui/Modal';
|
|
35
|
+
import Tooltip from '../ui/Tooltip';
|
|
36
|
+
import {
|
|
37
|
+
BADGE_EMOJI,
|
|
38
|
+
buildLlmUsageHtml,
|
|
39
|
+
formatDuration,
|
|
40
|
+
formatImpactWithApiUnit,
|
|
41
|
+
formatIntegerValue,
|
|
42
|
+
getImpactComparison,
|
|
43
|
+
getMetricValue,
|
|
44
|
+
LlmUsageLabels,
|
|
45
|
+
LlmUsageOnLine,
|
|
46
|
+
UsageBadgeType,
|
|
47
|
+
} from '../../helpers/llmUsage';
|
|
27
48
|
export interface Props {
|
|
28
49
|
memori: Memori;
|
|
29
50
|
tenant?: Tenant;
|
|
@@ -46,6 +67,7 @@ export interface Props {
|
|
|
46
67
|
showTranslationOriginal?: boolean;
|
|
47
68
|
showWhyThisAnswer?: boolean;
|
|
48
69
|
showReasoning?: boolean;
|
|
70
|
+
showMessageConsumption?: boolean;
|
|
49
71
|
client?: ReturnType<typeof memoriApiClient>;
|
|
50
72
|
preview?: boolean;
|
|
51
73
|
microphoneMode?: 'CONTINUOUS' | 'HOLD_TO_TALK';
|
|
@@ -83,6 +105,12 @@ export interface Props {
|
|
|
83
105
|
|
|
84
106
|
}
|
|
85
107
|
|
|
108
|
+
type MessageWithLlmUsage = Message & { llmUsage?: LlmUsageOnLine };
|
|
109
|
+
interface UsageBadgeModalState {
|
|
110
|
+
type: UsageBadgeType;
|
|
111
|
+
usage: LlmUsageOnLine;
|
|
112
|
+
}
|
|
113
|
+
|
|
86
114
|
const Chat: React.FC<Props> = ({
|
|
87
115
|
memori,
|
|
88
116
|
tenant,
|
|
@@ -105,6 +133,7 @@ const Chat: React.FC<Props> = ({
|
|
|
105
133
|
showCopyButton = true,
|
|
106
134
|
showTranslationOriginal = false,
|
|
107
135
|
showReasoning = false,
|
|
136
|
+
showMessageConsumption = false,
|
|
108
137
|
preview = false,
|
|
109
138
|
instruct = false,
|
|
110
139
|
showInputs = true,
|
|
@@ -138,7 +167,50 @@ const Chat: React.FC<Props> = ({
|
|
|
138
167
|
|
|
139
168
|
const [isTextareaExpanded, setIsTextareaExpanded] = useState(false);
|
|
140
169
|
const [isDragging, setIsDragging] = useState(false);
|
|
170
|
+
const [activeUsageBadge, setActiveUsageBadge] =
|
|
171
|
+
useState<UsageBadgeModalState | null>(null);
|
|
172
|
+
const chatWrapperRef = useRef<HTMLDivElement>(null);
|
|
141
173
|
const { t } = useTranslation();
|
|
174
|
+
const locale = (translateTo || memori.culture || 'it-IT').replace('_', '-');
|
|
175
|
+
|
|
176
|
+
const llmUsageLabels = useMemo<LlmUsageLabels>(
|
|
177
|
+
() => ({
|
|
178
|
+
llm: t('chatLogs.llm', { defaultValue: 'LLM' }),
|
|
179
|
+
model: t('chatLogs.model', { defaultValue: 'Model' }),
|
|
180
|
+
provider: t('chatLogs.provider', { defaultValue: 'Provider' }),
|
|
181
|
+
tokens: t('chatLogs.tokens', { defaultValue: 'Tokens' }),
|
|
182
|
+
input: t('chatLogs.input', { defaultValue: 'Input' }),
|
|
183
|
+
output: t('chatLogs.output', { defaultValue: 'Output' }),
|
|
184
|
+
cacheRead: t('chatLogs.cacheRead', { defaultValue: 'Cache read' }),
|
|
185
|
+
cacheWrite: t('chatLogs.cacheWrite', { defaultValue: 'Cache write' }),
|
|
186
|
+
duration: t('chatLogs.duration', { defaultValue: 'Duration' }),
|
|
187
|
+
energy: t('chatLogs.energy', { defaultValue: 'Energy' }),
|
|
188
|
+
co2: t('chatLogs.co2', { defaultValue: 'CO2' }),
|
|
189
|
+
water: t('chatLogs.water', { defaultValue: 'Water' }),
|
|
190
|
+
usageBadgesHint: t('chatLogs.usageBadgesHint', {
|
|
191
|
+
defaultValue: 'Click one of these buttons to show more information',
|
|
192
|
+
}),
|
|
193
|
+
}),
|
|
194
|
+
[t],
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const usageHtmlByIndex = useMemo(
|
|
198
|
+
() =>
|
|
199
|
+
history.map((message, index) => {
|
|
200
|
+
const messageWithUsage = message as MessageWithLlmUsage;
|
|
201
|
+
return showMessageConsumption &&
|
|
202
|
+
!message.fromUser &&
|
|
203
|
+
messageWithUsage.llmUsage
|
|
204
|
+
? buildLlmUsageHtml(
|
|
205
|
+
messageWithUsage.llmUsage,
|
|
206
|
+
llmUsageLabels,
|
|
207
|
+
index,
|
|
208
|
+
locale,
|
|
209
|
+
)
|
|
210
|
+
: '';
|
|
211
|
+
}),
|
|
212
|
+
[history, llmUsageLabels, locale, showMessageConsumption],
|
|
213
|
+
);
|
|
142
214
|
const scrollToBottom = useCallback(() => {
|
|
143
215
|
if (isHistoryView) return;
|
|
144
216
|
setTimeout(() => {
|
|
@@ -269,8 +341,40 @@ const Chat: React.FC<Props> = ({
|
|
|
269
341
|
};
|
|
270
342
|
}, [showUpload]);
|
|
271
343
|
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
const wrapper = chatWrapperRef.current;
|
|
346
|
+
if (!wrapper || !showMessageConsumption) return;
|
|
347
|
+
|
|
348
|
+
const handleUsageBadgeClick = (event: MouseEvent) => {
|
|
349
|
+
const target = event.target as HTMLElement | null;
|
|
350
|
+
const button = target?.closest<HTMLElement>(
|
|
351
|
+
'[data-llm-badge-type][data-line-index]',
|
|
352
|
+
);
|
|
353
|
+
if (!button) return;
|
|
354
|
+
|
|
355
|
+
const lineIndex = Number(button.dataset.lineIndex);
|
|
356
|
+
const badgeType = button.dataset.llmBadgeType as UsageBadgeType | undefined;
|
|
357
|
+
if (!Number.isInteger(lineIndex) || !badgeType) return;
|
|
358
|
+
|
|
359
|
+
const line = (history?.[lineIndex] as MessageWithLlmUsage) ?? null;
|
|
360
|
+
if (!line?.llmUsage) return;
|
|
361
|
+
|
|
362
|
+
event.preventDefault();
|
|
363
|
+
setActiveUsageBadge({
|
|
364
|
+
type: badgeType,
|
|
365
|
+
usage: line.llmUsage,
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
wrapper.addEventListener('click', handleUsageBadgeClick);
|
|
370
|
+
return () => {
|
|
371
|
+
wrapper.removeEventListener('click', handleUsageBadgeClick);
|
|
372
|
+
};
|
|
373
|
+
}, [history, showMessageConsumption]);
|
|
374
|
+
|
|
272
375
|
return (
|
|
273
376
|
<div
|
|
377
|
+
ref={chatWrapperRef}
|
|
274
378
|
className={cx('memori-chat--wrapper', {
|
|
275
379
|
'memori-chat-wrapper--translate': translateTo,
|
|
276
380
|
'memori-chat-wrapper--expanded': isTextareaExpanded,
|
|
@@ -423,6 +527,7 @@ const Chat: React.FC<Props> = ({
|
|
|
423
527
|
useMathFormatting={useMathFormatting}
|
|
424
528
|
showFunctionCache={showFunctionCache}
|
|
425
529
|
showReasoning={showReasoning}
|
|
530
|
+
usageHtml={usageHtmlByIndex[index]}
|
|
426
531
|
/>
|
|
427
532
|
|
|
428
533
|
{showDates && !!message.timestamp && (
|
|
@@ -552,9 +657,7 @@ const Chat: React.FC<Props> = ({
|
|
|
552
657
|
isPlayingAudio={isPlayingAudio}
|
|
553
658
|
showMicrophone={showMicrophone}
|
|
554
659
|
memoriID={memori?.memoriID}
|
|
555
|
-
maxTotalMessagePayload={
|
|
556
|
-
maxTotalMessagePayload ?? maxTotalMessagePayloadDefault
|
|
557
|
-
}
|
|
660
|
+
maxTotalMessagePayload={maxTotalMessagePayload}
|
|
558
661
|
maxTextareaCharacters={maxTextareaCharacters}
|
|
559
662
|
maxDocumentsPerMessage={maxDocumentsPerMessage}
|
|
560
663
|
maxDocumentContentLength={maxDocumentContentLength}
|
|
@@ -562,6 +665,136 @@ const Chat: React.FC<Props> = ({
|
|
|
562
665
|
pasteAsCardCharThreshold={pasteAsCardCharThreshold}
|
|
563
666
|
/>
|
|
564
667
|
)}
|
|
668
|
+
|
|
669
|
+
<Modal
|
|
670
|
+
open={!!activeUsageBadge}
|
|
671
|
+
onClose={() => setActiveUsageBadge(null)}
|
|
672
|
+
title={
|
|
673
|
+
activeUsageBadge?.type
|
|
674
|
+
? `${BADGE_EMOJI[activeUsageBadge.type]} ${
|
|
675
|
+
llmUsageLabels[activeUsageBadge.type]
|
|
676
|
+
}`
|
|
677
|
+
: undefined
|
|
678
|
+
}
|
|
679
|
+
className="memori-chat--usage-modal"
|
|
680
|
+
>
|
|
681
|
+
{activeUsageBadge?.type === 'llm' && (
|
|
682
|
+
<dl className="memori-chat--usage-details">
|
|
683
|
+
<div>
|
|
684
|
+
<dt>{llmUsageLabels.provider}</dt>
|
|
685
|
+
<dd>{activeUsageBadge.usage.provider ?? '—'}</dd>
|
|
686
|
+
</div>
|
|
687
|
+
<div>
|
|
688
|
+
<dt>{llmUsageLabels.model}</dt>
|
|
689
|
+
<dd>{activeUsageBadge.usage.model ?? '—'}</dd>
|
|
690
|
+
</div>
|
|
691
|
+
<div>
|
|
692
|
+
<dt>
|
|
693
|
+
{llmUsageLabels.tokens} {llmUsageLabels.input}
|
|
694
|
+
</dt>
|
|
695
|
+
<dd>
|
|
696
|
+
{formatIntegerValue(
|
|
697
|
+
activeUsageBadge.usage.totalInputTokens ?? 0,
|
|
698
|
+
locale,
|
|
699
|
+
)}
|
|
700
|
+
</dd>
|
|
701
|
+
</div>
|
|
702
|
+
<div>
|
|
703
|
+
<dt>
|
|
704
|
+
{llmUsageLabels.tokens} {llmUsageLabels.output}
|
|
705
|
+
</dt>
|
|
706
|
+
<dd>
|
|
707
|
+
{formatIntegerValue(activeUsageBadge.usage.outputTokens ?? 0, locale)}
|
|
708
|
+
</dd>
|
|
709
|
+
</div>
|
|
710
|
+
</dl>
|
|
711
|
+
)}
|
|
712
|
+
|
|
713
|
+
{activeUsageBadge?.type === 'energy' && (
|
|
714
|
+
<div className="memori-chat--usage-educational-content">
|
|
715
|
+
<strong className="memori-chat--usage-metric-value">
|
|
716
|
+
{formatImpactWithApiUnit(
|
|
717
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.energy) ?? 0,
|
|
718
|
+
activeUsageBadge.usage.energyImpact?.energyUnit,
|
|
719
|
+
'kWh',
|
|
720
|
+
'energy',
|
|
721
|
+
locale,
|
|
722
|
+
)}
|
|
723
|
+
</strong>
|
|
724
|
+
<Tooltip
|
|
725
|
+
content={t('chatLogs.approximateValuesTooltip', {
|
|
726
|
+
defaultValue: 'These values are approximate.',
|
|
727
|
+
})}
|
|
728
|
+
>
|
|
729
|
+
<p className="memori-chat--usage-comparable">
|
|
730
|
+
{getImpactComparison(
|
|
731
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.energy) ?? 0,
|
|
732
|
+
'energy',
|
|
733
|
+
locale,
|
|
734
|
+
t,
|
|
735
|
+
)}
|
|
736
|
+
</p>
|
|
737
|
+
</Tooltip>
|
|
738
|
+
<p>{t('chatLogs.energyImpactDescription')}</p>
|
|
739
|
+
</div>
|
|
740
|
+
)}
|
|
741
|
+
{activeUsageBadge?.type === 'co2' && (
|
|
742
|
+
<div className="memori-chat--usage-educational-content">
|
|
743
|
+
<strong className="memori-chat--usage-metric-value">
|
|
744
|
+
{formatImpactWithApiUnit(
|
|
745
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.gwp) ?? 0,
|
|
746
|
+
activeUsageBadge.usage.energyImpact?.gwpUnit,
|
|
747
|
+
'kgCO2eq',
|
|
748
|
+
'co2',
|
|
749
|
+
locale,
|
|
750
|
+
)}
|
|
751
|
+
</strong>
|
|
752
|
+
<Tooltip
|
|
753
|
+
content={t('chatLogs.approximateValuesTooltip', {
|
|
754
|
+
defaultValue: 'These values are approximate.',
|
|
755
|
+
})}
|
|
756
|
+
>
|
|
757
|
+
<p className="memori-chat--usage-comparable">
|
|
758
|
+
{getImpactComparison(
|
|
759
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.gwp) ?? 0,
|
|
760
|
+
'co2',
|
|
761
|
+
locale,
|
|
762
|
+
t,
|
|
763
|
+
)}
|
|
764
|
+
</p>
|
|
765
|
+
</Tooltip>
|
|
766
|
+
<p>{t('chatLogs.co2ImpactDescription')}</p>
|
|
767
|
+
</div>
|
|
768
|
+
)}
|
|
769
|
+
{activeUsageBadge?.type === 'water' && (
|
|
770
|
+
<div className="memori-chat--usage-educational-content">
|
|
771
|
+
<strong className="memori-chat--usage-metric-value">
|
|
772
|
+
{formatImpactWithApiUnit(
|
|
773
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.wcf) ?? 0,
|
|
774
|
+
activeUsageBadge.usage.energyImpact?.wcfUnit,
|
|
775
|
+
'L',
|
|
776
|
+
'water',
|
|
777
|
+
locale,
|
|
778
|
+
)}
|
|
779
|
+
</strong>
|
|
780
|
+
<Tooltip
|
|
781
|
+
content={t('chatLogs.approximateValuesTooltip', {
|
|
782
|
+
defaultValue: 'These values are approximate.',
|
|
783
|
+
})}
|
|
784
|
+
>
|
|
785
|
+
<p className="memori-chat--usage-comparable">
|
|
786
|
+
{getImpactComparison(
|
|
787
|
+
getMetricValue(activeUsageBadge.usage.energyImpact?.wcf) ?? 0,
|
|
788
|
+
'water',
|
|
789
|
+
locale,
|
|
790
|
+
t,
|
|
791
|
+
)}
|
|
792
|
+
</p>
|
|
793
|
+
</Tooltip>
|
|
794
|
+
<p>{t('chatLogs.waterImpactDescription')}</p>
|
|
795
|
+
</div>
|
|
796
|
+
)}
|
|
797
|
+
</Modal>
|
|
565
798
|
</div>
|
|
566
799
|
);
|
|
567
800
|
};
|