synth-ai 0.2.14__py3-none-any.whl → 0.4.4__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 (1086) hide show
  1. synth_ai/__init__.py +25 -46
  2. synth_ai/__main__.py +30 -3
  3. synth_ai/cli/__init__.py +98 -72
  4. synth_ai/cli/__main__.py +42 -0
  5. synth_ai/cli/_internal/__init__.py +5 -0
  6. synth_ai/cli/_internal/modal_wrapper.py +31 -0
  7. synth_ai/cli/_internal/storage.py +20 -0
  8. synth_ai/cli/_internal/typer_patch.py +47 -0
  9. synth_ai/cli/_internal/validate_task_app.py +29 -0
  10. synth_ai/cli/agents/__init__.py +17 -0
  11. synth_ai/cli/agents/claude.py +77 -0
  12. synth_ai/cli/agents/codex.py +265 -0
  13. synth_ai/cli/agents/opencode.py +253 -0
  14. synth_ai/cli/commands/__init__.py +18 -0
  15. synth_ai/cli/commands/artifacts/__init__.py +13 -0
  16. synth_ai/cli/commands/artifacts/client.py +119 -0
  17. synth_ai/cli/commands/artifacts/config.py +57 -0
  18. synth_ai/cli/commands/artifacts/core.py +24 -0
  19. synth_ai/cli/commands/artifacts/download.py +188 -0
  20. synth_ai/cli/commands/artifacts/export.py +186 -0
  21. synth_ai/cli/commands/artifacts/list.py +156 -0
  22. synth_ai/cli/commands/artifacts/parsing.py +250 -0
  23. synth_ai/cli/commands/artifacts/show.py +336 -0
  24. synth_ai/cli/commands/demo/__init__.py +3 -0
  25. synth_ai/cli/commands/demo/core.py +153 -0
  26. synth_ai/cli/commands/eval/__init__.py +10 -0
  27. synth_ai/cli/commands/eval/config.py +338 -0
  28. synth_ai/cli/commands/eval/core.py +258 -0
  29. synth_ai/cli/commands/eval/runner.py +704 -0
  30. synth_ai/cli/commands/eval/validation.py +60 -0
  31. synth_ai/cli/commands/filter/__init__.py +12 -0
  32. synth_ai/cli/commands/filter/core.py +424 -0
  33. synth_ai/cli/commands/filter/errors.py +55 -0
  34. synth_ai/cli/commands/filter/validation.py +77 -0
  35. synth_ai/cli/commands/help/__init__.py +185 -0
  36. synth_ai/cli/commands/help/core.py +72 -0
  37. synth_ai/cli/commands/scan/__init__.py +19 -0
  38. synth_ai/cli/commands/scan/cloudflare_scanner.py +403 -0
  39. synth_ai/cli/commands/scan/core.py +344 -0
  40. synth_ai/cli/commands/scan/health_checker.py +242 -0
  41. synth_ai/cli/commands/scan/local_scanner.py +278 -0
  42. synth_ai/cli/commands/scan/models.py +83 -0
  43. synth_ai/cli/commands/smoke/__init__.py +7 -0
  44. synth_ai/cli/commands/smoke/core.py +1428 -0
  45. synth_ai/cli/commands/status/__init__.py +3 -0
  46. synth_ai/cli/commands/status/client.py +91 -0
  47. synth_ai/cli/commands/status/config.py +12 -0
  48. synth_ai/cli/commands/status/errors.py +11 -0
  49. synth_ai/cli/commands/status/subcommands/__init__.py +3 -0
  50. synth_ai/cli/commands/status/subcommands/config.py +13 -0
  51. synth_ai/cli/commands/status/subcommands/files.py +34 -0
  52. synth_ai/cli/commands/status/subcommands/jobs.py +51 -0
  53. synth_ai/cli/commands/status/subcommands/models.py +35 -0
  54. synth_ai/cli/commands/status/subcommands/runs.py +34 -0
  55. synth_ai/cli/commands/status/subcommands/session.py +77 -0
  56. synth_ai/cli/commands/status/subcommands/summary.py +39 -0
  57. synth_ai/cli/commands/status/subcommands/utils.py +41 -0
  58. synth_ai/cli/commands/status/utils.py +23 -0
  59. synth_ai/cli/commands/train/__init__.py +51 -0
  60. synth_ai/cli/commands/train/core.py +22 -0
  61. synth_ai/cli/commands/train/errors.py +117 -0
  62. synth_ai/cli/commands/train/prompt_learning_validation.py +632 -0
  63. synth_ai/cli/commands/train/validation.py +392 -0
  64. synth_ai/cli/commands/train/verifier_schemas.py +200 -0
  65. synth_ai/cli/commands/train/verifier_validation.py +235 -0
  66. synth_ai/cli/demo_apps/__init__.py +10 -0
  67. synth_ai/cli/demo_apps/core/__init__.py +28 -0
  68. synth_ai/cli/demo_apps/core/cli.py +1735 -0
  69. synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +55 -0
  70. synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +186 -0
  71. synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +74 -0
  72. synth_ai/cli/demo_apps/demo_registry.py +176 -0
  73. synth_ai/cli/demo_apps/demo_task_apps/core.py +440 -0
  74. synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
  75. synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
  76. synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +73 -0
  77. synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +738 -0
  78. synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +39 -0
  79. synth_ai/cli/demo_apps/math/__init__.py +1 -0
  80. synth_ai/cli/demo_apps/math/_common.py +16 -0
  81. synth_ai/cli/demo_apps/math/app.py +38 -0
  82. synth_ai/cli/demo_apps/math/config.toml +75 -0
  83. synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
  84. synth_ai/cli/demo_apps/math/modal_task_app.py +698 -0
  85. synth_ai/cli/demo_apps/math/task_app_entry.py +53 -0
  86. synth_ai/cli/demo_apps/mipro/main.py +271 -0
  87. synth_ai/cli/demo_apps/mipro/task_app.py +911 -0
  88. synth_ai/cli/demo_apps/mipro/train_cfg.toml +92 -0
  89. synth_ai/cli/demos/__init__.py +12 -0
  90. synth_ai/cli/demos/demo.py +32 -0
  91. synth_ai/cli/demos/rl_demo.py +254 -0
  92. synth_ai/cli/deploy.py +216 -0
  93. synth_ai/cli/infra/__init__.py +14 -0
  94. synth_ai/cli/infra/balance.py +216 -0
  95. synth_ai/cli/infra/mcp.py +35 -0
  96. synth_ai/cli/infra/modal_app.py +36 -0
  97. synth_ai/cli/infra/setup.py +69 -0
  98. synth_ai/cli/infra/status.py +16 -0
  99. synth_ai/cli/infra/turso.py +77 -0
  100. synth_ai/cli/lib/__init__.py +10 -0
  101. synth_ai/cli/lib/agents.py +76 -0
  102. synth_ai/cli/lib/apps/modal_app.py +101 -0
  103. synth_ai/cli/lib/apps/task_app.py +642 -0
  104. synth_ai/cli/lib/bin.py +39 -0
  105. synth_ai/cli/lib/env.py +375 -0
  106. synth_ai/cli/lib/errors.py +85 -0
  107. synth_ai/cli/lib/modal.py +315 -0
  108. synth_ai/cli/lib/plotting.py +126 -0
  109. synth_ai/cli/lib/prompt_args.py +39 -0
  110. synth_ai/cli/lib/prompts.py +284 -0
  111. synth_ai/cli/lib/sqld.py +122 -0
  112. synth_ai/cli/lib/task_app_discovery.py +884 -0
  113. synth_ai/cli/lib/task_app_env.py +295 -0
  114. synth_ai/cli/lib/train_cfgs.py +300 -0
  115. synth_ai/cli/lib/tunnel_records.py +207 -0
  116. synth_ai/cli/local/__init__.py +14 -0
  117. synth_ai/cli/local/experiment_queue/__init__.py +72 -0
  118. synth_ai/cli/local/experiment_queue/api_schemas.py +221 -0
  119. synth_ai/cli/local/experiment_queue/celery_app.py +208 -0
  120. synth_ai/cli/local/experiment_queue/config.py +128 -0
  121. synth_ai/cli/local/experiment_queue/config_utils.py +272 -0
  122. synth_ai/cli/local/experiment_queue/database.py +175 -0
  123. synth_ai/cli/local/experiment_queue/dispatcher.py +119 -0
  124. synth_ai/cli/local/experiment_queue/models.py +231 -0
  125. synth_ai/cli/local/experiment_queue/progress_info.py +160 -0
  126. synth_ai/cli/local/experiment_queue/results.py +373 -0
  127. synth_ai/cli/local/experiment_queue/schemas.py +131 -0
  128. synth_ai/cli/local/experiment_queue/service.py +344 -0
  129. synth_ai/cli/local/experiment_queue/status.py +372 -0
  130. synth_ai/cli/local/experiment_queue/status_tracker.py +360 -0
  131. synth_ai/cli/local/experiment_queue/tasks.py +1984 -0
  132. synth_ai/cli/local/experiment_queue/trace_storage.py +65 -0
  133. synth_ai/cli/local/experiment_queue/validation.py +157 -0
  134. synth_ai/cli/local/session/__init__.py +92 -0
  135. synth_ai/cli/local/session/client.py +383 -0
  136. synth_ai/cli/local/session/constants.py +63 -0
  137. synth_ai/cli/local/session/exceptions.py +105 -0
  138. synth_ai/cli/local/session/manager.py +139 -0
  139. synth_ai/cli/local/session/models.py +89 -0
  140. synth_ai/cli/local/session/query.py +110 -0
  141. synth_ai/cli/root.py +30 -6
  142. synth_ai/cli/task_apps/__init__.py +37 -0
  143. synth_ai/cli/task_apps/commands.py +3145 -0
  144. synth_ai/cli/task_apps/deploy.py +7 -0
  145. synth_ai/cli/task_apps/list.py +26 -0
  146. synth_ai/cli/task_apps/main.py +36 -0
  147. synth_ai/cli/task_apps/modal_serve.py +11 -0
  148. synth_ai/cli/task_apps/serve.py +11 -0
  149. synth_ai/cli/training/__init__.py +8 -0
  150. synth_ai/cli/training/train.py +5 -0
  151. synth_ai/cli/training/train_cfg.py +34 -0
  152. synth_ai/cli/training/watch.py +506 -0
  153. synth_ai/cli/turso.py +34 -55
  154. synth_ai/cli/utils/__init__.py +8 -0
  155. synth_ai/cli/utils/experiments.py +235 -0
  156. synth_ai/cli/utils/queue.py +504 -0
  157. synth_ai/cli/utils/recent.py +133 -0
  158. synth_ai/cli/utils/traces.py +164 -0
  159. synth_ai/contracts/__init__.py +67 -0
  160. synth_ai/core/__init__.py +100 -0
  161. synth_ai/core/_utils/__init__.py +54 -0
  162. synth_ai/core/_utils/base_url.py +10 -0
  163. synth_ai/core/_utils/http.py +10 -0
  164. synth_ai/core/_utils/prompts.py +14 -0
  165. synth_ai/core/_utils/task_app_state.py +12 -0
  166. synth_ai/core/_utils/user_config.py +10 -0
  167. synth_ai/core/apps/common.py +116 -0
  168. synth_ai/core/auth.py +95 -0
  169. synth_ai/core/cfgs.py +240 -0
  170. synth_ai/core/config/__init__.py +16 -0
  171. synth_ai/core/config/base.py +168 -0
  172. synth_ai/core/config/resolver.py +89 -0
  173. synth_ai/core/env.py +231 -0
  174. synth_ai/core/errors.py +125 -0
  175. synth_ai/core/http.py +230 -0
  176. synth_ai/core/integrations/__init__.py +11 -0
  177. synth_ai/core/integrations/cloudflare.py +1886 -0
  178. synth_ai/core/integrations/mcp/__init__.py +6 -0
  179. synth_ai/core/integrations/mcp/__main__.py +8 -0
  180. synth_ai/core/integrations/mcp/claude.py +36 -0
  181. synth_ai/core/integrations/mcp/main.py +254 -0
  182. synth_ai/core/integrations/mcp/setup.py +100 -0
  183. synth_ai/core/integrations/modal.py +277 -0
  184. synth_ai/core/json.py +72 -0
  185. synth_ai/core/log_filter.py +99 -0
  186. synth_ai/core/logging.py +82 -0
  187. synth_ai/core/paths.py +107 -0
  188. synth_ai/core/pricing.py +109 -0
  189. synth_ai/core/process.py +233 -0
  190. synth_ai/core/ssl.py +25 -0
  191. synth_ai/core/storage/__init__.py +71 -0
  192. synth_ai/core/task_app_state.py +318 -0
  193. synth_ai/core/telemetry.py +282 -0
  194. synth_ai/core/tracing_v3/__init__.py +99 -0
  195. synth_ai/core/tracing_v3/abstractions.py +348 -0
  196. synth_ai/core/tracing_v3/config.py +229 -0
  197. synth_ai/core/tracing_v3/constants.py +21 -0
  198. synth_ai/core/tracing_v3/db_config.py +182 -0
  199. synth_ai/core/tracing_v3/decorators.py +401 -0
  200. synth_ai/core/tracing_v3/llm_call_record_helpers.py +437 -0
  201. synth_ai/core/tracing_v3/migration_helper.py +119 -0
  202. synth_ai/core/tracing_v3/session_tracer.py +542 -0
  203. synth_ai/core/tracing_v3/storage/base.py +211 -0
  204. synth_ai/core/tracing_v3/storage/config.py +109 -0
  205. synth_ai/core/tracing_v3/storage/factory.py +39 -0
  206. synth_ai/core/tracing_v3/trace_utils.py +326 -0
  207. synth_ai/core/tracing_v3/turso/daemon.py +278 -0
  208. synth_ai/core/tracing_v3/turso/models.py +470 -0
  209. synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
  210. synth_ai/core/tracing_v3/utils.py +108 -0
  211. synth_ai/core/urls.py +18 -0
  212. synth_ai/core/user_config.py +137 -0
  213. synth_ai/core/uvicorn.py +222 -0
  214. synth_ai/data/__init__.py +83 -0
  215. synth_ai/data/enums.py +122 -0
  216. synth_ai/data/rewards.py +249 -0
  217. synth_ai/data/traces.py +35 -0
  218. synth_ai/products/__init__.py +6 -0
  219. synth_ai/products/graph_evolve/__init__.py +45 -0
  220. synth_ai/products/graph_evolve/client.py +226 -0
  221. synth_ai/products/graph_evolve/config.py +591 -0
  222. synth_ai/products/graph_evolve/converters/__init__.py +42 -0
  223. synth_ai/products/graph_evolve/converters/openai_sft.py +484 -0
  224. synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +109 -0
  225. synth_ai/products/graph_evolve/run.py +222 -0
  226. synth_ai/products/graph_gepa/__init__.py +23 -0
  227. synth_ai/products/graph_gepa/converters/__init__.py +19 -0
  228. synth_ai/products/graph_gepa/converters/openai_sft.py +29 -0
  229. synth_ai/sdk/__init__.py +129 -0
  230. synth_ai/sdk/api/__init__.py +1 -0
  231. synth_ai/sdk/api/eval/__init__.py +33 -0
  232. synth_ai/sdk/api/eval/job.py +732 -0
  233. synth_ai/sdk/api/models/supported.py +514 -0
  234. synth_ai/sdk/api/research_agent/__init__.py +296 -0
  235. synth_ai/sdk/api/train/__init__.py +85 -0
  236. synth_ai/sdk/api/train/builders.py +1076 -0
  237. synth_ai/sdk/api/train/cli.py +2196 -0
  238. synth_ai/sdk/api/train/config_finder.py +267 -0
  239. synth_ai/sdk/api/train/configs/__init__.py +67 -0
  240. synth_ai/sdk/api/train/configs/prompt_learning.py +1800 -0
  241. synth_ai/sdk/api/train/configs/rl.py +436 -0
  242. synth_ai/sdk/api/train/configs/sft.py +263 -0
  243. synth_ai/sdk/api/train/configs/shared.py +81 -0
  244. synth_ai/sdk/api/train/context_learning.py +312 -0
  245. synth_ai/sdk/api/train/env_resolver.py +418 -0
  246. synth_ai/sdk/api/train/graph_validators.py +216 -0
  247. synth_ai/sdk/api/train/graphgen.py +1102 -0
  248. synth_ai/sdk/api/train/graphgen_models.py +873 -0
  249. synth_ai/sdk/api/train/graphgen_validators.py +109 -0
  250. synth_ai/sdk/api/train/local_api.py +10 -0
  251. synth_ai/sdk/api/train/pollers.py +160 -0
  252. synth_ai/sdk/api/train/progress/__init__.py +97 -0
  253. synth_ai/sdk/api/train/progress/dataclasses.py +569 -0
  254. synth_ai/sdk/api/train/progress/events.py +326 -0
  255. synth_ai/sdk/api/train/progress/results.py +428 -0
  256. synth_ai/sdk/api/train/progress/tracker.py +641 -0
  257. synth_ai/sdk/api/train/prompt_learning.py +800 -0
  258. synth_ai/sdk/api/train/rl.py +478 -0
  259. synth_ai/sdk/api/train/sft.py +398 -0
  260. synth_ai/sdk/api/train/summary.py +522 -0
  261. synth_ai/sdk/api/train/supported_algos.py +147 -0
  262. synth_ai/sdk/api/train/task_app.py +351 -0
  263. synth_ai/sdk/api/train/utils.py +279 -0
  264. synth_ai/sdk/api/train/validators.py +2424 -0
  265. synth_ai/sdk/graphs/__init__.py +15 -0
  266. synth_ai/sdk/graphs/completions.py +776 -0
  267. synth_ai/sdk/graphs/verifier_schemas.py +222 -0
  268. synth_ai/sdk/inference/__init__.py +6 -0
  269. synth_ai/sdk/inference/client.py +128 -0
  270. synth_ai/sdk/jobs/__init__.py +16 -0
  271. synth_ai/sdk/jobs/client.py +371 -0
  272. synth_ai/sdk/learning/__init__.py +99 -0
  273. synth_ai/sdk/learning/client.py +240 -0
  274. synth_ai/sdk/learning/context_learning_client.py +531 -0
  275. synth_ai/sdk/learning/context_learning_types.py +294 -0
  276. synth_ai/sdk/learning/ft_client.py +7 -0
  277. synth_ai/sdk/learning/health.py +49 -0
  278. synth_ai/sdk/learning/jobs.py +202 -0
  279. synth_ai/sdk/learning/prompt_extraction.py +334 -0
  280. synth_ai/sdk/learning/prompt_learning_client.py +455 -0
  281. synth_ai/sdk/learning/prompt_learning_types.py +186 -0
  282. synth_ai/sdk/learning/rl/__init__.py +35 -0
  283. synth_ai/sdk/learning/rl/client.py +268 -0
  284. synth_ai/sdk/learning/rl/contracts.py +23 -0
  285. synth_ai/sdk/learning/rl/env_keys.py +166 -0
  286. synth_ai/sdk/learning/rl/secrets.py +13 -0
  287. synth_ai/sdk/learning/sft/client.py +95 -0
  288. synth_ai/sdk/learning/sft/config.py +270 -0
  289. synth_ai/sdk/learning/sft/data.py +698 -0
  290. synth_ai/sdk/learning/validators.py +52 -0
  291. synth_ai/sdk/localapi/__init__.py +40 -0
  292. synth_ai/sdk/localapi/apps/__init__.py +28 -0
  293. synth_ai/sdk/localapi/client.py +10 -0
  294. synth_ai/sdk/localapi/contracts.py +10 -0
  295. synth_ai/sdk/localapi/helpers.py +519 -0
  296. synth_ai/sdk/localapi/rollouts.py +93 -0
  297. synth_ai/sdk/localapi/server.py +29 -0
  298. synth_ai/sdk/localapi/template.py +49 -0
  299. synth_ai/sdk/streaming/__init__.py +35 -0
  300. synth_ai/sdk/streaming/config.py +94 -0
  301. synth_ai/sdk/streaming/handlers.py +1997 -0
  302. synth_ai/sdk/streaming/streamer.py +708 -0
  303. synth_ai/sdk/streaming/types.py +112 -0
  304. synth_ai/sdk/task/__init__.py +164 -0
  305. synth_ai/sdk/task/apps/__init__.py +169 -0
  306. synth_ai/sdk/task/client.py +175 -0
  307. synth_ai/sdk/task/config.py +256 -0
  308. synth_ai/sdk/task/contracts.py +340 -0
  309. synth_ai/sdk/task/datasets.py +108 -0
  310. synth_ai/sdk/task/in_process.py +1200 -0
  311. synth_ai/sdk/task/in_process_runner.py +314 -0
  312. synth_ai/sdk/task/inference_api.py +299 -0
  313. synth_ai/sdk/task/proxy.py +287 -0
  314. synth_ai/sdk/task/rubrics/__init__.py +54 -0
  315. synth_ai/sdk/task/rubrics/loaders.py +156 -0
  316. synth_ai/sdk/task/rubrics/strict.py +148 -0
  317. synth_ai/sdk/task/rubrics.py +219 -0
  318. synth_ai/sdk/task/server.py +640 -0
  319. synth_ai/sdk/task/trace_correlation_helpers.py +557 -0
  320. synth_ai/sdk/task/tracing_utils.py +95 -0
  321. synth_ai/sdk/task/validators.py +441 -0
  322. synth_ai/sdk/training/__init__.py +93 -0
  323. synth_ai/sdk/tunnels/__init__.py +118 -0
  324. synth_ai/sdk/tunnels/cleanup.py +83 -0
  325. synth_ai/sdk/tunnels/ports.py +120 -0
  326. synth_ai/sdk/tunnels/tunneled_api.py +363 -0
  327. synth_ai/utils/__init__.py +213 -0
  328. synth_ai-0.4.4.dist-info/METADATA +262 -0
  329. synth_ai-0.4.4.dist-info/RECORD +369 -0
  330. synth_ai-0.4.4.dist-info/top_level.txt +1 -0
  331. examples/__init__.py +0 -16
  332. examples/analyze_semantic_words.sh +0 -17
  333. examples/crafter_debug_render.py +0 -186
  334. examples/dev/qwen3_32b_qlora_4xh100.toml +0 -40
  335. examples/multi_step/configs/README_verilog_rl.md +0 -77
  336. examples/multi_step/configs/VERILOG_REWARDS.md +0 -90
  337. examples/multi_step/configs/VERILOG_RL_CHECKLIST.md +0 -183
  338. examples/multi_step/configs/crafter_eval_synth_qwen4b.toml +0 -35
  339. examples/multi_step/configs/crafter_eval_text_only_groq_qwen32b.toml +0 -36
  340. examples/multi_step/configs/crafter_rl_outcome.toml +0 -74
  341. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +0 -187
  342. examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +0 -83
  343. examples/multi_step/configs/crafter_rl_stepwise_simple.toml +0 -78
  344. examples/multi_step/configs/crafter_synth_backend.md +0 -40
  345. examples/multi_step/configs/verilog_eval_groq_qwen32b.toml +0 -31
  346. examples/multi_step/configs/verilog_eval_synth_qwen8b.toml +0 -33
  347. examples/multi_step/configs/verilog_rl_lora.toml +0 -190
  348. examples/multi_step/crafter_rl_lora.md +0 -70
  349. examples/multi_step/judges/crafter_backend_judge.py +0 -220
  350. examples/multi_step/judges/verilog_backend_judge.py +0 -234
  351. examples/multi_step/readme.md +0 -48
  352. examples/multi_step/sse_metrics_streaming_notes.md +0 -357
  353. examples/multi_step/task_app_config_notes.md +0 -494
  354. examples/multi_step/verilog_rl_lora.md +0 -218
  355. examples/qwen_coder/README.md +0 -102
  356. examples/qwen_coder/_shared.py +0 -113
  357. examples/qwen_coder/configs/coder_lora_30b.toml +0 -61
  358. examples/qwen_coder/configs/coder_lora_4b.toml +0 -57
  359. examples/qwen_coder/configs/coder_lora_small.toml +0 -58
  360. examples/qwen_coder/generate_dataset.py +0 -98
  361. examples/qwen_coder/infer_ft_smoke.py +0 -65
  362. examples/qwen_coder/infer_prod_proxy.py +0 -73
  363. examples/qwen_coder/infer_via_synth.py +0 -87
  364. examples/qwen_coder/scripts/infer_coder.sh +0 -19
  365. examples/qwen_coder/scripts/train_coder_30b.sh +0 -22
  366. examples/qwen_coder/sft_full_17b.py +0 -103
  367. examples/qwen_coder/sft_lora_30b.py +0 -110
  368. examples/qwen_coder/subset_jsonl.py +0 -39
  369. examples/qwen_coder/todos.md +0 -38
  370. examples/qwen_coder/validate_jsonl.py +0 -60
  371. examples/rl/README.md +0 -169
  372. examples/rl/download_dataset.py +0 -80
  373. examples/run_crafter_demo.sh +0 -10
  374. examples/sft/README.md +0 -139
  375. examples/sft/configs/crafter_fft_qwen0p6b.toml +0 -44
  376. examples/sft/configs/crafter_lora_qwen0p6b.toml +0 -45
  377. examples/sft/evaluate.py +0 -119
  378. examples/sft/export_dataset.py +0 -117
  379. examples/sft/generate_traces.py +0 -164
  380. examples/swe/__init__.py +0 -12
  381. examples/swe/task_app/README.md +0 -105
  382. examples/swe/task_app/__init__.py +0 -2
  383. examples/swe/task_app/grpo_swe_mini.py +0 -601
  384. examples/swe/task_app/grpo_swe_mini_task_app.py +0 -136
  385. examples/swe/task_app/hosted/README.md +0 -173
  386. examples/swe/task_app/hosted/__init__.py +0 -5
  387. examples/swe/task_app/hosted/branching.py +0 -143
  388. examples/swe/task_app/hosted/environment_routes.py +0 -1289
  389. examples/swe/task_app/hosted/envs/__init__.py +0 -1
  390. examples/swe/task_app/hosted/envs/crafter/__init__.py +0 -6
  391. examples/swe/task_app/hosted/envs/crafter/app.py +0 -1
  392. examples/swe/task_app/hosted/envs/crafter/environment.py +0 -522
  393. examples/swe/task_app/hosted/envs/crafter/policy.py +0 -478
  394. examples/swe/task_app/hosted/envs/crafter/react_agent.py +0 -108
  395. examples/swe/task_app/hosted/envs/crafter/shared.py +0 -305
  396. examples/swe/task_app/hosted/envs/crafter/tools.py +0 -47
  397. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +0 -8
  398. examples/swe/task_app/hosted/envs/mini_swe/environment.py +0 -1164
  399. examples/swe/task_app/hosted/envs/mini_swe/policy.py +0 -355
  400. examples/swe/task_app/hosted/envs/mini_swe/shared.py +0 -83
  401. examples/swe/task_app/hosted/envs/mini_swe/tools.py +0 -96
  402. examples/swe/task_app/hosted/hosted_app.py +0 -204
  403. examples/swe/task_app/hosted/inference/__init__.py +0 -5
  404. examples/swe/task_app/hosted/inference/openai_client.py +0 -618
  405. examples/swe/task_app/hosted/main.py +0 -100
  406. examples/swe/task_app/hosted/policy_routes.py +0 -1079
  407. examples/swe/task_app/hosted/registry.py +0 -195
  408. examples/swe/task_app/hosted/rollout.py +0 -1911
  409. examples/swe/task_app/hosted/storage/__init__.py +0 -5
  410. examples/swe/task_app/hosted/storage/volume.py +0 -211
  411. examples/swe/task_app/hosted/test_agents.py +0 -161
  412. examples/swe/task_app/hosted/test_service.py +0 -136
  413. examples/swe/task_app/hosted/utils.py +0 -62
  414. examples/task_apps/IMAGE_ONLY_EVAL_QUICKSTART.md +0 -258
  415. examples/task_apps/TESTING.md +0 -275
  416. examples/task_apps/crafter/CREATE_SFT_DATASET.md +0 -273
  417. examples/task_apps/crafter/EVAL_IMAGE_ONLY_RESULTS.md +0 -152
  418. examples/task_apps/crafter/FILTER_COMMAND_STATUS.md +0 -174
  419. examples/task_apps/crafter/FILTER_COMMAND_SUCCESS.md +0 -268
  420. examples/task_apps/crafter/QUERY_EXAMPLES.md +0 -203
  421. examples/task_apps/crafter/README_IMAGE_ONLY_EVAL.md +0 -316
  422. examples/task_apps/crafter/__init__.py +0 -0
  423. examples/task_apps/crafter/eval_image_only_gpt4o.toml +0 -28
  424. examples/task_apps/crafter/eval_text_only_groq_llama.toml +0 -36
  425. examples/task_apps/crafter/filter_sft_dataset.toml +0 -16
  426. examples/task_apps/crafter/task_app/README.md +0 -42
  427. examples/task_apps/crafter/task_app/__init__.py +0 -5
  428. examples/task_apps/crafter/task_app/grpo_crafter.py +0 -973
  429. examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +0 -146
  430. examples/task_apps/crafter/task_app/synth_envs_hosted/README.md +0 -173
  431. examples/task_apps/crafter/task_app/synth_envs_hosted/__init__.py +0 -5
  432. examples/task_apps/crafter/task_app/synth_envs_hosted/branching.py +0 -143
  433. examples/task_apps/crafter/task_app/synth_envs_hosted/environment_routes.py +0 -1226
  434. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/__init__.py +0 -1
  435. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -6
  436. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/app.py +0 -1
  437. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -532
  438. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +0 -547
  439. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -123
  440. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -305
  441. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -47
  442. examples/task_apps/crafter/task_app/synth_envs_hosted/hosted_app.py +0 -204
  443. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/__init__.py +0 -5
  444. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +0 -704
  445. examples/task_apps/crafter/task_app/synth_envs_hosted/main.py +0 -100
  446. examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +0 -1152
  447. examples/task_apps/crafter/task_app/synth_envs_hosted/registry.py +0 -195
  448. examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +0 -2160
  449. examples/task_apps/crafter/task_app/synth_envs_hosted/storage/__init__.py +0 -5
  450. examples/task_apps/crafter/task_app/synth_envs_hosted/storage/volume.py +0 -211
  451. examples/task_apps/crafter/task_app/synth_envs_hosted/test_agents.py +0 -161
  452. examples/task_apps/crafter/task_app/synth_envs_hosted/test_service.py +0 -136
  453. examples/task_apps/crafter/task_app/synth_envs_hosted/utils.py +0 -218
  454. examples/task_apps/dev/pokemon_emerald/__init__.py +0 -2
  455. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +0 -811
  456. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +0 -120
  457. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +0 -160
  458. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +0 -155
  459. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +0 -69
  460. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +0 -96
  461. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +0 -1502
  462. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +0 -4
  463. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +0 -68
  464. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +0 -216
  465. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +0 -35
  466. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +0 -631
  467. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +0 -1544
  468. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +0 -1428
  469. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +0 -4848
  470. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +0 -41
  471. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +0 -298
  472. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +0 -95
  473. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +0 -204
  474. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
  475. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +0 -2152
  476. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +0 -429
  477. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +0 -155
  478. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +0 -78
  479. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
  480. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +0 -122
  481. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +0 -76
  482. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +0 -413
  483. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +0 -204
  484. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +0 -133
  485. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +0 -229
  486. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +0 -300
  487. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +0 -205
  488. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +0 -200
  489. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +0 -284
  490. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +0 -468
  491. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +0 -575
  492. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +0 -311
  493. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +0 -259
  494. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
  495. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +0 -372
  496. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +0 -296
  497. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +0 -275
  498. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +0 -22
  499. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +0 -44
  500. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +0 -514
  501. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +0 -415
  502. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +0 -1763
  503. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +0 -33
  504. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +0 -106
  505. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +0 -334
  506. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +0 -1020
  507. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +0 -188
  508. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +0 -1481
  509. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +0 -862
  510. examples/task_apps/dev/pokemon_emerald/modal_app.py +0 -114
  511. examples/task_apps/dev/pokemon_emerald/task_app/README.md +0 -81
  512. examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +0 -6
  513. examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +0 -685
  514. examples/task_apps/enron/__init__.py +0 -1
  515. examples/task_apps/enron/eval_groq_qwen32.toml +0 -16
  516. examples/task_apps/enron/filter_sft.toml +0 -5
  517. examples/task_apps/enron/task_app/README.md +0 -14
  518. examples/task_apps/enron/task_app/__init__.py +0 -1
  519. examples/task_apps/enron/task_app/grpo_enron.py +0 -906
  520. examples/task_apps/enron/task_app/grpo_enron_task_app.py +0 -146
  521. examples/task_apps/enron/tests/__init__.py +0 -4
  522. examples/task_apps/enron/tests/conftest.py +0 -115
  523. examples/task_apps/enron/tests/integration/__init__.py +0 -4
  524. examples/task_apps/enron/tests/integration/test_enron_eval.py +0 -179
  525. examples/task_apps/enron/tests/integration/test_enron_rollout.py +0 -135
  526. examples/task_apps/enron/tests/unit/__init__.py +0 -4
  527. examples/task_apps/enron/tests/unit/test_enron_environment.py +0 -126
  528. examples/task_apps/math/README.md +0 -22
  529. examples/task_apps/math/__init__.py +0 -0
  530. examples/task_apps/math/math_single_step.py +0 -1000
  531. examples/task_apps/math/math_task_app.py +0 -115
  532. examples/task_apps/pokemon_battle/__init__.py +0 -2
  533. examples/task_apps/pokemon_battle/modal_app.py +0 -104
  534. examples/task_apps/pokemon_battle/task_app/README.md +0 -68
  535. examples/task_apps/pokemon_battle/task_app/__init__.py +0 -6
  536. examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +0 -932
  537. examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_COMPLETE.md +0 -283
  538. examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_STATUS.md +0 -155
  539. examples/task_apps/pokemon_red/README.md +0 -357
  540. examples/task_apps/pokemon_red/README_IMAGE_ONLY_EVAL.md +0 -415
  541. examples/task_apps/pokemon_red/__init__.py +0 -3
  542. examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +0 -29
  543. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +0 -225
  544. examples/task_apps/pokemon_red/pallet_town_rl_config.toml +0 -75
  545. examples/task_apps/pokemon_red/task_app.py +0 -799
  546. examples/task_apps/pokemon_red/test_pallet_town_rewards.py +0 -193
  547. examples/task_apps/sokoban/README.md +0 -307
  548. examples/task_apps/sokoban/__init__.py +0 -3
  549. examples/task_apps/sokoban/eval_groq_qwen32.toml +0 -16
  550. examples/task_apps/sokoban/eval_openai_gpt5.toml +0 -16
  551. examples/task_apps/sokoban/filter_sft.toml +0 -5
  552. examples/task_apps/sokoban/task_app.py +0 -1058
  553. examples/task_apps/sokoban/tests/__init__.py +0 -4
  554. examples/task_apps/sokoban/tests/conftest.py +0 -113
  555. examples/task_apps/sokoban/tests/integration/__init__.py +0 -4
  556. examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +0 -57
  557. examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +0 -198
  558. examples/task_apps/sokoban/tests/unit/__init__.py +0 -4
  559. examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +0 -114
  560. examples/task_apps/verilog/__init__.py +0 -1
  561. examples/task_apps/verilog/eval_groq_qwen32b.toml +0 -24
  562. examples/task_apps/verilog/filter_sft.toml +0 -5
  563. examples/task_apps/verilog/task_app/README.md +0 -12
  564. examples/task_apps/verilog/task_app/__init__.py +0 -1
  565. examples/task_apps/verilog/task_app/grpo_verilog.py +0 -1166
  566. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +0 -145
  567. examples/task_apps/verilog/tests/__init__.py +0 -4
  568. examples/task_apps/verilog/tests/conftest.py +0 -115
  569. examples/task_apps/verilog/tests/integration/__init__.py +0 -4
  570. examples/task_apps/verilog/tests/integration/test_verilog_eval.py +0 -181
  571. examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +0 -55
  572. examples/task_apps/verilog/tests/unit/__init__.py +0 -4
  573. examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +0 -118
  574. examples/vlm/PROPOSAL.md +0 -53
  575. examples/vlm/README.md +0 -68
  576. examples/vlm/configs/crafter_vlm_gpt4o.toml +0 -44
  577. examples/vlm/crafter_image_only_agent.py +0 -207
  578. examples/vlm/crafter_openai_vlm_agent.py +0 -277
  579. examples/vlm/filter_image_rows.py +0 -63
  580. examples/vlm/run_crafter_vlm_benchmark.py +0 -316
  581. examples/warming_up_to_rl/analyze_trace_db.py +0 -422
  582. examples/warming_up_to_rl/configs/crafter_fft.toml +0 -48
  583. examples/warming_up_to_rl/configs/crafter_fft_4b.toml +0 -54
  584. examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +0 -20
  585. examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +0 -13
  586. examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +0 -23
  587. examples/warming_up_to_rl/configs/eval_stepwise_complex.toml +0 -35
  588. examples/warming_up_to_rl/configs/eval_stepwise_consistent.toml +0 -26
  589. examples/warming_up_to_rl/configs/eval_stepwise_per_achievement.toml +0 -36
  590. examples/warming_up_to_rl/configs/eval_stepwise_simple.toml +0 -32
  591. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +0 -83
  592. examples/warming_up_to_rl/configs/rl_from_ft.toml +0 -56
  593. examples/warming_up_to_rl/export_trace_sft.py +0 -723
  594. examples/warming_up_to_rl/groq_test.py +0 -97
  595. examples/warming_up_to_rl/manage_secrets.py +0 -131
  596. examples/warming_up_to_rl/old/event_rewards.md +0 -234
  597. examples/warming_up_to_rl/old/notes.md +0 -73
  598. examples/warming_up_to_rl/readme.md +0 -179
  599. examples/warming_up_to_rl/run_eval.py +0 -736
  600. examples/warming_up_to_rl/run_fft_and_save.py +0 -380
  601. examples/warming_up_to_rl/run_local_rollout.py +0 -239
  602. examples/warming_up_to_rl/run_local_rollout_modal.py +0 -248
  603. examples/warming_up_to_rl/run_local_rollout_parallel.py +0 -405
  604. examples/warming_up_to_rl/run_local_rollout_traced.py +0 -477
  605. examples/warming_up_to_rl/run_rl_and_save.py +0 -124
  606. examples/warming_up_to_rl/run_rollout_remote.py +0 -156
  607. examples/workflows/__init__.py +0 -0
  608. examples/workflows/math_rl/__init__.py +0 -0
  609. examples/workflows/math_rl/configs/eval_base_qwen.toml +0 -15
  610. examples/workflows/math_rl/configs/eval_rl_qwen.toml +0 -11
  611. examples/workflows/math_rl/configs/rl_from_base_qwen.toml +0 -35
  612. examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +0 -74
  613. examples/workflows/math_rl/configs/rl_from_ft_qwen.toml +0 -35
  614. examples/workflows/math_rl/download_dataset.py +0 -80
  615. examples/workflows/math_rl/run_eval.py +0 -436
  616. examples/workflows/math_rl/run_rl_and_save.py +0 -111
  617. synth_ai/api/models/supported.py +0 -377
  618. synth_ai/api/train/__init__.py +0 -5
  619. synth_ai/api/train/builders.py +0 -351
  620. synth_ai/api/train/cli.py +0 -635
  621. synth_ai/api/train/config_finder.py +0 -228
  622. synth_ai/api/train/configs/__init__.py +0 -44
  623. synth_ai/api/train/configs/rl.py +0 -134
  624. synth_ai/api/train/configs/sft.py +0 -95
  625. synth_ai/api/train/configs/shared.py +0 -24
  626. synth_ai/api/train/env_resolver.py +0 -349
  627. synth_ai/api/train/pollers.py +0 -75
  628. synth_ai/api/train/supported_algos.py +0 -147
  629. synth_ai/api/train/task_app.py +0 -195
  630. synth_ai/api/train/utils.py +0 -225
  631. synth_ai/cli/_modal_wrapper.py +0 -29
  632. synth_ai/cli/_storage.py +0 -20
  633. synth_ai/cli/_typer_patch.py +0 -49
  634. synth_ai/cli/_validate_task_app.py +0 -11
  635. synth_ai/cli/balance.py +0 -216
  636. synth_ai/cli/calc.py +0 -84
  637. synth_ai/cli/demo.py +0 -165
  638. synth_ai/cli/legacy_root_backup.py +0 -468
  639. synth_ai/cli/man.py +0 -106
  640. synth_ai/cli/recent.py +0 -132
  641. synth_ai/cli/rl_demo.py +0 -254
  642. synth_ai/cli/status.py +0 -134
  643. synth_ai/cli/task_apps.py +0 -4523
  644. synth_ai/cli/traces.py +0 -164
  645. synth_ai/cli/tui.py +0 -57
  646. synth_ai/cli/watch.py +0 -506
  647. synth_ai/compound/cais.py +0 -0
  648. synth_ai/config/base_url.py +0 -107
  649. synth_ai/core/experiment.py +0 -13
  650. synth_ai/core/system.py +0 -15
  651. synth_ai/demo_registry.py +0 -295
  652. synth_ai/demos/core/__init__.py +0 -1
  653. synth_ai/demos/core/cli.py +0 -1718
  654. synth_ai/demos/demo_task_apps/core.py +0 -440
  655. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +0 -184
  656. synth_ai/demos/demo_task_apps/math/config.toml +0 -74
  657. synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +0 -22
  658. synth_ai/demos/demo_task_apps/math/modal_task_app.py +0 -739
  659. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -37
  660. synth_ai/environments/__init__.py +0 -31
  661. synth_ai/environments/environment/__init__.py +0 -1
  662. synth_ai/environments/environment/artifacts/__init__.py +0 -1
  663. synth_ai/environments/environment/artifacts/base.py +0 -52
  664. synth_ai/environments/environment/core.py +0 -67
  665. synth_ai/environments/environment/db/__init__.py +0 -1
  666. synth_ai/environments/environment/db/sqlite.py +0 -45
  667. synth_ai/environments/environment/registry.py +0 -233
  668. synth_ai/environments/environment/resources/sqlite.py +0 -45
  669. synth_ai/environments/environment/results.py +0 -1
  670. synth_ai/environments/environment/rewards/__init__.py +0 -1
  671. synth_ai/environments/environment/rewards/core.py +0 -29
  672. synth_ai/environments/environment/shared_engine.py +0 -26
  673. synth_ai/environments/environment/tools/__init__.py +0 -200
  674. synth_ai/environments/examples/__init__.py +0 -1
  675. synth_ai/environments/examples/bandit/__init__.py +0 -33
  676. synth_ai/environments/examples/bandit/engine.py +0 -302
  677. synth_ai/environments/examples/bandit/environment.py +0 -194
  678. synth_ai/environments/examples/bandit/taskset.py +0 -200
  679. synth_ai/environments/examples/crafter_classic/__init__.py +0 -8
  680. synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +0 -250
  681. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +0 -59
  682. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +0 -152
  683. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +0 -24
  684. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +0 -1194
  685. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +0 -56
  686. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +0 -32
  687. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  688. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +0 -384
  689. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +0 -53
  690. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +0 -178
  691. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +0 -222
  692. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +0 -183
  693. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +0 -210
  694. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +0 -206
  695. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +0 -49
  696. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +0 -64
  697. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +0 -88
  698. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +0 -77
  699. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +0 -324
  700. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  701. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +0 -362
  702. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +0 -49
  703. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +0 -332
  704. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +0 -97
  705. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +0 -217
  706. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +0 -87
  707. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +0 -88
  708. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +0 -195
  709. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +0 -400
  710. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +0 -195
  711. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +0 -56
  712. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +0 -858
  713. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +0 -52
  714. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +0 -874
  715. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +0 -1412
  716. synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +0 -216
  717. synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +0 -296
  718. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +0 -58
  719. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +0 -464
  720. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +0 -152
  721. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +0 -51
  722. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +0 -1412
  723. synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +0 -112
  724. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +0 -203
  725. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +0 -305
  726. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +0 -126
  727. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +0 -94
  728. synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +0 -142
  729. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +0 -26
  730. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +0 -984
  731. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +0 -724
  732. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +0 -386
  733. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +0 -205
  734. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +0 -150
  735. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +0 -283
  736. synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +0 -280
  737. synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +0 -456
  738. synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +0 -166
  739. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +0 -102
  740. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +0 -128
  741. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +0 -655
  742. synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +0 -202
  743. synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +0 -166
  744. synth_ai/environments/examples/crafter_classic/config_logging.py +0 -111
  745. synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
  746. synth_ai/environments/examples/crafter_classic/engine.py +0 -579
  747. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +0 -64
  748. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +0 -6
  749. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +0 -75
  750. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +0 -267
  751. synth_ai/environments/examples/crafter_classic/environment.py +0 -495
  752. synth_ai/environments/examples/crafter_classic/taskset.py +0 -233
  753. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +0 -228
  754. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +0 -299
  755. synth_ai/environments/examples/crafter_custom/__init__.py +0 -4
  756. synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +0 -1
  757. synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +0 -202
  758. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +0 -7
  759. synth_ai/environments/examples/crafter_custom/crafter/config.py +0 -182
  760. synth_ai/environments/examples/crafter_custom/crafter/constants.py +0 -8
  761. synth_ai/environments/examples/crafter_custom/crafter/engine.py +0 -269
  762. synth_ai/environments/examples/crafter_custom/crafter/env.py +0 -262
  763. synth_ai/environments/examples/crafter_custom/crafter/objects.py +0 -417
  764. synth_ai/environments/examples/crafter_custom/crafter/recorder.py +0 -187
  765. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +0 -118
  766. synth_ai/environments/examples/crafter_custom/dataset_builder.py +0 -373
  767. synth_ai/environments/examples/crafter_custom/environment.py +0 -312
  768. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +0 -159
  769. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +0 -158
  770. synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +0 -71
  771. synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +0 -105
  772. synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +0 -119
  773. synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +0 -52
  774. synth_ai/environments/examples/crafter_custom/run_dataset.py +0 -305
  775. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +0 -156
  776. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +0 -281
  777. synth_ai/environments/examples/enron/art_helpers/types_enron.py +0 -25
  778. synth_ai/environments/examples/enron/engine.py +0 -300
  779. synth_ai/environments/examples/enron/environment.py +0 -234
  780. synth_ai/environments/examples/enron/taskset.py +0 -112
  781. synth_ai/environments/examples/enron/units/keyword_stats.py +0 -112
  782. synth_ai/environments/examples/minigrid/__init__.py +0 -48
  783. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +0 -1188
  784. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +0 -48
  785. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +0 -562
  786. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +0 -221
  787. synth_ai/environments/examples/minigrid/engine.py +0 -589
  788. synth_ai/environments/examples/minigrid/environment.py +0 -274
  789. synth_ai/environments/examples/minigrid/environment_mapping.py +0 -242
  790. synth_ai/environments/examples/minigrid/puzzle_loader.py +0 -417
  791. synth_ai/environments/examples/minigrid/taskset.py +0 -583
  792. synth_ai/environments/examples/nethack/__init__.py +0 -7
  793. synth_ai/environments/examples/nethack/achievements.py +0 -337
  794. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +0 -981
  795. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +0 -74
  796. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +0 -831
  797. synth_ai/environments/examples/nethack/engine.py +0 -739
  798. synth_ai/environments/examples/nethack/environment.py +0 -256
  799. synth_ai/environments/examples/nethack/helpers/__init__.py +0 -41
  800. synth_ai/environments/examples/nethack/helpers/action_mapping.py +0 -301
  801. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +0 -402
  802. synth_ai/environments/examples/nethack/helpers/observation_utils.py +0 -433
  803. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +0 -200
  804. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +0 -269
  805. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +0 -308
  806. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +0 -431
  807. synth_ai/environments/examples/nethack/taskset.py +0 -323
  808. synth_ai/environments/examples/red/__init__.py +0 -7
  809. synth_ai/environments/examples/red/agent_demos/__init__.py +0 -1
  810. synth_ai/environments/examples/red/config_logging.py +0 -110
  811. synth_ai/environments/examples/red/engine.py +0 -721
  812. synth_ai/environments/examples/red/engine_helpers/__init__.py +0 -1
  813. synth_ai/environments/examples/red/engine_helpers/memory_map.py +0 -35
  814. synth_ai/environments/examples/red/engine_helpers/reward_components.py +0 -276
  815. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +0 -142
  816. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +0 -57
  817. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +0 -284
  818. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +0 -150
  819. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +0 -138
  820. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +0 -57
  821. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +0 -331
  822. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +0 -121
  823. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +0 -477
  824. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +0 -559
  825. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +0 -313
  826. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +0 -148
  827. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +0 -247
  828. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +0 -368
  829. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +0 -172
  830. synth_ai/environments/examples/red/environment.py +0 -298
  831. synth_ai/environments/examples/red/taskset.py +0 -79
  832. synth_ai/environments/examples/red/units/__init__.py +0 -1
  833. synth_ai/environments/examples/sokoban/__init__.py +0 -1
  834. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +0 -899
  835. synth_ai/environments/examples/sokoban/engine.py +0 -678
  836. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +0 -1
  837. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +0 -657
  838. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +0 -18
  839. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +0 -3
  840. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +0 -131
  841. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +0 -370
  842. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +0 -332
  843. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +0 -306
  844. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +0 -67
  845. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +0 -115
  846. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +0 -123
  847. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +0 -394
  848. synth_ai/environments/examples/sokoban/environment.py +0 -229
  849. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +0 -440
  850. synth_ai/environments/examples/sokoban/puzzle_loader.py +0 -312
  851. synth_ai/environments/examples/sokoban/taskset.py +0 -544
  852. synth_ai/environments/examples/tictactoe/__init__.py +0 -1
  853. synth_ai/environments/examples/tictactoe/engine.py +0 -368
  854. synth_ai/environments/examples/tictactoe/environment.py +0 -240
  855. synth_ai/environments/examples/tictactoe/taskset.py +0 -215
  856. synth_ai/environments/examples/verilog/__init__.py +0 -10
  857. synth_ai/environments/examples/verilog/engine.py +0 -421
  858. synth_ai/environments/examples/verilog/environment.py +0 -350
  859. synth_ai/environments/examples/verilog/taskset.py +0 -420
  860. synth_ai/environments/examples/wordle/__init__.py +0 -29
  861. synth_ai/environments/examples/wordle/engine.py +0 -398
  862. synth_ai/environments/examples/wordle/environment.py +0 -159
  863. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +0 -75
  864. synth_ai/environments/examples/wordle/taskset.py +0 -230
  865. synth_ai/environments/reproducibility/core.py +0 -42
  866. synth_ai/environments/reproducibility/helpers.py +0 -0
  867. synth_ai/environments/reproducibility/tree.py +0 -363
  868. synth_ai/environments/service/app.py +0 -97
  869. synth_ai/environments/service/core_routes.py +0 -1021
  870. synth_ai/environments/service/external_registry.py +0 -56
  871. synth_ai/environments/service/registry.py +0 -9
  872. synth_ai/environments/stateful/__init__.py +0 -1
  873. synth_ai/environments/stateful/core.py +0 -163
  874. synth_ai/environments/stateful/engine.py +0 -21
  875. synth_ai/environments/stateful/state.py +0 -7
  876. synth_ai/environments/tasks/api.py +0 -19
  877. synth_ai/environments/tasks/core.py +0 -81
  878. synth_ai/environments/tasks/filters.py +0 -40
  879. synth_ai/environments/tasks/utils.py +0 -90
  880. synth_ai/environments/v0_observability/history.py +0 -3
  881. synth_ai/environments/v0_observability/log.py +0 -2
  882. synth_ai/evals/__init__.py +0 -15
  883. synth_ai/evals/base.py +0 -13
  884. synth_ai/evals/client.py +0 -82
  885. synth_ai/evals/types.py +0 -42
  886. synth_ai/handshake.py +0 -109
  887. synth_ai/http.py +0 -26
  888. synth_ai/http_client.py +0 -136
  889. synth_ai/inference/__init__.py +0 -5
  890. synth_ai/inference/client.py +0 -34
  891. synth_ai/jobs/client.py +0 -295
  892. synth_ai/judge_schemas.py +0 -127
  893. synth_ai/learning/__init__.py +0 -59
  894. synth_ai/learning/client.py +0 -241
  895. synth_ai/learning/ft_client.py +0 -7
  896. synth_ai/learning/health.py +0 -49
  897. synth_ai/learning/jobs.py +0 -201
  898. synth_ai/learning/rl/__init__.py +0 -39
  899. synth_ai/learning/rl/client.py +0 -267
  900. synth_ai/learning/rl/contracts.py +0 -27
  901. synth_ai/learning/rl/env_keys.py +0 -166
  902. synth_ai/learning/rl/secrets.py +0 -13
  903. synth_ai/learning/sft/client.py +0 -68
  904. synth_ai/learning/sft/config.py +0 -270
  905. synth_ai/learning/sft/data.py +0 -295
  906. synth_ai/learning/validators.py +0 -49
  907. synth_ai/lm/__init__.py +0 -25
  908. synth_ai/task/__init__.py +0 -121
  909. synth_ai/task/apps/__init__.py +0 -129
  910. synth_ai/task/client.py +0 -167
  911. synth_ai/task/config.py +0 -257
  912. synth_ai/task/contracts.py +0 -236
  913. synth_ai/task/datasets.py +0 -108
  914. synth_ai/task/proxy.py +0 -251
  915. synth_ai/task/rubrics/__init__.py +0 -56
  916. synth_ai/task/rubrics/loaders.py +0 -152
  917. synth_ai/task/rubrics/strict.py +0 -149
  918. synth_ai/task/server.py +0 -432
  919. synth_ai/task/trace_correlation_helpers.py +0 -315
  920. synth_ai/task/tracing_utils.py +0 -84
  921. synth_ai/task/validators.py +0 -418
  922. synth_ai/tracing_v3/__init__.py +0 -97
  923. synth_ai/tracing_v3/abstractions.py +0 -302
  924. synth_ai/tracing_v3/config.py +0 -84
  925. synth_ai/tracing_v3/db_config.py +0 -194
  926. synth_ai/tracing_v3/decorators.py +0 -398
  927. synth_ai/tracing_v3/llm_call_record_helpers.py +0 -391
  928. synth_ai/tracing_v3/migration_helper.py +0 -120
  929. synth_ai/tracing_v3/session_tracer.py +0 -540
  930. synth_ai/tracing_v3/storage/base.py +0 -210
  931. synth_ai/tracing_v3/storage/config.py +0 -75
  932. synth_ai/tracing_v3/storage/factory.py +0 -39
  933. synth_ai/tracing_v3/trace_utils.py +0 -317
  934. synth_ai/tracing_v3/turso/daemon.py +0 -151
  935. synth_ai/tracing_v3/turso/models.py +0 -469
  936. synth_ai/tracing_v3/turso/native_manager.py +0 -1209
  937. synth_ai/tracing_v3/utils.py +0 -108
  938. synth_ai/tui/__init__.py +0 -5
  939. synth_ai/tui/__main__.py +0 -13
  940. synth_ai/tui/cli/__init__.py +0 -1
  941. synth_ai/tui/cli/query_experiments.py +0 -164
  942. synth_ai/tui/cli/query_experiments_v3.py +0 -164
  943. synth_ai/tui/dashboard.py +0 -906
  944. synth_ai/v0/api/__init__.py +0 -8
  945. synth_ai/v0/api/models/__init__.py +0 -8
  946. synth_ai/v0/api/models/supported.py +0 -8
  947. synth_ai/v0/config/__init__.py +0 -15
  948. synth_ai/v0/config/base_url.py +0 -12
  949. synth_ai/v0/lm/__init__.py +0 -51
  950. synth_ai/v0/lm/caching/__init__.py +0 -0
  951. synth_ai/v0/lm/caching/constants.py +0 -6
  952. synth_ai/v0/lm/caching/dbs.py +0 -0
  953. synth_ai/v0/lm/caching/ephemeral.py +0 -100
  954. synth_ai/v0/lm/caching/handler.py +0 -137
  955. synth_ai/v0/lm/caching/initialize.py +0 -11
  956. synth_ai/v0/lm/caching/persistent.py +0 -114
  957. synth_ai/v0/lm/config.py +0 -115
  958. synth_ai/v0/lm/constants.py +0 -32
  959. synth_ai/v0/lm/core/__init__.py +0 -8
  960. synth_ai/v0/lm/core/all.py +0 -73
  961. synth_ai/v0/lm/core/exceptions.py +0 -5
  962. synth_ai/v0/lm/core/main.py +0 -331
  963. synth_ai/v0/lm/core/main_v3.py +0 -594
  964. synth_ai/v0/lm/core/synth_models.py +0 -35
  965. synth_ai/v0/lm/core/vendor_clients.py +0 -190
  966. synth_ai/v0/lm/cost/__init__.py +0 -0
  967. synth_ai/v0/lm/cost/monitor.py +0 -1
  968. synth_ai/v0/lm/cost/statefulness.py +0 -1
  969. synth_ai/v0/lm/injection.py +0 -80
  970. synth_ai/v0/lm/overrides.py +0 -206
  971. synth_ai/v0/lm/provider_support/__init__.py +0 -8
  972. synth_ai/v0/lm/provider_support/anthropic.py +0 -972
  973. synth_ai/v0/lm/provider_support/openai.py +0 -1139
  974. synth_ai/v0/lm/provider_support/suppress_logging.py +0 -31
  975. synth_ai/v0/lm/structured_outputs/__init__.py +0 -0
  976. synth_ai/v0/lm/structured_outputs/handler.py +0 -440
  977. synth_ai/v0/lm/structured_outputs/inject.py +0 -297
  978. synth_ai/v0/lm/structured_outputs/rehabilitate.py +0 -185
  979. synth_ai/v0/lm/tools/__init__.py +0 -3
  980. synth_ai/v0/lm/tools/base.py +0 -172
  981. synth_ai/v0/lm/unified_interface.py +0 -202
  982. synth_ai/v0/lm/vendors/__init__.py +0 -0
  983. synth_ai/v0/lm/vendors/base.py +0 -81
  984. synth_ai/v0/lm/vendors/core/__init__.py +0 -0
  985. synth_ai/v0/lm/vendors/core/anthropic_api.py +0 -387
  986. synth_ai/v0/lm/vendors/core/gemini_api.py +0 -292
  987. synth_ai/v0/lm/vendors/core/mistral_api.py +0 -322
  988. synth_ai/v0/lm/vendors/core/openai_api.py +0 -227
  989. synth_ai/v0/lm/vendors/core/synth_dev_api.py +0 -0
  990. synth_ai/v0/lm/vendors/local/__init__.py +0 -0
  991. synth_ai/v0/lm/vendors/local/ollama.py +0 -0
  992. synth_ai/v0/lm/vendors/openai_standard.py +0 -782
  993. synth_ai/v0/lm/vendors/openai_standard_responses.py +0 -259
  994. synth_ai/v0/lm/vendors/retries.py +0 -22
  995. synth_ai/v0/lm/vendors/supported/__init__.py +0 -0
  996. synth_ai/v0/lm/vendors/supported/custom_endpoint.py +0 -415
  997. synth_ai/v0/lm/vendors/supported/deepseek.py +0 -69
  998. synth_ai/v0/lm/vendors/supported/grok.py +0 -75
  999. synth_ai/v0/lm/vendors/supported/groq.py +0 -16
  1000. synth_ai/v0/lm/vendors/supported/ollama.py +0 -15
  1001. synth_ai/v0/lm/vendors/supported/openrouter.py +0 -74
  1002. synth_ai/v0/lm/vendors/supported/together.py +0 -11
  1003. synth_ai/v0/lm/vendors/synth_client.py +0 -835
  1004. synth_ai/v0/lm/warmup.py +0 -186
  1005. synth_ai/v0/tracing/__init__.py +0 -0
  1006. synth_ai/v0/tracing/abstractions.py +0 -224
  1007. synth_ai/v0/tracing/base_client.py +0 -91
  1008. synth_ai/v0/tracing/client_manager.py +0 -131
  1009. synth_ai/v0/tracing/config.py +0 -142
  1010. synth_ai/v0/tracing/context.py +0 -146
  1011. synth_ai/v0/tracing/decorators.py +0 -682
  1012. synth_ai/v0/tracing/events/__init__.py +0 -0
  1013. synth_ai/v0/tracing/events/manage.py +0 -147
  1014. synth_ai/v0/tracing/events/scope.py +0 -86
  1015. synth_ai/v0/tracing/events/store.py +0 -228
  1016. synth_ai/v0/tracing/immediate_client.py +0 -151
  1017. synth_ai/v0/tracing/local.py +0 -18
  1018. synth_ai/v0/tracing/log_client_base.py +0 -73
  1019. synth_ai/v0/tracing/retry_queue.py +0 -186
  1020. synth_ai/v0/tracing/trackers.py +0 -515
  1021. synth_ai/v0/tracing/upload.py +0 -409
  1022. synth_ai/v0/tracing/utils.py +0 -9
  1023. synth_ai/v0/tracing_v1/__init__.py +0 -16
  1024. synth_ai/v0/tracing_v1/abstractions.py +0 -224
  1025. synth_ai/v0/tracing_v1/base_client.py +0 -91
  1026. synth_ai/v0/tracing_v1/client_manager.py +0 -131
  1027. synth_ai/v0/tracing_v1/config.py +0 -142
  1028. synth_ai/v0/tracing_v1/context.py +0 -146
  1029. synth_ai/v0/tracing_v1/decorators.py +0 -703
  1030. synth_ai/v0/tracing_v1/events/__init__.py +0 -0
  1031. synth_ai/v0/tracing_v1/events/manage.py +0 -147
  1032. synth_ai/v0/tracing_v1/events/scope.py +0 -86
  1033. synth_ai/v0/tracing_v1/events/store.py +0 -228
  1034. synth_ai/v0/tracing_v1/immediate_client.py +0 -151
  1035. synth_ai/v0/tracing_v1/local.py +0 -18
  1036. synth_ai/v0/tracing_v1/log_client_base.py +0 -73
  1037. synth_ai/v0/tracing_v1/retry_queue.py +0 -186
  1038. synth_ai/v0/tracing_v1/trackers.py +0 -515
  1039. synth_ai/v0/tracing_v1/upload.py +0 -527
  1040. synth_ai/v0/tracing_v1/utils.py +0 -9
  1041. synth_ai/v0/tracing_v3/__init__.py +0 -10
  1042. synth_ai/v0/tracing_v3/abstractions.py +0 -3
  1043. synth_ai/v0/tracing_v3/decorators.py +0 -3
  1044. synth_ai/v0/tracing_v3/llm_call_record_helpers.py +0 -3
  1045. synth_ai/v0/tracing_v3/session_tracer.py +0 -3
  1046. synth_ai-0.2.14.dist-info/METADATA +0 -139
  1047. synth_ai-0.2.14.dist-info/RECORD +0 -762
  1048. synth_ai-0.2.14.dist-info/top_level.txt +0 -2
  1049. /synth_ai/{demos/demo_task_apps → cli/demo_apps}/crafter/__init__.py +0 -0
  1050. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/__init__.py +0 -0
  1051. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/crafter/configs/crafter_fft_4b.toml +0 -0
  1052. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +0 -0
  1053. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/__init__.py +0 -0
  1054. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/_common.py +0 -0
  1055. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/app.py +0 -0
  1056. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +0 -0
  1057. {examples/task_apps → synth_ai/core/apps}/__init__.py +0 -0
  1058. /synth_ai/{tracing_v3 → core/tracing_v3}/examples/basic_usage.py +0 -0
  1059. /synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +0 -0
  1060. /synth_ai/{tracing_v3 → core/tracing_v3}/lm_call_record_abstractions.py +0 -0
  1061. /synth_ai/{tracing_v3 → core/tracing_v3}/replica_sync.py +0 -0
  1062. /synth_ai/{tracing_v3 → core/tracing_v3}/serialization.py +0 -0
  1063. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/__init__.py +0 -0
  1064. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/exceptions.py +0 -0
  1065. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/types.py +0 -0
  1066. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/utils.py +0 -0
  1067. /synth_ai/{tracing_v3 → core/tracing_v3}/turso/__init__.py +0 -0
  1068. /synth_ai/{learning → sdk/learning}/algorithms.py +0 -0
  1069. /synth_ai/{learning → sdk/learning}/config.py +0 -0
  1070. /synth_ai/{learning → sdk/learning}/constants.py +0 -0
  1071. /synth_ai/{learning → sdk/learning}/core.py +0 -0
  1072. /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
  1073. /synth_ai/{learning → sdk/learning}/rl/config.py +0 -0
  1074. /synth_ai/{learning → sdk/learning}/rl_client.py +0 -0
  1075. /synth_ai/{learning → sdk/learning}/sft/__init__.py +0 -0
  1076. /synth_ai/{learning → sdk/learning}/sse.py +0 -0
  1077. /synth_ai/{task → sdk/task}/auth.py +0 -0
  1078. /synth_ai/{task → sdk/task}/errors.py +0 -0
  1079. /synth_ai/{task → sdk/task}/health.py +0 -0
  1080. /synth_ai/{task → sdk/task}/json.py +0 -0
  1081. /synth_ai/{task → sdk/task}/rubrics/models.py +0 -0
  1082. /synth_ai/{task → sdk/task}/rubrics/scoring.py +0 -0
  1083. /synth_ai/{task → sdk/task}/vendors.py +0 -0
  1084. {synth_ai-0.2.14.dist-info → synth_ai-0.4.4.dist-info}/WHEEL +0 -0
  1085. {synth_ai-0.2.14.dist-info → synth_ai-0.4.4.dist-info}/entry_points.txt +0 -0
  1086. {synth_ai-0.2.14.dist-info → synth_ai-0.4.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,1544 +0,0 @@
1
- import logging
2
- import time
3
- import threading
4
- import queue
5
- import tempfile
6
- import json
7
- import os
8
- import shutil
9
- import hashlib
10
- from pathlib import Path
11
- from typing import Dict, Any, Optional, List
12
- import numpy as np
13
- from PIL import Image
14
-
15
- import mgba.core
16
- import mgba.log
17
- import mgba.image
18
- from mgba._pylib import ffi, lib
19
-
20
- from .memory_reader import PokemonEmeraldReader
21
- from utils.state_formatter import save_persistent_world_map, load_persistent_world_map
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
- # some acknowledgement to https://github.com/dvruette/pygba
26
-
27
- class MilestoneTracker:
28
- """Persistent milestone tracking system integrated with emulator"""
29
-
30
- def __init__(self, filename: str = None):
31
- # Setup cache directory
32
- self.cache_dir = ".pokeagent_cache"
33
- os.makedirs(self.cache_dir, exist_ok=True)
34
-
35
- # Use cache folder for runtime milestone file
36
- if filename is None:
37
- filename = os.path.join(self.cache_dir, "milestones_progress.json")
38
- self.filename = filename # Runtime cache file (always in cache directory)
39
- self.loaded_state_milestones_file = None # Track if we loaded from a state-specific file
40
- self.milestones = {}
41
- self.latest_milestone = None
42
- self.latest_split_time = "00:00:00"
43
- # Don't automatically load from file - only load when explicitly requested
44
-
45
- def load_from_file(self):
46
- """Load milestone progress from file"""
47
- try:
48
- if os.path.exists(self.filename):
49
- with open(self.filename, 'r') as f:
50
- data = json.load(f)
51
- self.milestones = data.get('milestones', {})
52
-
53
- # Determine the latest completed milestone based on timestamps
54
- latest_timestamp = 0
55
- latest_milestone_id = None
56
- for milestone_id, milestone_data in self.milestones.items():
57
- if milestone_data.get('completed', False):
58
- timestamp = milestone_data.get('timestamp', 0)
59
- if timestamp > latest_timestamp:
60
- latest_timestamp = timestamp
61
- latest_milestone_id = milestone_id
62
-
63
- # Set the latest milestone if we found one
64
- if latest_milestone_id:
65
- self.latest_milestone = latest_milestone_id
66
- self.latest_split_time = self.milestones[latest_milestone_id].get('split_formatted', '00:00:00')
67
- logger.info(f"Latest milestone from file: {latest_milestone_id}")
68
-
69
- logger.info(f"Loaded {len(self.milestones)} milestone records from {self.filename}")
70
- else:
71
- logger.info(f"No existing milestone file found, starting fresh")
72
- self.milestones = {}
73
- except Exception as e:
74
- logger.warning(f"Error loading milestones from file: {e}")
75
- self.milestones = {}
76
-
77
- def save_to_file(self):
78
- """Save milestone progress to file"""
79
- try:
80
- data = {
81
- 'milestones': self.milestones,
82
- 'last_updated': time.time(),
83
- 'version': '1.0'
84
- }
85
- with open(self.filename, 'w') as f:
86
- json.dump(data, f, indent=2)
87
- logger.debug(f"Saved milestone progress to {self.filename}")
88
- except Exception as e:
89
- logger.warning(f"Error saving milestones to file: {e}")
90
-
91
- def mark_completed(self, milestone_id: str, timestamp: float = None):
92
- """Mark a milestone as completed and log split time"""
93
- if timestamp is None:
94
- timestamp = time.time()
95
-
96
- if milestone_id not in self.milestones or not self.milestones[milestone_id].get('completed', False):
97
- # Calculate split time from previous milestone or start
98
- split_time = self._calculate_split_time(milestone_id, timestamp)
99
-
100
- self.milestones[milestone_id] = {
101
- 'completed': True,
102
- 'timestamp': timestamp,
103
- 'first_completed': timestamp,
104
- 'split_time': split_time,
105
- 'split_formatted': self._format_time(split_time),
106
- 'total_time': self._calculate_total_time(timestamp),
107
- 'total_formatted': self._format_time(self._calculate_total_time(timestamp))
108
- }
109
-
110
- # Store the latest completed milestone for easy access
111
- self.latest_milestone = milestone_id
112
- self.latest_split_time = self._format_time(split_time)
113
-
114
- logger.info(f"Milestone completed: {milestone_id} (Split: {self._format_time(split_time)})")
115
- self.save_to_file()
116
- return True
117
- return False
118
-
119
- def is_completed(self, milestone_id: str) -> bool:
120
- """Check if a milestone is completed"""
121
- return self.milestones.get(milestone_id, {}).get('completed', False)
122
-
123
- def get_milestone_data(self, milestone_id: str) -> dict:
124
- """Get milestone data"""
125
- return self.milestones.get(milestone_id, {'completed': False, 'timestamp': None})
126
-
127
- def reset_milestone(self, milestone_id: str):
128
- """Reset a milestone (for testing)"""
129
- if milestone_id in self.milestones:
130
- del self.milestones[milestone_id]
131
- self.save_to_file()
132
- logger.info(f"Reset milestone: {milestone_id}")
133
-
134
- def _calculate_split_time(self, milestone_id: str, timestamp: float) -> float:
135
- """Calculate split time from previous milestone completion or start"""
136
- # Define milestone order for split calculation
137
- milestone_order = [
138
- # Phase 1: Game Initialization
139
- "GAME_RUNNING", "PLAYER_NAME_SET", "INTRO_CUTSCENE_COMPLETE",
140
-
141
- # Phase 2: Tutorial & Starting Town
142
- "LITTLEROOT_TOWN", "PLAYER_HOUSE_ENTERED", "PLAYER_BEDROOM",
143
- "RIVAL_HOUSE", "RIVAL_BEDROOM",
144
-
145
- # Phase 3: Professor Birch & Starter
146
- "ROUTE_101", "STARTER_CHOSEN", "BIRCH_LAB_VISITED",
147
-
148
- # Phase 4: Rival
149
- "OLDALE_TOWN", "ROUTE_103", "RECEIVED_POKEDEX",
150
-
151
- # Phase 5: Route 102 & Petalburg
152
- "ROUTE_102", "PETALBURG_CITY", "DAD_FIRST_MEETING", "GYM_EXPLANATION",
153
-
154
- # Phase 6: Road to Rustboro City
155
- "ROUTE_104_SOUTH", "PETALBURG_WOODS", "TEAM_AQUA_GRUNT_DEFEATED",
156
- "ROUTE_104_NORTH", "RUSTBORO_CITY",
157
-
158
- # Phase 7: First Gym Challenge
159
- "RUSTBORO_GYM_ENTERED", "ROXANNE_DEFEATED", "FIRST_GYM_COMPLETE",
160
-
161
- # Badge milestones (tracked separately)
162
- "STONE_BADGE"
163
- ]
164
-
165
- try:
166
- # Special case for first milestone - split time is 0
167
- if milestone_id == "GAME_RUNNING":
168
- return 0.0
169
-
170
- if milestone_id not in milestone_order:
171
- # For unlisted milestones, find the most recent completion
172
- latest_timestamp = 0
173
- for _, data in self.milestones.items():
174
- if data.get('completed', False) and data.get('timestamp', 0) > latest_timestamp:
175
- latest_timestamp = data.get('timestamp', 0)
176
- return timestamp - latest_timestamp if latest_timestamp > 0 else 0.0
177
-
178
- # Find the previous milestone in the order
179
- current_index = milestone_order.index(milestone_id)
180
-
181
- # Look backwards for the most recent completed milestone
182
- for i in range(current_index - 1, -1, -1):
183
- prev_milestone = milestone_order[i]
184
- if self.is_completed(prev_milestone):
185
- prev_timestamp = self.milestones[prev_milestone].get('timestamp', 0)
186
- return timestamp - prev_timestamp
187
-
188
- # If no previous milestone found, calculate from start if we have GAME_RUNNING
189
- if self.is_completed("GAME_RUNNING"):
190
- start_timestamp = self.milestones["GAME_RUNNING"].get('timestamp', 0)
191
- return timestamp - start_timestamp
192
-
193
- # Fallback - no split time available
194
- return 0.0
195
-
196
- except Exception as e:
197
- logger.warning(f"Error calculating split time for {milestone_id}: {e}")
198
- return 0.0
199
-
200
- def _format_time(self, seconds: float) -> str:
201
- """Format time in HH:MM:SS format"""
202
- try:
203
- hours = int(seconds // 3600)
204
- minutes = int((seconds % 3600) // 60)
205
- secs = int(seconds % 60)
206
- return f"{hours:02d}:{minutes:02d}:{secs:02d}"
207
- except:
208
- return "00:00:00"
209
-
210
- def _calculate_total_time(self, timestamp: float) -> float:
211
- """Calculate total time from game start"""
212
- try:
213
- if self.is_completed("GAME_RUNNING"):
214
- start_timestamp = self.milestones["GAME_RUNNING"].get('timestamp', timestamp)
215
- return timestamp - start_timestamp
216
- return 0.0
217
- except:
218
- return 0.0
219
-
220
- def get_latest_milestone_info(self) -> tuple:
221
- """Get the latest milestone information for submission logging
222
- Returns: (milestone_name, split_time_formatted, total_time_formatted)
223
- """
224
- if self.latest_milestone:
225
- milestone_data = self.milestones.get(self.latest_milestone, {})
226
- split_formatted = milestone_data.get('split_formatted', '00:00:00')
227
- total_formatted = milestone_data.get('total_formatted', '00:00:00')
228
- return (self.latest_milestone, split_formatted, total_formatted)
229
- return ("NONE", "00:00:00", "00:00:00")
230
-
231
- def get_all_completed_milestones(self) -> list:
232
- """Get a list of all completed milestones with their times"""
233
- completed = []
234
- for milestone_id, data in self.milestones.items():
235
- if data.get('completed', False):
236
- completed.append({
237
- 'id': milestone_id,
238
- 'timestamp': data.get('timestamp', 0),
239
- 'split_time': data.get('split_formatted', '00:00:00'),
240
- 'total_time': data.get('total_formatted', '00:00:00')
241
- })
242
- return sorted(completed, key=lambda x: x['timestamp'])
243
-
244
- def reset_all(self):
245
- """Reset all milestones (for testing)"""
246
- self.milestones = {}
247
- self.save_to_file()
248
- logger.info("Reset all milestones")
249
-
250
- def load_milestones_for_state(self, state_filename: str = None):
251
- """Load milestones from file, optionally with a specific state filename"""
252
- if state_filename:
253
- # If a state filename is provided, try to load milestones from a corresponding file
254
- # Get the directory and base name of the state file
255
- state_dir = os.path.dirname(state_filename)
256
- base_name = os.path.splitext(os.path.basename(state_filename))[0]
257
- milestone_filename = os.path.join(state_dir, f"{base_name}_milestones.json")
258
-
259
- # Track that we loaded from a state-specific file
260
- self.loaded_state_milestones_file = milestone_filename
261
- logger.info(f"Loading milestones from state-specific file: {milestone_filename}")
262
-
263
- try:
264
- # Temporarily change filename to load from state file
265
- original_filename = self.filename
266
- self.filename = milestone_filename
267
- self.load_from_file()
268
- # Restore runtime cache filename (always in main directory)
269
- self.filename = original_filename
270
- logger.info(f"Loaded {len(self.milestones)} milestones from state {state_filename}")
271
- logger.info(f"Runtime milestone cache will be saved to: {self.filename}")
272
- except FileNotFoundError:
273
- logger.info(f"Milestone file not found: {milestone_filename}, starting fresh milestones for this state")
274
- # Start with empty milestones for this state
275
- self.milestones = {}
276
- # Don't create the state file, just use runtime cache
277
- logger.info(f"Runtime milestone cache will be saved to: {self.filename}")
278
- except Exception as e:
279
- logger.error(f"Error loading milestone file {milestone_filename}: {e}")
280
- # Fall back to default milestone file
281
- logger.info(f"Using runtime milestone cache: {self.filename}")
282
- self.load_from_file()
283
- else:
284
- # No state filename provided, use default milestone file in cache
285
- self.loaded_state_milestones_file = None
286
- self.filename = os.path.join(self.cache_dir, "milestones_progress.json")
287
- logger.info(f"Loading milestones from default file: {self.filename}")
288
- self.load_from_file()
289
-
290
- def save_milestones_for_state(self, state_filename: str = None):
291
- """Save milestones to file, optionally with a specific state filename"""
292
- if state_filename:
293
- # If a state filename is provided, save milestones to a corresponding file
294
- # Get the directory and base name of the state file
295
- state_dir = os.path.dirname(state_filename)
296
- base_name = os.path.splitext(os.path.basename(state_filename))[0]
297
- milestone_filename = os.path.join(state_dir, f"{base_name}_milestones.json")
298
-
299
- original_filename = self.filename
300
- self.filename = milestone_filename
301
- logger.info(f"Saving {len(self.milestones)} milestones to state-specific file: {milestone_filename}")
302
-
303
- try:
304
- self.save_to_file()
305
- logger.info(f"Successfully saved milestones to {milestone_filename}")
306
- except Exception as e:
307
- logger.error(f"Error saving milestone file {milestone_filename}: {e}")
308
- # Fall back to default milestone file
309
- self.filename = original_filename
310
- self.save_to_file()
311
- return original_filename
312
- finally:
313
- # Restore original filename
314
- self.filename = original_filename
315
-
316
- return milestone_filename
317
- else:
318
- # Save to default milestone file
319
- logger.info(f"Saving {len(self.milestones)} milestones to default file: {self.filename}")
320
- self.save_to_file()
321
- return self.filename
322
-
323
-
324
- class EmeraldEmulator:
325
- """emulator wrapper for Pokémon Emerald with headless frame capture and scripted inputs."""
326
-
327
- def __init__(self, rom_path: str, headless: bool = True, sound: bool = False):
328
- self.rom_path = rom_path
329
- self.headless = headless
330
- self.sound = sound
331
-
332
- self.gba = None
333
- self.core = None
334
- self.width = 240
335
- self.height = 160
336
- self.running = False
337
-
338
- self.frame_queue = queue.Queue(maxsize=10)
339
- self.current_frame = None
340
- self.frame_thread = None
341
-
342
- # Memory reader for accessing game state
343
- self.memory_reader = None
344
-
345
- # Memory cache for efficient reading
346
- self._mem_cache = {}
347
-
348
- # Setup cache directory
349
- self.cache_dir = ".pokeagent_cache"
350
- os.makedirs(self.cache_dir, exist_ok=True)
351
-
352
- # Milestone tracker for progress tracking (using cache file)
353
- self.milestone_tracker = MilestoneTracker(os.path.join(self.cache_dir, "milestones_progress.json"))
354
-
355
- # Dialog state tracking for FPS adjustment
356
- self._cached_dialog_state = False
357
- self._last_dialog_check_time = 0
358
- self._dialog_check_interval = 0.05 # Check dialog state every 50ms (more responsive)
359
-
360
- # Track currently loaded state file
361
- self._current_state_file = None
362
-
363
- # Define key mapping for mgba
364
- self.KEY_MAP = {
365
- "a": lib.GBA_KEY_A,
366
- "b": lib.GBA_KEY_B,
367
- "start": lib.GBA_KEY_START,
368
- "select": lib.GBA_KEY_SELECT,
369
- "up": lib.GBA_KEY_UP,
370
- "down": lib.GBA_KEY_DOWN,
371
- "left": lib.GBA_KEY_LEFT,
372
- "right": lib.GBA_KEY_RIGHT,
373
- "l": lib.GBA_KEY_L,
374
- "r": lib.GBA_KEY_R
375
- }
376
-
377
- def initialize(self):
378
- """Load ROM and set up emulator"""
379
- try:
380
- # Prevents relentless spamming to stdout by libmgba.
381
- mgba.log.silence()
382
-
383
- # Create a temporary directory and copy the gba file into it
384
- # this is necessary to prevent mgba from overwriting the save file (and to prevent crashes)
385
- tmp_dir = Path(tempfile.mkdtemp())
386
- tmp_gba = tmp_dir / "rom.gba"
387
- tmp_gba.write_bytes(Path(self.rom_path).read_bytes())
388
-
389
- # Load the core
390
- self.core = mgba.core.load_path(str(tmp_gba))
391
- if self.core is None:
392
- raise ValueError(f"Failed to load GBA file: {self.rom_path}")
393
-
394
- # Auto-load save if it exists
395
- self.core.autoload_save()
396
- self.core.reset()
397
-
398
- # Get dimensions from the core
399
- self.width, self.height = self.core.desired_video_dimensions()
400
- logger.info(f"mgba initialized with ROM: {self.rom_path} and dimensions: {self.width}x{self.height}")
401
-
402
- # Set up video buffer for frame capture using mgba.image.Image
403
- self.video_buffer = mgba.image.Image(self.width, self.height)
404
- self.core.set_video_buffer(self.video_buffer)
405
- self.core.reset() # Reset after setting video buffer
406
-
407
- # Initialize memory reader
408
- self.memory_reader = PokemonEmeraldReader(self.core)
409
-
410
- # Set up callback for memory reader to invalidate emulator cache on area transitions
411
- def invalidate_emulator_cache():
412
- if hasattr(self, '_cached_state'):
413
- delattr(self, '_cached_state')
414
- if hasattr(self, '_cached_state_time'):
415
- delattr(self, '_cached_state_time')
416
-
417
- self.memory_reader._emulator_cache_invalidator = invalidate_emulator_cache
418
-
419
- # Set up frame callback to invalidate memory cache
420
- self.core.add_frame_callback(self._invalidate_mem_cache)
421
-
422
- logger.info(f"mgba initialized with ROM: {self.rom_path}")
423
- except Exception as e:
424
- raise RuntimeError(f"Failed to initialize mgba: {e}")
425
-
426
- def _invalidate_mem_cache(self):
427
- """Invalidate memory cache when frame changes"""
428
- self._mem_cache = {}
429
-
430
- def _get_memory_region(self, region_id: int):
431
- """Get memory region for efficient reading"""
432
- if region_id not in self._mem_cache:
433
- mem_core = self.core.memory.u8._core
434
- size = ffi.new("size_t *")
435
- ptr = ffi.cast("uint8_t *", mem_core.getMemoryBlock(mem_core, region_id, size))
436
- self._mem_cache[region_id] = ffi.buffer(ptr, size[0])[:]
437
- return self._mem_cache[region_id]
438
-
439
- def read_memory(self, address: int, size: int = 1):
440
- """Read memory at given address"""
441
- region_id = address >> lib.BASE_OFFSET
442
- mem_region = self._get_memory_region(region_id)
443
- mask = len(mem_region) - 1
444
- address &= mask
445
- return mem_region[address:address + size]
446
-
447
- def read_u8(self, address: int):
448
- """Read unsigned 8-bit value"""
449
- return int.from_bytes(self.read_memory(address, 1), byteorder='little', signed=False)
450
-
451
- def read_u16(self, address: int):
452
- """Read unsigned 16-bit value"""
453
- return int.from_bytes(self.read_memory(address, 2), byteorder='little', signed=False)
454
-
455
- def read_u32(self, address: int):
456
- """Read unsigned 32-bit value"""
457
- return int.from_bytes(self.read_memory(address, 4), byteorder='little', signed=False)
458
-
459
- def tick(self, frames: int = 1):
460
- """Advance emulator by given number of frames"""
461
- if self.core:
462
- for _ in range(frames):
463
- self.core.run_frame()
464
-
465
- def get_current_fps(self, base_fps: int = 30) -> int:
466
- """Get current FPS - quadruples during dialog for faster text progression"""
467
- # Use cached dialog state for performance
468
- return base_fps * 4 if self._cached_dialog_state else base_fps
469
-
470
- def _update_dialog_state_cache(self):
471
- """Update cached dialog state (called periodically for performance)"""
472
- import time
473
- current_time = time.time()
474
-
475
- # Only check dialog state periodically to avoid performance issues
476
- if current_time - self._last_dialog_check_time >= self._dialog_check_interval:
477
- if self.memory_reader:
478
- new_dialog_state = self.memory_reader.is_in_dialog()
479
- if new_dialog_state != self._cached_dialog_state:
480
- self._cached_dialog_state = new_dialog_state
481
- if new_dialog_state:
482
- logger.debug("🎯 Dialog detected - switching to 4x FPS")
483
- else:
484
- logger.debug("✅ Dialog ended - reverting to normal FPS")
485
- self._last_dialog_check_time = current_time
486
-
487
- def press_key(self, key: str, frames: int = 2):
488
- """Press a key for specified number of frames"""
489
- if key not in self.KEY_MAP:
490
- raise ValueError(f"Invalid key: {key}")
491
- if frames < 2:
492
- raise ValueError("Cannot press a key for less than 2 frames.")
493
-
494
- key_code = self.KEY_MAP[key]
495
- self.core.add_keys(key_code)
496
- self.tick(frames - 1)
497
- self.core.clear_keys(key_code)
498
- self.tick(1)
499
-
500
- def press_buttons(self, buttons: List[str], hold_frames: int = 10, release_frames: int = 10):
501
- """Press a sequence of buttons"""
502
- if not self.core:
503
- return "Emulator not initialized"
504
-
505
- for button in buttons:
506
- if button.lower() not in self.KEY_MAP:
507
- logger.warning(f"Unknown button: {button}")
508
- continue
509
-
510
- self.press_key(button.lower(), hold_frames)
511
-
512
- self.tick(release_frames)
513
- return f"Pressed: {'+'.join(buttons)}"
514
-
515
- def run_frame_with_buttons(self, buttons: List[str]):
516
- """Set buttons and advance one frame."""
517
- if not self.core:
518
- return
519
-
520
- # Set all buttons for one frame
521
- for button in buttons:
522
- if button.lower() in self.KEY_MAP:
523
- key_code = self.KEY_MAP[button.lower()]
524
- self.core.add_keys(key_code)
525
-
526
- self.core.run_frame()
527
-
528
- # Clear all buttons
529
- for button in buttons:
530
- if button.lower() in self.KEY_MAP:
531
- key_code = self.KEY_MAP[button.lower()]
532
- self.core.clear_keys(key_code)
533
-
534
- # Update dialog state cache for FPS adjustment
535
- self._update_dialog_state_cache()
536
-
537
- # Clear dialogue cache if A button was pressed (dismisses dialogue)
538
- if buttons and any(button.lower() == 'a' for button in buttons):
539
- if self.memory_reader:
540
- self.memory_reader.clear_dialogue_cache_on_button_press()
541
-
542
- # Clear state cache after action to ensure fresh data
543
- if hasattr(self, '_cached_state'):
544
- delattr(self, '_cached_state')
545
- if hasattr(self, '_cached_state_time'):
546
- delattr(self, '_cached_state_time')
547
-
548
- def get_screenshot(self) -> Optional[Image.Image]:
549
- """Return the current frame as a PIL image"""
550
- if not self.core or not self.video_buffer:
551
- return None
552
-
553
- try:
554
- # Use the built-in to_pil() method from mgba.image.Image
555
- if hasattr(self.video_buffer, 'to_pil'):
556
- screenshot = self.video_buffer.to_pil()
557
- if screenshot:
558
- screenshot = screenshot.convert("RGB")
559
- return screenshot
560
- else:
561
- logger.warning("mgba.image.Image does not have to_pil method")
562
- return None
563
- else:
564
- logger.warning("mgba.image.Image does not have to_pil method")
565
- return None
566
- except Exception as e:
567
- logger.error(f"Failed to get screenshot: {e}")
568
- return None
569
-
570
- def save_state(self, path: Optional[str] = None) -> Optional[bytes]:
571
- """Save current emulator state to file or return as bytes"""
572
- if not self.core:
573
- return None
574
-
575
- try:
576
- # Get the raw state data
577
- raw_data = self.core.save_raw_state()
578
-
579
- # Convert CFFI object to bytes if needed
580
- if hasattr(raw_data, 'buffer'):
581
- data = bytes(raw_data.buffer)
582
- elif hasattr(raw_data, '__len__'):
583
- data = bytes(raw_data)
584
- else:
585
- data = raw_data
586
-
587
- if path:
588
- with open(path, 'wb') as f:
589
- f.write(data)
590
- logger.info(f"State saved to {path}")
591
-
592
- # Save corresponding milestones for this state
593
- milestone_filename = self.milestone_tracker.save_milestones_for_state(path)
594
- logger.info(f"Milestones saved to {milestone_filename}")
595
-
596
- # Save the persistent location grids (contains all map data)
597
- self._save_persistent_grids_for_state(path)
598
-
599
- return data
600
- except Exception as e:
601
- logger.error(f"Failed to save state: {e}")
602
- return None
603
-
604
- def load_state(self, path: Optional[str] = None, state_bytes: Optional[bytes] = None):
605
- """Load emulator state from file or memory"""
606
- if not self.core:
607
- return
608
-
609
- try:
610
- if path:
611
- with open(path, 'rb') as f:
612
- state_bytes = f.read()
613
- if state_bytes:
614
- # Ensure state_bytes is actually bytes
615
- if not isinstance(state_bytes, bytes):
616
- state_bytes = bytes(state_bytes)
617
- self.core.load_raw_state(state_bytes)
618
- logger.info("State loaded.")
619
-
620
- # Reset dialog tracking and invalidate map cache when loading new state
621
- if self.memory_reader:
622
- self.memory_reader.reset_dialog_tracking()
623
- # Don't clear buffer address on state load to avoid expensive rescans
624
- self.memory_reader.invalidate_map_cache(clear_buffer_address=False)
625
-
626
- # Persistent location maps will be loaded from the state file later
627
-
628
- # Run a frame to ensure memory is properly loaded
629
- self.core.run_frame()
630
-
631
- # Only find map buffer addresses if we don't have them cached
632
- # This avoids expensive memory scanning on every state load
633
- if not self.memory_reader._map_buffer_addr:
634
- if not self.memory_reader._find_map_buffer_addresses():
635
- logger.warning("Could not find map buffer addresses after state load")
636
- else:
637
- logger.info(f"Map buffer found at 0x{self.memory_reader._map_buffer_addr:08X}")
638
- else:
639
- logger.debug(f"Using cached map buffer at 0x{self.memory_reader._map_buffer_addr:08X}")
640
-
641
- # Set the current state file for both emulator and memory reader
642
- self._current_state_file = path
643
- if self.memory_reader:
644
- self.memory_reader._current_state_file = path
645
-
646
- # Load corresponding milestones for this state
647
- if path:
648
- # print( Loading state from path: {path}")
649
- # Copy state files to cache first
650
- self._copy_state_files_to_cache(path)
651
- # Load milestones from cache file
652
- cache_milestones_file = os.path.join(self.cache_dir, "milestones_progress.json")
653
- if os.path.exists(cache_milestones_file):
654
- # Update filename and then load
655
- self.milestone_tracker.filename = cache_milestones_file
656
- self.milestone_tracker.load_from_file()
657
- logger.info(f"Milestones loaded from cache file: {cache_milestones_file}")
658
- else:
659
- # Fallback to state-specific file
660
- self.milestone_tracker.load_milestones_for_state(path)
661
- logger.info(f"Milestones loaded for state {path}")
662
-
663
- # Load the persistent location grids (contains all map data)
664
- # print( About to call _load_persistent_grids_for_state")
665
- self._load_persistent_grids_for_state(path)
666
- # print( Completed _load_persistent_grids_for_state")
667
- except Exception as e:
668
- logger.error(f"Failed to load state: {e}")
669
-
670
- def _save_persistent_grids_for_state(self, state_filename: str):
671
- """Save persistent location grids for a specific state file"""
672
- try:
673
- # Get the directory and base name of the state file
674
- state_dir = os.path.dirname(state_filename)
675
- base_name = os.path.splitext(os.path.basename(state_filename))[0]
676
-
677
- # Only save grids for non-manual saves (splits, checkpoints, etc.)
678
- # For manual saves, we only need the map_stitcher.json
679
- if not base_name.startswith("manual_save"):
680
- grids_filename = os.path.join(state_dir, f"{base_name}_grids.json")
681
- # Save the persistent grids
682
- save_persistent_world_map(grids_filename)
683
- logger.info(f"Persistent grids saved to {grids_filename}")
684
-
685
- # Always update and save MapStitcher data
686
- if hasattr(self, 'memory_reader') and self.memory_reader:
687
- # For manual saves, copy the current map_stitcher.json
688
- if base_name.startswith("manual_save"):
689
- # Copy the current map_stitcher_data.json from cache to manual_save_map_stitcher.json
690
- cache_dir = ".pokeagent_cache"
691
- current_stitcher_file = os.path.join(cache_dir, "map_stitcher_data.json")
692
-
693
- # Also check for the old location in case it exists
694
- if not os.path.exists(current_stitcher_file) and os.path.exists("map_stitcher_data.json"):
695
- current_stitcher_file = "map_stitcher_data.json"
696
-
697
- target_stitcher_file = os.path.join(state_dir, f"{base_name}_map_stitcher.json")
698
-
699
- if os.path.exists(current_stitcher_file):
700
- shutil.copy2(current_stitcher_file, target_stitcher_file)
701
- logger.info(f"Map stitcher data copied to {target_stitcher_file}")
702
-
703
- # Also save current milestones
704
- if hasattr(self, 'milestone_tracker'):
705
- milestone_filename = self.milestone_tracker.save_milestones_for_state(state_filename)
706
- logger.info(f"Milestones saved to {milestone_filename}")
707
- else:
708
- # For regular saves, update the map stitcher save file path
709
- self.memory_reader.update_map_stitcher_save_file(state_filename)
710
- # Force save the map stitcher data
711
- if self.memory_reader._map_stitcher:
712
- self.memory_reader._map_stitcher.save_to_file()
713
-
714
- except Exception as e:
715
- logger.error(f"Error saving persistent grids for state: {e}")
716
-
717
- def _load_persistent_grids_for_state(self, state_filename: str):
718
- """Load persistent location grids for a specific state file"""
719
- try:
720
- # print( _load_persistent_grids_for_state called with: {state_filename}")
721
- # Get the directory and base name of the state file
722
- state_dir = os.path.dirname(state_filename)
723
- base_name = os.path.splitext(os.path.basename(state_filename))[0]
724
- grids_filename = os.path.join(state_dir, f"{base_name}_grids.json")
725
-
726
- # Load persistent grids if they exist
727
- if os.path.exists(grids_filename):
728
- # Load the persistent grids
729
- load_persistent_world_map(grids_filename)
730
- logger.info(f"Persistent grids loaded from {grids_filename}")
731
- else:
732
- logger.info(f"No persistent grids file found for state: {grids_filename}")
733
-
734
- # # Initialize MapStitcher with cache file
735
- # if hasattr(self, 'memory_reader') and self.memory_reader:
736
- # # print( About to initialize MapStitcher for state: {state_filename}")
737
- # # Use cache file instead of state-specific file
738
- # self.memory_reader.update_map_stitcher_save_file(state_filename, is_cache_file=True)
739
- # # print( MapStitcher initialization completed for state: {state_filename}")
740
- # else:
741
- # # print( No memory_reader available, cannot initialize MapStitcher")
742
-
743
- except Exception as e:
744
- logger.error(f"Error loading persistent grids for state: {e}")
745
-
746
- def _copy_state_files_to_cache(self, state_filename: str):
747
- """Copy state-specific map stitcher and milestones to cache for working storage"""
748
- import os
749
- import shutil
750
-
751
- # Ensure cache directory exists
752
- cache_dir = ".pokeagent_cache"
753
- os.makedirs(cache_dir, exist_ok=True)
754
-
755
- # Copy map stitcher file to cache
756
- state_dir = os.path.dirname(state_filename)
757
- base_name = os.path.splitext(os.path.basename(state_filename))[0]
758
- state_map_stitcher_file = os.path.join(state_dir, f"{base_name}_map_stitcher.json")
759
- cache_map_stitcher_file = os.path.join(cache_dir, "map_stitcher_data.json")
760
-
761
- if os.path.exists(state_map_stitcher_file):
762
- # Check if the file has content
763
- if os.path.getsize(state_map_stitcher_file) > 0:
764
- shutil.copy2(state_map_stitcher_file, cache_map_stitcher_file)
765
- # print( Copied map stitcher from {state_map_stitcher_file} to {cache_map_stitcher_file}")
766
- else:
767
- # Create a valid empty JSON structure for fresh start
768
- import json
769
- empty_data = {"map_areas": {}, "location_connections": {}}
770
- with open(cache_map_stitcher_file, 'w') as f:
771
- json.dump(empty_data, f, indent=2)
772
- # print( State file empty, created fresh map stitcher cache")
773
- else:
774
- # Create a valid empty JSON structure for fresh start
775
- import json
776
- empty_data = {"map_areas": {}, "location_connections": {}}
777
- with open(cache_map_stitcher_file, 'w') as f:
778
- json.dump(empty_data, f, indent=2)
779
- # print( No state file found, created fresh map stitcher cache")
780
-
781
- # Copy milestones file to main directory (not cache, as requested)
782
- state_milestones_file = os.path.join(state_dir, f"{base_name}_milestones.json")
783
- cache_milestones_file = os.path.join(self.cache_dir, "milestones_progress.json") # Cache directory as requested
784
-
785
- if os.path.exists(state_milestones_file):
786
- shutil.copy2(state_milestones_file, cache_milestones_file)
787
- # # print( Copied milestones from {state_milestones_file} to {cache_milestones_file}")
788
- # else:
789
- # # print( No state-specific milestones file found: {state_milestones_file}")
790
-
791
- def start_frame_capture(self, fps: int = 30):
792
- """Start asynchronous frame capture"""
793
- self.running = True
794
- self.frame_thread = threading.Thread(target=self._frame_loop, args=(fps,), daemon=True)
795
- self.frame_thread.start()
796
-
797
- def _frame_loop(self, fps: int):
798
- interval = 1.0 / fps
799
- while self.running:
800
- start = time.time()
801
- frame = self.get_screenshot()
802
- if frame:
803
- np_frame = np.array(frame)
804
- if self.frame_queue.full():
805
- self.frame_queue.get_nowait()
806
- self.frame_queue.put(np_frame)
807
- self.current_frame = np_frame
808
- elapsed = time.time() - start
809
- time.sleep(max(0.001, interval - elapsed))
810
-
811
- def get_latest_frame(self) -> Optional[np.ndarray]:
812
- """Return last captured frame"""
813
- return self.current_frame.copy() if self.current_frame is not None else None
814
-
815
- def process_input(self, input_data: Dict[str, Any]) -> str:
816
- """Handle JSON-style input payload"""
817
- try:
818
- input_type = input_data.get('type', 'button')
819
- if input_type == 'button':
820
- button = input_data.get('button')
821
- if button:
822
- return self.press_buttons([button])
823
- elif input_type == 'sequence':
824
- buttons = input_data.get('buttons', [])
825
- return self.press_buttons(buttons)
826
- elif input_type == 'hold':
827
- button = input_data.get('button')
828
- duration = int(input_data.get('duration', 1.0) * 60)
829
- return self.press_buttons([button], hold_frames=duration)
830
- return "Invalid input type"
831
- except Exception as e:
832
- logger.error(f"Input error: {e}")
833
- return str(e)
834
-
835
- def stop(self):
836
- """Stop emulator and cleanup"""
837
- self.running = False
838
- if self.frame_thread and self.frame_thread.is_alive():
839
- self.frame_thread.join(timeout=1)
840
- if self.core:
841
- self.core = None
842
- logger.info("Emulator stopped.")
843
-
844
- def get_info(self) -> Dict[str, Any]:
845
- """Return metadata about emulator state"""
846
- return {
847
- "rom_path": self.rom_path,
848
- "dimensions": (self.width, self.height),
849
- "initialized": self.core is not None,
850
- "headless": self.headless,
851
- "sound": self.sound,
852
- }
853
-
854
- def get_comprehensive_state(self, screenshot=None) -> Dict[str, Any]:
855
- """Get comprehensive game state including visual and memory data using enhanced memory reader
856
-
857
- Args:
858
- screenshot: Optional PIL Image screenshot to use. If None, will call get_screenshot()
859
- """
860
- # Simple caching to avoid redundant calls within a short time window
861
- import time
862
- current_time = time.time()
863
-
864
- # Cache state for 100ms to avoid excessive memory reads
865
- if hasattr(self, '_cached_state') and hasattr(self, '_cached_state_time'):
866
- if current_time - self._cached_state_time < 0.1: # 100ms cache
867
- return self._cached_state
868
-
869
- # Use provided screenshot or get a new one
870
- if screenshot is None:
871
- screenshot = self.get_screenshot()
872
-
873
- # Use the enhanced memory reader's comprehensive state method
874
- if self.memory_reader:
875
- state = self.memory_reader.get_comprehensive_state(screenshot)
876
- else:
877
- # Fallback to basic state
878
- state = {
879
- "visual": {
880
- "screenshot": None,
881
- "resolution": [self.width, self.height]
882
- },
883
- "player": {
884
- "position": None,
885
- "location": None,
886
- "name": None
887
- },
888
- "game": {
889
- "money": None,
890
- "party": None,
891
- "game_state": None,
892
- "is_in_battle": None,
893
- "time": None,
894
- "badges": None,
895
- "items": None,
896
- "item_count": None,
897
- "pokedex_caught": None,
898
- "pokedex_seen": None
899
- },
900
- "map": {
901
- "tiles": None,
902
- "tile_names": None,
903
- "metatile_behaviors": None,
904
- "metatile_info": None,
905
- "traversability": None
906
- }
907
- }
908
-
909
- # Use screenshot already captured
910
- if screenshot is not None and hasattr(screenshot, 'save'):
911
- state["visual"]["screenshot"] = screenshot
912
-
913
- # Cache the result
914
- self._cached_state = state
915
- self._cached_state_time = current_time
916
-
917
- return state
918
-
919
- def _get_tile_passability(self, tile_data) -> bool:
920
- """Determine if a tile is passable based on collision bits (like GeminiPlaysPokemonLive)"""
921
- if not tile_data or len(tile_data) < 3:
922
- return True # Default to passable if no data
923
-
924
- # tile_data is (metatile_id, behavior, collision, elevation)
925
- collision = tile_data[2] if len(tile_data) > 2 else 0
926
-
927
- # Primary rule: collision == 0 means passable, non-zero means blocked
928
- return collision == 0
929
-
930
- def _get_tile_encounter_possible(self, tile_data) -> bool:
931
- """Determine if a tile can trigger encounters based on its behavior"""
932
- if not tile_data or len(tile_data) < 2:
933
- return False
934
-
935
- # Import here to avoid circular imports
936
- from .enums import MetatileBehavior
937
-
938
- behavior = tile_data[1] if len(tile_data) > 1 else None
939
- if not behavior:
940
- return False
941
-
942
- # Check for encounter tiles
943
- encounter_behaviors = {
944
- MetatileBehavior.TALL_GRASS,
945
- MetatileBehavior.LONG_GRASS,
946
- MetatileBehavior.UNUSED_05,
947
- MetatileBehavior.DEEP_SAND,
948
- MetatileBehavior.CAVE,
949
- MetatileBehavior.INDOOR_ENCOUNTER,
950
- MetatileBehavior.POND_WATER,
951
- MetatileBehavior.INTERIOR_DEEP_WATER,
952
- MetatileBehavior.DEEP_WATER,
953
- MetatileBehavior.OCEAN_WATER,
954
- MetatileBehavior.SEAWEED,
955
- MetatileBehavior.ASHGRASS,
956
- MetatileBehavior.FOOTPRINTS,
957
- MetatileBehavior.SEAWEED_NO_SURFACING
958
- }
959
-
960
- return behavior in encounter_behaviors
961
-
962
- def _get_tile_surfable(self, tile_data) -> bool:
963
- """Determine if a tile can be surfed on based on its behavior"""
964
- if not tile_data or len(tile_data) < 2:
965
- return False
966
-
967
- # Import here to avoid circular imports
968
- from .enums import MetatileBehavior
969
-
970
- behavior = tile_data[1] if len(tile_data) > 1 else None
971
- if not behavior:
972
- return False
973
-
974
- # Check for surfable tiles
975
- surfable_behaviors = {
976
- MetatileBehavior.POND_WATER,
977
- MetatileBehavior.INTERIOR_DEEP_WATER,
978
- MetatileBehavior.DEEP_WATER,
979
- MetatileBehavior.SOOTOPOLIS_DEEP_WATER,
980
- MetatileBehavior.OCEAN_WATER,
981
- MetatileBehavior.NO_SURFACING,
982
- MetatileBehavior.SEAWEED,
983
- MetatileBehavior.SEAWEED_NO_SURFACING
984
- }
985
-
986
- return behavior in surfable_behaviors
987
-
988
- def get_player_position(self) -> Optional[Dict[str, int]]:
989
- """Get current player position"""
990
- if self.memory_reader:
991
- try:
992
- coords = self.memory_reader.read_coordinates()
993
- if coords:
994
- return {"x": coords[0], "y": coords[1]}
995
- except Exception as e:
996
- logger.warning(f"Failed to read player position: {e}")
997
- return None
998
-
999
- def get_map_location(self) -> Optional[str]:
1000
- """Get current map location name"""
1001
- if self.memory_reader:
1002
- try:
1003
- return self.memory_reader.read_location()
1004
- except Exception as e:
1005
- logger.warning(f"Failed to read map location: {e}")
1006
- return None
1007
-
1008
- def get_money(self) -> Optional[int]:
1009
- """Get current money amount"""
1010
- if self.memory_reader:
1011
- try:
1012
- return self.memory_reader.read_money()
1013
- except Exception as e:
1014
- logger.warning(f"Failed to read money: {e}")
1015
- return None
1016
-
1017
- def get_party_pokemon(self) -> Optional[List[Dict[str, Any]]]:
1018
- """Get current party Pokemon"""
1019
- if self.memory_reader:
1020
- try:
1021
- party = self.memory_reader.read_party_pokemon()
1022
- if party:
1023
- return [
1024
- {
1025
- "species": pokemon.species_name,
1026
- "level": pokemon.level,
1027
- "current_hp": pokemon.current_hp,
1028
- "max_hp": pokemon.max_hp,
1029
- "status": pokemon.status.get_status_name() if pokemon.status else "OK",
1030
- "types": [t for t in [pokemon.type1.name if pokemon.type1 else None,
1031
- pokemon.type2.name if pokemon.type2 else None] if t is not None]
1032
- }
1033
- for pokemon in party
1034
- ]
1035
- except Exception as e:
1036
- logger.warning(f"Failed to read party Pokemon: {e}")
1037
- return None
1038
-
1039
- def get_map_tiles(self, radius: int = 7) -> Optional[List[List[tuple]]]:
1040
- """Get map tiles around player"""
1041
- if self.memory_reader:
1042
- try:
1043
- return self.memory_reader.read_map_around_player(radius=radius)
1044
- except Exception as e:
1045
- logger.warning(f"Failed to read map tiles: {e}")
1046
- return None
1047
-
1048
- def test_memory_reading(self) -> Dict[str, Any]:
1049
- """Test memory reading capabilities and return diagnostic information"""
1050
- if not self.memory_reader:
1051
- return {"error": "Memory reader not initialized"}
1052
-
1053
- try:
1054
- # Get memory diagnostics
1055
- diagnostics = self.memory_reader.test_memory_access()
1056
-
1057
- # Test some basic reads
1058
- test_results = {
1059
- "player_name": None,
1060
- "money": None,
1061
- "coordinates": None,
1062
- "party_size": None,
1063
- "location": None
1064
- }
1065
-
1066
- try:
1067
- test_results["player_name"] = self.memory_reader.read_player_name()
1068
- except Exception as e:
1069
- test_results["player_name_error"] = str(e)
1070
-
1071
- try:
1072
- test_results["money"] = self.memory_reader.read_money()
1073
- except Exception as e:
1074
- test_results["money_error"] = str(e)
1075
-
1076
- try:
1077
- test_results["coordinates"] = self.memory_reader.read_coordinates()
1078
- except Exception as e:
1079
- test_results["coordinates_error"] = str(e)
1080
-
1081
- try:
1082
- test_results["party_size"] = self.memory_reader.read_party_size()
1083
- except Exception as e:
1084
- test_results["party_size_error"] = str(e)
1085
-
1086
- try:
1087
- test_results["location"] = self.memory_reader.read_location()
1088
- except Exception as e:
1089
- test_results["location_error"] = str(e)
1090
-
1091
- return {
1092
- "diagnostics": diagnostics,
1093
- "test_results": test_results
1094
- }
1095
- except Exception as e:
1096
- return {"error": f"Failed to run memory tests: {e}"}
1097
-
1098
- def check_and_update_milestones(self, game_state: Dict[str, Any]):
1099
- """Check current game state and update milestones"""
1100
- try:
1101
- # Debug: Show current state
1102
- location = game_state.get("player", {}).get("location", "Unknown")
1103
- # print(f"🔍 Checking milestones for location: {location}")
1104
- # Only check milestones that aren't already completed
1105
- milestones_to_check = [
1106
- # Phase 1: Game Initialization
1107
- "GAME_RUNNING", "PLAYER_NAME_SET", "INTRO_CUTSCENE_COMPLETE",
1108
-
1109
- # Phase 2: Tutorial & Starting Town
1110
- "LITTLEROOT_TOWN", "PLAYER_HOUSE_ENTERED", "PLAYER_BEDROOM",
1111
- "RIVAL_HOUSE", "RIVAL_BEDROOM",
1112
-
1113
- # Phase 3: Professor Birch & Starter
1114
- "ROUTE_101", "STARTER_CHOSEN", "BIRCH_LAB_VISITED",
1115
-
1116
- # Phase 4: Rival
1117
- "OLDALE_TOWN", "ROUTE_103", "RECEIVED_POKEDEX",
1118
-
1119
- # Phase 5: Route 102 & Petalburg
1120
- "ROUTE_102", "PETALBURG_CITY", "DAD_FIRST_MEETING", "GYM_EXPLANATION",
1121
-
1122
- # Phase 6: Road to Rustboro City
1123
- "ROUTE_104_SOUTH", "PETALBURG_WOODS", "TEAM_AQUA_GRUNT_DEFEATED",
1124
- "ROUTE_104_NORTH", "RUSTBORO_CITY",
1125
-
1126
- # Phase 7: First Gym Challenge
1127
- "RUSTBORO_GYM_ENTERED", "ROXANNE_DEFEATED", "FIRST_GYM_COMPLETE"
1128
- ]
1129
-
1130
- for milestone_id in milestones_to_check:
1131
- if not self.milestone_tracker.is_completed(milestone_id):
1132
- if self._check_milestone_condition(milestone_id, game_state):
1133
- print(f"🎯 Milestone detected: {milestone_id}")
1134
- self.milestone_tracker.mark_completed(milestone_id)
1135
- except Exception as e:
1136
- logger.warning(f"Error checking milestones: {e}")
1137
-
1138
- def _check_milestone_condition(self, milestone_id: str, game_state: Dict[str, Any]) -> bool:
1139
- """Check if a specific milestone condition is met based on current game state"""
1140
- try:
1141
- # Test milestones (should always work)
1142
- if milestone_id == "GAME_RUNNING":
1143
- return True # If we can execute this, game is running
1144
- elif milestone_id == "HAS_PARTY":
1145
- if game_state:
1146
- party = game_state.get("player", {}).get("party", [])
1147
- return len(party) > 0
1148
- return False
1149
-
1150
- # Location-based milestones - check current location
1151
- elif milestone_id == "LITTLEROOT_TOWN":
1152
- if game_state:
1153
- location = game_state.get("player", {}).get("location", "")
1154
- return "LITTLEROOT" in str(location).upper()
1155
- return False
1156
-
1157
- elif milestone_id == "OLDALE_TOWN":
1158
- if game_state:
1159
- # Only count Oldale Town if we've already been to Littleroot Town
1160
- if not self.milestone_tracker.is_completed("LITTLEROOT_TOWN"):
1161
- return False
1162
- location = game_state.get("player", {}).get("location", "")
1163
- return "OLDALE" in str(location).upper()
1164
- return False
1165
- elif milestone_id == "RUSTBORO_CITY":
1166
- if game_state:
1167
- # Only count Rustboro City if we've already been to Petalburg City
1168
- if not self.milestone_tracker.is_completed("PETALBURG_CITY"):
1169
- return False
1170
- location = game_state.get("player", {}).get("location", "")
1171
- return "RUSTBORO" in str(location).upper()
1172
- return False
1173
- elif milestone_id == "DEWFORD_TOWN":
1174
- if game_state:
1175
- # Only count Dewford Town if we've already been to Rustboro City
1176
- if not self.milestone_tracker.is_completed("RUSTBORO_CITY"):
1177
- return False
1178
- location = game_state.get("player", {}).get("location", "")
1179
- return "DEWFORD" in str(location).upper()
1180
- return False
1181
- elif milestone_id == "SLATEPORT_CITY":
1182
- if game_state:
1183
- # Only count Slateport City if we've already been to Dewford Town
1184
- if not self.milestone_tracker.is_completed("DEWFORD_TOWN"):
1185
- return False
1186
- location = game_state.get("player", {}).get("location", "")
1187
- return "SLATEPORT" in str(location).upper()
1188
- return False
1189
- elif milestone_id == "MAUVILLE_CITY":
1190
- if game_state:
1191
- # Only count Mauville City if we've already been to Slateport City
1192
- if not self.milestone_tracker.is_completed("SLATEPORT_CITY"):
1193
- return False
1194
- location = game_state.get("player", {}).get("location", "")
1195
- return "MAUVILLE" in str(location).upper()
1196
- return False
1197
-
1198
-
1199
-
1200
- # Badge milestones - check badge count/list
1201
- elif milestone_id == "STONE_BADGE":
1202
- if game_state:
1203
- badges = game_state.get("game", {}).get("badges", [])
1204
- if isinstance(badges, list):
1205
- return len(badges) >= 1 or any("Stone" in str(b) for b in badges)
1206
- elif isinstance(badges, int):
1207
- return badges >= 1
1208
- return False
1209
- elif milestone_id == "KNUCKLE_BADGE":
1210
- if game_state:
1211
- badges = game_state.get("game", {}).get("badges", [])
1212
- if isinstance(badges, list):
1213
- return len(badges) >= 2 or any("Knuckle" in str(b) for b in badges)
1214
- elif isinstance(badges, int):
1215
- return badges >= 2
1216
- return False
1217
- elif milestone_id == "DYNAMO_BADGE":
1218
- if game_state:
1219
- badges = game_state.get("game", {}).get("badges", [])
1220
- if isinstance(badges, list):
1221
- return len(badges) >= 3 or any("Dynamo" in str(b) for b in badges)
1222
- elif isinstance(badges, int):
1223
- return badges >= 3
1224
- return False
1225
-
1226
- # Phase 1: Game Initialization milestones
1227
- elif milestone_id == "INTRO_CUTSCENE_COMPLETE":
1228
- if game_state:
1229
- location = game_state.get("player", {}).get("location", "")
1230
- return "MOVING_VAN" in str(location).upper()
1231
- return False
1232
- elif milestone_id == "PLAYER_NAME_SET":
1233
- if game_state:
1234
- player_name = game_state.get("player", {}).get("name", "")
1235
- # Player name is set if we have a non-empty name that's not the default
1236
- return (player_name and
1237
- str(player_name).strip() != "" and
1238
- str(player_name).strip() not in ["", "UNKNOWN", "PLAYER"])
1239
- return False
1240
-
1241
- # Phase 2: Tutorial & Starting Town milestones
1242
- elif milestone_id == "PLAYER_HOUSE_ENTERED":
1243
- if game_state:
1244
- location = game_state.get("player", {}).get("location", "")
1245
- return "LITTLEROOT TOWN BRENDANS HOUSE 1F" in str(location).upper()
1246
- return False
1247
- elif milestone_id == "PLAYER_BEDROOM":
1248
- if game_state:
1249
- location = game_state.get("player", {}).get("location", "")
1250
- return "LITTLEROOT TOWN BRENDANS HOUSE 2F" in str(location).upper()
1251
- return False
1252
- elif milestone_id == "RIVAL_HOUSE":
1253
- if game_state:
1254
- location = game_state.get("player", {}).get("location", "")
1255
- return "LITTLEROOT TOWN MAYS HOUSE 1F" in str(location).upper()
1256
- return False
1257
- elif milestone_id == "RIVAL_BEDROOM":
1258
- if game_state:
1259
- location = game_state.get("player", {}).get("location", "")
1260
- return "LITTLEROOT TOWN MAYS HOUSE 2F" in str(location).upper()
1261
- return False
1262
-
1263
- # Phase 3: Professor Birch & Starter milestones
1264
- elif milestone_id == "ROUTE_101":
1265
- if game_state:
1266
- location = game_state.get("player", {}).get("location", "")
1267
- return "ROUTE_101" in str(location).upper() or "ROUTE 101" in str(location).upper()
1268
- return False
1269
- elif milestone_id == "STARTER_CHOSEN":
1270
- if game_state:
1271
- party = game_state.get("player", {}).get("party", [])
1272
- return len(party) >= 1 and any(p.get("species_name", "").strip() for p in party)
1273
- return False
1274
- elif milestone_id == "BIRCH_LAB_VISITED":
1275
- if game_state:
1276
- location = game_state.get("player", {}).get("location", "")
1277
- return "LITTLEROOT TOWN PROFESSOR BIRCHS LAB" in str(location).upper()
1278
- return False
1279
-
1280
- # Phase 4: Early Route Progression milestones
1281
- elif milestone_id == "ROUTE_103":
1282
- if game_state:
1283
- # Only count Route 103 if we've already been to Route 101 and have starter
1284
- if not self.milestone_tracker.is_completed("ROUTE_101"):
1285
- return False
1286
- if not self.milestone_tracker.is_completed("STARTER_CHOSEN"):
1287
- return False
1288
- location = game_state.get("player", {}).get("location", "")
1289
- return "ROUTE_103" in str(location).upper() or "ROUTE 103" in str(location).upper()
1290
- return False
1291
- # elif milestone_id == "RIVAL_BATTLE_1":
1292
- # # Check for specific state hash from dialog after the battle (c9086d56)
1293
- # if game_state:
1294
- # # Create state hash for comparison
1295
- # state_str = str(game_state)
1296
- # state_hash = hashlib.md5(state_str.encode()).hexdigest()[:8]
1297
-
1298
- # # Check for battle completion state hash or traditional conditions
1299
- # return (state_hash == "c9086d56" or
1300
- # (self.milestone_tracker.is_completed("ROUTE_103") and
1301
- # self.milestone_tracker.is_completed("STARTER_CHOSEN")))
1302
- # return False
1303
- elif milestone_id == "RECEIVED_POKEDEX":
1304
- if game_state:
1305
- # Check if we're in Birch's lab AND have completed Route 103
1306
- location = game_state.get("player", {}).get("location", "")
1307
- return (self.milestone_tracker.is_completed("ROUTE_103") and
1308
- "LITTLEROOT TOWN PROFESSOR BIRCHS LAB" in str(location).upper())
1309
- return False
1310
- ## Phase 5: Route 102 & Petalburg
1311
- elif milestone_id == "ROUTE_102":
1312
- if game_state:
1313
- # Only count Route 102 if we've received Pokedex
1314
- if not self.milestone_tracker.is_completed("RECEIVED_POKEDEX"):
1315
- return False
1316
- location = game_state.get("player", {}).get("location", "")
1317
- return "ROUTE_102" in str(location).upper() or "ROUTE 102" in str(location).upper()
1318
- return False
1319
- elif milestone_id == "PETALBURG_CITY":
1320
- if game_state:
1321
- # Enforce proper game progression through required towns
1322
- if not self.milestone_tracker.is_completed("LITTLEROOT_TOWN"):
1323
- return False
1324
- if not self.milestone_tracker.is_completed("OLDALE_TOWN"):
1325
- return False
1326
- location = game_state.get("player", {}).get("location", "")
1327
- return "PETALBURG" in str(location).upper()
1328
- return False
1329
- elif milestone_id == "DAD_FIRST_MEETING":
1330
- # Meeting Dad happens in Petalburg Gym
1331
- if game_state:
1332
- # Must have visited Petalburg City first
1333
- if not self.milestone_tracker.is_completed("PETALBURG_CITY"):
1334
- return False
1335
- location = game_state.get("player", {}).get("location", "")
1336
- return "PETALBURG CITY GYM" in str(location).upper() or "PETALBURG_CITY_GYM" in str(location).upper()
1337
- return False
1338
- elif milestone_id == "GYM_EXPLANATION":
1339
- # Gym explanation happens after meeting Dad in the gym
1340
- if game_state:
1341
- # Must have met Dad and still be in gym
1342
- if not self.milestone_tracker.is_completed("DAD_FIRST_MEETING"):
1343
- return False
1344
- location = game_state.get("player", {}).get("location", "")
1345
- return "PETALBURG CITY GYM" in str(location).upper() or "PETALBURG_CITY_GYM" in str(location).upper()
1346
- return False
1347
-
1348
- # Phase 6: Pre-Gym Preparation milestones
1349
- elif milestone_id == "ROUTE_104_SOUTH":
1350
- if game_state:
1351
- # Only count if we've been to Petalburg
1352
- if not self.milestone_tracker.is_completed("PETALBURG_CITY"):
1353
- return False
1354
- location = game_state.get("player", {}).get("location", "")
1355
- return "ROUTE_104" in str(location).upper() or "ROUTE 104" in str(location).upper()
1356
- return False
1357
- elif milestone_id == "MR_BRINEY_MET":
1358
- # Assume meeting Mr. Briney happens on Route 104
1359
- if game_state:
1360
- return self.milestone_tracker.is_completed("ROUTE_104_SOUTH")
1361
- return False
1362
- elif milestone_id == "PETALBURG_WOODS":
1363
- if game_state:
1364
- # Only count if we've been to Route 104
1365
- if not self.milestone_tracker.is_completed("ROUTE_104_SOUTH"):
1366
- return False
1367
- location = game_state.get("player", {}).get("location", "")
1368
- return "PETALBURG_WOODS" in str(location).upper() or "PETALBURG WOODS" in str(location).upper()
1369
- return False
1370
- elif milestone_id == "TEAM_AQUA_GRUNT_DEFEATED":
1371
- # Team Aqua grunt defeated at specific location in Petalburg Woods
1372
- if game_state:
1373
- # Must have visited Petalburg Woods
1374
- if not self.milestone_tracker.is_completed("PETALBURG_WOODS"):
1375
- return False
1376
- location = game_state.get("player", {}).get("location", "")
1377
- # Check if in Petalburg Woods and at specific coordinates
1378
- if "PETALBURG_WOODS" in str(location).upper() or "PETALBURG WOODS" in str(location).upper():
1379
- # Check for Map_18_0B at coords (26,23) or (27,23)
1380
- pos = game_state.get("player", {}).get("pos", [])
1381
- if len(pos) >= 2:
1382
- x, y = pos[0], pos[1]
1383
- if y == 23 and x in [26, 27]:
1384
- return True
1385
- return self.milestone_tracker.is_completed("PETALBURG_WOODS")
1386
- return False
1387
- elif milestone_id == "DEVON_GOODS_OBTAINED":
1388
- # Assume Devon Goods obtained after defeating Team Aqua grunt
1389
- if game_state:
1390
- return self.milestone_tracker.is_completed("TEAM_AQUA_GRUNT_DEFEATED")
1391
- return False
1392
-
1393
- # Phase 8: Rustboro City Approach milestones
1394
- elif milestone_id == "ROUTE_104_NORTH":
1395
- if game_state:
1396
- # Only count if we've been through Petalburg Woods
1397
- if not self.milestone_tracker.is_completed("PETALBURG_WOODS"):
1398
- return False
1399
- location = game_state.get("player", {}).get("location", "")
1400
- return (("ROUTE_104" in str(location).upper() or "ROUTE 104" in str(location).upper()) and
1401
- self.milestone_tracker.is_completed("DEVON_GOODS_OBTAINED"))
1402
- return False
1403
- elif milestone_id == "DEVON_CORP_VISITED":
1404
- if game_state:
1405
- location = game_state.get("player", {}).get("location", "")
1406
- return ("DEVON" in str(location).upper() and
1407
- self.milestone_tracker.is_completed("RUSTBORO_CITY"))
1408
- return False
1409
- elif milestone_id == "DEVON_GOODS_DELIVERED":
1410
- # Assume goods delivered after visiting Devon Corp
1411
- if game_state:
1412
- return self.milestone_tracker.is_completed("DEVON_CORP_VISITED")
1413
- return False
1414
- elif milestone_id == "LETTER_RECEIVED":
1415
- # Assume letter received after delivering goods
1416
- if game_state:
1417
- return self.milestone_tracker.is_completed("DEVON_GOODS_DELIVERED")
1418
- return False
1419
- elif milestone_id == "POKEBALLS_PURCHASED":
1420
- # Assume Pokeballs purchased in Rustboro City
1421
- if game_state:
1422
- return self.milestone_tracker.is_completed("RUSTBORO_CITY")
1423
- return False
1424
-
1425
- # Phase 9: Gym Preparation milestones
1426
- elif milestone_id == "RUSTBORO_GYM_ENTERED":
1427
- if game_state:
1428
- # Must have visited Rustboro City first
1429
- if not self.milestone_tracker.is_completed("RUSTBORO_CITY"):
1430
- return False
1431
- location = game_state.get("player", {}).get("location", "")
1432
- return "RUSTBORO_GYM" in str(location).upper() or "RUSTBORO CITY GYM" in str(location).upper()
1433
- return False
1434
- elif milestone_id == "GYM_TRAINERS_DEFEATED":
1435
- # Assume gym trainers defeated after entering gym
1436
- if game_state:
1437
- return self.milestone_tracker.is_completed("RUSTBORO_GYM_ENTERED")
1438
- return False
1439
- elif milestone_id == "ROXANNE_BATTLE_STARTED":
1440
- # Assume Roxanne battle started after defeating gym trainers
1441
- if game_state:
1442
- return self.milestone_tracker.is_completed("GYM_TRAINERS_DEFEATED")
1443
- return False
1444
-
1445
- # Phase 10: First Gym Victory milestones
1446
- elif milestone_id == "ROXANNE_DEFEATED":
1447
- # Roxanne defeated when Stone Badge is obtained
1448
- if game_state:
1449
- # Must have Stone Badge
1450
- return self.milestone_tracker.is_completed("STONE_BADGE")
1451
- return False
1452
- elif milestone_id == "TM_ROCK_TOMB_RECEIVED":
1453
- # Assume TM received after defeating Roxanne
1454
- if game_state:
1455
- return self.milestone_tracker.is_completed("ROXANNE_DEFEATED")
1456
- return False
1457
- elif milestone_id == "FIRST_GYM_COMPLETE":
1458
- # Complete after getting Stone Badge and exiting gym
1459
- if game_state:
1460
- # Must have Stone Badge and not be in gym
1461
- if not self.milestone_tracker.is_completed("STONE_BADGE"):
1462
- return False
1463
- location = game_state.get("player", {}).get("location", "")
1464
- # Not in any gym
1465
- return "GYM" not in str(location).upper()
1466
- return False
1467
-
1468
- return False
1469
-
1470
- except Exception as e:
1471
- logger.warning(f"Error checking milestone condition {milestone_id}: {e}")
1472
- return False
1473
-
1474
- def get_milestones(self) -> Dict[str, Any]:
1475
- """Get current milestone data and progress"""
1476
- try:
1477
- # Get current game state and update milestones
1478
- # Use cached state if available to avoid redundant calls
1479
- game_state = self.get_comprehensive_state()
1480
- # Only update milestones occasionally to avoid performance issues
1481
- import time
1482
- current_time = time.time()
1483
- if not hasattr(self, '_last_milestone_update') or current_time - self._last_milestone_update > 1.0: # Update at most once per second
1484
- self.check_and_update_milestones(game_state)
1485
- self._last_milestone_update = current_time
1486
-
1487
- # Use loaded milestones from the milestone tracker
1488
- milestones = []
1489
- for i, (milestone_id, milestone_data) in enumerate(self.milestone_tracker.milestones.items()):
1490
- milestones.append({
1491
- "id": i + 1,
1492
- "name": milestone_data.get("name", milestone_id),
1493
- "category": milestone_data.get("category", "unknown"),
1494
- "completed": milestone_data.get("completed", False),
1495
- "timestamp": milestone_data.get("timestamp", None)
1496
- })
1497
-
1498
- # Calculate summary stats
1499
- completed_count = sum(1 for m in milestones if m["completed"])
1500
- total_count = len(milestones)
1501
-
1502
- # Handle location data properly
1503
- location_data = game_state.get("player", {}).get("location", "")
1504
- if isinstance(location_data, dict):
1505
- current_location = location_data.get("map_name", "UNKNOWN")
1506
- else:
1507
- current_location = str(location_data) if location_data else "UNKNOWN"
1508
-
1509
- # Handle badges data properly
1510
- badges_data = game_state.get("game", {}).get("badges", 0)
1511
- if isinstance(badges_data, list):
1512
- badge_count = sum(1 for b in badges_data if b)
1513
- else:
1514
- badge_count = badges_data if isinstance(badges_data, int) else 0
1515
-
1516
- return {
1517
- "milestones": milestones,
1518
- "completed": completed_count,
1519
- "total": total_count,
1520
- "progress": completed_count / total_count if total_count > 0 else 0,
1521
- "current_location": current_location,
1522
- "badges": badge_count,
1523
- "pokedex_seen": game_state.get("game", {}).get("pokedex_seen", 0),
1524
- "pokedex_caught": game_state.get("game", {}).get("pokedex_caught", 0),
1525
- "party_size": len(game_state.get("player", {}).get("party", [])),
1526
- "tracking_system": "file_based",
1527
- "milestone_file": self.milestone_tracker.filename
1528
- }
1529
-
1530
- except Exception as e:
1531
- logger.error(f"Error getting milestones: {e}")
1532
- # Fallback to basic milestones if memory reading fails
1533
- basic_milestones = [
1534
- {"id": 1, "name": "GAME_STARTED", "category": "basic", "completed": True, "timestamp": time.time()},
1535
- {"id": 2, "name": "EMULATOR_RUNNING", "category": "basic", "completed": True, "timestamp": time.time()},
1536
- ]
1537
- return {
1538
- "milestones": basic_milestones,
1539
- "completed": 2,
1540
- "total": 2,
1541
- "progress": 1.0,
1542
- "tracking_system": "fallback",
1543
- "error": str(e)
1544
- }