Metadata-Version: 2.4
Name: gymcraft
Version: 1.3.1
Summary: Python RPC client for GymCraft - Gymnasium-style RL environments in Minecraft
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: grpcio>=1.81.1
Requires-Dist: gymnasium>=1.3.0
Requires-Dist: jinja2>=3.1
Requires-Dist: mcrcon>=0.7.0
Requires-Dist: mypy>=2.1.0
Requires-Dist: openai>=3.3.1
Requires-Dist: python-dotenv>=1.2.3
Requires-Dist: protobuf>=6.33.6
Provides-Extra: mcp
Requires-Dist: mcp>=2.1.1; extra == "mcp"

# GymCraft Python RPC Client

Python 包仅使用 GymCraft 版本号（如 `gymcraft==1.3.0`），不带 Minecraft 版本后缀，默认兼容同一 GymCraft 版本的所有 MC 适配版本。GitHub Release 与模组附件使用 `<GymCraft版本>+mc<MC版本>` 命名；同一个 Python 版本只向 PyPI 发布一次。

Install dependencies:

```powershell
cd src\main\python
uv sync
```

Generate Python gRPC stubs from the mod proto files, then build the wheel:

```powershell
# from repository root
.\gradlew generatePythonStubs
.\gradlew packagePython
```

Or build directly with uv:

```powershell
cd src\main\python
uv build
```

Create an environment in-game with the env tool, then connect by entity UUID:

```python
from gymcraft import GymCraftEnv
from gymcraft.gym.action.components import noop_pb2

env = GymCraftEnv("entity-uuid-here")
obs, reset_info = env.reset(options={
    "disable_vanilla_ai": True,
    "max_actions_per_step": 9,
})  # Disable vanilla AI only while waiting between actions; defaults to False.
self_state = obs["gymcraft:self"]

obs, reward, terminated, truncated, step_info = env.step({
    "timeout_seconds": 0.0,  # seconds; <= 0 means no limit
    "actions": [
        {"component_id": "gymcraft:noop", "payload": noop_pb2.ProtoNoop()},
    ],
})
env.close()
```

`reset()` returns `(observation, info)` and `step()` returns `(observation, reward, terminated, truncated, info)` (Gymnasium-style). The observation is the unpacked `Observation` dict: a `header` plus component keys that are full registration ids such as `gymcraft:self`, `gymcraft:nearby_blocks`, `gymcraft:menu`. A step receives an ordered `ActionBatch`; each action has exactly one `component_id` and protobuf `payload`, and the server executes the list serially under one shared `timeout_seconds`. `make_action()` packs one action into `ProtoMcAction`, while `unpack_observation()` converts a raw `ProtoMcObservation` back to an `Observation`.

`max_actions_per_step` 默认为 `9`，限制每个 step 最多接受的 action 数量；超限批次会整体失败且不产生动作副作用，空批次 noop 仍可使用。设为 `0` 或负数表示不限制。

`gymcraft:update_interesting_blocks` 可通过 `add_block_ids` / `remove_block_ids` 批量维护当前 Agent 关注的方块类型；`gymcraft:interesting_blocks` 返回附近匹配类型的可见 `ProtoBlockView`。

Typecheck the Python client:

```powershell
cd src\main\python
uv run mypy src debug tests
```

GitHub Actions runs stub generation, mypy, packaging, and uploads the built distributions as the `gymcraft-python-dist` artifact.

## LLM Agent 工具包

`gymcraft.llm` 将 LLM 相关能力按组件声明式组织：

- `components/actions/`：19 个动作组件声明（DSL 命令、用法文案、解析器），由 `ACTION_REGISTRY` 自动收集；`ActionParser` / `encode_action_batch` 解析模型回复中的命令 DSL 并编码为 `ActionBatch`；
- `components/observations/`：9 个观测组件声明 + header 派生的 `last_actions` 虚拟组件，由 `OBSERVATION_REGISTRY` 自动收集；每个组件的文本格式由 `templates/` 下对应的 Jinja2 模板决定；
- `ConversationHistory` / `ContextAssembler`：组装通用 Chat Completions 消息；
- `LLMGymCraftEnv`：只负责串联上述组件与底层 `GymCraftEnv`，所有组件都可替换或单独使用。

