Skip to content

3.1 ModSmith Project Initialization and Environment Setup

Zhoumy303 edited this page Sep 26, 2026 · 1 revision

This guide walks you through setting up the ModSmith development environment from scratch and configuring Zhipu GLM as the LLM backend. All commands and files can be copied and executed directly.


Prerequisites

  • Python 3.10+
  • Terminal (Terminal on macOS/Linux, PowerShell on Windows)
  • Git (optional)
  • Zhipu API Key (Get it here)

Check the Python version:

python3 --version

Step 1: Create the Project Directory

cd ~
mkdir modsmith
cd modsmith

You can use any path you like.


Step 2: Create a Virtual Environment

python3 -m venv .venv

Activate the virtual environment:

# macOS / Linux
source .venv/bin/activate

# Windows PowerShell
.venv\Scripts\Activate.ps1

Verify: (.venv) appears before the terminal prompt.


Step 3: Create pyproject.toml

Create pyproject.toml in the project root:

[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "modsmith"
version = "0.1.0"
description = "Forge Fabric mods from natural language"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
    "anthropic>=0.40.0",
    "jsonschema>=4.23.0",
    "jinja2>=3.1.4",
    "pillow>=10.4.0",
    "typer>=0.15.0",
    "rich>=13.9.0",
    "python-dotenv>=1.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.3.0",
    "pytest-mock>=3.14.0",
]

[project.scripts]
modsmith = "modsmith.cli:app"

[tool.setuptools.packages.find]
where = ["."]
include = ["modsmith*"]

Step 4: Create the Directory Structure

mkdir -p modsmith/blueprint/examples
mkdir -p modsmith/llm
mkdir -p modsmith/generator
mkdir -p modsmith/verifier
mkdir -p modsmith/templates/fabric-project
mkdir -p modsmith/templates/java
mkdir -p modsmith/templates/resources
mkdir -p tests

Step 5: Create Initial Python Files

5.1 modsmith/__init__.py

"""ModSmith - Forge Fabric mods from natural language."""

__version__ = "0.1.0"

5.2 modsmith/blueprint/__init__.py

"""Blueprint definition, schema, and validation."""

5.3 modsmith/llm/__init__.py

"""LLM client for generating blueprints from natural language."""

5.4 modsmith/generator/__init__.py

"""Deterministic project and file generation."""

5.5 modsmith/verifier/__init__.py

"""Compile verification via Gradle."""

5.6 modsmith/cli.py

"""ModSmith CLI entry point."""

import typer
from rich.console import Console

app = typer.Typer(
    name="modsmith",
    help="Forge Fabric mods from natural language.",
    no_args_is_help=True,
)
console = Console()


@app.command()
def version() -> None:
    """Show ModSmith version."""
    from modsmith import __version__
    console.print(f"ModSmith v{__version__}")


@app.command()
def generate(
    description: str = typer.Argument(..., help="Natural language description of the mod"),
    output: str = typer.Option("./output", "--output", "-o", help="Output directory"),
) -> None:
    """Generate a Fabric mod from a natural language description."""
    console.print(f"[yellow]TODO:[/yellow] Generate mod from: {description}")
    console.print(f"[yellow]TODO:[/yellow] Output to: {output}")


if __name__ == "__main__":
    app()

5.7 tests/__init__.py

touch tests/__init__.py

5.8 modsmith/config.py

"""ModSmith global configuration: centralized version numbers and defaults."""

# ============================================================
# Target Minecraft version configuration
# Change only here when upgrading
# ============================================================

MINECRAFT_VERSION = "26.1.2"
FABRIC_LOADER_VERSION = "0.19.5"
LOOM_VERSION = "1.17-SNAPSHOT"
FABRIC_API_VERSION = "0.155.2+26.1.2"
JAVA_VERSION = 25

# ============================================================
# Default mod metadata
# ============================================================

DEFAULT_MOD_VERSION = "0.1.0"
DEFAULT_AUTHOR = "ModSmith User"
DEFAULT_DESCRIPTION = "A ModSmith generated mod"


