plancraft 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: plancraft
3
+ Version: 0.1.2
4
+ Summary: Plancraft: an evaluation dataset for planning with LLM agents
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: accelerate>=1.0.1
9
+ Requires-Dist: coloredlogs>=10.0
10
+ Requires-Dist: daemoniker>=0.2.3
11
+ Requires-Dist: datasets>=3.0.2
12
+ Requires-Dist: dill>=0.3.1.1
13
+ Requires-Dist: einops>=0.8.0
14
+ Requires-Dist: flaky>=3.8.1
15
+ Requires-Dist: hf-transfer>=0.1.8
16
+ Requires-Dist: huggingface-hub>=0.26.1
17
+ Requires-Dist: hydra-core>=1.3.2
18
+ Requires-Dist: imagehash>=4.0.0
19
+ Requires-Dist: imageio>=2.36.0
20
+ Requires-Dist: inflection>=0.3.1
21
+ Requires-Dist: ipython>=7.5.0
22
+ Requires-Dist: jinja2>=2.11.2
23
+ Requires-Dist: loguru>=0.7.2
24
+ Requires-Dist: lxml>=4.3.3
25
+ Requires-Dist: matplotlib>=3.9.2
26
+ Requires-Dist: networkx>=3.2.1
27
+ Requires-Dist: numpy<1.24,>=1.16.2
28
+ Requires-Dist: openai>=1.52.2
29
+ Requires-Dist: opencv-python>=4.1.0.25
30
+ Requires-Dist: pandas>=2.1.0
31
+ Requires-Dist: peft>=0.13.2
32
+ Requires-Dist: pillow>=8.0.0
33
+ Requires-Dist: psutil>=5.6.2
34
+ Requires-Dist: pydantic>=2.9.2
35
+ Requires-Dist: pyglet>=2.0.18
36
+ Requires-Dist: pyro4>=4.76
37
+ Requires-Dist: python-dotenv>=1.0.1
38
+ Requires-Dist: pyyaml>=6.0.2
39
+ Requires-Dist: requests>=2.20.0
40
+ Requires-Dist: seaborn>=0.13.2
41
+ Requires-Dist: setuptools>=49.2.0
42
+ Requires-Dist: tinydb>=4.8.2
43
+ Requires-Dist: torch>=2.5.0
44
+ Requires-Dist: torchvision>=0.20.0
45
+ Requires-Dist: tqdm>=4.32.2
46
+ Requires-Dist: transformers>=4.43.3
47
+ Requires-Dist: typing>=3.6.6
48
+ Requires-Dist: wandb>=0.18.5
49
+ Requires-Dist: xmltodict==0.12.0
50
+ Provides-Extra: full
51
+ Requires-Dist: gym<=0.23.1,>=0.19.0; extra == "full"
52
+
53
+ # plancraft
54
+
55
+ [![Test](https://github.com/gautierdag/plancraft/actions/workflows/test.yaml/badge.svg)](https://github.com/gautierdag/plancraft/actions/workflows/test.yaml)
56
+ ![Python Version](https://img.shields.io/badge/python-3.9+-blue)
57
+ ![Ruff](https://img.shields.io/badge/linter-ruff-blue)
58
+ [![PyPI Version](https://img.shields.io/pypi/v/plancraft)](https://pypi.org/project/plancraft/)
59
+
60
+ Plancraft is a minecraft environment and agent that innovates on planning LLM agents with a retriever
61
+
62
+ You can install the package by running the following command:
63
+
64
+ ```bash
65
+ pip install plancraft
66
+ ```
67
+
68
+ Should you need the multimodal version of the package, you will also need a custom [fork](https://github.com/gautierdag/minerl.git) of the minerl package. You can install it by running the following command:
69
+
70
+ ```bash
71
+ pip install git+hhttps://github.com/gautierdag/minerl.git
72
+ ```
73
+
74
+ Note that you may need to follow the same installation instructions as in the [minerl documentation](https://minerl.readthedocs.io/en/latest/tutorials/index.html).
@@ -0,0 +1,5 @@
1
+ plancraft-0.1.2.dist-info/LICENSE,sha256=YGR8ehDB4t-T-lOQKMfKNR-2zsOU7E3E5NA8t25HKE0,1070
2
+ plancraft-0.1.2.dist-info/METADATA,sha256=IDsYHFCvjx4Nhe4Jgyj0x7YsIVAR95ZyrSA-LIQBgeI,2631
3
+ plancraft-0.1.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
4
+ plancraft-0.1.2.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
5
+ plancraft-0.1.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1 @@
1
+
environments/__init__.py DELETED
File without changes
environments/actions.py DELETED
@@ -1,218 +0,0 @@
1
- from typing import Union
2
-
3
- from pydantic import BaseModel, field_validator, model_validator
4
-
5
-
6
- def convert_to_slot_index(slot: str) -> int:
7
- slot = slot.strip()
8
- grid_map = {
9
- "[0]": 0,
10
- "[A1]": 1,
11
- "[A2]": 2,
12
- "[A3]": 3,
13
- "[B1]": 4,
14
- "[B2]": 5,
15
- "[B3]": 6,
16
- "[C1]": 7,
17
- "[C2]": 8,
18
- "[C3]": 9,
19
- }
20
- if slot in grid_map:
21
- return grid_map[slot]
22
- else:
23
- return int(slot[2:-1]) + 9
24
-
25
-
26
- def convert_from_slot_index(slot_index: int) -> str:
27
- grid_map = {
28
- 0: "[0]",
29
- 1: "[A1]",
30
- 2: "[A2]",
31
- 3: "[A3]",
32
- 4: "[B1]",
33
- 5: "[B2]",
34
- 6: "[B3]",
35
- 7: "[C1]",
36
- 8: "[C2]",
37
- 9: "[C3]",
38
- }
39
- if slot_index < 10:
40
- return grid_map[slot_index]
41
- else:
42
- return f"[I{slot_index-9}]"
43
-
44
-
45
- class SymbolicMoveAction(BaseModel):
46
- """ "Moves an item from one slot to another"""
47
-
48
- slot_from: int
49
- slot_to: int
50
- quantity: int
51
- action_type: str = "move"
52
-
53
- @field_validator("action_type", mode="before")
54
- def fix_action_type(cls, value) -> str:
55
- return "move"
56
-
57
- @field_validator("slot_from", "slot_to", mode="before")
58
- def transform_str_to_int(cls, value) -> int:
59
- # if value is a string like [A1] or [I1], convert it to an integer
60
- if isinstance(value, str):
61
- try:
62
- return convert_to_slot_index(value)
63
- except ValueError:
64
- raise AttributeError(
65
- "slot_from and slot_to must be [0] or [A1] to [C3] or [I1] to [I36]"
66
- )
67
- return value
68
-
69
- @field_validator("quantity", mode="before")
70
- def transform_quantity(cls, value) -> int:
71
- if isinstance(value, str):
72
- try:
73
- return int(value)
74
- except ValueError:
75
- raise AttributeError("quantity must be an integer")
76
- return value
77
-
78
- @model_validator(mode="after")
79
- def validate(self):
80
- if self.slot_from == self.slot_to:
81
- raise AttributeError("slot_from and slot_to must be different")
82
- if self.slot_from < 0 or self.slot_from > 45:
83
- raise AttributeError("slot_from must be between 0 and 45")
84
- if self.slot_to < 1 or self.slot_to > 45:
85
- raise AttributeError("slot_to must be between 1 and 45")
86
- if self.quantity < 1 or self.quantity > 64:
87
- raise AttributeError("quantity must be between 1 and 64")
88
-
89
- def to_action_dict(self) -> dict:
90
- return {
91
- "inventory_command": [self.slot_from, self.slot_to, self.quantity],
92
- }
93
-
94
-
95
- class SymbolicSmeltAction(BaseModel):
96
- """Smelts an item and moves the result into a new slot"""
97
-
98
- slot_from: int
99
- slot_to: int
100
- quantity: int
101
- action_type: str = "smelt"
102
-
103
- @field_validator("action_type", mode="before")
104
- def fix_action_type(cls, value) -> str:
105
- return "smelt"
106
-
107
- @field_validator("slot_from", "slot_to", mode="before")
108
- def transform_str_to_int(cls, value) -> int:
109
- # if value is a string like [A1] or [I1], convert it to an integer
110
- if isinstance(value, str):
111
- try:
112
- return convert_to_slot_index(value)
113
- except ValueError:
114
- raise AttributeError(
115
- "slot_from and slot_to must be [0] or [A1] to [C3] or [I1] to [I36]"
116
- )
117
- return value
118
-
119
- @field_validator("quantity", mode="before")
120
- def transform_quantity(cls, value) -> int:
121
- if isinstance(value, str):
122
- try:
123
- return int(value)
124
- except ValueError:
125
- raise AttributeError("quantity must be an integer")
126
- return value
127
-
128
- @model_validator(mode="after")
129
- def validate(self):
130
- if self.slot_from == self.slot_to:
131
- raise AttributeError("slot_from and slot_to must be different")
132
- if self.slot_from < 0 or self.slot_from > 45:
133
- raise AttributeError("slot_from must be between 0 and 45")
134
- if self.slot_to < 1 or self.slot_to > 45:
135
- raise AttributeError("slot_to must be between 1 and 45")
136
- if self.quantity < 1 or self.quantity > 64:
137
- raise AttributeError("quantity must be between 1 and 64")
138
-
139
- def to_action_dict(self) -> dict:
140
- return {
141
- "smelt": [self.slot_from, self.slot_to, self.quantity],
142
- }
143
-
144
-
145
- class ThinkAction(BaseModel):
146
- """Think about the answer before answering"""
147
-
148
- thought: str
149
-
150
- def to_action_dict(self) -> dict:
151
- return {}
152
-
153
-
154
- class SearchAction(BaseModel):
155
- """Searches for a relevant document in the wiki"""
156
-
157
- search_string: str
158
-
159
- def to_action_dict(self) -> dict:
160
- return {
161
- "search": self.search_string,
162
- }
163
-
164
-
165
- class RealActionInteraction(BaseModel):
166
- mouse_direction_x: float = 0
167
- mouse_direction_y: float = 0
168
- right_click: bool = False
169
- left_click: bool = False
170
-
171
- @field_validator("mouse_direction_x", "mouse_direction_y")
172
- def prevent_zero(cls, v):
173
- if v > 10:
174
- return 10
175
- elif v < -10:
176
- return -10
177
- return v
178
-
179
- def to_action_dict(self) -> dict:
180
- return {
181
- "camera": [self.mouse_direction_x, self.mouse_direction_y],
182
- "use": int(self.right_click),
183
- "attack": int(self.left_click),
184
- }
185
-
186
-
187
- class StopAction(BaseModel):
188
- """
189
- Action that model can take to stop planning - decide impossible to continue
190
- Note: also known as the "impossible" action
191
- """
192
-
193
- reason: str = ""
194
-
195
-
196
- class NoOp(SymbolicMoveAction):
197
- """No operation action - special instance of move"""
198
-
199
- def __init__(self):
200
- super().__init__(slot_from=0, slot_to=1, quantity=1)
201
- self.slot_to = 0
202
-
203
- def __call__(self, *args, **kwargs):
204
- return None
205
-
206
- def __str__(self):
207
- return "NoOp"
208
-
209
-
210
- # when symbolic action is true, can either move objects around or smelt
211
- SymbolicAction = SymbolicMoveAction # | SymbolicSmeltAction
212
-
213
- # when symbolic action is false, then need to use mouse to move things around, but can use smelt action
214
- RealAction = RealActionInteraction | SymbolicSmeltAction
215
-
216
-
217
- class PydanticSymbolicAction(BaseModel):
218
- root: Union[SymbolicMoveAction, SymbolicSmeltAction]
environments/env_real.py DELETED
@@ -1,316 +0,0 @@
1
- from typing import Sequence, Union
2
-
3
- import numpy as np
4
- import json
5
-
6
- from loguru import logger
7
-
8
- from plancraft.environments.actions import RealAction
9
-
10
- try:
11
- from minerl.env import _singleagent
12
- from minerl.herobraine.env_specs.human_controls import HumanControlEnvSpec
13
- from minerl.herobraine.hero import handlers, mc, spaces
14
- from minerl.herobraine.hero.handler import Handler
15
- from minerl.herobraine.hero.handlers.agent.action import Action
16
- from minerl.herobraine.hero.handlers.agent.start import InventoryAgentStart
17
- from minerl.herobraine.hero.handlers.translation import TranslationHandler
18
-
19
- class InventoryCommandAction(Action):
20
- """
21
- Handler which lets agents programmatically interact with an open container
22
-
23
- Using this - agents can move a chosen quantity of items from one slot to another.
24
- """
25
-
26
- def to_string(self):
27
- return "inventory_command"
28
-
29
- def xml_template(self) -> str:
30
- return str("<InventoryCommands/>")
31
-
32
- def __init__(self):
33
- self._command = "inventory_command"
34
- # first argument is the slot to take from
35
- # second is the slot to put into
36
- # third is the count to take
37
- super().__init__(
38
- self.command,
39
- spaces.Tuple(
40
- (
41
- spaces.Discrete(46),
42
- spaces.Discrete(46),
43
- spaces.Discrete(64),
44
- )
45
- ),
46
- )
47
-
48
- def from_universal(self, x):
49
- return np.array([0, 0, 0], dtype=np.int32)
50
-
51
- class SmeltCommandAction(Action):
52
- """
53
- An action handler for smelting an item
54
- We assume smelting is immediate.
55
- @TODO: might be interesting to explore using the smelting time as an additional planning parameter.
56
-
57
- Using this agents can smelt items in their inventory.
58
- """
59
-
60
- def __init__(self):
61
- self._command = "smelt"
62
- # first argument is the slot to take from
63
- # second is the slot to put into
64
- # third is the count to smelt
65
- super().__init__(
66
- self.command,
67
- spaces.Tuple(
68
- (
69
- spaces.Discrete(46),
70
- spaces.Discrete(46),
71
- spaces.Discrete(64),
72
- )
73
- ),
74
- )
75
-
76
- def to_string(self):
77
- return "smelt"
78
-
79
- def xml_template(self) -> str:
80
- return str("<SmeltCommands/>")
81
-
82
- def from_universal(self, x):
83
- return np.array([0, 0, 0], dtype=np.int32)
84
-
85
- class InventoryResetAction(Action):
86
- def __init__(self):
87
- self._command = "inventory_reset"
88
- super().__init__(self._command, spaces.Text([1]))
89
-
90
- def to_string(self) -> str:
91
- return "inventory_reset"
92
-
93
- def to_hero(self, inventory_items: list[dict]):
94
- return "{} {}".format(self._command, json.dumps(inventory_items))
95
-
96
- def xml_template(self) -> str:
97
- return "<InventoryResetCommands/>"
98
-
99
- def from_universal(self, x):
100
- return []
101
-
102
- MINUTE = 20 * 60
103
-
104
- class CustomInventoryAgentStart(InventoryAgentStart):
105
- def __init__(self, inventory: list[dict[str, Union[str, int]]]):
106
- super().__init__({item["slot"]: item for item in inventory})
107
-
108
- class CraftingTableOnly(Handler):
109
- def to_string(self):
110
- return "start_with_crafting_table"
111
-
112
- def xml_template(self) -> str:
113
- return "<CraftingTableOnly>true</CraftingTableOnly>"
114
-
115
- class InventoryObservation(TranslationHandler):
116
- """
117
- Handles GUI Workbench Observations for selected items
118
- """
119
-
120
- def to_string(self):
121
- return "inventory"
122
-
123
- def xml_template(self) -> str:
124
- return str("""<ObservationFromFullInventory flat="false"/>""")
125
-
126
- def __init__(self, item_list, _other="other"):
127
- item_list = sorted(item_list)
128
- super().__init__(
129
- spaces.Dict(
130
- spaces={
131
- k: spaces.Box(
132
- low=0,
133
- high=2304,
134
- shape=(),
135
- dtype=np.int32,
136
- normalizer_scale="log",
137
- )
138
- for k in item_list
139
- }
140
- )
141
- )
142
- self.num_items = len(item_list)
143
- self.items = item_list
144
-
145
- def add_to_mission_spec(self, mission_spec):
146
- pass
147
-
148
- def from_hero(self, info):
149
- return info["inventory"]
150
-
151
- def from_universal(self, obs):
152
- raise NotImplementedError(
153
- "from_universal not implemented in InventoryObservation"
154
- )
155
-
156
- class PlancraftBaseEnvSpec(HumanControlEnvSpec):
157
- def __init__(
158
- self,
159
- symbolic_action_space=False,
160
- symbolic_observation_space=False,
161
- max_episode_steps=2 * MINUTE,
162
- inventory: Sequence[dict] = (),
163
- preferred_spawn_biome: str = "plains",
164
- resolution=[260, 180],
165
- ):
166
- self.inventory = inventory
167
- self.preferred_spawn_biome = preferred_spawn_biome
168
- self.symbolic_action_space = symbolic_action_space
169
- self.symbolic_observation_space = symbolic_observation_space
170
-
171
- mode = "real"
172
- if symbolic_action_space:
173
- mode += "-symbolic-act"
174
- else:
175
- mode += "-real-act"
176
-
177
- if symbolic_observation_space:
178
- mode += "-symbolic-obs"
179
-
180
- if symbolic_action_space:
181
- cursor_size = 1
182
- else:
183
- cursor_size = 16
184
-
185
- name = f"plancraft-{mode}-v0"
186
- super().__init__(
187
- name=name,
188
- max_episode_steps=max_episode_steps,
189
- resolution=resolution,
190
- cursor_size_range=[cursor_size, cursor_size],
191
- )
192
-
193
- def create_agent_start(self) -> list[Handler]:
194
- base_agent_start_handlers = super().create_agent_start()
195
- return base_agent_start_handlers + [
196
- CustomInventoryAgentStart(self.inventory),
197
- handlers.PreferredSpawnBiome(self.preferred_spawn_biome),
198
- handlers.DoneOnDeath(),
199
- CraftingTableOnly(),
200
- ]
201
-
202
- def create_observables(self) -> list[TranslationHandler]:
203
- if self.symbolic_observation_space:
204
- return [
205
- handlers.POVObservation(self.resolution),
206
- InventoryObservation([item["slot"] for item in self.inventory]),
207
- ]
208
- return [handlers.POVObservation(self.resolution)]
209
-
210
- def create_server_world_generators(self) -> list[Handler]:
211
- # TODO the original biome forced is not implemented yet. Use this for now.
212
- return [handlers.DefaultWorldGenerator(force_reset=True)]
213
-
214
- def create_server_quit_producers(self) -> list[Handler]:
215
- return [
216
- handlers.ServerQuitFromTimeUp(
217
- (self.max_episode_steps * mc.MS_PER_STEP)
218
- ),
219
- handlers.ServerQuitWhenAnyAgentFinishes(),
220
- ]
221
-
222
- def create_server_initial_conditions(self) -> list[Handler]:
223
- return [
224
- handlers.TimeInitialCondition(allow_passage_of_time=False),
225
- handlers.SpawningInitialCondition(allow_spawning=True),
226
- ]
227
-
228
- def create_actionables(self) -> list[TranslationHandler]:
229
- """
230
- Symbolic env can move items around in the inventory using function
231
- Real env can use camera/keyboard
232
- """
233
- # Camera and mouse
234
- if self.symbolic_action_space:
235
- return [
236
- InventoryCommandAction(),
237
- SmeltCommandAction(),
238
- InventoryResetAction(),
239
- ]
240
- return [
241
- handlers.KeybasedCommandAction(v, v) for k, v in mc.KEYMAP.items()
242
- ] + [
243
- handlers.CameraAction(),
244
- SmeltCommandAction(),
245
- InventoryResetAction(),
246
- ]
247
-
248
- def is_from_folder(self, folder: str) -> bool:
249
- return False
250
-
251
- def create_agent_handlers(self) -> list[Handler]:
252
- return []
253
-
254
- def create_mission_handlers(self):
255
- return []
256
-
257
- def create_monitors(self):
258
- return []
259
-
260
- def create_rewardables(self):
261
- return []
262
-
263
- def create_server_decorators(self) -> list[Handler]:
264
- return []
265
-
266
- def determine_success_from_rewards(self, rewards: list) -> bool:
267
- return False
268
-
269
- def get_docstring(self):
270
- return self.__class__.__doc__
271
-
272
- class RealPlancraft(_singleagent._SingleAgentEnv):
273
- def __init__(
274
- self,
275
- inventory: list[dict],
276
- preferred_spawn_biome="plains",
277
- symbolic_action_space=False,
278
- symbolic_observation_space=True,
279
- resolution=[512, 512],
280
- crop=True,
281
- ):
282
- # NOTE: crop is only supported for resolution 512x512 (default)
283
- self.crop = crop
284
- self.resolution = resolution
285
- env_spec = PlancraftBaseEnvSpec(
286
- symbolic_action_space=symbolic_action_space,
287
- symbolic_observation_space=symbolic_observation_space,
288
- preferred_spawn_biome=preferred_spawn_biome,
289
- inventory=inventory,
290
- resolution=resolution,
291
- )
292
- super(RealPlancraft, self).__init__(env_spec=env_spec)
293
- self.reset()
294
-
295
- def step(self, action: RealAction | dict):
296
- if not isinstance(action, dict):
297
- action = action.to_action_dict()
298
- obs, rew, done, info = super().step(action)
299
- if "pov" in obs and self.crop and self.resolution == [512, 512]:
300
- # crop at position x=174, y=170 with width=164 and height=173
301
- obs["pov"] = obs["pov"][174 : 174 + 164, 170 : 168 + 173]
302
- return obs, rew, done, info
303
-
304
- def fast_reset(self, new_inventory: list[dict]):
305
- super().step({"inventory_reset": new_inventory})
306
-
307
-
308
- except ImportError:
309
-
310
- class RealPlancraft:
311
- def __init__(self, *args, **kwargs):
312
- logger.warning(
313
- "The 'minerl' package is required to use RealPlancraft. "
314
- "Please install it using 'pip install plancraft[full]' or 'pip install minerl'."
315
- )
316
- raise ImportError("minerl package not found")