From 94aa2004242e5fec3676930bbf316148d52eeff4 Mon Sep 17 00:00:00 2001 From: Mike McNeil Date: Mon, 12 May 2025 18:01:43 -0400 Subject: [PATCH] More ai helpers (#29027) I will upstream these ahead of next week into sails-hook-organics. --- website/api/helpers/ai/compile.js | 59 ++++++++++++ website/api/helpers/ai/decide.js | 92 ++++++++++++++++++ website/api/helpers/ai/prompt.js | 6 ++ website/api/helpers/ai/satisfy.js | 19 ++-- website/api/helpers/ai/weigh.js | 94 +++++++++++++++++++ website/scripts/test-ai-compile.js | 27 ++++++ .../test-ai-constraint-satisfaction.js | 49 +++------- website/scripts/test-ai-decision.js | 54 +++++++++++ website/scripts/test-ai-weights.js | 55 +++++++++++ 9 files changed, 411 insertions(+), 44 deletions(-) create mode 100644 website/api/helpers/ai/compile.js create mode 100644 website/api/helpers/ai/decide.js create mode 100644 website/api/helpers/ai/weigh.js create mode 100644 website/scripts/test-ai-compile.js create mode 100644 website/scripts/test-ai-decision.js create mode 100644 website/scripts/test-ai-weights.js diff --git a/website/api/helpers/ai/compile.js b/website/api/helpers/ai/compile.js new file mode 100644 index 0000000000..8254dd9f79 --- /dev/null +++ b/website/api/helpers/ai/compile.js @@ -0,0 +1,59 @@ +module.exports = { + + + friendlyName: 'Compile', + + + description: 'Automatically generate code for use in a Sails app based on a human specification.', + + + extendedDescription: '', + + + inputs: { + + humanSpecification: { + required: true, + type: 'string', + example: 'Sign up: Handle a signup form by creating a user in the database and set the `.userId` key in the session.', + }, + + purpose: { + type: 'string', + isIn: ['action', 'helper'], + defaultsTo: 'action' + }, + + }, + + + exits: { + + success: { + outputFriendlyName: 'Code file', + outputDescription: 'The code for a Sails action or helper that implements the given human specification.', + extendedDescription: 'The generated code is formatted for Sails v1 and above (aka using "actions2", aka the node-machine spec).' + }, + + }, + + + fn: async function ({ humanSpecification, purpose }) { + + return await ƒ.prompt.with({ + baseModel: 'o4-mini-2025-04-16', + prompt: + 'Generate code for a sails app '+ + (purpose === 'action' ? + 'action (actions2 in sails v1+)' + : 'helper (in sails v1+)' + )+ + ' to accomplish the following specification:\n```\n'+ + humanSpecification+ + '\n```\nRespond only with pure JavaScript code, without code fences. It is ok to use try/catch as needed, but never use .catch(). When it helps result in less code with equivalent rigor, take advantage of .intercept() or .tolerate() for error handling. (Remember never to throw inside of the intercept or tolerate functions - instead for .intercept(), return the error to throw, and for .tolerate(), return the value the function should return) Do not make a separate exit for 5xx server errors-- instead just throw to take advatage of the built-in error exit. Do not include a second `exits` argument to `fn` -- instead just return or throw. Instead of using the `inputs` argument to `fn`, specify it using syntax like `fn: ({ foo, bar })=>{ /* implementation */ }. If writing or reading to the session, use the convention of `req.session.userId`. Never use `fetch` for making outgoing HTTP requests -- instead, use sails.helpers.http if needed.', + }); + } + + +}; + diff --git a/website/api/helpers/ai/decide.js b/website/api/helpers/ai/decide.js new file mode 100644 index 0000000000..535cfde0c6 --- /dev/null +++ b/website/api/helpers/ai/decide.js @@ -0,0 +1,92 @@ +module.exports = { + + + friendlyName: 'Decide', + + + description: 'Make an intelligent determination about some data from the provided choices.', + + + extendedDescription: 'e.g. for sentiment analysis for social network, or implementing "top posts" or featured content.', + + + sideEffects: 'cacheable', + + + inputs: { + + data: { + type: 'json', + required: true + }, + + choices: { + type: {}, + required: true, + description: 'The choices to pick from.', + extendedDescription: 'Each choice consists of a key (a string that will be returned if this choice is selected) and a value (the "predicate", i.e. a phrase describing the data that could either be true or false). You can think of this similar to how many popular `