@nyaruka/temba-components 0.130.1 → 0.130.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.
Files changed (246) hide show
  1. package/CHANGELOG.md +28 -4
  2. package/DEV_DATA.md +89 -0
  3. package/demo/data/flows/food-order.json +4 -4
  4. package/demo/data/flows/sample-flow.json +132 -147
  5. package/dist/temba-components.js +764 -628
  6. package/dist/temba-components.js.map +1 -1
  7. package/out-tsc/src/display/Chat.js +5 -3
  8. package/out-tsc/src/display/Chat.js.map +1 -1
  9. package/out-tsc/src/events.js.map +1 -1
  10. package/out-tsc/src/flow/CanvasNode.js +83 -78
  11. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  12. package/out-tsc/src/flow/Editor.js +1 -0
  13. package/out-tsc/src/flow/Editor.js.map +1 -1
  14. package/out-tsc/src/flow/NodeEditor.js +47 -3
  15. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  16. package/out-tsc/src/flow/actions/add_contact_urn.js +1 -1
  17. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  18. package/out-tsc/src/flow/actions/set_contact_channel.js +1 -1
  19. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  20. package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
  21. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  22. package/out-tsc/src/flow/actions/set_contact_language.js +3 -1
  23. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  24. package/out-tsc/src/flow/actions/set_contact_name.js +1 -1
  25. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  26. package/out-tsc/src/flow/actions/set_contact_status.js +17 -14
  27. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  28. package/out-tsc/src/flow/actions/set_run_result.js +1 -1
  29. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  30. package/out-tsc/src/flow/nodes/split_by_llm.js +12 -12
  31. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  32. package/out-tsc/src/flow/nodes/wait_for_response.js +609 -6
  33. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  34. package/out-tsc/src/flow/operators.js +194 -0
  35. package/out-tsc/src/flow/operators.js.map +1 -0
  36. package/out-tsc/src/flow/types.js.map +1 -1
  37. package/out-tsc/src/form/ArrayEditor.js +84 -19
  38. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  39. package/out-tsc/src/form/Checkbox.js +12 -0
  40. package/out-tsc/src/form/Checkbox.js.map +1 -1
  41. package/out-tsc/src/form/FieldRenderer.js +13 -3
  42. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  43. package/out-tsc/src/form/TextInput.js +20 -1
  44. package/out-tsc/src/form/TextInput.js.map +1 -1
  45. package/out-tsc/src/form/select/Select.js +7 -0
  46. package/out-tsc/src/form/select/Select.js.map +1 -1
  47. package/out-tsc/src/interfaces.js.map +1 -1
  48. package/out-tsc/src/layout/Dialog.js +3 -4
  49. package/out-tsc/src/layout/Dialog.js.map +1 -1
  50. package/out-tsc/src/list/RunList.js +2 -2
  51. package/out-tsc/src/list/RunList.js.map +1 -1
  52. package/out-tsc/src/live/ContactChat.js +114 -34
  53. package/out-tsc/src/live/ContactChat.js.map +1 -1
  54. package/out-tsc/src/live/ContactDetails.js +7 -0
  55. package/out-tsc/src/live/ContactDetails.js.map +1 -1
  56. package/out-tsc/src/live/ContactNameFetch.js +1 -1
  57. package/out-tsc/src/live/ContactNameFetch.js.map +1 -1
  58. package/out-tsc/test/NodeHelper.js +25 -27
  59. package/out-tsc/test/NodeHelper.js.map +1 -1
  60. package/out-tsc/test/nodes/split_by_llm.test.js +12 -4
  61. package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -1
  62. package/out-tsc/test/nodes/split_by_llm_categorize.test.js +101 -91
  63. package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -1
  64. package/out-tsc/test/nodes/split_by_random.test.js +120 -112
  65. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  66. package/out-tsc/test/nodes/wait_for_digits.test.js +131 -111
  67. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  68. package/out-tsc/test/nodes/wait_for_response.test.js +549 -85
  69. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  70. package/out-tsc/test/temba-checkbox.test.js +32 -32
  71. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  72. package/out-tsc/test/temba-contact-chat.test.js +2 -1
  73. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  74. package/out-tsc/test/temba-dropdown.test.js +0 -4
  75. package/out-tsc/test/temba-dropdown.test.js.map +1 -1
  76. package/out-tsc/test/temba-flow-editor-node.test.js +9 -4
  77. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  78. package/out-tsc/test/temba-integration-markdown.test.js +13 -15
  79. package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
  80. package/out-tsc/test/temba-node-editor.test.js +5 -38
  81. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  82. package/out-tsc/test/temba-run-list.test.js +2 -2
  83. package/out-tsc/test/temba-run-list.test.js.map +1 -1
  84. package/out-tsc/test/utils.test.js +2 -1
  85. package/out-tsc/test/utils.test.js.map +1 -1
  86. package/package.json +6 -2
  87. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  88. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  89. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  90. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  91. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  92. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  93. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  94. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  95. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  96. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  97. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  98. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  99. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  100. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  101. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  102. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  103. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  104. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  105. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  106. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  107. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  108. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  109. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  110. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  111. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  112. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  113. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  114. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  115. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  116. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  117. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  118. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  119. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  120. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  121. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  122. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  123. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  124. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  125. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  126. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  127. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  128. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  129. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  130. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  131. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  132. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  133. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  134. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  135. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  136. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  137. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  138. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  139. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  140. package/screenshots/truth/checkbox/checkbox-whitespace-label-no-background-hover.png +0 -0
  141. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  142. package/screenshots/truth/checkbox/checked.png +0 -0
  143. package/screenshots/truth/checkbox/default.png +0 -0
  144. package/screenshots/truth/editor/wait.png +0 -0
  145. package/screenshots/truth/integration/textinput-markdown-errors.png +0 -0
  146. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  147. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  148. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  149. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  150. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  151. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  152. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  153. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  154. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  155. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  156. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  157. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  158. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  159. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  160. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  161. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  162. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  163. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  164. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  165. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  166. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  167. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  168. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  169. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  170. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  171. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  172. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  173. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  174. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  175. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  176. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  177. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  178. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  179. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  180. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  181. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  182. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  183. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  184. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  185. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  186. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  187. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  188. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  189. package/screenshots/truth/run-list/basic.png +0 -0
  190. package/screenshots/truth/templates/default.png +0 -0
  191. package/screenshots/truth/wait-for-response/rules-editor.png +0 -0
  192. package/screenshots/truth/wait-for-response/timeout-editor-unchecked.png +0 -0
  193. package/screenshots/truth/wait-for-response/timeout-editor.png +0 -0
  194. package/scripts/dev-data-sync.mjs +182 -0
  195. package/src/display/Chat.ts +6 -4
  196. package/src/events.ts +6 -5
  197. package/src/flow/CanvasNode.ts +89 -79
  198. package/src/flow/Editor.ts +1 -0
  199. package/src/flow/NodeEditor.ts +55 -3
  200. package/src/flow/actions/add_contact_urn.ts +1 -1
  201. package/src/flow/actions/set_contact_channel.ts +1 -1
  202. package/src/flow/actions/set_contact_field.ts +2 -1
  203. package/src/flow/actions/set_contact_language.ts +3 -1
  204. package/src/flow/actions/set_contact_name.ts +1 -1
  205. package/src/flow/actions/set_contact_status.ts +18 -18
  206. package/src/flow/actions/set_run_result.ts +1 -1
  207. package/src/flow/nodes/split_by_llm.ts +14 -13
  208. package/src/flow/nodes/wait_for_response.ts +717 -5
  209. package/src/flow/operators.ts +215 -0
  210. package/src/flow/types.ts +10 -2
  211. package/src/form/ArrayEditor.ts +117 -37
  212. package/src/form/Checkbox.ts +12 -0
  213. package/src/form/FieldRenderer.ts +24 -3
  214. package/src/form/TextInput.ts +19 -1
  215. package/src/form/select/Select.ts +7 -0
  216. package/src/interfaces.ts +1 -1
  217. package/src/layout/Dialog.ts +4 -4
  218. package/src/list/RunList.ts +2 -2
  219. package/src/live/ContactChat.ts +144 -58
  220. package/src/live/ContactDetails.ts +7 -0
  221. package/src/live/ContactNameFetch.ts +1 -1
  222. package/static/api/labels.json +6 -1
  223. package/test/NodeHelper.ts +38 -40
  224. package/test/nodes/split_by_llm.test.ts +43 -32
  225. package/test/nodes/split_by_llm_categorize.test.ts +130 -120
  226. package/test/nodes/split_by_random.test.ts +136 -128
  227. package/test/nodes/wait_for_digits.test.ts +147 -127
  228. package/test/nodes/wait_for_response.test.ts +657 -104
  229. package/test/temba-checkbox.test.ts +36 -32
  230. package/test/temba-contact-chat.test.ts +2 -1
  231. package/test/temba-dropdown.test.ts +0 -12
  232. package/test/temba-flow-editor-node.test.ts +11 -4
  233. package/test/temba-integration-markdown.test.ts +16 -17
  234. package/test/temba-node-editor.test.ts +5 -43
  235. package/test/temba-run-list.test.ts +2 -2
  236. package/test/utils.test.ts +2 -1
  237. package/test-assets/list/runs.json +8 -8
  238. package/web-dev-mock.mjs +86 -30
  239. package/web-dev-server.config.mjs +272 -31
  240. package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
  241. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  242. package/screenshots/truth/editor/send_msg.png +0 -0
  243. package/screenshots/truth/editor/set_contact_language.png +0 -0
  244. package/screenshots/truth/editor/set_contact_name.png +0 -0
  245. package/screenshots/truth/editor/set_run_result.png +0 -0
  246. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