def default_context() -> dict:
    """Return the default context (version-related fields) for template rendering.

    These values are overridden by fields of the same name in the blueprint,
    but if the blueprint does not provide them, the defaults here are used.
    """
    return {
        "minecraft_version": MINECRAFT_VERSION,
        "fabric_loader_version": FABRIC_LOADER_VERSION,
        "loom_version": LOOM_VERSION,
        "fabric_api_version": FABRIC_API_VERSION,
        "mod_version": DEFAULT_MOD_VERSION,
        "author": DEFAULT_AUTHOR,
        "mod_description": DEFAULT_DESCRIPTION,
    }

Note: To upgrade versions later, just modify these constants here, and all modules will follow automatically.


Step 6: Configure the Zhipu GLM Client

6.1 Create modsmith/llm/client.py

"""LLM client for generating blueprints from natural language."""

import os
import json
from pathlib import Path

import anthropic
from dotenv import load_dotenv

# Load .env file
load_dotenv()

# Anthropic-compatible endpoint for Zhipu GLM
ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/anthropic"

# Default model (can be overridden in .env)
DEFAULT_MODEL = os.environ.get("GLM_MODEL", "glm-5.3")

# Blueprint schema paths
SCHEMA_PATH = Path(__file__).parent.parent / "blueprint" / "schema.json"
EXAMPLES_DIR = Path(__file__).parent.parent / "blueprint" / "examples"


def _load_schema() -> dict:
    """Load the blueprint JSON Schema."""
    with open(SCHEMA_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def _load_examples() -> list[dict]:
    """Load few-shot example blueprints."""
    from modsmith.config import default_context
    defaults = default_context()

    examples = []
    if EXAMPLES_DIR.exists():
        for path in sorted(EXAMPLES_DIR.glob("*.json")):
            with open(path, "r", encoding="utf-8") as f:
                example = json.load(f)
            # Override version fields in examples with configured versions
            example["minecraft_version"] = defaults["minecraft_version"]
            example["fabric_loader_version"] = defaults["fabric_loader_version"]
            examples.append(example)
    return examples


def _build_system_prompt() -> str:
    """Build the system prompt, including the Schema and few-shot examples."""
    schema = _load_schema()
    examples = _load_examples()

    prompt = f"""You are a Minecraft Fabric mod blueprint generator.

Your task is to generate a blueprint that conforms to the following JSON Schema based on the user's natural-language description.

## Schema

```json
{json.dumps(schema, indent=2, ensure_ascii=False)}
```

## Examples

"""

    for i, example in enumerate(examples, 1):
        prompt += f"### Example {i}\n\n```json\n{json.dumps(example, indent=2, ensure_ascii=False)}\n```\n\n"

    prompt += """## Rules

1. Output JSON only. Do not include any explanation, comments, or Markdown code fences.
2. All required fields must be present.
3. The `type` field can only be one of: basic, food, fuel, tool.
4. If the description is ambiguous, use reasonable default values.
5. If the `texture` field cannot be determined, use "auto".
"""

    return prompt


def get_client() -> anthropic.Anthropic:
    """Create and return an Anthropic client for Zhipu GLM."""
    api_key = os.environ.get("ANTHROPIC_API_KEY")
    if not api_key:
        raise ValueError(
            "ANTHROPIC_API_KEY is not set. Please configure your Zhipu API Key in the .env file."
        )

    return anthropic.Anthropic(
        api_key=api_key,
        base_url=ZHIPU_BASE_URL,
    )


def generate_blueprint(user_input: str) -> dict:
    """Convert a natural-language description into structured blueprint JSON.

    Args:
        user_input: The user's natural-language description of the mod.

    Returns:
        A blueprint dictionary that conforms to the Schema.

    Raises:
        ValueError: When the API key is not configured.
        json.JSONDecodeError: When the content returned by the LLM is not valid JSON.
    """
    client = get_client()
    system_prompt = _build_system_prompt()

    message = client.messages.create(
        model=DEFAULT_MODEL,
        max_tokens=4096,
        system=system_prompt,
        messages=[
            {"role": "user", "content": user_input},
        ],
    )

    # Extract text content
    response_text = message.content[0].text.strip()

    # Remove possible Markdown code fence markers
    if response_text.startswith("```"):
        lines = response_text.split("\n")
        response_text = "\n".join(lines[1:-1])

    # Parse JSON
    blueprint = json.loads(response_text)
    return blueprint

6.2 Create the .env File

Create .env in the project root:

# Zhipu GLM API configuration
ANTHROPIC_API_KEY=your_zhipu_api_key

# Optional: specify the model to use (default glm-5.2)
# GLM_MODEL=glm-5.2

Get an API Key: Visit Zhipu Open Platform to create one.

6.3 Create .gitignore

.venv/
__pycache__/
*.pyc
*.egg-info/
build/
dist/
output/
.pytest_cache/
.env

Step 7: Create a Minimal Blueprint Schema and Example (for Testing)

7.1 modsmith/blueprint/schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["mod_id", "package_name", "minecraft_version", "fabric_loader_version", "items"],
  "properties": {
    "mod_id": { "type": "string" },
    "package_name": { "type": "string" },
    "minecraft_version": { "type": "string" },
    "fabric_loader_version": { "type": "string" },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "type", "display_name_en"],
        "properties": {
          "id": { "type": "string" },
          "type": { "enum": ["basic", "food", "fuel", "tool"] },
          "display_name_en": { "type": "string" },
          "display_name_zh": { "type": "string" },
          "nutrition": { "type": "integer" },
          "saturation": { "type": "number" },
          "always_edible": { "type": "boolean" },
          "effects": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "effect": { "type": "string" },
                "duration_ticks": { "type": "integer" },
                "amplifier": { "type": "integer" }
              }
            }
          },
          "texture": { "type": "string" }
        }
      }
    }
  }
}

