@botpress/zai 1.0.0-beta.1 → 1.0.0-beta.3
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/src/env.d.ts +0 -36
- package/dist/src/zai.d.ts +6 -12
- package/dist/src/zai.js +2 -0
- package/dist/src/zai.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/typings.d.ts +0 -6
- package/dist/typings.tsbuildinfo +1 -1
- package/package.json +18 -9
- package/tsconfig.json +1 -0
- package/dist/src/operations/check.test.js +0 -129
- package/dist/src/operations/extract.test.js +0 -163
- package/dist/src/operations/filter.test.js +0 -151
- package/dist/src/operations/label.test.js +0 -213
- package/dist/src/operations/rewrite.test.js +0 -96
- package/dist/src/operations/summarize.test.js +0 -22
- package/dist/src/operations/text.test.js +0 -51
- package/dist/src/operations/zai-learn.test.js +0 -71
- package/dist/src/operations/zai-retry.test.js +0 -50
- package/src/adapters/adapter.ts +0 -35
- package/src/adapters/botpress-table.ts +0 -213
- package/src/adapters/memory.ts +0 -13
- package/src/env.ts +0 -54
- package/src/index.ts +0 -11
- package/src/models.ts +0 -347
- package/src/operations/__tests/botpress_docs.txt +0 -26040
- package/src/operations/__tests/index.ts +0 -30
- package/src/operations/check.test.ts +0 -155
- package/src/operations/check.ts +0 -187
- package/src/operations/constants.ts +0 -2
- package/src/operations/errors.ts +0 -9
- package/src/operations/extract.test.ts +0 -209
- package/src/operations/extract.ts +0 -291
- package/src/operations/filter.test.ts +0 -182
- package/src/operations/filter.ts +0 -231
- package/src/operations/label.test.ts +0 -239
- package/src/operations/label.ts +0 -332
- package/src/operations/rewrite.test.ts +0 -114
- package/src/operations/rewrite.ts +0 -148
- package/src/operations/summarize.test.ts +0 -25
- package/src/operations/summarize.ts +0 -193
- package/src/operations/text.test.ts +0 -60
- package/src/operations/text.ts +0 -63
- package/src/operations/zai-learn.test.ts +0 -85
- package/src/operations/zai-retry.test.ts +0 -64
- package/src/scripts/update-models.ts +0 -74
- package/src/utils.ts +0 -61
- package/src/zai.ts +0 -179
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from 'vitest';
|
|
2
|
-
import { getClient, getZai, metadata } from './__tests';
|
|
3
|
-
import { TableAdapter } from '../adapters/botpress-table';
|
|
4
|
-
describe('zai.filter', { timeout: 60_000 }, () => {
|
|
5
|
-
let zai = getZai();
|
|
6
|
-
beforeEach(async () => {
|
|
7
|
-
zai = getZai();
|
|
8
|
-
});
|
|
9
|
-
it('basic filter with small items', async () => {
|
|
10
|
-
const value = await zai.filter([
|
|
11
|
-
{ name: 'John', description: 'is a bad person' },
|
|
12
|
-
{ name: 'Alice', description: 'is a good person' },
|
|
13
|
-
{ name: 'Bob', description: 'is a good person' },
|
|
14
|
-
{ name: 'Eve', description: 'is a bad person' },
|
|
15
|
-
{ name: 'Alex', description: 'is a good person' },
|
|
16
|
-
{ name: 'Sara', description: 'donates to charity every month' },
|
|
17
|
-
{ name: 'Tom', description: 'commits crimes and is in jail' }
|
|
18
|
-
], 'generally good people');
|
|
19
|
-
const names = value.map((v) => v.name);
|
|
20
|
-
expect(names).toMatchInlineSnapshot(`
|
|
21
|
-
[
|
|
22
|
-
"Alice",
|
|
23
|
-
"Bob",
|
|
24
|
-
"Alex",
|
|
25
|
-
"Sara",
|
|
26
|
-
]
|
|
27
|
-
`);
|
|
28
|
-
});
|
|
29
|
-
it('filtering huge array chunks it up', async () => {
|
|
30
|
-
const callAction = vi.fn();
|
|
31
|
-
const client = { ...getClient(), callAction };
|
|
32
|
-
zai = getZai().with({
|
|
33
|
-
client
|
|
34
|
-
});
|
|
35
|
-
const hugeArray = Array.from({ length: 100 }, (_, i) => ({
|
|
36
|
-
name: `Person #${i}#`,
|
|
37
|
-
description: 'blah blah '.repeat(50_000)
|
|
38
|
-
}));
|
|
39
|
-
try {
|
|
40
|
-
await zai.filter(hugeArray, 'generally good people', { tokensPerItem: 100_000 });
|
|
41
|
-
}
|
|
42
|
-
catch (err) { }
|
|
43
|
-
expect(callAction.mock.calls.length).toBeGreaterThan(20);
|
|
44
|
-
expect(JSON.stringify(callAction.mock.calls.at(0))).toContain('Person #0#');
|
|
45
|
-
expect(JSON.stringify(callAction.mock.calls.at(0))).not.toContain('Person #99#');
|
|
46
|
-
expect(JSON.stringify(callAction.mock.calls.at(-1))).not.toContain('Person #0#');
|
|
47
|
-
expect(JSON.stringify(callAction.mock.calls.at(-1))).toContain('Person #99#');
|
|
48
|
-
callAction.mockReset();
|
|
49
|
-
try {
|
|
50
|
-
await zai.filter(hugeArray, 'generally good people', { tokensPerItem: 100 });
|
|
51
|
-
}
|
|
52
|
-
catch (err) { }
|
|
53
|
-
expect(callAction.mock.calls.length).toBe(2);
|
|
54
|
-
});
|
|
55
|
-
it('filter with examples', async () => {
|
|
56
|
-
const examples = [
|
|
57
|
-
{
|
|
58
|
-
input: 'Rasa (framework)',
|
|
59
|
-
filter: true,
|
|
60
|
-
reason: 'Rasa is a chatbot framework, so it competes with us (Botpress).'
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
input: 'Rasa (coffee company)',
|
|
64
|
-
filter: false,
|
|
65
|
-
reason: 'Rasa (coffee company) is not in the chatbot or AI agent industry, therefore it does not compete with us (Botpress).'
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
input: 'Dialogflow',
|
|
69
|
-
filter: true,
|
|
70
|
-
reason: 'Dialogflow is a chatbot development product, so it competes with us (Botpress).'
|
|
71
|
-
}
|
|
72
|
-
];
|
|
73
|
-
const value = await zai.filter([{ name: 'Moveworks' }, { name: 'Ada.cx' }, { name: 'Nike' }, { name: 'Voiceflow' }, { name: 'Adidas' }], 'competes with us', { examples });
|
|
74
|
-
const names = value.map((v) => v.name);
|
|
75
|
-
expect(names).toMatchInlineSnapshot(`
|
|
76
|
-
[
|
|
77
|
-
"Moveworks",
|
|
78
|
-
"Ada.cx",
|
|
79
|
-
"Voiceflow",
|
|
80
|
-
]
|
|
81
|
-
`);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe('zai.learn.filter', { timeout: 60_000 }, () => {
|
|
85
|
-
const client = getClient();
|
|
86
|
-
let tableName = 'ZaiTestFilterInternalTable';
|
|
87
|
-
let taskId = 'filter';
|
|
88
|
-
let zai = getZai();
|
|
89
|
-
beforeEach(async () => {
|
|
90
|
-
zai = getZai().with({
|
|
91
|
-
activeLearning: {
|
|
92
|
-
enable: true,
|
|
93
|
-
taskId,
|
|
94
|
-
tableName
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
afterEach(async () => {
|
|
99
|
-
try {
|
|
100
|
-
await client.deleteTableRows({ table: tableName, deleteAllRows: true });
|
|
101
|
-
}
|
|
102
|
-
catch (err) { }
|
|
103
|
-
});
|
|
104
|
-
afterAll(async () => {
|
|
105
|
-
try {
|
|
106
|
-
await client.deleteTable({ table: tableName });
|
|
107
|
-
}
|
|
108
|
-
catch (err) { }
|
|
109
|
-
});
|
|
110
|
-
it('learns a filtering rule from examples', async () => {
|
|
111
|
-
const adapter = new TableAdapter({
|
|
112
|
-
client,
|
|
113
|
-
tableName
|
|
114
|
-
});
|
|
115
|
-
await adapter.saveExample({
|
|
116
|
-
key: 't1',
|
|
117
|
-
taskId: `zai/${taskId}`,
|
|
118
|
-
taskType: 'zai.filter',
|
|
119
|
-
instructions: 'competes with us?',
|
|
120
|
-
input: ['Rasa (framework)', 'Rasa (coffee company)'],
|
|
121
|
-
output: ['Rasa (framework)'],
|
|
122
|
-
explanation: `Rasa is a chatbot framework, so it competes with us (Botpress). We should keep it. Rasa (coffee company) is not in the chatbot or AI agent industry, therefore it does not compete with us (Botpress). We should filter it out.`,
|
|
123
|
-
metadata,
|
|
124
|
-
status: 'approved'
|
|
125
|
-
});
|
|
126
|
-
await adapter.saveExample({
|
|
127
|
-
key: 't2',
|
|
128
|
-
taskId: `zai/${taskId}`,
|
|
129
|
-
taskType: 'zai.filter',
|
|
130
|
-
instructions: 'competes with us?',
|
|
131
|
-
input: ['Voiceflow', 'Dialogflow'],
|
|
132
|
-
output: ['Voiceflow', 'Dialogflow'],
|
|
133
|
-
explanation: `Voiceflow is a chatbot development product, so it competes with us (Botpress). We should keep it. Dialogflow is a chatbot development product, so it competes with us (Botpress). We should keep it.`,
|
|
134
|
-
metadata,
|
|
135
|
-
status: 'approved'
|
|
136
|
-
});
|
|
137
|
-
const second = await zai
|
|
138
|
-
.learn(taskId)
|
|
139
|
-
.filter(['Nike', 'Ada.cx', 'Adidas', 'Moveworks', 'Lululemon'], 'competes with us? (botpress)');
|
|
140
|
-
expect(second).toMatchInlineSnapshot(`
|
|
141
|
-
[
|
|
142
|
-
"Ada.cx",
|
|
143
|
-
"Moveworks",
|
|
144
|
-
]
|
|
145
|
-
`);
|
|
146
|
-
const rows = await client.findTableRows({ table: tableName });
|
|
147
|
-
expect(rows.rows.length).toBe(3);
|
|
148
|
-
expect(rows.rows.at(-1).output.value).toEqual(second);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
//# sourceMappingURL=filter.test.js.map
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
|
|
2
|
-
import { BotpressDocumentation, getClient, getZai, metadata } from './__tests';
|
|
3
|
-
import { TableAdapter } from '../adapters/botpress-table';
|
|
4
|
-
import { check } from '@botpress/vai';
|
|
5
|
-
describe('zai.label', { timeout: 60_000 }, () => {
|
|
6
|
-
const zai = getZai();
|
|
7
|
-
it('simple labels on small text', async () => {
|
|
8
|
-
const labels = await zai.label({
|
|
9
|
-
name: 'John',
|
|
10
|
-
story: ['John donated to charity last month.', 'John is loved by his community.'],
|
|
11
|
-
criminal_record: 'John has no criminal record.'
|
|
12
|
-
}, {
|
|
13
|
-
is_human: 'is the person a human?',
|
|
14
|
-
good_person: 'is the person a good person?',
|
|
15
|
-
bad_person: 'is the person a bad person?',
|
|
16
|
-
is_criminal: 'is the person a criminal?'
|
|
17
|
-
});
|
|
18
|
-
expect(labels).toMatchInlineSnapshot(`
|
|
19
|
-
{
|
|
20
|
-
"bad_person": false,
|
|
21
|
-
"good_person": true,
|
|
22
|
-
"is_criminal": false,
|
|
23
|
-
"is_human": true,
|
|
24
|
-
}
|
|
25
|
-
`);
|
|
26
|
-
});
|
|
27
|
-
it('simple labels with example', async () => {
|
|
28
|
-
const labels = {
|
|
29
|
-
is_human: 'is the person a human?',
|
|
30
|
-
good_person: 'is the person a good person?',
|
|
31
|
-
bad_person: 'is the person a bad person?',
|
|
32
|
-
canadian: 'is the person canadian?',
|
|
33
|
-
is_french: 'is the person french?'
|
|
34
|
-
};
|
|
35
|
-
const initial = await zai.label(`Sylvain Perron has no criminal record.`, labels);
|
|
36
|
-
expect(initial.canadian).toBe(false);
|
|
37
|
-
expect(initial.is_french).toBe(false);
|
|
38
|
-
expect(initial.bad_person).toBe(false);
|
|
39
|
-
expect(initial.is_human).toBe(true);
|
|
40
|
-
const second = await zai.label(`Sylvain Perron has no criminal record.`, labels, {
|
|
41
|
-
examples: [
|
|
42
|
-
{
|
|
43
|
-
input: 'Sylvain Pellerin has no criminal record.',
|
|
44
|
-
labels: {
|
|
45
|
-
is_french: {
|
|
46
|
-
label: 'ABSOLUTELY_YES',
|
|
47
|
-
explanation: 'Important: Sylvain Pellerin is a common French name.'
|
|
48
|
-
},
|
|
49
|
-
canadian: {
|
|
50
|
-
label: 'ABSOLUTELY_YES',
|
|
51
|
-
explanation: 'Important: We assume all person named Sylvain are Canadian (business rule).'
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
input: 'Sylvain Bouchard is a criminal.',
|
|
57
|
-
labels: {
|
|
58
|
-
bad_person: {
|
|
59
|
-
label: 'PROBABLY_YES',
|
|
60
|
-
explanation: 'Important: Sylvain Bouchard is a criminal, so probably a bad person.'
|
|
61
|
-
},
|
|
62
|
-
is_french: {
|
|
63
|
-
label: 'ABSOLUTELY_YES',
|
|
64
|
-
explanation: 'Important: Sylvain is a common French name.'
|
|
65
|
-
},
|
|
66
|
-
canadian: {
|
|
67
|
-
label: 'ABSOLUTELY_YES',
|
|
68
|
-
explanation: 'Important: We assume all person named Sylvain are Canadian (business rule).'
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
});
|
|
74
|
-
expect(second.canadian).toBe(true);
|
|
75
|
-
expect(second.is_french).toBe(true);
|
|
76
|
-
expect(second.is_human).toBe(true);
|
|
77
|
-
expect(second.bad_person).toBe(false);
|
|
78
|
-
});
|
|
79
|
-
it('label a huge text', async () => {
|
|
80
|
-
const labels = await zai.label(BotpressDocumentation, {
|
|
81
|
-
is_about_animals: 'is the text about animals?',
|
|
82
|
-
contains_lua_code: 'does the text contain Lua code?',
|
|
83
|
-
contains_python_code: 'does the text contain Python code?',
|
|
84
|
-
contains_js_code: 'does the text contain JavaScript code?',
|
|
85
|
-
is_botpress: 'is the text about Botpress?',
|
|
86
|
-
is_rasa: 'is the text about Rasa?',
|
|
87
|
-
has_flows: 'does the text mention flows?',
|
|
88
|
-
has_api: 'does the text mention the Botpress API?',
|
|
89
|
-
has_enterprise: 'does the text mention Botpress Enterprise?',
|
|
90
|
-
has_workspaces: 'does the text mention workspaces?',
|
|
91
|
-
has_webchat: 'does the text mention the Webchat?',
|
|
92
|
-
has_hitl: 'does the text mention HITL (human in the loop)?'
|
|
93
|
-
});
|
|
94
|
-
expect(labels).toMatchInlineSnapshot(`
|
|
95
|
-
{
|
|
96
|
-
"contains_js_code": true,
|
|
97
|
-
"contains_lua_code": false,
|
|
98
|
-
"contains_python_code": false,
|
|
99
|
-
"has_api": true,
|
|
100
|
-
"has_enterprise": true,
|
|
101
|
-
"has_flows": true,
|
|
102
|
-
"has_hitl": true,
|
|
103
|
-
"has_webchat": true,
|
|
104
|
-
"has_workspaces": true,
|
|
105
|
-
"is_about_animals": false,
|
|
106
|
-
"is_botpress": true,
|
|
107
|
-
"is_rasa": false,
|
|
108
|
-
}
|
|
109
|
-
`);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
describe('zai.learn.label', { timeout: 60_000 }, () => {
|
|
113
|
-
const client = getClient();
|
|
114
|
-
let tableName = 'ZaiTestLabelInternalTable';
|
|
115
|
-
let taskId = 'label';
|
|
116
|
-
let zai = getZai();
|
|
117
|
-
beforeEach(async () => {
|
|
118
|
-
zai = getZai().with({
|
|
119
|
-
activeLearning: {
|
|
120
|
-
enable: true,
|
|
121
|
-
taskId,
|
|
122
|
-
tableName
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
afterEach(async () => {
|
|
127
|
-
try {
|
|
128
|
-
await client.deleteTableRows({ table: tableName, deleteAllRows: true });
|
|
129
|
-
}
|
|
130
|
-
catch (err) { }
|
|
131
|
-
});
|
|
132
|
-
afterAll(async () => {
|
|
133
|
-
try {
|
|
134
|
-
await client.deleteTable({ table: tableName });
|
|
135
|
-
}
|
|
136
|
-
catch (err) { }
|
|
137
|
-
});
|
|
138
|
-
it('learns a labelling rule from examples', async () => {
|
|
139
|
-
const adapter = new TableAdapter({
|
|
140
|
-
client,
|
|
141
|
-
tableName
|
|
142
|
-
});
|
|
143
|
-
const value = await zai.learn(taskId).label(`Sylvain Perron has no criminal record.`, {
|
|
144
|
-
is_human: 'is the person a human?',
|
|
145
|
-
good_person: 'is the person a good person?',
|
|
146
|
-
bad_person: 'is the person a bad person?',
|
|
147
|
-
canadian: 'is the person canadian?',
|
|
148
|
-
is_french: 'is the person french?'
|
|
149
|
-
});
|
|
150
|
-
expect(value.is_human).toBe(true);
|
|
151
|
-
expect(value.is_french).toBe(false);
|
|
152
|
-
expect(value.canadian).toBe(false);
|
|
153
|
-
let rows = await client.findTableRows({ table: tableName });
|
|
154
|
-
expect(rows.rows.length).toBe(1);
|
|
155
|
-
await adapter.saveExample({
|
|
156
|
-
key: 't1',
|
|
157
|
-
taskId: `zai/${taskId}`,
|
|
158
|
-
taskType: 'zai.label',
|
|
159
|
-
instructions: 'label the sentence',
|
|
160
|
-
input: 'Sylvain Pellerin has no criminal record.',
|
|
161
|
-
output: {
|
|
162
|
-
is_french: {
|
|
163
|
-
label: 'ABSOLUTELY_YES',
|
|
164
|
-
explanation: 'Important: Sylvain is a common French name.'
|
|
165
|
-
},
|
|
166
|
-
canadian: {
|
|
167
|
-
label: 'ABSOLUTELY_YES',
|
|
168
|
-
explanation: 'Since we are doing business only in Canada, we assume all users are Canadians.'
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
// The below doesn't make sense on purpose, it's just to test the influence of the explanation on the next prediction
|
|
172
|
-
explanation: `IMPORTANT: Sylvain is a common French name and since we're doing business only in Canada, we assume ALL users are Canadians as soon as they mention a French name.`,
|
|
173
|
-
metadata,
|
|
174
|
-
status: 'approved'
|
|
175
|
-
});
|
|
176
|
-
await adapter.saveExample({
|
|
177
|
-
key: 't2',
|
|
178
|
-
taskId: `zai/${taskId}`,
|
|
179
|
-
taskType: 'zai.label',
|
|
180
|
-
instructions: 'label the sentence',
|
|
181
|
-
input: 'Joannie Côté has a dog.',
|
|
182
|
-
output: {
|
|
183
|
-
is_french: {
|
|
184
|
-
label: 'ABSOLUTELY_YES',
|
|
185
|
-
explanation: 'Important: Joannie is a common French name and Côté is a common French last name.'
|
|
186
|
-
},
|
|
187
|
-
canadian: {
|
|
188
|
-
label: 'ABSOLUTELY_YES',
|
|
189
|
-
explanation: 'Since we are doing business only in Canada, we assume all users are Canadians.'
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
metadata,
|
|
193
|
-
status: 'approved'
|
|
194
|
-
});
|
|
195
|
-
rows = await client.findTableRows({ table: tableName });
|
|
196
|
-
expect(rows.rows.length).toBe(3);
|
|
197
|
-
const second = await zai.learn(taskId).label(`Sylvain Perron has no criminal record.`, {
|
|
198
|
-
is_human: 'is the person a human?',
|
|
199
|
-
good_person: 'is the person a good person?',
|
|
200
|
-
bad_person: 'is the person a bad person?',
|
|
201
|
-
canadian: 'is the person canadian?',
|
|
202
|
-
is_french: 'is the person french?'
|
|
203
|
-
});
|
|
204
|
-
expect(second.is_human).toBe(true);
|
|
205
|
-
expect(second.is_french).toBe(true);
|
|
206
|
-
expect(second.canadian).toBe(true);
|
|
207
|
-
rows = await client.findTableRows({ table: tableName });
|
|
208
|
-
expect(rows.rows.length).toBe(3);
|
|
209
|
-
check(rows.rows[0].output.value.canadian, 'label is positive (yes) and there is an explanation of why').toBe(true);
|
|
210
|
-
check(rows.rows[0].output.value.is_french, 'label is positive (yes) and there is an explanation of why').toBe(true);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
//# sourceMappingURL=label.test.js.map
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
|
|
2
|
-
import { check } from '@botpress/vai';
|
|
3
|
-
import { getClient, getZai, metadata, tokenizer } from './__tests';
|
|
4
|
-
import { TableAdapter } from '../adapters/botpress-table';
|
|
5
|
-
const Zoe = `
|
|
6
|
-
Part 1. Zoe walks to the park.
|
|
7
|
-
Part 2. She meets her friend.
|
|
8
|
-
Part 3. They play together.
|
|
9
|
-
Part 4. They have a picnic.
|
|
10
|
-
Part 5. They go home.
|
|
11
|
-
`.trim();
|
|
12
|
-
describe('zai.rewrite', { timeout: 60_000 }, () => {
|
|
13
|
-
const zai = getZai();
|
|
14
|
-
it('transforms text to all caps', async () => {
|
|
15
|
-
const result = await zai.rewrite(`Hello, what is the time today?`, 'write in all caps');
|
|
16
|
-
expect(result).toBe(`HELLO, WHAT IS THE TIME TODAY?`);
|
|
17
|
-
});
|
|
18
|
-
it('transforms text to all caps and respects tokens restrictions', async () => {
|
|
19
|
-
const result = await zai.rewrite(Zoe, 'write in all caps', { length: 15 });
|
|
20
|
-
expect(tokenizer.count(result)).toBeLessThanOrEqual(20);
|
|
21
|
-
expect(result).toContain(`PART 1. ZOE WALKS TO THE PARK`);
|
|
22
|
-
expect(result).not.toContain(`PART 3`);
|
|
23
|
-
});
|
|
24
|
-
it('french translation of the story', async () => {
|
|
25
|
-
const result = await zai.rewrite(Zoe, 'translate to french');
|
|
26
|
-
check(result, 'is a french story about Zeo and with 5 parts').toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it('Throws if input is bigger than the model max tokens', async () => {
|
|
29
|
-
await expect(zai.rewrite(Zoe.repeat(100_000), 'translate to french')).rejects.toThrow(/tokens/i);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('zai.learn.rewrite', { timeout: 60_000 }, () => {
|
|
33
|
-
const client = getClient();
|
|
34
|
-
let tableName = 'ZaiTestRewriteInternalTable';
|
|
35
|
-
let taskId = 'rewrite';
|
|
36
|
-
let zai = getZai();
|
|
37
|
-
beforeEach(async () => {
|
|
38
|
-
zai = getZai().with({
|
|
39
|
-
activeLearning: {
|
|
40
|
-
enable: true,
|
|
41
|
-
taskId,
|
|
42
|
-
tableName
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
afterEach(async () => {
|
|
47
|
-
try {
|
|
48
|
-
await client.deleteTableRows({ table: tableName, deleteAllRows: true });
|
|
49
|
-
}
|
|
50
|
-
catch (err) { }
|
|
51
|
-
});
|
|
52
|
-
afterAll(async () => {
|
|
53
|
-
try {
|
|
54
|
-
await client.deleteTable({ table: tableName });
|
|
55
|
-
}
|
|
56
|
-
catch (err) { }
|
|
57
|
-
});
|
|
58
|
-
it('learns rewrite rules from examples', async () => {
|
|
59
|
-
const adapter = new TableAdapter({
|
|
60
|
-
client,
|
|
61
|
-
tableName
|
|
62
|
-
});
|
|
63
|
-
const value = await zai.learn(taskId).rewrite(`Botpress is awesome`, 'write it like we want it');
|
|
64
|
-
check(value, `The text means more or less the same as "Botpress is awesome" but slightly different`).toBe(true);
|
|
65
|
-
let rows = await client.findTableRows({ table: tableName });
|
|
66
|
-
expect(rows.rows.length).toBe(1);
|
|
67
|
-
expect(rows.rows[0].output.value).toBe(value);
|
|
68
|
-
await adapter.saveExample({
|
|
69
|
-
key: 't1',
|
|
70
|
-
taskId: `zai/${taskId}`,
|
|
71
|
-
taskType: 'zai.rewrite',
|
|
72
|
-
instructions: 'write it like we want it',
|
|
73
|
-
input: 'Microsoft is a big company',
|
|
74
|
-
output: `# MICROSOFT IS A BIG COMPANY`,
|
|
75
|
-
metadata,
|
|
76
|
-
status: 'approved'
|
|
77
|
-
});
|
|
78
|
-
await adapter.saveExample({
|
|
79
|
-
key: 't2',
|
|
80
|
-
taskId: `zai/${taskId}`,
|
|
81
|
-
taskType: 'zai.rewrite',
|
|
82
|
-
instructions: 'write it like we want it',
|
|
83
|
-
input: 'Google is an evil company',
|
|
84
|
-
output: `# GOOGLE IS AN EVIL COMPANY`,
|
|
85
|
-
metadata,
|
|
86
|
-
status: 'approved'
|
|
87
|
-
});
|
|
88
|
-
const second = await zai.learn(taskId).rewrite(`Botpress is awesome`, 'write it like we want it');
|
|
89
|
-
rows = await client.findTableRows({ table: tableName });
|
|
90
|
-
expect(rows.rows.length).toBe(3);
|
|
91
|
-
check(second, `The text is "BOTPRESS IS AWESOME" and starts with a hashtag`).toBe(true);
|
|
92
|
-
expect(rows.rows.length).toBe(3);
|
|
93
|
-
expect(rows.rows[0].output.value).toBe(second);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
//# sourceMappingURL=rewrite.test.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { check } from '@botpress/vai';
|
|
2
|
-
import { describe, it } from 'vitest';
|
|
3
|
-
import { BotpressDocumentation, getZai } from './__tests';
|
|
4
|
-
describe('zai.summarize', () => {
|
|
5
|
-
const zai = getZai();
|
|
6
|
-
it.skip('summarize long document to a concise 2000 token summary', async () => {
|
|
7
|
-
const result = await zai.summarize(BotpressDocumentation, {
|
|
8
|
-
length: 2000,
|
|
9
|
-
prompt: `Extract the Table of Contents for the Botpress Documentation. Pay special attention to all the different features. Focus on horizontal coverage of features rather than going in depth into one feature. The goal is to have a complete overview of what the documentation covers.`
|
|
10
|
-
});
|
|
11
|
-
check(result, 'The text is a summary of the Botpress documentation').toBe(true);
|
|
12
|
-
check(result, 'The text explains shortly what botpress is').toBe(true);
|
|
13
|
-
check(result, 'The text uses markdown format').toBe(true);
|
|
14
|
-
check(result, 'The text has some information about integrations').toBe(true);
|
|
15
|
-
check(result, 'The text has a section about Flows (or Workflows)').toBe(true);
|
|
16
|
-
check(result, 'The text has a section about the Botpress API').toBe(true);
|
|
17
|
-
check(result, 'The text mentions the notion of workspaces').toBe(true);
|
|
18
|
-
check(result, 'The text has some information about the Webchat').toBe(true);
|
|
19
|
-
check(result, 'The text has some information about HITL (human in the loop)').toBe(true);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
//# sourceMappingURL=summarize.test.js.map
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { check } from '@botpress/vai';
|
|
3
|
-
import { getZai, tokenizer } from './__tests';
|
|
4
|
-
describe('zai.text', { timeout: 60_000 }, () => {
|
|
5
|
-
const zai = getZai();
|
|
6
|
-
it('generate a horror novel with no params', async () => {
|
|
7
|
-
const story = await zai.text('write a short horror novel');
|
|
8
|
-
check(story, 'is a short horror story').toBe(true);
|
|
9
|
-
});
|
|
10
|
-
it('No fluffy text at the beginning', async () => {
|
|
11
|
-
const story = await zai.text('write a short horror novel');
|
|
12
|
-
check(story, 'There is no LLM fluff at the beginning', {
|
|
13
|
-
examples: [
|
|
14
|
-
{
|
|
15
|
-
value: 'Title: A horror story\nChapter 1: The woods\nOnce upen a time, ...',
|
|
16
|
-
expected: true,
|
|
17
|
-
reason: 'It begins straight with a story, no fluff at the beginning'
|
|
18
|
-
},
|
|
19
|
-
{ value: 'Once upon a time, a ...', expected: true, reason: 'The story starts directly' },
|
|
20
|
-
{
|
|
21
|
-
value: 'Sure, I will generate a story.\nOnce upen a time, a...',
|
|
22
|
-
expected: false,
|
|
23
|
-
reason: 'There is some fluff at the beginning'
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
}).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it('No fluffy text at the end', async () => {
|
|
29
|
-
const story = await zai.text('write a short horror novel');
|
|
30
|
-
check(story, 'There is no LLM fluff at the end', {
|
|
31
|
-
examples: [
|
|
32
|
-
{
|
|
33
|
-
value: 'Title: A horror story\nChapter 1: The woods\nOnce upen a time, ... The End.',
|
|
34
|
-
expected: true,
|
|
35
|
-
reason: 'The end is clear and direct, no fluff at the end'
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
value: 'Sure, I will generate a story.\nOnce upen a time, a... The End.\nLet me know if you want more or if you are happy with this.',
|
|
39
|
-
expected: false,
|
|
40
|
-
reason: 'There is some fluff from the assistant at the end.'
|
|
41
|
-
}
|
|
42
|
-
]
|
|
43
|
-
}).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it('length/max tokens param', async () => {
|
|
46
|
-
const story = await zai.text('write a short but complete horror story (with conclusion)', { length: 100 });
|
|
47
|
-
expect(tokenizer.count(story)).toBeLessThanOrEqual(110);
|
|
48
|
-
check(story, 'could be the beginning of a horror story').toBe(true);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
//# sourceMappingURL=text.test.js.map
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterAll, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { getClient, getZai } from './__tests';
|
|
3
|
-
import { check } from '@botpress/vai';
|
|
4
|
-
describe('zai.learn / generic', { timeout: 60_000 }, () => {
|
|
5
|
-
const client = getClient();
|
|
6
|
-
let tableName = 'ZaiTestInternalTable';
|
|
7
|
-
let taskId = 'test';
|
|
8
|
-
let zai = getZai();
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
zai = getZai().with({
|
|
11
|
-
activeLearning: {
|
|
12
|
-
enable: true,
|
|
13
|
-
taskId,
|
|
14
|
-
tableName
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
afterEach(async () => {
|
|
19
|
-
try {
|
|
20
|
-
await client.deleteTableRows({ table: tableName, deleteAllRows: true });
|
|
21
|
-
}
|
|
22
|
-
catch (err) { }
|
|
23
|
-
});
|
|
24
|
-
afterAll(async () => {
|
|
25
|
-
try {
|
|
26
|
-
await client.deleteTable({ table: tableName });
|
|
27
|
-
}
|
|
28
|
-
catch (err) { }
|
|
29
|
-
});
|
|
30
|
-
it('saves examples to tables', async () => {
|
|
31
|
-
const value = await zai
|
|
32
|
-
.learn(taskId)
|
|
33
|
-
.check('This text is very clearly written in English.', 'is an english sentence');
|
|
34
|
-
const { rows } = await client.findTableRows({ table: tableName });
|
|
35
|
-
expect(value).toBe(true);
|
|
36
|
-
expect(rows.length).toBe(1);
|
|
37
|
-
check(rows[0].explanation, 'is an explanation sentence');
|
|
38
|
-
expect(rows[0].explanation).not.toContain('Final Answer:');
|
|
39
|
-
expect(rows[0].output).toMatchObject({ value: true });
|
|
40
|
-
expect(rows[0].input).toMatchInlineSnapshot(`
|
|
41
|
-
{
|
|
42
|
-
"value": "This text is very clearly written in English.",
|
|
43
|
-
}
|
|
44
|
-
`);
|
|
45
|
-
expect(rows[0].taskId).toEqual('zai/test');
|
|
46
|
-
expect(rows[0].taskType).toBe('zai.check');
|
|
47
|
-
});
|
|
48
|
-
it('works even if tables are down', async () => {
|
|
49
|
-
const upsertTableRows = vi.fn(async () => {
|
|
50
|
-
throw new Error('Table is down');
|
|
51
|
-
});
|
|
52
|
-
const findTableRows = vi.fn(async () => {
|
|
53
|
-
throw new Error('Table is down');
|
|
54
|
-
});
|
|
55
|
-
const client = {
|
|
56
|
-
...getClient(),
|
|
57
|
-
findTableRows,
|
|
58
|
-
upsertTableRows
|
|
59
|
-
};
|
|
60
|
-
const value = await zai
|
|
61
|
-
.with({ client })
|
|
62
|
-
.learn(taskId)
|
|
63
|
-
.check('This text is very clearly written in English.', 'is an english sentence');
|
|
64
|
-
const { rows } = await getClient().findTableRows({ table: tableName });
|
|
65
|
-
expect(value).toBe(true);
|
|
66
|
-
expect(rows.length).toBe(0);
|
|
67
|
-
expect(upsertTableRows).toHaveBeenCalledTimes(1);
|
|
68
|
-
expect(findTableRows).toHaveBeenCalledTimes(1);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
//# sourceMappingURL=zai-learn.test.js.map
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { getClient, getZai } from './__tests';
|
|
3
|
-
describe('zai retry', { timeout: 60_000 }, () => {
|
|
4
|
-
const client = getClient();
|
|
5
|
-
let zai = getZai().with({ retry: { maxRetries: 3 } });
|
|
6
|
-
it('retries 3 times and succeeds', async () => {
|
|
7
|
-
let retryCount = 0;
|
|
8
|
-
const throwingClient = {
|
|
9
|
-
...client,
|
|
10
|
-
callAction: vi.fn((input) => {
|
|
11
|
-
if (++retryCount < 3) {
|
|
12
|
-
throw new Error('Failed to call model');
|
|
13
|
-
}
|
|
14
|
-
return client.callAction(input);
|
|
15
|
-
})
|
|
16
|
-
};
|
|
17
|
-
const value = await zai
|
|
18
|
-
.with({ client: throwingClient })
|
|
19
|
-
.check('This text is very clearly written in English.', 'is an english sentence');
|
|
20
|
-
expect(value).toBe(true);
|
|
21
|
-
expect(retryCount).toBe(3);
|
|
22
|
-
});
|
|
23
|
-
it('retries 0 times when success', async () => {
|
|
24
|
-
const fn = vi.fn((input) => client.callAction(input));
|
|
25
|
-
const throwingClient = {
|
|
26
|
-
...client,
|
|
27
|
-
callAction: fn
|
|
28
|
-
};
|
|
29
|
-
const value = await zai
|
|
30
|
-
.with({ client: throwingClient })
|
|
31
|
-
.check('This text is very clearly written in English.', 'is an english sentence');
|
|
32
|
-
expect(value).toBe(true);
|
|
33
|
-
expect(fn).toHaveBeenCalledOnce();
|
|
34
|
-
});
|
|
35
|
-
it('fails when exceeded max', async () => {
|
|
36
|
-
const fn = vi.fn(() => {
|
|
37
|
-
throw new Error('Failed to call model');
|
|
38
|
-
});
|
|
39
|
-
const throwingClient = {
|
|
40
|
-
...client,
|
|
41
|
-
callAction: fn
|
|
42
|
-
};
|
|
43
|
-
const value = zai
|
|
44
|
-
.with({ client: throwingClient, retry: { maxRetries: 1 } })
|
|
45
|
-
.check('This text is very clearly written in English.', 'is an english sentence');
|
|
46
|
-
await expect(value).rejects.toThrowError(/retries/);
|
|
47
|
-
expect(fn).toHaveBeenCalledTimes(2);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
//# sourceMappingURL=zai-retry.test.js.map
|