内置 system prompt、命令说明和动作纠错反馈均使用英文。观测只渲染决策所需的原生字段，不生成相对坐标等派生状态；渲染不做条数裁剪（后端返回多少渲染多少），条目数量统一由 reset options 的 `components` 键控制。输入模型的内容严格来自观测——reward/terminated/truncated/info 只出现在 `step` 返回值中，不进入任何消息；task 描述并入 system 消息。
`ConversationHistory` 按 `history_turns` 分批保留完整历史；当前批次满载后，下一轮会折叠整批历史并作为新批次的第一轮重新积累，不再逐轮滚动淘汰最旧上下文。

最小 wrapper 示例：

````python
from gymcraft import GymCraftEnv
from gymcraft.llm import LLMGymCraftEnv

base_env = GymCraftEnv("entity-uuid-here")
env = LLMGymCraftEnv(base_env, task="走到最近的箱子旁并打开它。")
context, info = env.reset(options={"disable_vanilla_ai": True})

# context["messages"] 可直接交给任意 Chat Completions 兼容 API。
model_text = """我先靠近箱子。

```gymcraft-action
/timeout 10
/move_to 12 64 -3 1
```
"""
context, reward, terminated, truncated, info = env.step(model_text)
env.close()
````

动作块位于回复末尾，每个非空行是一条 Minecraft 风格命令。一个动作块可以包含多个动作，服务端严格按文本行顺序串行执行，并允许重复同一组件。多条 `move_menu_item` 会保留为一个组件原生批量负载。调用 `ActionParser.command_reference()` 可以取得当前环境支持的完整命令表。

LLM 可用一条原子命令同时增删兴趣类型，例如 `/update_interesting_blocks add minecraft:diamond_ore mod:target_block remove minecraft:stone`。匹配结果会在后续观测的 `interesting_blocks` 段中按距离排序；扫描与返回的条目上限由 reset options 的 `components` 键控制（如 `{"components": {"interesting_blocks": {"max_blocks": 20}}}`）。

连接已创建的 `simple_mob` 环境进行真实 DSL/gRPC 调试：

```powershell
uv run python debug/interesting_blocks_debug.py <entity_uuid> --add minecraft:diamond_ore
```

格式错误不会推进游戏状态。wrapper 会把错误和模型原文加入上下文，默认允许两次原地纠正，连续第三次非法输出会截断当前 rollout。服务端返回的 reward、terminated 和 truncated 不会被 Python 任务逻辑改写。

## Chat Completions 闭环 Demo

从 PyPI 安装客户端后，demo 可通过统一命令直接运行：

```powershell
pip install gymcraft
```

运行 demo；`base_url`、API key 和模型均可替换为任意兼容服务提供的值：

```powershell
$env:LLM_BASE_URL = "https://api.openai.com/v1"
$env:LLM_API_KEY = "your-api-key"
$env:LLM_MODEL = "your-model-id"

gymcraft-demo llm-chat `
  <entity_uuid> `
  --task "找到最近的箱子并查看其中的物品"
```

demo 使用通用的 `client.chat.completions.create(model=..., messages=...)` 接口，不启用 Responses API、厂商工具调用或服务端会话存储。

连接 `gymcraft:iron_mining` 环境可运行完整生存工具链 demo：

```powershell
gymcraft-demo iron-mining `
  <entity_uuid> `
  --max-steps 64 `
  --trace traces/iron-mining.jsonl
```

该 demo 内置从空手采集原木、使用 self 菜单 $2\times2$ 合成、放置工作台、制作石镐并拾取粗铁的任务提示。`--task` 可覆盖提示，`--trace` 可保存完整 JSONL 轨迹。

## 人工终端交互

```powershell
uv run debug/llm_terminal.py <entity_uuid> --task "测试菜单和移动动作"
```

终端中可直接输入一条或多条 `/command`，空行提交；工具会自动补上 `gymcraft-action` 围栏。输入 `:obs` 查看最新观测、`:context` 查看实际消息历史、`:quit` 退出。

## 检查

```powershell
uv run python -m unittest discover -s tests -v
uv run mypy src debug tests
```
