cozy-search 2.2.18 → 2.2.20

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.
@@ -102,7 +102,7 @@ var ConversationListItem = function ConversationListItem(_ref) {
102
102
  }, (0, _helpers.getNameOfConversation)(conversation)),
103
103
  secondary: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", {
104
104
  className: (0, _classnames.default)('u-db u-ellipsis', styles['conversation-list-item-subtitle'])
105
- }, (0, _helpers.getDescriptionOfConversation)(conversation)), /*#__PURE__*/_react.default.createElement("span", {
105
+ }, (0, _helpers.getDescriptionOfConversation)(conversation, t)), /*#__PURE__*/_react.default.createElement("span", {
106
106
  className: (0, _classnames.default)('u-flex u-flex-items-center', styles['conversation-list-item-subtitle'], styles['conversation-list-item-meta'])
107
107
  }, /*#__PURE__*/_react.default.createElement(_AssistantAvatar.default, {
108
108
  assistant: conversation.assistant,
@@ -106,7 +106,7 @@ var ConversationListItemWider = function ConversationListItemWider(_ref) {
106
106
  secondary: /*#__PURE__*/_react.default.createElement(_Typography.default, {
107
107
  variant: "h6",
108
108
  className: "u-coolGrey"
109
- }, (0, _helpers.getDescriptionOfConversation)(conversation))
109
+ }, (0, _helpers.getDescriptionOfConversation)(conversation, t))
110
110
  }), /*#__PURE__*/_react.default.createElement(_Typography.default, {
111
111
  className: "u-miw-4 u-fz-xsmall u-ta-right u-dn-s"
112
112
  }, (0, _helpers.formatConversationDate)((_conversation$cozyMet = conversation.cozyMetadata) === null || _conversation$cozyMet === void 0 ? void 0 : _conversation$cozyMet.updatedAt, t, lang)));
@@ -63,13 +63,13 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
63
63
 
64
64
  var log = (0, _cozyMinilog.default)('🔍 [CozyAssistantRuntimeProvider]');
65
65
 
