diff --git a/packages/kbot/dist-in/commands/run-responses.d.ts b/packages/kbot/dist-in/commands/run-responses.d.ts index d09c38dd..9a506c50 100644 --- a/packages/kbot/dist-in/commands/run-responses.d.ts +++ b/packages/kbot/dist-in/commands/run-responses.d.ts @@ -1,3 +1,3 @@ import OpenAI from 'openai'; import { IKBotTask } from '@polymech/ai-tools'; -export declare const runResponses: (client: OpenAI, params: any, options: IKBotTask) => Promise; +export declare const runResponses: (client: OpenAI, params: any, options: IKBotTask) => Promise; diff --git a/packages/kbot/dist-in/commands/run-responses.js b/packages/kbot/dist-in/commands/run-responses.js index 1c9fa7ef..79f2eff2 100644 --- a/packages/kbot/dist-in/commands/run-responses.js +++ b/packages/kbot/dist-in/commands/run-responses.js @@ -1,3 +1,5 @@ +import { sync as readFS } from '@polymech/fs/read'; +import { sync as exists } from '@polymech/fs/exists'; import { onCompletion } from './run-completion.js'; export const runResponses = async (client, params, options) => { if (options.dry) { @@ -19,18 +21,36 @@ export const runResponses = async (client, params, options) => { }, // { type: "web_search_preview" }, ]; - const response = await client.responses.create({ - model: options.model, - input, - stream: false, - parallel_tool_calls: false, - tools - }); - if (!response || !response.output_text) { - return ''; + let format = null; + if (exists(options.format)) { + const content = readFS(options.format); + format = JSON.parse(content.toString()); + } + try { + const response = await client.responses.create({ + model: options.model, + input, + stream: false, + parallel_tool_calls: false, + tools: tools, + text: { + format: { + type: "json_schema", + name: "format", + schema: format, + strict: false + } + } + }); + if (!response || !response.output_text) { + return ''; + } + let result = response.output_text; + result = await onCompletion(result, options); + return result; + } + catch (e) { + options.logger?.error(`Error running responses mode: ${e.message}`, e.stack); } - let result = response.output_text; - result = await onCompletion(result, options); - return result; }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVuLXJlc3BvbnNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb21tYW5kcy9ydW4tcmVzcG9uc2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUVsRCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsS0FBSyxFQUFFLE1BQWMsRUFBRSxNQUFXLEVBQUUsT0FBa0IsRUFBRSxFQUFFO0lBQ2xGLElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2QsT0FBTyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtRQUNuRCxPQUFPLEtBQUssQ0FBQTtJQUNoQixDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLFFBQVE7U0FDeEIsR0FBRyxDQUFDLENBQUMsQ0FBNkIsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztTQUNqRCxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFakIsTUFBTSxLQUFLLEdBQUc7UUFDVjtZQUNJLElBQUksRUFBRSxZQUFZO1lBQ2xCLGFBQWEsRUFBRTtnQkFDWCxJQUFJLEVBQUUsYUFBYTtnQkFDbkIsT0FBTyxFQUFFLElBQUk7Z0JBQ2IsSUFBSSxFQUFFLFlBQVk7Z0JBQ2xCLE1BQU0sRUFBRSxXQUFXO2FBQ3RCO1NBQ0o7UUFDRCxrQ0FBa0M7S0FDckMsQ0FBQTtJQUVELE1BQU0sUUFBUSxHQUFHLE1BQU8sTUFBYyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUM7UUFDcEQsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLO1FBQ3BCLEtBQUs7UUFDTCxNQUFNLEVBQUUsS0FBSztRQUNiLG1CQUFtQixFQUFFLEtBQUs7UUFDMUIsS0FBSztLQUNSLENBQUMsQ0FBQTtJQUVGLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDckMsT0FBTyxFQUFFLENBQUE7SUFDYixDQUFDO0lBRUQsSUFBSSxNQUFNLEdBQUcsUUFBUSxDQUFDLFdBQVcsQ0FBQTtJQUNqQyxNQUFNLEdBQUcsTUFBTSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFBO0lBQzVDLE9BQU8sTUFBTSxDQUFBO0FBQ2pCLENBQUMsQ0FBQSJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVuLXJlc3BvbnNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb21tYW5kcy9ydW4tcmVzcG9uc2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBLE9BQU8sRUFBRSxJQUFJLElBQUksTUFBTSxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDbEQsT0FBTyxFQUFFLElBQUksSUFBSSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDbEQsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHLEtBQUssRUFBRSxNQUFjLEVBQUUsTUFBVyxFQUFFLE9BQWtCLEVBQUUsRUFBRTtJQUNsRixJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNkLE9BQU8sQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLDZCQUE2QixDQUFDLENBQUE7UUFDbkQsT0FBTyxLQUFLLENBQUE7SUFDaEIsQ0FBQztJQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxRQUFRO1NBQ3hCLEdBQUcsQ0FBQyxDQUFDLENBQTZCLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7U0FDakQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBRWpCLE1BQU0sS0FBSyxHQUFHO1FBQ1Y7WUFDSSxJQUFJLEVBQUUsWUFBWTtZQUNsQixhQUFhLEVBQUU7Z0JBQ1gsSUFBSSxFQUFFLGFBQWE7Z0JBQ25CLE9BQU8sRUFBRSxJQUFJO2dCQUNiLElBQUksRUFBRSxZQUFZO2dCQUNsQixNQUFNLEVBQUUsV0FBVzthQUN0QjtTQUNKO1FBQ0Qsa0NBQWtDO0tBQ3JDLENBQUE7SUFFRCxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFFbEIsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDekIsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QyxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0QsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztZQUMzQyxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsS0FBSztZQUNMLE1BQU0sRUFBRSxLQUFLO1lBQ2IsbUJBQW1CLEVBQUUsS0FBSztZQUMxQixLQUFLLEVBQUUsS0FBWTtZQUNuQixJQUFJLEVBQUU7Z0JBQ0YsTUFBTSxFQUFFO29CQUNKLElBQUksRUFBRSxhQUFhO29CQUNuQixJQUFJLEVBQUUsUUFBUTtvQkFDZCxNQUFNLEVBQUUsTUFBTTtvQkFDZCxNQUFNLEVBQUUsS0FBSztpQkFDaEI7YUFDSjtTQUNKLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDckMsT0FBTyxFQUFFLENBQUE7UUFDYixDQUFDO1FBRUQsSUFBSSxNQUFNLEdBQUcsUUFBUSxDQUFDLFdBQVcsQ0FBQTtRQUNqQyxNQUFNLEdBQUcsTUFBTSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFBO1FBQzVDLE9BQU8sTUFBTSxDQUFBO0lBQ2pCLENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1QsT0FBTyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsaUNBQWlDLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDaEYsQ0FBQztBQUNMLENBQUMsQ0FBQSJ9 \ No newline at end of file diff --git a/packages/kbot/src/commands/run-responses.ts b/packages/kbot/src/commands/run-responses.ts index c6a18b3e..f1659b1b 100644 --- a/packages/kbot/src/commands/run-responses.ts +++ b/packages/kbot/src/commands/run-responses.ts @@ -1,10 +1,9 @@ import OpenAI from 'openai' import { ChatCompletionMessageParam } from 'openai/resources/index.mjs' - import { IKBotTask } from '@polymech/ai-tools' - +import { sync as readFS } from '@polymech/fs/read' +import { sync as exists } from '@polymech/fs/exists' import { onCompletion } from './run-completion.js' - export const runResponses = async (client: OpenAI, params: any, options: IKBotTask) => { if (options.dry) { options.logger?.info('Dry run - skipping API call') @@ -28,19 +27,38 @@ export const runResponses = async (client: OpenAI, params: any, options: IKBotTa // { type: "web_search_preview" }, ] - const response = await (client as any).responses.create({ - model: options.model, - input, - stream: false, - parallel_tool_calls: false, - tools - }) + let format = null; - if (!response || !response.output_text) { - return '' + if (exists(options.format)) { + const content = readFS(options.format); + format = JSON.parse(content.toString()); } - let result = response.output_text - result = await onCompletion(result, options) - return result + try { + const response = await client.responses.create({ + model: options.model, + input, + stream: false, + parallel_tool_calls: false, + tools: tools as any, + text: { + format: { + type: "json_schema", + name: "format", + schema: format, + strict: false + } + } + }) + + if (!response || !response.output_text) { + return '' + } + + let result = response.output_text + result = await onCompletion(result, options) + return result + } catch (e) { + options.logger?.error(`Error running responses mode: ${e.message}`, e.stack) + } } \ No newline at end of file diff --git a/packages/kbot/tests/research-format.json b/packages/kbot/tests/research-format.json new file mode 100644 index 00000000..151d4283 --- /dev/null +++ b/packages/kbot/tests/research-format.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/oled-display-list.schema.json", + "title": "OLED Display List", + "description": "A schema for a list of OLED displays with their properties.", + "type": "object", + "properties": { + "displays": { + "type": "array", + "description": "A list of OLED displays suitable for ESP-32 with buttons.", + "items": { + "type": "object", + "properties": { + "model_name": { + "type": "string", + "description": "The model name of the OLED display." + }, + "link": { + "type": "string", + "description": "URL to the product page.", + "format": "uri" + }, + "price": { + "type": "number", + "description": "Price in USD.", + "minimum": 0 + }, + "type": { + "type": "string", + "description": "Type of the display (e.g., I2C, SPI)." + }, + "features": { + "type": "string", + "description": "Key features of the display." + }, + "pros": { + "type": "string", + "description": "Advantages of using this display." + }, + "cons": { + "type": "string", + "description": "Disadvantages of using this display." + } + }, + "required": [ + "model_name", + "link", + "price", + "type", + "features", + "pros", + "cons" + ] + } + } + }, + "required": [ + "displays" + ] +} \ No newline at end of file diff --git a/packages/kbot/tests/research.sh b/packages/kbot/tests/research.sh index dd28a5d5..d38c0367 100644 --- a/packages/kbot/tests/research.sh +++ b/packages/kbot/tests/research.sh @@ -2,7 +2,7 @@ # - --model=o3-deep-research needs verification : https://platform.openai.com/settings/organization/general -kbot-d "Create a comprehensive list of OLED displays, with buttons - to be used with a ESP-32, as Markdown table (links, prices, types, features, pros and cons). Elaborate on the features, and the pros and cons of each." \ +kbot-d "Create a comprehensive list of OLED displays, with buttons - to be used with a ESP-32. The output should be a JSON object that follows the provided schema. Elaborate on the features, and the pros and cons of each." \ --router=openai \ --model=gpt-4.1 \ --model_rs_1=o4-mini-deep-research \ @@ -10,6 +10,8 @@ kbot-d "Create a comprehensive list of OLED displays, with buttons - to be used --disable=npm,terminal,interact,git,search,web,user,email \ --preferences=none \ --mode=responses \ - --dst=./tests/research/oled-displays.md \ + --filters=code \ + --dst=./tests/research/oled-displays.json \ + --format=./tests/research-format.json \ --logLevel=2 diff --git a/packages/kbot/tests/research/oled-displays.json b/packages/kbot/tests/research/oled-displays.json new file mode 100644 index 00000000..a272eda9 --- /dev/null +++ b/packages/kbot/tests/research/oled-displays.json @@ -0,0 +1,49 @@ +{ + "displays": [ + { + "model_name": "TouchEye Dual Round OLED Display", + "link": "https://www.diyelectronics.us/2025/02/toucheye-compact-open-source-dual-oled.html", + "price": 49.0, + "type": "I2C", + "features": "Dual 1.28” round touch displays with 240x240 resolution, ESP32-S3 microcontroller, Wi-Fi and Bluetooth connectivity, microSD card slot, two programmable buttons, USB Type-C interface, battery connector with charging management.", + "pros": "Compact design with dual touch displays, wireless connectivity, expandable storage, and user-friendly interface.", + "cons": "Limited to round display format, higher price point compared to single-display modules." + }, + { + "model_name": "ESP32-CAM with Capacitive Touch Buttons and OLED", + "link": "https://robotzero.one/esp32-cam-oled-capacitive-touch-buttons/", + "price": 30.0, + "type": "I2C", + "features": "0.96” OLED display, ESP32-CAM module with camera, three capacitive touch buttons, microSD card slot, USB Type-C interface, battery-powered operation.", + "pros": "Integrated camera and display, touch button interface, portable design with battery operation.", + "cons": "Smaller display size, limited to three touch buttons, requires assembly and 3D printing for enclosure." + }, + { + "model_name": "FireBeetle Covers-OLED12864 Display", + "link": "https://community.dfrobot.com/makelog-313920.html", + "price": 20.0, + "type": "I2C", + "features": "1.3” OLED display with 128x64 resolution, two buttons, 5-way joystick, 3-axis accelerometer, compatible with FireBeetle ESP8266 board.", + "pros": "Comprehensive input options with joystick and buttons, integrated accelerometer for motion sensing, compact and stackable design.", + "cons": "Requires FireBeetle board for compatibility, limited to specific ecosystem, smaller display size." + }, + { + "model_name": "ESP32-S3 SuperMini with SSD1306 OLED and Button", + "link": "https://www.espboards.dev/blog/ssd1306-esp32-s3-super-mini-setup/", + "price": 15.0, + "type": "I2C", + "features": "0.96” OLED display with 128x64 resolution, single push button, ESP32-S3 microcontroller, USB Type-C interface.", + "pros": "Minimalist design, easy to integrate, cost-effective solution for simple interfaces.", + "cons": "Limited input options with only one button, smaller display size, basic functionality." + }, + { + "model_name": "Norvi IIOT-AE01-R with Built-in OLED and Push Buttons", + "link": "https://www.instructables.com/Working-With-Built-in-Display-and-Push-Buttons-of-/", + "price": 60.0, + "type": "I2C", + "features": "0.96” OLED SSD1306 display with 128x64 resolution, three push buttons connected via analog input, ESP32 microcontroller, industrial-grade design.", + "pros": "Industrial-grade build quality, integrated display and buttons, suitable for automation applications.", + "cons": "Higher price point, limited to three buttons, analog input method for buttons may require calibration." + } + ] +} \ No newline at end of file