7.2 modsmith/blueprint/examples/example1.json

{
  "mod_id": "example-mod",
  "package_name": "com.example",
  "minecraft_version": "26.1.2",
  "fabric_loader_version": "0.19.5",
  "items": [
    {
      "id": "healing_apple",
      "type": "food",
      "display_name_en": "Healing Apple",
      "display_name_zh": "治愈苹果",
      "nutrition": 4,
      "saturation": 0.3,
      "always_edible": true,
      "effects": [
        { "effect": "regeneration", "duration_ticks": 200, "amplifier": 0 }
      ],
      "texture": "auto"
    }
  ]
}

Step 8: Create README.md

# ModSmith

Forge Fabric mods from natural language.

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

## Usage

```bash
modsmith --help
modsmith version
modsmith generate "create an apple that restores 4 hunger points when eaten"
```

Step 9: Install the Project

Make sure the virtual environment is activated:

pip install -e ".[dev]"

For faster installation in China:

pip install -e ".[dev]" -i https://pypi.tuna.tsinghua.edu.cn/simple

Step 10: Verify the Installation

10.1 Verify the CLI

modsmith --help

It should display help information.

modsmith version

It should output ModSmith v0.1.0.

10.2 Verify the Zhipu GLM Connection

Create test_llm.py:

"""Test Zhipu GLM connection."""

import json
from modsmith.llm.client import generate_blueprint

result = generate_blueprint("Create an apple that restores 4 hunger points when eaten")
print(json.dumps(result, indent=2, ensure_ascii=False))

Run:

python test_llm.py

If configured correctly, it should output blueprint-like JSON.


Step 11: Initialize Git (Optional)

git init
git add .
git commit -m "Task 0: project initialization with Zhipu GLM"

Task 1 Completion Criteria

  • Project directory created
  • Virtual environment .venv/ created and activated
  • pyproject.toml created with all dependencies
  • Directory structure complete
  • modsmith --help displays help
  • modsmith version outputs ModSmith v0.1.0
  • pip install -e ".[dev]" succeeds
  • .env configured with Zhipu API Key
  • test_llm.py successfully calls Zhipu GLM and returns blueprint JSON
  • Git repository initialized (optional)

FAQ

Q1: pip install times out. Use a China mirror:

pip install -e ".[dev]" -i https://pypi.tuna.tsinghua.edu.cn/simple

Q2: ANTHROPIC_API_KEY is not set. Confirm that the .env file is in the project root and the key name is correct.

Q3: model_not_found. Check the GLM_MODEL environment variable and make sure it is a model code supported by Zhipu (for example, glm-5.2).

Q4: The returned content is not valid JSON. generate_blueprint already performs basic cleanup. If it still fails, strengthen the constraints in the system prompt or add regex extraction.


After completing the above steps, you will have a runnable ModSmith project skeleton and will be able to successfully call Zhipu GLM to generate blueprints. Next, proceed to Task 2: Define Blueprint Schema.

Clone this wiki locally