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.
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +12 -1
- examples/swe/task_app/grpo_swe_mini.py +55 -26
- examples/swe/task_app/hosted/rollout.py +40 -0
- examples/swe/task_app/hosted/test_service.py +5 -6
- examples/task_apps/TESTING.md +275 -0
- examples/task_apps/__init__.py +0 -0
- examples/task_apps/crafter/__init__.py +0 -0
- examples/task_apps/crafter/task_app/__init__.py +2 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +18 -13
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +60 -4
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +25 -3
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +10 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
- examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
- examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
- examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
- examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
- examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
- examples/task_apps/enron/task_app/README.md +14 -0
- examples/task_apps/enron/task_app/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron.py +906 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
- examples/task_apps/enron/tests/__init__.py +2 -0
- examples/task_apps/enron/tests/conftest.py +115 -0
- examples/task_apps/enron/tests/integration/__init__.py +2 -0
- examples/task_apps/enron/tests/integration/test_enron_eval.py +177 -0
- examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
- examples/task_apps/enron/tests/unit/__init__.py +2 -0
- examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
- examples/task_apps/math/__init__.py +0 -0
- examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
- examples/task_apps/pokemon_battle/__init__.py +2 -0
- examples/task_apps/pokemon_battle/modal_app.py +104 -0
- examples/task_apps/pokemon_battle/task_app/README.md +68 -0
- examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
- examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
- examples/task_apps/pokemon_red/README.md +357 -0
- examples/task_apps/pokemon_red/__init__.py +3 -0
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
- examples/task_apps/pokemon_red/pallet_town_rl_config.toml +73 -0
- examples/task_apps/pokemon_red/task_app.py +606 -0
- examples/task_apps/pokemon_red/test_pallet_town_rewards.py +191 -0
- examples/task_apps/sokoban/README.md +307 -0
- examples/task_apps/sokoban/__init__.py +3 -0
- examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
- examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
- examples/task_apps/sokoban/task_app.py +1058 -0
- examples/task_apps/sokoban/tests/__init__.py +2 -0
- examples/task_apps/sokoban/tests/conftest.py +113 -0
- examples/task_apps/sokoban/tests/integration/__init__.py +2 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
- examples/task_apps/sokoban/tests/unit/__init__.py +2 -0
- examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
- examples/task_apps/verilog/__init__.py +1 -0
- examples/task_apps/verilog/eval_groq_qwen32b.toml +20 -0
- examples/task_apps/verilog/task_app/README.md +12 -0
- examples/task_apps/verilog/task_app/__init__.py +1 -0
- examples/task_apps/verilog/task_app/grpo_verilog.py +931 -0
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
- examples/task_apps/verilog/tests/__init__.py +2 -0
- examples/task_apps/verilog/tests/conftest.py +115 -0
- examples/task_apps/verilog/tests/integration/__init__.py +2 -0
- examples/task_apps/verilog/tests/integration/test_verilog_eval.py +179 -0
- examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
- examples/task_apps/verilog/tests/unit/__init__.py +2 -0
- examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
- examples/vlm/crafter_openai_vlm_agent.py +4 -4
- examples/vlm/run_crafter_vlm_benchmark.py +4 -4
- examples/workflows/__init__.py +0 -0
- examples/workflows/math_rl/__init__.py +0 -0
- examples/workflows/math_rl/download_dataset.py +80 -0
- synth_ai/__init__.py +2 -2
- synth_ai/api/train/builders.py +25 -11
- synth_ai/api/train/cli.py +12 -6
- synth_ai/api/train/configs/__init__.py +10 -10
- synth_ai/api/train/configs/rl.py +5 -4
- synth_ai/api/train/configs/sft.py +4 -3
- synth_ai/api/train/env_resolver.py +5 -2
- synth_ai/api/train/supported_algos.py +10 -5
- synth_ai/api/train/utils.py +7 -4
- synth_ai/cli/__init__.py +7 -51
- synth_ai/cli/_storage.py +4 -3
- synth_ai/cli/_validate_task_app.py +11 -0
- synth_ai/cli/balance.py +4 -3
- synth_ai/cli/calc.py +2 -2
- synth_ai/cli/demo.py +14 -7
- synth_ai/cli/legacy_root_backup.py +1 -1
- synth_ai/cli/rl_demo.py +8 -7
- synth_ai/cli/root.py +0 -97
- synth_ai/cli/task_apps.py +1707 -186
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
- synth_ai/environments/examples/enron/engine.py +7 -2
- synth_ai/environments/examples/enron/environment.py +68 -0
- synth_ai/environments/examples/red/engine.py +27 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
- synth_ai/environments/examples/red/environment.py +60 -0
- synth_ai/environments/examples/sokoban/taskset.py +116 -0
- synth_ai/environments/examples/verilog/engine.py +30 -4
- synth_ai/evals/client.py +58 -61
- synth_ai/jobs/client.py +16 -4
- synth_ai/judge_schemas.py +16 -16
- synth_ai/py.typed +0 -0
- synth_ai/task/__init__.py +14 -5
- synth_ai/task/contracts.py +124 -38
- synth_ai/task/proxy.py +48 -56
- synth_ai/task/rubrics/__init__.py +53 -0
- synth_ai/task/rubrics/loaders.py +133 -0
- synth_ai/task/rubrics/models.py +57 -0
- synth_ai/task/rubrics/scoring.py +113 -0
- synth_ai/{rubrics/validators.py → task/rubrics/strict.py} +53 -30
- synth_ai/task/server.py +8 -7
- synth_ai/task/validators.py +269 -6
- synth_ai/tracing_v3/decorators.py +7 -3
- synth_ai/tracing_v3/replica_sync.py +4 -4
- synth_ai/tracing_v3/serialization.py +5 -5
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/native_manager.py +3 -3
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +214 -101
- examples/agora_ex/README_MoE.md +0 -224
- examples/agora_ex/__init__.py +0 -7
- examples/agora_ex/agora_ex.py +0 -65
- examples/agora_ex/agora_ex_task_app.py +0 -590
- examples/agora_ex/configs/rl_lora_qwen3_moe_2xh200.toml +0 -121
- examples/agora_ex/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/system_prompt_CURRENT.md +0 -63
- examples/agora_ex/task_app/agora_ex_task_app.py +0 -590
- examples/agora_ex/task_app/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/task_app/system_prompt_CURRENT.md +0 -63
- synth_ai/rubrics/__init__.py +0 -22
- synth_ai/task/rubrics.py +0 -219
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/utils.py +0 -0
- /examples/{rl/task_app → task_apps/math}/README.md +0 -0
- /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
- /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
import logging
|
|
5
|
+
import numpy as np
|
|
6
|
+
from collections import deque
|
|
7
|
+
|
|
8
|
+
# Set up logging
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
# Milestone tracking for progression verification - exact milestones for submission.log
|
|
12
|
+
MILESTONES = [
|
|
13
|
+
# Phase 1: Game Initialization
|
|
14
|
+
'GAME_RUNNING', 'PLAYER_NAME_SET', 'INTRO_CUTSCENE_COMPLETE',
|
|
15
|
+
|
|
16
|
+
# Phase 2: Tutorial & Starting Town
|
|
17
|
+
'LITTLEROOT_TOWN', 'PLAYER_HOUSE_ENTERED', 'PLAYER_BEDROOM',
|
|
18
|
+
'RIVAL_HOUSE', 'RIVAL_BEDROOM',
|
|
19
|
+
|
|
20
|
+
# Phase 3: Professor Birch & Starter
|
|
21
|
+
'ROUTE_101', 'STARTER_CHOSEN', 'BIRCH_LAB_VISITED',
|
|
22
|
+
|
|
23
|
+
# Phase 4: Rival
|
|
24
|
+
'OLDALE_TOWN', 'ROUTE_103', 'RECEIVED_POKEDEX',
|
|
25
|
+
|
|
26
|
+
# Phase 5: Route 102 & Petalburg
|
|
27
|
+
'ROUTE_102', 'PETALBURG_CITY', 'DAD_FIRST_MEETING', 'GYM_EXPLANATION',
|
|
28
|
+
|
|
29
|
+
# Phase 6: Road to Rustboro City
|
|
30
|
+
'ROUTE_104_SOUTH', 'PETALBURG_WOODS', 'TEAM_AQUA_GRUNT_DEFEATED',
|
|
31
|
+
'ROUTE_104_NORTH', 'RUSTBORO_CITY',
|
|
32
|
+
|
|
33
|
+
# Phase 7: First Gym Challenge
|
|
34
|
+
'RUSTBORO_GYM_ENTERED', 'ROXANNE_DEFEATED', 'FIRST_GYM_COMPLETE'
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
class AntiCheatTracker:
|
|
38
|
+
"""
|
|
39
|
+
Tracks anti-cheat metrics and behavioral patterns for Pokemon Emerald AI agent.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
self.latest_milestone = None
|
|
44
|
+
self.previous_position = None
|
|
45
|
+
self.decision_times = deque(maxlen=100) # Track last 100 decision times
|
|
46
|
+
self.invalid_actions = 0
|
|
47
|
+
self.total_actions = 0
|
|
48
|
+
self.exploration_moves = 0 # Moves that deviate from direct paths
|
|
49
|
+
self.backtrack_moves = 0 # Moves that return to previous positions
|
|
50
|
+
self.position_history = deque(maxlen=20) # Track recent positions
|
|
51
|
+
self.start_time = None # Will be set when logging is initialized
|
|
52
|
+
|
|
53
|
+
# Set up submission logging (avoid duplicate handlers)
|
|
54
|
+
self.submission_logger = logging.getLogger('submission')
|
|
55
|
+
self.submission_logger.setLevel(logging.INFO)
|
|
56
|
+
|
|
57
|
+
# Only add handler if none exists
|
|
58
|
+
if not self.submission_logger.handlers:
|
|
59
|
+
submission_handler = logging.FileHandler('submission.log', mode='a')
|
|
60
|
+
submission_handler.setLevel(logging.INFO) # Explicitly set handler level
|
|
61
|
+
submission_formatter = logging.Formatter('%(message)s')
|
|
62
|
+
submission_handler.setFormatter(submission_formatter)
|
|
63
|
+
self.submission_logger.addHandler(submission_handler)
|
|
64
|
+
self.submission_logger.propagate = False
|
|
65
|
+
|
|
66
|
+
def create_state_hash(self, state_data):
|
|
67
|
+
"""
|
|
68
|
+
Create a hash of critical game state elements for integrity verification.
|
|
69
|
+
"""
|
|
70
|
+
# Extract key elements that shouldn't change unexpectedly
|
|
71
|
+
hash_elements = {
|
|
72
|
+
'player_name': state_data.get('player', {}).get('name', ''),
|
|
73
|
+
'money': state_data.get('player', {}).get('money') or state_data.get('game', {}).get('money', 0),
|
|
74
|
+
'location': state_data.get('player', {}).get('location', ''),
|
|
75
|
+
'coordinates': state_data.get('player', {}).get('coordinates', {}),
|
|
76
|
+
'in_battle': state_data.get('game', {}).get('in_battle', False),
|
|
77
|
+
'step_number': state_data.get('step_number', 0)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Add party pokemon data
|
|
81
|
+
party_data = state_data.get('player', {}).get('party') or state_data.get('game', {}).get('party')
|
|
82
|
+
party_hash_data = []
|
|
83
|
+
if party_data:
|
|
84
|
+
pokemon_list = []
|
|
85
|
+
if isinstance(party_data, dict) and party_data.get('pokemon'):
|
|
86
|
+
pokemon_list = party_data.get('pokemon', [])
|
|
87
|
+
elif isinstance(party_data, list):
|
|
88
|
+
pokemon_list = party_data
|
|
89
|
+
|
|
90
|
+
for pokemon in pokemon_list[:6]:
|
|
91
|
+
if pokemon:
|
|
92
|
+
party_hash_data.append({
|
|
93
|
+
'species': pokemon.get('species', ''),
|
|
94
|
+
'level': pokemon.get('level', 0),
|
|
95
|
+
'max_hp': pokemon.get('max_hp', 0)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
hash_elements['party'] = party_hash_data
|
|
99
|
+
|
|
100
|
+
# Create deterministic hash
|
|
101
|
+
hash_string = json.dumps(hash_elements, sort_keys=True)
|
|
102
|
+
return hashlib.md5(hash_string.encode()).hexdigest()[:8]
|
|
103
|
+
|
|
104
|
+
def analyze_movement_behavior(self, current_pos, previous_pos, action_taken):
|
|
105
|
+
"""
|
|
106
|
+
Analyze movement patterns for behavioral fingerprinting.
|
|
107
|
+
"""
|
|
108
|
+
if not current_pos or not previous_pos:
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
current_x = current_pos.get('x')
|
|
112
|
+
current_y = current_pos.get('y')
|
|
113
|
+
prev_x = previous_pos.get('x')
|
|
114
|
+
prev_y = previous_pos.get('y')
|
|
115
|
+
|
|
116
|
+
if None in [current_x, current_y, prev_x, prev_y]:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Calculate movement delta
|
|
120
|
+
dx = current_x - prev_x
|
|
121
|
+
dy = current_y - prev_y
|
|
122
|
+
|
|
123
|
+
# Check if movement matches action
|
|
124
|
+
action_str = str(action_taken).upper()
|
|
125
|
+
expected_movement = False
|
|
126
|
+
|
|
127
|
+
if 'UP' in action_str and dy == -1 and dx == 0:
|
|
128
|
+
expected_movement = True
|
|
129
|
+
elif 'DOWN' in action_str and dy == 1 and dx == 0:
|
|
130
|
+
expected_movement = True
|
|
131
|
+
elif 'LEFT' in action_str and dx == -1 and dy == 0:
|
|
132
|
+
expected_movement = True
|
|
133
|
+
elif 'RIGHT' in action_str and dx == 1 and dy == 0:
|
|
134
|
+
expected_movement = True
|
|
135
|
+
elif action_str in ['A', 'B', 'START', 'SELECT'] and dx == 0 and dy == 0:
|
|
136
|
+
expected_movement = True # Non-movement actions
|
|
137
|
+
|
|
138
|
+
if not expected_movement and (dx != 0 or dy != 0):
|
|
139
|
+
self.invalid_actions += 1
|
|
140
|
+
|
|
141
|
+
# Check for backtracking (returning to a recent position)
|
|
142
|
+
current_pos_tuple = (current_x, current_y)
|
|
143
|
+
if current_pos_tuple in list(self.position_history)[-10:]: # Last 10 positions
|
|
144
|
+
self.backtrack_moves += 1
|
|
145
|
+
|
|
146
|
+
# Add to position history
|
|
147
|
+
self.position_history.append(current_pos_tuple)
|
|
148
|
+
|
|
149
|
+
# Simple exploration detection (movement without clear direction)
|
|
150
|
+
if abs(dx) + abs(dy) > 1: # Diagonal or multi-tile movement (unusual)
|
|
151
|
+
self.exploration_moves += 1
|
|
152
|
+
|
|
153
|
+
def calculate_behavioral_metrics(self):
|
|
154
|
+
"""
|
|
155
|
+
Calculate behavioral fingerprinting metrics.
|
|
156
|
+
"""
|
|
157
|
+
avg_decision_time = sum(self.decision_times) / len(self.decision_times) if self.decision_times else 0
|
|
158
|
+
error_rate = self.invalid_actions / max(self.total_actions, 1)
|
|
159
|
+
exploration_ratio = self.exploration_moves / max(self.total_actions, 1)
|
|
160
|
+
backtrack_ratio = self.backtrack_moves / max(self.total_actions, 1)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
'avg_decision_time': round(avg_decision_time, 3),
|
|
164
|
+
'error_rate': round(error_rate, 3),
|
|
165
|
+
'exploration_ratio': round(exploration_ratio, 3),
|
|
166
|
+
'backtrack_ratio': round(backtrack_ratio, 3),
|
|
167
|
+
'decision_variance': round(np.var(list(self.decision_times)), 3) if len(self.decision_times) > 1 else 0
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
def detect_milestone(self, location_name):
|
|
171
|
+
"""
|
|
172
|
+
Detect if the current location corresponds to a milestone.
|
|
173
|
+
Maps actual game location names to milestone identifiers.
|
|
174
|
+
"""
|
|
175
|
+
if not location_name or location_name == 'Unknown':
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
# Normalize location name (remove spaces, convert to uppercase)
|
|
179
|
+
normalized = location_name.replace(' ', '_').upper()
|
|
180
|
+
|
|
181
|
+
# Direct mapping for locations that match exactly
|
|
182
|
+
if normalized in MILESTONES:
|
|
183
|
+
return normalized
|
|
184
|
+
|
|
185
|
+
# Special mappings for locations that might have different names
|
|
186
|
+
location_mappings = {
|
|
187
|
+
# Phase 1: Game Initialization
|
|
188
|
+
'MOVING_VAN': 'INTRO_CUTSCENE_COMPLETE',
|
|
189
|
+
|
|
190
|
+
# Phase 2: Tutorial & Starting Town
|
|
191
|
+
'LITTLEROOT': 'LITTLEROOT_TOWN',
|
|
192
|
+
'BRENDANS_HOUSE_1F': 'PLAYER_HOUSE_ENTERED',
|
|
193
|
+
'BRENDANS_HOUSE_2F': 'PLAYER_BEDROOM',
|
|
194
|
+
'MAYS_HOUSE_1F': 'RIVAL_HOUSE',
|
|
195
|
+
'MAYS_HOUSE_2F': 'RIVAL_BEDROOM',
|
|
196
|
+
|
|
197
|
+
# Phase 3: Professor Birch & Starter
|
|
198
|
+
'ROUTE_101': 'ROUTE_101',
|
|
199
|
+
'ROUTE101': 'ROUTE_101',
|
|
200
|
+
'BIRCHS_LAB': 'BIRCH_LAB_VISITED',
|
|
201
|
+
'PROFESSOR_BIRCHS_LAB': 'BIRCH_LAB_VISITED',
|
|
202
|
+
|
|
203
|
+
# Phase 4: Rival
|
|
204
|
+
'OLDALE': 'OLDALE_TOWN',
|
|
205
|
+
'ROUTE_103': 'ROUTE_103',
|
|
206
|
+
'ROUTE103': 'ROUTE_103',
|
|
207
|
+
|
|
208
|
+
# Phase 5: Route 102 & Petalburg
|
|
209
|
+
'ROUTE_102': 'ROUTE_102',
|
|
210
|
+
'ROUTE102': 'ROUTE_102',
|
|
211
|
+
'PETALBURG': 'PETALBURG_CITY',
|
|
212
|
+
'PETALBURG_CITY_GYM': 'DAD_FIRST_MEETING',
|
|
213
|
+
'PETALBURG_GYM': 'DAD_FIRST_MEETING',
|
|
214
|
+
|
|
215
|
+
# Phase 6: Road to Rustboro City
|
|
216
|
+
'ROUTE_104': 'ROUTE_104_SOUTH',
|
|
217
|
+
'ROUTE104': 'ROUTE_104_SOUTH',
|
|
218
|
+
'PETALBURG_WOODS': 'PETALBURG_WOODS',
|
|
219
|
+
|
|
220
|
+
# Phase 7: First Gym Challenge
|
|
221
|
+
'RUSTBORO': 'RUSTBORO_CITY',
|
|
222
|
+
'RUSTBORO_GYM': 'RUSTBORO_GYM_ENTERED',
|
|
223
|
+
'RUSTBORO_CITY_GYM': 'RUSTBORO_GYM_ENTERED'
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Check for partial matches
|
|
227
|
+
for key, milestone in location_mappings.items():
|
|
228
|
+
if key in normalized:
|
|
229
|
+
return milestone
|
|
230
|
+
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
def update_milestone(self, current_location):
|
|
234
|
+
"""Update the latest milestone based on current location"""
|
|
235
|
+
detected_milestone = self.detect_milestone(current_location)
|
|
236
|
+
if detected_milestone:
|
|
237
|
+
# Check if this is a new milestone (further in progression)
|
|
238
|
+
if self.latest_milestone is None:
|
|
239
|
+
self.latest_milestone = detected_milestone
|
|
240
|
+
logger.info(f"[MILESTONE] First milestone detected: {self.latest_milestone}")
|
|
241
|
+
else:
|
|
242
|
+
try:
|
|
243
|
+
current_index = MILESTONES.index(self.latest_milestone)
|
|
244
|
+
new_index = MILESTONES.index(detected_milestone)
|
|
245
|
+
|
|
246
|
+
# Only update if we've progressed further
|
|
247
|
+
if new_index > current_index:
|
|
248
|
+
self.latest_milestone = detected_milestone
|
|
249
|
+
logger.info(f"[MILESTONE] New milestone reached: {self.latest_milestone}")
|
|
250
|
+
except ValueError:
|
|
251
|
+
# If milestone not found in list, just log it
|
|
252
|
+
logger.warning(f"[MILESTONE] Unknown milestone detected: {detected_milestone}")
|
|
253
|
+
|
|
254
|
+
return self.latest_milestone
|
|
255
|
+
|
|
256
|
+
def log_submission_data(self, step, state_data, action_taken, decision_time, state_hash, manual_mode=False, milestone_override=None):
|
|
257
|
+
"""Log structured data for anticheat verification"""
|
|
258
|
+
# Extract key information
|
|
259
|
+
player_data = state_data.get('player', {})
|
|
260
|
+
game_data = state_data.get('game', {})
|
|
261
|
+
map_info = state_data.get('map', {})
|
|
262
|
+
|
|
263
|
+
# Position - use the same logic as state formatter
|
|
264
|
+
position = None
|
|
265
|
+
if 'coordinates' in player_data:
|
|
266
|
+
position = player_data['coordinates']
|
|
267
|
+
elif 'position' in player_data and player_data['position']:
|
|
268
|
+
position = player_data['position']
|
|
269
|
+
|
|
270
|
+
if position and isinstance(position, dict):
|
|
271
|
+
x_val = position.get('x', '?')
|
|
272
|
+
y_val = position.get('y', '?')
|
|
273
|
+
pos_str = f"({x_val},{y_val})"
|
|
274
|
+
else:
|
|
275
|
+
pos_str = "(?,?)"
|
|
276
|
+
|
|
277
|
+
# Battle state
|
|
278
|
+
battle_state = "BATTLE" if game_data.get('in_battle', False) else "OVERWORLD"
|
|
279
|
+
|
|
280
|
+
# Party summary - use similar logic to state formatter
|
|
281
|
+
party_data = player_data.get('party') or game_data.get('party')
|
|
282
|
+
party_summary = []
|
|
283
|
+
if party_data:
|
|
284
|
+
pokemon_list = []
|
|
285
|
+
if isinstance(party_data, dict) and party_data.get('pokemon'):
|
|
286
|
+
pokemon_list = party_data.get('pokemon', [])
|
|
287
|
+
elif isinstance(party_data, list):
|
|
288
|
+
pokemon_list = party_data
|
|
289
|
+
|
|
290
|
+
for pokemon in pokemon_list[:6]: # Max 6 pokemon
|
|
291
|
+
if pokemon:
|
|
292
|
+
# Use same field names as get_comprehensive_state for consistency
|
|
293
|
+
species = pokemon.get('species_name', pokemon.get('species', 'Unknown'))
|
|
294
|
+
level = pokemon.get('level', '?')
|
|
295
|
+
hp = pokemon.get('current_hp', '?')
|
|
296
|
+
max_hp = pokemon.get('max_hp', '?')
|
|
297
|
+
status = pokemon.get('status', 'OK')
|
|
298
|
+
party_summary.append(f"{species}(Lv{level},HP:{hp}/{max_hp},{status})")
|
|
299
|
+
|
|
300
|
+
party_str = "[" + ",".join(party_summary) + "]" if party_summary else "[]"
|
|
301
|
+
|
|
302
|
+
# Action taken
|
|
303
|
+
action_str = str(action_taken) if action_taken else "None"
|
|
304
|
+
|
|
305
|
+
# Current map - correctly extract from player.location
|
|
306
|
+
current_map = player_data.get('location', 'Unknown')
|
|
307
|
+
|
|
308
|
+
# Use provided milestone or update and get latest milestone
|
|
309
|
+
if milestone_override:
|
|
310
|
+
milestone_str = milestone_override
|
|
311
|
+
else:
|
|
312
|
+
milestone = self.update_milestone(current_map)
|
|
313
|
+
milestone_str = milestone if milestone else "NONE"
|
|
314
|
+
|
|
315
|
+
# Money
|
|
316
|
+
money = player_data.get('money')
|
|
317
|
+
if money is None:
|
|
318
|
+
money = game_data.get('money', 0)
|
|
319
|
+
|
|
320
|
+
# Analyze movement behavior
|
|
321
|
+
if position and isinstance(position, dict):
|
|
322
|
+
self.analyze_movement_behavior(position, self.previous_position, action_taken)
|
|
323
|
+
self.previous_position = position.copy()
|
|
324
|
+
|
|
325
|
+
self.total_actions += 1
|
|
326
|
+
self.decision_times.append(decision_time)
|
|
327
|
+
|
|
328
|
+
# Get behavioral metrics
|
|
329
|
+
behavioral_metrics = self.calculate_behavioral_metrics()
|
|
330
|
+
|
|
331
|
+
# Calculate current runtime
|
|
332
|
+
current_runtime = 0.0
|
|
333
|
+
runtime_str = "00:00:00"
|
|
334
|
+
if self.start_time is not None:
|
|
335
|
+
current_runtime = time.time() - self.start_time
|
|
336
|
+
hours = int(current_runtime // 3600)
|
|
337
|
+
minutes = int((current_runtime % 3600) // 60)
|
|
338
|
+
seconds = int(current_runtime % 60)
|
|
339
|
+
runtime_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
340
|
+
|
|
341
|
+
# Determine mode string
|
|
342
|
+
mode_str = "MANUAL" if manual_mode else "AGENT"
|
|
343
|
+
|
|
344
|
+
# Log structured entry with enhanced anti-cheat data including runtime and mode
|
|
345
|
+
log_entry = (f"STEP={step} | POS={pos_str} | MAP={current_map} | MILESTONE={milestone_str} | "
|
|
346
|
+
f"STATE={battle_state} | MONEY=${money} | PARTY={party_str} | ACTION={action_str} | "
|
|
347
|
+
f"MODE={mode_str} | DECISION_TIME={decision_time:.3f}s | RUNTIME={runtime_str} | "
|
|
348
|
+
f"STATE_HASH={state_hash} | "
|
|
349
|
+
f"AVG_TIME={behavioral_metrics['avg_decision_time']}s | "
|
|
350
|
+
f"ERROR_RATE={behavioral_metrics['error_rate']} | "
|
|
351
|
+
f"EXPLORE_RATIO={behavioral_metrics['exploration_ratio']} | "
|
|
352
|
+
f"BACKTRACK_RATIO={behavioral_metrics['backtrack_ratio']} | "
|
|
353
|
+
f"TIME_VAR={behavioral_metrics['decision_variance']}")
|
|
354
|
+
|
|
355
|
+
self.submission_logger.info(log_entry)
|
|
356
|
+
|
|
357
|
+
# Force flush to ensure immediate write
|
|
358
|
+
for handler in self.submission_logger.handlers:
|
|
359
|
+
handler.flush()
|
|
360
|
+
|
|
361
|
+
def initialize_submission_log(self, model_name):
|
|
362
|
+
"""Initialize submission log with header information"""
|
|
363
|
+
self.start_time = time.time() # Store start time for total runtime calculation
|
|
364
|
+
|
|
365
|
+
# Clear the file first by writing directly
|
|
366
|
+
with open('submission.log', 'w') as f:
|
|
367
|
+
f.write("")
|
|
368
|
+
|
|
369
|
+
self.submission_logger.info("=== POKEMON EMERALD AGENT SUBMISSION LOG ===")
|
|
370
|
+
self.submission_logger.info(f"Model: {model_name} | Start Time: {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
371
|
+
self.submission_logger.info("Format: STEP | POS | MAP | MILESTONE | STATE | MONEY | PARTY | ACTION | MODE | DECISION_TIME | RUNTIME | STATE_HASH | AVG_TIME | ERROR_RATE | EXPLORE_RATIO | BACKTRACK_RATIO | TIME_VAR")
|
|
372
|
+
self.submission_logger.info("=" * 120)
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Checkpoint management utilities for saving and loading game states,
|
|
4
|
+
agent states, and LLM history.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import traceback
|
|
10
|
+
from collections import deque
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def save_checkpoint(emulator, llm_logger=None, agent_step_count=0):
|
|
15
|
+
"""
|
|
16
|
+
Save a complete checkpoint including game state, milestones, and LLM history.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
emulator: The game emulator instance
|
|
20
|
+
llm_logger: Optional LLM logger for saving conversation history
|
|
21
|
+
agent_step_count: Current agent step count
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
bool: True if checkpoint saved successfully, False otherwise
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
# Save emulator state
|
|
28
|
+
print("💾 Saving checkpoint...")
|
|
29
|
+
os.makedirs(".pokeagent_cache", exist_ok=True)
|
|
30
|
+
emulator.save_state(".pokeagent_cache/checkpoint.state")
|
|
31
|
+
print(f" ✅ Saved emulator state to .pokeagent_cache/checkpoint.state")
|
|
32
|
+
|
|
33
|
+
# Save milestones
|
|
34
|
+
milestone_data = {
|
|
35
|
+
"milestones": emulator.memory_reader.milestones if emulator.memory_reader else {},
|
|
36
|
+
"step_count": agent_step_count,
|
|
37
|
+
"timestamp": datetime.now().isoformat()
|
|
38
|
+
}
|
|
39
|
+
os.makedirs(".pokeagent_cache", exist_ok=True)
|
|
40
|
+
with open(".pokeagent_cache/checkpoint_milestones.json", "w") as f:
|
|
41
|
+
json.dump(milestone_data, f, indent=2)
|
|
42
|
+
print(f" ✅ Saved milestones to .pokeagent_cache/checkpoint_milestones.json")
|
|
43
|
+
|
|
44
|
+
# Save location maps
|
|
45
|
+
try:
|
|
46
|
+
from utils.state_formatter import save_persistent_world_map
|
|
47
|
+
os.makedirs(".pokeagent_cache", exist_ok=True)
|
|
48
|
+
save_persistent_world_map(".pokeagent_cache/checkpoint_maps.json")
|
|
49
|
+
print(f" ✅ Saved location maps to .pokeagent_cache/checkpoint_maps.json")
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(f" ⚠️ Failed to save location maps: {e}")
|
|
52
|
+
|
|
53
|
+
# Save LLM history if logger available
|
|
54
|
+
if llm_logger:
|
|
55
|
+
# Get conversation history from logger
|
|
56
|
+
history = llm_logger.get_conversation_history()
|
|
57
|
+
|
|
58
|
+
# Also get metrics if available
|
|
59
|
+
try:
|
|
60
|
+
metrics = llm_logger.get_interaction_metrics()
|
|
61
|
+
checkpoint_data = {
|
|
62
|
+
"history": history,
|
|
63
|
+
"metrics": metrics,
|
|
64
|
+
"step_counter": agent_step_count,
|
|
65
|
+
"timestamp": datetime.now().isoformat()
|
|
66
|
+
}
|
|
67
|
+
except:
|
|
68
|
+
checkpoint_data = {
|
|
69
|
+
"history": history,
|
|
70
|
+
"step_counter": agent_step_count,
|
|
71
|
+
"timestamp": datetime.now().isoformat()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Save to cache folder
|
|
75
|
+
cache_dir = ".pokeagent_cache"
|
|
76
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
77
|
+
checkpoint_file = os.path.join(cache_dir, "checkpoint_llm.txt")
|
|
78
|
+
with open(checkpoint_file, "w") as f:
|
|
79
|
+
json.dump(checkpoint_data, f, indent=2)
|
|
80
|
+
print(f" ✅ Saved LLM history to {checkpoint_file}")
|
|
81
|
+
|
|
82
|
+
print(f"✅ Checkpoint saved at step {agent_step_count}")
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"❌ Failed to save checkpoint: {e}")
|
|
87
|
+
traceback.print_exc()
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def load_checkpoint(emulator, llm_logger=None):
|
|
92
|
+
"""
|
|
93
|
+
Load a checkpoint including game state, milestones, and LLM history.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
emulator: The game emulator instance
|
|
97
|
+
llm_logger: Optional LLM logger for loading conversation history
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
dict: Checkpoint data including step_count, or None if failed
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
checkpoint_data = {}
|
|
104
|
+
|
|
105
|
+
# Load emulator state
|
|
106
|
+
if os.path.exists(".pokeagent_cache/checkpoint.state"):
|
|
107
|
+
print("🔄 Loading checkpoint...")
|
|
108
|
+
emulator.load_state(".pokeagent_cache/checkpoint.state")
|
|
109
|
+
print(f" ✅ Loaded emulator state from .pokeagent_cache/checkpoint.state")
|
|
110
|
+
else:
|
|
111
|
+
print(" ⚠️ No .pokeagent_cache/checkpoint.state found")
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
# Load milestones
|
|
115
|
+
if os.path.exists(".pokeagent_cache/checkpoint_milestones.json"):
|
|
116
|
+
with open(".pokeagent_cache/checkpoint_milestones.json", "r") as f:
|
|
117
|
+
milestone_data = json.load(f)
|
|
118
|
+
if emulator.memory_reader:
|
|
119
|
+
emulator.memory_reader.milestones = milestone_data.get("milestones", {})
|
|
120
|
+
checkpoint_data['step_count'] = milestone_data.get("step_count", 0)
|
|
121
|
+
print(f" ✅ Loaded milestones from .pokeagent_cache/checkpoint_milestones.json")
|
|
122
|
+
|
|
123
|
+
# Load location maps
|
|
124
|
+
try:
|
|
125
|
+
from utils.state_formatter import load_persistent_world_map
|
|
126
|
+
if os.path.exists(".pokeagent_cache/checkpoint_maps.json"):
|
|
127
|
+
load_persistent_world_map(".pokeagent_cache/checkpoint_maps.json")
|
|
128
|
+
print(f" ✅ Loaded location maps from .pokeagent_cache/checkpoint_maps.json")
|
|
129
|
+
else:
|
|
130
|
+
print(f" ⚠️ No .pokeagent_cache/checkpoint_maps.json found")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
print(f" ⚠️ Failed to load location maps: {e}")
|
|
133
|
+
|
|
134
|
+
# Load LLM history if logger available
|
|
135
|
+
cache_dir = ".pokeagent_cache"
|
|
136
|
+
checkpoint_file = os.path.join(cache_dir, "checkpoint_llm.txt") if os.path.exists(cache_dir) else "checkpoint_llm.txt"
|
|
137
|
+
if not os.path.exists(checkpoint_file) and os.path.exists("checkpoint_llm.txt"):
|
|
138
|
+
checkpoint_file = "checkpoint_llm.txt"
|
|
139
|
+
|
|
140
|
+
if llm_logger and os.path.exists(checkpoint_file):
|
|
141
|
+
with open(checkpoint_file, "r") as f:
|
|
142
|
+
llm_data = json.load(f)
|
|
143
|
+
|
|
144
|
+
# Restore conversation history
|
|
145
|
+
if 'history' in llm_data:
|
|
146
|
+
llm_logger.set_conversation_history(llm_data['history'])
|
|
147
|
+
|
|
148
|
+
checkpoint_data['llm_step_count'] = llm_data.get('step_counter', 0)
|
|
149
|
+
print(f" ✅ Loaded LLM history from checkpoint_llm.txt")
|
|
150
|
+
|
|
151
|
+
print(f"✅ Checkpoint loaded successfully")
|
|
152
|
+
return checkpoint_data
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
print(f"❌ Failed to load checkpoint: {e}")
|
|
156
|
+
traceback.print_exc()
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def save_simple_agent_state(simple_agent, filename="agent_state.json"):
|
|
161
|
+
"""
|
|
162
|
+
Save SimpleAgent state to JSON file.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
simple_agent: The SimpleAgent instance
|
|
166
|
+
filename: Output filename
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: True if saved successfully
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
# Convert SimpleAgent state to serializable format
|
|
173
|
+
state_data = {
|
|
174
|
+
"step_counter": simple_agent.state.step_counter,
|
|
175
|
+
"stuck_detection": simple_agent.state.stuck_detection,
|
|
176
|
+
"objectives_updated": simple_agent.state.objectives_updated,
|
|
177
|
+
"history": [],
|
|
178
|
+
"objectives": []
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Convert history entries
|
|
182
|
+
for entry in simple_agent.state.history:
|
|
183
|
+
state_data["history"].append({
|
|
184
|
+
"timestamp": entry.timestamp.isoformat() if hasattr(entry.timestamp, 'isoformat') else str(entry.timestamp),
|
|
185
|
+
"screenshot": None, # Don't save screenshots
|
|
186
|
+
"game_state": entry.game_state,
|
|
187
|
+
"llm_output": entry.llm_output
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
# Convert objectives
|
|
191
|
+
for obj in simple_agent.state.objectives:
|
|
192
|
+
state_data["objectives"].append({
|
|
193
|
+
"description": obj.description,
|
|
194
|
+
"completed": obj.completed
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
# Save to file
|
|
198
|
+
with open(filename, 'w') as f:
|
|
199
|
+
json.dump(state_data, f, indent=2)
|
|
200
|
+
|
|
201
|
+
print(f"✅ Saved SimpleAgent state to {filename}")
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
except Exception as e:
|
|
205
|
+
print(f"❌ Failed to save SimpleAgent state: {e}")
|
|
206
|
+
traceback.print_exc()
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def load_simple_agent_state(simple_agent, filename="agent_state.json"):
|
|
211
|
+
"""
|
|
212
|
+
Load SimpleAgent state from JSON file.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
simple_agent: The SimpleAgent instance to load state into
|
|
216
|
+
filename: Input filename
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
bool: True if loaded successfully
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
from agent.simple import HistoryEntry, Objective
|
|
223
|
+
|
|
224
|
+
with open(filename, 'r') as f:
|
|
225
|
+
state_data = json.load(f)
|
|
226
|
+
|
|
227
|
+
# Restore basic counters
|
|
228
|
+
simple_agent.state.step_counter = state_data.get("step_counter", 0)
|
|
229
|
+
simple_agent.state.stuck_detection = state_data.get("stuck_detection", {})
|
|
230
|
+
simple_agent.state.objectives_updated = state_data.get("objectives_updated", False)
|
|
231
|
+
|
|
232
|
+
# Restore history
|
|
233
|
+
simple_agent.state.history = deque(maxlen=10)
|
|
234
|
+
for entry_data in state_data.get("history", []):
|
|
235
|
+
# Parse timestamp
|
|
236
|
+
timestamp_str = entry_data.get("timestamp", "")
|
|
237
|
+
try:
|
|
238
|
+
timestamp = datetime.fromisoformat(timestamp_str)
|
|
239
|
+
except:
|
|
240
|
+
timestamp = datetime.now()
|
|
241
|
+
|
|
242
|
+
entry = HistoryEntry(
|
|
243
|
+
timestamp=timestamp,
|
|
244
|
+
screenshot=None, # Don't restore screenshots
|
|
245
|
+
game_state=entry_data.get("game_state", {}),
|
|
246
|
+
llm_output=entry_data.get("llm_output", {})
|
|
247
|
+
)
|
|
248
|
+
simple_agent.state.history.append(entry)
|
|
249
|
+
|
|
250
|
+
# Restore objectives
|
|
251
|
+
simple_agent.state.objectives = []
|
|
252
|
+
for obj_data in state_data.get("objectives", []):
|
|
253
|
+
obj = Objective(
|
|
254
|
+
description=obj_data.get("description", ""),
|
|
255
|
+
completed=obj_data.get("completed", False)
|
|
256
|
+
)
|
|
257
|
+
simple_agent.state.objectives.append(obj)
|
|
258
|
+
|
|
259
|
+
print(f"✅ Loaded SimpleAgent state from {filename}")
|
|
260
|
+
print(f" - Step counter: {simple_agent.state.step_counter}")
|
|
261
|
+
print(f" - History entries: {len(simple_agent.state.history)}")
|
|
262
|
+
print(f" - Objectives: {len(simple_agent.state.objectives)}")
|
|
263
|
+
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
except FileNotFoundError:
|
|
267
|
+
print(f"⚠️ No saved state found at {filename}")
|
|
268
|
+
return False
|
|
269
|
+
except Exception as e:
|
|
270
|
+
print(f"❌ Failed to load SimpleAgent state: {e}")
|
|
271
|
+
traceback.print_exc()
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def load_llm_checkpoint(filename="checkpoint_llm.txt"):
|
|
276
|
+
"""
|
|
277
|
+
Load LLM checkpoint data from file.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
filename: Input filename
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
dict: Checkpoint data or None if failed
|
|
284
|
+
"""
|
|
285
|
+
try:
|
|
286
|
+
if not os.path.exists(filename):
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
with open(filename, 'r') as f:
|
|
290
|
+
data = json.load(f)
|
|
291
|
+
|
|
292
|
+
return data
|
|
293
|
+
|
|
294
|
+
except Exception as e:
|
|
295
|
+
print(f"❌ Failed to load LLM checkpoint: {e}")
|
|
296
|
+
return None
|