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/src/index.ts
CHANGED
|
@@ -1,495 +1,519 @@
|
|
|
1
|
-
require('dotenv').config()
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { OpenAI } from "openai";
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})();
|
|
58
|
-
`;
|
|
59
|
-
|
|
60
|
-
const wrapCodeBlock = (code:string, codeType:string = "") => `
|
|
61
|
-
\`\`\`${codeType}
|
|
62
|
-
${code}
|
|
63
|
-
\`\`\`
|
|
64
|
-
`;
|
|
65
|
-
|
|
66
|
-
export function zodParseJSON<T>(schema:
|
|
67
|
-
return (input: string): T => schema.parse(JSON.parse(input));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function createBasePrompt(apiFilePath:string, apiGlobalName: string, documentationPath?: string, ): string {
|
|
71
|
-
const apiText: string = fs.readFileSync(apiFilePath, 'utf8');
|
|
72
|
-
const documentationText: string = documentationPath ? fs.readFileSync(documentationPath, 'utf8') : '';
|
|
73
|
-
|
|
74
|
-
//console.time("Loading templates");
|
|
75
|
-
const createTaskPrompt: string = fs.readFileSync( '
|
|
76
|
-
//console.timeEnd("Loading templates")
|
|
77
|
-
|
|
78
|
-
return createTaskPrompt;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const runCodeSchema = z.object({
|
|
83
|
-
code: z.string(),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const checkCodeSchema = z.object({
|
|
87
|
-
code: z.string(),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const submitCodeSchema = z.object({
|
|
91
|
-
code: z.string(),
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Basic harness for writing code for the sandbox.
|
|
95
|
-
export const generateCode = async function(instance:AIMyAPIInstance, queryText:string, userChatHistory:ChatCompletionMessageParam[], createTaskPrompt:string, debug:boolean = false, hideLogsFromAgent:boolean = false, model="gpt-5-mini", additionalModelOptions?: object, attempt:number = 0): Promise<GenerateCodeResult | null> {
|
|
96
|
-
let finalSubmittedCode = "";
|
|
97
|
-
|
|
98
|
-
if(debug) {
|
|
99
|
-
console.log("Generating code for query:", queryText);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const newHistoryItems = [];
|
|
103
|
-
// checkcode
|
|
104
|
-
const codingTools: AutoParseableTool<any, true>[] = [
|
|
105
|
-
{
|
|
106
|
-
type: "function",
|
|
107
|
-
function: {
|
|
108
|
-
name: "runCode",
|
|
109
|
-
// @ts-ignore
|
|
110
|
-
function: async ({code}: {code: string}) => {
|
|
111
|
-
if(debug) {
|
|
112
|
-
console.log("Running code...", code);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if(!
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
1
|
+
require('dotenv').config()
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { OpenAI } from "openai";
|
|
4
|
+
|
|
5
|
+
import { LogSource, SandboxLog } from "./logger";
|
|
6
|
+
import createSandbox, { CodeRunResult } from "./sandbox";
|
|
7
|
+
import { rejectOpenPromises } from './sandbox-wrappers';
|
|
8
|
+
|
|
9
|
+
import { ChatCompletionMessageParam } from 'openai/resources/index.mjs';
|
|
10
|
+
import { getQuickJS, QuickJSWASMModule } from "quickjs-emscripten";
|
|
11
|
+
|
|
12
|
+
import { AutoParseableTool } from 'openai/lib/parser.js';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
|
|
15
|
+
import path from 'path';
|
|
16
|
+
|
|
17
|
+
const openai = new OpenAI( { baseURL: process.env.OPENAI_API_BASE_URL || undefined, apiKey: process.env.OPENAI_API_KEY || undefined } );
|
|
18
|
+
|
|
19
|
+
let QuickJS:QuickJSWASMModule | null = null;
|
|
20
|
+
|
|
21
|
+
export interface AIMyAPIOptions {
|
|
22
|
+
apiObject: object;
|
|
23
|
+
apiExports?: object;
|
|
24
|
+
apiWhitelist?: string[]; // List of functions to expose to the user. Necessary to pass this yourself if you are extending your api from another class.
|
|
25
|
+
apiGlobals?: object;
|
|
26
|
+
apiDefFilePath: string; // Full path to the api definition file so that it can be loaded into the prompt
|
|
27
|
+
apiGlobalName?: string;
|
|
28
|
+
apiDocsPath?: string;
|
|
29
|
+
debug?: boolean;
|
|
30
|
+
model?: string;
|
|
31
|
+
additionalModelOptions?: object;
|
|
32
|
+
hideLogsFromAgent?: boolean; // If true, logs from code execution will not be included in the messages sent to the agent. This can be useful to prevent the agent from being overwhelmed with logs or to hide implementation details. Default is false (logs will be included in messages).
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export interface AIMyAPIInstance {
|
|
36
|
+
options:AIMyAPIOptions;
|
|
37
|
+
generateCode: (queryText:string, userChatHistory:ChatCompletionMessageParam[]) => Promise<GenerateCodeResult | null>;
|
|
38
|
+
runCode: (code:string) => Promise<CodeRunResult>;
|
|
39
|
+
checkCode: (code:string) => Promise<CodeRunResult>;
|
|
40
|
+
// run a single request with no history
|
|
41
|
+
processRequest: (userQuery:string, context?: object) => Promise<GenerateCodeResult | null>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface GenerateCodeResult {
|
|
45
|
+
success: boolean;
|
|
46
|
+
code?: string;
|
|
47
|
+
comments?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createLog( message: string, source: LogSource) {
|
|
51
|
+
return { type: "log", args: [message], source } as SandboxLog
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const wrapCode = (apiGlobalName:string, apiFileDefPath:string, code:string) => `
|
|
55
|
+
(async () => {
|
|
56
|
+
${code}
|
|
57
|
+
})();
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const wrapCodeBlock = (code:string, codeType:string = "") => `
|
|
61
|
+
\`\`\`${codeType}
|
|
62
|
+
${code}
|
|
63
|
+
\`\`\`
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
export function zodParseJSON<T>(schema: { parse: any }) {
|
|
67
|
+
return (input: string): T => schema.parse(JSON.parse(input));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createBasePrompt(apiFilePath:string, apiGlobalName: string, documentationPath?: string, ): string {
|
|
71
|
+
const apiText: string = fs.readFileSync(apiFilePath, 'utf8');
|
|
72
|
+
const documentationText: string = documentationPath ? fs.readFileSync(documentationPath, 'utf8') : '';
|
|
73
|
+
|
|
74
|
+
//console.time("Loading templates");
|
|
75
|
+
const createTaskPrompt: string = fs.readFileSync( path.resolve(__dirname, '../prompts/generate-code-prompt.md'), 'utf8').replace("{{DOCUMENTATION}}", documentationText).replace("{{API}}", apiText).replace("{{API_GLOBAL_NAME}}", apiGlobalName || "");
|
|
76
|
+
//console.timeEnd("Loading templates")
|
|
77
|
+
|
|
78
|
+
return createTaskPrompt;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const runCodeSchema = z.object({
|
|
83
|
+
code: z.string(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const checkCodeSchema = z.object({
|
|
87
|
+
code: z.string(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const submitCodeSchema = z.object({
|
|
91
|
+
code: z.string(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Basic harness for writing code for the sandbox.
|
|
95
|
+
export const generateCode = async function(instance:AIMyAPIInstance, queryText:string, userChatHistory:ChatCompletionMessageParam[], createTaskPrompt:string, debug:boolean = false, hideLogsFromAgent:boolean = false, model="gpt-5-mini", additionalModelOptions?: object, attempt:number = 0): Promise<GenerateCodeResult | null> {
|
|
96
|
+
let finalSubmittedCode = "";
|
|
97
|
+
|
|
98
|
+
if(debug) {
|
|
99
|
+
console.log("Generating code for query:", queryText);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const newHistoryItems: any[] = [];
|
|
103
|
+
// checkcode
|
|
104
|
+
const codingTools: AutoParseableTool<any, true>[] = [
|
|
105
|
+
{
|
|
106
|
+
type: "function",
|
|
107
|
+
function: {
|
|
108
|
+
name: "runCode",
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
function: async ({code}: {code: string}) => {
|
|
111
|
+
if(debug) {
|
|
112
|
+
console.log("Running code...", code);
|
|
113
|
+
}
|
|
114
|
+
const checkRes = await instance.checkCode(code);
|
|
115
|
+
if(!checkRes.success) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
reason: "Code check failed",
|
|
119
|
+
logs: checkRes.logs,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const res = await instance.runCode(code);
|
|
124
|
+
|
|
125
|
+
if(hideLogsFromAgent) {
|
|
126
|
+
res.logs = [];
|
|
127
|
+
}
|
|
128
|
+
return res;
|
|
129
|
+
},
|
|
130
|
+
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.",
|
|
131
|
+
parse: zodParseJSON(runCodeSchema),
|
|
132
|
+
parameters: runCodeSchema.toJSONSchema(),
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
// {
|
|
136
|
+
// type: "function",
|
|
137
|
+
// function: {
|
|
138
|
+
// name: "checkCode",
|
|
139
|
+
// // @ts-ignore
|
|
140
|
+
// function: async ({code}: {code: string}) => {
|
|
141
|
+
// if(debug) {
|
|
142
|
+
// console.log("Checking code...", code);
|
|
143
|
+
// }
|
|
144
|
+
// const res = await instance.checkCode(code);
|
|
145
|
+
|
|
146
|
+
// return res;
|
|
147
|
+
|
|
148
|
+
// },
|
|
149
|
+
// description: "Check the provided code for errors or issues without running it. Returns the result of the code check.",
|
|
150
|
+
// parse: zodParseJSON(checkCodeSchema),
|
|
151
|
+
// parameters: checkCodeSchema.toJSONSchema(),
|
|
152
|
+
// }
|
|
153
|
+
// },
|
|
154
|
+
{
|
|
155
|
+
type: "function",
|
|
156
|
+
function: {
|
|
157
|
+
name: "submitCode",
|
|
158
|
+
// @ts-ignore
|
|
159
|
+
function: async ({code}: {code: string}) => {
|
|
160
|
+
const checkRes = await instance.checkCode(code);
|
|
161
|
+
if(!checkRes.success) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
reason: "Code check failed",
|
|
165
|
+
logs: checkRes.logs,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const res = await instance.runCode(code);
|
|
170
|
+
|
|
171
|
+
if(hideLogsFromAgent) {
|
|
172
|
+
res.logs = [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if(res.success) {
|
|
176
|
+
finalSubmittedCode = code;
|
|
177
|
+
}
|
|
178
|
+
return res;
|
|
179
|
+
|
|
180
|
+
},
|
|
181
|
+
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.",
|
|
182
|
+
parse: zodParseJSON(submitCodeSchema),
|
|
183
|
+
parameters: submitCodeSchema.toJSONSchema(),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
|
|
190
|
+
const runner = openai.chat.completions
|
|
191
|
+
.runTools({
|
|
192
|
+
model,
|
|
193
|
+
messages: [
|
|
194
|
+
{
|
|
195
|
+
role: 'system',
|
|
196
|
+
content: "You will assist with writing or modifying code based on the user's request.",
|
|
197
|
+
},
|
|
198
|
+
...userChatHistory,
|
|
199
|
+
{
|
|
200
|
+
role: 'user',
|
|
201
|
+
content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText,
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
],
|
|
205
|
+
tools: codingTools,
|
|
206
|
+
...additionalModelOptions,
|
|
207
|
+
|
|
208
|
+
})
|
|
209
|
+
.on('message', async (message: ChatCompletionMessageParam) => {
|
|
210
|
+
if(debug) {
|
|
211
|
+
if("reasoning_content" in message) {
|
|
212
|
+
console.log(message.reasoning_content);
|
|
213
|
+
}
|
|
214
|
+
//console.log("Message:", JSON.stringify(message, null, 2));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
newHistoryItems.push(message);
|
|
218
|
+
})
|
|
219
|
+
.on('functionToolCall', (functionCall: OpenAI.Chat.Completions.ChatCompletionMessageFunctionToolCall.Function) => {
|
|
220
|
+
// if(debug) {
|
|
221
|
+
// console.log("Function Call:", JSON.stringify(functionCall, null, 2));
|
|
222
|
+
// }
|
|
223
|
+
})
|
|
224
|
+
.on('functionToolCallResult', (functionCall) => {
|
|
225
|
+
// if(debug) {
|
|
226
|
+
// console.log("Function Call Result:", JSON.stringify(functionCall, null, 2));
|
|
227
|
+
// }
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const finalContent = await runner.finalContent();
|
|
231
|
+
|
|
232
|
+
if(debug) {
|
|
233
|
+
console.log("Generate code final response:", finalContent);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if(!finalSubmittedCode) {
|
|
237
|
+
console.error("No code was submitted by the agent.");
|
|
238
|
+
if(attempt < 3) {
|
|
239
|
+
return generateCode(instance, `You must call submitCode with valid code before proceeding. Attempt ${attempt + 1} of 3.`, [
|
|
240
|
+
...userChatHistory,
|
|
241
|
+
{
|
|
242
|
+
role: 'user',
|
|
243
|
+
content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText,
|
|
244
|
+
},
|
|
245
|
+
...newHistoryItems
|
|
246
|
+
], createTaskPrompt, debug, hideLogsFromAgent, model, additionalModelOptions, attempt + 1);
|
|
247
|
+
} else {
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
comments: "No code was submitted by the agent after multiple attempts. Final comment: " + finalContent,
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
success: true,
|
|
258
|
+
code: finalSubmittedCode,
|
|
259
|
+
comments: finalContent || undefined,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
} catch(err) {
|
|
263
|
+
console.error(err);
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
comments: `Error generating code: ${err}`,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
async function createWithAPI(options:AIMyAPIOptions): Promise<AIMyAPIInstance> {
|
|
272
|
+
// extend options with defaults
|
|
273
|
+
const localOptions = {
|
|
274
|
+
...{
|
|
275
|
+
apiGlobalName: "api",
|
|
276
|
+
apiGlobals: {},
|
|
277
|
+
apiWhitelist: Object.getOwnPropertyNames(Object.getPrototypeOf(options.apiObject)).filter((f) => f !== "constructor" && !f.startsWith("_")),
|
|
278
|
+
debug: false,
|
|
279
|
+
model: process.env.OPENAI_API_MODEL || "gpt-5.4-mini",
|
|
280
|
+
additionalModelOptions: {},
|
|
281
|
+
hideLogsFromAgent: false,
|
|
282
|
+
},
|
|
283
|
+
...options
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
if(!localOptions.apiObject || !localOptions.apiDefFilePath) {
|
|
287
|
+
throw new Error("apiObject and apiDefFilePath are required");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const {apiObject, apiWhitelist, apiGlobalName, apiExports, apiDefFilePath, apiDocsPath, debug, model, additionalModelOptions, hideLogsFromAgent} = localOptions;
|
|
291
|
+
|
|
292
|
+
const createTaskPrompt = createBasePrompt( apiDefFilePath, apiGlobalName, apiDocsPath);
|
|
293
|
+
|
|
294
|
+
const instance = {
|
|
295
|
+
options: localOptions,
|
|
296
|
+
generateCode: async function(queryText:string, userChatHistory:ChatCompletionMessageParam[], currentContext:any = undefined) {
|
|
297
|
+
if(!queryText)
|
|
298
|
+
return null;
|
|
299
|
+
|
|
300
|
+
const formattedContext = wrapCodeBlock(JSON.stringify(currentContext, null, 2), "json");
|
|
301
|
+
|
|
302
|
+
return await generateCode(instance,
|
|
303
|
+
queryText,
|
|
304
|
+
userChatHistory,
|
|
305
|
+
createTaskPrompt.replace("{{CONTEXT}}", formattedContext),
|
|
306
|
+
debug,
|
|
307
|
+
hideLogsFromAgent,
|
|
308
|
+
model,
|
|
309
|
+
additionalModelOptions
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
checkCode: async function (generatedCode:string): Promise<CodeRunResult> {
|
|
313
|
+
if(!QuickJS) {
|
|
314
|
+
// Create QuickJS
|
|
315
|
+
if(debug)
|
|
316
|
+
console.log("Creating quick.js");
|
|
317
|
+
QuickJS = await getQuickJS();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let apiGlobals: { [key: string]: { value: any } } = {}
|
|
321
|
+
|
|
322
|
+
if(options.apiGlobals) {
|
|
323
|
+
for(const key in options.apiGlobals) {
|
|
324
|
+
apiGlobals[key] = {
|
|
325
|
+
value: (options.apiGlobals as Record<string, any>)[key],
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Create sandbox
|
|
331
|
+
const {vm, checkCode} = await createSandbox(QuickJS, apiExports ? {
|
|
332
|
+
[apiDefFilePath]: apiExports,
|
|
333
|
+
} : undefined, {
|
|
334
|
+
...apiGlobals,
|
|
335
|
+
[apiGlobalName]: {
|
|
336
|
+
value: apiObject,
|
|
337
|
+
whitelist: apiWhitelist
|
|
338
|
+
}
|
|
339
|
+
}, options.debug);
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
if(debug) {
|
|
343
|
+
console.log("checking Code:\n", generatedCode);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const res = await checkCode(wrapCode(apiGlobalName, apiDefFilePath, generatedCode));
|
|
347
|
+
|
|
348
|
+
if(!res) {
|
|
349
|
+
console.error("Unknown failure checking code");
|
|
350
|
+
return {
|
|
351
|
+
success: false,
|
|
352
|
+
logs: [createLog("Unknown failure checking code", "compiler")],
|
|
353
|
+
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
if(debug) {
|
|
359
|
+
console.log(res.success ? "Code check passed" : "Code check failed");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if(res.success) {
|
|
363
|
+
return {
|
|
364
|
+
success: true,
|
|
365
|
+
logs: [{ type: "log", args: ["✔️ Code check passed"], source: "compiler" }, ...res.logs],
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
logs: [{ type: "error", args: ["❌ Code check failed"], source: "compiler" }, ...res.logs],
|
|
372
|
+
};
|
|
373
|
+
} catch(err) {
|
|
374
|
+
if(debug) {
|
|
375
|
+
console.error("Error checking code:", err);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
vm.dispose();
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
logs: [],
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
runCode: async function (code:string): Promise<CodeRunResult> {
|
|
386
|
+
// TODO: fix code duplication with checkCode, maybe combine into a single function that takes a parameter for whether to actually run the code or just check it?
|
|
387
|
+
if(!QuickJS) {
|
|
388
|
+
// Create QuickJS
|
|
389
|
+
if(debug)
|
|
390
|
+
console.log("Creating quick.js");
|
|
391
|
+
QuickJS = await getQuickJS();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let apiGlobals: { [key: string]: { value: any } } = {}
|
|
395
|
+
|
|
396
|
+
if(options.apiGlobals) {
|
|
397
|
+
for(const key in options.apiGlobals) {
|
|
398
|
+
apiGlobals[key] = {
|
|
399
|
+
value: (options.apiGlobals as Record<string, any>)[key],
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Create sandbox
|
|
405
|
+
// NOTE: not sure why I have to replace the backslash here
|
|
406
|
+
const {vm, runCode: sandbox_runCode, isAsyncProcessRunning} = await createSandbox(QuickJS, apiExports ? {
|
|
407
|
+
[apiDefFilePath]: apiExports,
|
|
408
|
+
} : undefined, {
|
|
409
|
+
...apiGlobals,
|
|
410
|
+
[apiGlobalName]: {
|
|
411
|
+
value: apiObject,
|
|
412
|
+
whitelist: apiWhitelist
|
|
413
|
+
}
|
|
414
|
+
}, options.debug);
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
if(debug) {
|
|
418
|
+
console.log("Running Code:\n", code);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const res = await sandbox_runCode(wrapCode(apiGlobalName, apiDefFilePath, code));
|
|
422
|
+
|
|
423
|
+
if(!res) {
|
|
424
|
+
console.error("Unknown failure running task");
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
logs: [],
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
while(isAsyncProcessRunning()) {
|
|
432
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if(debug) {
|
|
436
|
+
console.log("Completed");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return res;
|
|
440
|
+
} catch(err) {
|
|
441
|
+
if(debug) {
|
|
442
|
+
console.error("Error running task:", err);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
447
|
+
|
|
448
|
+
rejectOpenPromises(vm);
|
|
449
|
+
vm.dispose();
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
success: false,
|
|
453
|
+
logs: [],
|
|
454
|
+
};
|
|
455
|
+
},
|
|
456
|
+
// generates code and runs it in one step
|
|
457
|
+
processRequest: async function (userQuery: string, currentContext:any = undefined):Promise<GenerateCodeResult | null> {
|
|
458
|
+
// Create prompt
|
|
459
|
+
|
|
460
|
+
if(debug) {
|
|
461
|
+
console.log("[debug] Query:", userQuery);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Generate Code
|
|
465
|
+
console.time("Generate Task")
|
|
466
|
+
const prompt = createTaskPrompt.replace("{{CONTEXT}}", currentContext ? JSON.stringify(currentContext, null, 2) : "" );
|
|
467
|
+
const generatedCode:GenerateCodeResult | null = await generateCode(instance,
|
|
468
|
+
userQuery,
|
|
469
|
+
[],
|
|
470
|
+
prompt,
|
|
471
|
+
debug,
|
|
472
|
+
hideLogsFromAgent,
|
|
473
|
+
model,
|
|
474
|
+
additionalModelOptions
|
|
475
|
+
);
|
|
476
|
+
console.timeEnd("Generate Task")
|
|
477
|
+
|
|
478
|
+
console.log(`generated code result\nSuccess?: ${generatedCode?.success}\nComments: ${generatedCode?.comments}\nRunning Code...`);
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
console.time("Run Code took")
|
|
482
|
+
if(generatedCode?.code) {
|
|
483
|
+
const codeRunResult = await instance.runCode(generatedCode.code);
|
|
484
|
+
if(!codeRunResult.success) {
|
|
485
|
+
console.error("Error running generated code");
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
console.error("No code generated", generatedCode);
|
|
489
|
+
}
|
|
490
|
+
console.timeEnd("Run Code took")
|
|
491
|
+
|
|
492
|
+
return generatedCode;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return instance;
|
|
497
|
+
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export interface AIMyAPIModuleExports {
|
|
501
|
+
createWithAPI: (options:AIMyAPIOptions) => Promise<AIMyAPIInstance>;
|
|
502
|
+
|
|
503
|
+
createBasePrompt: (apiFilePath:string, documentationPath: string) => string;
|
|
504
|
+
generateCode: (instance: AIMyAPIInstance, queryText:string, userChatHistory:ChatCompletionMessageParam[], createTaskPrompt:string, debug:boolean) => Promise<GenerateCodeResult | null>;
|
|
505
|
+
createSandbox: (QuickJS:QuickJSWASMModule, globals: any) => Promise<any>;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export const AIMyAPI:AIMyAPIModuleExports = {
|
|
509
|
+
// Standard way of using the library
|
|
510
|
+
createWithAPI,
|
|
511
|
+
|
|
512
|
+
// functions for customized usage
|
|
513
|
+
createBasePrompt,
|
|
514
|
+
generateCode,
|
|
515
|
+
createSandbox,
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export * from "./sandbox";
|
|
519
|
+
|