-
Notifications
You must be signed in to change notification settings - Fork 0
2. MVP Design
This document focuses on the technical design of the ModSmith MVP. Project motivation, industry background, target users, value positioning, and the argument for "why a blueprint layer is needed" are covered in 1. Project Background and are not repeated here.
The ModSmith MVP focuses on three item types: basic, food, and tool, to get the pipeline working end to end. The goal:
The user enters "create an apple that restores 4 hunger points when eaten," and the system outputs a compilable Fabric project zip, with the item verifiable in-game.
The MVP implements two modes: Chat and Execute (design rationale in 1. Project Background, Section 5):
| Mode | Responsibility | Output |
|---|---|---|
| Chat (concept Q&A) | Requirements consultant; multi-turn dialogue to clarify needs | Natural language + 【Requirement Summary】
|
| Execute (direct generation) | Takes a clear description and triggers the generation pipeline | Full project + jar |
Chat → Execute handoff: Chat ends by outputting text marked with 【Requirement Summary】; the Web UI's "⚡ Summarize Requirements" button calls /api/chat/summarize to extract the summary, fills it into the Execute description box, and switches tabs automatically.
The Web UI opens the Chat tab by default. Tab order: Concept Q&A → Generate Mod.
Plan mode is a future direction and is out of scope for the MVP (see Section 7).
flowchart TB
subgraph L1["① User Interaction Layer"]
direction TB
CLI["CLI<br/>modsmith generate / chat / web"]
WEB["Web UI<br/>Chat Tab / Execute Tab"]
end
subgraph L2["② LLM Parsing Layer"]
direction TB
GB["generate_blueprint()<br/>natural language → blueprint JSON"]
CR["chat_response()<br/>multi-turn dialogue"]
VB["validate_blueprint()<br/>Schema validation + feedback correction"]
end
subgraph L3["③ Deterministic Generation Layer"]
direction TB
RP["render_project()<br/>base project skeleton"]
GJ["generate_java_items()<br/>Java registration code"]
GR["generate_resources()<br/>models / translations / client items"]
GT["generate_all_textures()<br/>placeholder textures"]
end
subgraph L4["④ Verification & Output Layer"]
direction TB
BW["build_with_retry()<br/>compile verification + LLM auto-correction"]
RW["run_client_with_log()<br/>runtime verification"]
PO["package_output()<br/>package zip / jar / blueprint / README"]
end
L1 --> L2
L2 --> L3
L3 --> L4
flowchart TD
A[User description] --> B[Chat mode: multi-turn clarification]
B --> C[Output 【Requirement Summary】]
C --> D[Execute mode: clear mod description]
A --> D
D --> E[LLM generates blueprint JSON]
E --> F[Schema validation]
F --> G{Validation passed?}
G -- No, up to 3 times --> H[Feed error back to LLM]
H --> E
G -- Yes --> I[Blueprint approved]
I --> J[Jinja2 template rendering<br>generate Java/JSON/PNG files]
J --> K[./gradlew build]
K --> L{Compilation succeeded?}
L -- No, up to 3 times --> M[Feed error back to LLM to fix blueprint]
M --> E
L -- Yes --> N[Compilation succeeded]
N --> O[Package output<br>zip + jar + blueprint + README]
O --> P[./gradlew runClient]
P --> Q[Parse logs<br>check mod loading and item registration]
Q --> R[Return verification result]
All version numbers are managed centrally in modsmith/config.py:
MINECRAFT_VERSION = "26.2"
FABRIC_LOADER_VERSION = "0.19.5"
LOOM_VERSION = "1.17-SNAPSHOT"
FABRIC_API_VERSION = "0.161.0+26.2"
JAVA_VERSION = 25Upgrading the Minecraft version requires only changing this one place; template rendering, the System Prompt, and example injection all follow automatically.
A blueprint is a JSON object describing "what to generate":
{
"mod_id": "example-mod",
"package_name": "com.example",
"minecraft_version": "26.2",
"fabric_loader_version": "0.19.5",
"items": [
{
"id": "healing_apple",
"type": "food",
"display_name_en": "Healing Apple",
"display_name_zh": "Healing Apple",
"nutrition": 4,
"saturation": 0.3,
"always_edible": true,
"effects": [
{ "effect": "regeneration", "duration_ticks": 200, "amplifier": 0 }
],
"texture": "auto"
}
]
}Design points:
-
typeis an enum (basic,food,tool), determining which additional fields to generate. - Uses
allOf+if-thenfor conditional required fields:nutritionand similar fields are only required whentype=food. -
patternrestricts the naming format ofmod_idand itemid, preventing the AI from inventing names. - Version numbers are injected by
config.py; the Schema only enforces format constraints.
| Module | File | Responsibility |
|---|---|---|
| Blueprint gen | llm/client.py |
generate_blueprint(): natural language → blueprint JSON |
| Chat dialogue | llm/chat.py |
chat_response(): multi-turn dialogue, requirements clarification |
| Blueprint check | blueprint/validator.py |
Schema validation + feedback to LLM for correction |
System Prompt design:
- Execute mode: contains the full Schema, few-shot examples, required fields per type, and version requirements. Forces JSON output.
-
Chat mode: acts as a requirements consultant, explicitly stating capability boundaries (only basic/food/tool), limiting clarification dimensions (type/name/effect/value/appearance), refusing to answer technical questions, and ending with a
【Requirement Summary】marker.
Example injection: example blueprints have their version fields overridden by config.py values at load time, preventing the LLM from learning outdated version numbers from the examples.
| Module | File | Output |
|---|---|---|
| Project skeleton | generator/project.py |
build.gradle, gradle.properties, fabric.mod.json, gradlew
|
| Java code | generator/java.py |
ModItemIds.java, ModItems.java, ModItemsGenerated.java
|
| Resource files | generator/resources.py |
Client item, model JSON, translation files |
| Textures | generator/textures.py |
16x16 PNG placeholder textures |
Key decisions:
- Java code is rendered with Jinja2 templates, separating templates from data and guaranteeing syntactic correctness.
-
JSON files use
json.dumps()directly, no templates needed. - Texture colors are derived from the item ID's MD5 hash, ensuring the same ID produces the same color every time—deterministic and reproducible.
-
Static field declaration order: declare
FoodProperties,Consumable, andToolMaterialbeforeItem, avoiding Java's "illegal forward reference". -
Fabric template adapted for 26.2+: remove Yarn mappings, use the
net.fabricmc.fabric-loomplugin, Java 25, Gradle 9.5.1,implementationinstead ofmodImplementation, remove Mixin and thecliententrypoint.
Compile verification (verifier/gradle.py):
- Runs
./gradlew buildwith a 5-minute timeout. - Extracts error lines from stdout/stderr, normalizing them to
str. - On failure, feeds the error summary back to the LLM to correct the blueprint and retries, up to 3 times.
Runtime verification (verifier/runtime.py):
- Launches
./gradlew runClientand captures logs line by line. - Detects mod loading via
Hello Fabric world from. - Detects item registration via
item.<mod_id>.<item_id> registered. - Emits registration logs proactively in
ModItemsGenerated.javafor the parser.
Division of labor between the two verifications: compile verification ensures the code compiles; runtime verification ensures items are actually registered into the game. They complement each other.
packager/archive.py's package_output() emits four types of files:
| File | Purpose |
|---|---|
<mod_id>-src.zip |
Full project source, for further development |
<mod_id>-1.0.0.jar |
Mod ready to drop into mods/
|
<mod_id>-blueprint.json |
The blueprint from this run, for reproducibility |
README.md |
Installation instructions + full blueprint |
The source zip excludes build/, .gradle/, .idea/, and run/ to keep it clean.
pipeline.py's run_pipeline() integrates the full flow:
def run_pipeline(user_input, project_dir, output_dir, max_retries=3):
blueprint = generate_validated_blueprint(user_input, max_retries)
success, final_blueprint = build_with_retry(blueprint, project_dir, max_retries)
if not success:
return None
return package_output(final_blueprint, project_dir, output_dir)Both CLI and Web UI call it, ensuring consistent behavior.
| Layer | File | Responsibility |
|---|---|---|
| Backend | web/app.py |
FastAPI routes, SSE push, Chat session storage |
| Frontend | web/static/index.html |
Tab switching, Chat UI, Execute UI |
Backend endpoints:
| Endpoint | Purpose |
|---|---|
POST /api/generate |
Create a generation task |
GET /api/stream/{task_id} |
SSE push of generation logs |
POST /api/run_client |
Launch the game |
POST /api/auto_verify |
Runtime log verification |
POST /api/chat |
Append a Chat message |
GET /api/chat/stream/{session_id} |
SSE push of Chat responses |
POST /api/chat/summarize |
Extract requirement summary from conversation history |
POST /api/chat/clear |
Clear the session |
Key design points:
-
CHAT_SESSIONS(Chat sessions) andTASKS(generation tasks) are completely independent in-memory stores. - Chat supports SSE streaming output (typewriter effect).
- Chat history is persisted in
localStorageand restored on refresh. -
/api/chat/summarizefirst tries to extract the【Requirement Summary】marker, falling back to an LLM rewrite. - Default tab is Chat. Tab order: Concept Q&A → Generate Mod.
- No Google Fonts. Uses the system font stack so it loads in mainland China.
| Layer | Solution | Rationale |
|---|---|---|
| LLM | Zhipu GLM (Anthropic-compatible endpoint) | Accessible in China, supports structured output |
| Blueprint validation | JSON Schema + jsonschema
|
Clear definitions, friendly validation errors |
| Java code generation | Jinja2 template engine | Separates templates from data; simple and direct |
| JSON generation | Python json module |
Standard library, no extra dependencies |
| Texture generation | Pillow | No external dependencies; deterministic output |
| Compile verification | subprocess calling ./gradlew build
|
Reuses Gradle directly |
| Runtime verification | subprocess log capture + regex matching | No GUI dependency; stable |
| Backend framework | FastAPI | Lightweight, good SSE support |
| Frontend | Vanilla HTML + JS (no framework) | Zero build, easy to distribute |
modsmith/
├── modsmith/
│ ├── config.py # Single source of truth for versions
│ ├── cli.py # CLI entry point
│ ├── pipeline.py # End-to-end pipeline
│ ├── blueprint/ # Schema + validation
│ │ ├── schema.json
│ │ ├── validator.py
│ │ └── examples/
│ ├── llm/ # LLM clients
│ │ ├── client.py # Blueprint generation
│ │ └── chat.py # Chat dialogue
│ ├── generator/ # Deterministic generation
│ │ ├── project.py # Project skeleton
│ │ ├── java.py # Java code
│ │ ├── resources.py # Resource files
│ │ └── textures.py # Textures
│ ├── verifier/ # Verification
│ │ ├── gradle.py # Compile verification
│ │ └── runtime.py # Runtime verification
│ ├── packager/ # Packaging
│ │ └── archive.py
│ ├── web/ # Web UI
│ │ ├── app.py
│ │ └── static/
│ └── templates/ # Jinja2 templates
│ ├── fabric-project/
│ └── java/
└── tests/
| Phase | Goal | Status |
|---|---|---|
| Phase 1 | CLI tool, basic items only, get compilation working | ✅ Done |
| Phase 2 | Extend to basic/food/tool, add LLM auto-correction | ✅ Done |
| Phase 3 | Web UI: form, logs, download, preview, launch game | ✅ Done |
| Phase 4 | Chat mode + dual-tab routing + runtime log verification | ✅ Done |
| Phase 5 | Plan mode + more item types (blocks, armor) | 🔜 Future |
Referencing ModCrafting's "three-mode intelligent routing," ModSmith's long-term direction is Chat → Plan → Execute:
- Chat: clarify requirements
- Plan: confirm the approach (not yet implemented)
- Execute: run the generation
The core of Plan mode is having the LLM output a reviewable, editable structured plan before generation, including the files to create, fields, and verification steps. Users can review, edit, confirm, or roll back.
Why Plan is not implemented in the MVP:
- Chat mode already handles "requirement confirmation"; the
【Requirement Summary】is a lightweight "plan". - With only three simple item types supported, fields are few, and Plan's editing value is diluted.
- A full Plan implementation requires new backend (plan generation and execution), frontend (third tab, field-level editing), and Schema design—a nontrivial amount of work, unsuitable for the MVP.
- Plan will truly show its value once blocks, armor, recipes, or multi-item generation are supported.
Things explicitly out of scope to prevent scope creep:
| Not doing | Reason |
|---|---|
| Blocks, armor, entities | Focus on items; get the loop solid first |
| Crafting recipes | Recipes are complex; MVP only generates the item itself |
| Multi-item batch generation | Single item first; multi-item is future work |
| User accounts | MVP is for single-user local use |
| Cloud deployment | Runs locally; no server needed |
| Plan mode | See Section 7; future evolution |
| Custom renderers | Only generates the item; no advanced rendering |
The MVP is complete when the following end-to-end flow works:
- In the Web UI's Chat tab, the user types "I want to make an apple that heals when eaten."
- Chat asks for details; the user replies "call it Healing Apple, heals half a heart."
- Chat outputs the
【Requirement Summary】. - The user clicks "⚡ Summarize Requirements"; the Execute tab opens automatically with the summary pre-filled.
- The user clicks "⚡ Generate Mod."
- The system completes in order: blueprint generation → project generation → compilation → packaging.
- The page shows download links, the blueprint JSON, and the README.
- The user clicks "🔍 Log Verification."
- The system launches the game, parses the logs, and reports "mod loaded, item registered."
- The user downloads the jar, drops it into
mods/, and sees the Healing Apple in-game.
If any step fails, the system either gives a clear error message or automatically corrects itself.
The ModSmith MVP is a technical implementation that forges mod ideas into compilable, editable Fabric mods through "natural language → structured blueprint → deterministic code generation." It includes dual modes—Chat (requirements clarification) + Execute (direct generation)—along with a dual verification loop: compile verification + runtime verification.