aimyapi 0.2.1 → 1.0.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/index.js CHANGED
@@ -6,6 +6,8 @@ var openai$1 = require('openai');
6
6
  var path = require('path');
7
7
  var quickjsEmscripten = require('quickjs-emscripten');
8
8
  var ts = require('typescript');
9
+ var zod = require('zod');
10
+ var zodToJsonSchema = require('zod-to-json-schema');
9
11
 
10
12
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
13
  function _interopNamespaceDefault(e) {
@@ -218,8 +220,8 @@ function injectTimingFunctions(vm, cbAddedTimer, cbRemovedTimer, maxTimeout = 6e
218
220
  _clearInterval.dispose();
219
221
  }
220
222
 
221
- function tsCompile(source, options = null) {
222
- if (null === options) {
223
+ function tsCompile(source, options) {
224
+ if (!options) {
223
225
  options = {
224
226
  compilerOptions: {
225
227
  target: ts__namespace.ScriptTarget.ES2020,
@@ -229,6 +231,34 @@ function tsCompile(source, options = null) {
229
231
  }
230
232
  return ts__namespace.transpileModule(source, options).outputText;
231
233
  }
234
+ class Logger {
235
+ constructor(debug = false) {
236
+ this.logs = [];
237
+ this.debug = false;
238
+ this.debug = debug;
239
+ }
240
+ log(args, source) {
241
+ if (Array.isArray(args)) {
242
+ this.logs.push({ type: "log", args, source });
243
+ } else {
244
+ this.logs.push({ type: "log", args: [args], source });
245
+ }
246
+ if (this.debug) {
247
+ console.log(`[${source}]:`, ...args);
248
+ }
249
+ }
250
+ error(args, source) {
251
+ if (Array.isArray(args)) {
252
+ this.logs.push({ type: "error", args, source });
253
+ } else {
254
+ this.logs.push({ type: "error", args: [args], source });
255
+ }
256
+ console.error(`[${source}]:`, ...args);
257
+ }
258
+ clear() {
259
+ this.logs = [];
260
+ }
261
+ }
232
262
  async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug = false) {
233
263
  const vm = QuickJS.newContext();
234
264
  let errorState = false;
@@ -245,20 +275,21 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
245
275
  let isAsyncProcessRunning = () => {
246
276
  return asyncProcessesRunning > 0;
247
277
  };
278
+ const logger = new Logger(debug);
248
279
  const logHandle = vm.newFunction("log", (...args) => {
249
280
  const nativeArgs = args.map(vm.dump);
250
- console.log("QuickJS Log:", ...nativeArgs);
281
+ logger.log(nativeArgs, "sandbox");
251
282
  });
252
283
  const consoleHandle = vm.newObject();
253
284
  const exportsHandle = vm.newObject();
254
285
  const errorHandle = vm.newFunction("error", (...args) => {
255
286
  try {
256
287
  const nativeArgs = args.map(vm.dump);
257
- console.log("QuickJS console.error:", ...nativeArgs);
288
+ logger.error(nativeArgs, "sandbox");
258
289
  lastError = JSON.stringify(nativeArgs);
259
290
  errorState = true;
260
291
  } catch (e) {
261
- console.log("Error in error:", e);
292
+ logger.error(["? Error in error:", e], "sandbox");
262
293
  }
263
294
  });
264
295
  const globalNames = Object.getOwnPropertyNames(globals);
@@ -300,170 +331,326 @@ async function createSandbox(QuickJS, requireLookup = {}, globals = {}, debug =
300
331
  return lastError;
301
332
  },
302
333
  isAsyncProcessRunning,
303
- runTask: (task) => {
334
+ // make sure code passes the typescript compilation
335
+ checkCode: (code) => {
336
+ logger.clear();
304
337
  try {
305
- const compiled = tsCompile(task);
338
+ const compiled = tsCompile(code);
306
339
  if (!compiled || compiled.length === 0) {
307
- console.error("Could not compile", task);
308
- return false;
340
+ logger.error(["Could not compile:", code], "compiler");
341
+ return {
342
+ success: false,
343
+ logs: logger.logs
344
+ };
345
+ }
346
+ } catch (e) {
347
+ logger.error(["Error testing code:", e], "compiler");
348
+ return {
349
+ success: false,
350
+ logs: logger.logs
351
+ };
352
+ }
353
+ return {
354
+ success: true,
355
+ logs: logger.logs
356
+ };
357
+ },
358
+ runCode: (code) => {
359
+ try {
360
+ logger.clear();
361
+ const compiled = tsCompile(code);
362
+ if (!compiled || compiled.length === 0) {
363
+ logger.error(["Could not compile:", code], "compiler");
364
+ return {
365
+ success: false,
366
+ logs: logger.logs
367
+ };
309
368
  }
310
369
  errorState = false;
311
370
  const result = vm.evalCode(compiled);
312
371
  if (result.error) {
313
- const lines = compiled.split("\n");
314
- for (let i = 0; i < lines.length; i++) {
315
- console.log(`${i + 1}: ${lines[i]}`);
316
- }
317
- console.log("Execution failed:", vm.dump(result.error));
372
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
373
+ logger.error(["Execution failed:", lines.join("\n"), vm.dump(result.error)], "runner");
318
374
  result.error.dispose();
319
- return false;
320
- } else {
375
+ return {
376
+ success: false,
377
+ logs: logger.logs
378
+ };
379
+ } else if ("value" in result) {
321
380
  result.value.dispose();
322
381
  if (errorState) {
323
- const lines = compiled.split("\n");
324
- for (let i = 0; i < lines.length; i++) {
325
- console.log(`${i + 1}: ${lines[i]}`);
326
- }
382
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
383
+ logger.error(["An error was reported during execution. Compiled code with line numbers:", lines.join("\n")], "runner");
327
384
  }
328
- return !errorState;
385
+ return {
386
+ success: !errorState,
387
+ logs: logger.logs
388
+ };
389
+ } else {
390
+ logger.error(["Unknown execution result:", result], "runner");
391
+ return {
392
+ success: false,
393
+ logs: logger.logs
394
+ };
329
395
  }
330
396
  } catch (e) {
331
- console.log(e);
397
+ logger.error(["Error running code:", e], "runner");
332
398
  }
333
- return false;
399
+ return {
400
+ success: false,
401
+ logs: logger.logs
402
+ };
334
403
  }
335
404
  };
336
405
  }
337
406
 
338
407
  require$1("dotenv").config();
339
- const openai = new openai$1.OpenAI({
340
- apiKey: process.env.OPENAI_API_KEY
341
- // This is also the default, can be omitted
342
- });
343
- const CODE_START = `\`\`\`
344
- import * as ApiDefs from 'api.ts'
345
-
346
- (async() {
347
- try {`;
348
- const CODE_END = ` } catch(err) {
408
+ const openai = new openai$1.OpenAI();
409
+ let QuickJS = null;
410
+ const wrapCode = (apiGlobalName, apiFileDefPath, code) => `
411
+ (async () => {
412
+ try {
413
+ ${code}
414
+ } catch(err) {
349
415
  console.error(err);
350
416
  }
351
417
  })();
352
- \`\`\``;
353
- function indentString(str, numTabs) {
354
- const tabs = " ".repeat(numTabs);
355
- return str.split("\n").map((line) => tabs + line).join("\n");
418
+ `;
419
+ const wrapCodeBlock = (code, codeType = "") => `
420
+ \`\`\`${codeType}
421
+ ${code}
422
+ \`\`\`
423
+ `;
424
+ function zodParseJSON(schema) {
425
+ return (input) => schema.parse(JSON.parse(input));
356
426
  }
357
- function replaceCodeBlocks(text) {
358
- const codeBlockRegex = /```[\s\S]*?```/g;
359
- return text.replace(codeBlockRegex, (match) => {
360
- const code = match.slice(3, -3).trim();
361
- return `${CODE_START}
362
- ${indentString(code, 2)}
363
- ${CODE_END}`;
364
- });
365
- }
366
- function createBasePrompt(apiFilePath, documentationPath) {
427
+ function createBasePrompt(apiFilePath, apiGlobalName, documentationPath) {
367
428
  const apiText = fs.readFileSync(apiFilePath, "utf8");
368
429
  const documentationText = documentationPath ? fs.readFileSync(documentationPath, "utf8") : "";
369
- const createTaskPrompt = fs.readFileSync(path.join(__dirname, "../prompts/create-task-prompt.md"), "utf8").replace("{{DOCUMENTATION}}", replaceCodeBlocks(documentationText)).replace("{{API}}", apiText);
430
+ const createTaskPrompt = fs.readFileSync(path.join(__dirname, "../prompts/create-task-prompt.md"), "utf8").replace("{{DOCUMENTATION}}", documentationText).replace("{{API}}", apiText).replace("{{API_GLOBAL_NAME}}", apiGlobalName || "");
370
431
  return createTaskPrompt;
371
432
  }
372
- const generateCode = async function(queryText, userChatHistory, createTaskPrompt, debug = false, model = "gpt-4o-mini") {
373
- if (!queryText)
374
- return { code: "", loggableCode: "" };
375
- const prompt = createTaskPrompt.replace("{{QUERY_TEXT}}", queryText);
376
- const messages = [
433
+ const runCodeSchema = zod.z.object({
434
+ code: zod.z.string()
435
+ });
436
+ const checkCodeSchema = zod.z.object({
437
+ code: zod.z.string()
438
+ });
439
+ const submitCodeSchema = zod.z.object({
440
+ code: zod.z.string()
441
+ });
442
+ const generateCode = async function(instance, queryText, userChatHistory, createTaskPrompt, debug = false, hideLogsFromAgent = false, model = "gpt-5-mini", additionalModelOptions) {
443
+ let finalSubmittedCode = "";
444
+ if (debug) {
445
+ console.log("Generating code for query:", queryText);
446
+ }
447
+ const codingTools = [
377
448
  {
378
- role: "system",
379
- content: prompt
449
+ type: "function",
450
+ function: {
451
+ name: "runCode",
452
+ // @ts-ignore
453
+ function: async ({ code }) => {
454
+ if (debug) {
455
+ console.log("Running code...", code);
456
+ }
457
+ const res = await instance.runCode(code);
458
+ if (hideLogsFromAgent) {
459
+ res.logs = [];
460
+ }
461
+ return res;
462
+ },
463
+ description: "Run the provided code in a sandboxed environment. The code will have access to the API object. Returns the result of the code execution.",
464
+ parse: zodParseJSON(runCodeSchema),
465
+ parameters: zodToJsonSchema.zodToJsonSchema(runCodeSchema)
466
+ }
467
+ },
468
+ {
469
+ type: "function",
470
+ function: {
471
+ name: "checkCode",
472
+ // @ts-ignore
473
+ function: async ({ code }) => {
474
+ if (debug) {
475
+ console.log("Checking code...", code);
476
+ }
477
+ const res = await instance.checkCode(code);
478
+ return res;
479
+ },
480
+ description: "Check the provided code for errors or issues without running it. Returns the result of the code check.",
481
+ parse: zodParseJSON(checkCodeSchema),
482
+ parameters: zodToJsonSchema.zodToJsonSchema(checkCodeSchema)
483
+ }
380
484
  },
381
- ...userChatHistory,
382
485
  {
383
- role: "user",
384
- content: `${queryText}. Respond with json`,
385
- name: "user"
486
+ type: "function",
487
+ function: {
488
+ name: "submitCode",
489
+ // @ts-ignore
490
+ function: async ({ code }) => {
491
+ finalSubmittedCode = code;
492
+ return {
493
+ success: true
494
+ };
495
+ },
496
+ description: "Submit the provided code as the final code to run. This will end the code generation process and the code will be run in the sandbox. Use this when you are confident in the code you have generated and want to run it.",
497
+ parse: zodParseJSON(submitCodeSchema),
498
+ parameters: zodToJsonSchema.zodToJsonSchema(submitCodeSchema)
499
+ }
386
500
  }
387
501
  ];
388
502
  try {
389
- const results = await openai.chat.completions.create(
390
- {
391
- model,
392
- messages,
393
- temperature: 0,
394
- max_tokens: 2e3,
395
- response_format: {
396
- json_schema: {
397
- name: "codeResponse",
398
- description: "The code generated by the AI",
399
- schema: {
400
- type: "object",
401
- properties: {
402
- yourCode: {
403
- type: "string",
404
- description: "The code generated by the AI"
405
- }
406
- }
407
- }
408
- },
409
- type: "json_schema"
503
+ const runner = openai.chat.completions.runTools({
504
+ model,
505
+ messages: [
506
+ {
507
+ role: "system",
508
+ content: "You will assist with writing code to perform the user's request."
509
+ },
510
+ ...userChatHistory,
511
+ {
512
+ role: "user",
513
+ content: createTaskPrompt.replace("{{USER_QUERY}}", queryText)
410
514
  }
515
+ ],
516
+ tools: codingTools,
517
+ ...additionalModelOptions
518
+ }).on("message", async (message) => {
519
+ if (debug) {
520
+ console.log("Message:", JSON.stringify(message, null, 2));
411
521
  }
412
- );
413
- if (debug)
414
- console.log(results.usage);
415
- const response = results.choices[0].message.content;
416
- if (debug)
417
- console.log(response);
418
- try {
419
- const json = JSON.parse(response);
420
- const code = json.yourCode.replaceAll("```", "");
421
- return { code, loggableCode: code };
422
- } catch (err) {
423
- console.error("Error parsing response", err);
424
- return { code: "", loggableCode: "" };
522
+ }).on("functionToolCall", (functionCall) => {
523
+ if (debug) {
524
+ console.log("Function Call:", JSON.stringify(functionCall, null, 2));
525
+ }
526
+ }).on("functionToolCallResult", (functionCall) => {
527
+ if (debug) {
528
+ console.log("Function Call Result:", JSON.stringify(functionCall, null, 2));
529
+ }
530
+ });
531
+ const finalContent = await runner.finalContent();
532
+ if (debug) {
533
+ console.log("Final Content:", finalContent);
425
534
  }
535
+ return {
536
+ code: finalSubmittedCode,
537
+ comments: finalContent || void 0
538
+ };
426
539
  } catch (err) {
427
540
  console.error(err);
428
- return { code: "", loggableCode: "" };
541
+ return null;
429
542
  }
430
543
  };
431
- let QuickJS = null;
432
544
  async function createWithAPI(options) {
433
- options = {
434
- apiGlobalName: "api",
435
- apiGlobals: {},
436
- apiWhitelist: Object.getOwnPropertyNames(Object.getPrototypeOf(options.apiObject)).filter((f) => f !== "constructor" && !f.startsWith("_")),
437
- debug: false,
438
- model: "gpt-4o-mini",
545
+ const localOptions = {
546
+ ...{
547
+ apiGlobalName: "api",
548
+ apiGlobals: {},
549
+ apiWhitelist: Object.getOwnPropertyNames(Object.getPrototypeOf(options.apiObject)).filter((f) => f !== "constructor" && !f.startsWith("_")),
550
+ debug: false,
551
+ model: process.env.OPENAI_API_MODEL || "gpt-5.4-mini",
552
+ additionalModelOptions: {},
553
+ hideLogsFromAgent: false
554
+ },
439
555
  ...options
440
556
  };
441
- if (!options.apiObject || !options.apiDefFilePath) {
442
- throw new Error("apiObject and apiFilePath are required");
557
+ if (!localOptions.apiObject || !localOptions.apiDefFilePath) {
558
+ throw new Error("apiObject and apiDefFilePath are required");
443
559
  }
444
- const { apiObject, apiWhitelist, apiGlobalName, apiExports, apiDefFilePath, apiDocsPath, debug, model } = options;
445
- const createTaskPrompt = createBasePrompt(apiDefFilePath, apiDocsPath);
446
- return {
447
- options,
560
+ const { apiObject, apiWhitelist, apiGlobalName, apiExports, apiDefFilePath, apiDocsPath, debug, model, additionalModelOptions, hideLogsFromAgent } = localOptions;
561
+ const createTaskPrompt = createBasePrompt(apiDefFilePath, apiGlobalName, apiDocsPath);
562
+ const instance = {
563
+ options: localOptions,
448
564
  generateCode: async function(queryText, userChatHistory, currentContext = void 0) {
449
565
  if (!queryText)
450
- return { code: "", loggableCode: "" };
451
- return await generateCode(queryText, userChatHistory, createTaskPrompt.replace("{{CONTEXT}}", currentContext ? "```" + JSON.stringify(currentContext, null, 2) + "```" : ""), debug, model);
566
+ return null;
567
+ const formattedContext = wrapCodeBlock(JSON.stringify(currentContext, null, 2), "json");
568
+ return await generateCode(
569
+ instance,
570
+ queryText,
571
+ userChatHistory,
572
+ createTaskPrompt.replace("{{CONTEXT}}", formattedContext),
573
+ debug,
574
+ hideLogsFromAgent,
575
+ model,
576
+ additionalModelOptions
577
+ );
452
578
  },
453
- runCode: async function(generatedCode) {
579
+ checkCode: async function(generatedCode) {
454
580
  if (!QuickJS) {
455
581
  if (debug)
456
582
  console.log("Creating quick.js");
457
583
  QuickJS = await quickjsEmscripten.getQuickJS();
458
584
  }
459
585
  let apiGlobals = {};
460
- for (const key in options.apiGlobals) {
461
- apiGlobals[key] = {
462
- value: options.apiGlobals[key]
586
+ if (options.apiGlobals) {
587
+ for (const key in options.apiGlobals) {
588
+ apiGlobals[key] = {
589
+ value: options.apiGlobals[key]
590
+ };
591
+ }
592
+ }
593
+ const { vm, checkCode } = await createSandbox(QuickJS, {
594
+ [apiDefFilePath]: apiExports
595
+ }, {
596
+ ...apiGlobals,
597
+ [apiGlobalName]: {
598
+ value: apiObject,
599
+ whitelist: apiWhitelist
600
+ }
601
+ }, options.debug);
602
+ try {
603
+ if (debug) {
604
+ console.log("checking Code:\n", generatedCode);
605
+ }
606
+ const res = await checkCode(wrapCode(apiGlobalName, apiDefFilePath, generatedCode));
607
+ if (!res) {
608
+ console.error("Unknown failure checking code");
609
+ return {
610
+ success: false,
611
+ logs: []
612
+ };
613
+ }
614
+ if (debug) {
615
+ console.log(res.success ? "Code check passed" : "Code check failed");
616
+ }
617
+ if (res.success) {
618
+ return {
619
+ success: true,
620
+ logs: [{ type: "log", args: ["\u2714\uFE0F Code check passed"], source: "compiler" }, ...res.logs]
621
+ };
622
+ }
623
+ return {
624
+ success: false,
625
+ logs: [{ type: "error", args: ["\u274C Code check failed"], source: "compiler" }, ...res.logs]
463
626
  };
627
+ } catch (err) {
628
+ if (debug) {
629
+ console.error("Error checking code:", err);
630
+ }
631
+ }
632
+ vm.dispose();
633
+ return {
634
+ success: false,
635
+ logs: []
636
+ };
637
+ },
638
+ runCode: async function(code) {
639
+ if (!QuickJS) {
640
+ if (debug)
641
+ console.log("Creating quick.js");
642
+ QuickJS = await quickjsEmscripten.getQuickJS();
464
643
  }
465
- const { vm, runTask, isAsyncProcessRunning } = await createSandbox(QuickJS, {
466
- [apiDefFilePath.replaceAll("\\", "")]: apiExports
644
+ let apiGlobals = {};
645
+ if (options.apiGlobals) {
646
+ for (const key in options.apiGlobals) {
647
+ apiGlobals[key] = {
648
+ value: options.apiGlobals[key]
649
+ };
650
+ }
651
+ }
652
+ const { vm, runCode: runTask, isAsyncProcessRunning } = await createSandbox(QuickJS, {
653
+ [apiDefFilePath]: apiExports
467
654
  }, {
468
655
  ...apiGlobals,
469
656
  [apiGlobalName]: {
@@ -473,11 +660,15 @@ async function createWithAPI(options) {
473
660
  }, options.debug);
474
661
  try {
475
662
  if (debug) {
476
- console.log("Running Code:\n", generatedCode);
663
+ console.log("Running Code:\n", code);
477
664
  }
478
- const res = await runTask(generatedCode.replace("'api.ts'", `'${options.apiDefFilePath}'`));
665
+ const res = await runTask(wrapCode(apiGlobalName, apiDefFilePath, code));
479
666
  if (!res) {
480
- console.error("Task unsuccessful");
667
+ console.error("Unknown failure running task");
668
+ return {
669
+ success: false,
670
+ logs: []
671
+ };
481
672
  }
482
673
  while (isAsyncProcessRunning()) {
483
674
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -485,6 +676,7 @@ async function createWithAPI(options) {
485
676
  if (debug) {
486
677
  console.log("Completed");
487
678
  }
679
+ return res;
488
680
  } catch (err) {
489
681
  if (debug) {
490
682
  console.error("Error running task:", err);
@@ -493,20 +685,39 @@ async function createWithAPI(options) {
493
685
  await new Promise((resolve) => setTimeout(resolve, 100));
494
686
  rejectOpenPromises(vm);
495
687
  vm.dispose();
688
+ return {
689
+ success: false,
690
+ logs: []
691
+ };
496
692
  },
693
+ // generates code and runs it in one step
497
694
  processRequest: async function(userQuery, currentContext = void 0) {
498
695
  if (debug) {
499
696
  console.log("Query:", userQuery);
500
697
  }
501
698
  console.time("Generate Task");
502
- const generatedCode = await generateCode(userQuery, [], createTaskPrompt.replace("{{CONTEXT}}", currentContext ? "```" + JSON.stringify(currentContext, null, 2) + "```" : ""));
699
+ const generatedCode = await generateCode(
700
+ instance,
701
+ userQuery,
702
+ [],
703
+ createTaskPrompt.replace("{{CONTEXT}}", currentContext ? JSON.stringify(currentContext, null, 2) : ""),
704
+ debug,
705
+ hideLogsFromAgent,
706
+ model,
707
+ additionalModelOptions
708
+ );
503
709
  console.timeEnd("Generate Task");
504
710
  console.time("Run Code");
505
- await this.runCode(generatedCode.code.replace("'api.ts'", `'${options.apiDefFilePath}'`));
711
+ if (generatedCode?.code) {
712
+ await instance.runCode(generatedCode.code);
713
+ } else {
714
+ console.error("No code generated", generatedCode);
715
+ }
506
716
  console.timeEnd("Run Code");
507
717
  return generatedCode;
508
718
  }
509
719
  };
720
+ return instance;
510
721
  }
511
722
  const AIMyAPI = {
512
723
  // Standard way of using the library
@@ -520,3 +731,4 @@ const AIMyAPI = {
520
731
  exports.AIMyAPI = AIMyAPI;
521
732
  exports.createBasePrompt = createBasePrompt;
522
733
  exports.generateCode = generateCode;
734
+ exports.zodParseJSON = zodParseJSON;