aimyapi 1.0.7 → 1.0.8

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 WhiteNoise
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 WhiteNoise
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,195 +1,195 @@
1
- # AI My API
2
-
3
- AIMyAPI is a typescript code sandbox for your LLM using [QuickJS](https://github.com/justjake/quickjs-emscripten). It allows you to expose a global API object and allows your agent to write functions against it without giving it access to the internet or your filesystem except through the methods you define.
4
-
5
- Requires an OpenAI compatible API
6
-
7
- AIMyAPI will handle async operations and timers in your API object.
8
-
9
- ## About version 1.0 update and motivation
10
-
11
- AIMyAPI was originally written at a time (circa ChatGPT 3.5 / GPT-4) before tool calling existed, before Claude code and codex and just before OpenAI released their code interpreter. LLM models have also gotten drastically better since. Some of the problems it originally aimed to solve are better solved today with simple tool calling. However, there is still a need to safely run small programs in a sandboxed way, reliable way. Tools are good if you have a set interface but there may be times that an agent needs to do some sorting or filtering, or an algorithm or analysis, or some repeatable workflow which isn't implemented by the available tool functions. Writing code allows the agent to do things it might not otherwise be able to do with tools alone. All the AI models are thoroughly trained to write code do solve all kinds of problems. Having the ability to write code allows the tools API surface to be smaller; it could simply be a set of CRUD operations.
12
-
13
- The other advantage of writing code is privacy and security. When an agent executes code in a sandbox, it doesn't see any of the results*, API keys or other things you may want to keep hidden. When an agent uses tools, it sees all the parameters and results of the tool calls. Code can be audited for security and correctness before running it. Finally, code can encompass a series of operations, whereas tool calls need to be approved one at a time or just trusted, often without knowing what will come next.
14
-
15
- Version 1.0 updates AIMyAPI code generation to use the tool calling process for writing code. This allows the agent to check the code and run some tests or API calls before committing to a final version. This should result in better code since the previous model relied on the agent always being able to one-shot the code, which depended on it having a complete view of the problem before writing the code which wasn't always realistic. We could also update this process a bit to add "Plan" / "Build" / "Test" phases instead of just one code generation prompt.
16
-
17
- *You'll have the option to hide logs and errors from the agent - logging can allow the agent to fix it's own coding mistakes and test the code, but it also allows information to leave the sandbox which you may want to limit depending on the sensitivity the data your api is handling. Another approach would be to implement a mocked version of your API that the agent can use to write code and debug against while keeping your real data hidden and safe from potentially destructive operations.
18
-
19
- ## Quickstart
20
-
21
- Install the package
22
-
23
- ```bash
24
- npm i aimyapi --save
25
- ```
26
-
27
- **Setup:** create a .env file in the root of your project and add your OpenAI api key:
28
- ```bash
29
- OPENAI_API_KEY=XXXX
30
- OPENAI_API_BASE_URL=XXXX // If using your own server, otherwise omit to use the default
31
- OPENAI_API_MODEL=XXXX // defaults to "gpt-5-mini"
32
- ```
33
-
34
-
35
- Simply define your strongly-typed typescript API:
36
-
37
- `sales_api.ts`
38
- ```typescript
39
- // Sales data for each month, per business unit
40
- export interface SalesData {
41
- sales: number;
42
- month: number; // 1...12
43
- year: number;
44
- businessUnit: "Advertising" | "Hardware" | "Software";
45
- }
46
-
47
- export interface APIInterface {
48
- getSalesData(): SalesData[];
49
- // Returns an array of sales data.
50
-
51
- print(text: string): void;
52
- // Prints the specified text to the user.
53
- };
54
-
55
- // Global object you will use for accessing the APIInterface. Any other globals can go here too if you declare them in the `apiGlobals` parameter.
56
- declare global {
57
- const api: APIInterface;
58
- }
59
-
60
- ```
61
-
62
- Implement it (`sales_api_impl.ts`):
63
- ```typescript
64
- export class API implements APIInterface {
65
-
66
- getSalesData() {
67
- ...
68
- ```
69
-
70
- Document it (`sales_api.md`):
71
-
72
- ```
73
- Examples of how you should respond:
74
-
75
- user: How are you today?
76
-
77
- assistant:
78
-
79
- api.print("I'm great, thanks for asking!");
80
- ```
81
-
82
- Instantiate your api and AIMyAPI. AIMyAPI will wrap your API so that it can be called from within the QuickJS sandbox.
83
-
84
- ```typescript
85
- import { AIMyAPI } from "aimyapi"
86
-
87
- const api = new API();
88
-
89
- const aimyapi = await AIMyAPI.createWithAPI({
90
- apiObject: api,
91
- apiExports: APIExports,
92
- apiDefFilePath: "./sales_api.ts",
93
- apiDocsPath: "./sales_api.md",
94
- })
95
- ```
96
-
97
- And now you can make requests against the API in natural language.
98
- Any code generated by the LLM will be run within the sandbox. The LLM will ONLY have access to functions provided within your API and cannot use javascript to make http requests etc.
99
-
100
- ```typescript
101
- await aimyapi.processRequest("Which business unit had the highest sales in 2020?");
102
- ```
103
-
104
- This would produce the following code generated by the LLM prompt, which is run in the sandbox and makes calls on your API object:
105
-
106
- ```typescript
107
-
108
- let salesData = api.getSalesData();
109
- let maxSales = 0;
110
- let businessUnitWithMaxSales = "";
111
- for (let i = 0; i < salesData.length; i++) {
112
- if (salesData[i].year === 2020 && salesData[i].sales > maxSales) {
113
- maxSales = salesData[i].sales;
114
- businessUnitWithMaxSales = salesData[i].businessUnit;
115
- }
116
- }
117
- api.print(\`The business unit with the highest sales in 2020 was ${businessUnitWithMaxSales}.\`);
118
-
119
- ```
120
-
121
- This example does not take advantage of the chat history and treats every request as a fresh chat. See `example2` for a more complex example that uses the chat history.
122
-
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
-
125
-
126
- ## API Reference for AIMyAPI
127
-
128
- ### createWithAPI(options: AIMyAPIOptions): Promise<CreateWithAPIExports>
129
-
130
- Primary function you will use to create a module for working your API API. For a simple example with no chat history, see example1. For an example with chat history see example2.
131
-
132
- #### Options:
133
-
134
- ```typescript
135
- export interface AIMyAPIOptions {
136
- apiObject: object;
137
- apiExports: object;
138
- apiWhitelist?: string[];
139
- apiGlobals?: object;
140
- apiDefFilePath: string;
141
- apiGlobalName?: string;
142
- apiDocsPath?: string;
143
- debug?: boolean;
144
- model?: string;
145
- additionalModelOptions?: object;
146
- hideLogsFromAgent?: boolean;
147
- };
148
- ```
149
-
150
- - `apiObject`: An object representing the API to be used.
151
- - `apiExports`: An object representing the exported functions and variables from the API.
152
- - `apiGlobals` (optional): An object representing global variables for the API.
153
- - `apiDefFilePath`: A string representing the full path to the API definition file.
154
- - `apiGlobalName` (optional): A string representing the name of the global variable for the API.
155
- - `apiDocsPath` (optional): A string representing the path to the documentation for the API.
156
- - `debug` (optional): A boolean indicating whether to output debugging information.
157
-
158
- ### The AIMyAPI module (type: CreateWithAPIExports)
159
-
160
- The module created by the `createWithAPI` function:
161
-
162
- #### Methods
163
-
164
- ```typescript
165
- export interface AIMyAPIInstance {
166
- options:AIMyAPIOptions;
167
- generateCode: (queryText:string, userChatHistory:ChatCompletionMessageParam[]) => Promise<GenerateCodeResult | null>;
168
- runCode: (code:string) => Promise<CodeRunResult>;
169
- checkCode: (code:string) => Promise<CodeRunResult>;
170
- // run a single request with no history
171
- processRequest: (userQuery:string, context?: object) => Promise<GenerateCodeResult | null>;
172
- }
173
- ```
174
-
175
- - `generateCode()`: A function that generates code to be run in the sandbox for the given query text and user chat history.
176
- - `runCode()`: A function that runs the generated code in the sandbox.
177
- - `checkCode()`: Typecheck / compile the code
178
- - `processRequest()`: A single function that generates and runs code assuming no chat history.
179
-
180
-
181
- ## Safety notes:
182
-
183
- - If the user is giving sensitive information in a query, that information isn't private. Refer to the OpenAI policies and plan accordingly.
184
- - The LLM will do stupid things with your API. You should idiot proof your functions, make sure you sanity check any inputs, etc.
185
- - Even though we are running the code in sandbox, you should still treat anything coming back from the LLM as untrusted code.
186
- - You should never give the LLM access to anything mission critical through your API such as live production data or services.
187
- - Give as few permissions as is necessary - read only access would be preferred.
188
- - Users can trick the LLM to do things that you may not want or intend. Again, access controls are critical. You shouldn't let users access anything they wouldn't normally be able to access via your API.
189
- - Any 'dangerous' operations should involve some kind of human oversight and approval.
190
- - A way to audit and rollback anything the LLM does would be wise. The LLM can misunderstand requests due to false assumptions or incomplete information (or trickery). If you allow it to modify your data it could mess things up.
191
- - By using this library you acknowledge that you take full responsibility for anything that happens as a result of it's use. We cannot gaurantee what will happen based on your instructions to the LLM or how the LLM interprets them etc.
192
-
193
-
194
-
195
-
1
+ # AI My API
2
+
3
+ AIMyAPI is a typescript code sandbox for your LLM using [QuickJS](https://github.com/justjake/quickjs-emscripten). It allows you to expose a global API object and allows your agent to write functions against it without giving it access to the internet or your filesystem except through the methods you define.
4
+
5
+ Requires an OpenAI compatible API
6
+
7
+ AIMyAPI will handle async operations and timers in your API object.
8
+
9
+ ## About version 1.0 update and motivation
10
+
11
+ AIMyAPI was originally written at a time (circa ChatGPT 3.5 / GPT-4) before tool calling existed, before Claude code and codex and just before OpenAI released their code interpreter. LLM models have also gotten drastically better since. Some of the problems it originally aimed to solve are better solved today with simple tool calling. However, there is still a need to safely run small programs in a sandboxed way, reliable way. Tools are good if you have a set interface but there may be times that an agent needs to do some sorting or filtering, or an algorithm or analysis, or some repeatable workflow which isn't implemented by the available tool functions. Writing code allows the agent to do things it might not otherwise be able to do with tools alone. All the AI models are thoroughly trained to write code do solve all kinds of problems. Having the ability to write code allows the tools API surface to be smaller; it could simply be a set of CRUD operations.
12
+
13
+ The other advantage of writing code is privacy and security. When an agent executes code in a sandbox, it doesn't see any of the results*, API keys or other things you may want to keep hidden. When an agent uses tools, it sees all the parameters and results of the tool calls. Code can be audited for security and correctness before running it. Finally, code can encompass a series of operations, whereas tool calls need to be approved one at a time or just trusted, often without knowing what will come next.
14
+
15
+ Version 1.0 updates AIMyAPI code generation to use the tool calling process for writing code. This allows the agent to check the code and run some tests or API calls before committing to a final version. This should result in better code since the previous model relied on the agent always being able to one-shot the code, which depended on it having a complete view of the problem before writing the code which wasn't always realistic. We could also update this process a bit to add "Plan" / "Build" / "Test" phases instead of just one code generation prompt.
16
+
17
+ *You'll have the option to hide logs and errors from the agent - logging can allow the agent to fix it's own coding mistakes and test the code, but it also allows information to leave the sandbox which you may want to limit depending on the sensitivity the data your api is handling. Another approach would be to implement a mocked version of your API that the agent can use to write code and debug against while keeping your real data hidden and safe from potentially destructive operations.
18
+
19
+ ## Quickstart
20
+
21
+ Install the package
22
+
23
+ ```bash
24
+ npm i aimyapi --save
25
+ ```
26
+
27
+ **Setup:** create a .env file in the root of your project and add your OpenAI api key:
28
+ ```bash
29
+ OPENAI_API_KEY=XXXX
30
+ OPENAI_API_BASE_URL=XXXX // If using your own server, otherwise omit to use the default
31
+ OPENAI_API_MODEL=XXXX // defaults to "gpt-5-mini"
32
+ ```
33
+
34
+
35
+ Simply define your strongly-typed typescript API:
36
+
37
+ `sales_api.ts`
38
+ ```typescript
39
+ // Sales data for each month, per business unit
40
+ export interface SalesData {
41
+ sales: number;
42
+ month: number; // 1...12
43
+ year: number;
44
+ businessUnit: "Advertising" | "Hardware" | "Software";
45
+ }
46
+
47
+ export interface APIInterface {
48
+ getSalesData(): SalesData[];
49
+ // Returns an array of sales data.
50
+
51
+ print(text: string): void;
52
+ // Prints the specified text to the user.
53
+ };
54
+
55
+ // Global object you will use for accessing the APIInterface. Any other globals can go here too if you declare them in the `apiGlobals` parameter.
56
+ declare global {
57
+ const api: APIInterface;
58
+ }
59
+
60
+ ```
61
+
62
+ Implement it (`sales_api_impl.ts`):
63
+ ```typescript
64
+ export class API implements APIInterface {
65
+
66
+ getSalesData() {
67
+ ...
68
+ ```
69
+
70
+ Document it (`sales_api.md`):
71
+
72
+ ```
73
+ Examples of how you should respond:
74
+
75
+ user: How are you today?
76
+
77
+ assistant:
78
+
79
+ api.print("I'm great, thanks for asking!");
80
+ ```
81
+
82
+ Instantiate your api and AIMyAPI. AIMyAPI will wrap your API so that it can be called from within the QuickJS sandbox.
83
+
84
+ ```typescript
85
+ import { AIMyAPI } from "aimyapi"
86
+
87
+ const api = new API();
88
+
89
+ const aimyapi = await AIMyAPI.createWithAPI({
90
+ apiObject: api,
91
+ apiExports: APIExports,
92
+ apiDefFilePath: "./sales_api.ts",
93
+ apiDocsPath: "./sales_api.md",
94
+ })
95
+ ```
96
+
97
+ And now you can make requests against the API in natural language.
98
+ Any code generated by the LLM will be run within the sandbox. The LLM will ONLY have access to functions provided within your API and cannot use javascript to make http requests etc.
99
+
100
+ ```typescript
101
+ await aimyapi.processRequest("Which business unit had the highest sales in 2020?");
102
+ ```
103
+
104
+ This would produce the following code generated by the LLM prompt, which is run in the sandbox and makes calls on your API object:
105
+
106
+ ```typescript
107
+
108
+ let salesData = api.getSalesData();
109
+ let maxSales = 0;
110
+ let businessUnitWithMaxSales = "";
111
+ for (let i = 0; i < salesData.length; i++) {
112
+ if (salesData[i].year === 2020 && salesData[i].sales > maxSales) {
113
+ maxSales = salesData[i].sales;
114
+ businessUnitWithMaxSales = salesData[i].businessUnit;
115
+ }
116
+ }
117
+ api.print(\`The business unit with the highest sales in 2020 was ${businessUnitWithMaxSales}.\`);
118
+
119
+ ```
120
+
121
+ This example does not take advantage of the chat history and treats every request as a fresh chat. See `example2` for a more complex example that uses the chat history.
122
+
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
+
125
+
126
+ ## API Reference for AIMyAPI
127
+
128
+ ### createWithAPI(options: AIMyAPIOptions): Promise<CreateWithAPIExports>
129
+
130
+ Primary function you will use to create a module for working your API API. For a simple example with no chat history, see example1. For an example with chat history see example2.
131
+
132
+ #### Options:
133
+
134
+ ```typescript
135
+ export interface AIMyAPIOptions {
136
+ apiObject: object;
137
+ apiExports: object;
138
+ apiWhitelist?: string[];
139
+ apiGlobals?: object;
140
+ apiDefFilePath: string;
141
+ apiGlobalName?: string;
142
+ apiDocsPath?: string;
143
+ debug?: boolean;
144
+ model?: string;
145
+ additionalModelOptions?: object;
146
+ hideLogsFromAgent?: boolean;
147
+ };
148
+ ```
149
+
150
+ - `apiObject`: An object representing the API to be used.
151
+ - `apiExports`: An object representing the exported functions and variables from the API.
152
+ - `apiGlobals` (optional): An object representing global variables for the API.
153
+ - `apiDefFilePath`: A string representing the full path to the API definition file.
154
+ - `apiGlobalName` (optional): A string representing the name of the global variable for the API.
155
+ - `apiDocsPath` (optional): A string representing the path to the documentation for the API.
156
+ - `debug` (optional): A boolean indicating whether to output debugging information.
157
+
158
+ ### The AIMyAPI module (type: CreateWithAPIExports)
159
+
160
+ The module created by the `createWithAPI` function:
161
+
162
+ #### Methods
163
+
164
+ ```typescript
165
+ export interface AIMyAPIInstance {
166
+ options:AIMyAPIOptions;
167
+ generateCode: (queryText:string, userChatHistory:ChatCompletionMessageParam[]) => Promise<GenerateCodeResult | null>;
168
+ runCode: (code:string) => Promise<CodeRunResult>;
169
+ checkCode: (code:string) => Promise<CodeRunResult>;
170
+ // run a single request with no history
171
+ processRequest: (userQuery:string, context?: object) => Promise<GenerateCodeResult | null>;
172
+ }
173
+ ```
174
+
175
+ - `generateCode()`: A function that generates code to be run in the sandbox for the given query text and user chat history.
176
+ - `runCode()`: A function that runs the generated code in the sandbox.
177
+ - `checkCode()`: Typecheck / compile the code
178
+ - `processRequest()`: A single function that generates and runs code assuming no chat history.
179
+
180
+
181
+ ## Safety notes:
182
+
183
+ - If the user is giving sensitive information in a query, that information isn't private. Refer to the OpenAI policies and plan accordingly.
184
+ - The LLM will do stupid things with your API. You should idiot proof your functions, make sure you sanity check any inputs, etc.
185
+ - Even though we are running the code in sandbox, you should still treat anything coming back from the LLM as untrusted code.
186
+ - You should never give the LLM access to anything mission critical through your API such as live production data or services.
187
+ - Give as few permissions as is necessary - read only access would be preferred.
188
+ - Users can trick the LLM to do things that you may not want or intend. Again, access controls are critical. You shouldn't let users access anything they wouldn't normally be able to access via your API.
189
+ - Any 'dangerous' operations should involve some kind of human oversight and approval.
190
+ - A way to audit and rollback anything the LLM does would be wise. The LLM can misunderstand requests due to false assumptions or incomplete information (or trickery). If you allow it to modify your data it could mess things up.
191
+ - By using this library you acknowledge that you take full responsibility for anything that happens as a result of it's use. We cannot gaurantee what will happen based on your instructions to the LLM or how the LLM interprets them etc.
192
+
193
+
194
+
195
+
package/dist/index.cjs CHANGED
@@ -438,11 +438,12 @@ const checkCodeSchema = zod.z.object({
438
438
  const submitCodeSchema = zod.z.object({
439
439
  code: zod.z.string()
440
440
  });
441
- const generateCode = async function(instance, queryText, userChatHistory, createTaskPrompt, debug = false, hideLogsFromAgent = false, model = "gpt-5-mini", additionalModelOptions) {
441
+ const generateCode = async function(instance, queryText, userChatHistory, createTaskPrompt, debug = false, hideLogsFromAgent = false, model = "gpt-5-mini", additionalModelOptions, attempt = 0) {
442
442
  let finalSubmittedCode = "";
443
443
  if (debug) {
444
444
  console.log("Generating code for query:", queryText);
445
445
  }
446
+ const newHistoryItems = [];
446
447
  const codingTools = [
447
448
  {
448
449
  type: "function",
@@ -487,6 +488,14 @@ const generateCode = async function(instance, queryText, userChatHistory, create
487
488
  name: "submitCode",
488
489
  // @ts-ignore
489
490
  function: async ({ code }) => {
491
+ const checkRes = await instance.checkCode(code);
492
+ if (!checkRes.success) {
493
+ return {
494
+ success: false,
495
+ reason: "Code check failed",
496
+ logs: checkRes.logs
497
+ };
498
+ }
490
499
  finalSubmittedCode = code;
491
500
  return {
492
501
  success: true
@@ -509,7 +518,7 @@ const generateCode = async function(instance, queryText, userChatHistory, create
509
518
  ...userChatHistory,
510
519
  {
511
520
  role: "user",
512
- content: createTaskPrompt.replace("{{USER_QUERY}}", queryText)
521
+ content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText
513
522
  }
514
523
  ],
515
524
  tools: codingTools,
@@ -518,6 +527,7 @@ const generateCode = async function(instance, queryText, userChatHistory, create
518
527
  if (debug) {
519
528
  console.log("Message:", JSON.stringify(message, null, 2));
520
529
  }
530
+ newHistoryItems.push(message);
521
531
  }).on("functionToolCall", (functionCall) => {
522
532
  if (debug) {
523
533
  console.log("Function Call:", JSON.stringify(functionCall, null, 2));
@@ -529,15 +539,37 @@ const generateCode = async function(instance, queryText, userChatHistory, create
529
539
  });
530
540
  const finalContent = await runner.finalContent();
531
541
  if (debug) {
532
- console.log("Final Content:", finalContent);
542
+ console.log("Generate code final response:", finalContent);
543
+ }
544
+ if (!finalSubmittedCode) {
545
+ console.error("No code was submitted by the agent.");
546
+ if (attempt < 3) {
547
+ return generateCode(instance, `You must call submitCode with valid code before proceeding. Attempt ${attempt + 1} of 3.`, [
548
+ ...userChatHistory,
549
+ {
550
+ role: "user",
551
+ content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText
552
+ },
553
+ ...newHistoryItems
554
+ ], createTaskPrompt, debug, hideLogsFromAgent, model, additionalModelOptions, attempt + 1);
555
+ } else {
556
+ return {
557
+ success: false,
558
+ comments: "No code was submitted by the agent after multiple attempts. Final comment: " + finalContent
559
+ };
560
+ }
533
561
  }
534
562
  return {
563
+ success: true,
535
564
  code: finalSubmittedCode,
536
565
  comments: finalContent || void 0
537
566
  };
538
567
  } catch (err) {
539
568
  console.error(err);
540
- return null;
569
+ return {
570
+ success: false,
571
+ comments: `Error generating code: ${err}`
572
+ };
541
573
  }
542
574
  };
543
575
  async function createWithAPI(options) {
package/dist/index.d.cts CHANGED
@@ -40,12 +40,13 @@ interface AIMyAPIInstance {
40
40
  processRequest: (userQuery: string, context?: object) => Promise<GenerateCodeResult | null>;
41
41
  }
42
42
  interface GenerateCodeResult {
43
+ success: boolean;
43
44
  code?: string;
44
45
  comments?: string;
45
46
  }
46
47
  declare function zodParseJSON<T>(schema: ZodSchema<T>): (input: string) => T;
47
48
  declare function createBasePrompt(apiFilePath: string, apiGlobalName: string, documentationPath?: string): string;
48
- declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object) => Promise<GenerateCodeResult | null>;
49
+ declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object, attempt?: number) => Promise<GenerateCodeResult | null>;
49
50
  interface AIMyAPIModuleExports {
50
51
  createWithAPI: (options: AIMyAPIOptions) => Promise<AIMyAPIInstance>;
51
52
  createBasePrompt: (apiFilePath: string, documentationPath: string) => string;
package/dist/index.d.mts CHANGED
@@ -40,12 +40,13 @@ interface AIMyAPIInstance {
40
40
  processRequest: (userQuery: string, context?: object) => Promise<GenerateCodeResult | null>;
41
41
  }
42
42
  interface GenerateCodeResult {
43
+ success: boolean;
43
44
  code?: string;
44
45
  comments?: string;
45
46
  }
46
47
  declare function zodParseJSON<T>(schema: ZodSchema<T>): (input: string) => T;
47
48
  declare function createBasePrompt(apiFilePath: string, apiGlobalName: string, documentationPath?: string): string;
48
- declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object) => Promise<GenerateCodeResult | null>;
49
+ declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object, attempt?: number) => Promise<GenerateCodeResult | null>;
49
50
  interface AIMyAPIModuleExports {
50
51
  createWithAPI: (options: AIMyAPIOptions) => Promise<AIMyAPIInstance>;
51
52
  createBasePrompt: (apiFilePath: string, documentationPath: string) => string;
package/dist/index.d.ts CHANGED
@@ -40,12 +40,13 @@ interface AIMyAPIInstance {
40
40
  processRequest: (userQuery: string, context?: object) => Promise<GenerateCodeResult | null>;
41
41
  }
42
42
  interface GenerateCodeResult {
43
+ success: boolean;
43
44
  code?: string;
44
45
  comments?: string;
45
46
  }
46
47
  declare function zodParseJSON<T>(schema: ZodSchema<T>): (input: string) => T;
47
48
  declare function createBasePrompt(apiFilePath: string, apiGlobalName: string, documentationPath?: string): string;
48
- declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object) => Promise<GenerateCodeResult | null>;
49
+ declare const generateCode: (instance: AIMyAPIInstance, queryText: string, userChatHistory: ChatCompletionMessageParam[], createTaskPrompt: string, debug?: boolean, hideLogsFromAgent?: boolean, model?: string, additionalModelOptions?: object, attempt?: number) => Promise<GenerateCodeResult | null>;
49
50
  interface AIMyAPIModuleExports {
50
51
  createWithAPI: (options: AIMyAPIOptions) => Promise<AIMyAPIInstance>;
51
52
  createBasePrompt: (apiFilePath: string, documentationPath: string) => string;
package/dist/index.js CHANGED
@@ -438,11 +438,12 @@ const checkCodeSchema = zod.z.object({
438
438
  const submitCodeSchema = zod.z.object({
439
439
  code: zod.z.string()
440
440
  });
441
- const generateCode = async function(instance, queryText, userChatHistory, createTaskPrompt, debug = false, hideLogsFromAgent = false, model = "gpt-5-mini", additionalModelOptions) {
441
+ const generateCode = async function(instance, queryText, userChatHistory, createTaskPrompt, debug = false, hideLogsFromAgent = false, model = "gpt-5-mini", additionalModelOptions, attempt = 0) {
442
442
  let finalSubmittedCode = "";
443
443
  if (debug) {
444
444
  console.log("Generating code for query:", queryText);
445
445
  }
446
+ const newHistoryItems = [];
446
447
  const codingTools = [
447
448
  {
448
449
  type: "function",
@@ -487,6 +488,14 @@ const generateCode = async function(instance, queryText, userChatHistory, create
487
488
  name: "submitCode",
488
489
  // @ts-ignore
489
490
  function: async ({ code }) => {
491
+ const checkRes = await instance.checkCode(code);
492
+ if (!checkRes.success) {
493
+ return {
494
+ success: false,
495
+ reason: "Code check failed",
496
+ logs: checkRes.logs
497
+ };
498
+ }
490
499
  finalSubmittedCode = code;
491
500
  return {
492
501
  success: true
@@ -509,7 +518,7 @@ const generateCode = async function(instance, queryText, userChatHistory, create
509
518
  ...userChatHistory,
510
519
  {
511
520
  role: "user",
512
- content: createTaskPrompt.replace("{{USER_QUERY}}", queryText)
521
+ content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText
513
522
  }
514
523
  ],
515
524
  tools: codingTools,
@@ -518,6 +527,7 @@ const generateCode = async function(instance, queryText, userChatHistory, create
518
527
  if (debug) {
519
528
  console.log("Message:", JSON.stringify(message, null, 2));
520
529
  }
530
+ newHistoryItems.push(message);
521
531
  }).on("functionToolCall", (functionCall) => {
522
532
  if (debug) {
523
533
  console.log("Function Call:", JSON.stringify(functionCall, null, 2));
@@ -529,15 +539,37 @@ const generateCode = async function(instance, queryText, userChatHistory, create
529
539
  });
530
540
  const finalContent = await runner.finalContent();
531
541
  if (debug) {
532
- console.log("Final Content:", finalContent);
542
+ console.log("Generate code final response:", finalContent);
543
+ }
544
+ if (!finalSubmittedCode) {
545
+ console.error("No code was submitted by the agent.");
546
+ if (attempt < 3) {
547
+ return generateCode(instance, `You must call submitCode with valid code before proceeding. Attempt ${attempt + 1} of 3.`, [
548
+ ...userChatHistory,
549
+ {
550
+ role: "user",
551
+ content: attempt == 0 ? createTaskPrompt.replace("{{USER_QUERY}}", queryText) : queryText
552
+ },
553
+ ...newHistoryItems
554
+ ], createTaskPrompt, debug, hideLogsFromAgent, model, additionalModelOptions, attempt + 1);
555
+ } else {
556
+ return {
557
+ success: false,
558
+ comments: "No code was submitted by the agent after multiple attempts. Final comment: " + finalContent
559
+ };
560
+ }
533
561
  }
534
562
  return {
563
+ success: true,
535
564
  code: finalSubmittedCode,
536
565
  comments: finalContent || void 0
537
566
  };
538
567
  } catch (err) {
539
568
  console.error(err);
540
- return null;
569
+ return {
570
+ success: false,
571
+ comments: `Error generating code: ${err}`
572
+ };
541
573
  }
542
574
  };
543
575
  async function createWithAPI(options) {