c9ai
Version:
Universal AI assistant with vibe-based workflows, hybrid cloud+local AI, and comprehensive tool integration
445 lines (384 loc) ⢠13.7 kB
JavaScript
"use strict";
/**
* Function Generator
* Generates JavaScript functions dynamically using AI (local first, cloud fallback)
*/
const { getProvider } = require("../providers");
// NOTE: XML-Lisp system archived - will be replaced with BASIC interpreter
// const { XMLLispTranspiler } = require("./xml-lisp-transpiler");
const vm = require("vm");
class FunctionGenerator {
constructor(options = {}) {
this.localProvider = options.localProvider || "llamacpp";
this.cloudProvider = options.cloudProvider || "openai";
}
/**
* Generate an XML-Lisp function using AI, then transpile to JavaScript
*/
async generateFunction(functionName, parameters, context = {}) {
// First try to generate XML-Lisp representation
const xmlResult = await this.generateXMLFunction(functionName, parameters, context);
if (xmlResult.success) {
// Transpile XML to JavaScript
const jsResult = await this.transpileXMLToJS(xmlResult.xml, functionName);
if (jsResult.success) {
return {
success: true,
xml: xmlResult.xml,
javascript: jsResult.javascript,
source: xmlResult.provider,
transpiled: true
};
}
// If XML transpilation failed (transpiler not available), fall back to direct JS generation
console.log(`â ď¸ XML transpilation failed; falling back to direct JavaScript generation for ${functionName}`);
const jsFallback = await this.generateJavaScriptFunction(functionName, parameters, context);
if (jsFallback.success) {
return {
success: true,
xml: xmlResult.xml,
javascript: jsFallback.javascript,
source: jsFallback.provider || xmlResult.provider,
transpiled: false,
format: 'javascript'
};
}
// As a last resort, still return the XML so callers can handle it
return {
success: true,
xml: xmlResult.xml,
javascript: undefined,
source: xmlResult.provider,
transpiled: false
};
}
// Fallback to direct JavaScript generation
return await this.generateJavaScriptFunction(functionName, parameters, context);
}
/**
* Generate XML-Lisp function representation
*/
async generateXMLFunction(functionName, parameters, context = {}) {
const systemPrompt = `Generate an XML-Lisp function for '${functionName}' with parameters: ${parameters}
Requirements:
- Return ONLY the XML function definition, no explanations
- Use XML-Lisp format with <function>, <params>, <body> structure
- Support mathematical operations: <add>, <subtract>, <multiply>, <divide>, <power>
- Use <ref> for parameter references and <number> for constants
- Handle conditional logic with <if>, <condition>, <then>, <else>
- Optimize for mathematical accuracy and business logic
XML-Lisp Schema:
<function name="functionName">
<params>
<param name="param1" type="number" description="..."/>
<param name="param2" type="number" description="..."/>
</params>
<body>
<operation>
<ref>param1</ref>
<ref>param2</ref>
</operation>
</body>
</function>
Business Function Examples:
compound_interest:
<function name="compound_interest">
<params>
<param name="principal" type="number" description="Initial investment"/>
<param name="rate" type="number" description="Annual interest rate (decimal)"/>
<param name="periods" type="number" description="Number of compounding periods"/>
</params>
<body>
<multiply>
<ref>principal</ref>
<power>
<add><number>1</number><ref>rate</ref></add>
<ref>periods</ref>
</power>
</multiply>
</body>
</function>
customer_lifetime_value:
<function name="customer_lifetime_value">
<params>
<param name="monthly_revenue" type="number" description="Average monthly revenue per customer"/>
<param name="lifespan_months" type="number" description="Expected customer lifespan in months"/>
<param name="churn_rate" type="number" description="Monthly churn rate (decimal)"/>
</params>
<body>
<multiply>
<multiply><ref>monthly_revenue</ref><ref>lifespan_months</ref></multiply>
<divide>
<number>1</number>
<add><number>1</number><ref>churn_rate</ref></add>
</divide>
</multiply>
</body>
</function>
break_even_point:
<function name="break_even_point">
<params>
<param name="fixed_costs" type="number" description="Total fixed costs"/>
<param name="price_per_unit" type="number" description="Selling price per unit"/>
<param name="variable_cost_per_unit" type="number" description="Variable cost per unit"/>
</params>
<body>
<divide>
<ref>fixed_costs</ref>
<subtract>
<ref>price_per_unit</ref>
<ref>variable_cost_per_unit</ref>
</subtract>
</divide>
</body>
</function>
roi_calculation:
<function name="roi_calculation">
<params>
<param name="gain" type="number" description="Investment gain or return"/>
<param name="cost" type="number" description="Cost of investment"/>
</params>
<body>
<multiply>
<divide>
<subtract><ref>gain</ref><ref>cost</ref></subtract>
<ref>cost</ref>
</divide>
<number>100</number>
</multiply>
</body>
</function>
Generate the ${functionName} function:`;
// Try cloud AI for XML-Lisp generation (skip local for XML complexity)
try {
console.log(`đ Using cloud AI (${this.cloudProvider}) to generate XML-Lisp for ${functionName}`);
const cloudProvider = getProvider(this.cloudProvider);
const response = await cloudProvider.call({
messages: [{ role: "user", content: systemPrompt }],
temperature: 0.1,
max_tokens: 500
});
const xmlCode = this.cleanXMLCode(response.text);
if (this.validateXMLFunction(xmlCode, functionName)) {
console.log(`â
Cloud AI successfully generated XML-Lisp for ${functionName}`);
return {
success: true,
xml: xmlCode,
source: 'cloud',
provider: this.cloudProvider
};
} else {
console.log(`â Cloud AI generated invalid XML-Lisp for ${functionName}`);
return {
success: false,
error: 'Invalid XML-Lisp function generated',
xml: xmlCode
};
}
} catch (cloudError) {
console.log(`â ď¸ Cloud AI failed to generate ${functionName}: ${cloudError.message}`);
}
// Fallback to cloud AI
try {
console.log(`đ Trying cloud AI (${this.cloudProvider}) to generate ${functionName} function`);
const cloudProvider = getProvider(this.cloudProvider);
const response = await cloudProvider.call({
messages: [{ role: "user", content: systemPrompt }],
temperature: 0.1,
max_tokens: 300
});
const code = this.cleanFunctionCode(response.text);
if (this.validateFunction(code, functionName)) {
console.log(`â
Cloud AI successfully generated ${functionName} function`);
return {
success: true,
code,
source: 'cloud',
provider: this.cloudProvider
};
} else {
console.log(`â Cloud AI generated invalid code for ${functionName}`);
}
} catch (cloudError) {
console.log(`â Cloud AI failed to generate ${functionName}: ${cloudError.message}`);
}
return {
success: false,
error: `Cloud AI failed to generate XML-Lisp for ${functionName} function`
};
}
/**
* Fallback to direct JavaScript generation (legacy support)
*/
async generateJavaScriptFunction(functionName, parameters, context = {}) {
const systemPrompt = `Generate a JavaScript function called '${functionName}' that takes parameters: ${parameters}
Requirements:
- Return ONLY the function code, no explanations
- Use proper JavaScript syntax
- Handle edge cases (negative numbers, zero, etc.)
- Optimize for mathematical accuracy
- Function should be pure (no side effects)
Generate the ${functionName} function:`;
try {
console.log(`đ Using cloud AI for direct JavaScript generation of ${functionName}`);
const cloudProvider = getProvider(this.cloudProvider);
const response = await cloudProvider.call({
messages: [{ role: "user", content: systemPrompt }],
temperature: 0.1,
max_tokens: 300
});
const code = this.cleanFunctionCode(response.text);
if (this.validateFunction(code, functionName)) {
return {
success: true,
javascript: code,
source: 'cloud',
provider: this.cloudProvider,
format: 'javascript'
};
}
} catch (error) {
console.log(`â JavaScript generation failed: ${error.message}`);
}
return {
success: false,
error: `Failed to generate ${functionName} function in any format`
};
}
/**
* Clean and extract XML function from AI response
*/
cleanXMLCode(text) {
if (!text) return '';
// Remove markdown code blocks
let xml = text.replace(/```(?:xml)?\n?([\s\S]*?)\n?```/g, '$1');
// Extract function definition
const functionMatch = xml.match(/<function[\s\S]*?<\/function>/);
if (functionMatch) {
xml = functionMatch[0];
}
// Clean up whitespace
xml = xml.trim();
return xml;
}
/**
* Clean and extract function code from AI response (legacy)
*/
cleanFunctionCode(text) {
if (!text) return '';
// Remove markdown code blocks
let code = text.replace(/```(?:javascript|js)?\n?([\s\S]*?)\n?```/g, '$1');
// Extract function definition
const functionMatch = code.match(/function\s+\w+\s*\([^)]*\)\s*{[\s\S]*?}/);
if (functionMatch) {
code = functionMatch[0];
}
// Clean up whitespace and ensure proper formatting
code = code.trim();
return code;
}
/**
* Validate XML-Lisp function structure
*/
validateXMLFunction(xml, expectedFunctionName) {
if (!xml) return false;
try {
// Basic XML structure validation
if (!xml.includes('<function') || !xml.includes('</function>')) {
return false;
}
// Check for required elements
const hasParams = xml.includes('<params>') && xml.includes('</params>');
const hasBody = xml.includes('<body>') && xml.includes('</body>');
const hasCorrectName = xml.includes(`name="${expectedFunctionName}"`);
return hasParams && hasBody && hasCorrectName;
} catch (error) {
console.log(`XML validation failed: ${error.message}`);
return false;
}
}
/**
* Transpile XML-Lisp function to executable JavaScript
*/
async transpileXMLToJS(xml, functionName) {
try {
console.log(`đ Transpiling XML-Lisp to JavaScript for ${functionName}`);
// Create a simple XMLâJS transpiler
const transpiler = new XMLLispTranspiler();
// Occasionally cleanup cache (1% chance)
if (Math.random() < 0.01) {
transpiler.cleanupCache();
}
const javascript = transpiler.transpile(xml);
// Validate the transpiled JavaScript
if (this.validateFunction(javascript, functionName)) {
return {
success: true,
javascript: javascript,
transpiled: true
};
} else {
return {
success: false,
error: 'Transpiled JavaScript validation failed',
javascript: javascript
};
}
} catch (error) {
console.log(`â XML transpilation failed: ${error.message}`);
return {
success: false,
error: `Transpilation failed: ${error.message}`
};
}
}
/**
* Validate that the generated code is a proper JavaScript function
*/
validateFunction(code, expectedFunctionName) {
if (!code) return false;
try {
// Create a safe VM context for validation
const context = {};
vm.createContext(context);
// Try to evaluate the function
vm.runInContext(code, context, { timeout: 1000 });
// Check if function exists and is callable
if (typeof context[expectedFunctionName] === 'function') {
return true;
}
return false;
} catch (error) {
console.log(`Function validation failed: ${error.message}`);
return false;
}
}
/**
* Execute the generated function with parameters
*/
async executeFunction(code, functionName, parameters) {
try {
const context = {
Math: Math,
// Add other safe globals if needed
};
vm.createContext(context);
// Add the function to context
vm.runInContext(code, context, { timeout: 5000 });
// Execute the function
const expression = `${functionName}(${parameters})`;
const result = vm.runInContext(expression, context, { timeout: 5000 });
return {
success: true,
result,
expression
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
}
module.exports = { FunctionGenerator };