66
- var convertMessagesToThreadMessages = function convertMessagesToThreadMessages(messages) {
66
+ var convertMessagesToThreadMessages = function convertMessagesToThreadMessages(messages, t) {
67
67
  if (!messages) return [];
68
68
  return messages.map(function (msg, idx) {
69
69
  return {
70
70
  id: msg.id || "msg-".concat(idx),
71
71
  role: msg.role,
72
- content: (0, _helpers.sanitizeChatContent)(msg.content),
72
+ content: (0, _helpers.formatAnswer)(msg, t),
73
73
  metadata: msg.role === 'assistant' && msg.sources ? {
74
74
  custom: {
75
75
  sources: msg.sources
@@ -85,6 +85,9 @@ var ConversationLoader = function ConversationLoader(_ref) {
85
85
  var children = _ref.children,
86
86
  conversationId = _ref.conversationId;
87
87
 
88
+ var _useI18n = (0, _twakeI18n.useI18n)(),
89
+ t = _useI18n.t;
90
+
88
91
  var _useAssistant = (0, _AssistantProvider.useAssistant)(),
89
92
  setSelectedAssistantId = _useAssistant.setSelectedAssistantId;
90
93
 
@@ -93,8 +96,8 @@ var ConversationLoader = function ConversationLoader(_ref) {
93
96
  var conversation = queryResult.data;
94
97
  var isLoading = (0, _cozyClient.isQueryLoading)(queryResult);
95
98
  var initialMessages = (0, _react2.useMemo)(function () {
96
- return convertMessagesToThreadMessages(conversation === null || conversation === void 0 ? void 0 : conversation.messages);
97
- }, [conversation === null || conversation === void 0 ? void 0 : conversation.messages]);
99
+ return convertMessagesToThreadMessages(conversation === null || conversation === void 0 ? void 0 : conversation.messages, t);
100
+ }, [conversation === null || conversation === void 0 ? void 0 : conversation.messages, t]);
98
101
  (0, _react2.useEffect)(function () {
99
102
  var _conversation$relatio, _conversation$relatio2, _conversation$relatio3;
100
103
 
@@ -121,8 +124,8 @@ var CozyAssistantRuntimeProviderInner = function CozyAssistantRuntimeProviderInn
121
124
  conversationId = _ref2.conversationId,
122
125
  initialMessages = _ref2.initialMessages;
123
126
 
124
- var _useI18n = (0, _twakeI18n.useI18n)(),
125
- t = _useI18n.t;
127
+ var _useI18n2 = (0, _twakeI18n.useI18n)(),
128
+ t = _useI18n2.t;
126
129
 
127
130
  var client = (0, _cozyClient.useClient)();
128
131
  var streamBridgeRef = (0, _react2.useRef)(new _StreamBridge.StreamBridge());
@@ -332,8 +335,8 @@ var CozyAssistantErrorBoundary = /*#__PURE__*/function (_React$Component) {
332
335
  }(_react2.default.Component);
333
336
 
334
337
  var CozyAssistantRuntimeProviderWithErrorBoundary = function CozyAssistantRuntimeProviderWithErrorBoundary(props) {
335
- var _useI18n2 = (0, _twakeI18n.useI18n)(),
336
- t = _useI18n2.t;
338
+ var _useI18n3 = (0, _twakeI18n.useI18n)(),
339
+ t = _useI18n3.t;
337
340
 
338
341
  return /*#__PURE__*/_react2.default.createElement(CozyAssistantErrorBoundary, {
339
342
  t: t
@@ -21,6 +21,8 @@ var _CozyTheme = require("cozy-ui-plus/dist/providers/CozyTheme");
21
21
 
22
22
  var _twakeI18n = require("twake-i18n");
23
23
 
24
+ var _helpers = require("./helpers");
25
+
24
26
  var styles = {
25
27
  "BasicInfoStep": "styles__BasicInfoStep___2r0eM",
26
28
  "avatar-container": "styles__avatar-container___kJ_64",
@@ -498,7 +500,7 @@ var Provider = function Provider(_ref) {
498
500
  }, /*#__PURE__*/_react.default.createElement(_Typography.default, {
499
501
  variant: "body1",
500
502
  className: "u-flex u-flex-justify-between u-flex-items-start"
501
- }, provider.id === 'custom' ? t(provider.name) : provider.name), /*#__PURE__*/_react.default.createElement(_Typography.default, {
503
+ }, (0, _helpers.getProviderName)(provider, t)), /*#__PURE__*/_react.default.createElement(_Typography.default, {
502
504
  variant: "body2",
503
505
  className: "u-mt-half u-c-text-secondary"
504
506
  }, t(provider.description))), isSelected && /*#__PURE__*/_react.default.createElement(_twakeIcons.Icon, {
@@ -1,2 +1,4 @@
1
1
  export function getSelectedProviderById(providerId: string): object;
2
+ export function getProviderName(provider: object, t: Function): string;
3
+ export function getProviderNameById(providerId: string, t: Function): string;
2
4
  export function checkIfModelUnsupported(provider: object, model: string): boolean;
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.getSelectedProviderById = exports.checkIfModelUnsupported = void 0;
8
+ exports.getSelectedProviderById = exports.getProviderNameById = exports.getProviderName = exports.checkIfModelUnsupported = void 0;
9
9
 
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
 
@@ -66,6 +66,35 @@ var getSelectedProviderById = function getSelectedProviderById(providerId) {
66
66
  return provider.id === 'custom';
67
67
  }));
68
68
  };
69
+ /**
70
+ * Get the display name of a provider.
71
+ * The custom provider has no name of its own, only a translation key.
72
+ * @param {object} provider - The provider object.
73
+ * @param {Function} t - The translation function.
74
+ * @returns {string} The provider display name.
75
+ */
76
+
77
+
78
+ exports.getSelectedProviderById = getSelectedProviderById;
79
+
80
+ var getProviderName = function getProviderName(provider, t) {
81
+ return provider.id === 'custom' ? t(provider.name) : provider.name;
82
+ };
83
+ /**
84
+ * Get the display name of a provider from its ID, used to name its account.
85
+ * Resolves against providers.json rather than against a selected provider,
86
+ * whose name is overridden by the dialogs.
87
+ * @param {string} providerId - The ID of the provider.
88
+ * @param {Function} t - The translation function.
89
+ * @returns {string} The provider display name.
90
+ */
91
+
92
+
93
+ exports.getProviderName = getProviderName;
94
+
95
+ var getProviderNameById = function getProviderNameById(providerId, t) {
96
+ return getProviderName(getSelectedProviderById(providerId), t);
97
+ };
69
98
  /**
70
99
  * Check whether the given model is unsupported by the provider.
71
100
  * @param {object} provider - The provider object.
@@ -74,7 +103,7 @@ var getSelectedProviderById = function getSelectedProviderById(providerId) {
74
103
  */
75
104
 
76
105
 
77
- exports.getSelectedProviderById = getSelectedProviderById;
106
+ exports.getProviderNameById = getProviderNameById;
78
107
 
79
108
  var checkIfModelUnsupported = function checkIfModelUnsupported(provider, model) {
80
109
  var unsupportedModels = (provider === null || provider === void 0 ? void 0 : provider.unsupportedModels) || [];
@@ -131,7 +131,8 @@ var CreateAssistantDialog = function CreateAssistantDialog(_ref) {
131
131
  model: formData.model,
132
132
  apiKey: formData.apiKey,
133
133
  baseUrl: formData.baseUrl,
134
- providerId: selectedProvider.id
134
+ providerId: selectedProvider.id,
135
+ providerName: (0, _helpers.getProviderNameById)(selectedProvider.id, t)
135
136
  });
136
137
 
137
138
  case 2:
@@ -116,9 +116,9 @@ var EditAssistantDialog = function EditAssistantDialog(_ref) {
116
116
 
117
117
  var fetchAssistant = /*#__PURE__*/function () {
118
118
  var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
119
- var _assistant$relationsh, _assistant$relationsh2, _assistant$relationsh3, _assistant$relationsh4, _provider$auth, _provider$data, _provider$auth2, _provider$auth3, _provider$auth4, _provider$data2, _provider$auth5;
119
+ var _assistant$relationsh, _assistant$relationsh2, _assistant$relationsh3, _assistant$relationsh4, _provider$data, _provider$auth, _provider$data2, _provider$auth2, _provider$auth3, _provider$data3, _provider$auth4;
120
120
 
121
- var response, assistant, provider, providerId, selectProviderDefault;
121
+ var response, assistant, provider, providerId, model, selectProviderDefault;
122
122
  return _regenerator.default.wrap(function _callee$(_context) {
123
123
  while (1) {
124
124
  switch (_context.prev = _context.next) {
@@ -130,13 +130,16 @@ var EditAssistantDialog = function EditAssistantDialog(_ref) {
130
130
  response = _context.sent;
131
131
  assistant = response.data;
132
132
  provider = response.included[0];
133
- providerId = assistant === null || assistant === void 0 ? void 0 : (_assistant$relationsh = assistant.relationships) === null || _assistant$relationsh === void 0 ? void 0 : (_assistant$relationsh2 = _assistant$relationsh.provider) === null || _assistant$relationsh2 === void 0 ? void 0 : (_assistant$relationsh3 = _assistant$relationsh2.data) === null || _assistant$relationsh3 === void 0 ? void 0 : (_assistant$relationsh4 = _assistant$relationsh3.metadata) === null || _assistant$relationsh4 === void 0 ? void 0 : _assistant$relationsh4.providerId;
133
+ providerId = assistant === null || assistant === void 0 ? void 0 : (_assistant$relationsh = assistant.relationships) === null || _assistant$relationsh === void 0 ? void 0 : (_assistant$relationsh2 = _assistant$relationsh.provider) === null || _assistant$relationsh2 === void 0 ? void 0 : (_assistant$relationsh3 = _assistant$relationsh2.data) === null || _assistant$relationsh3 === void 0 ? void 0 : (_assistant$relationsh4 = _assistant$relationsh3.metadata) === null || _assistant$relationsh4 === void 0 ? void 0 : _assistant$relationsh4.providerId; // `auth.login` held the model before it moved to `data.model`: kept as a
134
+ // fallback for accounts that have not been rewritten yet.
135
+
136
+ model = (provider === null || provider === void 0 ? void 0 : (_provider$data = provider.data) === null || _provider$data === void 0 ? void 0 : _provider$data.model) || (provider === null || provider === void 0 ? void 0 : (_provider$auth = provider.auth) === null || _provider$auth === void 0 ? void 0 : _provider$auth.login);
134
137
  setFormData({
135
138
  name: assistant.name || '',
136
139
  description: assistant.prompt || '',
137
140
  icon: assistant.icon || '',
138
- model: (provider === null || provider === void 0 ? void 0 : (_provider$auth = provider.auth) === null || _provider$auth === void 0 ? void 0 : _provider$auth.login) || '',
139
- baseUrl: (provider === null || provider === void 0 ? void 0 : (_provider$data = provider.data) === null || _provider$data === void 0 ? void 0 : _provider$data.baseUrl) || '',
141
+ model: model || '',
142
+ baseUrl: (provider === null || provider === void 0 ? void 0 : (_provider$data2 = provider.data) === null || _provider$data2 === void 0 ? void 0 : _provider$data2.baseUrl) || '',
140
143
  apiKey: (provider === null || provider === void 0 ? void 0 : (_provider$auth2 = provider.auth) === null || _provider$auth2 === void 0 ? void 0 : _provider$auth2.apiKey) || '',
141
144
  encryptedApiKey: (provider === null || provider === void 0 ? void 0 : (_provider$auth3 = provider.auth) === null || _provider$auth3 === void 0 ? void 0 : _provider$auth3.credentials_encrypted) || '',
142
145
  providerId: providerId,
@@ -144,12 +147,12 @@ var EditAssistantDialog = function EditAssistantDialog(_ref) {
144
147
  });
145
148
  selectProviderDefault = (0, _helpers.getSelectedProviderById)(providerId);
146
149
  setSelectedProvider(_objectSpread(_objectSpread({}, selectProviderDefault), {}, {
147
- model: provider === null || provider === void 0 ? void 0 : (_provider$auth4 = provider.auth) === null || _provider$auth4 === void 0 ? void 0 : _provider$auth4.login,
148
- baseUrl: provider === null || provider === void 0 ? void 0 : (_provider$data2 = provider.data) === null || _provider$data2 === void 0 ? void 0 : _provider$data2.baseUrl,
149
- name: selectProviderDefault.id === 'custom' ? provider === null || provider === void 0 ? void 0 : (_provider$auth5 = provider.auth) === null || _provider$auth5 === void 0 ? void 0 : _provider$auth5.login : selectProviderDefault.name
150
+ model: model,
151
+ baseUrl: provider === null || provider === void 0 ? void 0 : (_provider$data3 = provider.data) === null || _provider$data3 === void 0 ? void 0 : _provider$data3.baseUrl,
152
+ name: selectProviderDefault.id === 'custom' ? (provider === null || provider === void 0 ? void 0 : (_provider$auth4 = provider.auth) === null || _provider$auth4 === void 0 ? void 0 : _provider$auth4.accountName) || model : selectProviderDefault.name
150
153
  }));
151
154
 
152
- case 9:
155
+ case 10:
153
156
  case "end":
154
157
  return _context.stop();
155
158
  }
@@ -187,7 +190,8 @@ var EditAssistantDialog = function EditAssistantDialog(_ref) {
187
190
  model: formData.model,
188
191
  apiKey: formData.apiKey,
189
192
  baseUrl: formData.baseUrl,
190
- providerId: selectedProvider.id
193
+ providerId: selectedProvider.id,
194
+ providerName: (0, _helpers.getProviderNameById)(selectedProvider.id, t)
191
195
  });
192
196
 
193
197
  case 2:
@@ -66,7 +66,7 @@ var createCozyRealtimeChatAdapter = function createCozyRealtimeChatAdapter(optio
66
66
  var messages = _ref.messages,
67
67
  abortSignal = _ref.abortSignal;
68
68
  return (0, _wrapAsyncGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
69
- var client, conversationId, assistantId, websearchEnabled, streamBridge, userQuery, stream, fullText, wasAborted, _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, chunk, sanitizedText, finalText, sources;
69
+ var client, conversationId, assistantId, websearchEnabled, streamBridge, userQuery, stream, fullText, wasAborted, _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, chunk, sanitizedText, sources;
70
70
 
71
71
  return _regenerator.default.wrap(function _callee$(_context) {
72
72
  while (1) {
@@ -133,25 +133,24 @@ var createCozyRealtimeChatAdapter = function createCozyRealtimeChatAdapter(optio
133
133
 
134
134
  case 23:
135
135
  if (!(_iteratorAbruptCompletion = !(_step = _context.sent).done)) {
136
- _context.next = 36;
136
+ _context.next = 35;
137
137
  break;
138
138
  }
139
139
 
140
140
  chunk = _step.value;
141
141
 
142
142
  if (!(abortSignal !== null && abortSignal !== void 0 && abortSignal.aborted)) {
143
- _context.next = 29;
143
+ _context.next = 28;
144
144
  break;
145
145
  }
146
146
 
147
147
  wasAborted = true;
148
- streamBridge.cleanup(conversationId);
149
- return _context.abrupt("break", 36);
148
+ return _context.abrupt("break", 35);
150
149
 
151
- case 29:
150
+ case 28:
152
151
  fullText += chunk;
153
152
  sanitizedText = (0, _helpers.sanitizeChatContent)(fullText);
154
- _context.next = 33;
153
+ _context.next = 32;
155
154
  return {
156
155
  content: [{
157
156
  type: 'text',
@@ -162,62 +161,64 @@ var createCozyRealtimeChatAdapter = function createCozyRealtimeChatAdapter(optio
162
161
  }
163
162
  };
164
163
 
165
- case 33:
164
+ case 32:
166
165
  _iteratorAbruptCompletion = false;
167
166
  _context.next = 21;
168
167
  break;
169
168
 
170
- case 36:
171
- _context.next = 42;
169
+ case 35:
170
+ _context.next = 41;
172
171
  break;
173
172
 
174
- case 38:
175
- _context.prev = 38;
173
+ case 37:
174
+ _context.prev = 37;
176
175
  _context.t0 = _context["catch"](19);
177
176
  _didIteratorError = true;
178
177
  _iteratorError = _context.t0;
179
178
 
180
- case 42:
179
+ case 41:
180
+ _context.prev = 41;
181
181
  _context.prev = 42;
182
- _context.prev = 43;
183
182
 
184
183
  if (!(_iteratorAbruptCompletion && _iterator.return != null)) {
185
- _context.next = 47;
184
+ _context.next = 46;
186
185
  break;
187
186
  }
188
187
 
189
- _context.next = 47;
188
+ _context.next = 46;
190
189
  return (0, _awaitAsyncGenerator2.default)(_iterator.return());
191
190
 
192
- case 47:
193
- _context.prev = 47;
191
+ case 46:
192
+ _context.prev = 46;
194
193
 
195
194
  if (!_didIteratorError) {
196
- _context.next = 50;
195
+ _context.next = 49;
197
196
  break;
198
197
  }
199
198
 
200
199
  throw _iteratorError;
201
200
 
201
+ case 49:
202
+ return _context.finish(46);
203
+
202
204
  case 50:
203
- return _context.finish(47);
205
+ return _context.finish(41);
204
206
 
205
207
  case 51:
206
- return _context.finish(42);
207
-
208
- case 52:
209
- if (wasAborted) {
210
- _context.next = 58;
208
+ if (!(!wasAborted && !(abortSignal !== null && abortSignal !== void 0 && abortSignal.aborted))) {
209
+ _context.next = 55;
211
210
  break;
212
211
  }
213
212
 
214
- finalText = (0, _helpers.sanitizeChatContent)(fullText);
215
213
  sources = streamBridge.getSources(conversationId);
216
- _context.next = 57;
214
+ _context.next = 55;
217
215
  return _objectSpread({
218
216
  content: [{
219
217
  type: 'text',
220
- text: finalText
218
+ text: (0, _helpers.formatAnswer)({
219
+ role: 'assistant',
220
+ content: fullText
221
+ }, t)
221
222
  }],
222
223
  status: {
223
224
  type: 'complete',
@@ -231,19 +232,17 @@ var createCozyRealtimeChatAdapter = function createCozyRealtimeChatAdapter(optio
231
232
  }
232
233
  } : {});
233
234
 
234
- case 57:
235
+ case 55:
235
236
  streamBridge.cleanup(conversationId);
236
-
237
- case 58:
238
- _context.next = 66;
237
+ _context.next = 64;
239
238
  break;
240
239
 
241
- case 60:
242
- _context.prev = 60;
240
+ case 58:
241
+ _context.prev = 58;
243
242
  _context.t1 = _context["catch"](10);
244
243
  log.error('Error:', _context.t1);
245
244
  streamBridge.cleanup(conversationId);
246
- _context.next = 66;
245
+ _context.next = 64;
247
246
  return {
248
247
  content: [{
249
248
  type: 'text',
@@ -260,12 +259,12 @@ var createCozyRealtimeChatAdapter = function createCozyRealtimeChatAdapter(optio
260
259
  }
261
260
  };
262
261
 
263
- case 66:
262
+ case 64:
264
263
  case "end":
265
264
  return _context.stop();
266
265
  }
267
266
  }
268
- }, _callee, null, [[10, 60], [19, 38, 42, 52], [43,, 47, 51]]);
267
+ }, _callee, null, [[10, 58], [19, 37, 41, 51], [42,, 46, 50]]);
269
268
  }))();
270
269
  }
271
270
  };
@@ -1,4 +1,7 @@
1
- import type { ChatModelRunOptions } from '@assistant-ui/react'
1
+ import type {
2
+ ChatModelRunOptions,
3
+ ChatModelRunResult
4
+ } from '@assistant-ui/react'
2
5
 
3
6
  import { createCozyRealtimeChatAdapter } from './CozyRealtimeChatAdapter'
4
7
  import type { StreamBridge } from './StreamBridge'
@@ -38,6 +41,27 @@ const runAdapter = async (assistantId?: string): Promise<jest.Mock> => {
38
41
  return fetchJSON
39
42
  }
40
43
 
44
+ const runAdapterAndCollectResults = async (
45
+ streamBridge: StreamBridge
46
+ ): Promise<ChatModelRunResult[]> => {
47
+ const adapter = createCozyRealtimeChatAdapter(
48
+ {
49
+ client: { stackClient: { fetchJSON: jest.fn().mockResolvedValue({}) } },
50
+ conversationId: 'conv-1'
51
+ },
52
+ key => key,
53
+ { current: streamBridge }
54
+ )
55
+ const generator = adapter.run(
56
+ makeRunOptions()
57
+ ) as AsyncGenerator<ChatModelRunResult>
58
+ const results: ChatModelRunResult[] = []
59
+ for await (const result of generator) {
60
+ results.push(result)
61
+ }
62
+ return results
63
+ }
64
+
41
65
  describe('CozyRealtimeChatAdapter', () => {
42
66
  it('sends the assistantID for a real assistant', async () => {
43
67
  const fetchJSON = await runAdapter('real-assistant-id')
@@ -67,4 +91,28 @@ describe('CozyRealtimeChatAdapter', () => {
67
91
  ]
68
92
  expect(body).not.toHaveProperty('assistantID')
69
93
  })
94
+
95
+ it('yields the fallback message when the stream completes with no content', async () => {
96
+ const results = await runAdapterAndCollectResults(makeStreamBridge())
97
+ const finalResult = results[results.length - 1]
98
+ expect(finalResult.content).toEqual([
99
+ { type: 'text', text: 'assistant.default_empty_response' }
100
+ ])
101
+ })
102
+
103
+ it('yields the sanitized content when the stream returns text', async () => {
104
+ const streamBridge = {
105
+ // eslint-disable-next-line @typescript-eslint/require-await
106
+ createStream: jest.fn(async function* () {
107
+ yield 'Hello '
108
+ yield 'world'
109
+ }),
110
+ getSources: jest.fn(() => null),
111
+ cleanup: jest.fn()
112
+ } as unknown as StreamBridge
113
+
114
+ const results = await runAdapterAndCollectResults(streamBridge)
115
+ const finalResult = results[results.length - 1]
116
+ expect(finalResult.content).toEqual([{ type: 'text', text: 'Hello world' }])
117
+ })
70
118
  })
@@ -1,6 +1,10 @@
1
1
  export function makeConversationId(): string;
2
2
  export function isAssistantEnabled(): any;
3
3
  export function sanitizeChatContent(content: string): string;
4
+ export function formatAnswer(message: {
5
+ role: string;
6
+ content: string;
7
+ }, t: (key: string) => string): string;
4
8
  export function formatConversationDate(dateString: any, t: any, lang: any): string;
5
9
  export function getNameOfConversation(conversation: any): any;
6
- export function getDescriptionOfConversation(conversation: any): any;
10
+ export function getDescriptionOfConversation(conversation: any, t: any): string | undefined;
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.sanitizeChatContent = exports.makeConversationId = exports.isAssistantEnabled = exports.getNameOfConversation = exports.getDescriptionOfConversation = exports.formatConversationDate = void 0;
8
+ exports.sanitizeChatContent = exports.makeConversationId = exports.isAssistantEnabled = exports.getNameOfConversation = exports.getDescriptionOfConversation = exports.formatConversationDate = exports.formatAnswer = void 0;
9
9
 
10
10
  var _cozyFlags = _interopRequireDefault(require("cozy-flags"));
11
11
 
@@ -41,9 +41,26 @@ var sanitizeChatContent = function sanitizeChatContent(content) {
41
41
  .replace(/\s?\[Source\s+\d+(?:\s*,\s*(?:Source\s+)?\d+)*\]/g, '') // remove « [Sources: 1, 3, 6] » citations, with optional empty link parens
42
42
  .replace(/\s?\[Sources?:\s*\d+(?:\s*,\s*\d+)*\s*\](?:\([^)]*\))?/g, '');
43
43
  };
44
+ /**
45
+ * Formats a chat message's answer: sanitizes citation markup, and
46
+ * substitutes a translated fallback when an assistant answer is empty.
47
+ * Used wherever an assistant response may come back empty from the RAG backend.
48
+ *
49
+ * @param {{ role: string, content: string }} message
50
+ * @param {(key: string) => string} t - translation function from twake-i18n
51
+ * @returns {string}
52
+ */
53
+
44
54
 
45
55
  exports.sanitizeChatContent = sanitizeChatContent;
46
56
 
57
+ var formatAnswer = function formatAnswer(message, t) {
58
+ var sanitized = sanitizeChatContent(message.content);
59
+ return message.role === 'assistant' && !sanitized.trim() ? t('assistant.default_empty_response') : sanitized;
60
+ };
61
+
62
+ exports.formatAnswer = formatAnswer;
63
+
47
64
  var formatConversationDate = function formatConversationDate(dateString, t, lang) {
48
65
  if (!dateString) return '';
49
66
  var date = new Date(dateString);
@@ -91,11 +108,12 @@ var getNameOfConversation = function getNameOfConversation(conversation) {
91
108
 
92
109
  exports.getNameOfConversation = getNameOfConversation;
93
110
 
94
- var getDescriptionOfConversation = function getDescriptionOfConversation(conversation) {
95
- var _conversation$message4, _conversation$message5;
111
+ var getDescriptionOfConversation = function getDescriptionOfConversation(conversation, t) {
112
+ var _conversation$message4;
96
113
 
97
- var content = (_conversation$message4 = conversation.messages) === null || _conversation$message4 === void 0 ? void 0 : (_conversation$message5 = _conversation$message4[conversation.messages.length - 1]) === null || _conversation$message5 === void 0 ? void 0 : _conversation$message5.content;
98
- return content && sanitizeChatContent(content);
114
+ var lastMessage = conversation === null || conversation === void 0 ? void 0 : (_conversation$message4 = conversation.messages) === null || _conversation$message4 === void 0 ? void 0 : _conversation$message4[conversation.messages.length - 1];
115
+ if (!lastMessage) return undefined;
116
+ return formatAnswer(lastMessage, t);
99
117
  };
100
118
 
101
119
  exports.getDescriptionOfConversation = getDescriptionOfConversation;
@@ -179,4 +179,25 @@ describe('getDescriptionOfConversation', () => {
179
179
  }
180
180
  expect(getDescriptionOfConversation(convo)).toBe('It is 4')
181
181
  })
182
+
183
+ it('returns the translated fallback when the last assistant message has no content', () => {
184
+ const t = key => key
185
+ const convo = {
186
+ messages: [
187
+ { role: 'user', content: 'sum?' },
188
+ { role: 'assistant', content: '' }
189
+ ]
190
+ }
191
+ expect(getDescriptionOfConversation(convo, t)).toBe(
192
+ 'assistant.default_empty_response'
193
+ )
194
+ })
195
+
196
+ it('does not apply the fallback when the last message is from the user', () => {
197
+ const t = key => key
198
+ const convo = {
199
+ messages: [{ role: 'user', content: '[Source 1]' }]
200
+ }
201
+ expect(getDescriptionOfConversation(convo, t)).toBe('')
202
+ })
182
203
  })
@@ -47,6 +47,7 @@
47
47
  "yesterday": "Yesterday"
48
48
  },
49
49
  "default_error": "An error occurred during response generation. Please try again",
50
+ "default_empty_response": "I could not find an answer to your request. Please try rephrasing it",
50
51
  "hide": "Hide",
51
52
  "show": "Show",
52
53
  "search_conversation": {
@@ -47,6 +47,7 @@
47
47
  "yesterday": "Hier"
48
48
  },
49
49
  "default_error": "Une erreur est survenue lors de la génération de la réponse. Veuillez réessayer",
50
+ "default_empty_response": "Je n'ai pas trouvé de réponse à votre demande. Essayez de reformuler votre question",
50
51
  "hide": "Masquer",
51
52
  "show": "Afficher",
52
53
  "search_conversation": {
@@ -53,6 +53,7 @@ var en = {
53
53
  yesterday: "Yesterday"
54
54
  },
55
55
  default_error: "An error occurred during response generation. Please try again",
56
+ default_empty_response: "I could not find an answer to your request. Please try rephrasing it",
56
57
  hide: "Hide",
57
58
  show: "Show",
58
59
  search_conversation: {
@@ -236,6 +237,7 @@ var fr = {
236
237
  yesterday: "Hier"
237
238
  },
238
239
  default_error: "Une erreur est survenue lors de la g\xE9n\xE9ration de la r\xE9ponse. Veuillez r\xE9essayer",
240
+ default_empty_response: "Je n'ai pas trouv\xE9 de r\xE9ponse \xE0 votre demande. Essayez de reformuler votre question",
239
241
  hide: "Masquer",
240
242
  show: "Afficher",
241
243
  search_conversation: {
@@ -409,6 +411,7 @@ var ru = {
409
411
  yesterday: "\u0412\u0447\u0435\u0440\u0430"
410
412
  },
411
413
  default_error: "\u041F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430, \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0441\u043D\u043E\u0432\u0430",
414
+ default_empty_response: "\u042F \u043D\u0435 \u0441\u043C\u043E\u0433 \u043D\u0430\u0439\u0442\u0438 \u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \u0432\u0430\u0448 \u0437\u0430\u043F\u0440\u043E\u0441. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043F\u0435\u0440\u0435\u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u043E\u043F\u0440\u043E\u0441",
412
415
  hide: "\u0421\u043A\u0440\u044B\u0442\u044C",
413
416
  show: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C",
414
417
  search_conversation: {
@@ -616,6 +619,7 @@ var vi = {
616
619
  yesterday: "H\xF4m qua"
617
620
  },
618
621
  default_error: "\u0110\xE3 x\u1EA3y ra l\u1ED7i, vui l\xF2ng th\u1EED l\u1EA1i",
622
+ default_empty_response: "T\xF4i kh\xF4ng t\xECm th\u1EA5y c\xE2u tr\u1EA3 l\u1EDDi cho y\xEAu c\u1EA7u c\u1EE7a b\u1EA1n. Vui l\xF2ng th\u1EED di\u1EC5n \u0111\u1EA1t l\u1EA1i",
619
623
  hide: "\u1EA8n",
620
624
  show: "Hi\u1EC7n",
621
625
  message: {
@@ -37,6 +37,7 @@
37
37
  "yesterday": "Вчера"
38
38
  },
39
39
  "default_error": "Произошла ошибка, попробуйте снова",
40
+ "default_empty_response": "Я не смог найти ответ на ваш запрос. Попробуйте переформулировать вопрос",
40
41
  "hide": "Скрыть",
41
42
  "show": "Показать",
42
43
  "search_conversation": {
@@ -44,6 +44,7 @@
44
44
  "yesterday": "Hôm qua"
45
45
  },
46
46
  "default_error": "Đã xảy ra lỗi, vui lòng thử lại",
47
+ "default_empty_response": "Tôi không tìm thấy câu trả lời cho yêu cầu của bạn. Vui lòng thử diễn đạt lại",
47
48
  "hide": "Ẩn",
48
49
  "show": "Hiện",
49
50
  "message": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cozy-search",
3
3
  "description": "UI components about search bar and IA assistant",
4
- "version": "2.2.18",
4
+ "version": "2.2.20",
5
5
  "author": {
6
6
  "name": "Cozy Cloud",
7
7
  "url": "https://github.com/cozy"
@@ -27,7 +27,7 @@
27
27
  "babel-plugin-module-resolver": "^4.0.0",
28
28
  "babel-plugin-tsconfig-paths": "^1.0.3",
29
29
  "babel-preset-cozy-app": "^2.8.4",
30
- "cozy-client": "60.26.0",
30
+ "cozy-client": "60.30.6",
31
31
  "cozy-device-helper": "^4.0.3",
32
32
  "cozy-flags": "^4.9.1",
33
33
  "cozy-intent": "^2.31.1",
@@ -63,7 +63,7 @@
63
63
  },
64
64
  "peerDependencies": {
65
65
  "@linagora/twake-icons": ">=2.6.6",
66
- "cozy-client": ">=60.26.0",
66
+ "cozy-client": ">=60.30.6",
67
67
  "cozy-device-helper": ">=3.7.1",
68
68
  "cozy-flags": ">=4.6.1",
69
69
  "cozy-intent": ">=2.26.0",
@@ -96,5 +96,5 @@
96
96
  "lint": "cd .. && yarn eslint --ext js,jsx,ts packages/cozy-search"
97
97
  },
98
98
  "types": "dist/index.d.ts",
99
- "gitHead": "c442a0903e06f8877c303548a9d22be5ee60bf01"
99
+ "gitHead": "7294e89af6717116c64614562c24e9153db0494f"
100
100
  }