This document describes LocalScript's components, the session state machine, the generation and validation pipeline, and the optional RAG flow.
- System Overview
- Components
- Request Lifecycle
- Session State Machine
- Generation Pipeline
- Validation Pipeline
- RAG Flow
- Project Layout
LocalScript is a small microservice system with three first-party components and two optional third-party dependencies:
┌──────────┐ ┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐
│ curl / │───▶│ llm-service │───▶│ Ollama │───▶│ sandbox-service│
│ TUI │ │ FastAPI :8080 │ │ qwen2.5-coder │ │ Axum :6778 │
│ │ │ state machine │ │ :11434 │ │ AST + sandbox │
└──────────┘ └────────┬────────┘ └───────────────┘ └────────┬────────┘
│ │
└──────── Plan / Code ──── User ────────────┘
│
(optional)
│
┌──────▼───────┐
│ Qdrant │
│ :6333 (RAG) │
└──────────────┘
All components communicate over plain HTTP/JSON on a private Docker bridge network (backend).
The orchestrator and the only public API surface. Responsibilities:
- Accepts generation requests (
POST /generate) and health checks (GET /health). - Hosts the session state machine and an in-memory session store.
- Drives the LLM through three roles: architect (plans), coder (code), critic (code review).
- Calls
sandbox-serviceto validate every generated or revised code snippet. - Optionally builds RAG context from Qdrant before code generation.
Source structure:
llm-service/app/
├── main.py # FastAPI app, routes /generate and /health
├── config.py # All environment-based configuration
├── api/
│ ├── schemas.py # Pydantic request/response models
│ └── state_machine.py # Session states, transitions, and orchestration
├── core/
│ ├── pipeline.py # GenerationPipeline: plan / code / critique calls
│ └── prompts.py # System prompts for architect, coder, critic
├── clients/
│ ├── ollama.py # Ollama chat API client
│ ├── sandbox.py # sandbox-service client + feedback extraction
│ └── rag.py # Qdrant search + embedding helpers
├── utils/
│ └── json_parser.py # Extracts workflow context JSON from the task text
└── scripts/ # run_model.py, qdrant_delete.py
The code validator. It is not meant to be exposed publicly (see Operational Notes). Responsibilities:
- Parses Lua with tree-sitter and validates the AST (syntax).
- Runs static safety checks (dangerous text patterns, forbidden calls).
- Optionally executes the code in a hardened
mluasandbox with memory and time limits. - Returns structured errors with line numbers and source snippets.
Source structure:
sandbox-service/src/
├── main.rs # Axum server, routes /pipeline and /health
├── models.rs # PipelineRequest / PipelineResponse models
├── routes/
│ └── pipeline.rs # Validation pipeline orchestration
├── ast/
│ ├── parser.rs # tree-sitter Lua parsing + error walking
│ ├── extractor.rs # Function-call extraction from the AST
│ └── safety.rs # Dangerous patterns + forbidden call rules
└── executor/
└── sandbox.rs # mlua runtime sandbox, context injection, error mapping
A terminal client. It talks only to llm-service and renders the session as a chat interface. See tui.md.
Hosts the generation model (qwen2.5-coder:7b by default). A dedicated ollama-init container pulls the model on first startup.
Stores Lua pattern/reference documents for RAG. See RAG Flow.
User ── task ──▶ llm-service
│
├── create session (GENERATING_PLAN)
├── generate plan (architect prompt) ──▶ Ollama
▼
AWAITING_PLAN_CONFIRMATION ◀── plan returned to user
│
user approves / requests edits
│
▼
build RAG context (optional) ──▶ Qdrant
│
├── generate code (coder prompt) ──▶ Ollama
├── validate in sandbox-service (retry loop, up to 20)
├── LLM critique (optional) ──▶ Ollama
▼
AWAITING_CODE_APPROVAL ◀── code returned to user
│
user approves / requests edits
│
▼
DONE
- A client sends a
task(optionally with inline workflow context JSON) toPOST /generate. A new session is created. llm-serviceasks Ollama (architect role) to produce a step-by-step plan and returns it.- The user either approves the plan or sends revision feedback.
- On approval,
llm-serviceoptionally builds RAG context from the plan, then asks Ollama (coder role) to generate Lua code. - The code is sent to
sandbox-service. If validation fails, the code is regenerated with the sandbox's feedback (loop). - Optionally, the code is reviewed by Ollama (critic role), which replies
CODE_OKor a list of issues. - The user approves the final code (or requests more edits). On approval the session transitions to
done.
Sessions are tracked server-side in memory (sessions: dict[str, SessionData]), keyed by session_id.
| State | Description |
|---|---|
generating_plan |
Initial state; plan generation is in progress. |
awaiting_plan_confirmation |
Plan is ready; waiting for approval or edits. |
generating_code |
Code generation and validation in progress. |
awaiting_code_approval |
Code is ready and validated; waiting for approval or edits. |
done |
Code approved; session is complete. |
| From | Condition | To / action |
|---|---|---|
generating_plan |
plan generated | awaiting_plan_confirmation |
awaiting_plan_confirmation |
user_response is an approval word |
generating_code |
awaiting_plan_confirmation |
any other user_response |
plan revision → awaiting_plan_confirmation |
generating_code |
sandbox (+ critic) pass | awaiting_code_approval |
generating_code |
validation fails | awaiting_code_approval (with feedback) |
awaiting_code_approval |
user_response == "подтвердить" |
done |
awaiting_code_approval |
any other user_response |
code revision → awaiting_code_approval |
done |
any request | done (returns the approved code) |
- Plan approval accepts a list of words (case-insensitive):
подтвердить,да,согласен,утверждаю,approve,confirm,yes,ok,хорошо,принять,ок, plus some demo aliases (78,67,docker,борзячка). - Code approval is stricter: only the exact word
подтвердить(case-insensitive) completes the session. Any other response is treated as revision feedback.
Note: server-generated human messages are currently in Russian (e.g.,
"План сгенерирован. Подтвердите или укажите исправления.").
GenerationPipeline (llm-service/app/core/pipeline.py) wraps an OllamaClient and three prompt builders (llm-service/app/core/prompts.py):
| Method | LLM role | Purpose |
|---|---|---|
_generate_plan(task, context) |
architect | Produces a plan, with steps separated by $:. |
_generate_code(plan, task, rag_data, previous_code, critic_feedback, context) |
coder | Generates or revises Lua code. |
_critique_code(code, rag_data, context) |
critic | Reviews the code; returns CODE_OK or issues. |
Prompts encode "LowCode environment" rules the model must follow, for example:
- Access workflow data via the global
wftable (wf.vars,wf.initVariables), not JsonPath. - Iterate arrays with
for _, item in ipairs(...) do ... end. - Store newly created variables under
wf.vars. - Use
table.removeinstead of niling array holes; avoid the#operator on arrays with holes.
The model and Ollama endpoint are configured via GENERATION_MODEL and OLLAMA_URL (see configuration.md).
After code is generated (or revised), llm-service calls sandbox-service (POST /pipeline). The sandbox pipeline has four stages:
- Parse — tree-sitter parses the code; syntax errors are reported with line/column and a source snippet.
- Static safety — text-level dangerous patterns and AST-level forbidden calls are checked.
- Execute (optional, controlled by
execute) — the code runs in a hardenedmluasandbox. - Report — a structured response with status, logs, AST analysis, and execution stats.
The sandbox retry loop in llm-service (validate_code) regenerates code with feedback for up to CODE_RETRIES_SANDBOX (default 20) attempts. When execute is off, the response still returns AST analysis.
Detailed security rules: sandbox-security.md.
With llm_validation: true (the default), llm-service asks Ollama (critic role) to review the code. The response is compared against CONFIRM_WORD (default CODE_OK), case-insensitively. If it matches, the code proceeds to the user; otherwise, the critic's comments are returned as sandbox_feedback for the user to act on.
The llm_validation flag can be set to false per request to skip this step (see api-reference.md).
RAG is optional and degrades gracefully — if Qdrant or the embeddings endpoint is unreachable, generation continues without reference context.
- When the plan is approved,
llm-servicesplits the plan into chunks on$:markers. - Each chunk is embedded via an OpenAI-compatible embeddings endpoint (
EMBEDDINGS_URL, modelbge-m3). - The embedding searches the Qdrant collection (
QDRANT_COLLECTION, defaultlua_patterns, top-1 result). - Matched documents (
description+validation_checklistpayload fields) are concatenated intorag_data. rag_datais injected into the coder prompt asReference dataand into the critic prompt.
Implementation: llm-service/app/clients/rag.py. Required configuration: configuration.md.
.
├── docker-compose.yml # Service orchestration (ollama, qdrant, sandbox, api, tui)
├── README.md # Project overview and quick start
├── LICENSE # Apache 2.0
├── llm-service/ # FastAPI orchestrator (Python 3.12)
├── sandbox-service/ # Lua sandbox validator (Rust)
├── llm-tui/ # Terminal client (Rust / Ratatui)
├── qdrant_data/ # Qdrant storage (bind mount, persists)
└── docs/ # This documentation