@@ -107,126 +107,136 @@ describe('split_by_llm_categorize node config', () => {
107
107
  });
108
108
 
109
109
  describe('node scenarios', () => {
110
- const basicRouter = createSplitRouter(['Greeting', 'Question']);
111
- helper.testNode(
112
- {
113
- uuid: 'test-node-1',
114
- actions: [
115
- {
116
- uuid: 'call-llm-uuid',
117
- type: 'call_llm',
118
- llm: { uuid: 'llm-123', name: 'Claude' },
119
- input: '@input',
120
- instructions:
121
- '@(prompt("categorize", slice(node.categories, 0, -2)))',
122
- output_local: '_llm_output'
123
- } as any
124
- ],
125
- router: basicRouter.router,
126
- exits: basicRouter.exits
127
- } as Node,
128
- { type: 'split_by_llm_categorize' },
129
- 'basic-categorization'
130
- );
131
-
132
- const premiumRouter = createSplitRouter(['Premium', 'Regular', 'VIP']);
133
- helper.testNode(
134
- {
135
- uuid: 'test-node-2',
136
- actions: [
137
- {
138
- uuid: 'call-llm-uuid-2',
139
- type: 'call_llm',
140
- llm: { uuid: 'llm-456', name: 'GPT-4' },
141
- input: '@contact.name',
142
- instructions:
143
- '@(prompt("categorize", slice(node.categories, 0, -2)))',
144
- output_local: '_llm_output'
145
- } as any
146
- ],
147
- router: premiumRouter.router,
148
- exits: premiumRouter.exits
149
- } as Node,
150
- { type: 'split_by_llm_categorize' },
151
- 'custom-input-and-result-name'
152
- );
153
-
154
- const priorityRouter = createSplitRouter([
155
- 'High',
156
- 'Medium',
157
- 'Low',
158
- 'Critical',
159
- 'Urgent'
160
- ]);
161
- helper.testNode(
162
- {
163
- uuid: 'test-node-3',
164
- actions: [
165
- {
166
- uuid: 'call-llm-uuid-3',
167
- type: 'call_llm',
168
- llm: { uuid: 'llm-789', name: 'Gemini' },
169
- input: '@fields.priority',
170
- instructions:
171
- '@(prompt("categorize", slice(node.categories, 0, -2)))',
172
- output_local: '_llm_output'
173
- } as any
174
- ],
175
- router: priorityRouter.router,
176
- exits: priorityRouter.exits
177
- } as Node,
178
- { type: 'split_by_llm_categorize' },
179
- 'many-categories'
180
- );
181
-
182
- const minimalRouter = createSplitRouter(['Yes']);
183
- helper.testNode(
184
- {
185
- uuid: 'test-node-4',
186
- actions: [
187
- {
188
- uuid: 'call-llm-uuid-4',
189
- type: 'call_llm',
190
- llm: { uuid: 'llm-minimal', name: 'Basic LLM' },
191
- input: '@input',
192
- instructions:
193
- '@(prompt("categorize", slice(node.categories, 0, -2)))',
194
- output_local: '_llm_output'
195
- } as any
196
- ],
197
- router: minimalRouter.router,
198
- exits: minimalRouter.exits
199
- } as Node,
200
- { type: 'split_by_llm_categorize' },
201
- 'minimal-categories'
202
- );
203
-
204
- const feedbackRouter = createSplitRouter([
205
- 'Bug Report',
206
- 'Feature Request',
207
- 'General Feedback',
208
- 'Support Request'
209
- ]);
210
- helper.testNode(
211
- {
212
- uuid: 'test-node-5',
213
- actions: [
214
- {
215
- uuid: 'call-llm-uuid-5',
216
- type: 'call_llm',
217
- llm: { uuid: 'llm-special', name: 'Special Characters LLM' },
218
- input: '@contact.fields.feedback',
219
- instructions:
220
- '@(prompt("categorize", slice(node.categories, 0, -2)))',
221
- output_local: '_llm_output'
222
- } as any
223
- ],
224
- router: feedbackRouter.router,
225
- exits: feedbackRouter.exits
226
- } as Node,
227
- { type: 'split_by_llm_categorize' },
228
- 'feedback-categorization'
229
- );
110
+ it('renders basic categorization', async () => {
111
+ const basicRouter = createSplitRouter(['Greeting', 'Question']);
112
+ await helper.testNode(
113
+ {
114
+ uuid: 'test-node-1',
115
+ actions: [
116
+ {
117
+ uuid: 'call-llm-uuid',
118
+ type: 'call_llm',
119
+ llm: { uuid: 'llm-123', name: 'Claude' },
120
+ input: '@input',
121
+ instructions:
122
+ '@(prompt("categorize", slice(node.categories, 0, -2)))',
123
+ output_local: '_llm_output'
124
+ } as any
125
+ ],
126
+ router: basicRouter.router,
127
+ exits: basicRouter.exits
128
+ } as Node,
129
+ { type: 'split_by_llm_categorize' },
130
+ 'basic-categorization'
131
+ );
132
+ });
133
+
134
+ it('renders custom input and result name', async () => {
135
+ const premiumRouter = createSplitRouter(['Premium', 'Regular', 'VIP']);
136
+ await helper.testNode(
137
+ {
138
+ uuid: 'test-node-2',
139
+ actions: [
140
+ {
141
+ uuid: 'call-llm-uuid-2',
142
+ type: 'call_llm',
143
+ llm: { uuid: 'llm-456', name: 'GPT-4' },
144
+ input: '@contact.name',
145
+ instructions:
146
+ '@(prompt("categorize", slice(node.categories, 0, -2)))',
147
+ output_local: '_llm_output'
148
+ } as any
149
+ ],
150
+ router: premiumRouter.router,
151
+ exits: premiumRouter.exits
152
+ } as Node,
153
+ { type: 'split_by_llm_categorize' },
154
+ 'custom-input-and-result-name'
155
+ );
156
+ });
157
+
158
+ it('renders many categories', async () => {
159
+ const priorityRouter = createSplitRouter([
160
+ 'High',
161
+ 'Medium',
162
+ 'Low',
163
+ 'Critical',
164
+ 'Urgent'
165
+ ]);
166
+ await helper.testNode(
167
+ {
168
+ uuid: 'test-node-3',
169
+ actions: [
170
+ {
171
+ uuid: 'call-llm-uuid-3',
172
+ type: 'call_llm',
173
+ llm: { uuid: 'llm-789', name: 'Gemini' },
174
+ input: '@fields.priority',
175
+ instructions:
176
+ '@(prompt("categorize", slice(node.categories, 0, -2)))',
177
+ output_local: '_llm_output'
178
+ } as any
179
+ ],
180
+ router: priorityRouter.router,
181
+ exits: priorityRouter.exits
182
+ } as Node,
183
+ { type: 'split_by_llm_categorize' },
184
+ 'many-categories'
185
+ );
186
+ });
187
+
188
+ it('renders minimal categories', async () => {
189
+ const minimalRouter = createSplitRouter(['Yes']);
190
+ await helper.testNode(
191
+ {
192
+ uuid: 'test-node-4',
193
+ actions: [
194
+ {
195
+ uuid: 'call-llm-uuid-4',
196
+ type: 'call_llm',
197
+ llm: { uuid: 'llm-minimal', name: 'Basic LLM' },
198
+ input: '@input',
199
+ instructions:
200
+ '@(prompt("categorize", slice(node.categories, 0, -2)))',
201
+ output_local: '_llm_output'
202
+ } as any
203
+ ],
204
+ router: minimalRouter.router,
205
+ exits: minimalRouter.exits
206
+ } as Node,
207
+ { type: 'split_by_llm_categorize' },
208
+ 'minimal-categories'
209
+ );
210
+ });
211
+
212
+ it('renders feedback categorization', async () => {
213
+ const feedbackRouter = createSplitRouter([
214
+ 'Bug Report',
215
+ 'Feature Request',
216
+ 'General Feedback',
217
+ 'Support Request'
218
+ ]);
219
+ await helper.testNode(
220
+ {
221
+ uuid: 'test-node-5',
222
+ actions: [
223
+ {
224
+ uuid: 'call-llm-uuid-5',
225
+ type: 'call_llm',
226
+ llm: { uuid: 'llm-special', name: 'Special Characters LLM' },
227
+ input: '@contact.fields.feedback',
228
+ instructions:
229
+ '@(prompt("categorize", slice(node.categories, 0, -2)))',
230
+ output_local: '_llm_output'
231
+ } as any
232
+ ],
233
+ router: feedbackRouter.router,
234
+ exits: feedbackRouter.exits
235
+ } as Node,
236
+ { type: 'split_by_llm_categorize' },
237
+ 'feedback-categorization'
238
+ );
239
+ });
230
240
  });
