@miso.ai/client-sdk-ui 1.12.1-beta.0 → 1.12.1-beta.2

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.
@@ -14,15 +14,11 @@ exports.query = query;
14
14
  exports.request = request;
15
15
  exports.response = response;
16
16
  exports.session = session;
17
- exports.suggestions = suggestions;
18
17
  exports.tracker = tracker;
19
18
  exports.view = view;
20
19
  function session() {
21
20
  return 'session';
22
21
  }
23
- function suggestions() {
24
- return 'suggestions';
25
- }
26
22
  function completions() {
27
23
  return 'completions';
28
24
  }
@@ -9,25 +9,32 @@ var _constants = require("../constants.js");
9
9
  var fields = _interopRequireWildcard(require("./fields.js"));
10
10
  var _proxy = _interopRequireDefault(require("../util/proxy.js"));
11
11
  var _tracker = _interopRequireDefault(require("./tracker.js"));
12
- var _utils = require("../element/util/utils.js");
13
12
  const _excluded = ["value", "error", "status", "meta"];
14
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
14
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
15
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
16
+ //import { triggerMisoAction } from '../element/util/utils.js';
17
+
17
18
  function statesEqual(a, b) {
18
19
  return a === b || a && b && a.status === b.status && a.error === b.error && a.value === b.value &&
19
20
  // skip deep equals
20
- a.ongoing === b.ongoing && sessionEquals(a.session, b.session);
21
+ a.ongoing === b.ongoing && stateEmptyness(a) === stateEmptyness(b) &&
22
+ // TODO: ad-hoc
23
+ sessionEquals(a.session, b.session);
21
24
  }
22
25
  function sessionEquals(a, b) {
23
26
  return a === b || a && b && a.uuid === b.uuid;
24
27
  }
28
+ function stateEmptyness(data) {
29
+ return data && data.meta && data.meta.empty;
30
+ }
25
31
  class ViewActor {
26
32
  constructor(views, role) {
27
33
  this._events = new _commons.EventEmitter({
28
34
  target: this
29
35
  });
30
36
  this._views = views;
37
+ // TODO: we should pull this up to views, for the results are also used in judging container emptyness
31
38
  this._mapping = asMappingFunction((views._roles.mappings || {})[role] || role);
32
39
  (0, _commons.defineValues)(this, {
33
40
  role,
@@ -113,6 +120,9 @@ class ViewActor {
113
120
  }
114
121
  return this._tracker;
115
122
  }
123
+ get workflow() {
124
+ return this._views._workflow;
125
+ }
116
126
  get workflowOptions() {
117
127
  return this._views._options.resolved;
118
128
  }
@@ -162,7 +172,7 @@ class ViewActor {
162
172
  request,
163
173
  meta,
164
174
  data: value
165
- }, getDataEmptyness(data));
175
+ }, getDataEmptyness(this.role, data));
166
176
 
167
177
  // render
168
178
  const notifyUpdate = (state = {}, options) => {
@@ -194,15 +204,17 @@ class ViewActor {
194
204
  silent
195
205
  });
196
206
  }
