Sculpt job
A sculpt job (pipeline/sculpt/llm_blender.js, sculptWithLlm) is the LLM-driven
build loop: prompt → the model writes bpy code → execute through the
Blender session → iterate → export GLB → run the
verification gate. The model is the brain; every
execution still goes through the Blender MCP layer, and the model key comes
from Skarbiec like every other secret.
Entry points: glina sculpt <prompt> (which Glina Desktop runs too) and MCP
tool glina_sculpt.
Definition and shape
Job inputs: prompt, outDir (default assets/models), filename
(default: slugified prompt + .glb), maxRounds (the request's value, else
llm.maxRounds; with neither the job refuses sculpt needs a round cap: pass --rounds <n> or set llm.maxRounds in the pipeline config before Blender
starts). Result: { outPath, verification, rounds } — the CLI
strips the transcript from what it prints.
Each model reply must be one JSON object:
{ "thought": "one short sentence", "code": "python bpy code", "done": false }
The reply parser tolerates fences and surrounding prose (first balanced JSON
object wins) but a round with no usable JSON fails the job:
model reply not usable in round N: <reason>.
The system prompt's execution contract
The model is told, verbatim in spirit (see SYSTEM_PROMPT):
- each block executes in a fresh namespace containing only
bpy— nothing persists between calls except Blender data itself, - never use
bpy.contextfor object access; work throughbpy.data, - never call
bpy.ops.wm.read_factory_settings— it kills the bridge, - flat shading, material colors only, Thronefall-style chunky low-poly; the
triangle budget is named only when
verify.triTargetstates one, - when animations are required: one armature, automatic weights, named
actions (
idleplus characteristic motions; as many asverify.minAnimationClipsstates) keyframed over a short range of frames, - reply
done: truewith empty code when finished.
Animations become required in the job when verify.requireAnimations is
true or verify.minAnimationClips > 0 — the requirement is injected into the
first user message, since the gate will refuse static meshes anyway.
Round loop
- Ask the model with
llm.maxTokens(required; refused assculpt needs a reply length: set llm.maxTokens in the pipeline config). - Execute
step.codeif non-empty. Execution errors are fed back to the model asERROR: <message>— the model gets a chance to repair its own bug. - Bridge-down rule: after a failed block the bridge is asked for its
tools; when it refuses, the run ends with
Blender bridge is down: <reason>; the bridge then refused listTools too: <reason>— the model cannot heal a dead bridge. - If the Blender MCP server exposes
get_viewport_screenshot, a screenshot is attached to the next round (best-effort; the loop works text-only). done: truebreaks the loop. Running out of rounds fails the job:model did not finish within N rounds.
Export and gate
The scene is exported with exportGlb and then, unless
verify.enabled: false, gated with throwOnFail — a failing asset raises
asset failed verification: <reasons> and the job fails. The Blender session
is closed in a finally either way.