Skip to content

simulatecraft.minecraft.observations

simulatecraft.minecraft.observations

Typed Minecraft observation models.

MinecraftObservation is what the environment hands to an agent's brain each tick. It extends the base Observation with rich Minecraft-specific fields so the LLM prompt gets structured, token-efficient context instead of a raw JSON blob.

The data is populated by MinecraftEnvironment.observe() which queries the Mineflayer bot over the IPC bridge.

Vec3

Bases: BaseModel

3-D float coordinate.

InventoryItem

Bases: BaseModel

One stack in the bot's inventory.

NearbyBlock

Bases: BaseModel

A block within the scan radius.

NearbyEntity

Bases: BaseModel

A mob or player the bot can see.

ChatMessage

Bases: BaseModel

One line from the Minecraft chat log.

BotStats

Bases: BaseModel

Vital statistics of the bot.

RecipeInfo

Bases: BaseModel

A craftable item the bot currently has materials for.

MinecraftObservation

Bases: Observation

Full structured state snapshot handed to the agent brain each tick.

All fields have sensible defaults so partial observations work: the bridge can omit fields it hasn't queried yet and the brain still gets a valid model.

nearby_blocks class-attribute instance-attribute

nearby_blocks: list[NearbyBlock] = Field(default_factory=list)

Blocks within the configured scan radius, sorted by distance.

nearby_entities class-attribute instance-attribute

nearby_entities: list[NearbyEntity] = Field(default_factory=list)

Mobs and players the bot can detect.

chat_log class-attribute instance-attribute

chat_log: list[ChatMessage] = Field(default_factory=list)

Last N chat messages (configurable in MinecraftEnvironment).

render

render() -> str

Compact text summary injected into the LLM prompt.

Source code in src/simulatecraft/minecraft/observations.py
def render(self) -> str:
    """Compact text summary injected into the LLM prompt."""
    lines: list[str] = [
        f"Tick {self.tick} | Pos {self.position} | Biome: {self.biome}",
        f"Health {self.stats.health}/20 | Food {self.stats.food}/20 "
        f"| Time {self.stats.time_of_day} | Rain: {self.stats.is_raining}",
    ]

    if self.equipped_item:
        lines.append(f"Holding: {self.equipped_item}")

    if self.inventory:
        inv_summary = ", ".join(f"{item.count}x {item.name}" for item in self.inventory[:12])
        if len(self.inventory) > 12:
            inv_summary += f" ... (+{len(self.inventory) - 12} more)"
        lines.append(f"Inventory: {inv_summary}")

    if self.nearby_blocks:
        block_summary = ", ".join(
            f"{b.name}@({b.x},{b.y},{b.z})" for b in self.nearby_blocks[:8]
        )
        lines.append(f"Nearby blocks: {block_summary}")

    if self.nearby_entities:
        ent_summary = ", ".join(
            f"{e.name}({e.entity_type}) ~{e.distance:.1f}m" for e in self.nearby_entities[:6]
        )
        lines.append(f"Nearby entities: {ent_summary}")

    if self.craftable:
        craft_summary = ", ".join(r.item_name for r in self.craftable[:6])
        lines.append(f"Craftable: {craft_summary}")

    if self.chat_log:
        lines.append("Recent chat:")
        for msg in self.chat_log[-4:]:
            prefix = f"<{msg.sender}> " if msg.sender else "[server] "
            lines.append(f"  {prefix}{msg.text}")

    if self.current_goal:
        lines.append(f"Current goal: {self.current_goal}")

    return "\n".join(lines)