@botpress/zai 2.0.8 → 2.0.11
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/adapters/botpress-table.js +2 -2
- package/dist/index.d.ts +9 -7
- package/dist/operations/check.js +15 -4
- package/dist/operations/extract.js +1 -1
- package/e2e/client.ts +151 -0
- package/e2e/data/cache.jsonl +113 -118
- package/e2e/utils.ts +2 -54
- package/package.json +4 -3
- package/src/adapters/botpress-table.ts +2 -2
- package/src/operations/check.ts +23 -9
- package/src/operations/extract.ts +1 -1
- package/src/zai.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from "@bpinternal/zui";
|
|
1
|
+
import { transforms, z } from "@bpinternal/zui";
|
|
2
2
|
import { Adapter } from "./adapter";
|
|
3
3
|
const CRITICAL_TAGS = {
|
|
4
4
|
system: "true",
|
|
@@ -37,7 +37,7 @@ const TableSchema = z.object({
|
|
|
37
37
|
});
|
|
38
38
|
const searchableColumns = ["input"];
|
|
39
39
|
const TableJsonSchema = Object.entries(TableSchema.shape).reduce((acc, [key, value]) => {
|
|
40
|
-
acc[key] =
|
|
40
|
+
acc[key] = transforms.toJSONSchemaLegacy(value);
|
|
41
41
|
acc[key]["x-zui"] ??= {};
|
|
42
42
|
acc[key]["x-zui"].searchable = searchableColumns.includes(key);
|
|
43
43
|
return acc;
|
package/dist/index.d.ts
CHANGED
|
@@ -85,14 +85,14 @@ declare module '@botpress/zai' {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
type Example$
|
|
88
|
+
type Example$3 = {
|
|
89
89
|
input: string;
|
|
90
90
|
output: string;
|
|
91
91
|
instructions?: string;
|
|
92
92
|
};
|
|
93
93
|
type Options$5 = {
|
|
94
94
|
/** Examples to guide the rewriting */
|
|
95
|
-
examples?: Array<Example$
|
|
95
|
+
examples?: Array<Example$3>;
|
|
96
96
|
/** The maximum number of tokens to generate */
|
|
97
97
|
length?: number;
|
|
98
98
|
};
|
|
@@ -127,13 +127,15 @@ declare module '@botpress/zai' {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
type Example$2 = {
|
|
131
|
+
input: unknown;
|
|
132
|
+
check: boolean;
|
|
133
|
+
reason?: string;
|
|
134
|
+
condition?: string;
|
|
135
|
+
};
|
|
130
136
|
type Options$3 = {
|
|
131
137
|
/** Examples to check the condition against */
|
|
132
|
-
examples?: Array<
|
|
133
|
-
input: unknown;
|
|
134
|
-
check: boolean;
|
|
135
|
-
reason?: string;
|
|
136
|
-
}>;
|
|
138
|
+
examples?: Array<Example$2>;
|
|
137
139
|
};
|
|
138
140
|
declare module '@botpress/zai' {
|
|
139
141
|
interface Zai {
|
package/dist/operations/check.js
CHANGED
|
@@ -5,7 +5,8 @@ import { PROMPT_INPUT_BUFFER } from "./constants";
|
|
|
5
5
|
const _Example = z.object({
|
|
6
6
|
input: z.any(),
|
|
7
7
|
check: z.boolean(),
|
|
8
|
-
reason: z.string().optional()
|
|
8
|
+
reason: z.string().optional(),
|
|
9
|
+
condition: z.string().optional()
|
|
9
10
|
});
|
|
10
11
|
const _Options = z.object({
|
|
11
12
|
examples: z.array(_Example).describe("Examples to check the condition against").default([])
|
|
@@ -45,11 +46,17 @@ Zai.prototype.check = async function(input, condition, _options) {
|
|
|
45
46
|
return { explanation: exactMatch.explanation ?? "", value: exactMatch.output };
|
|
46
47
|
}
|
|
47
48
|
const defaultExamples = [
|
|
48
|
-
{
|
|
49
|
+
{
|
|
50
|
+
input: "50 Cent",
|
|
51
|
+
check: true,
|
|
52
|
+
reason: "50 Cent is widely recognized as a public personality.",
|
|
53
|
+
condition: "Is the input a public personality?"
|
|
54
|
+
},
|
|
49
55
|
{
|
|
50
56
|
input: ["apple", "banana", "carrot", "house"],
|
|
51
57
|
check: false,
|
|
52
|
-
reason: "The list contains a house, which is not a fruit. Also, the list contains a carrot, which is a vegetable."
|
|
58
|
+
reason: "The list contains a house, which is not a fruit. Also, the list contains a carrot, which is a vegetable.",
|
|
59
|
+
condition: "Is the input exclusively a list of fruits?"
|
|
53
60
|
}
|
|
54
61
|
];
|
|
55
62
|
const userExamples = [
|
|
@@ -74,7 +81,11 @@ ${END}
|
|
|
74
81
|
`.trim();
|
|
75
82
|
};
|
|
76
83
|
const formatExample = (example) => [
|
|
77
|
-
{
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
content: formatInput(stringify(example.input ?? null), example.condition ?? condition),
|
|
87
|
+
role: "user"
|
|
88
|
+
},
|
|
78
89
|
{
|
|
79
90
|
type: "text",
|
|
80
91
|
content: formatOutput(example.check, example.reason ?? ""),
|
|
@@ -39,7 +39,7 @@ Zai.prototype.extract = async function(input, _schema, _options) {
|
|
|
39
39
|
} else {
|
|
40
40
|
throw new Error("Schema must be either a ZuiObject or a ZuiArray<ZuiObject>");
|
|
41
41
|
}
|
|
42
|
-
const schemaTypescript = schema.
|
|
42
|
+
const schemaTypescript = schema.toTypescriptType({ declaration: false });
|
|
43
43
|
const schemaLength = tokenizer.count(schemaTypescript);
|
|
44
44
|
options.chunkLength = Math.min(
|
|
45
45
|
options.chunkLength,
|
package/e2e/client.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Client } from '@botpress/client'
|
|
2
|
+
import { Cognitive } from '@botpress/cognitive'
|
|
3
|
+
import { diffLines } from 'diff'
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { expect } from 'vitest'
|
|
7
|
+
|
|
8
|
+
function stringifyWithSortedKeys(obj: any, space?: number): string {
|
|
9
|
+
function sortKeys(input: any): any {
|
|
10
|
+
if (Array.isArray(input)) {
|
|
11
|
+
return input.map(sortKeys)
|
|
12
|
+
} else if (input && typeof input === 'object' && input.constructor === Object) {
|
|
13
|
+
return Object.keys(input)
|
|
14
|
+
.sort()
|
|
15
|
+
.reduce(
|
|
16
|
+
(acc, key) => {
|
|
17
|
+
acc[key] = sortKeys(input[key])
|
|
18
|
+
return acc
|
|
19
|
+
},
|
|
20
|
+
{} as Record<string, any>
|
|
21
|
+
)
|
|
22
|
+
} else {
|
|
23
|
+
return input
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return JSON.stringify(sortKeys(obj), null, space)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readJSONL<T>(filePath: string, keyProperty: keyof T): Map<string, T> {
|
|
31
|
+
const lines = fs.readFileSync(filePath, 'utf-8').split(/\r?\n/).filter(Boolean)
|
|
32
|
+
|
|
33
|
+
const map = new Map<string, T>()
|
|
34
|
+
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
try {
|
|
37
|
+
const obj = JSON.parse(line) as T
|
|
38
|
+
const key = String(obj[keyProperty])
|
|
39
|
+
map.set(key, obj)
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return map
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type CacheEntry = { key: string; value: any; test: string; input: string }
|
|
47
|
+
|
|
48
|
+
const cache: Map<string, CacheEntry> = readJSONL(path.resolve(__dirname, './data/cache.jsonl'), 'key')
|
|
49
|
+
const cacheByTest: Map<string, CacheEntry> = readJSONL(path.resolve(__dirname, './data/cache.jsonl'), 'test')
|
|
50
|
+
|
|
51
|
+
class CachedClient extends Client {
|
|
52
|
+
#client: Client
|
|
53
|
+
#callsByTest: Record<string, number> = {}
|
|
54
|
+
|
|
55
|
+
public constructor(options: ConstructorParameters<typeof Client>[0]) {
|
|
56
|
+
super(options)
|
|
57
|
+
this.#client = new Client(options)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public callAction = async (...args: Parameters<Client['callAction']>) => {
|
|
61
|
+
const currentTestName = expect.getState().currentTestName ?? 'default'
|
|
62
|
+
this.#callsByTest[currentTestName] ||= 0
|
|
63
|
+
this.#callsByTest[currentTestName]++
|
|
64
|
+
|
|
65
|
+
const testKey = `${currentTestName}-${this.#callsByTest[currentTestName]}`
|
|
66
|
+
|
|
67
|
+
const key = fastHash(stringifyWithSortedKeys(args))
|
|
68
|
+
const cached = cache.get(key)
|
|
69
|
+
|
|
70
|
+
if (cached) {
|
|
71
|
+
return cached.value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (process.env.CI && cacheByTest.has(testKey)) {
|
|
75
|
+
console.info(`Cache miss for ${key} in test ${testKey}`)
|
|
76
|
+
console.info(
|
|
77
|
+
diffLines(
|
|
78
|
+
JSON.stringify(JSON.parse(cacheByTest.get(testKey)?.input!), null, 2),
|
|
79
|
+
JSON.stringify(JSON.parse(stringifyWithSortedKeys(args)), null, 2)
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const response = await this.#client.callAction(...args)
|
|
85
|
+
cache.set(key, { key, value: response, test: testKey, input: stringifyWithSortedKeys(args) })
|
|
86
|
+
|
|
87
|
+
fs.appendFileSync(
|
|
88
|
+
path.resolve(__dirname, './data/cache.jsonl'),
|
|
89
|
+
JSON.stringify({
|
|
90
|
+
test: testKey,
|
|
91
|
+
key,
|
|
92
|
+
input: stringifyWithSortedKeys(args),
|
|
93
|
+
value: response,
|
|
94
|
+
}) + '\n'
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return response
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public clone() {
|
|
101
|
+
return this
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const getCachedCognitiveClient = () => {
|
|
106
|
+
const cognitive = new Cognitive({
|
|
107
|
+
client: new CachedClient({
|
|
108
|
+
apiUrl: process.env.CLOUD_API_ENDPOINT ?? 'https://api.botpress.dev',
|
|
109
|
+
botId: process.env.CLOUD_BOT_ID,
|
|
110
|
+
token: process.env.CLOUD_PAT,
|
|
111
|
+
}),
|
|
112
|
+
provider: {
|
|
113
|
+
deleteModelPreferences: async () => {},
|
|
114
|
+
saveModelPreferences: async () => {},
|
|
115
|
+
fetchInstalledModels: async () => [
|
|
116
|
+
{
|
|
117
|
+
ref: 'openai:gpt-4o-2024-11-20',
|
|
118
|
+
integration: 'openai',
|
|
119
|
+
id: 'gpt-4o-2024-11-20',
|
|
120
|
+
name: 'GPT-4o (November 2024)',
|
|
121
|
+
description:
|
|
122
|
+
"GPT-4o (“o” for “omni”) is OpenAI's most advanced model. It is multimodal (accepting text or image inputs and outputting text), and it has the same high intelligence as GPT-4 Turbo but is cheaper and more efficient.",
|
|
123
|
+
input: {
|
|
124
|
+
costPer1MTokens: 2.5,
|
|
125
|
+
maxTokens: 128000,
|
|
126
|
+
},
|
|
127
|
+
output: {
|
|
128
|
+
costPer1MTokens: 10,
|
|
129
|
+
maxTokens: 16384,
|
|
130
|
+
},
|
|
131
|
+
tags: ['recommended', 'vision', 'general-purpose', 'coding', 'agents', 'function-calling'],
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
fetchModelPreferences: async () => ({
|
|
135
|
+
best: ['openai:gpt-4o-2024-11-20'] as const,
|
|
136
|
+
fast: ['openai:gpt-4o-2024-11-20'] as const,
|
|
137
|
+
downtimes: [],
|
|
138
|
+
}),
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
return cognitive
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function fastHash(str: string): string {
|
|
145
|
+
let hash = 0
|
|
146
|
+
for (let i = 0; i < str.length; i++) {
|
|
147
|
+
hash = (hash << 5) - hash + str.charCodeAt(i)
|
|
148
|
+
hash |= 0 // Convert to 32bit integer
|
|
149
|
+
}
|
|
150
|
+
return (hash >>> 0).toString(16) // Convert to unsigned and then to hex
|
|
151
|
+
}
|