UNPKG

17.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = createClient;
7
8var _lodash = require('lodash');
9
10var _lodash2 = _interopRequireDefault(_lodash);
11
12var _request = require('./request');
13
14var _request2 = _interopRequireDefault(_request);
15
16var _debug = require('debug');
17
18var _debug2 = _interopRequireDefault(_debug);
19
20var _interpreter = require('./interpreter');
21
22var _defaults = require('./defaults');
23
24var _defaults2 = _interopRequireDefault(_defaults);
25
26var _jwtDecode2 = require('jwt-decode');
27
28var _jwtDecode3 = _interopRequireDefault(_jwtDecode2);
29
30var _time = require('./time');
31
32var _time2 = _interopRequireDefault(_time);
33
34var _constants = require('./constants');
35
36var _errors = require('./errors');
37
38function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39
40var debug = (0, _debug2.default)('craft-ai:client');
41
42function resolveAfterTimeout(timeout) {
43 return new Promise(function (resolve) {
44 return setTimeout(function () {
45 return resolve();
46 }, timeout);
47 });
48}
49
50// A very simple regex, helps detect some issues.
51var SIMPLE_HTTP_URL_REGEX = /^https?:\/\/.*$/;
52
53function isUrl(url) {
54 return SIMPLE_HTTP_URL_REGEX.test(url);
55}
56
57function createClient(tokenOrCfg) {
58 var cfg = _lodash2.default.defaults({}, _lodash2.default.isString(tokenOrCfg) ? { token: tokenOrCfg } : tokenOrCfg, _defaults2.default);
59
60 // Initialization check
61 if (!_lodash2.default.has(cfg, 'token') || !_lodash2.default.isString(cfg.token)) {
62 throw new _errors.CraftAiBadRequestError('Bad Request, unable to create a client with no or invalid token provided.');
63 }
64 try {
65 var _jwtDecode = (0, _jwtDecode3.default)(cfg.token),
66 owner = _jwtDecode.owner,
67 platform = _jwtDecode.platform,
68 project = _jwtDecode.project;
69
70 // Keep the provided values
71
72
73 cfg.owner = cfg.owner || owner;
74 cfg.project = cfg.project || project;
75 cfg.url = cfg.url || platform;
76 } catch (e) {
77 throw new _errors.CraftAiCredentialsError();
78 }
79 if (!_lodash2.default.has(cfg, 'url') || !isUrl(cfg.url)) {
80 throw new _errors.CraftAiBadRequestError('Bad Request, unable to create a client with no or invalid url provided.');
81 }
82 if (!_lodash2.default.has(cfg, 'project') || !_lodash2.default.isString(cfg.project)) {
83 throw new _errors.CraftAiBadRequestError('Bad Request, unable to create a client with no or invalid project provided.');
84 } else {
85 var splittedProject = cfg.project.split('/');
86 if (splittedProject.length >= 2) {
87 cfg.owner = splittedProject[0];
88 cfg.project = splittedProject[1];
89 }
90 }
91 if (!_lodash2.default.has(cfg, 'owner') || !_lodash2.default.isString(cfg.owner)) {
92 throw new _errors.CraftAiBadRequestError('Bad Request, unable to create a client with no or invalid owner provided.');
93 }
94 if (cfg.proxy != null && !isUrl(cfg.proxy)) {
95 throw new _errors.CraftAiBadRequestError('Bad Request, unable to create a client with an invalid proxy url provided.');
96 }
97
98 debug('Creating a client instance for project \'' + cfg.owner + '/' + cfg.project + '\' on \'' + cfg.url + '\'.');
99
100 var request = (0, _request2.default)(cfg);
101
102 // 'Public' attributes & methods
103 var instance = {
104 cfg: cfg,
105 createAgent: function createAgent(configuration) {
106 var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
107
108 if (_lodash2.default.isUndefined(configuration) || !_lodash2.default.isObject(configuration)) {
109 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to create an agent with no or invalid configuration provided.'));
110 }
111
112 if (!_lodash2.default.isUndefined(id) && !_constants.AGENT_ID_ALLOWED_REGEXP.test(id)) {
113 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to create an agent with invalid agent id. It must only contain characters in "a-zA-Z0-9_-" and must be a string between 1 and ' + _constants.AGENT_ID_MAX_LENGTH + ' characters.'));
114 }
115
116 return request({
117 method: 'POST',
118 path: '/agents',
119 body: {
120 id: id,
121 configuration: configuration
122 }
123 }).then(function (_ref) {
124 var body = _ref.body;
125
126 debug('Agent \'' + body.id + '\' created.');
127 return body;
128 });
129 },
130 getAgent: function getAgent(agentId) {
131 if (!_constants.AGENT_ID_ALLOWED_REGEXP.test(agentId)) {
132 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get an agent with invalid agent id. It must only contain characters in "a-zA-Z0-9_-" and cannot be the empty string.'));
133 }
134
135 return request({
136 method: 'GET',
137 path: '/agents/' + agentId
138 }).then(function (_ref2) {
139 var body = _ref2.body;
140 return body;
141 });
142 },
143 listAgents: function listAgents(agentId) {
144 return request({
145 method: 'GET',
146 path: '/agents'
147 }).then(function (_ref3) {
148 var body = _ref3.body;
149 return body.agentsList;
150 });
151 },
152 deleteAgent: function deleteAgent(agentId) {
153 if (_lodash2.default.isUndefined(agentId)) {
154 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to delete an agent with no agentId provided.'));
155 }
156
157 return request({
158 method: 'DELETE',
159 path: '/agents/' + agentId
160 }).then(function (_ref4) {
161 var body = _ref4.body;
162
163 debug('Agent \'' + agentId + '\' deleted');
164 return body;
165 });
166 },
167 destroyAgent: function destroyAgent(agentId) {
168 (0, _constants.deprecation)('client.destroyAgent', 'client.deleteAgent');
169 return this.deleteAgent(agentId);
170 },
171 getAgentContext: function getAgentContext(agentId) {
172 var t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
173
174 if (_lodash2.default.isUndefined(agentId)) {
175 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get the agent context with no agentId provided.'));
176 }
177 var posixTimestamp = (0, _time2.default)(t).timestamp;
178 if (_lodash2.default.isUndefined(posixTimestamp)) {
179 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get the agent context with an invalid timestamp provided.'));
180 }
181
182 return request({
183 method: 'GET',
184 path: '/agents/' + agentId + '/context/state',
185 query: {
186 t: posixTimestamp
187 }
188 }).then(function (_ref5) {
189 var body = _ref5.body;
190 return body;
191 });
192 },
193 addAgentContextOperations: function addAgentContextOperations(agentId, operations) {
194 if (_lodash2.default.isUndefined(agentId)) {
195 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to add agent context operations with no agentId provided.'));
196 }
197 if (!_lodash2.default.isArray(operations)) {
198 // Only one given operation
199 operations = [operations];
200 }
201 operations = _lodash2.default.compact(operations);
202
203 if (!operations.length) {
204 var message = 'No operation to add to the agent ' + cfg.owner + '/' + cfg.project + '/' + agentId + ' context.';
205
206 debug(message);
207
208 return Promise.resolve({ message: message });
209 }
210
211 return (0, _lodash2.default)(operations).map(function (_ref6) {
212 var context = _ref6.context,
213 timestamp = _ref6.timestamp;
214 return {
215 context: context,
216 timestamp: (0, _time2.default)(timestamp).timestamp
217 };
218 }).orderBy('timestamp').chunk(cfg.operationsChunksSize).reduce(function (p, chunk) {
219 return p.then(function () {
220 return request({
221 method: 'POST',
222 path: '/agents/' + agentId + '/context',
223 body: chunk
224 });
225 });
226 }, Promise.resolve()).then(function () {
227 var message = 'Successfully added ' + operations.length + ' operation(s) to the agent ' + cfg.owner + '/' + cfg.project + '/' + agentId + ' context.';
228 debug(message);
229 return { message: message };
230 });
231 },
232 getAgentContextOperations: function getAgentContextOperations(agentId) {
233 var _this = this;
234
235 var start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
236 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
237
238 if (_lodash2.default.isUndefined(agentId)) {
239 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent context operations with no agentId provided.'));
240 }
241 var startTimestamp = void 0;
242 if (start) {
243 startTimestamp = (0, _time2.default)(start).timestamp;
244 if (_lodash2.default.isUndefined(startTimestamp)) {
245 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent context operations with an invalid \'start\' timestamp provided.'));
246 }
247 }
248 var endTimestamp = void 0;
249 if (end) {
250 endTimestamp = (0, _time2.default)(end).timestamp;
251 if (_lodash2.default.isUndefined(endTimestamp)) {
252 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent context operations with an invalid \'end\' timestamp provided.'));
253 }
254 }
255
256 var requestFollowingPages = function requestFollowingPages(_ref7) {
257 var operations = _ref7.operations,
258 nextPageUrl = _ref7.nextPageUrl;
259
260 if (!nextPageUrl) {
261 return Promise.resolve(operations);
262 }
263 return request({ url: nextPageUrl }, _this).then(function (_ref8) {
264 var body = _ref8.body,
265 nextPageUrl = _ref8.nextPageUrl;
266 return requestFollowingPages({
267 operations: operations.concat(body),
268 nextPageUrl: nextPageUrl
269 });
270 });
271 };
272
273 return request({
274 method: 'GET',
275 path: '/agents/' + agentId + '/context',
276 query: {
277 start: startTimestamp,
278 end: endTimestamp
279 }
280 }).then(function (_ref9) {
281 var body = _ref9.body,
282 nextPageUrl = _ref9.nextPageUrl;
283 return requestFollowingPages({
284 operations: body,
285 nextPageUrl: nextPageUrl
286 });
287 });
288 },
289 getAgentStateHistory: function getAgentStateHistory(agentId) {
290 var start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
291 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
292
293 if (_lodash2.default.isUndefined(agentId)) {
294 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent state history with no agentId provided.'));
295 }
296 var startTimestamp = void 0;
297 if (start) {
298 startTimestamp = (0, _time2.default)(start).timestamp;
299 if (_lodash2.default.isUndefined(startTimestamp)) {
300 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent state history with an invalid \'start\' timestamp provided.'));
301 }
302 }
303 var endTimestamp = void 0;
304 if (end) {
305 endTimestamp = (0, _time2.default)(end).timestamp;
306 if (_lodash2.default.isUndefined(endTimestamp)) {
307 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to get agent state history with an invalid \'end\' timestamp provided.'));
308 }
309 }
310
311 var requestFollowingPages = function requestFollowingPages(_ref10) {
312 var stateHistory = _ref10.stateHistory,
313 nextPageUrl = _ref10.nextPageUrl;
314
315 if (!nextPageUrl) {
316 return Promise.resolve(stateHistory);
317 }
318 return request({ url: nextPageUrl }).then(function (_ref11) {
319 var body = _ref11.body,
320 nextPageUrl = _ref11.nextPageUrl;
321 return requestFollowingPages({
322 stateHistory: stateHistory.concat(body),
323 nextPageUrl: nextPageUrl
324 });
325 });
326 };
327
328 return request({
329 method: 'GET',
330 path: '/agents/' + agentId + '/context/state/history',
331 query: {
332 start: startTimestamp,
333 end: endTimestamp
334 }
335 }).then(function (_ref12) {
336 var body = _ref12.body,
337 nextPageUrl = _ref12.nextPageUrl;
338 return requestFollowingPages({
339 stateHistory: body,
340 nextPageUrl: nextPageUrl
341 });
342 });
343 },
344 getAgentInspectorUrl: function getAgentInspectorUrl(agentId) {
345 var t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
346
347 (0, _constants.deprecation)('client.getAgentInspectorUrl', 'client.getSharedAgentInspectorUrl');
348 return this.getSharedAgentInspectorUrl(agentId, t);
349 },
350 getSharedAgentInspectorUrl: function getSharedAgentInspectorUrl(agentId) {
351 var t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
352
353 return request({
354 method: 'GET',
355 path: '/agents/' + agentId + '/shared'
356 }).then(function (_ref13) {
357 var body = _ref13.body;
358
359 if (_lodash2.default.isUndefined(t)) {
360 return body.shortUrl;
361 } else {
362 var posixTimestamp = (0, _time2.default)(t).timestamp;
363 return body.shortUrl + '?t=' + posixTimestamp;
364 }
365 });
366 },
367 deleteSharedAgentInspectorUrl: function deleteSharedAgentInspectorUrl(agentId) {
368 return request({
369 method: 'DELETE',
370 path: '/agents/' + agentId + '/shared'
371 }).then(function () {
372 debug('Delete shared inspector link for agent "' + agentId + '".');
373 });
374 },
375 getAgentDecisionTree: function getAgentDecisionTree(agentId) {
376 var t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
377 var version = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _constants.DEFAULT_DECISION_TREE_VERSION;
378
379 if (_lodash2.default.isUndefined(agentId)) {
380 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to retrieve an agent decision tree with no agentId provided.'));
381 }
382 var posixTimestamp = (0, _time2.default)(t).timestamp;
383 if (_lodash2.default.isUndefined(posixTimestamp)) {
384 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to retrieve an agent decision tree with an invalid timestamp provided.'));
385 }
386
387 var agentDecisionTreeRequest = function agentDecisionTreeRequest() {
388 return request({
389 method: 'GET',
390 path: '/agents/' + agentId + '/decision/tree',
391 query: {
392 t: posixTimestamp
393 },
394 headers: {
395 'x-craft-ai-tree-version': version
396 }
397 }).then(function (_ref14) {
398 var body = _ref14.body;
399 return body;
400 });
401 };
402
403 if (!cfg.decisionTreeRetrievalTimeout) {
404 // Don't retry
405 return agentDecisionTreeRequest();
406 } else {
407 var start = Date.now();
408 return Promise.race([agentDecisionTreeRequest().catch(function (error) {
409 var requestDuration = Date.now() - start;
410 var expectedRetryDuration = requestDuration + 2000; // Let's add some margin
411 var timeoutBeforeRetrying = cfg.decisionTreeRetrievalTimeout - requestDuration - expectedRetryDuration;
412 if (error instanceof _errors.CraftAiLongRequestTimeOutError && timeoutBeforeRetrying > 0) {
413 // First timeout, let's retry once near the end of the set timeout
414 return resolveAfterTimeout(timeoutBeforeRetrying).then(function () {
415 return agentDecisionTreeRequest();
416 });
417 } else {
418 return Promise.reject(error);
419 }
420 }), resolveAfterTimeout(cfg.decisionTreeRetrievalTimeout).then(function () {
421 throw new _errors.CraftAiLongRequestTimeOutError();
422 })]);
423 }
424 },
425 computeAgentDecision: function computeAgentDecision(agentId, t) {
426 for (var _len = arguments.length, contexts = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
427 contexts[_key - 2] = arguments[_key];
428 }
429
430 if (_lodash2.default.isUndefined(agentId)) {
431 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to compute an agent decision with no agentId provided.'));
432 }
433 var posixTimestamp = (0, _time2.default)(t).timestamp;
434 if (_lodash2.default.isUndefined(posixTimestamp)) {
435 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to compute an agent decision with no or invalid timestamp provided.'));
436 }
437 if (_lodash2.default.isUndefined(contexts) || _lodash2.default.size(contexts) === 0) {
438 return Promise.reject(new _errors.CraftAiBadRequestError('Bad Request, unable to compute an agent decision with no context provided.'));
439 }
440
441 return request({
442 method: 'GET',
443 path: '/agents/' + agentId + '/decision/tree',
444 query: {
445 t: posixTimestamp
446 }
447 }).then(function (_ref15) {
448 var body = _ref15.body;
449
450 var decision = _interpreter.decide.apply(undefined, [body].concat(contexts));
451 decision.timestamp = posixTimestamp;
452 return decision;
453 });
454 }
455 };
456
457 return instance;
458}
\No newline at end of file