cozy-search 2.5.0 → 2.6.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/README.md +62 -38
- package/dist/components/KnowledgeBase/autoprovision.d.ts +1 -4
- package/dist/components/KnowledgeBase/autoprovision.js +105 -67
- package/dist/components/KnowledgeBase/autoprovision.spec.js +69 -44
- package/dist/components/KnowledgeBase/useAssistantsSetup.d.ts +1 -0
- package/dist/components/KnowledgeBase/useAssistantsSetup.js +25 -0
- package/dist/components/KnowledgeBase/useAssistantsSetup.spec.js +33 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -7
- package/package.json +3 -3
- package/dist/components/KnowledgeBase/useRagIndexTriggers.d.ts +0 -1
- package/dist/components/KnowledgeBase/useRagIndexTriggers.js +0 -27
- package/dist/components/KnowledgeBase/useRagIndexTriggers.spec.js +0 -47
- /package/dist/components/KnowledgeBase/{useRagIndexTriggers.spec.d.ts → useAssistantsSetup.spec.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -59,48 +59,72 @@ of verbs (cozy-home and cozy-drive use `ALL`).
|
|
|
59
59
|
|
|
60
60
|
### RAG indexing setup
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
they change before the assistant is ever opened. The assistants are
|
|
73
|
-
provisioned when the assistant opens; a host app that wants that earlier
|
|
74
|
-
can call `autoprovisionAssistants(client)` or the
|
|
75
|
-
`useAssistantsAutoprovision` hook. Every call shares the first one's
|
|
76
|
-
promise, the triggers step included, so nothing runs twice.
|
|
77
|
-
|
|
78
|
-
`setupRagIndexing(client)` makes sure the instance's two
|
|
79
|
-
`rag-index` triggers exist (one on `io.cozy.files`, one on
|
|
80
|
-
`io.cozy.ai.chat.assistants`) and gives the root folder to any assistant
|
|
81
|
-
that has no knowledge base folder yet — the stack's `rag-index` worker
|
|
82
|
-
reads the assistants to know what to index, cozy-search only has to keep
|
|
83
|
-
the triggers and the assistants' `knowledgeBase` in shape.
|
|
84
|
-
|
|
85
|
-
It needs the following permissions: `io.cozy.triggers` and `io.cozy.jobs`
|
|
86
|
-
(to create and launch the triggers), `io.cozy.ai.chat.assistants` and
|
|
87
|
-
`io.cozy.files` (to read and migrate the assistants). It is idempotent and
|
|
88
|
-
never throws: on a stack whose `rag-index` worker is still reserved, the
|
|
89
|
-
403 is logged and the app keeps working. `ensureProvisionedAssistants`
|
|
90
|
-
additionally creates one `io.cozy.accounts` document per provisioned
|
|
91
|
-
assistant, and the `dirName` folder in `io.cozy.files`.
|
|
92
|
-
|
|
93
|
-
An entry of `cozy.assistant.autoprovision` can carry `"default": true`
|
|
94
|
-
(the first flagged entry wins if there are several). Every NEW
|
|
95
|
-
conversation then starts on that assistant once its document exists —
|
|
96
|
-
from the session after it was provisioned onward. Existing conversations
|
|
97
|
-
are unaffected: they keep their own assistant, or stay unscoped if they
|
|
98
|
-
had none.
|
|
62
|
+
The stack's `rag-index` worker indexes the files of the assistants'
|
|
63
|
+
knowledge base folders. For that an instance needs two things: its two
|
|
64
|
+
`rag-index` triggers (one on `io.cozy.files`, one on
|
|
65
|
+
`io.cozy.ai.chat.assistants`), and assistants that carry a knowledge base
|
|
66
|
+
folder. cozy-search sets both up when the `cozy.assistant.autoprovision`
|
|
67
|
+
flag lists assistants. Without the flag, it does nothing.
|
|
68
|
+
|
|
69
|
+
#### The flag
|
|
70
|
+
|
|
71
|
+
A list of assistants to create, one entry each:
|
|
99
72
|
|
|
100
73
|
```json
|
|
101
|
-
{ "name": "Mes documents", "dirName": "Documents", "default": true }
|
|
74
|
+
[{ "name": "Mes documents", "dirName": "Documents", "default": true }]
|
|
102
75
|
```
|
|
103
76
|
|
|
77
|
+
- `name`: the name of the assistant. Its id is derived from it.
|
|
78
|
+
- `dirId` or `dirName`: its knowledge base folder. `dirId` wins; it can be
|
|
79
|
+
the root folder or a magic folder (`io.cozy.apps/<slug>`). `dirName` is
|
|
80
|
+
a folder at the root of the Drive, created when it does not exist.
|
|
81
|
+
- `prompt`, `icon`: optional.
|
|
82
|
+
- `default`: new conversations start on this assistant once it exists
|
|
83
|
+
(the first entry flagged so wins). Existing conversations keep their
|
|
84
|
+
own assistant.
|
|
85
|
+
|
|
86
|
+
#### Normal behaviour
|
|
87
|
+
|
|
88
|
+
A host app calls `ensureAssistantsSetup(client)` at startup (or its
|
|
89
|
+
`useAssistantsSetup` hook):
|
|
90
|
+
|
|
91
|
+
1. It fetches the `rag-index` triggers. When both exist, an earlier
|
|
92
|
+
session went through the setup and it stops there: one request.
|
|
93
|
+
2. Otherwise it runs the whole setup, `autoprovisionAssistants(client)`:
|
|
94
|
+
each entry of the flag gets its `io.cozy.accounts` document, its
|
|
95
|
+
assistant and its knowledge base folder, then the two triggers are
|
|
96
|
+
created and the files one is launched, which starts the indexing of
|
|
97
|
+
the folders. The triggers come last so that their presence means the
|
|
98
|
+
setup completed.
|
|
99
|
+
|
|
100
|
+
cozy-search also runs `autoprovisionAssistants(client)` itself when the
|
|
101
|
+
assistant is opened. On an instance already set up it only lists the
|
|
102
|
+
assistants; it is there to catch a flag changed since the setup, and to
|
|
103
|
+
give its folder back to a provisioned assistant that lost it.
|
|
104
|
+
|
|
105
|
+
Both run once per session and share their work, so nothing runs twice.
|
|
106
|
+
They never throw: on a stack whose `rag-index` worker is still reserved,
|
|
107
|
+
the 403 on the triggers is logged and the app keeps working.
|
|
108
|
+
|
|
109
|
+
#### Migration of the existing assistants
|
|
110
|
+
|
|
111
|
+
Assistants created before a knowledge base folder became mandatory have
|
|
112
|
+
none, and the worker indexes nothing for them. The setup gives them the
|
|
113
|
+
root folder, that is the whole Drive. This is a one-off data migration,
|
|
114
|
+
unrelated to the entries of the flag: once every assistant has a folder,
|
|
115
|
+
it finds nothing to do.
|
|
116
|
+
|
|
117
|
+
An app that manages its assistants itself, without the flag, can call
|
|
118
|
+
`setupRagIndexing(client)`: the triggers and this migration, no
|
|
119
|
+
provisioning.
|
|
120
|
+
|
|
121
|
+
#### Permissions
|
|
122
|
+
|
|
123
|
+
`io.cozy.triggers` and `io.cozy.jobs` (to create and launch the
|
|
124
|
+
triggers), `io.cozy.ai.chat.assistants` and `io.cozy.files` (to read,
|
|
125
|
+
migrate and provision the assistants and their folders), and
|
|
126
|
+
`io.cozy.accounts` (one document per provisioned assistant).
|
|
127
|
+
|
|
104
128
|
### On desktop
|
|
105
129
|
|
|
106
130
|
You can add the search bar like this :
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export function resetAutoprovisionForTests(): void;
|
|
2
2
|
export function autoprovisionEntries(): Array<object> | null;
|
|
3
|
-
export function ensureRagIndexTriggers(client: import("cozy-client").CozyClient): Promise<{
|
|
4
|
-
triggers: string[];
|
|
5
|
-
error: Error | null;
|
|
6
|
-
}>;
|
|
7
3
|
export function autoprovisionAssistants(client: import("cozy-client").CozyClient): Promise<null | {
|
|
8
4
|
setup: object;
|
|
9
5
|
created: string[];
|
|
@@ -13,3 +9,4 @@ export function autoprovisionAssistants(client: import("cozy-client").CozyClient
|
|
|
13
9
|
reason: string;
|
|
14
10
|
}[];
|
|
15
11
|
}>;
|
|
12
|
+
export function ensureAssistantsSetup(client: import("cozy-client").CozyClient): Promise<null | object>;
|
|
@@ -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.resetAutoprovisionForTests = exports.
|
|
8
|
+
exports.resetAutoprovisionForTests = exports.ensureAssistantsSetup = exports.autoprovisionEntries = exports.autoprovisionAssistants = void 0;
|
|
9
9
|
|
|
10
10
|
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
11
11
|
|
|
@@ -34,12 +34,12 @@ var warn = function warn() {
|
|
|
34
34
|
(_console = console).warn.apply(_console, ['cozy-search autoprovision:'].concat(args));
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
var triggersPending = null;
|
|
38
37
|
var pending = null;
|
|
38
|
+
var setupPending = null;
|
|
39
39
|
|
|
40
40
|
var resetAutoprovisionForTests = function resetAutoprovisionForTests() {
|
|
41
|
-
triggersPending = null;
|
|
42
41
|
pending = null;
|
|
42
|
+
setupPending = null;
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
45
|
* The entries of the autoprovision flag, or null when it lists nothing.
|
|
@@ -53,44 +53,12 @@ var autoprovisionEntries = function autoprovisionEntries() {
|
|
|
53
53
|
var entries = (0, _cozyFlags.default)(_provisioning.AUTOPROVISION_FLAG);
|
|
54
54
|
return Array.isArray(entries) && entries.length > 0 ? entries : null;
|
|
55
55
|
};
|
|
56
|
-
/**
|
|
57
|
-
* Makes sure the instance has its rag-index triggers, so the files of the
|
|
58
|
-
* knowledge base folders get indexed as they change. Runs once per session
|
|
59
|
-
* and never rejects: a host app can call it at startup, it costs a single
|
|
60
|
-
* request when the triggers already exist.
|
|
61
|
-
* @param {import('cozy-client').CozyClient} client - The cozy client.
|
|
62
|
-
* @returns {Promise<{triggers: string[], error: Error|null}>} The
|
|
63
|
-
* `arguments` of the triggers created, and the failure if any.
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
56
|
|
|
67
57
|
exports.autoprovisionEntries = autoprovisionEntries;
|
|
68
58
|
|
|
69
|
-
var ensureRagIndexTriggers = function ensureRagIndexTriggers(client) {
|
|
70
|
-
if (!triggersPending) {
|
|
71
|
-
triggersPending = (0, _ragIndexing.createRagIndexTriggers)(client).then(function (triggers) {
|
|
72
|
-
return {
|
|
73
|
-
triggers: triggers,
|
|
74
|
-
error: null
|
|
75
|
-
};
|
|
76
|
-
}, function (error) {
|
|
77
|
-
warn('cannot set up the rag-index triggers', error);
|
|
78
|
-
return {
|
|
79
|
-
triggers: [],
|
|
80
|
-
error: error
|
|
81
|
-
};
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return triggersPending;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
exports.ensureRagIndexTriggers = ensureRagIndexTriggers;
|
|
89
|
-
|
|
90
59
|
var run = /*#__PURE__*/function () {
|
|
91
60
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(client) {
|
|
92
|
-
var entries, setup,
|
|
93
|
-
|
|
61
|
+
var entries, setup, assistants, result;
|
|
94
62
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
95
63
|
while (1) {
|
|
96
64
|
switch (_context.prev = _context.next) {
|
|
@@ -110,59 +78,68 @@ var run = /*#__PURE__*/function () {
|
|
|
110
78
|
migrated: [],
|
|
111
79
|
errors: []
|
|
112
80
|
};
|
|
113
|
-
_context.next = 6;
|
|
114
|
-
return ensureRagIndexTriggers(client);
|
|
115
|
-
|
|
116
|
-
case 6:
|
|
117
|
-
_yield$ensureRagIndex = _context.sent;
|
|
118
|
-
triggers = _yield$ensureRagIndex.triggers;
|
|
119
|
-
error = _yield$ensureRagIndex.error;
|
|
120
|
-
setup.triggers = triggers;
|
|
121
|
-
if (error) setup.errors.push(error);
|
|
122
81
|
assistants = null;
|
|
123
|
-
_context.prev =
|
|
124
|
-
_context.next =
|
|
82
|
+
_context.prev = 5;
|
|
83
|
+
_context.next = 8;
|
|
125
84
|
return (0, _ragIndexing.fetchAssistants)(client);
|
|
126
85
|
|
|
127
|
-
case
|
|
86
|
+
case 8:
|
|
128
87
|
assistants = _context.sent;
|
|
129
|
-
_context.next =
|
|
88
|
+
_context.next = 11;
|
|
130
89
|
return (0, _ragIndexing.migrateAssistantsWithoutFolder)(client, assistants);
|
|
131
90
|
|
|
132
|
-
case
|
|
91
|
+
case 11:
|
|
133
92
|
setup.migrated = _context.sent;
|
|
134
|
-
_context.next =
|
|
93
|
+
_context.next = 18;
|
|
135
94
|
break;
|
|
136
95
|
|
|
137
|
-
case
|
|
138
|
-
_context.prev =
|
|
139
|
-
_context.t0 = _context["catch"](
|
|
96
|
+
case 14:
|
|
97
|
+
_context.prev = 14;
|
|
98
|
+
_context.t0 = _context["catch"](5);
|
|
140
99
|
warn('cannot migrate the assistants without folder', _context.t0);
|
|
141
100
|
setup.errors.push(_context.t0);
|
|
142
101
|
|
|
143
|
-
case
|
|
144
|
-
_context.next =
|
|
102
|
+
case 18:
|
|
103
|
+
_context.next = 20;
|
|
145
104
|
return (0, _provisioning.ensureProvisionedAssistants)(client, entries, {
|
|
146
105
|
assistants: assistants
|
|
147
106
|
});
|
|
148
107
|
|
|
149
|
-
case
|
|
108
|
+
case 20:
|
|
150
109
|
result = _context.sent;
|
|
151
110
|
|
|
152
111
|
if (result.skipped.length > 0) {
|
|
153
112
|
warn('skipped entries', result.skipped);
|
|
154
|
-
}
|
|
113
|
+
} // Last: the triggers tell ensureAssistantsSetup that the setup completed,
|
|
114
|
+
// and the launch of the files one indexes the folders just provisioned.
|
|
155
115
|
|
|
116
|
+
|
|
117
|
+
_context.prev = 22;
|
|
118
|
+
_context.next = 25;
|
|
119
|
+
return (0, _ragIndexing.createRagIndexTriggers)(client);
|
|
120
|
+
|
|
121
|
+
case 25:
|
|
122
|
+
setup.triggers = _context.sent;
|
|
123
|
+
_context.next = 32;
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case 28:
|
|
127
|
+
_context.prev = 28;
|
|
128
|
+
_context.t1 = _context["catch"](22);
|
|
129
|
+
warn('cannot set up the rag-index triggers', _context.t1);
|
|
130
|
+
setup.errors.push(_context.t1);
|
|
131
|
+
|
|
132
|
+
case 32:
|
|
156
133
|
return _context.abrupt("return", _objectSpread({
|
|
157
134
|
setup: setup
|
|
158
135
|
}, result));
|
|
159
136
|
|
|
160
|
-
case
|
|
137
|
+
case 33:
|
|
161
138
|
case "end":
|
|
162
139
|
return _context.stop();
|
|
163
140
|
}
|
|
164
141
|
}
|
|
165
|
-
}, _callee, null, [[
|
|
142
|
+
}, _callee, null, [[5, 14], [22, 28]]);
|
|
166
143
|
}));
|
|
167
144
|
|
|
168
145
|
return function run(_x) {
|
|
@@ -170,12 +147,10 @@ var run = /*#__PURE__*/function () {
|
|
|
170
147
|
};
|
|
171
148
|
}();
|
|
172
149
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* app that ensured them at startup does not pay for them twice. Never
|
|
178
|
-
* rejects.
|
|
150
|
+
* Gives a folder to the assistants that lack one, provisions the
|
|
151
|
+
* assistants listed in the autoprovision flag, then sets up the rag-index
|
|
152
|
+
* triggers. Runs once per session: every call shares the first one's
|
|
153
|
+
* promise. Never rejects.
|
|
179
154
|
* @param {import('cozy-client').CozyClient} client - The cozy client.
|
|
180
155
|
* @returns {Promise<null|{setup: object, created: string[], ensured: string[], skipped: {id: string, reason: string}[]}>} Null when the flag lists nothing.
|
|
181
156
|
*/
|
|
@@ -191,5 +166,68 @@ var autoprovisionAssistants = function autoprovisionAssistants(client) {
|
|
|
191
166
|
|
|
192
167
|
return pending;
|
|
193
168
|
};
|
|
169
|
+
/**
|
|
170
|
+
* The startup entry point of a host app: one request when the instance is
|
|
171
|
+
* already set up. The rag-index triggers are created last by
|
|
172
|
+
* autoprovisionAssistants, so finding both means an earlier session went
|
|
173
|
+
* through; anything changed since is caught when the assistant opens.
|
|
174
|
+
* Runs once per session, never rejects.
|
|
175
|
+
* @param {import('cozy-client').CozyClient} client - The cozy client.
|
|
176
|
+
* @returns {Promise<null|object>} Null when there was nothing to do, the
|
|
177
|
+
* result of autoprovisionAssistants otherwise.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
exports.autoprovisionAssistants = autoprovisionAssistants;
|
|
182
|
+
|
|
183
|
+
var ensureAssistantsSetup = function ensureAssistantsSetup(client) {
|
|
184
|
+
if (!setupPending) {
|
|
185
|
+
setupPending = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
|
|
186
|
+
var _yield$findRagIndexTr, files, assistants;
|
|
187
|
+
|
|
188
|
+
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
189
|
+
while (1) {
|
|
190
|
+
switch (_context2.prev = _context2.next) {
|
|
191
|
+
case 0:
|
|
192
|
+
if (autoprovisionEntries()) {
|
|
193
|
+
_context2.next = 2;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return _context2.abrupt("return", null);
|
|
198
|
+
|
|
199
|
+
case 2:
|
|
200
|
+
_context2.next = 4;
|
|
201
|
+
return (0, _ragIndexing.findRagIndexTriggers)(client);
|
|
202
|
+
|
|
203
|
+
case 4:
|
|
204
|
+
_yield$findRagIndexTr = _context2.sent;
|
|
205
|
+
files = _yield$findRagIndexTr.files;
|
|
206
|
+
assistants = _yield$findRagIndexTr.assistants;
|
|
207
|
+
|
|
208
|
+
if (!(files && assistants)) {
|
|
209
|
+
_context2.next = 9;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return _context2.abrupt("return", null);
|
|
214
|
+
|
|
215
|
+
case 9:
|
|
216
|
+
return _context2.abrupt("return", autoprovisionAssistants(client));
|
|
217
|
+
|
|
218
|
+
case 10:
|
|
219
|
+
case "end":
|
|
220
|
+
return _context2.stop();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}, _callee2);
|
|
224
|
+
}))().catch(function (error) {
|
|
225
|
+
warn('cannot check the rag-index triggers', error);
|
|
226
|
+
return null;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return setupPending;
|
|
231
|
+
};
|
|
194
232
|
|
|
195
|
-
exports.
|
|
233
|
+
exports.ensureAssistantsSetup = ensureAssistantsSetup;
|
|
@@ -3,13 +3,14 @@ import flag from 'cozy-flags'
|
|
|
3
3
|
import {
|
|
4
4
|
autoprovisionAssistants,
|
|
5
5
|
autoprovisionEntries,
|
|
6
|
-
|
|
6
|
+
ensureAssistantsSetup,
|
|
7
7
|
resetAutoprovisionForTests
|
|
8
8
|
} from './autoprovision'
|
|
9
9
|
import { ensureProvisionedAssistants } from './provisioning'
|
|
10
10
|
import {
|
|
11
11
|
createRagIndexTriggers,
|
|
12
12
|
fetchAssistants,
|
|
13
|
+
findRagIndexTriggers,
|
|
13
14
|
migrateAssistantsWithoutFolder
|
|
14
15
|
} from './ragIndexing'
|
|
15
16
|
|
|
@@ -21,6 +22,7 @@ jest.mock('./provisioning', () => ({
|
|
|
21
22
|
jest.mock('./ragIndexing', () => ({
|
|
22
23
|
createRagIndexTriggers: jest.fn(),
|
|
23
24
|
fetchAssistants: jest.fn(),
|
|
25
|
+
findRagIndexTriggers: jest.fn(),
|
|
24
26
|
migrateAssistantsWithoutFolder: jest.fn()
|
|
25
27
|
}))
|
|
26
28
|
|
|
@@ -35,6 +37,9 @@ beforeEach(() => {
|
|
|
35
37
|
warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
36
38
|
createRagIndexTriggers.mockReset().mockResolvedValue(['io.cozy.files'])
|
|
37
39
|
fetchAssistants.mockReset().mockResolvedValue(assistants)
|
|
40
|
+
findRagIndexTriggers
|
|
41
|
+
.mockReset()
|
|
42
|
+
.mockResolvedValue({ files: null, assistants: null })
|
|
38
43
|
migrateAssistantsWithoutFolder.mockReset().mockResolvedValue([])
|
|
39
44
|
ensureProvisionedAssistants
|
|
40
45
|
.mockReset()
|
|
@@ -58,35 +63,6 @@ describe('autoprovisionEntries', () => {
|
|
|
58
63
|
})
|
|
59
64
|
})
|
|
60
65
|
|
|
61
|
-
describe('ensureRagIndexTriggers', () => {
|
|
62
|
-
it('creates the missing triggers once per session', async () => {
|
|
63
|
-
const first = ensureRagIndexTriggers(client)
|
|
64
|
-
expect(ensureRagIndexTriggers(client)).toBe(first)
|
|
65
|
-
await expect(first).resolves.toEqual({
|
|
66
|
-
triggers: ['io.cozy.files'],
|
|
67
|
-
error: null
|
|
68
|
-
})
|
|
69
|
-
await ensureRagIndexTriggers(client)
|
|
70
|
-
|
|
71
|
-
expect(createRagIndexTriggers).toHaveBeenCalledTimes(1)
|
|
72
|
-
expect(createRagIndexTriggers).toHaveBeenCalledWith(client)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('reports a failure without rejecting', async () => {
|
|
76
|
-
const error = new Error('forbidden')
|
|
77
|
-
createRagIndexTriggers.mockRejectedValue(error)
|
|
78
|
-
await expect(ensureRagIndexTriggers(client)).resolves.toEqual({
|
|
79
|
-
triggers: [],
|
|
80
|
-
error
|
|
81
|
-
})
|
|
82
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
83
|
-
'cozy-search autoprovision:',
|
|
84
|
-
'cannot set up the rag-index triggers',
|
|
85
|
-
error
|
|
86
|
-
)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
66
|
describe('autoprovisionAssistants', () => {
|
|
91
67
|
it('does nothing without flag entries', async () => {
|
|
92
68
|
flag.mockReturnValue(null)
|
|
@@ -109,7 +85,7 @@ describe('autoprovisionAssistants', () => {
|
|
|
109
85
|
expect(result.setup.errors.map(e => e.message)).toEqual(['nope'])
|
|
110
86
|
})
|
|
111
87
|
|
|
112
|
-
it('
|
|
88
|
+
it('migrates, provisions the flag entries, then ensures the triggers', async () => {
|
|
113
89
|
flag.mockReturnValue(entries)
|
|
114
90
|
migrateAssistantsWithoutFolder.mockResolvedValue(['legacy'])
|
|
115
91
|
const result = await autoprovisionAssistants(client)
|
|
@@ -124,9 +100,9 @@ describe('autoprovisionAssistants', () => {
|
|
|
124
100
|
assistants
|
|
125
101
|
})
|
|
126
102
|
const order = [
|
|
127
|
-
createRagIndexTriggers,
|
|
128
103
|
migrateAssistantsWithoutFolder,
|
|
129
|
-
ensureProvisionedAssistants
|
|
104
|
+
ensureProvisionedAssistants,
|
|
105
|
+
createRagIndexTriggers
|
|
130
106
|
].map(fn => fn.mock.invocationCallOrder[0])
|
|
131
107
|
expect(order).toEqual([...order].sort((a, b) => a - b))
|
|
132
108
|
expect(result).toEqual({
|
|
@@ -148,15 +124,6 @@ describe('autoprovisionAssistants', () => {
|
|
|
148
124
|
expect(ensureProvisionedAssistants).toHaveBeenCalledTimes(1)
|
|
149
125
|
})
|
|
150
126
|
|
|
151
|
-
it('shares the triggers step with ensureRagIndexTriggers', async () => {
|
|
152
|
-
flag.mockReturnValue(entries)
|
|
153
|
-
await ensureRagIndexTriggers(client)
|
|
154
|
-
await autoprovisionAssistants(client)
|
|
155
|
-
|
|
156
|
-
expect(createRagIndexTriggers).toHaveBeenCalledTimes(1)
|
|
157
|
-
expect(ensureProvisionedAssistants).toHaveBeenCalledTimes(1)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
127
|
it('still provisions when the setup steps failed', async () => {
|
|
161
128
|
flag.mockReturnValue(entries)
|
|
162
129
|
createRagIndexTriggers.mockRejectedValue(new Error('forbidden'))
|
|
@@ -165,8 +132,8 @@ describe('autoprovisionAssistants', () => {
|
|
|
165
132
|
|
|
166
133
|
expect(ensureProvisionedAssistants).toHaveBeenCalledTimes(1)
|
|
167
134
|
expect(result.setup.errors.map(e => e.message)).toEqual([
|
|
168
|
-
'
|
|
169
|
-
'
|
|
135
|
+
'nope',
|
|
136
|
+
'forbidden'
|
|
170
137
|
])
|
|
171
138
|
})
|
|
172
139
|
|
|
@@ -197,3 +164,61 @@ describe('autoprovisionAssistants', () => {
|
|
|
197
164
|
)
|
|
198
165
|
})
|
|
199
166
|
})
|
|
167
|
+
|
|
168
|
+
describe('ensureAssistantsSetup', () => {
|
|
169
|
+
it('does nothing without flag entries', async () => {
|
|
170
|
+
flag.mockReturnValue(null)
|
|
171
|
+
await expect(ensureAssistantsSetup(client)).resolves.toBeNull()
|
|
172
|
+
expect(findRagIndexTriggers).not.toHaveBeenCalled()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('stops at the triggers when both exist', async () => {
|
|
176
|
+
flag.mockReturnValue(entries)
|
|
177
|
+
findRagIndexTriggers.mockResolvedValue({
|
|
178
|
+
files: { _id: 'files' },
|
|
179
|
+
assistants: { _id: 'assistants' }
|
|
180
|
+
})
|
|
181
|
+
await expect(ensureAssistantsSetup(client)).resolves.toBeNull()
|
|
182
|
+
|
|
183
|
+
expect(findRagIndexTriggers).toHaveBeenCalledWith(client)
|
|
184
|
+
expect(fetchAssistants).not.toHaveBeenCalled()
|
|
185
|
+
expect(ensureProvisionedAssistants).not.toHaveBeenCalled()
|
|
186
|
+
expect(createRagIndexTriggers).not.toHaveBeenCalled()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it.each([
|
|
190
|
+
['none', { files: null, assistants: null }],
|
|
191
|
+
['only the files one', { files: { _id: 'files' }, assistants: null }],
|
|
192
|
+
['only the assistants one', { files: null, assistants: { _id: 'a' } }]
|
|
193
|
+
])('runs the whole setup with %s', async (_label, found) => {
|
|
194
|
+
flag.mockReturnValue(entries)
|
|
195
|
+
findRagIndexTriggers.mockResolvedValue(found)
|
|
196
|
+
const result = await ensureAssistantsSetup(client)
|
|
197
|
+
|
|
198
|
+
expect(ensureProvisionedAssistants).toHaveBeenCalledTimes(1)
|
|
199
|
+
expect(createRagIndexTriggers).toHaveBeenCalledTimes(1)
|
|
200
|
+
expect(result.created).toEqual(['docs'])
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('runs once per session and shares the setup with the assistant', async () => {
|
|
204
|
+
flag.mockReturnValue(entries)
|
|
205
|
+
const first = ensureAssistantsSetup(client)
|
|
206
|
+
expect(ensureAssistantsSetup(client)).toBe(first)
|
|
207
|
+
await first
|
|
208
|
+
await autoprovisionAssistants(client)
|
|
209
|
+
|
|
210
|
+
expect(findRagIndexTriggers).toHaveBeenCalledTimes(1)
|
|
211
|
+
expect(ensureProvisionedAssistants).toHaveBeenCalledTimes(1)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('never rejects', async () => {
|
|
215
|
+
flag.mockReturnValue(entries)
|
|
216
|
+
findRagIndexTriggers.mockRejectedValue(new Error('offline'))
|
|
217
|
+
await expect(ensureAssistantsSetup(client)).resolves.toBeNull()
|
|
218
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
219
|
+
'cozy-search autoprovision:',
|
|
220
|
+
'cannot check the rag-index triggers',
|
|
221
|
+
expect.any(Error)
|
|
222
|
+
)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function useAssistantsSetup(): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useAssistantsSetup = void 0;
|
|
7
|
+
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
|
|
10
|
+
var _cozyClient = require("cozy-client");
|
|
11
|
+
|
|
12
|
+
var _autoprovision = require("./autoprovision");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Sets the flagged assistants and their RAG indexing up at the startup of
|
|
16
|
+
* a host app, the first time only: see ensureAssistantsSetup.
|
|
17
|
+
*/
|
|
18
|
+
var useAssistantsSetup = function useAssistantsSetup() {
|
|
19
|
+
var client = (0, _cozyClient.useClient)();
|
|
20
|
+
(0, _react.useEffect)(function () {
|
|
21
|
+
if (client) (0, _autoprovision.ensureAssistantsSetup)(client);
|
|
22
|
+
}, [client]);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
exports.useAssistantsSetup = useAssistantsSetup;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks/dom'
|
|
2
|
+
|
|
3
|
+
import { useClient } from 'cozy-client'
|
|
4
|
+
|
|
5
|
+
import { ensureAssistantsSetup } from './autoprovision'
|
|
6
|
+
import { useAssistantsSetup } from './useAssistantsSetup'
|
|
7
|
+
|
|
8
|
+
jest.mock('cozy-client', () => ({ useClient: jest.fn() }))
|
|
9
|
+
jest.mock('./autoprovision', () => ({ ensureAssistantsSetup: jest.fn() }))
|
|
10
|
+
|
|
11
|
+
const client = { id: 'client' }
|
|
12
|
+
|
|
13
|
+
describe('useAssistantsSetup', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
useClient.mockReset().mockReturnValue(client)
|
|
16
|
+
ensureAssistantsSetup.mockReset().mockResolvedValue(null)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('ensures the setup with the client on mount', () => {
|
|
20
|
+
const { rerender } = renderHook(() => useAssistantsSetup())
|
|
21
|
+
rerender()
|
|
22
|
+
|
|
23
|
+
expect(ensureAssistantsSetup).toHaveBeenCalledTimes(1)
|
|
24
|
+
expect(ensureAssistantsSetup).toHaveBeenCalledWith(client)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('waits for a client', () => {
|
|
28
|
+
useClient.mockReturnValue(null)
|
|
29
|
+
renderHook(() => useAssistantsSetup())
|
|
30
|
+
|
|
31
|
+
expect(ensureAssistantsSetup).not.toHaveBeenCalled()
|
|
32
|
+
})
|
|
33
|
+
})
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ export { StreamBridge, createCozyRealtimeChatAdapter } from './components/adapte
|
|
|
11
11
|
export { default as AssistantView } from './components/Views/AssistantView';
|
|
12
12
|
export { assistantIdFromName, AUTOPROVISION_FLAG, ensureProvisionedAssistants, getDefaultProvisionedAssistantId } from './components/KnowledgeBase/provisioning';
|
|
13
13
|
export { setupRagIndexing } from './components/KnowledgeBase/ragIndexing';
|
|
14
|
-
export { autoprovisionAssistants,
|
|
15
|
-
export {
|
|
14
|
+
export { autoprovisionAssistants, ensureAssistantsSetup } from './components/KnowledgeBase/autoprovision';
|
|
15
|
+
export { useAssistantsSetup } from './components/KnowledgeBase/useAssistantsSetup';
|
|
16
16
|
export { useAssistantsAutoprovision } from './components/KnowledgeBase/useAssistantsAutoprovision';
|
package/dist/index.js
CHANGED
|
@@ -95,16 +95,16 @@ Object.defineProperty(exports, "createCozyRealtimeChatAdapter", {
|
|
|
95
95
|
return _adapters.createCozyRealtimeChatAdapter;
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
|
-
Object.defineProperty(exports, "
|
|
98
|
+
Object.defineProperty(exports, "ensureAssistantsSetup", {
|
|
99
99
|
enumerable: true,
|
|
100
100
|
get: function get() {
|
|
101
|
-
return
|
|
101
|
+
return _autoprovision.ensureAssistantsSetup;
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
|
-
Object.defineProperty(exports, "
|
|
104
|
+
Object.defineProperty(exports, "ensureProvisionedAssistants", {
|
|
105
105
|
enumerable: true,
|
|
106
106
|
get: function get() {
|
|
107
|
-
return
|
|
107
|
+
return _provisioning.ensureProvisionedAssistants;
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
Object.defineProperty(exports, "getDefaultProvisionedAssistantId", {
|
|
@@ -125,10 +125,10 @@ Object.defineProperty(exports, "useAssistantsAutoprovision", {
|
|
|
125
125
|
return _useAssistantsAutoprovision.useAssistantsAutoprovision;
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
|
-
Object.defineProperty(exports, "
|
|
128
|
+
Object.defineProperty(exports, "useAssistantsSetup", {
|
|
129
129
|
enumerable: true,
|
|
130
130
|
get: function get() {
|
|
131
|
-
return
|
|
131
|
+
return _useAssistantsSetup.useAssistantsSetup;
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
|
|
@@ -160,6 +160,6 @@ var _ragIndexing = require("./components/KnowledgeBase/ragIndexing");
|
|
|
160
160
|
|
|
161
161
|
var _autoprovision = require("./components/KnowledgeBase/autoprovision");
|
|
162
162
|
|
|
163
|
-
var
|
|
163
|
+
var _useAssistantsSetup = require("./components/KnowledgeBase/useAssistantsSetup");
|
|
164
164
|
|
|
165
165
|
var _useAssistantsAutoprovision = require("./components/KnowledgeBase/useAssistantsAutoprovision");
|
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.
|
|
4
|
+
"version": "2.6.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cozy Cloud",
|
|
7
7
|
"url": "https://github.com/cozy"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"cozy-pouch-link": "^54.0.0",
|
|
38
38
|
"cozy-realtime": "^5.9.4",
|
|
39
39
|
"cozy-ui": "^138.4.0",
|
|
40
|
-
"cozy-ui-plus": "^13.0.
|
|
40
|
+
"cozy-ui-plus": "^13.0.5",
|
|
41
41
|
"cross-fetch": "^4.0.0",
|
|
42
42
|
"jest": "30.3.0",
|
|
43
43
|
"react": "18.2.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": "
|
|
99
|
+
"gitHead": "2103fb9d929275519efab0419630593d07d1ce8a"
|
|
100
100
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export function useRagIndexTriggers(): void;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.useRagIndexTriggers = void 0;
|
|
7
|
-
|
|
8
|
-
var _react = require("react");
|
|
9
|
-
|
|
10
|
-
var _cozyClient = require("cozy-client");
|
|
11
|
-
|
|
12
|
-
var _autoprovision = require("./autoprovision");
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Ensures the rag-index triggers at startup, when the autoprovision flag
|
|
16
|
-
* lists assistants: the knowledge base folders are then indexed as they
|
|
17
|
-
* change, before the assistant is ever opened. The assistants themselves
|
|
18
|
-
* are provisioned when the assistant opens.
|
|
19
|
-
*/
|
|
20
|
-
var useRagIndexTriggers = function useRagIndexTriggers() {
|
|
21
|
-
var client = (0, _cozyClient.useClient)();
|
|
22
|
-
(0, _react.useEffect)(function () {
|
|
23
|
-
if (client && (0, _autoprovision.autoprovisionEntries)()) (0, _autoprovision.ensureRagIndexTriggers)(client);
|
|
24
|
-
}, [client]);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
exports.useRagIndexTriggers = useRagIndexTriggers;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { renderHook } from '@testing-library/react-hooks/dom'
|
|
2
|
-
|
|
3
|
-
import { useClient } from 'cozy-client'
|
|
4
|
-
|
|
5
|
-
import { autoprovisionEntries, ensureRagIndexTriggers } from './autoprovision'
|
|
6
|
-
import { useRagIndexTriggers } from './useRagIndexTriggers'
|
|
7
|
-
|
|
8
|
-
jest.mock('cozy-client', () => ({ useClient: jest.fn() }))
|
|
9
|
-
jest.mock('./autoprovision', () => ({
|
|
10
|
-
autoprovisionEntries: jest.fn(),
|
|
11
|
-
ensureRagIndexTriggers: jest.fn()
|
|
12
|
-
}))
|
|
13
|
-
|
|
14
|
-
const client = { id: 'client' }
|
|
15
|
-
|
|
16
|
-
describe('useRagIndexTriggers', () => {
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
useClient.mockReset().mockReturnValue(client)
|
|
19
|
-
autoprovisionEntries.mockReset().mockReturnValue([{ name: 'Docs' }])
|
|
20
|
-
ensureRagIndexTriggers.mockReset().mockResolvedValue({
|
|
21
|
-
triggers: [],
|
|
22
|
-
error: null
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('ensures the triggers with the client on mount', () => {
|
|
27
|
-
const { rerender } = renderHook(() => useRagIndexTriggers())
|
|
28
|
-
rerender()
|
|
29
|
-
|
|
30
|
-
expect(ensureRagIndexTriggers).toHaveBeenCalledTimes(1)
|
|
31
|
-
expect(ensureRagIndexTriggers).toHaveBeenCalledWith(client)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('does nothing without flag entries', () => {
|
|
35
|
-
autoprovisionEntries.mockReturnValue(null)
|
|
36
|
-
renderHook(() => useRagIndexTriggers())
|
|
37
|
-
|
|
38
|
-
expect(ensureRagIndexTriggers).not.toHaveBeenCalled()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('waits for a client', () => {
|
|
42
|
-
useClient.mockReturnValue(null)
|
|
43
|
-
renderHook(() => useRagIndexTriggers())
|
|
44
|
-
|
|
45
|
-
expect(ensureRagIndexTriggers).not.toHaveBeenCalled()
|
|
46
|
-
})
|
|
47
|
-
})
|
|
File without changes
|