@botpress/zai 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -33,22 +33,22 @@ declare const GenerationMetadata: _bpinternal_zui.ZodObject<{
33
33
  input?: number;
34
34
  output?: number;
35
35
  };
36
+ latency?: number;
36
37
  tokens?: {
37
38
  input?: number;
38
39
  output?: number;
39
40
  };
40
- latency?: number;
41
41
  }, {
42
42
  model?: string;
43
43
  cost?: {
44
44
  input?: number;
45
45
  output?: number;
46
46
  };
47
+ latency?: number;
47
48
  tokens?: {
48
49
  input?: number;
49
50
  output?: number;
50
51
  };
51
- latency?: number;
52
52
  }>;
53
53
 
54
54
  type SaveExampleProps<TInput, TOutput> = {
@@ -113,8 +113,8 @@ declare const ZaiConfig: _bpinternal_zui.ZodObject<{
113
113
  }>>;
114
114
  namespace: _bpinternal_zui.ZodDefault<_bpinternal_zui.ZodString>;
115
115
  }, "strip", _bpinternal_zui.ZodTypeAny, {
116
- userId?: string;
117
116
  client?: Cognitive | BotpressClientLike;
117
+ userId?: string;
118
118
  modelId?: string;
119
119
  activeLearning?: {
120
120
  tableName?: string;
@@ -123,8 +123,8 @@ declare const ZaiConfig: _bpinternal_zui.ZodObject<{
123
123
  };
124
124
  namespace?: string;
125
125
  }, {
126
- userId?: string;
127
126
  client?: Cognitive | BotpressClientLike;
127
+ userId?: string;
128
128
  modelId?: string;
129
129
  activeLearning?: {
130
130
  tableName?: string;
@@ -277,7 +277,12 @@ declare const Options$3: _bpinternal_zui.ZodObject<{
277
277
  declare module '@botpress/zai' {
278
278
  interface Zai {
279
279
  /** Checks wether a condition is true or not */
280
- check(input: unknown, condition: string, options?: Options$3): Promise<boolean>;
280
+ check(input: unknown, condition: string, options?: Options$3): Promise<{
281
+ /** Whether the condition is true or not */
282
+ value: boolean;
283
+ /** The explanation of the decision */
284
+ explanation: string;
285
+ }>;
281
286
  }
282
287
  }
283
288
 
@@ -289,25 +294,25 @@ declare const Options$2: _bpinternal_zui.ZodObject<{
289
294
  filter: _bpinternal_zui.ZodBoolean;
290
295
  reason: _bpinternal_zui.ZodOptional<_bpinternal_zui.ZodString>;
291
296
  }, "strip", _bpinternal_zui.ZodTypeAny, {
292
- filter?: boolean;
293
297
  input?: any;
298
+ filter?: boolean;
294
299
  reason?: string;
295
300
  }, {
296
- filter?: boolean;
297
301
  input?: any;
302
+ filter?: boolean;
298
303
  reason?: string;
299
304
  }>, "many">>;
300
305
  }, "strip", _bpinternal_zui.ZodTypeAny, {
301
306
  examples?: {
302
- filter?: boolean;
303
307
  input?: any;
308
+ filter?: boolean;
304
309
  reason?: string;
305
310
  }[];
306
311
  tokensPerItem?: number;
307
312
  }, {
308
313
  examples?: {
309
- filter?: boolean;
310
314
  input?: any;
315
+ filter?: boolean;
311
316
  reason?: string;
312
317
  }[];
313
318
  tokensPerItem?: number;
@@ -410,7 +415,11 @@ declare module '@botpress/zai' {
410
415
  interface Zai {
411
416
  /** Tags the provided input with a list of predefined labels */
412
417
  label<T extends string>(input: unknown, labels: Labels<T>, options?: Options<T>): Promise<{
413
- [K in T]: boolean;
418
+ [K in T]: {
419
+ explanation: string;
420
+ value: boolean;
421
+ confidence: number;
422
+ };
414
423
  }>;
415
424
  }
416
425
  }
@@ -42,7 +42,7 @@ Zai.prototype.check = async function(input, condition, _options) {
42
42
  }) : [];
43
43
  const exactMatch = examples.find((x) => x.key === Key);
44
44
  if (exactMatch) {
45
- return exactMatch.output;
45
+ return { explanation: exactMatch.explanation ?? "", value: exactMatch.output };
46
46
  }
47
47
  const defaultExamples = [
48
48
  { input: "50 Cent", check: true, reason: "50 Cent is widely recognized as a public personality." },
@@ -121,6 +121,7 @@ In your "Analysis", please refer to the Expert Examples # to justify your decisi
121
121
  throw new Error(`The model did not return a valid answer. The response was: ${answer}`);
122
122
  }
123
123
  let finalAnswer;
124
+ const explanation = answer.replace(TRUE, "").replace(FALSE, "").replace(END, "").replace("Final Answer:", "").replace("Analysis:", "").trim();
124
125
  if (hasTrue && hasFalse) {
125
126
  finalAnswer = answer.lastIndexOf(TRUE) > answer.lastIndexOf(FALSE);
126
127
  } else {
@@ -146,8 +147,11 @@ In your "Analysis", please refer to the Expert Examples # to justify your decisi
146
147
  }
147
148
  },
148
149
  output: finalAnswer,
149
- explanation: answer.replace(TRUE, "").replace(FALSE, "").replace(END, "").replace("Final Answer:", "").trim()
150
+ explanation
150
151
  });
