SoulFire LogoSoulFire
SDK

Python SDK

Connect to SoulFire from asynchronous or synchronous Python 3.14 programs.

The Python SDK requires CPython 3.14 or newer. Use AsyncSoulFire for asynchronous services. Use the synchronous SoulFire client for scripts, notebooks, and command-line tools.

Install

python -m pip install soulfire

Connect and send chat

import os

from soulfire import AsyncSoulFire


async with AsyncSoulFire.connect(
    "https://soulfire.example.com",
    token=os.environ["SOULFIRE_TOKEN"],
) as soulfire:
    bot = soulfire.instance("instance-uuid").bot("bot-uuid")
    await bot.start()
    await bot.chat.send("Hello from SoulFire")

The connection handshake rejects an incompatible server or missing required plugin before application work begins.

Use structured concurrency

Python 3.14 TaskGroup gives fleet operations one cancellation and failure boundary:

async with asyncio.TaskGroup() as group:
    for bot_id in bot_ids:
        bot = soulfire.instance(instance_id).bot(bot_id)
        group.create_task(bot.chat.send("Ready"))

Use asyncio.timeout() when a workflow needs one scoped deadline. Concurrent failures remain available through ExceptionGroup.

Stream events

async for event in bot.events():
    print(event.WhichOneof("event"))

The stream stays associated with the configured bot across Minecraft reconnects.

Observe synchronized state

Open a session

session = await bot.observe()

Read state and consume changes

async with session:
    print(session.state.player)

    async for event in session.events():
        print(event)

The session merges snapshots and deltas, resumes streams when possible, and requests a new snapshot when continuity is lost.

Interact with blocks and beds

Use interact_block for doors, buttons, beds, redstone controls, and modded interactive blocks:

await bot.interact_block(
    button_position,
    BLOCK_FACE_NORTH,
)

await bot.sleep(bed_position)
await bot.wake()

SoulFire checks that the target block is loaded and reachable. sleep waits for the server-confirmed sleeping state, so an occupied bed or an invalid sleep attempt fails instead of appearing successful.

Run durable tasks

task = await bot.tasks.auto_eat(
    ["minecraft:bread", "minecraft:cooked_beef"],
    food_level=14,
    maximum_meals=1,
)

result = await task.result()
print(result.meals_eaten)

The same operation is synchronous through bot.tasks.auto_eat(...) and task.result(). See durable tasks for ownership, cancellation, and progress.

Craft and smelt

Production operations live beside recipe discovery:

from soulfire.inventory_pb2 import ItemSelector


recipes = await bot.recipes.list(result_item_id="minecraft:iron_ingot")
task = await bot.recipes.smelt(
    ItemSelector(item_ids=["minecraft:raw_iron"]),
    count=8,
    fuel=ItemSelector(tags=["minecraft:coals"]),
    station=furnace_position,
)
result = await task.result()

Use bot.recipes.craft(recipe_id, ...) for a recipe returned by list. Inventory-sized recipes need no station. The synchronous client exposes the same methods without await.

Use generated protocol clients

Generated protobuf messages and ConnectRPC clients remain available as an advanced escape hatch:

from soulfire.instance_connect import InstanceServiceClient

instances = soulfire.service(InstanceServiceClient)

Generated clients expose the wire contract directly. Prefer the high-level object model when it covers the operation.

Continue with durable tasks or plugin-defined APIs.

How is this page?

Last updated on

On this page