From 94ca51a670cfa664ba7abde24991bf831dac4fbd Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Mon, 16 Mar 2026 23:28:12 -0300 Subject: [PATCH] Docs: add execution-modes.md (RUNTIME vs WORKFLOW), update HEAR and script-execution-flow --- src/04-basic-scripting/execution-modes.md | 116 ++++++++++++++++++ src/04-basic-scripting/keyword-hear.md | 11 ++ .../script-execution-flow.md | 2 + src/SUMMARY.md | 1 + 4 files changed, 130 insertions(+) create mode 100644 src/04-basic-scripting/execution-modes.md diff --git a/src/04-basic-scripting/execution-modes.md b/src/04-basic-scripting/execution-modes.md new file mode 100644 index 00000000..fce7361f --- /dev/null +++ b/src/04-basic-scripting/execution-modes.md @@ -0,0 +1,116 @@ +# BASIC Execution Modes: RUNTIME vs WORKFLOW + +General Bots BASIC scripts run in one of two execution modes. The mode is selected by a pragma at the top of the `.bas` file. + +## Quick Comparison + +| Feature | RUNTIME mode (default) | WORKFLOW mode ⚗️ | +|---------|----------------------|-----------------| +| **Pragma** | _(none)_ | `#workflow` | +| **Engine** | Rhai AST | Step engine + PostgreSQL | +| **HEAR behavior** | Blocks a thread | Suspends to DB, zero threads | +| **Server restart** | Loses position | Resumes exact step | +| **Side-effect re-run** | ❌ possible on crash | ✅ never | +| **Multi-day flows** | ❌ (1h timeout) | ✅ unlimited | +| **FOR EACH loops** | ✅ | ❌ | +| **FUNCTION / SUB** | ✅ | ❌ | +| **USE WEBSITE** | ✅ | ❌ | +| **Startup time** | ~1ms | ~2ms | +| **RAM per session** | 1 thread (~64KB) | 0 threads | +| **Observability** | Logs only | DB rows (queryable) | +| **Best for** | Tools, short dialogs | Multi-step dialogs, tickets, approvals | + +--- + +## RUNTIME Mode (default) + +Every `.bas` file without `#workflow` runs in RUNTIME mode. The script compiles to a Rhai AST and executes in a `spawn_blocking` thread. `HEAR` blocks the thread until the user replies (up to `hear-timeout-secs`, default 3600). + +```basic +' ticket.bas — RUNTIME mode (no pragma) +TALK "Describe the issue" +HEAR description ' blocks thread, waits +SET ticket = CREATE(description) +TALK "Ticket #{ticket} created" +``` + +**When to use:** Tool scripts called by LLM, short dialogs (< 10 minutes), scripts using `FOR EACH`, `FUNCTION`, or `USE WEBSITE`. + +--- + +## WORKFLOW Mode ⚗️ + +> **Status: Planned feature** — see `botserver/WORKFLOW_PLAN.md` + +Add `#workflow` as the first line. The compiler produces a `Vec` instead of a Rhai AST. Each step is persisted to `workflow_executions` in PostgreSQL before execution. On `HEAR`, the engine saves state and returns — no thread held. On the next user message, execution resumes from the exact step. + +```basic +#workflow +' ticket.bas — WORKFLOW mode +TALK "Describe the issue" +HEAR description ' saves state, returns, zero threads +SET ticket = CREATE(description) +TALK "Ticket #{ticket} created" +``` + +**When to use:** Multi-step dialogs, ticket creation, approval flows, anything that may span minutes or days. + +### Keyword compatibility in WORKFLOW mode + +| Category | Keywords | WORKFLOW support | +|----------|----------|-----------------| +| **Dialog** | `TALK`, `HEAR`, `WAIT` | ✅ | +| **Data** | `SET`, `GET`, `FIND`, `SAVE`, `INSERT`, `UPDATE`, `DELETE` | ✅ | +| **Communication** | `SEND MAIL`, `SEND TEMPLATE`, `SMS` | ✅ | +| **AI** | `USE KB`, `USE TOOL`, `REMEMBER`, `THINK KB` | ✅ | +| **HTTP** | `GET` (http), `POST`, `PUT`, `PATCH`, `DELETE` (http) | ✅ | +| **Scheduling** | `SCHEDULE`, `BOOK`, `CREATE TASK` | ✅ | +| **Expressions** | `FORMAT`, math, datetime, string functions | ✅ (via Rhai eval) | +| **Control flow** | `IF/ELSE/END IF` | ✅ | +| **Loops** | `FOR EACH / NEXT` | ❌ use RUNTIME | +| **Procedures** | `FUNCTION`, `SUB`, `CALL` | ❌ use RUNTIME | +| **Browser** | `USE WEBSITE` | ❌ use RUNTIME | +| **Events** | `ON EMAIL`, `ON CHANGE`, `WEBHOOK` | ❌ use RUNTIME | + +### How WORKFLOW compiles + +The compiler does **not** use Rhai for workflow mode. It is a line-by-line parser: + +``` +TALK "Hello ${name}" → Step::Talk { template: "Hello ${name}" } +HEAR description → Step::Hear { var: "description", type: "any" } +SET x = score + 1 → Step::Set { var: "x", expr: "score + 1" } +IF score > 10 THEN → Step::If { cond: "score > 10", then_steps, else_steps } +SEND MAIL to, s, body → Step::SendMail { to, subject, body } +``` + +Expressions (`score + 1`, `score > 10`) are stored as strings and evaluated at runtime using Rhai as a pure expression calculator — no custom syntax, no side effects. + +### Observability + +In WORKFLOW mode, every step is a DB row. You can query execution state directly: + +```sql +SELECT script_path, current_step, state_json, status, updated_at +FROM workflow_executions +WHERE session_id = '' +ORDER BY updated_at DESC; +``` + +--- + +## Choosing a Mode + +``` +Does the script use FOR EACH, FUNCTION, or USE WEBSITE? + YES → RUNTIME (no pragma) + +Does the script have HEAR and may run for > 1 hour? + YES → WORKFLOW (#workflow) + +Is it a tool script called by LLM (short, no HEAR)? + YES → RUNTIME (no pragma) + +Is it a multi-step dialog (ticket, approval, enrollment)? + YES → WORKFLOW (#workflow) ⚗️ when available +``` diff --git a/src/04-basic-scripting/keyword-hear.md b/src/04-basic-scripting/keyword-hear.md index 1692e79f..f7f1d26c 100644 --- a/src/04-basic-scripting/keyword-hear.md +++ b/src/04-basic-scripting/keyword-hear.md @@ -2,6 +2,17 @@ The `HEAR` keyword pauses script execution and waits for user input. With optional type validation, it automatically verifies and normalizes input, retrying with helpful error messages when validation fails. +## Execution behavior by mode + +| Mode | HEAR behavior | Thread held | Crash-safe | +|------|--------------|-------------|------------| +| **RUNTIME** (default) | Blocks `spawn_blocking` thread | Yes (up to `hear-timeout-secs`) | No | +| **WORKFLOW** ⚗️ | Saves state to DB, returns immediately | No | Yes | + +In RUNTIME mode, the script thread is suspended — not re-run from the top. All code above `HEAR` does **not** execute again when the user replies. The timeout is configurable via `hear-timeout-secs` in `config.csv` (default: 3600 seconds). + +In WORKFLOW mode (`#workflow` pragma), `HEAR` persists the current step and all variables to PostgreSQL and returns. The thread is released. When the user replies, execution resumes from the exact `HEAR` line. See [Execution Modes](./execution-modes.md). + General Bots ## Basic Syntax diff --git a/src/04-basic-scripting/script-execution-flow.md b/src/04-basic-scripting/script-execution-flow.md index 7ede5963..92f07a7a 100644 --- a/src/04-basic-scripting/script-execution-flow.md +++ b/src/04-basic-scripting/script-execution-flow.md @@ -2,6 +2,8 @@ Understanding how General Bots BASIC scripts are loaded, compiled, and executed is essential for building effective automation. This document covers the complete execution lifecycle. +> **Two execution modes exist:** RUNTIME (default) and WORKFLOW. See [Execution Modes](./execution-modes.md) for the full comparison. + ## Execution Entry Points Scripts in General Bots can be triggered through several entry points: diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 1808cd37..7de81651 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -73,6 +73,7 @@ - [Chapter 4: BASIC Scripting](./04-basic-scripting/README.md) - [BASIC Basics](./04-basic-scripting/basics.md) + - [Execution Modes: RUNTIME vs WORKFLOW](./04-basic-scripting/execution-modes.md) - [API Possibilities](./04-basic-scripting/api-possibilities.md) - [Universal Messaging](./04-basic-scripting/universal-messaging.md) - [BASIC vs n8n/Zapier/Make](./04-basic-scripting/basic-vs-automation-tools.md)