231
241
 
232
242
  describe('round-trip conversion validation', () => {
@@ -31,140 +31,148 @@ describe('split_by_random node config', () => {
31
31
  });
32
32
 
33
33
  describe('node scenarios', () => {
34
- helper.testNode(
35
- {
36
- uuid: 'test-random-node-1',
37
- actions: [],
38
- router: {
39
- type: 'random',
40
- categories: [
41
- {
42
- uuid: 'random-cat-1',
43
- name: 'Bucket A',
44
- exit_uuid: 'random-exit-1'
45
- },
46
- {
47
- uuid: 'random-cat-2',
48
- name: 'Bucket B',
49
- exit_uuid: 'random-exit-2'
50
- }
34
+ it('renders two bucket split', async () => {
35
+ await helper.testNode(
36
+ {
37
+ uuid: 'test-random-node-1',
38
+ actions: [],
39
+ router: {
40
+ type: 'random',
41
+ categories: [
42
+ {
43
+ uuid: 'random-cat-1',
44
+ name: 'Bucket A',
45
+ exit_uuid: 'random-exit-1'
46
+ },
47
+ {
48
+ uuid: 'random-cat-2',
49
+ name: 'Bucket B',
50
+ exit_uuid: 'random-exit-2'
51
+ }
52
+ ]
53
+ },
54
+ exits: [
55
+ { uuid: 'random-exit-1', destination_uuid: null },
56
+ { uuid: 'random-exit-2', destination_uuid: null }
51
57
  ]
52
- },
53
- exits: [
54
- { uuid: 'random-exit-1', destination_uuid: null },
55
- { uuid: 'random-exit-2', destination_uuid: null }
56
- ]
57
- } as Node,
58
- { type: 'split_by_random' },
59
- 'two-bucket-split'
60
- );
58
+ } as Node,
59
+ { type: 'split_by_random' },
60
+ 'two-bucket-split'
61
+ );
62
+ });
61
63
 
62
- helper.testNode(
63
- {
64
- uuid: 'test-random-node-2',
65
- actions: [],
66
- router: {
67
- type: 'random',
68
- categories: [
69
- {
70
- uuid: 'random-cat-1',
71
- name: 'Group A',
72
- exit_uuid: 'random-exit-1'
73
- },
74
- {
75
- uuid: 'random-cat-2',
76
- name: 'Group B',
77
- exit_uuid: 'random-exit-2'
78
- },
79
- {
80
- uuid: 'random-cat-3',
81
- name: 'Group C',
82
- exit_uuid: 'random-exit-3'
83
- }
64
+ it('renders three way split', async () => {
65
+ await helper.testNode(
66
+ {
67
+ uuid: 'test-random-node-2',
68
+ actions: [],
69
+ router: {
70
+ type: 'random',
71
+ categories: [
72
+ {
73
+ uuid: 'random-cat-1',
74
+ name: 'Group A',
75
+ exit_uuid: 'random-exit-1'
76
+ },
77
+ {
78
+ uuid: 'random-cat-2',
79
+ name: 'Group B',
80
+ exit_uuid: 'random-exit-2'
81
+ },
82
+ {
83
+ uuid: 'random-cat-3',
84
+ name: 'Group C',
85
+ exit_uuid: 'random-exit-3'
86
+ }
87
+ ]
88
+ },
89
+ exits: [
90
+ { uuid: 'random-exit-1', destination_uuid: null },
91
+ { uuid: 'random-exit-2', destination_uuid: null },
92
+ { uuid: 'random-exit-3', destination_uuid: null }
84
93
  ]
85
- },
86
- exits: [
87
- { uuid: 'random-exit-1', destination_uuid: null },
88
- { uuid: 'random-exit-2', destination_uuid: null },
89
- { uuid: 'random-exit-3', destination_uuid: null }
90
- ]
91
- } as Node,
92
- { type: 'split_by_random' },
93
- 'three-way-split'
94
- );
94
+ } as Node,
95
+ { type: 'split_by_random' },
96
+ 'three-way-split'
97
+ );
98
+ });
95
99
 
96
- helper.testNode(
97
- {
98
- uuid: 'test-random-node-3',
99
- actions: [],
100
- router: {
101
- type: 'random',
102
- categories: [
103
- {
104
- uuid: 'random-cat-1',
105
- name: 'Treatment',
106
- exit_uuid: 'random-exit-1'
107
- },
108
- {
109
- uuid: 'random-cat-2',
110
- name: 'Control',
111
- exit_uuid: 'random-exit-2'
112
- },
113
- {
114
- uuid: 'random-cat-3',
115
- name: 'Variant A',
116
- exit_uuid: 'random-exit-3'
117
- },
118
- {
119
- uuid: 'random-cat-4',
120
- name: 'Variant B',
121
- exit_uuid: 'random-exit-4'
122
- },
123
- {
124
- uuid: 'random-cat-5',
125
- name: 'Holdout',
126
- exit_uuid: 'random-exit-5'
127
- }
100
+ it('renders ab test multiple variants', async () => {
101
+ await helper.testNode(
102
+ {
103
+ uuid: 'test-random-node-3',
104
+ actions: [],
105
+ router: {
106
+ type: 'random',
107
+ categories: [
108
+ {
109
+ uuid: 'random-cat-1',
110
+ name: 'Treatment',
111
+ exit_uuid: 'random-exit-1'
112
+ },
113
+ {
114
+ uuid: 'random-cat-2',
115
+ name: 'Control',
116
+ exit_uuid: 'random-exit-2'
117
+ },
118
+ {
119
+ uuid: 'random-cat-3',
120
+ name: 'Variant A',
121
+ exit_uuid: 'random-exit-3'
122
+ },
123
+ {
124
+ uuid: 'random-cat-4',
125
+ name: 'Variant B',
126
+ exit_uuid: 'random-exit-4'
127
+ },
128
+ {
129
+ uuid: 'random-cat-5',
130
+ name: 'Holdout',
131
+ exit_uuid: 'random-exit-5'
132
+ }
133
+ ]
134
+ },
135
+ exits: [
136
+ { uuid: 'random-exit-1', destination_uuid: null },
137
+ { uuid: 'random-exit-2', destination_uuid: null },
138
+ { uuid: 'random-exit-3', destination_uuid: null },
139
+ { uuid: 'random-exit-4', destination_uuid: null },
140
+ { uuid: 'random-exit-5', destination_uuid: null }
128
141
  ]
129
- },
130
- exits: [
131
- { uuid: 'random-exit-1', destination_uuid: null },
132
- { uuid: 'random-exit-2', destination_uuid: null },
133
- { uuid: 'random-exit-3', destination_uuid: null },
134
- { uuid: 'random-exit-4', destination_uuid: null },
135
- { uuid: 'random-exit-5', destination_uuid: null }
136
- ]
137
- } as Node,
138
- { type: 'split_by_random' },
139
- 'ab-test-multiple-variants'
140
- );
142
+ } as Node,
143
+ { type: 'split_by_random' },
144
+ 'ab-test-multiple-variants'
145
+ );
146
+ });
141
147
 
142
- helper.testNode(
143
- {
144
- uuid: 'test-random-node-4',
145
- actions: [],
146
- router: {
147
- type: 'random',
148
- categories: [
149
- {
150
- uuid: 'random-cat-1',
151
- name: 'Sample Group',
152
- exit_uuid: 'random-exit-1'
153
- },
154
- {
155
- uuid: 'random-cat-2',
156
- name: 'Remaining Population',
157
- exit_uuid: 'random-exit-2'
158
- }
148
+ it('renders sampling split', async () => {
149
+ await helper.testNode(
150
+ {
151
+ uuid: 'test-random-node-4',
152
+ actions: [],
153
+ router: {
154
+ type: 'random',
155
+ categories: [
156
+ {
157
+ uuid: 'random-cat-1',
158
+ name: 'Sample Group',
159
+ exit_uuid: 'random-exit-1'
160
+ },
161
+ {
162
+ uuid: 'random-cat-2',
163
+ name: 'Remaining Population',
164
+ exit_uuid: 'random-exit-2'
165
+ }
166
+ ]
167
+ },
168
+ exits: [
169
+ { uuid: 'random-exit-1', destination_uuid: null },
170
+ { uuid: 'random-exit-2', destination_uuid: null }
159
171
  ]
160
- },
161
- exits: [
162
- { uuid: 'random-exit-1', destination_uuid: null },
163
- { uuid: 'random-exit-2', destination_uuid: null }
164
- ]
165
- } as Node,
166
- { type: 'split_by_random' },
167
- 'sampling-split'
168
- );
172
+ } as Node,
173
+ { type: 'split_by_random' },
174
+ 'sampling-split'
175
+ );
176
+ });
169
177
  });
170
178
  });