From 54a104276b25eb51adfc8d551a5306acfda32543 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 23 Jan 2025 16:15:32 -0500 Subject: [PATCH] dev: add tools normalization/converting --- .../src/modules/puterai/AIChatService.js | 5 + .../modules/puterai/lib/FunctionCalling.js | 93 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/backend/src/modules/puterai/lib/FunctionCalling.js diff --git a/src/backend/src/modules/puterai/AIChatService.js b/src/backend/src/modules/puterai/AIChatService.js index 6ea8c305..d547d1ba 100644 --- a/src/backend/src/modules/puterai/AIChatService.js +++ b/src/backend/src/modules/puterai/AIChatService.js @@ -26,6 +26,7 @@ const { TypeSpec } = require("../../services/drivers/meta/Construct"); const { TypedValue } = require("../../services/drivers/meta/Runtime"); const { Context } = require("../../util/context"); const { AsModeration } = require("./lib/AsModeration"); +const FunctionCalling = require("./lib/FunctionCalling"); // Maximum number of fallback attempts when a model fails, including the first attempt const MAX_FALLBACKS = 3 + 1; // includes first attempt @@ -354,6 +355,10 @@ class AIChatService extends BaseService { } } + if ( parameters.tools ) { + FunctionCalling.normalize_tools_object(parameters.tools); + } + if ( intended_service === this.service_name ) { throw new Error('Calling ai-chat directly is not yet supported'); } diff --git a/src/backend/src/modules/puterai/lib/FunctionCalling.js b/src/backend/src/modules/puterai/lib/FunctionCalling.js new file mode 100644 index 00000000..5b8a0eb8 --- /dev/null +++ b/src/backend/src/modules/puterai/lib/FunctionCalling.js @@ -0,0 +1,93 @@ +module.exports = class FunctionCalling { + /** + * Normalizes the 'tools' object in-place. + * + * This function will accept an array of tools provided by the + * user, and produce a normalized object that can then be + * converted to the apprpriate representation for another + * service. + * + * We will accept conventions from either service that a user + * might expect to work, prioritizing the OpenAI convention + * when conflicting conventions are present. + * + * @param {*} tools + */ + static normalize_tools_object (tools) { + for ( let i=0 ; i < tools.length ; i++ ) { + const tool = tools[i]; + let normalized_tool = {}; + + const normalize_function = fn => { + const normal_fn = {}; + const parameters = + fn.parameters || + fn.input_schema; + + normal_fn.parameters = parameters ?? { + type: 'object', + }; + + if ( fn.name ) { + normal_fn.name = fn.name; + } + + if ( fn.description ) { + normal_fn.description = fn.description; + } + + return normal_fn; + } + + if ( tool.input_schema ) { + normalized_tool = { + type: 'function', + function: normalize_function(tool), + }; + } else if ( tool.type === 'function' ) { + normalized_tool = { + type: 'function', + function: normalize_function(tool.function), + } + } else { + normalized_tool = { + type: 'function', + function: normalize_function(tool), + }; + } + + tools[i] = normalized_tool; + } + return tools; + } + + /** + * This function will convert a normalized tools object to the + * format expected by OpenAI. + * + * @param {*} tools + * @returns + */ + static make_openai_tools (tools) { + return tools; + } + + /** + * This function will convert a normalized tools object to the + * format expected by Claude. + * + * @param {*} tools + * @returns + */ + static make_claude_tools (tools) { + if ( ! tools ) return undefined; + return tools.map(tool => { + const { name, description, parameters } = tool.function; + return { + name, + description, + input_schema: parameters, + }; + }); + } +}