Specification
Overview
LukiScript is a declarative DSL for describing how an LLM searches, not just what it answers.
Overview
LukiScript is a declarative DSL for describing how an LLM searches, not just what it answers.
What is LukiScript
LukiScript is a declarative DSL for describing how an LLM searches, not just what it answers. It is named after Wittgenstein's childhood nickname.
Search strategies — Tree of Thoughts, debate, recursive refinement, or custom patterns — are expressed in YAML configs. Every stilt config is validated, versioned, and shareable. Deployed stilts expose themselves as OpenAI-compatible API endpoints, so anything that talks to OpenAI can talk to a stilt by swapping the base URL.
Mental Model
A stilt is a search strategy written in LukiScript. It is defined in YAML, saved under an account, and called via API. The caller receives a response in standard OpenAI-compatible format — the internal execution is not exposed.
Stilts are created in Foundry — either through the visual step editor or by pasting YAML directly. The stilt is saved under the author's account. Visibility can be set to private, unlisted, or public.
When a stilt runs, Studio and Foundry render a live timeline — each step appears as a node as it executes. Intermediate outputs are inspectable.
The components of a stilt:
Steps are the building blocks. Each step makes one or more LLM calls. Steps run in order, and a step can read outputs from any previous step. Three types are available: normal (parallel nodes), sequential (nodes run one after another), and group (multiple sibling steps running side by side).
Fields define what goes into each step's prompt. Each field has a name and a source. When the stilt runs, every field resolves to a concrete value and gets rendered like this:
Query: What is the meaning of life?
Evidence 1: First source output here
Evidence 2: Second source output here
Node Number: 3
Branch Count: 5
[System Instruction]
You are an evaluator. Score each answer on a scale of 1-10.Fields pull from user input (text), from another step's output (ingest), from multiple outputs at once (multi_ingest), or inject runtime values like the current node number (nodeInfo) or a knob value (knobInfo).
Knobs are caller-tunable controls. They appear as sliders or number inputs when someone runs the stilt — branch count, iteration depth, coverage breadth, etc. The config defines the range and defaults; the caller adjusts at call time.
Nodes control fanout. A step with 5 nodes makes 5 parallel LLM calls — each one gets its own node number (1, 2, 3, 4, 5) and produces its own output. The node count can be hardcoded, driven by a knob, or derived from a previous step's output.
Loops repeat the entire steps sequence N times. Each loop can read outputs from previous loops. Loop 2 can read what loop 1 produced, loop 3 can read loops 1 and 2, and so on.
Gates prune branches. A step with continueIf checks each node's output against an expected value. Nodes that don't match are pruned — downstream steps won't see them.
Recursion lets a step spawn a child stilt. The step's output becomes the child's input, the child runs its own full execution, and the result bubbles back up. The child can itself recurse, up to maxDepth.
Groups run sibling steps in parallel. A group contains two or more steps that execute simultaneously — for independent analysis tasks that feed into a shared downstream step.
Timeline controls what's visible. Steps marked with timeline: "circle" appear as dots in Studio's timeline. Steps without a timeline marker still execute but are invisible in the UI.
Core Concepts at a Glance
| Concept | What it controls | Summary |
|---|---|---|
| Steps | Execution units | Each step = one or more LLM calls |
| Fields | Data flow | How steps read from inputs and each other |
| Knobs | Runtime controls | Caller-adjustable parameters (slider or numerical) |
| Nodes | Fanout | How many parallel LLM calls a step makes |
| Loops | Iteration | Repeat all steps N times, carry context forward |
| Gates | Pruning | Filter branches by output match (continueIf) |
| Recursion | Depth | Spawn child stilts from a step's output, up to maxDepth |
| Groups | Parallelism | Run sibling steps simultaneously |
| Timeline | Observability | Mark steps as visible dots in Studio |
Not every stilt uses all of these. A minimal stilt is one step with one field and no knobs. More complex stilts combine groups, multi-node fanout, gates, loops, and recursion to implement multi-round search strategies.
The Runtime Lifecycle
When a stilt is called via the API:
The config is parsed and validated — every field reference must point to a real step, every knob reference must resolve, and the exit step must exist.
Knobs are resolved from the caller's values, falling back to defaults. Slider positions map to their numeric values. Numerical knobs are clamped to their min/max range.
Steps execute in order. For each step, the node count is resolved, every field is resolved to a concrete string, the prompt is assembled, and the LLM call(s) are made. Outputs are stored keyed by loop index, step id, and node number.
If a step has a continueIf gate, outputs are filtered — non-matching nodes are pruned. If a step has recursion, a child stilt runs and its result replaces the step's output.
After all steps complete, the loop index advances. Steps in the next loop can reference outputs from previous loops. This repeats until maxLoops is reached.
The output of the exit step from the last loop is returned as the stilt's response, in standard OpenAI-compatible format.
Minimal Example
The smallest valid stilt — one step, one field, no knobs:
id: hello
name: Hello Stilt
author: Redeo Labs
version: 1
allowedTargets:
strategy: universal
exit: step0
knobs: {}
steps:
- id: step0
name: Answer
type: normal
fields:
- name: Context
type: text
from: input.context
systemPrompt: "Answer clearly and directly."Execution:
- One step (
step0) with nonodes, so exactly one LLM call. - The single field (
Context) readsinput.contextfrom the caller's message and renders asContext: <the user's text>. - The system prompt is appended as
[System Instruction]. - The assembled prompt goes to the LLM, the response is stored as
step0's output. - Since
exit: step0, that output is the stilt's final answer.
From Config to Running Stilt
The full journey from writing a stilt to getting a response:
Write. Create a stilt in Foundry's visual editor — add steps, define fields, set system prompts, configure knobs — or paste raw YAML directly. Foundry validates the config during editing.
Save. Save the stilt. Set visibility: private (only the author), unlisted (anyone with the link), or public (visible in the library).
Call. Hit the stilt's endpoint using the standard OpenAI chat completions format. The stilt is addressed by the author's username and the stilt's slug name in the URL path:
curl https://api.redeo.ai/v1/redeo-labs/hello/chat/completions \
-H "Authorization: Bearer $REDEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Explain quantum entanglement"}]
}'The URL path /v1/{author}/{stilt}/chat/completions identifies which stilt to run. The last user message becomes input.context inside the stilt. The model field must be allowed by the stilt's allowedTargets policy.
Respond. The exit step's output from the final loop is returned in standard OpenAI-compatible format.