synth-ai 0.2.13.dev1__py3-none-any.whl → 0.2.13.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synth-ai might be problematic. Click here for more details.

Files changed (226) hide show
  1. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +12 -1
  2. examples/swe/task_app/grpo_swe_mini.py +55 -26
  3. examples/swe/task_app/hosted/rollout.py +40 -0
  4. examples/swe/task_app/hosted/test_service.py +5 -6
  5. examples/task_apps/TESTING.md +275 -0
  6. examples/task_apps/__init__.py +0 -0
  7. examples/task_apps/crafter/__init__.py +0 -0
  8. examples/task_apps/crafter/task_app/__init__.py +2 -0
  9. examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +18 -13
  10. examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
  11. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +60 -4
  12. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +25 -3
  13. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +10 -0
  14. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
  15. examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
  16. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
  17. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
  18. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
  19. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
  20. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
  21. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
  22. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
  23. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
  24. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
  25. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
  26. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
  27. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
  28. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
  29. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
  30. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
  31. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
  32. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
  33. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
  34. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
  35. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
  36. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
  37. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
  38. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
  39. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
  40. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
  41. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
  42. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
  43. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
  44. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
  45. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
  46. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
  47. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
  48. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
  49. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
  50. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
  51. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
  52. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
  53. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
  54. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
  55. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
  56. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
  57. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
  58. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
  59. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
  60. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
  61. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
  62. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
  63. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
  64. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
  65. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
  66. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
  67. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
  68. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
  69. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
  70. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
  71. examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
  72. examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
  73. examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
  74. examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
  75. examples/task_apps/enron/__init__.py +1 -0
  76. examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
  77. examples/task_apps/enron/task_app/README.md +14 -0
  78. examples/task_apps/enron/task_app/__init__.py +1 -0
  79. examples/task_apps/enron/task_app/grpo_enron.py +906 -0
  80. examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
  81. examples/task_apps/enron/tests/__init__.py +2 -0
  82. examples/task_apps/enron/tests/conftest.py +115 -0
  83. examples/task_apps/enron/tests/integration/__init__.py +2 -0
  84. examples/task_apps/enron/tests/integration/test_enron_eval.py +177 -0
  85. examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
  86. examples/task_apps/enron/tests/unit/__init__.py +2 -0
  87. examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
  88. examples/task_apps/math/__init__.py +0 -0
  89. examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
  90. examples/task_apps/pokemon_battle/__init__.py +2 -0
  91. examples/task_apps/pokemon_battle/modal_app.py +104 -0
  92. examples/task_apps/pokemon_battle/task_app/README.md +68 -0
  93. examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
  94. examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
  95. examples/task_apps/pokemon_red/README.md +357 -0
  96. examples/task_apps/pokemon_red/__init__.py +3 -0
  97. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
  98. examples/task_apps/pokemon_red/pallet_town_rl_config.toml +73 -0
  99. examples/task_apps/pokemon_red/task_app.py +606 -0
  100. examples/task_apps/pokemon_red/test_pallet_town_rewards.py +191 -0
  101. examples/task_apps/sokoban/README.md +307 -0
  102. examples/task_apps/sokoban/__init__.py +3 -0
  103. examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
  104. examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
  105. examples/task_apps/sokoban/task_app.py +1058 -0
  106. examples/task_apps/sokoban/tests/__init__.py +2 -0
  107. examples/task_apps/sokoban/tests/conftest.py +113 -0
  108. examples/task_apps/sokoban/tests/integration/__init__.py +2 -0
  109. examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
  110. examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
  111. examples/task_apps/sokoban/tests/unit/__init__.py +2 -0
  112. examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
  113. examples/task_apps/verilog/__init__.py +1 -0
  114. examples/task_apps/verilog/eval_groq_qwen32b.toml +20 -0
  115. examples/task_apps/verilog/task_app/README.md +12 -0
  116. examples/task_apps/verilog/task_app/__init__.py +1 -0
  117. examples/task_apps/verilog/task_app/grpo_verilog.py +931 -0
  118. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
  119. examples/task_apps/verilog/tests/__init__.py +2 -0
  120. examples/task_apps/verilog/tests/conftest.py +115 -0
  121. examples/task_apps/verilog/tests/integration/__init__.py +2 -0
  122. examples/task_apps/verilog/tests/integration/test_verilog_eval.py +179 -0
  123. examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
  124. examples/task_apps/verilog/tests/unit/__init__.py +2 -0
  125. examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
  126. examples/vlm/crafter_openai_vlm_agent.py +4 -4
  127. examples/vlm/run_crafter_vlm_benchmark.py +4 -4
  128. examples/workflows/__init__.py +0 -0
  129. examples/workflows/math_rl/__init__.py +0 -0
  130. examples/workflows/math_rl/download_dataset.py +80 -0
  131. synth_ai/__init__.py +2 -2
  132. synth_ai/api/train/builders.py +25 -11
  133. synth_ai/api/train/cli.py +12 -6
  134. synth_ai/api/train/configs/__init__.py +10 -10
  135. synth_ai/api/train/configs/rl.py +5 -4
  136. synth_ai/api/train/configs/sft.py +4 -3
  137. synth_ai/api/train/env_resolver.py +5 -2
  138. synth_ai/api/train/supported_algos.py +10 -5
  139. synth_ai/api/train/utils.py +7 -4
  140. synth_ai/cli/__init__.py +7 -51
  141. synth_ai/cli/_storage.py +4 -3
  142. synth_ai/cli/_validate_task_app.py +11 -0
  143. synth_ai/cli/balance.py +4 -3
  144. synth_ai/cli/calc.py +2 -2
  145. synth_ai/cli/demo.py +14 -7
  146. synth_ai/cli/legacy_root_backup.py +1 -1
  147. synth_ai/cli/rl_demo.py +8 -7
  148. synth_ai/cli/root.py +0 -97
  149. synth_ai/cli/task_apps.py +1707 -186
  150. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
  151. synth_ai/environments/examples/enron/engine.py +7 -2
  152. synth_ai/environments/examples/enron/environment.py +68 -0
  153. synth_ai/environments/examples/red/engine.py +27 -0
  154. synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
  155. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
  156. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
  157. synth_ai/environments/examples/red/environment.py +60 -0
  158. synth_ai/environments/examples/sokoban/taskset.py +116 -0
  159. synth_ai/environments/examples/verilog/engine.py +30 -4
  160. synth_ai/evals/client.py +58 -61
  161. synth_ai/jobs/client.py +16 -4
  162. synth_ai/judge_schemas.py +16 -16
  163. synth_ai/py.typed +0 -0
  164. synth_ai/task/__init__.py +14 -5
  165. synth_ai/task/contracts.py +124 -38
  166. synth_ai/task/proxy.py +48 -56
  167. synth_ai/task/rubrics/__init__.py +53 -0
  168. synth_ai/task/rubrics/loaders.py +133 -0
  169. synth_ai/task/rubrics/models.py +57 -0
  170. synth_ai/task/rubrics/scoring.py +113 -0
  171. synth_ai/{rubrics/validators.py → task/rubrics/strict.py} +53 -30
  172. synth_ai/task/server.py +8 -7
  173. synth_ai/task/validators.py +269 -6
  174. synth_ai/tracing_v3/decorators.py +7 -3
  175. synth_ai/tracing_v3/replica_sync.py +4 -4
  176. synth_ai/tracing_v3/serialization.py +5 -5
  177. synth_ai/tracing_v3/trace_utils.py +317 -0
  178. synth_ai/tracing_v3/turso/native_manager.py +3 -3
  179. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
  180. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +214 -101
  181. examples/agora_ex/README_MoE.md +0 -224
  182. examples/agora_ex/__init__.py +0 -7
  183. examples/agora_ex/agora_ex.py +0 -65
  184. examples/agora_ex/agora_ex_task_app.py +0 -590
  185. examples/agora_ex/configs/rl_lora_qwen3_moe_2xh200.toml +0 -121
  186. examples/agora_ex/reward_fn_grpo-human.py +0 -129
  187. examples/agora_ex/system_prompt_CURRENT.md +0 -63
  188. examples/agora_ex/task_app/agora_ex_task_app.py +0 -590
  189. examples/agora_ex/task_app/reward_fn_grpo-human.py +0 -129
  190. examples/agora_ex/task_app/system_prompt_CURRENT.md +0 -63
  191. synth_ai/rubrics/__init__.py +0 -22
  192. synth_ai/task/rubrics.py +0 -219
  193. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
  194. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
  195. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
  196. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
  197. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
  198. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
  199. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
  200. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
  201. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -0
  202. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -0
  203. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
  204. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
  205. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
  206. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
  207. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +0 -0
  208. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
  209. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
  210. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
  211. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
  212. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
  213. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/utils.py +0 -0
  214. /examples/{rl/task_app → task_apps/math}/README.md +0 -0
  215. /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
  216. /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
  217. /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
  218. /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
  219. /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
  220. /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
  221. /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
  222. /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
  223. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
  224. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -0
  225. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
  226. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,41 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from .enums import StatusCondition, PokemonType
