LocalScript exposes two HTTP services. This document is the complete API contract for both.
| Service | Base URL (default) | Port |
|---|---|---|
llm-service |
http://localhost:8080 |
8080 |
sandbox-service |
http://localhost:6778 |
6778 |
- llm-service:
GET /health - llm-service:
POST /generate - sandbox-service:
GET /health - sandbox-service:
POST /pipeline - Worked Example
Returns service status and the active session count.
curl -s http://localhost:8080/healthResponse 200:
{
"status": "ok",
"active_sessions": 2,
"pipeline_model": "qwen2.5-coder:7b"
}| Field | Type | Description |
|---|---|---|
status |
string | Always "ok" when the process is up. |
active_sessions |
integer | Number of in-memory sessions currently tracked. |
pipeline_model |
string | The configured GENERATION_MODEL. |
Creates a new generation session or continues an existing one. This is the primary API.
{
"session_id": "string (optional)",
"task": "string",
"user_response": "string (optional)",
"llm_validation": true
}| Field | Type | Default | Description |
|---|---|---|---|
session_id |
string | null | null |
Omit on the first call. Reuse the returned value to continue a session. |
task |
string | "" |
The natural-language task. Required for a new session. May include inline workflow-context JSON (see Task Context). |
user_response |
string | "" |
The user's answer to the current state: approval or revision feedback. |
llm_validation |
boolean | true |
When false, skips the LLM critic step (sandbox validation still runs). |
Errors:
| Status | Condition |
|---|---|
400 |
task is missing/empty and no existing session_id was provided. |
500 |
The session is in an unexpected state. |
{
"session_id": "string",
"state": "string",
"plan": "string | null",
"code": "string | null",
"sandbox_feedback": "string | null",
"message": "string"
}| Field | Type | Description |
|---|---|---|
session_id |
string | The session identifier; reuse it in the next request. |
state |
string | One of the session states. |
plan |
string | null | The generated plan (present when a plan was produced). |
code |
string | null | The generated/revised Lua code (present when code exists). |
sandbox_feedback |
string | null | Validation/critique feedback when the code did not pass, null otherwise. |
message |
string | Human-readable guidance for the next step (currently in Russian). |
| State | Meaning |
|---|---|
generating_plan |
Plan generation in progress (transient). |
awaiting_plan_confirmation |
Plan ready — send an approval word or revision feedback. |
generating_code |
Code generation + validation in progress (transient). |
awaiting_code_approval |
Code ready and validated — send approval or revision feedback. |
done |
Code approved. The response returns the final code. |
- Plan approval (case-insensitive):
подтвердить,да,согласен,утверждаю,approve,confirm,yes,ok,хорошо,принять,ок(plus demo aliases78,67,docker,борзячка). Any other value is treated as revision feedback. - Code approval (case-insensitive): only
подтвердить. Any other value triggers a code revision.
The task field may embed a JSON object describing workflow state. llm-service extracts the JSON, strips it from the task text, normalizes it to the {"wf": {...}} shape, and injects it into the sandbox as the global wf table.
Example task:
Clean the values of variables ID, ENTITY_ID, CALL
{"wf": {"vars": {"RESTbody": {"result": [{"ID": 123}]}}, "initVariables": {}}}
Within generated Lua code, the data is available as:
wf.vars.<name>— mutable workflow variables.wf.initVariables.<name>— read-only input parameters.
curl -s http://localhost:6778/healthResponse 200: plain text ok.
Validates (and optionally executes) a Lua snippet. This is the endpoint llm-service uses internally, but it can be called directly for testing or integration.
{
"code": "string",
"execute": true,
"timeout": 2,
"context": {
"wf": {
"vars": {},
"initVariables": {}
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
code |
string | — (required) | The Lua source code to validate. |
execute |
boolean | false |
If true, run the code in the runtime sandbox. If false, only static checks are performed. |
timeout |
integer | 2 |
Execution timeout in seconds, clamped to [1, 10]. |
context |
object | null | null |
Injected as the global wf table (wf.vars, wf.initVariables). Defaults to empty wf if omitted. |
{
"status": "ok",
"source_code": "string",
"output": "string | null",
"logs": ["string"],
"warnings": ["string"],
"error_detail": { "kind": "string", "message": "string", "line": 2, "raw": "string", "snippet": "string | null" } | null,
"ast_analysis": {
"function_calls": ["string"],
"has_dangerous_patterns": false,
"has_forbidden_calls": false
} | null,
"execution_stats": {
"memory_used_bytes": 12345,
"execution_time_ms": 2
} | null
}| Field | Type | Description |
|---|---|---|
status |
string | ok, syntax_error, safety_error, runtime_error, or timeout. |
source_code |
string | Echo of the submitted code. |
output |
string | null | Values returned by the script (only when execute is true and execution succeeds). |
logs |
string[] | Sandbox log lines ([stdout], [warn], [exec], [error], [fatal]). |
warnings |
string[] | Non-fatal notices (e.g., timeout normalization, execution skipped). |
error_detail |
object | null | Structured error; null on success. |
ast_analysis |
object | null | Extracted function calls and safety flags; present when parsing succeeded. |
execution_stats |
object | null | Memory used and execution time; present when execution ran. |
| Kind | Meaning |
|---|---|
syntax_error |
Code could not be parsed. |
safety_error |
Dangerous text pattern or forbidden call detected. |
runtime_error |
Lua runtime error. |
timeout |
Execution exceeded the configured timeout. |
memory_limit |
Execution exceeded the 8 MB memory limit. |
stack_overflow |
Lua C-stack overflow. |
forbidden_access |
Attempted to access a disabled global (os, io, …). |
unknown |
Unclassified error. |
error_detail.line and error_detail.snippet point at the offending location; the snippet highlights the error line with >>>.
Successful execution:
{
"status": "ok",
"source_code": "return 1 + 1",
"output": "2",
"logs": [
"[exec] starting, timeout=2s",
"[exec] code: 12 bytes",
"[exec] memory used: 4096 bytes"
],
"warnings": [],
"error_detail": null,
"ast_analysis": {
"function_calls": [],
"has_dangerous_patterns": false,
"has_forbidden_calls": false
},
"execution_stats": {
"memory_used_bytes": 4096,
"execution_time_ms": 1
}
}Forbidden call:
{
"status": "safety_error",
"source_code": "os.execute('rm -rf /')",
"output": null,
"logs": [],
"warnings": [],
"error_detail": {
"kind": "safety_error",
"message": "dangerous text pattern found: rm -rf, forbidden function call found: os.execute",
"line": null,
"raw": "dangerous text pattern found: rm -rf, forbidden function call found: os.execute",
"snippet": null
},
"ast_analysis": {
"function_calls": ["os.execute"],
"has_dangerous_patterns": true,
"has_forbidden_calls": true
},
"execution_stats": null
}A full session via llm-service, from new task to final approval.
Step 1 — create session, get a plan:
curl -sS -X POST http://localhost:8080/generate \
-H "Content-Type: application/json" \
-d '{"task":"Write a Lua function that filters a table of orders by total >= 100"}'{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"state": "awaiting_plan_confirmation",
"plan": "$: Define the input handling\n$: Filter items where total >= 100\n$: Return the filtered table",
"code": null,
"sandbox_feedback": null,
"message": "План сгенерирован. Подтвердите или укажите исправления."
}Step 2 — request a plan revision (optional):
curl -sS -X POST http://localhost:8080/generate \
-H "Content-Type: application/json" \
-d '{"session_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","user_response":"Also compute the sum of filtered totals"}'Step 3 — approve the plan, receive validated code:
curl -sS -X POST http://localhost:8080/generate \
-H "Content-Type: application/json" \
-d '{"session_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","user_response":"approve"}'{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"state": "awaiting_code_approval",
"plan": null,
"code": "local filtered = {}\nfor _, order in ipairs(wf.vars.orders or {}) do\n if order.total >= 100 then\n table.insert(filtered, order)\n end\nend\nreturn filtered",
"sandbox_feedback": null,
"message": "Код прошёл проверки. Подтвердите или укажите исправления."
}Step 4 — approve the code, session completes:
curl -sS -X POST http://localhost:8080/generate \
-H "Content-Type: application/json" \
-d '{"session_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","user_response":"подтвердить"}'{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"state": "done",
"plan": null,
"code": "local filtered = {}\nfor _, order in ipairs(wf.vars.orders or {}) do\n if order.total >= 100 then\n table.insert(filtered, order)\n end\nend\nreturn filtered",
"sandbox_feedback": null,
"message": "Код одобрен. Генерация завершена."
}Because LLM output is non-deterministic, the actual plan/code text will differ. The shape of the responses is what matters.