@botpress/zai 1.0.1 → 1.2.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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/build.ts +9 -0
  3. package/dist/adapters/adapter.js +2 -0
  4. package/dist/adapters/botpress-table.js +168 -0
  5. package/dist/adapters/memory.js +12 -0
  6. package/dist/index.d.ts +111 -609
  7. package/dist/index.js +9 -1873
  8. package/dist/operations/check.js +153 -0
  9. package/dist/operations/constants.js +2 -0
  10. package/dist/operations/errors.js +15 -0
  11. package/dist/operations/extract.js +232 -0
  12. package/dist/operations/filter.js +191 -0
  13. package/dist/operations/label.js +249 -0
  14. package/dist/operations/rewrite.js +123 -0
  15. package/dist/operations/summarize.js +133 -0
  16. package/dist/operations/text.js +47 -0
  17. package/dist/utils.js +37 -0
  18. package/dist/zai.js +100 -0
  19. package/e2e/data/botpress_docs.txt +26040 -0
  20. package/e2e/data/cache.jsonl +107 -0
  21. package/e2e/utils.ts +89 -0
  22. package/package.json +33 -29
  23. package/src/adapters/adapter.ts +35 -0
  24. package/src/adapters/botpress-table.ts +210 -0
  25. package/src/adapters/memory.ts +13 -0
  26. package/src/index.ts +11 -0
  27. package/src/operations/check.ts +201 -0
  28. package/src/operations/constants.ts +2 -0
  29. package/src/operations/errors.ts +9 -0
  30. package/src/operations/extract.ts +309 -0
  31. package/src/operations/filter.ts +244 -0
  32. package/src/operations/label.ts +345 -0
  33. package/src/operations/rewrite.ts +161 -0
  34. package/src/operations/summarize.ts +195 -0
  35. package/src/operations/text.ts +65 -0
  36. package/src/utils.ts +52 -0
  37. package/src/zai.ts +147 -0
  38. package/tsconfig.json +3 -23
  39. package/dist/index.cjs +0 -1903
  40. package/dist/index.cjs.map +0 -1
  41. package/dist/index.d.cts +0 -916
  42. package/dist/index.js.map +0 -1
  43. package/tsup.config.ts +0 -16
  44. package/vitest.config.ts +0 -9
  45. package/vitest.setup.ts +0 -24
package/README.md CHANGED
@@ -10,7 +10,7 @@ It's built on top of Zui and the Botpress client to interface with the different
10
10
  import Zai from '@botpress/zai'
11
11
 
12
12
  const zai = new Zai({
13
- client: new Client({}) // your botpress client here
13
+ client: new Client({}), // your botpress client here
14
14
  })
