-
Notifications
You must be signed in to change notification settings - Fork 0
Calls and PreCalls
In FML, the call instruction is used to run deterministic operations—including direct tool invocations, database mutations, and sandboxed JavaScript transformations—prior to LLM prompt execution.
-
Deterministic Execution: Unlike LLM sessions which are generative and non-deterministic,
callblocks execute exact code or API requests with 100% predictability. -
Synchronous & Sequential: When multiple
callblocks exist in a file or within a session, they execute in the exact order of declaration. - Fail-Fast Error Handling: Any unhandled exception, tool failure, or script error halts execution immediately. The containing session and entire plan are aborted.
-
Scoping:
- Global Calls: Executed once before any sessions begin. Must specify an output target routing.
- Session Calls (PreCalls): Executed during Phase 1 of the session lifecycle before pre-prompts or prompts run.
The arrow operator (->) routes the output of a call to a destination namespace.
# 1. Variables Routing (Explicit)
call("fetch_user") -> vars:user_profile {
user_id = "{{ .params.user_id }}"
}
# 2. Variables Routing (Implicit - omitting namespace prefix defaults to vars)
call("fetch_settings") -> app_settings {
env = "prod"
}
# 3. Context Routing (Direct injection into context bus)
call("cache_data") -> context:analytics_cache {
id = "daily_snapshot"
}
# 4. Omitted Target (Session-level only)
call("inject_ambient_telemetry") {
region = "{{ .params.region }}"
}
| Routing Syntax | Destination | Scope | Notes |
|---|---|---|---|
-> vars:name |
vars.name |
Local to session (or global if at root) | Recommended pattern for intermediate state. |
-> name |
vars.name |
Local to session (or global if at root) | Implicitly defaults to vars:name. |
-> context:key |
context.key |
Available to all downstream sessions | Direct publish. Use with care. |
| Omitted | Ambient Context | Current session LLM context only | Session-level only. Directly visible to the LLM without requiring variable references. |
Caution
Colon Syntax Requirement: Always use a colon (:) between the namespace and variable name: -> vars:my_data. Using a dot (e.g. -> vars.my_data) is a syntax error.
In addition to calling named tools, call blocks can execute embedded JavaScript via the code(...) construct. This provides a lightweight sandboxed environment for manipulating data without LLM overhead.
Inputs declared in the call block body are accessible inside JavaScript via the args object. Programmatic tool calls can be made synchronously using runFunction(name, params).
FML supports two distinct JavaScript notation formats:
Ideal for quick transformations, array mappings, or simple filtering.
- Wrap the entire code block in outer parentheses:
code( ( ... ) ). -
Do NOT use
returnstatements at the top level. - Terminate multi-line statements with semicolons (
;). - The last evaluated expression becomes the return value of the call.
call("sanitize_and_filter_users") -> vars:active_emails {
user_list = $(context.fetch_directory.users)
code(
(
const users = args.user_list || [];
const active = users.filter(u => u.status === "ACTIVE");
active.map(u => u.email); # Completion value!
)
)
}
Required for scripts with complex control flow, loops, early returns, or multi-step logic.
- Wrap the script in an IIFE:
code( ( (() => { ... })() ) ). - Standard JavaScript rules apply: use standard
returnstatements,if/else, loops, and local variable declarations. - Ideal for programmatic tool execution loops.
call("bulk_sync_contacts") -> vars:sync_summary {
new_emails = $(context.extract_attendees.emails)
code(
(
(() => {
if (!args.new_emails || args.new_emails.length === 0) {
return { synced: 0, skipped: 0 };
}
# Synchronous tool call inside script
const existing = runFunction("query_contacts", { filter: "all" });
const existingSet = new Set(existing.map(c => c.email));
let syncedCount = 0;
let skippedCount = 0;
for (const email of args.new_emails) {
if (existingSet.has(email)) {
skippedCount++;
} else {
runFunction("create_contact", { email: email });
syncedCount++;
}
}
return {
synced: syncedCount,
skipped: skippedCount,
total: args.new_emails.length
};
})()
)
)
}
Inside both code(...) formats, you can synchronously call any registered tool function using:
const result = runFunction("tool_function_name", { arg1: "val1", arg2: 123 });- Synchronous: Execution pauses until the tool responds.
- Return Value: Parsed JSON / native JavaScript object or array returned by the tool.
- Errors: If the tool throws an error or fails, the script halts and the plan aborts.
call("process_payload") -> vars:output {
# 1. String interpolation with Go Templates (double curly braces)
search_query = "{{ .params.query }}"
environment = "{{ .vars.env }}"
# 2. Native types with Antonmedv Expr (wrapped in $(...))
raw_array = $(context.upstream_session.items)
max_count = $(params.limit)
is_enabled = $(vars.flag)
}
The following example demonstrates a two-step PreCall pipeline inside a session: Step 1 retrieves raw telemetry, Step 2 cleans and filters it using JavaScript, and only the cleaned data is exposed to the LLM:
session("audit_service_health", target="health_report") {
# PreCall 1: Direct Tool Call
call("fetch_raw_logs") -> vars:raw_logs {
service_name = "{{ .params.service }}"
limit = 500
}
# PreCall 2: Deterministic Data Cleansing (IIFE)
call("filter_fatal_errors") -> vars:critical_events {
log_records = $(vars.raw_logs)
code(
(
(() => {
const logs = args.log_records || [];
return logs
.filter(entry => entry.level === "FATAL" || entry.level === "CRITICAL")
.map(entry => ({
time: entry.timestamp,
message: entry.message,
trace: entry.stack_trace ? entry.stack_trace.slice(0, 200) : ""
}));
})()
)
)
}
# Phase 2: Pre-prompt (LLM reasons over the pre-filtered events)
+ Review the critical events in vars:
{{ .vars.critical_events | json }}
Assess the primary failure root cause.
# Phase 3: Prompt (Tool calls disabled, strict JSON output)
- Produce the service health summary.
schema {
service: string
critical_count: int
primary_root_cause: string
recommended_action: string
}
}
FML (Frags Modeling Language) | Getting Started | Cheat Sheet | Examples
Documentation for FML & Gemini Agent Workflows — Maintained by Frags HQ