4
+
5
+ @dataclass
6
+ class PokemonData:
7
+ """Complete Pokemon data structure for Emerald"""
8
+ species_id: int
9
+ species_name: str
10
+ current_hp: int
11
+ max_hp: int
12
+ level: int
13
+ status: StatusCondition
14
+ type1: PokemonType
15
+ type2: Optional[PokemonType]
16
+ moves: list[str] # Move names
17
+ move_pp: list[int] # PP for each move
18
+ trainer_id: int
19
+ nickname: Optional[str] = None
20
+ experience: Optional[int] = None
21
+
22
+ @property
23
+ def is_asleep(self) -> bool:
24
+ """Check if the Pokémon is asleep"""
25
+ return self.status.is_asleep
26
+
27
+ @property
28
+ def status_name(self) -> str:
29
+ """Return a human-readable status name"""
30
+ if self.is_asleep:
31
+ return "SLEEP"
32
+ elif self.status & StatusCondition.PARALYSIS:
33
+ return "PARALYSIS"
34
+ elif self.status & StatusCondition.FREEZE:
35
+ return "FREEZE"
36
+ elif self.status & StatusCondition.BURN:
37
+ return "BURN"
38
+ elif self.status & StatusCondition.POISON:
39
+ return "POISON"
40
+ else:
41
+ return "OK"
@@ -0,0 +1,298 @@
1
+ """
2
+ Utility functions for Pokemon Emerald memory reading
3
+ """
4
+
5
+ from typing import List, Tuple, Optional
6
+ from .enums import MetatileBehavior, PokemonType, PokemonSpecies, Move
7
+
8
+
9
+ def is_passable_behavior(behavior: MetatileBehavior) -> bool:
10
+ """
11
+ Check if a metatile behavior allows the player to walk on it
12
+
13
+ Args:
14
+ behavior: The metatile behavior to check
15
+
16
+ Returns:
17
+ True if the tile is passable, False otherwise
18
+ """
19
+ # List of behaviors that are passable
20
+ passable_behaviors = {
21
+ MetatileBehavior.NORMAL,
22
+ MetatileBehavior.TALL_GRASS,
23
+ MetatileBehavior.LONG_GRASS,
24
+ MetatileBehavior.SHORT_GRASS,
25
+ MetatileBehavior.SAND,
26
+ MetatileBehavior.ASHGRASS,
27
+ MetatileBehavior.FOOTPRINTS,
28
+ MetatileBehavior.PUDDLE,
29
+ MetatileBehavior.SHALLOW_WATER,
30
+ MetatileBehavior.ICE,
31
+ MetatileBehavior.THIN_ICE,
32
+ MetatileBehavior.CRACKED_ICE,
33
+ MetatileBehavior.HOT_SPRINGS,
34
+ MetatileBehavior.MUDDY_SLOPE,
35
+ MetatileBehavior.BUMPY_SLOPE,
36
+ MetatileBehavior.CRACKED_FLOOR,
37
+ MetatileBehavior.VERTICAL_RAIL,
38
+ MetatileBehavior.HORIZONTAL_RAIL,
39
+ MetatileBehavior.ISOLATED_VERTICAL_RAIL,
40
+ MetatileBehavior.ISOLATED_HORIZONTAL_RAIL,
41
+ }
42
+
43
+ return behavior in passable_behaviors
44
+
45
+
46
+ def is_encounter_behavior(behavior: MetatileBehavior) -> bool:
47
+ """
48
+ Check if a metatile behavior can trigger wild Pokemon encounters
49
+
50
+ Args:
51
+ behavior: The metatile behavior to check
52
+
53
+ Returns:
54
+ True if the tile can trigger encounters, False otherwise
55
+ """
56
+ encounter_behaviors = {
57
+ MetatileBehavior.TALL_GRASS,
58
+ MetatileBehavior.LONG_GRASS,
59
+ MetatileBehavior.INDOOR_ENCOUNTER,
60
+ MetatileBehavior.CAVE,
61
+ MetatileBehavior.DEEP_WATER,
62
+ MetatileBehavior.OCEAN_WATER,
63
+ MetatileBehavior.SHALLOW_WATER,
64
+ }
65
+
66
+ return behavior in encounter_behaviors
67
+
68
+
69
+ def is_surfable_behavior(behavior: MetatileBehavior) -> bool:
70
+ """
71
+ Check if a metatile behavior allows surfing
72
+
73
+ Args:
74
+ behavior: The metatile behavior to check
75
+
76
+ Returns:
77
+ True if the tile is surfable, False otherwise
78
+ """
79
+ surfable_behaviors = {
80
+ MetatileBehavior.DEEP_WATER,
81
+ MetatileBehavior.OCEAN_WATER,
82
+ MetatileBehavior.SHALLOW_WATER,
83
+ MetatileBehavior.POND_WATER,
84
+ MetatileBehavior.INTERIOR_DEEP_WATER,
85
+ MetatileBehavior.SOOTOPOLIS_DEEP_WATER,
86
+ }
87
+
88
+ return behavior in surfable_behaviors
89
+
90
+
91
+ def get_type_effectiveness(attacking_type: PokemonType, defending_type: PokemonType) -> float:
92
+ """
93
+ Calculate type effectiveness between two Pokemon types
94
+
95
+ Args:
96
+ attacking_type: The type of the attacking move
97
+ defending_type: The type of the defending Pokemon
98
+
99
+ Returns:
100
+ Effectiveness multiplier (0.0, 0.25, 0.5, 1.0, 2.0, or 4.0)
101
+ """
102
+ # Type effectiveness chart (simplified - only includes common types)
103
+ # This is a basic implementation - a full chart would be much larger
104
+ effectiveness_chart = {
105
+ PokemonType.NORMAL: {
106
+ PokemonType.ROCK: 0.5,
107
+ PokemonType.GHOST: 0.0,
108
+ PokemonType.STEEL: 0.5,
109
+ },
110
+ PokemonType.FIRE: {
111
+ PokemonType.FIRE: 0.5,
112
+ PokemonType.WATER: 0.5,
113
+ PokemonType.GRASS: 2.0,
114
+ PokemonType.ICE: 2.0,
115
+ PokemonType.BUG: 2.0,
116
+ PokemonType.ROCK: 0.5,
117
+ PokemonType.DRAGON: 0.5,
118
+ PokemonType.STEEL: 2.0,
119
+ },
120
+ PokemonType.WATER: {
121
+ PokemonType.FIRE: 2.0,
122
+ PokemonType.WATER: 0.5,
123
+ PokemonType.GRASS: 0.5,
124
+ PokemonType.GROUND: 2.0,
125
+ PokemonType.ROCK: 2.0,
126
+ PokemonType.DRAGON: 0.5,
127
+ },
128
+ PokemonType.GRASS: {
129
+ PokemonType.FIRE: 0.5,
130
+ PokemonType.WATER: 2.0,
131
+ PokemonType.GRASS: 0.5,
132
+ PokemonType.POISON: 0.5,
133
+ PokemonType.GROUND: 2.0,
134
+ PokemonType.FLYING: 0.5,
135
+ PokemonType.BUG: 0.5,
136
+ PokemonType.ROCK: 2.0,
137
+ PokemonType.DRAGON: 0.5,
138
+ PokemonType.STEEL: 0.5,
139
+ },
140
+ PokemonType.ELECTRIC: {
141
+ PokemonType.WATER: 2.0,
142
+ PokemonType.GRASS: 0.5,
143
+ PokemonType.ELECTRIC: 0.5,
144
+ PokemonType.GROUND: 0.0,
145
+ PokemonType.FLYING: 2.0,
146
+ PokemonType.DRAGON: 0.5,
147
+ },
148
+ PokemonType.ICE: {
149
+ PokemonType.FIRE: 0.5,
150
+ PokemonType.WATER: 0.5,
151
+ PokemonType.GRASS: 2.0,
152
+ PokemonType.ICE: 0.5,
153
+ PokemonType.GROUND: 2.0,
154
+ PokemonType.FLYING: 2.0,
155
+ PokemonType.DRAGON: 2.0,
156
+ PokemonType.STEEL: 0.5,
157
+ },
158
+ PokemonType.FIGHTING: {
159
+ PokemonType.NORMAL: 2.0,
160
+ PokemonType.ICE: 2.0,
161
+ PokemonType.POISON: 0.5,
162
+ PokemonType.FLYING: 0.5,
163
+ PokemonType.PSYCHIC: 0.5,
164
+ PokemonType.BUG: 0.5,
165
+ PokemonType.ROCK: 2.0,
166
+ PokemonType.GHOST: 0.0,
167
+ PokemonType.STEEL: 2.0,
168
+ PokemonType.DARK: 2.0,
169
+ },
170
+ PokemonType.POISON: {
171
+ PokemonType.GRASS: 2.0,
172
+ PokemonType.POISON: 0.5,
173
+ PokemonType.GROUND: 0.5,
174
+ PokemonType.ROCK: 0.5,
175
+ PokemonType.GHOST: 0.5,
176
+ PokemonType.STEEL: 0.0,
177
+ },
178
+ PokemonType.GROUND: {
179
+ PokemonType.FIRE: 2.0,
180
+ PokemonType.GRASS: 0.5,
181
+ PokemonType.ELECTRIC: 2.0,
182
+ PokemonType.POISON: 2.0,
183
+ PokemonType.FLYING: 0.0,
184
+ PokemonType.BUG: 0.5,
185
+ PokemonType.ROCK: 2.0,
186
+ PokemonType.STEEL: 2.0,
187
+ },
188
+ PokemonType.FLYING: {
189
+ PokemonType.GRASS: 2.0,
190
+ PokemonType.ELECTRIC: 0.5,
191
+ PokemonType.FIGHTING: 2.0,
192
+ PokemonType.BUG: 2.0,
193
+ PokemonType.ROCK: 0.5,
194
+ PokemonType.STEEL: 0.5,
195
+ },
196
+ PokemonType.PSYCHIC: {
197
+ PokemonType.FIGHTING: 2.0,
198
+ PokemonType.POISON: 2.0,
199
+ PokemonType.PSYCHIC: 0.5,
200
+ PokemonType.DARK: 0.0,
201
+ PokemonType.STEEL: 0.5,
202
+ },
203
+ PokemonType.BUG: {
204
+ PokemonType.FIRE: 0.5,
205
+ PokemonType.GRASS: 2.0,
206
+ PokemonType.FIGHTING: 0.5,
207
+ PokemonType.POISON: 0.5,
208
+ PokemonType.FLYING: 0.5,
209
+ PokemonType.PSYCHIC: 2.0,
210
+ PokemonType.GHOST: 0.5,
211
+ PokemonType.DARK: 2.0,
212
+ PokemonType.STEEL: 0.5,
213
+ },
214
+ PokemonType.ROCK: {
215
+ PokemonType.FIRE: 2.0,
216
+ PokemonType.ICE: 2.0,
217
+ PokemonType.FIGHTING: 0.5,
218
+ PokemonType.GROUND: 0.5,
219
+ PokemonType.FLYING: 2.0,
220
+ PokemonType.BUG: 2.0,
221
+ PokemonType.STEEL: 0.5,
222
+ },
223
+ PokemonType.GHOST: {
224
+ PokemonType.NORMAL: 0.0,
225
+ PokemonType.PSYCHIC: 2.0,
226
+ PokemonType.GHOST: 2.0,
227
+ PokemonType.DARK: 0.5,
228
+ },
229
+ PokemonType.DRAGON: {
230
+ PokemonType.DRAGON: 2.0,
231
+ PokemonType.STEEL: 0.5,
232
+ },
233
+ PokemonType.DARK: {
234
+ PokemonType.FIGHTING: 0.5,
235
+ PokemonType.PSYCHIC: 2.0,
236
+ PokemonType.GHOST: 2.0,
237
+ PokemonType.DARK: 0.5,
238
+ PokemonType.STEEL: 0.5,
239
+ },
240
+ PokemonType.STEEL: {
241
+ PokemonType.FIRE: 0.5,
242
+ PokemonType.WATER: 0.5,
243
+ PokemonType.ELECTRIC: 0.5,
244
+ PokemonType.ICE: 2.0,
245
+ PokemonType.ROCK: 2.0,
246
+ PokemonType.STEEL: 0.5,
247
+ },
248
+ }
249
+
250
+ if attacking_type in effectiveness_chart and defending_type in effectiveness_chart[attacking_type]:
251
+ return effectiveness_chart[attacking_type][defending_type]
252
+
253
+ return 1.0 # Normal effectiveness
254
+
255
+
256
+ def format_time(hours: int, minutes: int, seconds: int) -> str:
257
+ """
258
+ Format game time in a human-readable format
259
+
260
+ Args:
261
+ hours: Number of hours
262
+ minutes: Number of minutes
263
+ seconds: Number of seconds
264
+
265
+ Returns:
266
+ Formatted time string
267
+ """
268
+ return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
269
+
270
+
271
+ def format_money(amount: int) -> str:
272
+ """
273
+ Format money amount with commas
274
+
275
+ Args:
276
+ amount: Money amount in cents
277
+
278
+ Returns:
279
+ Formatted money string
280
+ """
281
+ return f"${amount:,}"
282
+
283
+
284
+ def get_pokemon_type_names(type1: PokemonType, type2: Optional[PokemonType] = None) -> str:
285
+ """
286
+ Get formatted type names for a Pokemon
287
+
288
+ Args:
289
+ type1: Primary type
290
+ type2: Secondary type (optional)
291
+
292
+ Returns:
293
+ Formatted type string
294
+ """
295
+ if type2 is None or type1 == type2:
296
+ return type1.name.replace("_", " ")
297
+ else:
298
+ return f"{type1.name.replace('_', ' ')} / {type2.name.replace('_', ' ')}"
@@ -0,0 +1,95 @@
1
+ [project]
2
+ name = "pokeagent-speedrun"
3
+ version = "0.1.0"
4
+ description = "AI agent that plays Pokémon Emerald using vision-language models"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10,<3.12"
7
+
8
+ dependencies = [
9
+ "flask==3.0.2",
10
+ "flask-socketio==5.3.6",
11
+ "python-socketio==5.11.1",
12
+ "python-engineio==4.9.0",
13
+ "numpy==1.26.4",
14
+ "opencv-python==4.9.0.80",
15
+ "pillow==10.2.0",
16
+ "pygame==2.6.1",
17
+ "requests>=2.32.2",
18
+ "python-dotenv==1.0.1",
19
+ "eventlet==0.35.2",
20
+ "gevent==23.9.1",
21
+ "gevent-websocket==0.10.1",
22
+ "fastapi>=0.104.0",
23
+ "uvicorn[standard]>=0.24.0",
24
+ "pydantic>=2.0.0",
25
+ "mgba==0.10.2",
26
+ "pyyaml>=5.1",
27
+ "xxhash",
28
+ "more-itertools",
29
+ "pytz>=2020.1",
30
+ "six>=1.14.0",
31
+ "colorama",
32
+ "grpcio>=1.48.2",
33
+ "protobuf!=4.24.0,<5.0.0,>=3.19.6",
34
+ "openai==1.90.0",
35
+ "google-generativeai>=0.3.0",
36
+ "torch>=2.0.0",
37
+ "transformers>=4.36.0",
38
+ "bitsandbytes>=0.41.0",
39
+ "accelerate>=0.25.0",
40
+ "sentencepiece>=0.1.99",
41
+ "cffi>=1.6",
42
+ "pytesseract>=0.3.10",
43
+ ]
44
+
45
+ [project.optional-dependencies]
46
+ dev = [
47
+ "pytest>=7.0.0",
48
+ "pytest-cov>=4.0.0",
49
+ "ruff>=0.1.0",
50
+ "mypy>=1.0.0",
51
+ ]
52
+
53
+ [build-system]
54
+ requires = ["hatchling"]
55
+ build-backend = "hatchling.build"
56
+
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["agent", "pokemon_env", "utils", "server"]
59
+
60
+ [tool.uv]
61
+ dev-dependencies = [
62
+ "pytest>=7.0.0",
63
+ "pytest-cov>=4.0.0",
64
+ "ruff>=0.1.0",
65
+ "mypy>=1.0.0",
66
+ ]
67
+
68
+ [tool.ruff]
69
+ line-length = 120
70
+ target-version = "py310"
71
+
72
+ [tool.ruff.lint]
73
+ select = [
74
+ "E", # pycodestyle errors
75
+ "W", # pycodestyle warnings
76
+ "F", # pyflakes
77
+ "I", # isort
78
+ "B", # flake8-bugbear
79
+ "C4", # flake8-comprehensions
80
+ "UP", # pyupgrade
81
+ ]
82
+ ignore = [
83
+ "E501", # line too long
84
+ "B008", # do not perform function calls in argument defaults
85
+ "W191", # indentation contains tabs
86
+ ]
87
+
88
+ [tool.ruff.lint.isort]
89
+ known-first-party = ["agent", "pokemon_env", "utils", "server"]
90
+
91
+ [tool.mypy]
92
+ python_version = "3.10"
93
+ warn_return_any = true
94
+ warn_unused_configs = true
95
+ disallow_untyped_defs = false
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Main entry point for the Pokemon Agent.
4
+ This is a streamlined version that focuses on multiprocess mode only.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import time
10
+ import argparse
11
+ import subprocess
12
+ import signal
13
+
14
+ # Add parent directory to path for imports
15
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
16
+ from server.client import run_multiprocess_client
17
+
18
+
19
+ def start_server(args):
20
+ """Start the server process with appropriate arguments"""
21
+ # Use the same Python executable that's running this script
22
+ python_exe = sys.executable
23
+ server_cmd = [python_exe, "-m", "server.app", "--port", str(args.port)]
24
+
25
+ # Pass through server-relevant arguments
26
+ if args.record:
27
+ server_cmd.append("--record")
28
+
29
+ if args.load_checkpoint:
30
+ # Auto-load checkpoint.state when --load-checkpoint is used
31
+ checkpoint_state = ".pokeagent_cache/checkpoint.state"
32
+ if os.path.exists(checkpoint_state):
33
+ server_cmd.extend(["--load-state", checkpoint_state])
34
+ # Set environment variable to enable LLM checkpoint loading
35
+ os.environ["LOAD_CHECKPOINT_MODE"] = "true"
36
+ print(f"🔄 Server will load checkpoint: {checkpoint_state}")
37
+ print(f"🔄 LLM metrics will be restored from .pokeagent_cache/checkpoint_llm.txt")
38
+ else:
39
+ print(f"⚠️ Checkpoint file not found: {checkpoint_state}")
40
+ elif args.load_state:
41
+ server_cmd.extend(["--load-state", args.load_state])
42
+
43
+ # Don't pass --manual to server - server should always run in server mode
44
+ # The --manual flag only affects client behavior
45
+
46
+ if args.no_ocr:
47
+ server_cmd.append("--no-ocr")
48
+
49
+ # Server always runs headless - display handled by client
50
+
51
+ # Start server as subprocess
52
+ try:
53
+ print(f"📋 Server command: {' '.join(server_cmd)}")
54
+ server_process = subprocess.Popen(
55
+ server_cmd,
56
+ universal_newlines=True,
57
+ bufsize=1
58
+ )
59
+ print(f"✅ Server started with PID {server_process.pid}")
60
+ print("⏳ Waiting 3 seconds for server to initialize...")
61
+ time.sleep(3)
62
+
63
+ return server_process
64
+
65
+ except Exception as e:
66
+ print(f"❌ Failed to start server: {e}")
67
+ return None
68
+
69
+
70
+ def start_frame_server():
71
+ """Start the lightweight frame server for stream.html visualization"""
72
+ try:
73
+ frame_cmd = ["python", "-m", "server.frame_server"]
74
+ frame_process = subprocess.Popen(
75
+ frame_cmd,
76
+ stdout=subprocess.PIPE,
77
+ stderr=subprocess.PIPE
78
+ )
79
+ print(f"🖼️ Frame server started with PID {frame_process.pid}")
80
+ return frame_process
81
+ except Exception as e:
82
+ print(f"⚠️ Could not start frame server: {e}")
83
+ return None
84
+
85
+
86
+ def main():
87
+ """Main entry point for the Pokemon Agent"""
88
+ parser = argparse.ArgumentParser(description="Pokemon Emerald AI Agent")
89
+
90
+ # Core arguments
91
+ parser.add_argument("--rom", type=str, default="Emerald-GBAdvance/rom.gba",
92
+ help="Path to ROM file")
93
+ parser.add_argument("--port", type=int, default=8000,
94
+ help="Port for web interface")
95
+
96
+ # State loading
97
+ parser.add_argument("--load-state", type=str,
98
+ help="Load a saved state file on startup")
99
+ parser.add_argument("--load-checkpoint", action="store_true",
100
+ help="Load from checkpoint files")
101
+
102
+ # Agent configuration
103
+ parser.add_argument("--backend", type=str, default="gemini",
104
+ help="VLM backend (openai, gemini, local, openrouter)")
105
+ parser.add_argument("--model-name", type=str, default="gemini-2.5-flash",
106
+ help="Model name to use")
107
+ parser.add_argument("--simple", action="store_true",
108
+ help="Simple mode: direct frame->action without 4-module architecture")
109
+
110
+ # Operation modes
111
+ parser.add_argument("--headless", action="store_true",
112
+ help="Run without pygame display (headless)")
113
+ parser.add_argument("--agent-auto", action="store_true",
114
+ help="Agent acts automatically")
115
+ parser.add_argument("--manual", action="store_true",
116
+ help="Start in manual mode instead of agent mode")
117
+
118
+ # Features
119
+ parser.add_argument("--record", action="store_true",
120
+ help="Record video of the gameplay")
121
+ parser.add_argument("--no-ocr", action="store_true",
122
+ help="Disable OCR dialogue detection")
123
+
124
+ args = parser.parse_args()
125
+
126
+ print("=" * 60)
127
+ print("🎮 Pokemon Emerald AI Agent")
128
+ print("=" * 60)
129
+
130
+ server_process = None
131
+ frame_server_process = None
132
+
133
+ try:
134
+ # Auto-start server if requested
135
+ if args.agent_auto or args.manual:
136
+ print("\n📡 Starting server process...")
137
+ server_process = start_server(args)
138
+
139
+ if not server_process:
140
+ print("❌ Failed to start server, exiting...")
141
+ return 1
142
+
143
+ # Also start frame server for web visualization
144
+ frame_server_process = start_frame_server()
145
+ else:
146
+ print("\n📋 Manual server mode - start server separately with:")
147
+ print(" python -m server.app --port", args.port)
148
+ if args.load_state:
149
+ print(f" (Add --load-state {args.load_state} to server command)")
150
+ print("\n⏳ Waiting 3 seconds for manual server startup...")
151
+ time.sleep(3)
152
+
153
+ # Display configuration
154
+ print("\n🤖 Agent Configuration:")
155
+ print(f" Backend: {args.backend}")
156
+ print(f" Model: {args.model_name}")
157
+ if args.simple:
158
+ print(" Mode: Simple (direct frame->action)")
159
+ else:
160
+ print(" Mode: Four-module architecture")
161
+ if args.no_ocr:
162
+ print(" OCR: Disabled")
163
+ if args.record:
164
+ print(" Recording: Enabled")
165
+
166
+ print(f"\n🌐 Web Interface: http://127.0.0.1:{args.port}")
167
+ print(f"🎥 Stream View: http://127.0.0.1:{args.port}/stream.html")
168
+
169
+ print("\n🚀 Starting client...")
170
+ print("-" * 60)
171
+
172
+ # Run the client
173
+ success = run_multiprocess_client(server_port=args.port, args=args)
174
+
175
+ return 0 if success else 1
176
+
177
+ except KeyboardInterrupt:
178
+ print("\n\n🛑 Shutdown requested by user")
179
+ return 0
180
+
181
+ finally:
182
+ # Clean up server processes
183
+ if server_process:
184
+ print("\n📡 Stopping server process...")
185
+ server_process.terminate()
186
+ try:
187
+ server_process.wait(timeout=5)
188
+ except subprocess.TimeoutExpired:
189
+ print(" Force killing server...")
190
+ server_process.kill()
191
+
192
+ if frame_server_process:
193
+ print("🖼️ Stopping frame server...")
194
+ frame_server_process.terminate()
195
+ try:
196
+ frame_server_process.wait(timeout=2)
197
+ except:
198
+ frame_server_process.kill()
199
+
200
+ print("👋 Goodbye!")
201
+
202
+
203
+ if __name__ == "__main__":
204
+ sys.exit(main())