15
15
  ```
16
16
 
package/build.ts ADDED
@@ -0,0 +1,9 @@
1
+ import esbuild from 'esbuild'
2
+ import glob from 'glob'
3
+
4
+ const entryPoints = glob.sync('./src/**/*.ts')
5
+ void esbuild.build({
6
+ entryPoints,
7
+ platform: 'neutral',
8
+ outdir: './dist',
9
+ })
@@ -0,0 +1,2 @@
1
+ export class Adapter {
2
+ }
@@ -0,0 +1,168 @@
1
+ import { z } from "@bpinternal/zui";
2
+ import { GenerationMetadata } from "../utils";
3
+ import { Adapter } from "./adapter";
4
+ const CRITICAL_TAGS = {
5
+ system: "true",
6
+ "schema-purpose": "active-learning",
7
+ "schema-version": "Oct-2024"
8
+ };
9
+ const OPTIONAL_TAGS = {
10
+ "x-studio-title": "Active Learning",
11
+ "x-studio-description": "Table for storing active learning tasks and examples",
12
+ "x-studio-readonly": "true",
13
+ "x-studio-icon": "lucide://atom",
14
+ "x-studio-color": "green"
15
+ };
16
+ const FACTOR = 30;
17
+ const Props = z.object({
18
+ client: z.custom(() => true),
19
+ tableName: z.string().regex(
20
+ /^[a-zA-Z0-9_]{1,45}Table$/,
21
+ "Table name must be lowercase and contain only letters, numbers and underscores"
22
+ )
23
+ });
24
+ const TableSchema = z.object({
25
+ taskType: z.string().describe("The type of the task (filter, extract, etc.)"),
26
+ taskId: z.string(),
27
+ key: z.string().describe("A unique key for the task (e.g. a hash of the input, taskId, taskType and instructions)"),
28
+ instructions: z.string(),
29
+ input: z.object({}).passthrough().describe("The input to the task"),
30
+ output: z.object({}).passthrough().describe("The expected output"),
31
+ explanation: z.string().nullable(),
32
+ metadata: GenerationMetadata,
33
+ status: z.enum(["pending", "rejected", "approved"]),
34
+ feedback: z.object({
35
+ rating: z.enum(["very-bad", "bad", "good", "very-good"]),
36
+ comment: z.string().nullable()
37
+ }).nullable().default(null)
38
+ });
39
+ const searchableColumns = ["input"];
40
+ const TableJsonSchema = Object.entries(TableSchema.shape).reduce((acc, [key, value]) => {
41
+ acc[key] = value.toJsonSchema();
42
+ acc[key]["x-zui"] ??= {};
43
+ acc[key]["x-zui"].searchable = searchableColumns.includes(key);
44
+ return acc;
45
+ }, {});
46
+ export class TableAdapter extends Adapter {
47
+ _client;
48
+ _tableName;
49
+ _status;
50
+ constructor(props) {
51
+ super();
52
+ props = Props.parse(props);
53
+ this._client = props.client;
54
+ this._tableName = props.tableName;
55
+ this._status = "ready";
56
+ }
57
+ async getExamples({ taskType, taskId, input }) {
58
+ await this._assertTableExists();
59
+ const { rows } = await this._client.findTableRows({
60
+ table: this._tableName,
61
+ search: JSON.stringify({ value: input }).substring(0, 1023),
62
+ // Search is limited to 1024 characters
63
+ limit: 10,
64
+ // TODO
65
+ filter: {
66
+ // Proximity match of approved examples
67
+ taskType,
68
+ taskId,
69
+ status: "approved"
70
+ }
71
+ }).catch((err) => {
72
+ console.error(`Error fetching examples: ${err.message}`);
73
+ return { rows: [] };
74
+ });
75
+ return rows.map((row) => ({
76
+ key: row.key,
77
+ input: row.input.value,
78
+ output: row.output.value,
79
+ explanation: row.explanation,
80
+ similarity: row.similarity ?? 0
81
+ }));
82
+ }
83
+ async saveExample({
84
+ key,
85
+ taskType,
86
+ taskId,
87
+ instructions,
88
+ input,
89
+ output,
90
+ explanation,
91
+ metadata,
92
+ status = "pending"
93
+ }) {
94
+ await this._assertTableExists();
95
+ await this._client.upsertTableRows({
96
+ table: this._tableName,
97
+ keyColumn: "key",
98
+ rows: [
99
+ {
100
+ key,
101
+ taskType,
102
+ taskId,
103
+ instructions,
104
+ input: { value: input },
105
+ output: { value: output },
106
+ explanation: explanation ?? null,
107
+ status,
108
+ metadata
109
+ }
110
+ ]
111
+ }).catch(() => {
112
+ });
113
+ }
114
+ async _assertTableExists() {
115
+ if (this._status !== "ready") {
116
+ return;
117
+ }
118
+ const { table, created } = await this._client.getOrCreateTable({
119
+ table: this._tableName,
120
+ factor: FACTOR,
121
+ frozen: true,
122
+ isComputeEnabled: false,
123
+ tags: {
124
+ ...CRITICAL_TAGS,
125
+ ...OPTIONAL_TAGS
126
+ },
127
+ schema: TableJsonSchema
128
+ }).catch(() => {
129
+ this._status = "error";
130
+ return { table: null, created: false };
131
+ });
132
+ if (!table) {
133
+ return;
134
+ }
135
+ if (!created) {
136
+ const issues = [];
137
+ if (table.factor !== FACTOR) {
138
+ issues.push(`Factor is ${table.factor} instead of ${FACTOR}`);
139
+ }
140
+ if (table.frozen !== true) {
141
+ issues.push("Table is not frozen");
142
+ }
143
+ for (const [key, value] of Object.entries(CRITICAL_TAGS)) {
144
+ if (table.tags?.[key] !== value) {
145
+ issues.push(`Tag ${key} is ${table.tags?.[key]} instead of ${value}`);
146
+ }
147
+ }
148
+ for (const key of Object.keys(TableJsonSchema)) {
149
+ const column = table.schema?.properties[key];
150
+ const expected = TableJsonSchema[key];
151
+ if (!column) {
152
+ issues.push(`Column ${key} is missing`);
153
+ continue;
154
+ }
155
+ if (column.type !== expected.type) {
156
+ issues.push(`Column ${key} has type ${column.type} instead of ${expected.type}`);
157
+ }
158
+ if (expected["x-zui"].searchable && !column["x-zui"].searchable) {
159
+ issues.push(`Column ${key} is not searchable but should be`);
160
+ }
161
+ }
162
+ if (issues.length) {
163
+ this._status = "error";
164
+ }
165
+ }
166
+ this._status = "initialized";
167
+ }
168
+ }
@@ -0,0 +1,12 @@
1
+ import { Adapter } from "./adapter";
2
+ export class MemoryAdapter extends Adapter {
3
+ constructor(examples) {
4
+ super();
5
+ this.examples = examples;
6
+ }
7
+ async getExamples() {
8
+ return this.examples;
9
+ }
10
+ async saveExample() {
11
+ }
12
+ }