From 67d7b294777d243f25ecb1fdd40c4987a7e8fd46 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 13:49:59 +0700 Subject: [PATCH 1/7] Update README.md to enhance project description, add features, installation instructions, and examples for the Rohas DSL --- README.md | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 335 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 23cc818..4520e4d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,228 @@ -# Experiment! +# Experiments!!! -# Rohas Domain-Specific Languages (DSLs) - -A scripting language for building LLM-powered workflows, written in Rust. +# Rohas **Rohas** (រហ័ស) means "rapid" or "fast" in Khmer - perfect for fast AI workflows! +A domain-specific language (DSL) for building LLM-powered workflows, written in Rust. Rohas makes it easy to create complex AI applications with a simple, expressive syntax. + +## Features + +- 🚀 **Simple Syntax** - Clean, readable language inspired by modern scripting languages +- 🤖 **Multi-Provider LLM Support** - OpenAI, Anthropic, Ollama, and Local models +- 🔧 **Tool & Plugin System** - Extend functionality with custom tools and plugins +- 📊 **Built-in Data Loaders** - CSV, Excel, and PostgreSQL support +- 🔄 **Flow Engine** - Multi-step workflows with parallel execution +- 💾 **Memory & State Management** - Maintain context across workflow steps +- 🎯 **Type Safety** - Optional type annotations for better code quality +- ⚡ **Performance** - Built on Rust for speed and reliability +- 🔍 **Static Analysis** - Catch errors before runtime +- 💰 **Token Optimization** - Automatic token usage optimization + +## Installation + +### Prerequisites + +- Rust 1.70+ (install from [rustup.rs](https://rustup.rs/)) +- LLM API keys (for OpenAI, Anthropic, etc.) or local model setup + +### Build from Source + +```bash +git clone https://github.com/rohas-dev/rohas.git +cd rohas +cargo build --release +``` + +The binary will be available at `target/release/rohas`. + +### Quick Start + +```bash +# Run a Rohas script +rohas run examples/hello-world.ro --provider openai --api-key YOUR_API_KEY + +# You can run a Rohas script without provider but you can't use prompt syntax +rohas run examples/hello-world.ro + +# Analyze a script for errors +rohas analyze examples/hello-world.ro + +# Optimize a script +rohas optimize examples/hello-world.ro -o optimized.ro +``` + +## Language Overview + +### Basic Syntax + +```ro +# Variables +name = "Sok" +age = 30 +greeting = "Hello, " + name + "!" + +# Template literals +message = "{name} is {age} years old" +count = 5 +info = "You have {count} items, which is {count * 2} total" + +# Prompts +prompt greeting + model: "gpt-4" + temperature: 0.7 + maxTokens: 100 +``` + +### Functions + +```ro +# Synchronous function +function greet(name): String { + return "Hello, " + name + "!" +} + +# Usage +message = greet("World") +``` + +### Flows + +Flows enable multi-step workflows with state management: + +```ro +flow researchFlow { + step gatherInfo { + topic = "artificial intelligence" + prompt "Give me a brief overview of " + topic + } + + step analyzeInfo { + prompt "Analyze the previous response and provide key insights" + } + + step summarize { + prompt "Create a concise summary of the analysis" + } +} +``` + +### Parallel Execution + +```ro +flow parallelResearch { + parallel step research1 { + prompt "What are the causes of climate change?" + } + + parallel step research2 { + prompt "What are the effects of climate change?" + } + + parallel step research3 { + prompt "What are solutions for climate change?" + } + + step combineResults { + prompt "Combine all the previous research into a comprehensive report" + } +} +``` + +### Tool Calls + +```ro +# Call a tool/plugin +city = "San Francisco" +weather = call weatherTool(city) + +result = call calculatorTool("add", 10, 20) + +# Inline tool definitions +prompt "What's the weather like in San Francisco?" + tools: [ + { + name: "weatherTool", + description: "Provides forecast data for a given city", + parameters: { + city: "String" + } + } + ] +``` + +### Type Safety + +```ro +type SaleDataRecord = record { + date: string, + product: string, + quantity: number, + price: number, +} + +salesData = call csvLoader<[SaleDataRecord]>("data.csv") +``` + +### Memory & State + +```ro +flow supportFlow { + step capture { + prompt "Capture issue details" + memory: "short_term" + state: { topic: "customer_support" } + } + + step respond { + prompt "Provide helpful guidance" + memory: "short_term" + state: { topic: "customer_support", priority: "high" } + } +} +``` + +### Conditional Logic + +```ro +flow translationFlow { + step checkInput { + isValid = sourceText != "" + + if isValid { + prompt "Translate: " + sourceText + } else { + error = "Invalid input provided" + } + } +} +``` + +### Loops + +```ro +for sale in salesData { + if sale.quantity > 100 { + prompt "High volume sale: " + sale.product + } +} +``` + +## Examples + +Check out the `examples/` directory for more examples: + +- `hello-world.ro` - Basic introduction +- `template-literal-example.ro` - Template literals +- `functions-and-await.ro` - Async functions +- `flow-news.ro` - Flow composition +- `parallel-steps.ro` - Parallel execution +- `plugin-example.ro` - Using plugins +- `tool-call-example.ro` - Tool calls +- `csv-loader-example.ro` - Data loading +- `memory-and-state.ro` - Memory management +- `conditional-flow.ro` - Conditional logic + ## Architecture ``` @@ -26,3 +243,117 @@ Rohas Flow Engine Multi-Step Workflows, Tool Calls, Memory & State ``` +## Project Structure + +``` +rohas/ +├── rohas-core/ # Lexer, Parser, AST +├── rohas-analyzer/ # Static analysis +├── rohas-optimizer/ # Token optimization +├── rohas-runtime/ # Execution engine +├── rohas-llm/ # LLM provider integrations +├── rohas-flow/ # Flow engine +├── rohas-plugins/ # Built-in plugins +├── rohas-cli/ # Command-line interface +├── plugins/ # Custom plugins +├── examples/ # Example scripts +└── docs/ # Documentation +``` + +## LLM Providers + +Rohas supports multiple LLM providers: + +### OpenAI + +```bash +rohas run script.ro --provider openai --api-key YOUR_KEY --model gpt-4 +``` + +### Anthropic + +```bash +rohas run script.ro --provider anthropic --api-key YOUR_KEY --model claude-3-opus +``` + +### Ollama + +```bash +rohas run script.ro --provider ollama --base-url http://localhost:11434 --model llama2 +``` + +### Local + +```bash +rohas run script.ro --provider local --base-url http://localhost:8000 +``` + +## Plugins + +Rohas supports multiple plugin types: + +- **Built-in Plugins**: + - CSV + - Excel + - PostgreSQL + - Vector DB for vector databases (TODO) +- **Subprocess Plugins**: Python, JavaScript, or any executable +- **Embedded Plugins**: Python and JavaScript embedded in the runtime +- **HTTP Plugins**: Remote plugins via HTTP (TODO) +- **WASM Plugins**: WebAssembly plugins (TODO) + + +See [docs/PLUGINS.md](docs/PLUGINS.md) for detailed plugin documentation. + +## Documentation + +- [Language Guide](docs/LANGUAGE_GUIDE.md) - Complete language reference +- [Flows & Workflows](docs/FLOWS.md) - Flow engine documentation +- [Plugins](docs/PLUGINS.md) - Plugin system documentation +- [Embedded Plugins](docs/EMBEDDED_PLUGINS.md) - Embedded plugin guide +- [LLM Providers](docs/LLM_PROVIDERS.md) - LLM provider configuration + +## CLI Commands + +### `rohas run ` + +Execute a Rohas script. + +Options: +- `--provider ` - LLM provider (openai, anthropic, ollama, local) +- `--api-key ` - API key for the provider +- `--base-url ` - Base URL for the provider +- `--model ` - Default model to use + +### `rohas analyze ` + +Analyze a script for errors without executing it. + +### `rohas optimize [-o output]` + +Optimize a script for token usage. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +Licensed under either of: + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Acknowledgments + +Rohas is built with: +- Rust for performance and safety +- Tokio for async runtime +- Various LLM provider SDKs +- And many other amazing open-source projects + +--- + +**Rohas** - Rapid AI workflows, made simple! 🚀 From 00dc384e3f9e22b2922bf6edd08ca9daf5581871 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 13:50:15 +0700 Subject: [PATCH 2/7] feat: add comprehensive documentation for embedded plugins and plugin system in Rohas --- docs/EMBEDDED_PLUGINS.md | 401 +++++++++++++++++++++++++++++++++++++++ docs/PLUGINS.md | 397 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 798 insertions(+) create mode 100644 docs/PLUGINS.md diff --git a/docs/EMBEDDED_PLUGINS.md b/docs/EMBEDDED_PLUGINS.md index e69de29..7f44094 100644 --- a/docs/EMBEDDED_PLUGINS.md +++ b/docs/EMBEDDED_PLUGINS.md @@ -0,0 +1,401 @@ +# Embedded Plugins + +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. + +## Table of Contents + +1. [Overview](#overview) +2. [Python Embedded Plugins](#python-embedded-plugins) +3. [JavaScript Embedded Plugins](#javascript-embedded-plugins) +4. [Creating Embedded Plugins](#creating-embedded-plugins) +5. [Plugin Manifest](#plugin-manifest) +6. [Best Practices](#best-practices) + +## Overview + +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 + +## Python Embedded Plugins + +### Creating a Python Embedded Plugin + +Create `plugins/my-python-plugin/plugin.toml`: + +```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`: + +```python +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} +``` + +### Using Python Embedded Plugins + +```ro +result = call myPythonTool("add", 10, 20, 30) +# result = {"result": 60} + +sum = result.result +prompt "Sum: {sum}" +``` + +### Python Plugin Example: Calculator + +`plugins/calculator-tool-embedded/plugin.toml`: + +```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`: + +```python +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: + +```ro +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}" +``` + +## JavaScript Embedded Plugins + +### Creating a JavaScript Embedded Plugin + +Create `plugins/my-js-plugin/plugin.toml`: + +```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`: + +```javascript +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 }; +} +``` + +### Using JavaScript Embedded Plugins + +```ro +result = call myJSTool("add", 10, 20, 30) +# result = {"result": 60} +``` + +### JavaScript Plugin Example: String Processor + +`plugins/string-processor/plugin.toml`: + +```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`: + +```javascript +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: + +```ro +text = "Hello World" +upper = call stringProcessor("uppercase", text) +words = call stringProcessor("words", text) + +prompt "Uppercase: {upper.result}, Words: {words.result}" +``` + +## Creating Embedded Plugins + +### Plugin Structure + +``` +plugins/ +└── my-embedded-plugin/ + ├── plugin.toml + └── myTool.embedded.py (or .js) +``` + +### Plugin Manifest Format + +```toml +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 script +``` + +### Function Signature + +Both Python and JavaScript plugins must define a `main` function: + +**Python:** +```python +def main(args): + # args is a list of arguments + # Return a value (dict, list, string, number, etc.) + return {"result": "value"} +``` + +**JavaScript:** +```javascript +function main(args) { + // args is an array of arguments + // Return a value (object, array, string, number, etc.) + return { result: "value" }; +} +``` + +### Return Values + +Return values are automatically serialized to JSON and available in Rohas: + +```python +# 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} +} +``` + +### Error Handling + +Return error information in the result: + +```python +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: + +```javascript +function main(args) { + try { + const result = process(args); + return { result: result, error: null }; + } catch (e) { + return { result: null, error: e.message }; + } +} +``` + +## Plugin Manifest + +The plugin manifest (`plugin.toml`) must specify: + +- `type = "embedded"` - Identifies as embedded plugin +- `language = "python"` or `"javascript"` - Specifies the language +- `path` - Path to the embedded script file + +Example: + +```toml +name = "myEmbeddedTool" +type = "embedded" +description = "My embedded tool" +version = "1.0.0" +language = "python" +path = "myTool.embedded.py" +``` + +## Best Practices + +1. **Use descriptive function names** - Make your `main` function clear +2. **Validate inputs** - Check arguments before processing +3. **Handle errors gracefully** - Return error information, don't throw exceptions +4. **Document your functions** - Add docstrings/comments +5. **Keep functions pure** - Avoid side effects when possible +6. **Return structured data** - Use records/objects for complex results +7. **Test independently** - Test your plugin code outside Rohas first + +## Python vs JavaScript + +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 + +## Limitations + +Embedded plugins have some limitations: + +1. **No external libraries** - Can't import npm packages or pip packages +2. **Limited standard library** - Only core language features available +3. **No file I/O** - Can't read/write files directly +4. **No network access** - Can't make HTTP requests + +For these use cases, use subprocess plugins instead. + +## Examples + +See `plugins/calculator-tool-embedded/` for a complete Python embedded plugin example. + +See `examples/embedded-plugins-example.ro` for usage examples. + diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md new file mode 100644 index 0000000..ecc9a58 --- /dev/null +++ b/docs/PLUGINS.md @@ -0,0 +1,397 @@ +# Plugins + +Rohas supports a powerful plugin system that allows you to extend functionality with custom tools and integrations. + +## Table of Contents + +1. [Plugin Types](#plugin-types) +2. [Built-in Plugins](#built-in-plugins) +3. [Subprocess Plugins](#subprocess-plugins) +4. [Embedded Plugins](#embedded-plugins) +5. [HTTP Plugins](#http-plugins) +6. [WASM Plugins](#wasm-plugins) +7. [Creating Plugins](#creating-plugins) +8. [Plugin Manifest](#plugin-manifest) + +## Plugin Types + +Rohas supports several plugin types: + +1. **Built-in Plugins** - Native Rust plugins (CSV, Excel, PostgreSQL) +2. **Subprocess Plugins** - External executables (Python, JavaScript, etc.) +3. **Embedded Plugins** - Python/JavaScript embedded in the runtime +4. **HTTP Plugins** - Remote plugins via HTTP +5. **WASM Plugins** - WebAssembly plugins + +## Built-in Plugins + +Built-in plugins are compiled into Rohas and available by default. + +### CSV Loader + +Load CSV files with type safety: + +```ro +type SaleDataRecord = record { + date: string, + product: string, + category: string, + quantity: number, + price: number, + region: string, +} + +salesData = call csvLoader<[SaleDataRecord]>("examples/sales_data.csv") + +for sale in salesData { + prompt "Analyze sale: {sale.product} - {sale.quantity} units" +} +``` + +### Excel Loader + +Load Excel files: + +```ro +data = call excelLoader("data.xlsx", "Sheet1") +``` + +### PostgreSQL + +Query PostgreSQL databases: + +```ro +connectionString = "postgresql://user:password@localhost/dbname" +query = "SELECT * FROM users WHERE age > 18" +users = call postgresQuery(connectionString, query) +``` + +## Subprocess Plugins + +Subprocess plugins run external executables. They're defined in `plugin.toml`: + +```toml +name = "calculatorTool" +type = "subprocess" +description = "Calculator tool (Python subprocess)" +version = "1.0.0" +path = "calculatorTool.py" +interpreter = "python3" +args = [] +``` + +### Python Plugin Example + +Create `plugins/calculator-tool/calculatorTool.py`: + +```python +import sys +import json + +def main(): + if len(sys.argv) < 2: + print(json.dumps({"error": "Invalid arguments"})) + sys.exit(1) + + operation = sys.argv[1] + args = [float(x) for x in sys.argv[2:]] + + if operation == "add": + result = sum(args) + elif operation == "multiply": + result = 1 + for x in args: + result *= x + elif operation == "power": + result = args[0] ** args[1] + else: + result = {"error": "Unknown operation"} + + print(json.dumps({"result": result})) + +if __name__ == "__main__": + main() +``` + +Use in Rohas: + +```ro +result1 = call calculatorTool("add", 10, 20) +result2 = call calculatorTool("multiply", 5, 6) +result3 = call calculatorTool("power", 2, 8) + +prompt "Calculations: 10 + 20 = {result1}, 5 * 6 = {result2}, 2^8 = {result3}" +``` + +### JavaScript Plugin Example + +Create `plugins/weather-tool/weatherTool.js`: + +```javascript +const city = process.argv[2]; + +// Simulate weather API call +const weather = { + city: city, + temperature: 72, + condition: "Sunny", + humidity: 65 +}; + +console.log(JSON.stringify(weather)); +``` + +Plugin manifest: + +```toml +name = "weatherTool" +type = "subprocess" +description = "Weather tool (Node.js subprocess)" +version = "1.0.0" +path = "weatherTool.js" +interpreter = "node" +args = [] +``` + +## Embedded Plugins + +Embedded plugins run Python or JavaScript code directly in the Rohas runtime. See [EMBEDDED_PLUGINS.md](EMBEDDED_PLUGINS.md) for detailed documentation. + +### Quick Example + +```ro +# Embedded Python plugin +result = call calculatorTool("add", 10, 20) +``` + +## HTTP Plugins + +HTTP plugins call remote services: + +```toml +name = "remoteAPI" +type = "http" +description = "Remote API plugin" +version = "1.0.0" +base_url = "https://api.example.com" +endpoint = "/process" +method = "POST" +``` + +## WASM Plugins + +WASM plugins run WebAssembly code: + +```toml +name = "wasmPlugin" +type = "wasm" +description = "WASM plugin" +version = "1.0.0" +path = "plugin.wasm" +``` + +## Creating Plugins + +### Plugin Directory Structure + +``` +plugins/ +├── my-plugin/ +│ ├── plugin.toml +│ ├── myPlugin.py (or .js, .wasm, etc.) +│ └── README.md (optional) +└── plugins.toml +``` + +### Plugin Manifest (plugin.toml) + +Required fields: + +```toml +name = "myPlugin" # Plugin name (used in call statements) +type = "subprocess" # Plugin type +description = "..." # Description +version = "1.0.0" # Version +``` + +Type-specific fields: + +**Subprocess:** +```toml +path = "script.py" # Path to script +interpreter = "python3" # Interpreter command +args = [] # Additional arguments +``` + +**HTTP:** +```toml +base_url = "https://api.example.com" +endpoint = "/endpoint" +method = "POST" +headers = { "Authorization": "Bearer token" } +``` + +**WASM:** +```toml +path = "plugin.wasm" +``` + +### Plugin Communication Protocol + +Plugins communicate via JSON: + +**Input:** Arguments are passed as command-line arguments (for subprocess) or JSON (for HTTP/WASM) + +**Output:** Plugins must output JSON to stdout: + +```json +{ + "result": "value", + "error": null +} +``` + +Or for errors: + +```json +{ + "result": null, + "error": "Error message" +} +``` + +### Example: Complete Python Plugin + +`plugins/my-tool/plugin.toml`: +```toml +name = "myTool" +type = "subprocess" +description = "My custom tool" +version = "1.0.0" +path = "myTool.py" +interpreter = "python3" +args = [] +``` + +`plugins/my-tool/myTool.py`: +```python +import sys +import json + +def main(): + try: + # Get arguments + args = sys.argv[1:] + + # Process arguments + result = process(args) + + # Output JSON + output = { + "result": result, + "error": None + } + print(json.dumps(output)) + + except Exception as e: + error_output = { + "result": None, + "error": str(e) + } + print(json.dumps(error_output)) + sys.exit(1) + +def process(args): + # Your plugin logic here + return {"processed": args} + +if __name__ == "__main__": + main() +``` + +Use in Rohas: + +```ro +result = call myTool("arg1", "arg2", "arg3") +prompt "Result: {result}" +``` + +## Plugin Manifest (plugins.toml) + +The root `plugins/plugins.toml` file tells Rohas which directories to scan: + +```toml +# Rohas Plugin Manifest +# All plugins now live in dedicated folders under `plugins/` +# This file simply tells the runtime which directories to scan. + +# Scan the current directory (plugins/) for plugin subdirectories +directories = ["."] +``` + +## Plugin Discovery + +Rohas automatically discovers plugins by: + +1. Scanning directories listed in `plugins/plugins.toml` +2. Loading `plugin.toml` from each plugin directory +3. Registering plugins for use in Rohas scripts + +## Using Plugins + +### Calling Plugins + +```ro +# Simple call +result = call myPlugin(arg1, arg2) + +# Using results +data = call csvLoader("data.csv") +processed = call processData(data) +output = call saveData(processed) +``` + +### Plugin Results + +Plugin results are automatically parsed as JSON and available as Rohas values: + +```ro +weather = call weatherTool("San Francisco") +# weather is a record: { city: "San Francisco", temperature: 72, ... } + +temperature = weather.temperature +prompt "Temperature in {weather.city}: {temperature}°F" +``` + +### Error Handling + +```ro +result = call myPlugin(arg1, arg2) + +if result.error != null { + prompt "Error: " + result.error +} else { + prompt "Success: " + result.result +} +``` + +## Best Practices + +1. **Use type annotations** - Define expected input/output types +2. **Handle errors gracefully** - Always return error information in JSON +3. **Validate inputs** - Check arguments before processing +4. **Use descriptive names** - Plugin names should be clear and descriptive +5. **Document your plugins** - Add README.md files to plugin directories +6. **Version your plugins** - Use semantic versioning +7. **Test plugins** - Test plugins independently before using in Rohas + +## Examples + +See the `plugins/` directory for example plugins: + +- `calculator-tool/` - Python subprocess plugin +- `calculator-tool-embedded/` - Embedded Python plugin +- `weather-tool/` - Weather API plugin + +See `examples/plugin-example.ro` and `examples/embedded-plugins-example.ro` for usage examples. + From bfd0ad9cf39a3daa40db0299d7451ee0f51274ac Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 13:51:02 +0700 Subject: [PATCH 3/7] feat: add extensive documentation for Flows, Language Guide, and LLM Providers in Rohas --- docs/FLOWS.md | 455 ++++++++++++++++++++++++++++++++ docs/LANGUAGE_GUIDE.md | 571 +++++++++++++++++++++++++++++++++++++++++ docs/LLM_PROVIDERS.md | 363 ++++++++++++++++++++++++++ 3 files changed, 1389 insertions(+) create mode 100644 docs/FLOWS.md create mode 100644 docs/LANGUAGE_GUIDE.md create mode 100644 docs/LLM_PROVIDERS.md diff --git a/docs/FLOWS.md b/docs/FLOWS.md new file mode 100644 index 0000000..0461060 --- /dev/null +++ b/docs/FLOWS.md @@ -0,0 +1,455 @@ +# Flows & Workflows + +Flows are Rohas' way of organizing multi-step workflows with state management, parallel execution, and memory. + +## Table of Contents + +1. [Basic Flows](#basic-flows) +2. [Flow Steps](#flow-steps) +3. [Parallel Execution](#parallel-execution) +4. [Flow Composition](#flow-composition) +5. [State Management](#state-management) +6. [Memory in Flows](#memory-in-flows) +7. [Conditional Flows](#conditional-flows) +8. [Flow Outputs](#flow-outputs) + +## Basic Flows + +A flow is a named block that contains one or more steps: + +```ro +flow myFlow { + step first { + prompt "First step" + } + + step second { + prompt "Second step" + } +} +``` + +### Simple Example + +```ro +flow greetUser { + step welcome { + name = "Alice" + prompt "Welcome, " + name + "!" + } + + step personalize { + prompt "How can I help you today, " + name + "?" + } +} +``` + +## Flow Steps + +Steps are executed sequentially by default. Each step can contain any valid Rohas code: + +```ro +flow researchFlow { + step gatherInfo { + topic = "artificial intelligence" + prompt1 = "Give me a brief overview of " + topic + prompt prompt1 + model: "gpt-4" + } + + step analyzeInfo { + prompt2 = "Analyze the previous response and provide key insights" + prompt prompt2 + model: "gpt-4" + } + + step summarize { + prompt3 = "Create a concise summary of the analysis" + prompt prompt3 + model: "gpt-4" + } +} +``` + +### Step Variables + +Variables defined in one step are available to subsequent steps: + +```ro +flow dataProcessing { + step load { + data = call csvLoader("data.csv") + } + + step process { + # 'data' is available here + processed = processData(data) + } + + step output { + # Both 'data' and 'processed' are available + prompt "Processed {processed.length} records from {data.length} total" + } +} +``` + +## Parallel Execution + +Use `parallel step` to execute steps concurrently: + +```ro +flow parallelResearch { + parallel step research1 { + prompt "What are the causes of climate change?" + } + + parallel step research2 { + prompt "What are the effects of climate change?" + } + + parallel step research3 { + prompt "What are solutions for climate change?" + } + + step combineResults { + # This step waits for all parallel steps to complete + prompt "Combine all the previous research into a comprehensive report" + } +} +``` + +### Parallel vs Sequential + +```ro +flow comparison { + # Sequential - each step waits for the previous + step step1 { + prompt "Step 1" + } + + step step2 { + prompt "Step 2" + } + + # Parallel - execute simultaneously + parallel step taskA { + prompt "Task A" + } + + parallel step taskB { + prompt "Task B" + } + + # Sequential - waits for parallel steps + step final { + prompt "Final step" + } +} +``` + +## Flow Composition + +Flows can use other flows with the `uses` keyword: + +```ro +flow fetchNews { + step search { + prompt "Get top 5 tech news today" + output { headlines: string[] } + } +} + +flow summarizeNews { + uses fetchNews + step summarize { + prompt "Summarize these headlines in 3 sentences" + output { summary: string } + } +} +``` + +### Flow Dependencies + +```ro +flow baseFlow { + step initialize { + config = loadConfig() + } +} + +flow extendedFlow { + uses baseFlow + step process { + # 'config' from baseFlow is available + result = processWithConfig(config) + } +} +``` + +## State Management + +State allows passing data between flow steps explicitly: + +```ro +flow orderProcessing { + step validate { + orderId = "12345" + prompt "Validate order {orderId}" + state: { orderId: orderId, status: "validating" } + } + + step process { + prompt "Process order" + state: { orderId: "12345", status: "processing" } + } + + step complete { + prompt "Complete order" + state: { orderId: "12345", status: "completed" } + } +} +``` + +### State Persistence + +State is maintained across steps and can be accessed: + +```ro +flow statefulFlow { + step init { + prompt "Initialize" + state: { counter: 0, items: [] } + } + + step add { + prompt "Add item" + state: { counter: 1, items: ["item1"] } + } + + step process { + prompt "Process items" + state: { counter: 1, items: ["item1"], processed: true } + } +} +``` + +## Memory in Flows + +Memory can be used within flows to maintain context: + +```ro +flow supportFlow { + step capture { + message = "Capture issue details for customer support" + prompt message + memory: "short_term" + state: { topic: "customer_support" } + } + + step respond { + response = "Provide helpful guidance for customer support" + prompt response + memory: "short_term" + state: { topic: "customer_support", priority: "high" } + } +} +``` + +### Memory Types + +- `short_term` - Memory for the current flow execution +- `long_term` - Persistent memory across flow executions + +```ro +flow memoryExample { + step remember { + prompt "My favorite programming language is Rust" + memory: "long_term" + } + + step recall { + prompt "What's my favorite programming language?" + memory: "long_term" + } +} +``` + +## Conditional Flows + +Flows can contain conditional logic: + +```ro +flow conditionalFlow { + step check { + userInput = "translate" + sourceText = "Hello, how are you?" + targetLanguage = "Khmer" + + isValid = sourceText != "" + + if isValid { + message = "Translate the following text to " + targetLanguage + ": " + sourceText + prompt message + } else { + error = "Invalid input provided" + prompt "Error: " + error + } + } +} +``` + +### Complex Conditionals + +```ro +flow complexFlow { + step decision { + score = 85 + + if score >= 90 { + grade = "A" + feedback = "Excellent work!" + } else if score >= 80 { + grade = "B" + feedback = "Good job!" + } else if score >= 70 { + grade = "C" + feedback = "Keep improving!" + } else { + grade = "F" + feedback = "Needs improvement" + } + + prompt "Your grade is {grade}. {feedback}" + } +} +``` + +## Flow Outputs + +Flows can define outputs using the `output` keyword: + +```ro +flow dataFlow { + step process { + data = call csvLoader("data.csv") + summary = analyzeData(data) + prompt "Analysis complete" + output { + recordCount: data.length, + summary: summary, + timestamp: getCurrentTime() + } + } +} +``` + +### Using Flow Outputs + +```ro +flow parentFlow { + step callChild { + result = executeFlow(dataFlow) + # Access outputs + count = result.recordCount + summary = result.summary + prompt "Processed {count} records. Summary: {summary}" + } +} +``` + +## Advanced Patterns + +### Retry Logic + +```ro +flow retryFlow { + step attempt { + maxRetries = 3 + retryCount = 0 + success = false + + while retryCount < maxRetries && !success { + try { + result = call externalAPI() + success = true + } catch { + retryCount = retryCount + 1 + } + } + + if success { + prompt "Success: " + result + } else { + prompt "Failed after {maxRetries} attempts" + } + } +} +``` + +### Pipeline Pattern + +```ro +flow dataPipeline { + step extract { + rawData = call extractData() + } + + step transform { + cleanedData = transformData(rawData) + } + + step load { + result = loadData(cleanedData) + prompt "Pipeline complete. Loaded {result.count} records" + } +} +``` + +### Fan-out/Fan-in Pattern + +```ro +flow fanOutIn { + step fanOut { + items = [1, 2, 3, 4, 5] + } + + parallel step process1 { + result1 = processItem(items[0]) + } + + parallel step process2 { + result2 = processItem(items[1]) + } + + parallel step process3 { + result3 = processItem(items[2]) + } + + step fanIn { + allResults = [result1, result2, result3] + prompt "Processed {allResults.length} items" + } +} +``` + +## Best Practices + +1. **Keep flows focused** - Each flow should have a single, clear purpose +2. **Use parallel steps** - For independent operations that can run concurrently +3. **Manage state explicitly** - Use state to pass data between steps +4. **Use memory for context** - When you need to maintain context across prompts +5. **Compose flows** - Break complex workflows into smaller, reusable flows +6. **Handle errors** - Use conditional logic to handle error cases +7. **Define outputs** - Make flow outputs explicit for better composability + +## Examples + +See the `examples/` directory for more flow examples: + +- `flow-news.ro` - Flow composition +- `multi-step-flow.ro` - Multi-step workflows +- `parallel-steps.ro` - Parallel execution +- `conditional-flow.ro` - Conditional logic +- `flow-retry.ro` - Retry patterns +- `memory-and-state.ro` - Memory and state management + diff --git a/docs/LANGUAGE_GUIDE.md b/docs/LANGUAGE_GUIDE.md new file mode 100644 index 0000000..e6d16b4 --- /dev/null +++ b/docs/LANGUAGE_GUIDE.md @@ -0,0 +1,571 @@ +# Rohas Language Guide + +Complete reference for the Rohas programming language. + +## Table of Contents + +1. [Variables](#variables) +2. [Data Types](#data-types) +3. [Operators](#operators) +4. [Template Literals](#template-literals) +5. [Functions](#functions) +6. [Control Flow](#control-flow) +7. [Prompts](#prompts) +8. [Flows](#flows) +9. [Tool Calls](#tool-calls) +10. [Type System](#type-system) +11. [Memory & State](#memory--state) + +## Variables + +Variables in Rohas are dynamically typed by default, but can be explicitly typed. + +```ro +# Simple assignment +name = "Alice" +age = 30 +isActive = true + +# Type annotations (optional) +name: string = "Alice" +age: number = 30 +isActive: boolean = true +``` + +Variables are mutable by default. Use `const` for constants: + +```ro +const PI = 3.14159 +const API_URL = "https://api.example.com" +``` + +## Data Types + +### Primitives + +- `string` - Text data +- `number` - Numeric data (integers and floats) +- `boolean` - `true` or `false` +- `null` - Null value + +### Collections + +- `array` - Ordered lists: `[1, 2, 3]` +- `record` - Key-value pairs: `{ name: "Alice", age: 30 }` + +### Examples + +```ro +# Strings +greeting = "Hello, World!" +multiline = "Line 1 +Line 2 +Line 3" + +# Numbers +integer = 42 +float = 3.14 +negative = -10 + +# Booleans +isTrue = true +isFalse = false + +# Arrays +numbers = [1, 2, 3, 4, 5] +names = ["Alice", "Bob", "Charlie"] +mixed = [1, "two", true] + +# Records +person = { + name: "Alice", + age: 30, + city: "San Francisco" +} + +# Nested structures +company = { + name: "Acme Corp", + employees: [ + { name: "Alice", role: "Engineer" }, + { name: "Bob", role: "Designer" } + ] +} +``` + +## Operators + +### Arithmetic + +```ro +a = 10 +b = 3 + +sum = a + b # 13 +difference = a - b # 7 +product = a * b # 30 +quotient = a / b # 3.333... +remainder = a % b # 1 +power = a ^ b # 1000 +``` + +### Comparison + +```ro +a = 10 +b = 20 + +a == b # false +a != b # true +a < b # true +a > b # false +a <= b # true +a >= b # false +``` + +### Logical + +```ro +a = true +b = false + +a && b # false (AND) +a || b # true (OR) +!a # false (NOT) +``` + +### String Concatenation + +```ro +firstName = "Alice" +lastName = "Smith" +fullName = firstName + " " + lastName # "Alice Smith" +``` + +## Template Literals + +Template literals allow embedded expressions using `{expression}` syntax: + +```ro +name = "Alice" +age = 30 +city = "San Francisco" + +# Simple interpolation +greeting = "Hello, {name}!" + +# Expression evaluation +info = "{name} is {age} years old and lives in {city}" + +# Complex expressions +count = 5 +message = "You have {count} items, which is {count * 2} total" + +# Nested expressions +price = 10.50 +quantity = 3 +total = "Total: ${price * quantity}" +``` + +## Functions + +### Synchronous Functions + +```ro +function greet(name): String { + return "Hello, " + name + "!" +} + +function add(a, b): Number { + return a + b +} + +# Usage +message = greet("World") +result = add(5, 3) +``` + +### Async Functions + +Async functions can use `await` to wait for asynchronous operations: + +```ro +async function fetchData(url): String { + response = await httpGet(url) + return response +} + +async function processData(input): String { + data = await fetchData("https://api.example.com/data") + processed = await process(data) + return processed +} + +# Usage +data = await fetchData("https://api.example.com") +``` + +### Function Parameters + +Functions can have typed or untyped parameters: + +```ro +# Typed parameters +function multiply(a: number, b: number): number { + return a * b +} + +# Untyped parameters +function greet(name) { + return "Hello, " + name + "!" +} + +# Default values (if supported) +function greet(name = "World") { + return "Hello, " + name + "!" +} +``` + +## Control Flow + +### If/Else + +```ro +age = 20 + +if age >= 18 { + status = "adult" +} else { + status = "minor" +} + +# Nested conditions +if score >= 90 { + grade = "A" +} else if score >= 80 { + grade = "B" +} else if score >= 70 { + grade = "C" +} else { + grade = "F" +} +``` + +### Loops + +#### For Loop + +```ro +# Iterate over array +numbers = [1, 2, 3, 4, 5] +for num in numbers { + print num * 2 +} + +# Iterate over records +sales = [ + { product: "Widget", quantity: 10 }, + { product: "Gadget", quantity: 5 } +] + +for sale in sales { + if sale.quantity > 5 { + prompt "High volume: " + sale.product + } +} +``` + +#### While Loop + +```ro +count = 0 +while count < 10 { + print count + count = count + 1 +} +``` + +## Prompts + +Prompts are the core feature for interacting with LLMs: + +### Basic Prompt + +```ro +prompt "Hello, how are you?" +``` + +### Prompt with Options + +```ro +prompt "Explain quantum computing" + model: "gpt-4" + temperature: 0.7 + maxTokens: 500 + stream: true +``` + +### Prompt with Variables + +```ro +topic = "artificial intelligence" +prompt "Explain " + topic + " in simple terms" + model: "gpt-4" + temperature: 0.5 +``` + +### Prompt with Template Literals + +```ro +name = "Alice" +age = 30 +prompt "{name} is {age} years old. Write a birthday message." + model: "gpt-4" +``` + +### Prompt with Tools + +```ro +prompt "What's the weather in San Francisco?" + tools: [ + { + name: "weatherTool", + description: "Get weather for a city", + parameters: { + city: "String" + } + } + ] + model: "gpt-4" +``` + +### Prompt with Memory + +```ro +prompt "Remember my name is Alice" + memory: "long_term" + +prompt "What's my name?" + memory: "long_term" +``` + +### Prompt with State + +```ro +prompt "Process this order" + state: { + orderId: "12345", + customerId: "67890", + priority: "high" + } +``` + +## Flows + +Flows enable multi-step workflows. See [FLOWS.md](FLOWS.md) for detailed documentation. + +### Basic Flow + +```ro +flow myFlow { + step first { + prompt "First step" + } + + step second { + prompt "Second step" + } +} +``` + +### Flow with State + +```ro +flow processOrder { + step validate { + prompt "Validate order" + state: { orderId: "12345" } + } + + step process { + prompt "Process order" + state: { orderId: "12345", status: "processing" } + } +} +``` + +### Parallel Steps + +```ro +flow parallelFlow { + parallel step task1 { + prompt "Task 1" + } + + parallel step task2 { + prompt "Task 2" + } + + step combine { + prompt "Combine results" + } +} +``` + +## Tool Calls + +### Calling Tools + +```ro +# Call a tool +result = call calculatorTool("add", 10, 20) + +# Multiple calls +weather = call weatherTool("San Francisco") +temp = call temperatureTool(weather) +``` + +### Tool Results + +Tool calls return values that can be used in expressions: + +```ro +result1 = call calculatorTool("add", 10, 20) +result2 = call calculatorTool("multiply", 5, 6) + +prompt "Calculations: 10 + 20 = {result1}, 5 * 6 = {result2}" +``` + +## Type System + +### Type Annotations + +```ro +# Variable types +name: string = "Alice" +age: number = 30 +isActive: boolean = true + +# Function types +function add(a: number, b: number): number { + return a + b +} +``` + +### Record Types + +```ro +type Person = record { + name: string, + age: number, + email: string +} + +person: Person = { + name: "Alice", + age: 30, + email: "alice@example.com" +} +``` + +### Array Types + +```ro +type NumberArray = number[] +numbers: NumberArray = [1, 2, 3, 4, 5] + +# Generic array type +type PersonList = Person[] +people: PersonList = [ + { name: "Alice", age: 30, email: "alice@example.com" }, + { name: "Bob", age: 25, email: "bob@example.com" } +] +``` + +### Type Inference + +Rohas supports type inference, so explicit types are optional: + +```ro +# Type inferred as string +name = "Alice" + +# Type inferred as number +age = 30 + +# Type inferred as boolean +isActive = true +``` + +## Memory & State + +### Memory Types + +- `short_term` - Temporary memory for the current session +- `long_term` - Persistent memory across sessions + +### Using Memory + +```ro +# Store in memory +prompt "My favorite color is blue" + memory: "long_term" + +# Retrieve from memory +prompt "What's my favorite color?" + memory: "long_term" +``` + +### State Management + +State is passed between flow steps: + +```ro +flow myFlow { + step first { + prompt "First step" + state: { step: 1, data: "initial" } + } + + step second { + prompt "Second step" + state: { step: 2, data: "processed" } + } +} +``` + +## Comments + +```ro +# Single-line comment + +# Multi-line comments use multiple single-line comments +# This is a multi-line comment +# spanning multiple lines +``` + +## Best Practices + +1. **Use type annotations** for better code clarity and error detection +2. **Use const** for values that shouldn't change +3. **Use template literals** for string interpolation +4. **Organize complex logic** into functions +5. **Use flows** for multi-step workflows +6. **Leverage parallel steps** for independent operations +7. **Use memory** to maintain context across prompts +8. **Use state** to pass data between flow steps + +## Error Handling + +Rohas provides static analysis to catch errors before runtime: + +```bash +rohas analyze script.ro +``` + +Common errors include: +- Undefined variables +- Type mismatches +- Invalid function calls +- Missing required parameters + diff --git a/docs/LLM_PROVIDERS.md b/docs/LLM_PROVIDERS.md new file mode 100644 index 0000000..6402137 --- /dev/null +++ b/docs/LLM_PROVIDERS.md @@ -0,0 +1,363 @@ +# LLM Providers + +Rohas supports multiple LLM providers, allowing you to use different AI models based on your needs. + +## Table of Contents + +1. [Supported Providers](#supported-providers) +2. [OpenAI](#openai) +3. [Anthropic](#anthropic) +4. [Ollama](#ollama) +5. [Local Models](#local-models) +6. [Provider Configuration](#provider-configuration) +7. [Model Selection](#model-selection) + +## Supported Providers + +- **OpenAI** - GPT-4, GPT-3.5, and other OpenAI models +- **Anthropic** - Claude 3 Opus, Sonnet, Haiku +- **Ollama** - Local models via Ollama +- **Local** - Custom local model servers + +## OpenAI + +### Setup + +1. Get an API key from [OpenAI](https://platform.openai.com/api-keys) +2. Use the `--provider openai` flag with your API key + +### Usage + +```bash +rohas run script.ro --provider openai --api-key YOUR_API_KEY +``` + +### In Script + +```ro +prompt "Hello, how are you?" + model: "gpt-4" + temperature: 0.7 + maxTokens: 100 +``` + +### Available Models + +- `gpt-4` - Most capable model +- `gpt-4-turbo` - Faster GPT-4 +- `gpt-3.5-turbo` - Fast and cost-effective +- `gpt-4o` - Latest GPT-4 variant + +### Example + +```ro +prompt "Explain quantum computing" + model: "gpt-4" + temperature: 0.7 + maxTokens: 500 +``` + +## Anthropic + +### Setup + +1. Get an API key from [Anthropic](https://console.anthropic.com/) +2. Use the `--provider anthropic` flag with your API key + +### Usage + +```bash +rohas run script.ro --provider anthropic --api-key YOUR_API_KEY +``` + +### In Script + +```ro +prompt "Hello, how are you?" + model: "claude-3-opus-20240229" + temperature: 0.7 + maxTokens: 100 +``` + +### Available Models + +- `claude-3-opus-20240229` - Most capable +- `claude-3-sonnet-20240229` - Balanced performance +- `claude-3-haiku-20240307` - Fast and efficient + +### Example + +```ro +prompt "Write a short story" + model: "claude-3-opus-20240229" + temperature: 0.8 + maxTokens: 1000 +``` + +## Ollama + +Ollama allows you to run models locally on your machine. + +### Setup + +1. Install [Ollama](https://ollama.ai/) +2. Pull a model: `ollama pull llama2` +3. Start Ollama server (usually runs on `http://localhost:11434`) + +### Usage + +```bash +rohas run script.ro --provider ollama --base-url http://localhost:11434 --model llama2 +``` + +### In Script + +```ro +prompt "Hello, how are you?" + model: "llama2" + temperature: 0.7 + maxTokens: 100 +``` + +### Available Models + +Any model available in Ollama: +- `llama2` +- `mistral` +- `codellama` +- `phi` +- And many more... + +### Example + +```ro +prompt "Explain Rust programming" + model: "llama2" + temperature: 0.5 + maxTokens: 300 +``` + +## Local Models + +For custom local model servers (e.g., vLLM, text-generation-inference). + +### Setup + +1. Set up your local model server +2. Ensure it's compatible with OpenAI API format (or use adapter) + +### Usage + +```bash +rohas run script.ro --provider local --base-url http://localhost:8000 --model your-model +``` + +### In Script + +```ro +prompt "Hello, how are you?" + model: "your-model" + temperature: 0.7 + maxTokens: 100 +``` + +### Example + +```ro +prompt "Generate code" + model: "local-model" + temperature: 0.3 + maxTokens: 500 +``` + +## Provider Configuration + +### Command Line + +```bash +# OpenAI +rohas run script.ro --provider openai --api-key KEY --model gpt-4 + +# Anthropic +rohas run script.ro --provider anthropic --api-key KEY --model claude-3-opus-20240229 + +# Ollama +rohas run script.ro --provider ollama --base-url http://localhost:11434 --model llama2 + +# Local +rohas run script.ro --provider local --base-url http://localhost:8000 --model custom-model +``` + +### Environment Variables + +You can also set provider configuration via environment variables: + +```bash +export ROHAS_PROVIDER=openai +export ROHAS_API_KEY=your_key +export ROHAS_MODEL=gpt-4 + +rohas run script.ro +``` + +### Default Provider + +If no provider is specified, Rohas will attempt to use environment variables or default to a local provider if available. + +## Model Selection + +### Per-Prompt Model Selection + +You can specify different models for different prompts: + +```ro +prompt "Simple question" + model: "gpt-3.5-turbo" # Use cheaper model + +prompt "Complex analysis" + model: "gpt-4" # Use more capable model +``` + +### Default Model + +Set a default model via command line: + +```bash +rohas run script.ro --provider openai --api-key KEY --model gpt-4 +``` + +All prompts will use `gpt-4` unless overridden: + +```ro +prompt "Uses gpt-4 (default)" + # model not specified, uses default + +prompt "Uses gpt-3.5-turbo" + model: "gpt-3.5-turbo" # Override default +``` + +## Prompt Options + +All providers support these prompt options: + +### Temperature + +Controls randomness (0.0 to 2.0): + +```ro +prompt "Creative writing" + temperature: 0.9 # More creative + +prompt "Factual answer" + temperature: 0.1 # More focused +``` + +### Max Tokens + +Maximum tokens in response: + +```ro +prompt "Short answer" + maxTokens: 50 + +prompt "Long explanation" + maxTokens: 1000 +``` + +### Stream + +Stream responses (if supported): + +```ro +prompt "Long response" + stream: true + maxTokens: 1000 +``` + +## Provider-Specific Features + +### OpenAI + +- Function calling / tool use +- Streaming +- Multiple model variants + +### Anthropic + +- Long context windows +- Structured outputs +- System prompts + +### Ollama + +- Local execution +- No API costs +- Custom model support + +### Local + +- Full control +- Custom APIs +- Private deployment + +## Best Practices + +1. **Choose the right model** - Use cheaper models for simple tasks, powerful models for complex ones +2. **Set appropriate temperature** - Lower for factual tasks, higher for creative tasks +3. **Limit max tokens** - Set reasonable limits to control costs +4. **Use streaming** - For long responses, use streaming for better UX +5. **Cache responses** - Consider caching for repeated queries +6. **Handle rate limits** - Implement retry logic for production use + +## Troubleshooting + +### API Key Issues + +```bash +# Check API key is set +echo $ROHAS_API_KEY + +# Or pass explicitly +rohas run script.ro --provider openai --api-key YOUR_KEY +``` + +### Connection Issues + +```bash +# Check base URL for local/Ollama +rohas run script.ro --provider ollama --base-url http://localhost:11434 + +# Test connection +curl http://localhost:11434/api/tags # For Ollama +``` + +### Model Not Found + +```bash +# List available models (Ollama) +ollama list + +# Use correct model name +rohas run script.ro --provider ollama --model llama2 +``` + +## Examples + +See the `examples/` directory for provider-specific examples: + +- `hello-world.ro` - Basic prompt +- `template-literal-example.ro` - Template literals with prompts +- `functions-and-await.ro` - Async prompts + +## Cost Considerations + +- **OpenAI**: Pay per token (varies by model) +- **Anthropic**: Pay per token (varies by model) +- **Ollama**: Free (runs locally) +- **Local**: Free (your infrastructure) + +Choose based on your needs: +- Development/testing: Ollama or local models +- Production: OpenAI or Anthropic for reliability +- High volume: Consider local models or caching + From c5185ba5ed9b27dc8104b7f74317d684ea71b1a7 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 14:33:55 +0700 Subject: [PATCH 4/7] fix: update cache key pattern in GitHub Actions workflows to use Cargo.toml for improved dependency management --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64cea8f..02aef69 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13fbb5b..b76e269 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- From 4bd4e3a25c754e22477cef10cf5b79baa4980d79 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 14:36:08 +0700 Subject: [PATCH 5/7] fix: standardize cache key pattern in GitHub Actions workflows to use Cargo.toml for consistency --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02aef69..296946b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b76e269..989d241 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- From 742b5c67b486f393513a84de8217c339476b4741 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 14:39:44 +0700 Subject: [PATCH 6/7] fix: update cache key pattern in GitHub Actions workflows to include Cargo.lock for better cache management --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 296946b..742ba79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 989d241..1b5ba43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- From 77a6d09249aa9cdc46748694d92149644e0f9087 Mon Sep 17 00:00:00 2001 From: sophatvathana Date: Sun, 23 Nov 2025 14:43:53 +0700 Subject: [PATCH 7/7] feat: add Cargo.lock hash retrieval to GitHub Actions workflows for improved caching --- .github/workflows/build.yml | 20 +++++++++++++++++++- .github/workflows/release.yml | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 742ba79..9acdd7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,24 @@ jobs: sudo apt-get update sudo apt-get install -y make perl + - name: Get Cargo lock hash + id: cargo-lock + shell: bash + run: | + if [ -f "Cargo.lock" ]; then + # macOS uses shasum, Linux uses sha256sum, Windows bash has sha256sum + if command -v shasum > /dev/null 2>&1; then + echo "hash=$(shasum -a 256 Cargo.lock | cut -d' ' -f1)" >> $GITHUB_OUTPUT + elif command -v sha256sum > /dev/null 2>&1; then + echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> $GITHUB_OUTPUT + else + # Fallback: use git hash-object if available + echo "hash=$(git hash-object Cargo.lock)" >> $GITHUB_OUTPUT + fi + else + echo "hash=no-lock" >> $GITHUB_OUTPUT + fi + - name: Cache cargo registry uses: actions/cache@v4 with: @@ -63,7 +81,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ steps.cargo-lock.outputs.hash }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b5ba43..5fdee5c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,6 +57,24 @@ jobs: sudo apt-get update sudo apt-get install -y make perl + - name: Get Cargo lock hash + id: cargo-lock + shell: bash + run: | + if [ -f "Cargo.lock" ]; then + # macOS uses shasum, Linux uses sha256sum, Windows bash has sha256sum + if command -v shasum > /dev/null 2>&1; then + echo "hash=$(shasum -a 256 Cargo.lock | cut -d' ' -f1)" >> $GITHUB_OUTPUT + elif command -v sha256sum > /dev/null 2>&1; then + echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> $GITHUB_OUTPUT + else + # Fallback: use git hash-object if available + echo "hash=$(git hash-object Cargo.lock)" >> $GITHUB_OUTPUT + fi + else + echo "hash=no-lock" >> $GITHUB_OUTPUT + fi + - name: Cache cargo registry uses: actions/cache@v4 with: @@ -66,7 +84,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.toml', 'Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ steps.cargo-lock.outputs.hash }} restore-keys: | ${{ runner.os }}-cargo-${{ matrix.target }}-