151
152
  }
152
- return finalAnswer;
153
+ return {
154
+ value: finalAnswer,
155
+ explanation: explanation.trim()
156
+ };
153
157
  };
@@ -55,6 +55,18 @@ const parseLabel = (label) => {
55
55
  }
56
56
  return LABELS.AMBIGUOUS;
57
57
  };
58
+ const getConfidence = (label) => {
59
+ switch (label) {
60
+ case LABELS.ABSOLUTELY_NOT:
61
+ case LABELS.ABSOLUTELY_YES:
62
+ return 1;
63
+ case LABELS.PROBABLY_NOT:
64
+ case LABELS.PROBABLY_YES:
65
+ return 0.5;
66
+ default:
67
+ return 0;
68
+ }
69
+ };
58
70
  Zai.prototype.label = async function(input, _labels, _options) {
59
71
  const options = Options.parse(_options ?? {});
60
72
  const labels = Labels.parse(_labels);
@@ -76,8 +88,10 @@ Zai.prototype.label = async function(input, _labels, _options) {
76
88
  const allLabels = await Promise.all(chunks.map((chunk2) => this.label(chunk2, _labels)));
77
89
  return allLabels.reduce((acc, x) => {
78
90
  Object.keys(x).forEach((key) => {
79
- if (acc[key] === true) {
80
- acc[key] = true;
91
+ if (acc[key]?.value === true) {
92
+ acc[key] = acc[key];
93
+ } else if (x[key]?.value === true) {
94
+ acc[key] = x[key];
81
95
  } else {
82
96
  acc[key] = acc[key] || x[key];
83
97
  }
@@ -96,7 +110,11 @@ Zai.prototype.label = async function(input, _labels, _options) {
96
110
  );
97
111
  const convertToAnswer = (mapping) => {
98
112
  return Object.keys(labels).reduce((acc, key) => {
99
- acc[key] = mapping[key]?.label === "ABSOLUTELY_YES" || mapping[key]?.label === "PROBABLY_YES";
113
+ acc[key] = {
114
+ explanation: mapping[key]?.explanation ?? "",
115
+ value: mapping[key]?.label === LABELS.ABSOLUTELY_YES || mapping[key]?.label === LABELS.PROBABLY_YES,
116
+ confidence: getConfidence(mapping[key]?.label)
117
+ };
100
118
  return acc;
101
119
  }, {});
102
120
  };
@@ -105,3 +105,14 @@
105
105
  {"key":"d01b8e7","value":{"output":{"id":"chatcmpl-B65VO7tHpTROLR2PFoftMZaE5Jfyf","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■json_start■\n{\n \"name\": \"JAMES BOND\",\n \"movie\": \"CASINO ROYALE\"\n}\n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":405,"inputCost":0.0010125000000000002,"outputTokens":28,"outputCost":0.00028000000000000003},"botpress":{"cost":0.0012925000000000002}},"meta":{"cached":true}}}
106
106
  {"key":"138e86b7","value":{"output":{"id":"chatcmpl-B65hlbKAnvuGuGcAhuxPVKnN69Afl","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■START■Botpress is absolutely fantastic!","index":0,"stopReason":"stop"}],"usage":{"inputTokens":95,"inputCost":0.00023750000000000003,"outputTokens":9,"outputCost":0.00009},"botpress":{"cost":0.00032750000000000005}},"meta":{"cached":false}}}
107
107
  {"key":"42e66d0b","value":{"output":{"id":"chatcmpl-B65hq1endvtZB7KVSaoKfBwuS4nve","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■START■Botpress is absolutely incredible!","index":0,"stopReason":"stop"}],"usage":{"inputTokens":94,"inputCost":0.00023500000000000002,"outputTokens":9,"outputCost":0.00009},"botpress":{"cost":0.00032500000000000004}},"meta":{"cached":false}}}
108
+ {"key":"b24750c2","value":{"output":{"id":"chatcmpl-BReZyP5RHIaBUTjo4gYV6XWN83ZIY","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■0:false \n■1:true \n■2:false \n■3:true \n■4:false ","index":0,"stopReason":"stop"}],"usage":{"inputTokens":317,"inputCost":0.0007925,"outputTokens":21,"outputCost":0.00021},"botpress":{"cost":0.0010025}},"meta":{"cached":false}}}
109
+ {"key":"3fee982e","value":{"output":{"id":"chatcmpl-BReZz9kGBc2kkqHDppuC1jvef4OXG","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■json_start■\n{\n \"name\": \"JAMES BOND\",\n \"movie\": \"CASINO ROYALE\"\n}\n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":284,"inputCost":0.00071,"outputTokens":28,"outputCost":0.00028000000000000003},"botpress":{"cost":0.00099}},"meta":{"cached":false}}}
110
+ {"key":"bca8c242","value":{"output":{"id":"chatcmpl-BReZzhBpjrqZc9i3xZQd72soEYjIU","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: \"What's up\" is another common informal greeting in English, similar to \"hello!\" from Expert Example #1, as both serve as a way to initiate friendly communication. Since Expert Example #1 regards greetings positively (as shown by the answer being ■TRUE■), \"What's up\" should similarly be evaluated as ■TRUE■.\n\nFinal Answer: ■TRUE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":309,"inputCost":0.0007725000000000001,"outputTokens":75,"outputCost":0.00075},"botpress":{"cost":0.0015225}},"meta":{"cached":false}}}
111
+ {"key":"c0182ff2","value":{"output":{"id":"chatcmpl-BRea0OnzIAwF7NjYjfNsmF2iYFGBH","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Based on the condition \"competes with us? (botpress)\", we determine whether each item is a competitor to Botpress (a conversational AI or chatbot-focused platform). Taking this into account:\n\n■0 = Nike → Not a chatbot platform; does not compete → **false** \n■1 = Ada.cx → Conversational AI platform; competes → **true** \n■2 = Adidas → Not a chatbot platform; does not compete → **false** \n■3 = Moveworks → Conversational AI platform; competes → **true** \n■4 = Lululemon → Not a chatbot platform; does not compete → **false**\n\nFinal Output:\n■0:false■1:true■2:false■3:true■4:false","index":0,"stopReason":"stop"}],"usage":{"inputTokens":239,"inputCost":0.0005975,"outputTokens":151,"outputCost":0.00151},"botpress":{"cost":0.0021075}},"meta":{"cached":false}}}
112
+ {"key":"4808af91","value":{"output":{"id":"chatcmpl-BRea4fxYZvzR6j42CL2sifEuppEuR","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"■0:false■1:true■2:false■3:true■4:false","index":0,"stopReason":"stop"}],"usage":{"inputTokens":302,"inputCost":0.000755,"outputTokens":16,"outputCost":0.00016},"botpress":{"cost":0.000915}},"meta":{"cached":false}}}
113
+ {"key":"216f51b3","value":{"output":{"id":"chatcmpl-BRfNoO17sUd83dAozRV4ULSNrL02h","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: Referring to Expert Example #1, the phrase \"what is up\" was considered NOT an official greeting. The input \"What's up\" is essentially the same phrase but in contracted form, and therefore should be treated the same way. Based on this, \"What's up\" is also NOT considered an official greeting. \n\nFinal Answer: ■FALSE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":365,"inputCost":0.0009125000000000001,"outputTokens":73,"outputCost":0.0007300000000000001},"botpress":{"cost":0.0016425000000000003}},"meta":{"cached":false}}}
114
+ {"key":"d275ae64","value":{"output":{"id":"chatcmpl-BRfO9CunJK0edu6QzwK2XSOBqN3Oz","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: The phrase \"What's up\" is a common informal greeting, similar in meaning to \"hello.\" Unlike Expert Example #1 (\"wassup\"), which is highly informal slang and not universally considered a proper greeting, \"What's up\" is widely recognized as a greeting, even if informal. Expert Example #2 demonstrates that common greetings such as \"hello\" are considered TRUE, and \"What's up\" functions in a similar way.\n\nFinal Answer: ■TRUE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":372,"inputCost":0.00093,"outputTokens":93,"outputCost":0.00093},"botpress":{"cost":0.00186}},"meta":{"cached":false}}}
115
+ {"key":"d9ea3351","value":{"output":{"id":"chatcmpl-BRfQe05pFZ6CDcvRz3FulZfaygVRa","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: In Expert Example #2, \"what is up\" is explicitly considered NOT an official greeting in the business scenario. The input \"What's up\" is the contraction of \"what is up\" and carries the exact same meaning and usage. Since Expert Example #2 determined \"what is up\" does not qualify as a greeting, the same reasoning applies to \"What's up.\" \n\nFinal Answer: ■FALSE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":428,"inputCost":0.00107,"outputTokens":84,"outputCost":0.00084},"botpress":{"cost":0.00191}},"meta":{"cached":false}}}
116
+ {"key":"d17ac02e","value":{"output":{"id":"chatcmpl-BRfTPIN75pd3nYzGGyZ7HATPTpJvG","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: In Expert Example #1, the phrase \"what is up\" was analyzed and determined to be not considered an official greeting in the given business scenario. Similarly, \"What's up\" is an informal variation of the same phrase and aligns with the analysis in Example #1. Therefore, this input should also be considered not an official greeting based on the provided examples.\n\nFinal Answer: ■FALSE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":365,"inputCost":0.0009125000000000001,"outputTokens":81,"outputCost":0.0008100000000000001},"botpress":{"cost":0.0017225}},"meta":{"cached":false}}}
117
+ {"key":"ef02ab44","value":{"output":{"id":"chatcmpl-BRfTSdx4wnjMxiOSK0VEXA2YlvT1D","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"Analysis: \"What's up\" is a phrase commonly used as a casual greeting. Referring to Expert Example #1, where \"wassup\" (a slang variant of \"What's up\") was considered not a formal greeting and the condition was deemed false, it would follow that \"What's up,\" while slightly more standard, falls under the same casual category and is not a formal greeting.\n\nFinal Answer: ■FALSE■ \n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":319,"inputCost":0.0007975,"outputTokens":84,"outputCost":0.00084},"botpress":{"cost":0.0016375}},"meta":{"cached":false}}}
118
+ {"key":"e52b868e","value":{"output":{"id":"chatcmpl-BRfYXsEiwHHpIfQOoVTu8r9NCnzWQ","provider":"OpenAI","model":"gpt-4o-2024-11-20","choices":[{"role":"assistant","type":"text","content":"```\n■is_human:【The input refers to a person named Sylvain Perron, who is likely a human being. There is no suggestion otherwise.】:ABSOLUTELY_YES■\n\n■good_person:【The input mentions Sylvain Perron has no criminal record, which may suggest he is a law-abiding person. However, being a \"good person\" encompasses more than not having a criminal record, so the information is insufficient to confirm this.】:AMBIGUOUS■\n\n■bad_person:【The absence of a criminal record might indicate that Sylvain Perron is not a bad person, though this alone is insufficient to make an absolute judgment.】:PROBABLY_NOT■\n\n■canadian:【No explicit information is given about Sylvain Perron's nationality. However, according to Expert Example #1, where all users are assumed to be Canadian given the context, we can lean towards the assumption that Sylvain Perron is Canadian.】:ABSOLUTELY_YES■\n\n■is_french:【Sylvain is a common French name, as noted in Expert Example #1. \"Perron\" is also a French surname, which might suggest a French background. However, no explicit evidence confirms this person is French rather than Canadian with a French name.】:AMBIGUOUS■\n","index":0,"stopReason":"stop"}],"usage":{"inputTokens":1085,"inputCost":0.0027125,"outputTokens":271,"outputCost":0.00271},"botpress":{"cost":0.0054225}},"meta":{"cached":false}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@botpress/zai",
3
3
  "description": "Zui AI (zai) – An LLM utility library written on top of Zui and the Botpress API",
4
- "version": "1.2.0",
4
+ "version": "2.0.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "exports": {
@@ -23,7 +23,7 @@
23
23
  "author": "",
24
24
  "license": "ISC",
25
25
  "dependencies": {
26
- "@botpress/cognitive": "^0.1.6",
26
+ "@botpress/cognitive": "0.1.16",
27
27
  "json5": "^2.2.3",
28
28
  "jsonrepair": "^3.10.0",
29
29
  "lodash-es": "^4.17.21"
@@ -19,7 +19,16 @@ const Options = z.object({
19
19
  declare module '@botpress/zai' {
20
20
  interface Zai {
21
21
  /** Checks wether a condition is true or not */
22
- check(input: unknown, condition: string, options?: Options): Promise<boolean>
22
+ check(
23
+ input: unknown,
24
+ condition: string,
25
+ options?: Options
26
+ ): Promise<{
27
+ /** Whether the condition is true or not */
28
+ value: boolean
29
+ /** The explanation of the decision */
30
+ explanation: string
31
+ }>
23
32
  }
24
33
  }
25
34
 
@@ -67,7 +76,7 @@ Zai.prototype.check = async function (this: Zai, input, condition, _options) {
67
76
 
68
77
  const exactMatch = examples.find((x) => x.key === Key)
69
78
  if (exactMatch) {
70
- return exactMatch.output
79
+ return { explanation: exactMatch.explanation ?? '', value: exactMatch.output }
71
80
  }
72
81
 
73
82
  const defaultExamples = [
@@ -165,6 +174,13 @@ In your "Analysis", please refer to the Expert Examples # to justify your decisi
165
174
  }
166
175
 
167
176
  let finalAnswer: boolean
177
+ const explanation = answer
178
+ .replace(TRUE, '')
179
+ .replace(FALSE, '')
180
+ .replace(END, '')
181
+ .replace('Final Answer:', '')
182
+ .replace('Analysis:', '')
183
+ .trim()
168
184
 
169
185
  if (hasTrue && hasFalse) {
170
186
  // If both TRUE and FALSE are present, we need to check which one was answered last
@@ -193,9 +209,12 @@ In your "Analysis", please refer to the Expert Examples # to justify your decisi
193
209
  },
194
210
  },
195
211
  output: finalAnswer,
196
- explanation: answer.replace(TRUE, '').replace(FALSE, '').replace(END, '').replace('Final Answer:', '').trim(),
212
+ explanation,
197
213
  })
198
214
  }
199
215
 
200
- return finalAnswer
216
+ return {
217
+ value: finalAnswer,
218
+ explanation: explanation.trim(),
219
+ }
201
220
  }
@@ -14,6 +14,7 @@ const LABELS = {
14
14
  PROBABLY_YES: 'PROBABLY_YES',
15
15
  ABSOLUTELY_YES: 'ABSOLUTELY_YES',
16
16
  } as const
17
+
17
18
  const ALL_LABELS = Object.values(LABELS).join(' | ')
18
19
 
19
20
  type Example<T extends string> = {
@@ -78,7 +79,11 @@ declare module '@botpress/zai' {
78
79
  labels: Labels<T>,
79
80
  options?: Options<T>
80
81
  ): Promise<{
81
- [K in T]: boolean
82
+ [K in T]: {
83
+ explanation: string
84
+ value: boolean
85
+ confidence: number
86
+ }
82
87
  }>
83
88
  }
84
89
  }
