diff --git a/cli2.js b/cli2.js
index 75645c2..fd75d77 100644
--- a/cli2.js
+++ b/cli2.js
@@ -1,12 +1,19 @@
import ModelDialog from './modelDialog.js';
+import chalk from 'chalk';
+
+
+
+
+
+
const modelDialog = new ModelDialog();
modelDialog.on('outputUpdate', (output) => {
- console.log('output',output);
+ console.log(chalk.blue('output event'),output);
});
modelDialog.on('reasoningUpdate', (output) => {
- console.log('reasoning',output);
+ console.log(chalk.blue('reasoning event'),output);
});
@@ -15,7 +22,9 @@ modelDialog.on('reasoningUpdate', (output) => {
(async ()=>{
const output = await modelDialog.interrogate('Can you remember "seven" ?');
- console.log(output.output,output.reasoning);
- const output2 = await modelDialog.interrogate('And what comes after that?');
- console.log(output2.output,output2.reasoning,output2.inputTokens,output2.cachedTokens,output2.outputTokens);
+ console.log(output.output,JSON.stringify(output.reasoning,null,2));
+ const output2 = await modelDialog.interrogate('read a file that is what you remebered plus 1 as a word with txt ending, check that file.');
+ console.log('final output:',output2.output);
+ console.log('reasoning:',output2.reasoning);
+ console.log('Tokens:',output2.inputTokens,output2.cachedTokens,output2.outputTokens);
})()
diff --git a/modelDialog.js b/modelDialog.js
index 3c4a7f2..51b6bb1 100644
--- a/modelDialog.js
+++ b/modelDialog.js
@@ -4,6 +4,7 @@ import EventEmitter from 'events';
import path from 'path';
import fs from 'fs/promises';
import { fileURLToPath } from 'node:url';
+import chalk from 'chalk';
async function loadTools() {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -43,6 +44,7 @@ if (!Array.fromAsync) {
class ModelDialog {
constructor() {
this.messages = [systemprompt];
+ this.messagesSent = [];
this.isActive = false;
this.currentStream = null;
this.previousResponseId = null;
@@ -79,54 +81,99 @@ class ModelDialog {
this.messages.push({"role": "user", "content": [ {"type": "input_text","text": prompt }]});
- const call = {
- model: 'gpt-5-nano',
- input: structuredClone(this.messages),
- text: { format: { type: 'text' }, verbosity: 'low' },
- reasoning: { effort: 'medium', summary: 'detailed' },
- tools: Object.values(toolsByFile).map(t => t.def),
- store: true,
- previous_response_id: this.previousResponseId
- }
+ const outputs = [];
- this.currentStream = await openai.responses.stream(call);
- this.currentStream.on('response.created', (event) => {
- this.previousResponseId = event.response.id;
- });
+ do{
+ const messagesToSend = this.messages.splice(0);
+ this.messagesSent.push(...messagesToSend);
- const deltas = [];
- this.currentStream.on('response.output_text.delta', (event) => {
- deltas.push(event.delta);
- this.emitter.emit('outputUpdate', deltas.join(''));
- });
+ const call = {
+ model: 'gpt-5-nano',
+ input: messagesToSend,
+ text: { format: { type: 'text' }, verbosity: 'low' },
+ reasoning: { effort: 'medium', summary: 'detailed' },
+ tools: Object.values(toolsByFile).map(t => t.def),
+ store: true,
+ previous_response_id: this.previousResponseId
+ }
- const reasoningDeltas = [];
- this.currentStream.on('response.reasoning_summary_text.delta', (event) => {
- if(!reasoningDeltas[event.summary_index]) reasoningDeltas[event.summary_index] = [];
- reasoningDeltas[event.summary_index].push(event.delta);
- this.emitter.emit('reasoningUpdate', reasoningDeltas[event.summary_index].join(''));
- });
+ this.currentStream = openai.responses.stream(call);
+ this.currentStream.on('response.created', (event) => {
+ this.previousResponseId = event.response.id;
+ });
- this.currentStream.on('response.reasoning_summary_text.done', (event) => {
- //console.log(event);
- });
+ const deltas = [];
+ this.currentStream.on('response.output_text.delta', (event) => {
+ deltas.push(event.delta);
+ this.emitter.emit('outputUpdate', deltas.join(''));
+ });
+
+ const reasoningDeltas = [];
+ this.currentStream.on('response.reasoning_summary_text.delta', (event) => {
+ if(!reasoningDeltas[event.summary_index]) reasoningDeltas[event.summary_index] = [];
+ reasoningDeltas[event.summary_index].push(event.delta);
+ this.emitter.emit('reasoningUpdate', reasoningDeltas[event.summary_index].join(''));
+ });
+
+ this.currentStream.on('response.reasoning_summary_text.done', (event) => {
+ //console.log(event);
+ });
+ this.currentStream.on('response.function_call_arguments.delta', (event) => {
+ process.stdout.write(chalk.yellow(event.delta));
+ });
+ this.currentStream.on('response.function_call_arguments.done', (event) => {
+ process.stdout.write("\n");
+ });
+ this.currentStream.on('response.completed', async (event) => {
+ this.handleUsage(event.response.usage, event.response.model);
+ outputs.push(...event.response.output);
- let output;
- this.currentStream.on('response.completed', async (event) => {
- this.handleUsage(event.response.usage, event.response.model);
- output = event.response.output;
- });
+ for(const toolCall of event.response.output.filter(i => i.type === 'function_call')){
+ // Limit the 'arguments' field to 400 characters for logging
+ const limitedArgs = typeof toolCall.arguments === 'string'
+ ? (toolCall.arguments.length > 400 ? toolCall.arguments.slice(0, 400) + '...[truncated]' : toolCall.arguments)
+ : toolCall.arguments;
+ console.log(
+ chalk.green('tool call:'),
+ { ...toolCall, arguments: limitedArgs }
+ );
+ const tool = toolsByFile[toolCall.name];
+ let args;
+ try{
+ args = JSON.parse(toolCall.arguments);
+ } catch(e){
+ console.error(chalk.red('Error parsing arguments:'), e, toolCall.arguments);
+ this.messages.push({
+ type: "function_call_output",
+ call_id: toolCall.call_id,
+ output: {error: 'Exception in parsing arguments', exception: e},
+ });
+ continue;
+ }
+ const result = await tool.run(args);
+ console.log(chalk.green('function call result:'),'',toolCall.name,'\n','',args,'\n','',result,'');
+ this.messages.push({
+ type: "function_call_output",
+ call_id: toolCall.call_id,
+ output: JSON.stringify(result),
+ });
+ }
+ });
+
+ await Array.fromAsync(this.currentStream);
+
+ console.log(chalk.green('Do we need to loop? messages in array = '),this.messages.length)
+ } while(this.messages.length > 0);
- await Array.fromAsync(this.currentStream);
this.isActive = false;
- const now = Date.now();
- this.lastDebouncedUpdate = now;
+ this.lastDebouncedUpdate = 0;
return {
- output: output.filter(i => i.type === 'message').map(i => i.content[0].text).join('\n') ,
- reasoning: reasoningDeltas.map(i => i.join('')),
- inputTokens: this.inputTokens, outputTokens: this.outputTokens, cachedTokens: this.cachedTokens};
+ output: outputs.filter(i => i.type === 'message').map(i => i.content[0].text) ,
+ reasoning: outputs.filter(i => i.type === 'reasoning').map(i => i.summary.map(j => j.text).join('\n')),
+ inputTokens: this.inputTokens, outputTokens: this.outputTokens, cachedTokens: this.cachedTokens
+ };
}
}
diff --git a/package-lock.json b/package-lock.json
index 3e76790..78a4ba5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "chalk": "^5.5.0",
"dotenv": "^17.2.1",
"exa-js": "^1.8.27",
"ink": "^6.1.0",
diff --git a/package.json b/package.json
index 877185d..cf4b7a9 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"license": "ISC",
"description": "",
"dependencies": {
+ "chalk": "^5.5.0",
"dotenv": "^17.2.1",
"exa-js": "^1.8.27",
"ink": "^6.1.0",
diff --git a/tools/list_files.js b/tools/list_files.js
index 7c5c97d..cdaac44 100644
--- a/tools/list_files.js
+++ b/tools/list_files.js
@@ -120,7 +120,7 @@ export async function run(args) {
const inputPath = args?.path || "";
const depth = Number.isInteger(args?.depth) ? args.depth : 1;
const includeHidden = args?.includeHidden ?? false;
- const chrootPath = '/workspaces/aiTools';
+ const chrootPath = '/workspaces/aiTools/root';
if (!chrootPath) {
return { err: "Chroot path is required" };
diff --git a/tools/patch_files.js b/tools/patch_files.js
index 943c491..8dc1eda 100644
--- a/tools/patch_files.js
+++ b/tools/patch_files.js
@@ -808,7 +808,7 @@ export async function run(args) {
open_file,
write_file,
remove_file,
- '/workspaces/aiTools'
+ '/workspaces/aiTools/root'
);
return result;
} catch (error) {
diff --git a/tools/read_file.js b/tools/read_file.js
index 0269633..0285a70 100644
--- a/tools/read_file.js
+++ b/tools/read_file.js
@@ -3,7 +3,7 @@ import { createInterface } from "node:readline";
import path from "node:path";
-const virtual_chroot = '/workspaces/aiTools';
+const virtual_chroot = '/workspaces/aiTools/root';
// Ensures reads are confined to `virtual_chroot`.
@@ -13,7 +13,7 @@ export default {
type: "object", required: ["path","linesToSkip","linesToRead"], additionalProperties: false, properties: {
path: { type: "string", description: "The path to the file to read.", },
linesToSkip: { type: "integer", description: "The number of lines to skip. Use 0 to read from the beginning.", minimum: 0 },
- linesToRead: { type: "integer", description: "1-400 The number of lines to read. Use 0 or more than 400 to read 400 lines.", minimum: 0 }
+ linesToRead: { type: "integer", description: "1-400 The number of lines to read.", minimum: 1, maximum: 400 }
}
}
};
@@ -29,6 +29,8 @@ export async function run(args) {
// Normalize linesToRead (1-400, with 0 or >400 meaning 400)
const maxLines = (linesToRead <= 0 || linesToRead > 400) ? 400 : linesToRead;
+
+ // return 'FILE DOES NOT EXIST'
try {
diff --git a/tools/ripgrep.js b/tools/ripgrep.js
index 277a618..a814914 100644
--- a/tools/ripgrep.js
+++ b/tools/ripgrep.js
@@ -1,6 +1,6 @@
import { spawnSync } from "node:child_process";
-const virtual_chroot = '/workspaces/aiTools';
+const virtual_chroot = '/workspaces/aiTools/root';
export default {
type: "function", name: "ripgrep", strict: true,
diff --git a/tools/wget.js b/tools/wget.js
index dab99ae..11d9d84 100644
--- a/tools/wget.js
+++ b/tools/wget.js
@@ -7,7 +7,10 @@ export async function run(args){
const buffer = await res.buffer();
const filename = new Date().getTime() + '.' + url.split('.').pop();
const content = buffer.slice(0, 500).toString('utf8');
- return { filename, content };
+ // save the file to the chroot
+ const filePath = `/workspaces/aiTools/root/${filename}`;
+ fs.writeFileSync(filePath, content);
+ return { 'Downloaded to:': filename };
};
// metadata for the tool runner