tpmkms 7.12.4 → 7.12.5-beta.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.
@@ -30,6 +30,8 @@ const fastfood_instance = require('./fastfood.instance.json')
30
30
  double hamburger
31
31
  number 1 and 2
32
32
  number 1 2 and 3
33
+ combo 1 through 5
34
+
33
35
  */
34
36
 
35
37
  const template = {
@@ -160,7 +162,6 @@ const template = {
160
162
  { "context": [['smoothie_ingredient', 1], ['list', 0], ['smoothie_ingredient', 1], ['smoothie', 1]], ordered: true, choose: [1] },
161
163
 
162
164
  { "context": [['list', 0], ['number', 1], ['combo', 1], ['number', 1]], ordered: true, choose: [2,3] },
163
- { "context": [['withModification', 0], ['modification', 1], ['list', 0], ['modification', 1]], ordered: true, choose: [2] },
164
165
 
165
166
 
166
167
  { context: [['comboNumber', 0], ['counting',0]], choose: [0] },
@@ -224,7 +225,7 @@ const template = {
224
225
  {
225
226
  id: 'withModification',
226
227
  level: 0,
227
- isA: ['preposition'],
228
+ before: ['preposition'],
228
229
  generatorp: ({context, gp}) => `with ${gp(context.modifications)}`,
229
230
  bridge: "{ ...next(operator), modifications: after[0], flatten: false }",
230
231
  },
@@ -250,7 +251,7 @@ const template = {
250
251
  {
251
252
  id: 'comboNumber',
252
253
  convolution: true,
253
- before: ['combo'],
254
+ before: ['combo', 'preposition'],
254
255
  bridge: "{ ...next(before[0]), postModifiers: append(before[0].postModifiers, ['comboNumber']), comboNumber: after[0], instance: true, flatten: true }",
255
256
  },
256
257
  {
@@ -297,17 +298,24 @@ const template = {
297
298
  const naArray = api.getAskedForButNotAvailable()
298
299
  naArray.forEach((f) => f.paraphrase = true)
299
300
  const naContext = toContext(naArray)
300
- /*
301
- naContext.isResponse = true
302
- naContext.marker = 'verbatim'
303
- naContext.verbatim = `The following are not menu items: ${gp(naContext)}`
304
- insert(naContext)
305
- */
306
301
  verbatim(`The following are not menu items: ${gp(naContext)}`)
307
302
  // allow other motivation to run
308
303
  context.cascade = true
309
304
  }
310
305
  },
306
+ {
307
+ where: where(),
308
+ match: ({context, api}) => context.marker == 'controlEnd' && api.hasAskedForButNotAvailableModification(),
309
+ apply: ({context, api, gp, toContext, verbatim}) => {
310
+ const naArray = api.getAskedForButNotAvailableModification().map(({ item, modification }) => {
311
+ // return `${gp(item)} can not be modified with ${gp(modification)}.`
312
+ return `XXX can not be modified with ${gp(modification)}.`
313
+ })
314
+ verbatim(naArray.join(' '))
315
+ // allow other motivation to run
316
+ context.cascade = true
317
+ }
318
+ },
311
319
  ]
312
320
  },
313
321
  ({ask, api}) => {
@@ -342,17 +350,35 @@ const template = {
342
350
  {
343
351
  where: where(),
344
352
  oneShot: false,
345
- matchq: (args) => askAbout(args).length > 0,
353
+ onNevermind: ({verbatim, ...args}) => {
354
+ // this is cross km boundaries from the dialogues km to this one so the api if for dialogs.
355
+ // i need to get the one for fastfood here.
356
+ const api = args.kms.fastfood.api
357
+ const needsDrink = askAbout({ args, api })
358
+ for (const item of needsDrink) {
359
+ api.remove(item)
360
+ }
361
+ },
362
+ matchq: (args) => askAbout(args).length > 0 && args.context.marker == 'controlEnd',
346
363
  applyq: (args) => {
347
364
  args.context.cascade = true
348
365
  const needsDrink = askAbout(args)
366
+ // const details = args.gp({ marker: 'list', value: needsDrink.map((item) => item.food)})
349
367
  if (needsDrink.length > 1) {
350
368
  return `What drinks do you want?`
351
369
  } else {
352
370
  return `What drink do you want?`
353
371
  }
354
372
  },
355
- matchr: (args) => args.isA(args.context.marker, 'drink') && askAbout(args).length > 0,
373
+ matchr: (args) => {
374
+ if (args.isA(args.context.marker, 'drink') && askAbout(args).length > 0) {
375
+ const needsDrink = askAbout(args)
376
+ if (args.api.isAvailableModification(needsDrink[0].food, { ...args.context , id: args.context.value })) {
377
+ return true
378
+ }
379
+ }
380
+ return false
381
+ },
356
382
  applyr: (args) => {
357
383
  // TODO check for is available for all modifications
358
384
  const needsDrink = askAbout(args)
@@ -375,6 +401,33 @@ const template = {
375
401
  ])
376
402
  },
377
403
  {
404
+ operators: [
405
+ "([change] (meal/* || drink/*) (to/1))",
406
+ ],
407
+ hierarchy: [
408
+ ['meal', 'toAble'],
409
+ ['drink', 'toAble'],
410
+ ],
411
+ bridges: [
412
+ {
413
+ id: "change",
414
+ isA: ['verby'],
415
+ localHierarchy: [ ['thisitthat', 'meal'] ],
416
+ generatorp: ({context, gp}) => `change ${gp(context.from)} to ${gp(context.to)}`,
417
+ bridge: "{ ...next(operator), from: after[0], to: after[1].toObject }",
418
+ semantic: ({context, api, e}) => {
419
+ const state = api.state
420
+ const eFrom = e(context.from).evalue
421
+ const from = state.getIdCombo(eFrom.fromSTM ? eFrom : context.from)
422
+ const to = state.getIdCombo(context.to)
423
+ for (const item of api.items()) {
424
+ if (item.id == from.id) {
425
+ api.modify(item, { id: to.id, food: context.to })
426
+ }
427
+ }
428
+ }
429
+ },
430
+ ],
378
431
  priorities: [
379
432
  { context: [['combo', 0], ['number',1], ['list', 0], ['combo', 0]], ordered: true, choose: [0,1] },
380
433
  { context: [['list', 0], ['combo',0], ['number',1]], ordered: true, choose: [1,2] },
@@ -382,6 +435,41 @@ const template = {
382
435
  { context: [['mango', 0], ['passion',0], ['list', 0]], ordered: true, choose: [0,1] },
383
436
  { context: [['number', 1], ['mango_passion',1], ['list', 0]], ordered: true, choose: [0,1] },
384
437
  { context: [['mango', 0], ['mango_passion',0], ['passion',0], ['list', 0]], ordered: true, choose: [0,1,2] },
438
+
439
+ { context: [['drink', 0], ['list',0], ['combo',0], ['number', 0]], ordered: true, choose: [2,3] },
440
+ { context: [['drink', 1], ['list',0], ['combo',0], ['number', 0]], ordered: true, choose: [2,3] },
441
+ { context: [['withModification', 0], ['modification', 1], ['list', 0], ['modification', 1]], ordered: true, choose: [2] },
442
+ { context: [['withModification', 0], ['modification', 1], ['list', 0], ['combo', 1]], ordered: true, choose: [0] },
443
+ { context: [['combo', 2], ['list', 0], ['combo', 1], ['withModification', 1]], ordered: true, choose: [3] },
444
+ ],
445
+ },
446
+ {
447
+ operators: [
448
+ "([remove|remove,delete,drop,ditch,forget,no] (food/*))",
449
+ "([reset|reset,restart,clear])",
450
+ ],
451
+ bridges: [
452
+ {
453
+ id: 'remove',
454
+ isA: ['verby'],
455
+ bridge: "{ ...next(operator), remove: after[0], postModifiers: ['remove'] }",
456
+ semantic: ({context, api}) => {
457
+ const state = api.state
458
+ for (const item of api.items()) {
459
+ if (state.match(context.remove, item)) {
460
+ api.remove(item)
461
+ }
462
+ }
463
+ }
464
+ },
465
+ {
466
+ id: 'reset',
467
+ isA: ['verby'],
468
+ bridge: "{ ...next(operator) }",
469
+ semantic: ({context, api}) => {
470
+ api.reset()
471
+ }
472
+ },
385
473
  ],
386
474
  },
387
475
  ],
@@ -390,35 +478,66 @@ const template = {
390
478
  class API {
391
479
  initialize({ objects, config }) {
392
480
  this._objects = objects
393
- this._objects.items = []
394
- this._objects.notAvailable = []
481
+ this.reset()
395
482
  }
396
483
 
397
484
  show() {
398
485
  this._objects.show = this._objects.items
399
486
  }
400
487
 
488
+ toItem(item_id) {
489
+ if (Array.isArray(item_id)) {
490
+ return this._objects.items[item_id[0]].modifications[item_id[1]]
491
+ } else {
492
+ return this._objects.items[item_id]
493
+ }
494
+ }
495
+
496
+ new_item_id() {
497
+ const item_id = this._objects.item_id_counter
498
+ this._objects.item_id_counter += 1
499
+ return item_id
500
+ }
501
+
401
502
  // returns an item id so things can be updated if needed
402
503
  add(item) {
403
- item.item_id = this._objects.items.length
504
+ item.item_id = this.new_item_id()
404
505
  if (!item.modifications) {
405
506
  item.modifications = []
406
507
  }
508
+ item.modifications.forEach((modification, index) => {
509
+ modification.item_id = [item.item_id, index]
510
+ })
407
511
  this._objects.items.push(item)
408
- return item.item_id
512
+ }
513
+
514
+ reset() {
515
+ this._objects.items = []
516
+ this._objects.notAvailable = []
517
+ this._objects.notAvailableModification = []
518
+ this._objects.item_id_counter = 0
409
519
  }
410
520
 
411
521
  get(item_id) {
412
- return this._objects.items[item_id]
522
+ return this.toItem(item_id)
523
+ }
524
+
525
+ modify(item, changes) {
526
+ Object.assign(this.toItem(item.item_id), changes)
527
+ }
528
+
529
+ remove(item) {
530
+ this._objects.items = this._objects.items.filter( (i) => i.item_id !== item.item_id )
413
531
  }
414
532
 
415
533
  items() {
416
- return this._objects.items
534
+ return [...this._objects.items]
417
535
  }
418
536
 
419
537
  addDrink(item_id, drink) {
420
- this._objects.items[item_id].modifications.push(drink)
421
- this._objects.items[item_id].needsDrink = false
538
+ const item = this.toItem(item_id)
539
+ item.modifications.push(drink)
540
+ item.needsDrink = false
422
541
  }
423
542
 
424
543
  say(response) {
@@ -430,17 +549,40 @@ class API {
430
549
  return this._objects.notAvailable.length > 0
431
550
  }
432
551
 
552
+ hasAskedForButNotAvailableModification(item) {
553
+ return this._objects.notAvailableModification.length > 0
554
+ }
555
+
433
556
  getAskedForButNotAvailable(item) {
434
557
  const na = this._objects.notAvailable
435
558
  this._objects.notAvailable = []
436
559
  return na
437
560
  }
438
561
 
562
+ getAskedForButNotAvailableModification(item) {
563
+ const na = this._objects.notAvailableModification
564
+ this._objects.notAvailableModification = []
565
+ return na
566
+ }
567
+
439
568
  addAskedForButNotAvailable(item) {
440
569
  this._objects.notAvailable.push(item)
441
570
  }
442
571
 
572
+ addAskedForButNotAvailableModification(item, modification) {
573
+ this._objects.notAvailableModification.push({item, modification})
574
+ }
575
+
576
+ isAvailableModification(food, modification) {
577
+ if (this.isAvailable(modification)) {
578
+ if (this.args.isA(modification.id, 'fry') || this.args.isA(modification.id, 'pop')) {
579
+ return true
580
+ }
581
+ }
582
+ }
583
+
443
584
  isAvailable(item) {
585
+ item.id = item.id || item.value
444
586
  if (item.id == 'chicken_nugget') {
445
587
  if (![4,5,6,10].includes(item.pieces)) {
446
588
  return false
@@ -468,6 +610,10 @@ class API {
468
610
  item.id = 'coca_cola'
469
611
  }
470
612
 
613
+ if (item.id == 'fry') {
614
+ item.id = 'french_fry'
615
+ }
616
+
471
617
  return [
472
618
  "hamburger",
473
619
  "cheeseburger",
@@ -558,6 +704,27 @@ class API {
558
704
  }
559
705
  return map[number]
560
706
  }
707
+
708
+ canBeCombo(id) {
709
+ return this.getComboNumber(id) > 0
710
+ }
711
+
712
+ getComboNumber(id) {
713
+ const combos = [
714
+ 'single',
715
+ 'double',
716
+ 'triple',
717
+ 'baconator',
718
+ 'bacon_deluxe',
719
+ 'spicy',
720
+ 'homestyle',
721
+ 'asiago_range_chicken_club',
722
+ 'ultimate_chicken_grill',
723
+ 'chicken_nugget',
724
+ 'premium_cod',
725
+ ]
726
+ return combos.findIndex((e) => e == id) + 1
727
+ }
561
728
  }
562
729
 
563
730
  const api = new API()
@@ -567,17 +734,13 @@ class State {
567
734
  this.api = api
568
735
  }
569
736
 
570
- add(food) {
571
- let quantity = 1
572
- if (food.quantity) {
573
- quantity = food.quantity.value
574
- }
737
+ getIdCombo(food) {
575
738
  let id, combo
576
739
  if (food.comboNumber?.marker == 'numberNumberCombo') {
577
740
  id = this.api.getCombo(food.comboNumber.comboNumber.value)
578
741
  if (!id) {
579
742
  this.api.addAskedForButNotAvailable(food)
580
- return
743
+ return { done: true }
581
744
  }
582
745
  combo = true
583
746
  }
@@ -585,11 +748,11 @@ class State {
585
748
  id = this.api.getCombo(food.comboNumber.value)
586
749
  if (!id) {
587
750
  this.api.addAskedForButNotAvailable(food)
588
- return
751
+ return { done: true }
589
752
  }
590
753
  combo = true
591
754
  } else if (food.marker == 'combo') {
592
- id = food.type.value
755
+ id = food.type?.value
593
756
  combo = true
594
757
  } else {
595
758
  id = food.value
@@ -600,6 +763,32 @@ class State {
600
763
  id = 'chicken_nugget'
601
764
  }
602
765
 
766
+ return { id, combo }
767
+ }
768
+
769
+ match(pattern, item) {
770
+ Object.assign(pattern, this.getIdCombo(pattern))
771
+ if (pattern.id == item.id) {
772
+ return true
773
+ }
774
+ if (!pattern.id) {
775
+ if (pattern.combo == item.combo) {
776
+ return true
777
+ }
778
+ }
779
+ }
780
+
781
+ add(food) {
782
+ let quantity = 1
783
+ if (food.quantity) {
784
+ quantity = food.quantity.value
785
+ }
786
+
787
+ const { id, combo, done } = this.getIdCombo(food)
788
+ if (done) {
789
+ return
790
+ }
791
+
603
792
  const addSize = (item, data) => {
604
793
  if (item.size) {
605
794
  data.size = item.size.value
@@ -607,15 +796,39 @@ class State {
607
796
  return data
608
797
  }
609
798
 
799
+ const getAvailableChildren = (item) => {
800
+ // see if this is a categories of items
801
+ const descendants = this.api.args.hierarchy.descendants(item.id)
802
+ const available = []
803
+ for (const descendant of descendants) {
804
+ if (this.api.isAvailable({ id: descendant})) {
805
+ available.push(descendant)
806
+ }
807
+ }
808
+ return available
809
+ }
810
+
610
811
  let modifications
812
+ const addsInsteadOfModifications = []
611
813
  if (food.modifications) {
612
814
  modifications = []
613
815
  for (const modification of propertyToArray(food.modifications.modifications)) {
614
816
  if (modification.size) {
615
817
  food.size = modification.size
616
818
  }
617
- addSize(modification, { id: modification.value })
618
- modifications.push(addSize(modification, { id: modification.value }))
819
+
820
+ // if not a modification treat as top level request
821
+ if (!this.api.isAvailableModification(food, { ...modification, id: modification.value })) {
822
+ if (this.api.isAvailable(modification)) {
823
+ //this.add(modification)
824
+ addsInsteadOfModifications.push(modification)
825
+ } else {
826
+ this.api.addAskedForButNotAvailable(modification)
827
+ }
828
+ } else {
829
+ addSize(modification, { id: modification.value })
830
+ modifications.push(addSize(modification, { id: modification.value }))
831
+ }
619
832
  }
620
833
  }
621
834
 
@@ -632,46 +845,82 @@ class State {
632
845
  for (let i = 0; i < quantity; ++i) {
633
846
  const item = addSize(food, { id, combo, modifications, pieces, food })
634
847
  if (!this.api.isAvailable(item)) {
635
- this.api.addAskedForButNotAvailable(food)
636
- return
637
- }
638
-
639
- const item_id = this.api.add(item)
640
-
641
- if (false) {
642
- // see if followup for drink is needed
643
-
644
- const hasDrink = (item_id) => {
645
- const item = this.api.get(item_id)
646
- let hasDrink = false
647
- for (let modification of (item.modifications || [])) {
648
- if (!this.api.args.isA(modification.id, 'drink')) {
649
- hasDrink = true
650
- break
651
- }
848
+ const available = []
849
+ for (const descendant of this.api.args.hierarchy.descendants(food.marker)) {
850
+ if (this.api.isAvailable({ id: descendant })) {
851
+ available.push(descendant)
652
852
  }
653
- return hasDrink
654
- }
655
- const needsDrink = (item_id) => {
656
- const item = this.api.get(item_id)
657
- return item.needsDrink
658
853
  }
659
854
 
660
- if (!hasDrink(item_id) && needsDrink(item_id)) {
855
+ // this sentence runs but it doesnt setup the hierarchy: 'combo 1, 2, 3, 4, 5, 6, 7, 9, 10, and 11 are combos'
856
+ // i made a wrong design choice when i setup the phrase 'combo 1 etc'. I should have mapped that to the 'single_combo'
857
+ // but instead had it be a combo with a comboNumber property. That means the language layer doesnt know about the mapping
858
+ // so that phrase doesnt work. if I set it up the other way that phrase would work. This is just a demo and I have other
859
+ // demoes to write so i am not fixin that and instead will do || is a combo
860
+
861
+ if (available.length > 0 || food.marker == 'combo') {
661
862
  this.api.args.ask([
662
- {
663
- where: where(),
664
- matchq: ({objects}) => !hasDrink(item_id) && needsDrink(item_id),
665
- applyq: () => `What drink do you want?`,
666
- matchr: ({context, isA}) => isA(context.marker, 'drink'),
667
- applyr: ({context, objects, api}) => {
668
- // TODO check for is available for all modifications
669
- this.api.addDrink(item_id, { id: context.value })
670
- }
863
+ {
864
+ where: where(),
865
+ oneShot: true,
866
+ matchq: ({context}) => context.marker == 'controlEnd',
867
+ applyq: () => {
868
+ // args.context.cascade = true
869
+ const word = food.word
870
+ return `What kind of ${word}?`
671
871
  },
672
- ]
673
- )
872
+ semanticsr: [
873
+ // stuipid hack one because i didnt put combo fully into the NLI layer
874
+ {
875
+ where: where(),
876
+ match: ({context, callId, isA, api}) => {
877
+ return api.canBeCombo(context.marker)
878
+ },
879
+ apply: ({context}) => {
880
+ const comboNumber = {
881
+ value: api.getComboNumber(context.marker)
882
+ }
883
+ food.comboNumber = comboNumber
884
+ this.add(food)
885
+ }
886
+ },
887
+ {
888
+ where: where(),
889
+ match: ({context, isA}) => isA(context.marker, 'number') && !context.evaluate,
890
+ apply: ({context, e}) => {
891
+ food.comboNumber = { value: e(context).value }
892
+ this.add(Object.assign(food, context))
893
+ }
894
+ },
895
+ {
896
+ where: where(),
897
+ match: ({context, isA}) => isA(context.marker, food.marker),
898
+ apply: ({context}) => {
899
+ this.add(Object.assign(food, context))
900
+ }
901
+ },
902
+ {
903
+ where: where(),
904
+ match: ({context, isA}) => isA(context.marker, `${food.marker}_modifier`),
905
+ apply: ({context}) => {
906
+ const value = `${context.value}_${food.value}`
907
+ this.add(Object.assign(food, { value }))
908
+ }
909
+ },
910
+ ]
911
+ },
912
+ ])
913
+ } else {
914
+ this.api.addAskedForButNotAvailable(food)
674
915
  }
916
+ return
917
+ }
918
+
919
+ this.api.add(item)
920
+ this.api.args.mentioned(food)
921
+
922
+ for (const addIt of addsInsteadOfModifications) {
923
+ this.add(addIt)
675
924
  }
676
925
  }
677
926
  }
@@ -686,7 +935,7 @@ class State {
686
935
  }
687
936
  }
688
937
 
689
- const createConfig = () => {
938
+ const createConfig = (additionalConfig) => {
690
939
  const config = new Config({
691
940
  name: 'fastfood',
692
941
  operators: [
@@ -708,7 +957,7 @@ const createConfig = () => {
708
957
  },
709
958
  {
710
959
  where: where(),
711
- match: ({context, isAListable}) => isAListable(context, 'edible') && context.marker !== 'edible' && !context.same && !context.isResponse,
960
+ match: ({context, isAListable}) => isAListable(context, 'edible') && context.marker !== 'edible' && !context.same && !context.isResponse && !context.evaluate,
712
961
  apply: ({context, km, api, instance}) => {
713
962
  for (const element of propertyToArray(context)) {
714
963
  km('fastfood').api.state.add(element)
@@ -743,6 +992,9 @@ const createConfig = () => {
743
992
  config.initializer( ({api}) => {
744
993
  api.state = new State(api)
745
994
  })
995
+ if (additionalConfig) {
996
+ additionalConfig(config)
997
+ }
746
998
  config.restart_auto_rebuild()
747
999
  return config
748
1000
  }
@@ -751,16 +1003,30 @@ knowledgeModule( {
751
1003
  module,
752
1004
  description: 'fastfood related concepts',
753
1005
  createConfig,
1006
+ acceptsAdditionalConfig: true,
754
1007
  test: {
755
1008
  name: './fastfood.test.json',
756
1009
  contents: fastfood_tests,
757
1010
  checks: {
758
1011
  objects: [
759
1012
  'show',
760
- { property: 'items', filter: ['combo', 'pieces', 'size', 'item_id', 'id', 'modifications', 'needsDrink'] },
1013
+ {
1014
+ property: 'items',
1015
+ filter: [
1016
+ 'combo',
1017
+ 'pieces',
1018
+ 'size',
1019
+ 'item_id',
1020
+ 'id',
1021
+ { property: 'food', filter: [ 'marker', 'value', 'text' ] },
1022
+ { property: 'modifications', filter: [ 'id', 'item_id', 'food' ] },
1023
+ 'needsDrink'
1024
+ ],
1025
+ },
761
1026
  'changes',
762
1027
  'response',
763
1028
  { property: 'notAvailable', filter: [ 'marker', 'value', 'text' ] },
1029
+ { property: 'notAvailableModification', filter: [ 'marker', 'value', 'text' ] },
764
1030
  { property: 'quantity', filter: ['marker', 'value', 'text' ] },
765
1031
  { property: 'pieces', filter: ['marker', 'value', 'text' ] },
766
1032
  ],