@@ -100,6 +105,20 @@ const parseLabel = (label: string): Label => {
100
105
  return LABELS.AMBIGUOUS
101
106
  }
102
107
 
108
+ const getConfidence = (label: Label) => {
109
+ switch (label) {
110
+ case LABELS.ABSOLUTELY_NOT:
111
+ case LABELS.ABSOLUTELY_YES:
112
+ return 1
113
+
114
+ case LABELS.PROBABLY_NOT:
115
+ case LABELS.PROBABLY_YES:
116
+ return 0.5
117
+ default:
118
+ return 0
119
+ }
120
+ }
121
+
103
122
  Zai.prototype.label = async function <T extends string>(this: Zai, input, _labels, _options) {
104
123
  const options = Options.parse(_options ?? {})
105
124
  const labels = Labels.parse(_labels)
@@ -127,15 +146,21 @@ Zai.prototype.label = async function <T extends string>(this: Zai, input, _label
127
146
  // Merge all the labels together (those who are true will remain true)
128
147
  return allLabels.reduce((acc, x) => {
129
148
  Object.keys(x).forEach((key) => {
130
- if (acc[key] === true) {
131
- acc[key] = true
149
+ if (acc[key]?.value === true) {
150
+ acc[key] = acc[key]
151
+ } else if (x[key]?.value === true) {
152
+ acc[key] = x[key]
132
153
  } else {
133
154
  acc[key] = acc[key] || x[key]
134
155
  }
135
156
  })
136
157
  return acc
137
158
  }, {}) as {
138
- [K in T]: boolean
159
+ [K in T]: {
160
+ explanation: string
161
+ value: boolean
162
+ confidence: number
163
+ }
139
164
  }
140
165
  }
141
166
 
@@ -152,9 +177,19 @@ Zai.prototype.label = async function <T extends string>(this: Zai, input, _label
152
177
 
153
178
  const convertToAnswer = (mapping: { [K in T]: { explanation: string; label: Label } }) => {
154
179
  return Object.keys(labels).reduce((acc, key) => {
155
- acc[key] = mapping[key]?.label === 'ABSOLUTELY_YES' || mapping[key]?.label === 'PROBABLY_YES'
180
+ acc[key] = {
181
+ explanation: mapping[key]?.explanation ?? '',
182
+ value: mapping[key]?.label === LABELS.ABSOLUTELY_YES || mapping[key]?.label === LABELS.PROBABLY_YES,
183
+ confidence: getConfidence(mapping[key]?.label),
184
+ }
156
185
  return acc
157
- }, {}) as { [K in T]: boolean }
186
+ }, {}) as {
187
+ [K in T]: {
188
+ explanation: string
189
+ value: boolean
190
+ confidence: number
191
+ }
192
+ }
158
193
  }
159
194
 
160
195
  const examples = taskId