Embedded plugins allow you to run Python or JavaScript code directly within the Rohas runtime, without spawning external processes. This provides better performance and tighter integration.
- Overview
- Python Embedded Plugins
- JavaScript Embedded Plugins
- Creating Embedded Plugins
- Plugin Manifest
- Best Practices
Embedded plugins are different from subprocess plugins:
- Subprocess plugins: Spawn external processes (slower, more isolation)
- Embedded plugins: Run code directly in the runtime (faster, tighter integration)
Embedded plugins use:
- Python: PyO3 for Python 3.x support
- JavaScript: QuickJS for JavaScript/ES2020 support
Create plugins/my-python-plugin/plugin.toml:
name = "myPythonTool"
type = "embedded"
description = "Python embedded plugin"
version = "1.0.0"
language = "python"
path = "myPythonTool.embedded.py"Create plugins/my-python-plugin/myPythonTool.embedded.py:
def main(args):
"""
Main function for the embedded plugin.
Args:
args: List of arguments passed from Rohas
Returns:
Result value (will be serialized to JSON)
"""
if len(args) < 2:
return {"error": "Need at least 2 arguments"}
operation = args[0]
values = [float(x) for x in args[1:]]
if operation == "add":
result = sum(values)
elif operation == "multiply":
result = 1
for x in values:
result *= x
else:
return {"error": "Unknown operation: " + operation}
return {"result": result}result = call myPythonTool("add", 10, 20, 30)
# result = {"result": 60}
sum = result.result
prompt "Sum: {sum}"
plugins/calculator-tool-embedded/plugin.toml:
name = "calculatorTool"
type = "embedded"
description = "Calculator tool (embedded Python)"
version = "1.0.0"
language = "python"
path = "calculatorTool.embedded.py"plugins/calculator-tool-embedded/calculatorTool.embedded.py:
def main(args):
"""
Calculator tool supporting add, multiply, subtract, divide, and power operations.
Usage:
calculatorTool("add", 10, 20) -> {"result": 30}
calculatorTool("multiply", 5, 6) -> {"result": 30}
calculatorTool("power", 2, 8) -> {"result": 256}
"""
if len(args) < 3:
return {"error": "Need operation and at least 2 numbers"}
operation = args[0]
try:
a = float(args[1])
b = float(args[2])
except ValueError:
return {"error": "Invalid numbers"}
if operation == "add":
result = a + b
elif operation == "subtract":
result = a - b
elif operation == "multiply":
result = a * b
elif operation == "divide":
if b == 0:
return {"error": "Division by zero"}
result = a / b
elif operation == "power":
result = a ** b
else:
return {"error": f"Unknown operation: {operation}"}
return {"result": result}Use in Rohas:
result1 = call calculatorTool("add", 10, 20)
result2 = call calculatorTool("multiply", 5, 6)
result3 = call calculatorTool("power", 2, 8)
prompt "Calculations: 10 + 20 = {result1.result}, 5 * 6 = {result2.result}, 2^8 = {result3.result}"
Create plugins/my-js-plugin/plugin.toml:
name = "myJSTool"
type = "embedded"
description = "JavaScript embedded plugin"
version = "1.0.0"
language = "javascript"
path = "myJSTool.embedded.js"Create plugins/my-js-plugin/myJSTool.embedded.js:
function main(args) {
/**
* Main function for the embedded plugin.
*
* @param {Array} args - Arguments passed from Rohas
* @returns {Object} Result value (will be serialized to JSON)
*/
if (args.length < 2) {
return { error: "Need at least 2 arguments" };
}
const operation = args[0];
const values = args.slice(1).map(x => parseFloat(x));
let result;
if (operation === "add") {
result = values.reduce((a, b) => a + b, 0);
} else if (operation === "multiply") {
result = values.reduce((a, b) => a * b, 1);
} else {
return { error: "Unknown operation: " + operation };
}
return { result: result };
}result = call myJSTool("add", 10, 20, 30)
# result = {"result": 60}
plugins/string-processor/plugin.toml:
name = "stringProcessor"
type = "embedded"
description = "String processing tool (embedded JavaScript)"
version = "1.0.0"
language = "javascript"
path = "stringProcessor.embedded.js"plugins/string-processor/stringProcessor.embedded.js:
function main(args) {
if (args.length < 2) {
return { error: "Need operation and string" };
}
const operation = args[0];
const str = args[1];
let result;
switch (operation) {
case "uppercase":
result = str.toUpperCase();
break;
case "lowercase":
result = str.toLowerCase();
break;
case "reverse":
result = str.split("").reverse().join("");
break;
case "words":
result = str.split(/\s+/).filter(w => w.length > 0);
break;
default:
return { error: "Unknown operation: " + operation };
}
return { result: result };
}Use in Rohas:
text = "Hello World"
upper = call stringProcessor("uppercase", text)
words = call stringProcessor("words", text)
prompt "Uppercase: {upper.result}, Words: {words.result}"
plugins/
└── my-embedded-plugin/
├── plugin.toml
└── myTool.embedded.py (or .js)
name = "toolName" # Name used in call statements
type = "embedded" # Must be "embedded"
description = "..." # Description
version = "1.0.0" # Version
language = "python" # "python" or "javascript"
path = "tool.embedded.py" # Path to embedded scriptBoth Python and JavaScript plugins must define a main function:
Python:
def main(args):
# args is a list of arguments
# Return a value (dict, list, string, number, etc.)
return {"result": "value"}JavaScript:
function main(args) {
// args is an array of arguments
// Return a value (object, array, string, number, etc.)
return { result: "value" };
}Return values are automatically serialized to JSON and available in Rohas:
# Return a simple value
return "hello"
# Return a record/object
return {"key": "value", "number": 42}
# Return an array
return [1, 2, 3]
# Return nested structures
return {
"data": [1, 2, 3],
"meta": {"count": 3}
}Return error information in the result:
def main(args):
try:
result = process(args)
return {"result": result, "error": None}
except Exception as e:
return {"result": None, "error": str(e)}Or in JavaScript:
function main(args) {
try {
const result = process(args);
return { result: result, error: null };
} catch (e) {
return { result: null, error: e.message };
}
}The plugin manifest (plugin.toml) must specify:
type = "embedded"- Identifies as embedded pluginlanguage = "python"or"javascript"- Specifies the languagepath- Path to the embedded script file
Example:
name = "myEmbeddedTool"
type = "embedded"
description = "My embedded tool"
version = "1.0.0"
language = "python"
path = "myTool.embedded.py"- Use descriptive function names - Make your
mainfunction clear - Validate inputs - Check arguments before processing
- Handle errors gracefully - Return error information, don't throw exceptions
- Document your functions - Add docstrings/comments
- Keep functions pure - Avoid side effects when possible
- Return structured data - Use records/objects for complex results
- Test independently - Test your plugin code outside Rohas first
Choose based on your needs:
Python:
- Better for data processing, scientific computing
- Rich ecosystem of libraries (if needed)
- More familiar to many developers
JavaScript:
- Better for string manipulation, JSON processing
- Familiar to web developers
- Lighter weight
Embedded plugins have some limitations:
- No external libraries - Can't import npm packages or pip packages
- Limited standard library - Only core language features available
- No file I/O - Can't read/write files directly
- No network access - Can't make HTTP requests
For these use cases, use subprocess plugins instead.
See plugins/calculator-tool-embedded/ for a complete Python embedded plugin example.
See examples/embedded-plugins-example.ro for usage examples.