207
+
208
+ /*
197
209
  triggerAction(data) {
198
- const {
199
- element
200
- } = this;
210
+ const { element } = this;
201
211
  if (!element) {
202
212
  return;
203
213
  }
204
- (0, _utils.triggerMisoAction)(element, data);
214
+ triggerMisoAction(element, data);
205
215
  }
216
+ */
217
+
206
218
  _sliceData(data) {
207
219
  const {
208
220
  value,
@@ -219,12 +231,12 @@ class ViewActor {
219
231
  if (this.role === _constants.ROLE.CONTAINER) {
220
232
  // pass empty/nonempty information
221
233
  sliced.meta = Object.assign({}, meta, {
222
- empty: this._isContainerEmpty(value)
234
+ empty: this._isContainerEmpty(data)
223
235
  });
224
236
  }
225
237
  return (0, _commons.trimObj)(sliced);
226
238
  }
227
- _isContainerEmpty(value) {
239
+ _isContainerEmpty(data) {
228
240
  const {
229
241
  element
230
242
  } = this;
@@ -239,14 +251,10 @@ class ViewActor {
239
251
  if (!(0, _constants.isDataRole)(role)) {
240
252
  continue;
241
253
  }
242
- let sliced = value && value[role];
243
- if (role === _constants.ROLE.QUERY_SUGGESTIONS) {
244
- // special case: query suggestions -> look up value from hub
245
- // TODO: adhoc
246
- const data = this.hub.states[fields.suggestions()];
247
- sliced = data && data.value;
248
- }
249
- if (sliced && sliced.length !== 0) {
254
+ // respect data mapping
255
+ const mapping = asMappingFunction((this._views._roles.mappings || {})[role] || role);
256
+ const sliced = mapping(data, this); // TODO: the view passed in should be the view of the role, not container role
257
+ if (!isDateValueEmpty(role, sliced)) {
250
258
  return false;
251
259
  }
252
260
  }
@@ -289,17 +297,21 @@ function asMappingFunction(fn) {
289
297
  throw new Error(`Invalid mapping function: ${fn}`);
290
298
  }
291
299
  }
292
- function getDataEmptyness(data) {
300
+ function getDataEmptyness(role, data) {
293
301
  return isDataStatusRelevantToEmptyness(data) ? {
294
- empty: !!isDateValueEmpty(data)
302
+ empty: !!isDateValueEmpty(role, data && data.value)
295
303
  } : {};
296
304
  }
297
305
  function isDataStatusRelevantToEmptyness(data) {
298
306
  return data.status === _constants.STATUS.READY && !data.ongoing;
299
307
  }
300
- function isDateValueEmpty(data) {
301
- const {
302
- value
303
- } = data;
304
- return value === undefined || (Array.isArray(value) || typeof value === 'string') && !value.length;
308
+ function isDateValueEmpty(role, value) {
309
+ // TODO: ad-hoc
310
+ switch (role) {
311
+ case _constants.ROLE.ANSWER:
312
+ case _constants.ROLE.REASONING:
313
+ return !value || !value.value;
314
+ default:
315
+ return value === undefined || (Array.isArray(value) || typeof value === 'string') && !value.length;
316
+ }
305
317
  }
@@ -22,7 +22,8 @@ class ViewsActor {
22
22
  options
23
23
  }) {
24
24
  this._hub = hub;
25
- this._workflowName = workflow;
25
+ this._workflow = workflow;
26
+ this._workflowName = workflow._name;
26
27
  this._extensions = extensions;
27
28
  this._layoutFactory = layouts;
28
29
  this._options = options;
@@ -189,19 +190,6 @@ class ViewsActor {
189
190
  data = this._getData(data);
190
191
  // container first
191
192
  let views = this._getViews(true);
192
- if (!force && data._inst) {
193
- const {
194
- includes,
195
- excludes
196
- } = data._inst;
197
- if (includes) {
198
- // always include containers
199
- views = views.filter(view => view.role === _constants.ROLE.CONTAINER || includes.includes(view.role));
200
- }
201
- if (excludes) {
202
- views = views.filter(view => !excludes.includes(view.role));
203
- }
204
- }
205
193
  await Promise.all(views.map(view => view.refresh({
206
194
  force,
207
195
  data
@@ -23,11 +23,24 @@ class Sections extends _templates.Sections {
23
23
  const {
24
24
  container
25
25
  } = this._helpers;
26
- return [...super.answerGroup(options), container(options, {
26
+ return [this.question(options), this.reasoning(options), this.answer(options), this.affiliation(options), this.sources(options), container(options, {
27
27
  name: 'bottom-spacing',
28
28
  visibleWhen: 'ongoing'
29
29
  })];
30
30
  }
31
+ question(options) {
32
+ const {
33
+ container,
34
+ phrase
35
+ } = this._helpers;
36
+ return container(options, {
37
+ name: 'question',
38
+ visibleWhen: 'ready'
39
+ }, [options.parentQuestionId ? '<hr>' : '', phrase(options, {
40
+ name: 'question',
41
+ tag: 'div'
42
+ }), '<miso-question></miso-question>']);
43
+ }
31
44
  answer(options) {
32
45
  const {
33
46
  container
@@ -35,28 +48,33 @@ class Sections extends _templates.Sections {
35
48
  return container(options, {
36
49
  name: 'answer',
37
50
  visibleWhen: 'ready'
38
- }, [options.parentQuestionId ? '<hr>' : '', ...this.question(options), this.reasoning(options), '<miso-answer></miso-answer>', '<miso-feedback></miso-feedback>']);
51
+ }, ['<miso-answer></miso-answer>', '<miso-feedback></miso-feedback>']);
39
52
  }
40
- reasoning({
41
- phrases,
42
- reasoning: options = {}
43
- } = {}) {
53
+ reasoning(options = {}) {
54
+ const {
55
+ phrases,
56
+ reasoning: reasoningOptions = {}
57
+ } = options;
44
58
  const {
59
+ container,
45
60
  element,
46
61
  phraseText
47
62
  } = this._helpers;
48
63
  const classes = [];
49
- options.hide !== false && classes.push('hidden');
50
- options.expand && classes.push('expanded');
51
- return element('miso-collapsible', {
64
+ //reasoningOptions.hide !== false && classes.push('hidden');
65
+ reasoningOptions.expand && classes.push('expanded');
66
+ return container(options, {
67
+ name: 'reasoning',
68
+ visibleWhen: 'nonempty'
69
+ }, [element('miso-collapsible', {
52
70
  classes,
53
71
  'data-role': 'reasoning-container'
54
72
  }, [element('div', {
55
73
  classes: ['miso-collapsible__header'],
56
74
  'data-role': 'toggle-expand'
57
- }, [`<div class="miso-collapsible__icon">${options.icon || _svgs.BULB}</div>`, `<div class="miso-collapsible__title" data-role="title">${phraseText(phrases, 'thoughts')}</div>`, `<div class="miso-collapsible__toggle">${_svgs.CHEVRON}</div>`]), element('div', {
75
+ }, [`<div class="miso-collapsible__icon">${reasoningOptions.icon || _svgs.BULB}</div>`, `<div class="miso-collapsible__title" data-role="title">${phraseText(phrases, 'thoughts')}</div>`, `<div class="miso-collapsible__toggle">${_svgs.CHEVRON}</div>`]), element('div', {
58
76
  classes: ['miso-collapsible__body']
59
- }, ['<miso-reasoning></miso-reasoning>'])]);
77
+ }, ['<miso-reasoning></miso-reasoning>'])])]);
60
78
  }
61
79
  sources(options) {
62
80
  const {
@@ -77,16 +77,9 @@ class OptionListLayout extends _template.default {
77
77
  }
78
78
  initialize(view) {
79
79
  const {
80
- proxyElement,
81
- hub
80
+ proxyElement
82
81
  } = view;
83
- this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e)), hub.on(_index.fields.suggestions(), () => this._handleInput()) // TODO: use data mapping
84
- ];
85
- }
86
- _preprocess() {
87
- return super._preprocess({
88
- state: this._view.hub.states[_index.fields.suggestions()] // TODO: use data mapping
89
- });
82
+ this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e))];
90
83
  }
91
84
  _render(element, data, controls) {
92
85
  super._render(element, data, controls);
@@ -130,7 +130,7 @@ class TypewriterLayout extends _progressive.default {
130
130
  const sameSession = (0, _utils.fromSameSession)(prev, state);
131
131
  const sameStreak = sameSession && stage === prev.stage;
132
132
  const streak = !prev ? 0 : sameStreak ? prev.streak : prev.streak + 1;
133
- const doneAt = sameSession && prev && prev.doneAt || (status === _constants.STATUS.READY && finished ? Date.now() : undefined);
133
+ const doneAt = sameSession && prev && prev.doneAt || (status === _constants.STATUS.READY && finished ? performance.now() : undefined);
134
134
  const done = doneAt !== undefined;
135
135
  return this._prevState = Object.freeze((0, _commons.trimObj)({
136
136
  session,
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.12.1-beta.0';
7
+ var _default = exports.default = '1.12.1-beta.2';
@@ -47,7 +47,10 @@ const DEFAULT_OPTIONS = Object.freeze(Object.assign({}, _answerBased.default.DEF
47
47
  trackers: DEFAULT_TRACKERS
48
48
  }));
49
49
  const ROLES_OPTIONS = (0, _index3.mergeRolesOptions)(_answerBased.default.ROLES_OPTIONS, {
50
- members: Object.keys(DEFAULT_LAYOUTS)
50
+ members: Object.keys(DEFAULT_LAYOUTS),
51
+ mappings: {
52
+ [_constants.ROLE.QUERY_SUGGESTIONS]: _processors.mappingSuggestionsData
53
+ }
51
54
  });
52
55
  class Ask extends _answerBased.default {
53
56
  constructor(context, parentQuestionId) {
@@ -69,24 +72,8 @@ class Ask extends _answerBased.default {
69
72
  });
70
73
  }
71
74
  _initSubscriptions(args) {
72
- this._setSuggestedQuestions();
73
75
  super._initSubscriptions(args);
74
- }
75
- _setSuggestedQuestions() {
76
- const {
77
- previous
78
- } = this;
79
- if (!previous) {
80
- return;
81
- }
82
- // TODO: write question_source from payload._meta, so we don't need to write it to session.meta
83
- const values = previous.states[_index.fields.data()].value;
84
- const value = values.suggested_followup_questions || values.followup_questions || [];
85
- this._hub.update(_index.fields.suggestions(), {
86
- value: value.map(text => ({
87
- text
88
- }))
89
- });
76
+ this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.view(_constants.ROLE.REASONING), state => this._onReasoningViewUpdate(state))];
90
77
  }
91
78
  _initSession(args) {
92
79
  // register at context
@@ -153,25 +140,52 @@ class Ask extends _answerBased.default {
153
140
  // data //
154
141
  _updateData(data) {
155
142
  super._updateData(data);
156
- this._triggerReasoningShowIfNecessary(data);
143
+ this._triggerSuggestionsRefreshIfNecessary();
144
+ this._showReasoningContainerIfNecessary(data);
145
+ }
146
+ _triggerSuggestionsRefreshIfNecessary() {
147
+ const {
148
+ next
149
+ } = this;
150
+ if (!next) {
151
+ return;
152
+ }
153
+ const view = next._views.get(_constants.ROLE.QUERY_SUGGESTIONS);
154
+ view && view.refresh();
157
155
  }
158
- _triggerReasoningShowIfNecessary({
156
+ _showReasoningContainerIfNecessary({
159
157
  session,
160
158
  value
161
159
  } = {}) {
162
160
  if (!value || !value.reasoning) {
163
161
  return;
164
162
  }
163
+ // TODO: shall we solve this issue with container?
164
+ // TODO: shall we just look up on the miso-collapse element?
165
165
  const context = this._getSessionContext(session);
166
166
  if (context.reasoningShown) {
167
167
  return;
168
168
  }
169
- context.reasoningShown = true;
170
- const view = this._views.get(_constants.ROLE.REASONING);
171
- view && view.triggerAction({
172
- name: 'show',
173
- target: '[data-role="reasoning-container"]'
174
- });
169
+ const element = this._getReasoningContainerElement();
170
+ if (!element) {
171
+ return;
172
+ }
173
+ if (typeof element.show === 'function') {
174
+ element.show();
175
+ context.reasoningShown = true;
176
+ }
177
+ }
178
+
179
+ // view //
180
+ _onReasoningViewUpdate({
181
+ status,
182
+ ongoing
183
+ }) {
184
+ if (status !== _constants.STATUS.READY || ongoing) {
185
+ return;
186
+ }
187
+ const element = this._getReasoningContainerElement();
188
+ element && element.classList.add('done');
175
189
  }
176
190
 
177
191
  // interactions //
@@ -219,6 +233,11 @@ class Ask extends _answerBased.default {
219
233
  this._questionId = undefined;
220
234
  }
221
235
  }
236
+ _getReasoningContainerElement() {
237
+ const view = this._views.get(_constants.ROLE.REASONING);
238
+ const element = view && view.element;
239
+ return element && element.closest('[data-role="reasoning-container"]');
240
+ }
222
241
 
223
242
  // destroy //
224
243
  _destroy(options) {
@@ -63,7 +63,6 @@ class Workflow extends _commons.Component {
63
63
  }
64
64
  _initProperties() {}
65
65
  _initActors({
66
- name,
67
66
  roles
68
67
  }) {
69
68
  const hub = this._hub;
@@ -81,7 +80,7 @@ class Workflow extends _commons.Component {
81
80
  layouts,
82
81
  roles,
83
82
  options,
84
- workflow: name
83
+ workflow: this
85
84
  });
86
85
  this._interactions = new _index.InteractionsActor(hub, {
87
86
  client,
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _constants = require("../constants.js");
8
7
  var _hybridSearchResults = _interopRequireDefault(require("./hybrid-search-results.js"));
9
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
9
  function getSubworkflowByRole(role) {
@@ -9,6 +9,7 @@ exports.getRevision = getRevision;
9
9
  exports.mappingAnswerData = mappingAnswerData;
10
10
  exports.mappingReasoningData = mappingReasoningData;
11
11
  exports.mappingSortData = mappingSortData;
12
+ exports.mappingSuggestionsData = mappingSuggestionsData;
12
13
  exports.mergeInteraction = mergeInteraction;
13
14
  exports.retainFacetCountsInData = retainFacetCountsInData;
14
15
  exports.writeAffiliationInfoToInteraction = writeAffiliationInfoToInteraction;
@@ -51,9 +52,17 @@ function mappingSortData(_, {
51
52
  });
52
53
  });
53
54
  }
55
+ function findDefaultSortOption(options) {
56
+ for (const option of options) {
57
+ if (option.default) {
58
+ return option;
59
+ }
60
+ }
61
+ return options[0];
62
+ }
54
63
  function mappingAnswerData(data) {
55
64
  if (!data || !data.value) {
56
- return data;
65
+ return undefined;
57
66
  }
58
67
  const {
59
68
  answer = '',
@@ -68,7 +77,7 @@ function mappingAnswerData(data) {
68
77
  }
69
78
  function mappingReasoningData(data) {
70
79
  if (!data || !data.value) {
71
- return data;
80
+ return undefined;
72
81
  }
73
82
  const {
74
83
  reasoning = '',
@@ -80,13 +89,15 @@ function mappingReasoningData(data) {
80
89
  finished: !!reasoning_finished
81
90
  });
82
91
  }
83
- function findDefaultSortOption(options) {
84
- for (const option of options) {
85
- if (option.default) {
86
- return option;
87
- }
88
- }
89
- return options[0];
92
+ function mappingSuggestionsData(_, {
93
+ workflow
94
+ } = {}) {
95
+ const previous = workflow.previous;
96
+ const values = previous && previous.states[_index.fields.data()].value;
97
+ const value = values && (values.suggested_followup_questions || values.followup_questions || []);
98
+ return value && value.map(text => ({
99
+ text
100
+ }));
90
101
  }
91
102
 
92
103
  // data //
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.12.1-beta.0"
29
+ "@miso.ai/commons": "1.12.1-beta.2"
30
30
  },
31
- "version": "1.12.1-beta.0"
31
+ "version": "1.12.1-beta.2"
32
32
  }
@@ -2,10 +2,6 @@ export function session() {
2
2
  return 'session';
3
3
  }
4
4
 
5
- export function suggestions() {
6
- return 'suggestions';
7
- }
8
-
9
5
  export function completions() {
10
6
  return 'completions';
11
7
  }
package/src/actor/view.js CHANGED
@@ -3,7 +3,7 @@ import { STATUS, ROLE, isDataRole } from '../constants.js';
3
3
  import * as fields from './fields.js';
4
4
  import ProxyElement from '../util/proxy.js';
5
5
  import Tracker from './tracker.js';
6
- import { triggerMisoAction } from '../element/util/utils.js';
6
+ //import { triggerMisoAction } from '../element/util/utils.js';
7
7
 
8
8
  function statesEqual(a, b) {
9
9
  return a === b || (a && b &&
@@ -11,6 +11,7 @@ function statesEqual(a, b) {
11
11
  a.error === b.error &&
12
12
  a.value === b.value && // skip deep equals
13
13
  a.ongoing === b.ongoing &&
14
+ stateEmptyness(a) === stateEmptyness(b) && // TODO: ad-hoc
14
15
  sessionEquals(a.session, b.session));
15
16
  }
16
17
 
@@ -18,11 +19,16 @@ function sessionEquals(a, b) {
18
19
  return a === b || (a && b && a.uuid === b.uuid);
19
20
  }
20
21
 
22
+ function stateEmptyness(data) {
23
+ return data && data.meta && data.meta.empty;
24
+ }
25
+
21
26
  export default class ViewActor {
22
27
 
23
28
  constructor(views, role) {
24
29
  this._events = new EventEmitter({ target: this });
25
30
  this._views = views;
31
+ // TODO: we should pull this up to views, for the results are also used in judging container emptyness
26
32
  this._mapping = asMappingFunction((views._roles.mappings || {})[role] || role);
27
33
 
28
34
  defineValues(this, {
@@ -103,6 +109,10 @@ export default class ViewActor {
103
109
  }
104
110
  return this._tracker;
105
111
  }
112
+
113
+ get workflow() {
114
+ return this._views._workflow;
115
+ }
106
116
 
107
117
  get workflowOptions() {
108
118
  return this._views._options.resolved;
@@ -138,7 +148,7 @@ export default class ViewActor {
138
148
  }
139
149
 
140
150
  const { session, status, ongoing, request, meta, value } = data;
141
- const baseState = { session, status, ongoing, request, meta, data: value, ...getDataEmptyness(data) };
151
+ const baseState = { session, status, ongoing, request, meta, data: value, ...getDataEmptyness(this.role, data) };
142
152
 
143
153
  // render
144
154
  const notifyUpdate = (state = {}, options) => {
@@ -160,6 +170,7 @@ export default class ViewActor {
160
170
  this.hub.update(fields.view(role), state, { silent });
161
171
  }
162
172
 
173
+ /*
163
174
  triggerAction(data) {
164
175
  const { element } = this;
165
176
  if (!element) {
@@ -167,6 +178,7 @@ export default class ViewActor {
167
178
  }
168
179
  triggerMisoAction(element, data);
169
180
  }
181
+ */
170
182
 
171
183
  _sliceData(data) {
172
184
  const { value, error, status, meta, ...rest } = data;
@@ -181,13 +193,13 @@ export default class ViewActor {
181
193
  // pass empty/nonempty information
182
194
  sliced.meta = {
183
195
  ...meta,
184
- empty: this._isContainerEmpty(value),
196
+ empty: this._isContainerEmpty(data),
185
197
  };
186
198
  }
187
199
  return trimObj(sliced);
188
200
  }
189
201
 
190
- _isContainerEmpty(value) {
202
+ _isContainerEmpty(data) {
191
203
  const { element } = this;
192
204
  if (!element) {
193
205
  return false;
@@ -198,14 +210,10 @@ export default class ViewActor {
198
210
  if (!isDataRole(role)) {
199
211
  continue;
200
212
  }
201
- let sliced = value && value[role];
202
- if (role === ROLE.QUERY_SUGGESTIONS) {
203
- // special case: query suggestions -> look up value from hub
204
- // TODO: adhoc
205
- const data = this.hub.states[fields.suggestions()];
206
- sliced = data && data.value;
207
- }
208
- if (sliced && sliced.length !== 0) {
213
+ // respect data mapping
214
+ const mapping = asMappingFunction((this._views._roles.mappings || {})[role] || role);
215
+ const sliced = mapping(data, this); // TODO: the view passed in should be the view of the role, not container role
216
+ if (!isDateValueEmpty(role, sliced)) {
209
217
  return false;
210
218
  }
211
219
  }
@@ -257,15 +265,21 @@ function asMappingFunction(fn) {
257
265
  }
258
266
  }
259
267
 
260
- function getDataEmptyness(data) {
261
- return isDataStatusRelevantToEmptyness(data) ? { empty: !!isDateValueEmpty(data) } : {};
268
+ function getDataEmptyness(role, data) {
269
+ return isDataStatusRelevantToEmptyness(data) ? { empty: !!isDateValueEmpty(role, data && data.value) } : {};
262
270
  }
263
271
 
264
272
  function isDataStatusRelevantToEmptyness(data) {
265
273
  return data.status === STATUS.READY && !data.ongoing;
266
274
  }
267
275
 
268
- function isDateValueEmpty(data) {
269
- const { value } = data;
270
- return value === undefined || ((Array.isArray(value) || typeof value === 'string') && !value.length);
276
+ function isDateValueEmpty(role, value) {
277
+ // TODO: ad-hoc
278
+ switch (role) {
279
+ case ROLE.ANSWER:
280
+ case ROLE.REASONING:
281
+ return !value || !value.value;
282
+ default:
283
+ return value === undefined || ((Array.isArray(value) || typeof value === 'string') && !value.length);
284
+ }
271
285
  }
@@ -16,7 +16,8 @@ export default class ViewsActor {
16
16
  options,
17
17
  }) {
18
18
  this._hub = hub;
19
- this._workflowName = workflow;
19
+ this._workflow = workflow;
20
+ this._workflowName = workflow._name;
20
21
  this._extensions = extensions;
21
22
  this._layoutFactory = layouts;
22
23
  this._options = options;
@@ -193,17 +194,6 @@ export default class ViewsActor {
193
194
  // container first
194
195
  let views = this._getViews(true);
195
196
 
196
- if (!force && data._inst) {
197
- const { includes, excludes } = data._inst;
198
- if (includes) {
199
- // always include containers
200
- views = views.filter(view => view.role === ROLE.CONTAINER || includes.includes(view.role));
201
- }
202
- if (excludes) {
203
- views = views.filter(view => !excludes.includes(view.role));
204
- }
205
- }
206
-
207
197
  await Promise.all(views.map(view => view.refresh({ force, data })));
208
198
  }
209
199
 
@@ -13,43 +13,56 @@ class Sections extends _Sections {
13
13
  answerGroup(options) {
14
14
  const { container } = this._helpers;
15
15
  return [
16
- ...super.answerGroup(options),
16
+ this.question(options),
17
+ this.reasoning(options),
18
+ this.answer(options),
19
+ this.affiliation(options),
20
+ this.sources(options),
17
21
  container(options, { name: 'bottom-spacing', visibleWhen: 'ongoing' }),
18
22
  ];
19
23
  }
20
24
 
25
+ question(options) {
26
+ const { container, phrase } = this._helpers;
27
+ return container(options, { name: 'question', visibleWhen: 'ready' }, [
28
+ options.parentQuestionId ? '<hr>' : '',
29
+ phrase(options, { name: 'question', tag: 'div' }),
30
+ '<miso-question></miso-question>',
31
+ ]);
32
+ }
33
+
21
34
  answer(options) {
22
35
  const { container } = this._helpers;
23
36
  return container(options, { name: 'answer', visibleWhen: 'ready' }, [
24
- options.parentQuestionId ? '<hr>' : '',
25
- ...this.question(options),
26
- this.reasoning(options),
27
37
  '<miso-answer></miso-answer>',
28
38
  '<miso-feedback></miso-feedback>',
29
39
  ]);
30
40
  }
31
41
 
32
- reasoning({ phrases, reasoning: options = {} } = {}) {
33
- const { element, phraseText } = this._helpers;
42
+ reasoning(options = {}) {
43
+ const { phrases, reasoning: reasoningOptions = {} } = options;
44
+ const { container, element, phraseText } = this._helpers;
34
45
  const classes = [];
35
- options.hide !== false && classes.push('hidden');
36
- options.expand && classes.push('expanded');
37
- return element('miso-collapsible', {
38
- classes,
39
- 'data-role': 'reasoning-container',
40
- }, [
41
- element('div', {
42
- classes: ['miso-collapsible__header'],
43
- 'data-role': 'toggle-expand',
44
- }, [
45
- `<div class="miso-collapsible__icon">${options.icon || BULB}</div>`,
46
- `<div class="miso-collapsible__title" data-role="title">${phraseText(phrases, 'thoughts')}</div>`,
47
- `<div class="miso-collapsible__toggle">${CHEVRON}</div>`,
48
- ]),
49
- element('div', {
50
- classes: ['miso-collapsible__body'],
46
+ //reasoningOptions.hide !== false && classes.push('hidden');
47
+ reasoningOptions.expand && classes.push('expanded');
48
+ return container(options, { name: 'reasoning', visibleWhen: 'nonempty' }, [
49
+ element('miso-collapsible', {
50
+ classes,
51
+ 'data-role': 'reasoning-container',
51
52
  }, [
52
- '<miso-reasoning></miso-reasoning>',
53
+ element('div', {
54
+ classes: ['miso-collapsible__header'],
55
+ 'data-role': 'toggle-expand',
56
+ }, [
57
+ `<div class="miso-collapsible__icon">${reasoningOptions.icon || BULB}</div>`,
58
+ `<div class="miso-collapsible__title" data-role="title">${phraseText(phrases, 'thoughts')}</div>`,
59
+ `<div class="miso-collapsible__toggle">${CHEVRON}</div>`,
60
+ ]),
61
+ element('div', {
62
+ classes: ['miso-collapsible__body'],
63
+ }, [
64
+ '<miso-reasoning></miso-reasoning>',
65
+ ]),
53
66
  ]),
54
67
  ]);
55
68
  }
@@ -63,20 +63,13 @@ export default class OptionListLayout extends TemplateBasedLayout {
63
63
  }
64
64
 
65
65
  initialize(view) {
66
- const { proxyElement, hub } = view;
66
+ const { proxyElement } = view;
67
67
  this._unsubscribes = [
68
68
  ...this._unsubscribes,
69
69
  proxyElement.on('click', (e) => this._handleClick(e)),
70
- hub.on(fields.suggestions(), () => this._handleInput()), // TODO: use data mapping
71
70
  ];
72
71
  }
73
72
 
74
- _preprocess() {
75
- return super._preprocess({
76
- state: this._view.hub.states[fields.suggestions()], // TODO: use data mapping
77
- });
78
- }
79
-
80
73
  _render(element, data, controls) {
81
74
  super._render(element, data, controls);
82
75
  const { state } = data;
@@ -112,7 +112,7 @@ export default class TypewriterLayout extends ProgressiveLayout {
112
112
  const sameStreak = sameSession && (stage === prev.stage);
113
113
  const streak = !prev ? 0 : sameStreak ? prev.streak : prev.streak + 1;
114
114
 
115
- const doneAt = (sameSession && prev && prev.doneAt) || (status === STATUS.READY && finished ? Date.now() : undefined);
115
+ const doneAt = (sameSession && prev && prev.doneAt) || (status === STATUS.READY && finished ? performance.now() : undefined);
116
116
  const done = doneAt !== undefined;
117
117
 
118
118
  return this._prevState = Object.freeze(trimObj({
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.12.1-beta.0';
1
+ export default '1.12.1-beta.2';
@@ -1,10 +1,11 @@
1
1
  import { defineValues, trimObj, API } from '@miso.ai/commons';
2
2
  import AnswerBasedWorkflow from './answer-based.js';
3
3
  import { fields } from '../actor/index.js';
4
- import { ROLE, ORGANIC_QUESTION_SOURCE } from '../constants.js';
4
+ import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
5
5
  import { OptionListLayout, ListLayout, SearchBoxLayout, TypewriterLayout } from '../layout/index.js';
6
6
  import { mergeInteraction } from './processors.js';
7
7
  import { mergeRolesOptions, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
8
+ import { mappingSuggestionsData } from './processors.js';
8
9
 
9
10
  const DEFAULT_API_OPTIONS = Object.freeze({
10
11
  ...AnswerBasedWorkflow.DEFAULT_API_OPTIONS,
@@ -44,6 +45,9 @@ const DEFAULT_OPTIONS = Object.freeze({
44
45
 
45
46
  const ROLES_OPTIONS = mergeRolesOptions(AnswerBasedWorkflow.ROLES_OPTIONS, {
46
47
  members: Object.keys(DEFAULT_LAYOUTS),
48
+ mappings: {
49
+ [ROLE.QUERY_SUGGESTIONS]: mappingSuggestionsData,
50
+ },
47
51
  });
48
52
 
49
53
  export default class Ask extends AnswerBasedWorkflow {
@@ -65,19 +69,11 @@ export default class Ask extends AnswerBasedWorkflow {
65
69
  }
66
70
 
67
71
  _initSubscriptions(args) {
68
- this._setSuggestedQuestions();
69
72
  super._initSubscriptions(args);
70
- }
71
-
72
- _setSuggestedQuestions() {
73
- const { previous } = this;
74
- if (!previous) {
75
- return;
76
- }
77
- // TODO: write question_source from payload._meta, so we don't need to write it to session.meta
78
- const values = previous.states[fields.data()].value;
79
- const value = values.suggested_followup_questions || values.followup_questions || [];
80
- this._hub.update(fields.suggestions(), { value: value.map(text => ({ text })) });
73
+ this._unsubscribes = [
74
+ ...this._unsubscribes,
75
+ this._hub.on(fields.view(ROLE.REASONING), state => this._onReasoningViewUpdate(state)),
76
+ ];
81
77
  }
82
78
 
83
79
  _initSession(args) {
@@ -134,20 +130,46 @@ export default class Ask extends AnswerBasedWorkflow {
134
130
  // data //
135
131
  _updateData(data) {
136
132
  super._updateData(data);
137
- this._triggerReasoningShowIfNecessary(data);
133
+ this._triggerSuggestionsRefreshIfNecessary();
134
+ this._showReasoningContainerIfNecessary(data);
138
135
  }
139
136
 
140
- _triggerReasoningShowIfNecessary({ session, value } = {}) {
137
+ _triggerSuggestionsRefreshIfNecessary() {
138
+ const { next } = this;
139
+ if (!next) {
140
+ return;
141
+ }
142
+ const view = next._views.get(ROLE.QUERY_SUGGESTIONS);
143
+ view && view.refresh();
144
+ }
145
+
146
+ _showReasoningContainerIfNecessary({ session, value } = {}) {
141
147
  if (!value || !value.reasoning) {
142
148
  return;
143
149
  }
150
+ // TODO: shall we solve this issue with container?
151
+ // TODO: shall we just look up on the miso-collapse element?
144
152
  const context = this._getSessionContext(session);
145
153
  if (context.reasoningShown) {
146
154
  return;
147
155
  }
148
- context.reasoningShown = true;
149
- const view = this._views.get(ROLE.REASONING);
150
- view && view.triggerAction({ name: 'show', target: '[data-role="reasoning-container"]' });
156
+ const element = this._getReasoningContainerElement();
157
+ if (!element) {
158
+ return;
159
+ }
160
+ if (typeof element.show === 'function') {
161
+ element.show();
162
+ context.reasoningShown = true;
163
+ }
164
+ }
165
+
166
+ // view //
167
+ _onReasoningViewUpdate({ status, ongoing }) {
168
+ if (status !== STATUS.READY || ongoing) {
169
+ return;
170
+ }
171
+ const element = this._getReasoningContainerElement();
172
+ element && element.classList.add('done');
151
173
  }
152
174
 
153
175
  // interactions //
@@ -197,6 +219,12 @@ export default class Ask extends AnswerBasedWorkflow {
197
219
  }
198
220
  }
199
221
 
222
+ _getReasoningContainerElement() {
223
+ const view = this._views.get(ROLE.REASONING);
224
+ const element = view && view.element;
225
+ return element && element.closest('[data-role="reasoning-container"]');
226
+ }
227
+
200
228
  // destroy //
201
229
  _destroy(options) {
202
230
  const { parentQuestionId, questionId } = this;
@@ -60,7 +60,7 @@ export default class Workflow extends Component {
60
60
 
61
61
  _initProperties() {}
62
62
 
63
- _initActors({ name, roles }) {
63
+ _initActors({ roles }) {
64
64
  const hub = this._hub;
65
65
  const client = this._client;
66
66
  const options = this._options;
@@ -69,7 +69,7 @@ export default class Workflow extends Component {
69
69
 
70
70
  this._sessions = new SessionMaker(hub);
71
71
  this._data = new DataActor(hub, { source: sources.api(client), options });
72
- this._views = new ViewsActor(hub, { extensions, layouts, roles, options, workflow: name });
72
+ this._views = new ViewsActor(hub, { extensions, layouts, roles, options, workflow: this });
73
73
  this._interactions = new InteractionsActor(hub, { client, options });
74
74
  }
75
75
 
@@ -1,4 +1,3 @@
1
- import { ROLE } from '../constants.js';
2
1
  import HybridSearchResults from './hybrid-search-results.js';
3
2
 
4
3
  function getSubworkflowByRole(role) {
@@ -22,9 +22,18 @@ export function mappingSortData(_, { hub, workflowOptions = {} } = {}) {
22
22
  });
23
23
  }
24
24
 
25
+ function findDefaultSortOption(options) {
26
+ for (const option of options) {
27
+ if (option.default) {
28
+ return option;
29
+ }
30
+ }
31
+ return options[0];
32
+ }
33
+
25
34
  export function mappingAnswerData(data) {
26
35
  if (!data || !data.value) {
27
- return data;
36
+ return undefined;
28
37
  }
29
38
  const { answer = '', answer_stage, finished } = data.value;
30
39
  return Object.freeze({ value: answer, stage: answer_stage, finished: !!finished });
@@ -32,19 +41,17 @@ export function mappingAnswerData(data) {
32
41
 
33
42
  export function mappingReasoningData(data) {
34
43
  if (!data || !data.value) {
35
- return data;
44
+ return undefined;
36
45
  }
37
46
  const { reasoning = '', reasoning_finished } = data.value;
38
47
  return Object.freeze({ value: reasoning, stage: 'reasoning', finished: !!reasoning_finished });
39
48
  }
40
49
 
41
- function findDefaultSortOption(options) {
42
- for (const option of options) {
43
- if (option.default) {
44
- return option;
45
- }
46
- }
47
- return options[0];
50
+ export function mappingSuggestionsData(_, { workflow } = {}) {
51
+ const previous = workflow.previous;
52
+ const values = previous && previous.states[fields.data()].value;
53
+ const value = values && (values.suggested_followup_questions || values.followup_questions || []);
54
+ return value && value.map(text => ({ text }));
48
55
  }
49
56
 
50
57
  // data //