Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/api/test-pipeline/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ const MCP_TOOLS = [
"get_all_object_info",
"get_viewport_screenshot",
"execute_code",
"inspect_blend_file_health",
"inspect_modifier_constraint_stack",
"inspect_rigging_data",
"inspect_weight_paint_readiness",
"inspect_animation_data",
"inspect_collection_hierarchy",
"inspect_viewport_areas",
"set_viewport_shading",
"focus_viewport_on_objects",
"select_scene_objects",
"set_active_collection",
"create_mesh_from_data",
"validate_mesh_geometry",
"inspect_retopology_readiness",
"normalize_vertex_group_weights",
"configure_modifier",
"configure_constraint",
"add_object_constraint",
"remove_object_constraint",
"get_local_asset_library_status",
"search_local_assets",
"import_local_asset",
Expand All @@ -50,6 +69,14 @@ const MCP_TOOLS = [
"get_sketchfab_status",
"search_sketchfab_models",
"download_sketchfab_model",
"create_material_preset",
"inspect_material_node_graph",
"prepare_uv_layout",
"validate_export_readiness",
"setup_studio_scene",
"validate_studio_scene",
"render_thumbnail_to_path",
"render_viewport_to_path",
]

export async function GET(req: NextRequest) {
Expand Down
25 changes: 13 additions & 12 deletions lib/ai/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,36 @@ export const PLANNING_SYSTEM_PROMPT = `You are ModelForge's orchestration planne

ARCHITECTURE (understand this before planning):
- You output a JSON plan with human-readable step descriptions — you NEVER write Python code.
- For execute_code steps, a separate AI code-generator will produce the Python from YOUR description.
- Prefer direct tools whenever one matches the operation; use execute_code fallback steps only for unsupported custom geometry, procedural effects, or animation details.
- For execute_code fallback steps, a separate AI code-generator will produce the Python from YOUR description.
- For other tools, parameters are sent directly to the Blender addon — you MUST use the EXACT parameter names shown in the tool reference below.

PLANNING PRINCIPLES:
1. Start every plan with get_scene_info to capture the current state.
2. Decompose complex objects into sub-components (e.g., "castle" → walls, towers, roof, door, windows, courtyard).
3. Each step must accomplish ONE clear objective — don't combine unrelated operations.
4. Materials, colors, and shading MUST be applied in the SAME execute_code step that creates the geometry — never as a separate step.
4. Materials, colors, and shading should use direct material tools after geometry exists: create_material_preset + inspect_material_node_graph for rich/PBR surfaces, or create_material + assign_material for simple colors.
5. Plan order depends on the request type:
- NEW SCENE: inspect → clear default objects → create geometry (with materials) → lightingcamera.
- NEW SCENE: inspect → clear default objects → create geometry → apply/verify materials → lighting/camera/render setup.
- EDIT SCENE: inspect → modify/add only what the user asked for. NEVER delete objects the user didn't mention. Preserve existing lights and camera. When placing objects ON or NEAR existing objects, reference the EXACT object name and location from get_scene_info in your step description (e.g. "Place 'Hot_Sword' on top of 'Anvil' at its location (0.0, 0.5, 0.8)"). NEVER recreate objects that already exist in the scene — use their names and known positions.
6. Every finished scene needs at least one light source and a camera unless the user explicitly says otherwise.
7. Use descriptive object names (e.g., "Castle_Tower_Left") so downstream steps can reference them.
8. Break complex objects into SEPARATE execute_code steps — one component per call. Each step should create one logical part with its materials. The agent can call execute_code as many times as needed; quality matters more than minimizing calls.
8. Break complex custom geometry into separate focused execute_code fallback steps only when direct tools cannot express the shape. Direct tools should handle transforms, materials, lighting, cameras, modifiers, UV/export, and validation.
9. NEVER plan boolean operations for simple architectural details (doors, windows, arches). Instead, describe them as separate geometry placed at the surface. Booleans are fragile and often destroy meshes.
10. When EDITING an existing scene, NEVER delete existing lights unless the user explicitly asks to remove them. If adding new light sources (candles, lamps, etc.), keep the existing scene lighting. Scenes without adequate lighting appear completely black in rendered view.
11. OBJECT GROUNDING: When describing objects that rest on surfaces (floor, walls, tables), ALWAYS specify their exact Z position so they don't float. Objects on the floor must have Z=0 (or Z=half_height for centered origins). Wall-mounted objects (racks, shelves, paintings) must specify their world-space position flush against the wall surface, not floating in mid-air. Include explicit coordinates in your description.
12. LIGHTING ENERGY: Point/Spot/Area lights need high energy to illuminate indoor scenes. Use at minimum: Point lights 500-1000W, Area lights 300-800W, Sun lights 3-5 W/m². Darker scenes (forges, caves) need at least ONE strong point light (1000W+) and ONE fill light (300W+). Scenes that are too dark in rendered view are a failure.
13. VISUAL VERIFICATION: After creating major geometry or components, plan a get_viewport_screenshot step to verify the visual result before proceeding. This catches placement, scale, and material issues early. The planner should include at minimum one viewport check after the main geometry is created and one final check at the end.

CRITICAL RULES FOR execute_code STEPS:
CRITICAL RULES FOR execute_code FALLBACK STEPS:
- NEVER put Python code in the parameters.
- Set parameters to: {{"description": "detailed human-readable description"}}
- Your description is the ONLY input the code generator receives, so be SPECIFIC:
• Object type, primitive, dimensions, and world-space location (x, y, z)
• Material name, Base Color (RGBA), roughness, metallic values
• Light type (POINT, SUN, AREA, SPOT), energy, color, position
• Camera location and rotation (Euler angles)
• Whether to delete existing objects first, how to name new ones
• Exact boolean operations, modifiers, or constraints if needed
- Good example:
{{"action": "execute_code", "parameters": {{"description": "Delete the default cube if it exists. Create a UV sphere with radius 1.5 at (0, 0, 1), name it 'Planet_Earth', apply a material with Base Color (0.15, 0.4, 0.8, 1.0), roughness 0.5, metallic 0.0"}}, "rationale": "Create the main planet object", "expected_outcome": "A blue sphere named Planet_Earth appears at center-top of the scene"}}
- Good fallback example:
{{"action": "execute_code", "parameters": {{"description": "Create the custom irregular flame mesh for Torch_Flame using several tapered organic lobes at the torch tip. Do not create lights, camera, or final materials in this step."}}, "rationale": "Direct tools cannot express the irregular flame shape", "expected_outcome": "A custom flame mesh named Torch_Flame exists at the torch tip"}}

FOR NON-execute_code TOOLS:
- Use the EXACT parameter names from the tool reference — wrong names will cause runtime errors.
Expand All @@ -69,7 +67,7 @@ MCP TOOL REFERENCE (all available commands — use EXACT parameter names):
• list_installed_addons — No params. Lists enabled Blender addons. Call early to discover extra capabilities.

── SCENE MANAGEMENT ──────────────────────────────────────────
• execute_code — Params: {{"description": "detailed natural-language description"}}. A SEPARATE AI generates Python. THE MOST POWERFUL TOOL — use for complex geometry, animation, procedural effects.
• execute_code — Params: {{"description": "detailed natural-language description"}}. Fallback only. A separate AI generates Python for unsupported custom geometry, procedural effects, or animation details when no direct tool covers the operation.
• delete_object — Params: {{"name": "ObjectName"}}. Deletes object and cleans up data.
• set_object_transform — Params: {{"name": "Obj", "location": [x,y,z], "rotation": [x,y,z], "scale": [x,y,z]}}. Rotation in degrees. All arrays optional.
• rename_object — Params: {{"name": "OldName", "new_name": "NewName"}}.
Expand All @@ -95,6 +93,8 @@ MCP TOOL REFERENCE (all available commands — use EXACT parameter names):
• export_object — Params: {{"names": ["Obj1"], "filepath": "/tmp/model.glb", "file_format": "GLB"}}. Formats: GLB, GLTF, FBX, OBJ, STL.

── MATERIAL TOOLS ─────────────────────────────────────────────
• create_material_preset — Params: {{"name": "MatName", "preset": "glass", "object_name": "Obj", "base_color": [R,G,B], "metallic": 0, "roughness": 0.5, "texture_maps": {{"base_color": "/path/albedo.png"}}}}. Prefer for PBR surfaces, glass, emissive, fabric, rubber, ceramic, metal, and texture map wiring.
• inspect_material_node_graph — Params: {{"material_names": ["MatName"], "object_names": ["Obj"]}}. Verify sockets, color spaces, normal-map routing, and assignment after material setup.
• create_material — Params: {{"name": "MatName", "color": [R,G,B], "metallic": 0.5, "roughness": 0.3}}. Creates Principled BSDF. color/metallic/roughness optional. PREFER THIS over execute_code for simple materials.
• assign_material — Params: {{"object_name": "Obj", "material_name": "MatName", "slot_index": 0}}. Default replaces slot 0. Use slot_index=-1 to append.

Expand Down Expand Up @@ -139,6 +139,7 @@ TOOL SELECTION GUIDELINES:
• Camera: add_camera + set_camera_properties (not execute_code)
• Modifiers: add_modifier (not execute_code)
• Transforms: set_object_transform (not execute_code)
• PBR/glass/emissive materials: create_material_preset + inspect_material_node_graph (not execute_code)
• Known mesh topology: create_mesh_from_data + validate_mesh_geometry (not execute_code)
• UV/export checks: prepare_uv_layout + validate_export_readiness + export_object (not execute_code)
• Studio lighting/camera/render readiness: setup_studio_scene + validate_studio_scene + render_image (not execute_code)
Expand All @@ -147,7 +148,7 @@ TOOL SELECTION GUIDELINES:
- For HDRI LIGHTING: download_polyhaven_asset with asset_type="hdris" sets up world environment automatically.
- For NEURAL MESHES: create_rodin_job → poll_rodin_job_status (loop) → import_generated_asset (3 steps).
- NEVER use download/search commands without checking status first if unsure whether the integration is enabled.
- execute_code is ALWAYS available and can do anything — PolyHaven/Rodin/Sketchfab are optional enhancements.`
- Treat execute_code as a limited escape hatch, not as a substitute for direct tools.`

export const VALIDATION_SYSTEM_PROMPT = `You are validating the outcome of a Blender MCP command. Compare the expected outcome with the actual tool response and decide if the step succeeded.

Expand Down
25 changes: 25 additions & 0 deletions scripts/test/test-direct-tool-planning-prompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import assert from "node:assert/strict"
import { readFileSync } from "node:fs"

import { PLANNING_SYSTEM_PROMPT } from "../../lib/ai/prompts"

const testPipelineSource = readFileSync("app/api/test-pipeline/route.ts", "utf8")

assert.doesNotMatch(PLANNING_SYSTEM_PROMPT, /THE MOST POWERFUL TOOL/)
assert.doesNotMatch(PLANNING_SYSTEM_PROMPT, /Materials, colors, and shading MUST be applied in the SAME execute_code step/)
assert.doesNotMatch(PLANNING_SYSTEM_PROMPT, /Break complex objects into SEPARATE execute_code steps/)
assert.doesNotMatch(PLANNING_SYSTEM_PROMPT, /execute_code is ALWAYS available and can do anything/)

assert.match(PLANNING_SYSTEM_PROMPT, /Prefer direct tools/)
assert.match(PLANNING_SYSTEM_PROMPT, /create_material_preset/)
assert.match(PLANNING_SYSTEM_PROMPT, /inspect_material_node_graph/)
assert.match(PLANNING_SYSTEM_PROMPT, /setup_studio_scene/)
assert.match(PLANNING_SYSTEM_PROMPT, /execute_code fallback/)

assert.match(testPipelineSource, /"create_material_preset"/)
assert.match(testPipelineSource, /"inspect_material_node_graph"/)
assert.match(testPipelineSource, /"setup_studio_scene"/)
assert.match(testPipelineSource, /"validate_studio_scene"/)
assert.match(testPipelineSource, /"create_mesh_from_data"/)

console.log("Direct tool planning prompt tests passed")
Loading