aimyapi 1.0.9 → 1.0.10
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/README.md +28 -2
- package/dist/index.cjs +232 -111
- package/dist/index.d.cts +16 -8
- package/dist/index.d.mts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +232 -111
- package/dist/index.mjs +234 -113
- package/example/example1.ts +21 -10
- package/example/sales_api.md +1 -5
- package/example/sales_api.ts +4 -2
- package/example/sales_api_impl.ts +18 -4
- package/package.json +2 -3
- package/prompts/generate-code-prompt.md +37 -0
- package/src/index.ts +519 -495
- package/src/log-tester-inject.ts +233 -0
- package/src/log-tester.ts +185 -0
- package/src/logger.ts +113 -0
- package/src/sandbox-wrappers.ts +91 -46
- package/src/sandbox.ts +254 -277
- package/src/utils.ts +31 -1
- package/prompts/create-task-prompt.md +0 -37
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ for (let i = 0; i < salesData.length; i++) {
|
|
|
114
114
|
businessUnitWithMaxSales = salesData[i].businessUnit;
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
api.
|
|
117
|
+
api.display(\`The business unit with the highest sales in 2020 was ${businessUnitWithMaxSales}.\`);
|
|
118
118
|
|
|
119
119
|
```
|
|
120
120
|
|
|
@@ -122,6 +122,31 @@ This example does not take advantage of the chat history and treats every reques
|
|
|
122
122
|
|
|
123
123
|
APIs should be kept simple and understandable. If you have a complicated API you may want to write a simpler facade for it.
|
|
124
124
|
|
|
125
|
+
### Limitations
|
|
126
|
+
|
|
127
|
+
The sandbox implementation supports basic types and the built in objects Promise and Date. It doesn't currently support Map, Set, Regex, etc. So you cannot return those types from your API functions. Some Date functionality differs from current javascript specs.
|
|
128
|
+
|
|
129
|
+
### Recommendations
|
|
130
|
+
|
|
131
|
+
Create a separate sandbox with a mocked API for generating code. This way the agent doesn't modify anything while troubleshooting or running test scripts. Additionally it prevents the agent from ever seeing real data if you want to maintain privacy. Use the real API only when you want to run code.
|
|
132
|
+
|
|
133
|
+
Implement schema validation for your API functions. Follow the convention by defining `_params_[FUNCTIONNAME]` as a Zod object:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// Schema validation for function parameters. Will throw if parameters are invalid to prevent silent failures and surface info to the agent.
|
|
137
|
+
// fields should be defined in the same order that they occur in the function
|
|
138
|
+
public _params_sendEmail = z.object({
|
|
139
|
+
to: z.string(),
|
|
140
|
+
subject: z.string(),
|
|
141
|
+
body: z.string().min(5)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// Mock implementation, doesn't actually send an email
|
|
145
|
+
async sendEmail(to: string, subject: string, body: string): Promise<void> {
|
|
146
|
+
...
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
125
150
|
|
|
126
151
|
## API Reference for AIMyAPI
|
|
127
152
|
|
|
@@ -175,11 +200,12 @@ export interface AIMyAPIInstance {
|
|
|
175
200
|
- `generateCode()`: A function that generates code to be run in the sandbox for the given query text and user chat history.
|
|
176
201
|
- `runCode()`: A function that runs the generated code in the sandbox.
|
|
177
202
|
- `checkCode()`: Typecheck / compile the code
|
|
178
|
-
- `processRequest()`: A single function that generates and runs code assuming no chat history.
|
|
203
|
+
- `processRequest()`: A single function that generates and runs code assuming no chat history. This is more of a convenience function for the examples, since code gen and runn
|
|
179
204
|
|
|
180
205
|
|
|
181
206
|
## Safety notes:
|
|
182
207
|
|
|
208
|
+
- When generating code, you should use a mocked version of your API. You can create separate sandboxes for writing code vs. running code.
|
|
183
209
|
- If the user is giving sensitive information in a query, that information isn't private. Refer to the OpenAI policies and plan accordingly.
|
|
184
210
|
- The LLM will do stupid things with your API. You should idiot proof your functions, make sure you sanity check any inputs, etc.
|
|
185
211
|
- Even though we are running the code in sandbox, you should still treat anything coming back from the LLM as untrusted code.
|
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var openai$1 = require('openai');
|
|
|
6
6
|
var quickjsEmscripten = require('quickjs-emscripten');
|
|
7
7
|
var ts = require('typescript');
|
|
8
8
|
var zod = require('zod');
|
|
9
|
-
var
|
|
9
|
+
var path = require('path');
|
|
10
10
|
|
|
11
11
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
12
12
|
function _interopNamespaceDefault(e) {
|
|
@@ -34,23 +34,104 @@ var require$1 = (
|
|
|
34
34
|
: require
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
+
class Logger {
|
|
38
|
+
constructor(debug = false) {
|
|
39
|
+
this.logs = [];
|
|
40
|
+
this.debug = false;
|
|
41
|
+
this.debug = debug;
|
|
42
|
+
}
|
|
43
|
+
getLogs() {
|
|
44
|
+
return this.logs;
|
|
45
|
+
}
|
|
46
|
+
addEntry(type, args, source) {
|
|
47
|
+
if (Array.isArray(args)) {
|
|
48
|
+
this.logs.push({ type, args, source });
|
|
49
|
+
} else {
|
|
50
|
+
this.logs.push({ type, args: [args], source });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
log(args, source) {
|
|
54
|
+
this.addEntry("log", args, source);
|
|
55
|
+
if (this.debug) {
|
|
56
|
+
console.log(`[${source}]:`, ...args);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
trace(fnName, fnArgs, result) {
|
|
60
|
+
this.logs.push({ type: "trace", fnName, fnArgs, result });
|
|
61
|
+
if (this.debug) {
|
|
62
|
+
console.debug(`[sandbox]: ${fnName}(${fnArgs.map((arg) => JSON.stringify(Logger.limitObjectForLogs(arg))).join(",")}) -> ${JSON.stringify(Logger.limitObjectForLogs(result))}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
error(args, source) {
|
|
66
|
+
this.addEntry("error", args, source);
|
|
67
|
+
if (this.debug) {
|
|
68
|
+
console.error(`[${source}]:`, ...args);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
clear() {
|
|
72
|
+
this.logs = [];
|
|
73
|
+
}
|
|
74
|
+
getPrettyLogs() {
|
|
75
|
+
return this.logs.map((entry) => Logger.limitObjectForLogs(entry));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Returns a trimmed copy of `value` that is safe to log.
|
|
79
|
+
* - Objects/arrays nested deeper than `maxDepth` become "[Object]" / "[Array(n)]".
|
|
80
|
+
* - Arrays longer than `maxArrayItems` keep the first items plus a "+N more items" string.
|
|
81
|
+
* - Circular references become "[Circular]".
|
|
82
|
+
*/
|
|
83
|
+
static limitObjectForLogs(value, maxDepth = 3, maxArrayItems = 5) {
|
|
84
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
85
|
+
const trim = (val, depth) => {
|
|
86
|
+
if (val === null || typeof val !== "object") {
|
|
87
|
+
if (typeof val === "function") return `[Function${val.name ? `: ${val.name}` : ""}]`;
|
|
88
|
+
if (typeof val === "bigint") return `${val}n`;
|
|
89
|
+
if (typeof val === "symbol") return val.toString();
|
|
90
|
+
return val;
|
|
91
|
+
}
|
|
92
|
+
if (val instanceof Date) return val.toISOString();
|
|
93
|
+
if (val instanceof Error) {
|
|
94
|
+
return { name: val.name, message: val.message, stack: val.stack };
|
|
95
|
+
}
|
|
96
|
+
if (seen.has(val)) return "[Circular]";
|
|
97
|
+
if (depth >= maxDepth) {
|
|
98
|
+
return Array.isArray(val) ? `[Array(${val.length})]` : "[Object]";
|
|
99
|
+
}
|
|
100
|
+
seen.add(val);
|
|
101
|
+
let result;
|
|
102
|
+
if (Array.isArray(val)) {
|
|
103
|
+
result = val.slice(0, maxArrayItems).map((item) => trim(item, depth + 1));
|
|
104
|
+
if (val.length > maxArrayItems) {
|
|
105
|
+
result.push(`+${val.length - maxArrayItems} more items`);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
result = {};
|
|
109
|
+
for (const key of Object.keys(val)) {
|
|
110
|
+
result[key] = trim(val[key], depth + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
seen.delete(val);
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
return trim(value, 0);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
37
120
|
let promiseId = 0;
|
|
38
121
|
let openPromises = {};
|
|
39
|
-
function wrapPromise(vm, promise, beginAsyncProcess, endAsyncProcess) {
|
|
122
|
+
function wrapPromise(vm, promise, beginAsyncProcess, endAsyncProcess, logger) {
|
|
40
123
|
promiseId += 1;
|
|
41
124
|
const vmPromise = vm.newPromise();
|
|
42
125
|
vmPromise._promiseId = promiseId;
|
|
43
126
|
beginAsyncProcess();
|
|
44
127
|
promise.then((...args) => {
|
|
45
128
|
endAsyncProcess();
|
|
46
|
-
const wrappedArgs = args.map((arg) => wrap(vm, arg, void 0, beginAsyncProcess, endAsyncProcess));
|
|
129
|
+
const wrappedArgs = args.map((arg) => wrap(vm, arg, void 0, beginAsyncProcess, endAsyncProcess, logger));
|
|
47
130
|
openPromises[vmPromise._promiseId] = null;
|
|
48
131
|
vmPromise.resolve(...wrappedArgs);
|
|
49
132
|
wrappedArgs.forEach((arg) => arg.dispose());
|
|
50
|
-
}).catch((
|
|
51
|
-
|
|
52
|
-
vmPromise.reject(...wrappedArgs);
|
|
53
|
-
wrappedArgs.forEach((arg) => arg.dispose());
|
|
133
|
+
}).catch((error) => {
|
|
134
|
+
vmPromise.reject(vm.newError(error?.message || "An error occurred"));
|
|
54
135
|
openPromises[vmPromise._promiseId] = null;
|
|
55
136
|
});
|
|
56
137
|
vmPromise.settled.then(() => {
|
|
@@ -69,21 +150,25 @@ function rejectOpenPromises(vm) {
|
|
|
69
150
|
}
|
|
70
151
|
vm.runtime.executePendingJobs();
|
|
71
152
|
}
|
|
72
|
-
|
|
153
|
+
const isPromise = (value) => {
|
|
154
|
+
return typeof value === "object" && typeof value.then === "function";
|
|
155
|
+
};
|
|
156
|
+
function wrap(vm, value, context, beginAsyncProcess, endAsyncProcess, logger) {
|
|
73
157
|
if (value === null) {
|
|
74
158
|
return null;
|
|
75
|
-
} else if (
|
|
76
|
-
return wrapPromise(vm, value, beginAsyncProcess, endAsyncProcess);
|
|
159
|
+
} else if (isPromise(value)) {
|
|
160
|
+
return wrapPromise(vm, value, beginAsyncProcess, endAsyncProcess, logger);
|
|
77
161
|
} else if (typeof value === "function") {
|
|
78
|
-
return wrapGenericFunction(vm, value, context, beginAsyncProcess, endAsyncProcess);
|
|
162
|
+
return wrapGenericFunction(vm, value, context, beginAsyncProcess, endAsyncProcess, logger);
|
|
79
163
|
} else if (Array.isArray(value)) {
|
|
80
|
-
return wrapArray(vm, value, beginAsyncProcess, endAsyncProcess);
|
|
164
|
+
return wrapArray(vm, value, beginAsyncProcess, endAsyncProcess, logger);
|
|
81
165
|
} else if (value === null) {
|
|
82
166
|
return vm.null;
|
|
83
167
|
} else if (value instanceof Date) {
|
|
84
|
-
|
|
168
|
+
const res = vm.evalCode(`new Date(${value.getTime()})`);
|
|
169
|
+
return quickjsEmscripten.isSuccess(res) ? res.value : null;
|
|
85
170
|
} else if (typeof value === "object") {
|
|
86
|
-
return wrapObject(vm, value, beginAsyncProcess, endAsyncProcess);
|
|
171
|
+
return wrapObject(vm, value, beginAsyncProcess, endAsyncProcess, logger);
|
|
87
172
|
} else if (typeof value === "string") {
|
|
88
173
|
return vm.newString(value);
|
|
89
174
|
} else if (typeof value === "number") {
|
|
@@ -91,17 +176,19 @@ function wrap(vm, value, context, beginAsyncProcess, endAsyncProcess) {
|
|
|
91
176
|
} else if (typeof value === "bigint") {
|
|
92
177
|
return vm.newBigInt(value);
|
|
93
178
|
} else if (typeof value === "boolean") {
|
|
94
|
-
|
|
179
|
+
const res = vm.evalCode(value ? "true" : "false");
|
|
180
|
+
return quickjsEmscripten.isSuccess(res) ? res.value : null;
|
|
95
181
|
} else if (typeof value === "undefined") {
|
|
96
182
|
return vm.undefined;
|
|
97
183
|
} else {
|
|
184
|
+
console.error("Unknown type", value);
|
|
98
185
|
return null;
|
|
99
186
|
}
|
|
100
187
|
}
|
|
101
|
-
function wrapObject(vm, obj, beginAsyncProcess, endAsyncProcess) {
|
|
188
|
+
function wrapObject(vm, obj, beginAsyncProcess, endAsyncProcess, logger) {
|
|
102
189
|
const vmObject = vm.newObject();
|
|
103
190
|
for (const [key, value] of Object.entries(obj)) {
|
|
104
|
-
let wrappedValue = wrap(vm, value, obj, beginAsyncProcess, endAsyncProcess);
|
|
191
|
+
let wrappedValue = wrap(vm, value, obj, beginAsyncProcess, endAsyncProcess, logger);
|
|
105
192
|
if (wrappedValue !== null) {
|
|
106
193
|
vm.setProp(vmObject, key, wrappedValue);
|
|
107
194
|
if (wrappedValue != vm.undefined && wrappedValue != vm.null && wrappedValue.dispose) {
|
|
@@ -111,11 +198,11 @@ function wrapObject(vm, obj, beginAsyncProcess, endAsyncProcess) {
|
|
|
111
198
|
}
|
|
112
199
|
return vmObject;
|
|
113
200
|
}
|
|
114
|
-
function wrapObjectWhitelist(vm, obj, whitelist, beginAsyncProcess, endAsyncProcess) {
|
|
201
|
+
function wrapObjectWhitelist(vm, obj, whitelist, beginAsyncProcess, endAsyncProcess, logger) {
|
|
115
202
|
const vmObject = vm.newObject();
|
|
116
203
|
whitelist.forEach((key) => {
|
|
117
204
|
const value = obj[key];
|
|
118
|
-
const wrappedValue = wrap(vm, value, obj, beginAsyncProcess, endAsyncProcess);
|
|
205
|
+
const wrappedValue = wrap(vm, value, obj, beginAsyncProcess, endAsyncProcess, logger);
|
|
119
206
|
if (wrappedValue !== null) {
|
|
120
207
|
vm.setProp(vmObject, key, wrappedValue);
|
|
121
208
|
if (wrappedValue != vm.undefined && wrappedValue != vm.null && wrappedValue.dispose) {
|
|
@@ -125,10 +212,10 @@ function wrapObjectWhitelist(vm, obj, whitelist, beginAsyncProcess, endAsyncProc
|
|
|
125
212
|
});
|
|
126
213
|
return vmObject;
|
|
127
214
|
}
|
|
128
|
-
function wrapArray(vm, arr, beginAsyncProcess, endAsyncProcess) {
|
|
215
|
+
function wrapArray(vm, arr, beginAsyncProcess, endAsyncProcess, logger) {
|
|
129
216
|
const vmArray = vm.newArray();
|
|
130
217
|
for (let i = 0; i < arr.length; i++) {
|
|
131
|
-
let wrappedValue = wrap(vm, arr[i], arr, beginAsyncProcess, endAsyncProcess);
|
|
218
|
+
let wrappedValue = wrap(vm, arr[i], arr, beginAsyncProcess, endAsyncProcess, logger);
|
|
132
219
|
vm.setProp(vmArray, i, wrappedValue);
|
|
133
220
|
if (wrappedValue != vm.undefined && wrappedValue != vm.null) {
|
|
134
221
|
wrappedValue.dispose();
|
|
@@ -136,15 +223,47 @@ function wrapArray(vm, arr, beginAsyncProcess, endAsyncProcess) {
|
|
|
136
223
|
}
|
|
137
224
|
return vmArray;
|
|
138
225
|
}
|
|
139
|
-
function
|
|
226
|
+
function validationDecorator(schema) {
|
|
227
|
+
return function(...args) {
|
|
228
|
+
let argumentObject = {};
|
|
229
|
+
const params = Object.keys(schema.def.shape);
|
|
230
|
+
args.forEach((value, index) => {
|
|
231
|
+
if (index < params.length) {
|
|
232
|
+
argumentObject[params[index]] = value;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
schema.parse(argumentObject);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function wrapGenericFunction(vm, fn, context = void 0, beginAsyncProcess, endAsyncProcess, logger) {
|
|
140
239
|
const vmFn = vm.newFunction(fn.name, (...args) => {
|
|
141
240
|
const unwrappedArgs = args.map(vm.dump);
|
|
142
241
|
try {
|
|
242
|
+
const validatorName = `_params_${fn.name}`;
|
|
243
|
+
const hasValidator = context != void 0 && validatorName in context && "parse" in context[validatorName];
|
|
244
|
+
if (hasValidator) {
|
|
245
|
+
const schema = context[validatorName];
|
|
246
|
+
try {
|
|
247
|
+
const validator = validationDecorator(schema);
|
|
248
|
+
validator(...unwrappedArgs);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
if (e instanceof zod.ZodError) {
|
|
251
|
+
throw new Error(`Parameter validation failed for function "${fn.name}". Expected Parameters: [${Object.keys(schema.def.shape).join(",")}]. Errors:
|
|
252
|
+
${zod.z.prettifyError(e)}`);
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`Parameter validation error calling ${fn.name}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
143
257
|
const result = fn.call(context, ...unwrappedArgs);
|
|
144
|
-
|
|
258
|
+
logger.trace(fn.name, unwrappedArgs, result);
|
|
259
|
+
const wrappedResult = wrap(vm, result, void 0, beginAsyncProcess, endAsyncProcess, logger);
|
|
145
260
|
return wrappedResult;
|
|
146
261
|
} catch (e) {
|
|
147
|
-
|
|
262
|
+
logger.error([e], "sandbox");
|
|
263
|
+
if (e instanceof Error) {
|
|
264
|
+
throw e;
|
|
265
|
+
}
|
|
266
|
+
throw new Error("An exception occurred calling " + fn.name);
|
|
148
267
|
}
|
|
149
268
|
});
|
|
150
269
|
return vmFn;
|
|
@@ -232,34 +351,6 @@ function tsCompile(source, options) {
|
|
|
232
351
|
}
|
|
233
352
|
return ts__namespace.transpileModule(source, options).outputText;
|
|
234
353
|
}
|
|
235
|
-
class Logger {
|
|
236
|
-
constructor(debug = false) {
|
|
237
|
-
this.logs = [];
|
|
238
|
-
this.debug = false;
|
|
239
|
-
this.debug = debug;
|
|
240
|
-
}
|
|
241
|
-
log(args, source) {
|
|
242
|
-
if (Array.isArray(args)) {
|
|
243
|
-
this.logs.push({ type: "log", args, source });
|
|
244
|
-
} else {
|
|
245
|
-
this.logs.push({ type: "log", args: [args], source });
|
|
246
|
-
}
|
|
247
|
-
if (this.debug) {
|
|
248
|
-
console.log(`[${source}]:`, ...args);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
error(args, source) {
|
|
252
|
-
if (Array.isArray(args)) {
|
|
253
|
-
this.logs.push({ type: "error", args, source });
|
|
254
|
-
} else {
|
|
255
|
-
this.logs.push({ type: "error", args: [args], source });
|
|
256
|
-
}
|
|
257
|
-
console.error(`[${source}]:`, ...args);
|
|
258
|
-
}
|
|
259
|
-
clear() {
|
|
260
|
-
this.logs = [];
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
354
|
async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug = false) {
|
|
264
355
|
const vm = QuickJS.newContext();
|
|
265
356
|
let errorState = false;
|
|
@@ -301,8 +392,9 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
301
392
|
globalOption.value,
|
|
302
393
|
globalOption.whitelist,
|
|
303
394
|
beginAsyncProcess,
|
|
304
|
-
endAsyncProcess
|
|
305
|
-
|
|
395
|
+
endAsyncProcess,
|
|
396
|
+
logger
|
|
397
|
+
) : wrap(vm, globalOption.value, globalOption, beginAsyncProcess, endAsyncProcess, logger);
|
|
306
398
|
vm.setProp(vm.global, globalNames[i], globalObj);
|
|
307
399
|
globalObj.dispose();
|
|
308
400
|
}
|
|
@@ -310,7 +402,7 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
310
402
|
const requireHandle = vm.newFunction("require", (...args) => {
|
|
311
403
|
const nativeArgs = args.map(vm.dump);
|
|
312
404
|
if (requireLookup[nativeArgs[0]]) {
|
|
313
|
-
const returnObj = wrapObject(vm, requireLookup[nativeArgs[0]], beginAsyncProcess, endAsyncProcess);
|
|
405
|
+
const returnObj = wrapObject(vm, requireLookup[nativeArgs[0]], beginAsyncProcess, endAsyncProcess, logger);
|
|
314
406
|
return returnObj;
|
|
315
407
|
} else {
|
|
316
408
|
return vm.undefined;
|
|
@@ -341,22 +433,22 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
341
433
|
logger.error(["Could not compile:", code], "compiler");
|
|
342
434
|
return {
|
|
343
435
|
success: false,
|
|
344
|
-
logs: logger.
|
|
436
|
+
logs: logger.getLogs()
|
|
345
437
|
};
|
|
346
438
|
}
|
|
347
439
|
} catch (e) {
|
|
348
440
|
logger.error(["Error testing code:", e], "compiler");
|
|
349
441
|
return {
|
|
350
442
|
success: false,
|
|
351
|
-
logs: logger.
|
|
443
|
+
logs: logger.getLogs()
|
|
352
444
|
};
|
|
353
445
|
}
|
|
354
446
|
return {
|
|
355
447
|
success: true,
|
|
356
|
-
logs: logger.
|
|
448
|
+
logs: logger.getLogs()
|
|
357
449
|
};
|
|
358
450
|
},
|
|
359
|
-
runCode: (code) => {
|
|
451
|
+
runCode: async (code) => {
|
|
360
452
|
try {
|
|
361
453
|
logger.clear();
|
|
362
454
|
const compiled = tsCompile(code);
|
|
@@ -364,7 +456,7 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
364
456
|
logger.error(["Could not compile:", code], "compiler");
|
|
365
457
|
return {
|
|
366
458
|
success: false,
|
|
367
|
-
logs: logger.
|
|
459
|
+
logs: logger.getLogs()
|
|
368
460
|
};
|
|
369
461
|
}
|
|
370
462
|
errorState = false;
|
|
@@ -375,23 +467,37 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
375
467
|
result.error.dispose();
|
|
376
468
|
return {
|
|
377
469
|
success: false,
|
|
378
|
-
logs: logger.
|
|
470
|
+
logs: logger.getPrettyLogs()
|
|
379
471
|
};
|
|
380
472
|
} else if ("value" in result) {
|
|
473
|
+
const settled = vm.resolvePromise(result.value);
|
|
381
474
|
result.value.dispose();
|
|
475
|
+
vm.runtime.executePendingJobs();
|
|
476
|
+
const promiseResult = await settled;
|
|
477
|
+
if (promiseResult.error) {
|
|
478
|
+
const err = vm.dump(promiseResult.error);
|
|
479
|
+
promiseResult.error.dispose();
|
|
480
|
+
const lines = compiled.split("\n").map((l, i) => `${i + 1}: ${l}`);
|
|
481
|
+
logger.error(["Uncaught exception:", err, "\n", lines.join("\n")], "runner");
|
|
482
|
+
return {
|
|
483
|
+
success: false,
|
|
484
|
+
logs: logger.getPrettyLogs()
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
promiseResult.value.dispose();
|
|
382
488
|
if (errorState) {
|
|
383
489
|
const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
|
|
384
490
|
logger.error(["An error was reported during execution. Compiled code with line numbers:\n", lines.join("\n")], "runner");
|
|
385
491
|
}
|
|
386
492
|
return {
|
|
387
493
|
success: !errorState,
|
|
388
|
-
logs: logger.
|
|
494
|
+
logs: logger.getPrettyLogs()
|
|
389
495
|
};
|
|
390
496
|
} else {
|
|
391
497
|
logger.error(["Unknown execution result:", result], "runner");
|
|
392
498
|
return {
|
|
393
499
|
success: false,
|
|
394
|
-
logs: logger.
|
|
500
|
+
logs: logger.getPrettyLogs()
|
|
395
501
|
};
|
|
396
502
|
}
|
|
397
503
|
} catch (e) {
|
|
@@ -399,7 +505,7 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
399
505
|
}
|
|
400
506
|
return {
|
|
401
507
|
success: false,
|
|
402
|
-
logs: logger.
|
|
508
|
+
logs: logger.getLogs()
|
|
403
509
|
};
|
|
404
510
|
}
|
|
405
511
|
};
|
|
@@ -408,13 +514,12 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
|
|
|
408
514
|
require$1("dotenv").config();
|
|
409
515
|
const openai = new openai$1.OpenAI({ baseURL: process.env.OPENAI_API_BASE_URL || void 0, apiKey: process.env.OPENAI_API_KEY || void 0 });
|
|
410
516
|
let QuickJS = null;
|
|
517
|
+
function createLog(message, source) {
|
|
518
|
+
return { type: "log", args: [message], source };
|
|
519
|
+
}
|
|
411
520
|
const wrapCode = (apiGlobalName, apiFileDefPath, code) => `
|
|
412
521
|
(async () => {
|
|
413
|
-
|
|
414
|
-
${code}
|
|
415
|
-
} catch(err) {
|
|
416
|
-
console.error(err);
|
|
417
|
-
}
|
|
522
|
+
${code}
|
|
418
523
|
})();
|
|
419
524
|
`;
|
|
420
525
|
const wrapCodeBlock = (code, codeType = "") => `
|
|
@@ -428,13 +533,13 @@ function zodParseJSON(schema) {
|
|
|
428
533
|
function createBasePrompt(apiFilePath, apiGlobalName, documentationPath) {
|
|
429
534
|
const apiText = fs.readFileSync(apiFilePath, "utf8");
|
|
430
535
|
const documentationText = documentationPath ? fs.readFileSync(documentationPath, "utf8") : "";
|
|
431
|
-
const createTaskPrompt = fs.readFileSync("
|
|
536
|
+
const createTaskPrompt = fs.readFileSync(path.resolve(__dirname, "../prompts/generate-code-prompt.md"), "utf8").replace("{{DOCUMENTATION}}", documentationText).replace("{{API}}", apiText).replace("{{API_GLOBAL_NAME}}", apiGlobalName || "");
|
|
432
537
|
return createTaskPrompt;
|
|
433
538
|
}
|
|
434
539
|
const runCodeSchema = zod.z.object({
|
|
435
540
|
code: zod.z.string()
|
|
436
541
|
});
|
|
437
|
-
|
|
542
|
+
zod.z.object({
|
|
438
543
|
code: zod.z.string()
|
|
439
544
|
});
|
|
440
545
|
const submitCodeSchema = zod.z.object({
|
|
@@ -456,34 +561,42 @@ const generateCode = async function(instance, queryText, userChatHistory, create
|
|
|
456
561
|
if (debug) {
|
|
457
562
|
console.log("Running code...", code);
|
|
458
563
|
}
|
|
564
|
+
const checkRes = await instance.checkCode(code);
|
|
565
|
+
if (!checkRes.success) {
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
reason: "Code check failed",
|
|
569
|
+
logs: checkRes.logs
|
|
570
|
+
};
|
|
571
|
+
}
|
|
459
572
|
const res = await instance.runCode(code);
|
|
460
573
|
if (hideLogsFromAgent) {
|
|
461
574
|
res.logs = [];
|
|
462
575
|
}
|
|
463
576
|
return res;
|
|
464
577
|
},
|
|
465
|
-
description: "
|
|
578
|
+
description: "Check and run the provided code in a sandboxed environment. The code will have access to the API object. Returns the result of the code execution. Use this for testing and experimentation.",
|
|
466
579
|
parse: zodParseJSON(runCodeSchema),
|
|
467
|
-
parameters:
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
type: "function",
|
|
472
|
-
function: {
|
|
473
|
-
name: "checkCode",
|
|
474
|
-
// @ts-ignore
|
|
475
|
-
function: async ({ code }) => {
|
|
476
|
-
if (debug) {
|
|
477
|
-
console.log("Checking code...", code);
|
|
478
|
-
}
|
|
479
|
-
const res = await instance.checkCode(code);
|
|
480
|
-
return res;
|
|
481
|
-
},
|
|
482
|
-
description: "Check the provided code for errors or issues without running it. Returns the result of the code check.",
|
|
483
|
-
parse: zodParseJSON(checkCodeSchema),
|
|
484
|
-
parameters: zodToJsonSchema.zodToJsonSchema(checkCodeSchema)
|
|
580
|
+
parameters: runCodeSchema.toJSONSchema()
|
|
485
581
|
}
|
|
486
582
|
},
|
|
583
|
+
// {
|
|
584
|
+
// type: "function",
|
|
585
|
+
// function: {
|
|
586
|
+
// name: "checkCode",
|
|
587
|
+
// // @ts-ignore
|
|
588
|
+
// function: async ({code}: {code: string}) => {
|
|
589
|
+
// if(debug) {
|
|
590
|
+
// console.log("Checking code...", code);
|
|
591
|
+
// }
|
|
592
|
+
// const res = await instance.checkCode(code);
|
|
593
|
+
// return res;
|
|
594
|
+
// },
|
|
595
|
+
// description: "Check the provided code for errors or issues without running it. Returns the result of the code check.",
|
|
596
|
+
// parse: zodParseJSON(checkCodeSchema),
|
|
597
|
+
// parameters: checkCodeSchema.toJSONSchema(),
|
|
598
|
+
// }
|
|
599
|
+
// },
|
|
487
600
|
{
|
|
488
601
|
type: "function",
|
|
489
602
|
function: {
|
|
@@ -498,14 +611,18 @@ const generateCode = async function(instance, queryText, userChatHistory, create
|
|
|
498
611
|
logs: checkRes.logs
|
|
499
612
|
};
|
|
500
613
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
614
|
+
const res = await instance.runCode(code);
|
|
615
|
+
if (hideLogsFromAgent) {
|
|
616
|
+
res.logs = [];
|
|
617
|
+
}
|
|
618
|
+
if (res.success) {
|
|
619
|
+
finalSubmittedCode = code;
|
|
620
|
+
}
|
|
621
|
+
return res;
|
|
505
622
|
},
|
|
506
|
-
description: "Submit the provided code as the final code
|
|
623
|
+
description: "Submit the provided code as a candidate for the final code. The code will be checked and run and output logs and traces will be provided. Check the logs, if satisifed, respond to the user otherwise submit again.",
|
|
507
624
|
parse: zodParseJSON(submitCodeSchema),
|
|
508
|
-
parameters:
|
|
625
|
+
parameters: submitCodeSchema.toJSONSchema()
|
|
509
626
|
}
|
|
510
627
|
}
|
|
511
628
|
];
|
|
@@ -527,17 +644,13 @@ const generateCode = async function(instance, queryText, userChatHistory, create
|
|
|
527
644
|
...additionalModelOptions
|
|
528
645
|
}).on("message", async (message) => {
|
|
529
646
|
if (debug) {
|
|
530
|
-
|
|
647
|
+
if ("reasoning_content" in message) {
|
|
648
|
+
console.log(message.reasoning_content);
|
|
649
|
+
}
|
|
531
650
|
}
|
|
532
651
|
newHistoryItems.push(message);
|
|
533
652
|
}).on("functionToolCall", (functionCall) => {
|
|
534
|
-
if (debug) {
|
|
535
|
-
console.log("Function Call:", JSON.stringify(functionCall, null, 2));
|
|
536
|
-
}
|
|
537
653
|
}).on("functionToolCallResult", (functionCall) => {
|
|
538
|
-
if (debug) {
|
|
539
|
-
console.log("Function Call Result:", JSON.stringify(functionCall, null, 2));
|
|
540
|
-
}
|
|
541
654
|
});
|
|
542
655
|
const finalContent = await runner.finalContent();
|
|
543
656
|
if (debug) {
|
|
@@ -641,7 +754,7 @@ async function createWithAPI(options) {
|
|
|
641
754
|
console.error("Unknown failure checking code");
|
|
642
755
|
return {
|
|
643
756
|
success: false,
|
|
644
|
-
logs: []
|
|
757
|
+
logs: [createLog("Unknown failure checking code", "compiler")]
|
|
645
758
|
};
|
|
646
759
|
}
|
|
647
760
|
if (debug) {
|
|
@@ -682,7 +795,7 @@ async function createWithAPI(options) {
|
|
|
682
795
|
};
|
|
683
796
|
}
|
|
684
797
|
}
|
|
685
|
-
const { vm, runCode:
|
|
798
|
+
const { vm, runCode: sandbox_runCode, isAsyncProcessRunning } = await createSandbox(QuickJS, apiExports ? {
|
|
686
799
|
[apiDefFilePath]: apiExports
|
|
687
800
|
} : void 0, {
|
|
688
801
|
...apiGlobals,
|
|
@@ -695,7 +808,7 @@ async function createWithAPI(options) {
|
|
|
695
808
|
if (debug) {
|
|
696
809
|
console.log("Running Code:\n", code);
|
|
697
810
|
}
|
|
698
|
-
const res = await
|
|
811
|
+
const res = await sandbox_runCode(wrapCode(apiGlobalName, apiDefFilePath, code));
|
|
699
812
|
if (!res) {
|
|
700
813
|
console.error("Unknown failure running task");
|
|
701
814
|
return {
|
|
@@ -726,27 +839,35 @@ async function createWithAPI(options) {
|
|
|
726
839
|
// generates code and runs it in one step
|
|
727
840
|
processRequest: async function(userQuery, currentContext = void 0) {
|
|
728
841
|
if (debug) {
|
|
729
|
-
console.log("Query:", userQuery);
|
|
842
|
+
console.log("[debug] Query:", userQuery);
|
|
730
843
|
}
|
|
731
844
|
console.time("Generate Task");
|
|
845
|
+
const prompt = createTaskPrompt.replace("{{CONTEXT}}", currentContext ? JSON.stringify(currentContext, null, 2) : "");
|
|
732
846
|
const generatedCode = await generateCode(
|
|
733
847
|
instance,
|
|
734
848
|
userQuery,
|
|
735
849
|
[],
|
|
736
|
-
|
|
850
|
+
prompt,
|
|
737
851
|
debug,
|
|
738
852
|
hideLogsFromAgent,
|
|
739
853
|
model,
|
|
740
854
|
additionalModelOptions
|
|
741
855
|
);
|
|
742
856
|
console.timeEnd("Generate Task");
|
|
743
|
-
console.
|
|
857
|
+
console.log(`generated code result
|
|
858
|
+
Success?: ${generatedCode?.success}
|
|
859
|
+
Comments: ${generatedCode?.comments}
|
|
860
|
+
Running Code...`);
|
|
861
|
+
console.time("Run Code took");
|
|
744
862
|
if (generatedCode?.code) {
|
|
745
|
-
await instance.runCode(generatedCode.code);
|
|
863
|
+
const codeRunResult = await instance.runCode(generatedCode.code);
|
|
864
|
+
if (!codeRunResult.success) {
|
|
865
|
+
console.error("Error running generated code");
|
|
866
|
+
}
|
|
746
867
|
} else {
|
|
747
868
|
console.error("No code generated", generatedCode);
|
|
748
869
|
}
|
|
749
|
-
console.timeEnd("Run Code");
|
|
870
|
+
console.timeEnd("Run Code took");
|
|
750
871
|
return generatedCode;
|
|
751
872
|
}
|
|
752
873
|
};
|