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,1481 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- State Formatter Utility
4
-
5
- Converts comprehensive game state objects to formatted text for LLM prompts and debugging.
6
- Centralizes all state formatting logic for consistency across agent modules.
7
- """
8
-
9
- import json
10
- import logging
11
- import numpy as np
12
- from PIL import Image
13
- from utils.map_formatter import format_map_grid, format_map_for_llm, generate_dynamic_legend, format_tile_to_symbol
14
- import base64
15
- import io
16
- import os, sys
17
- from pokemon_env.enums import MetatileBehavior
18
- from utils import state_formatter as sf
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
- # Global location tracking - MapStitcher handles all persistent storage
23
- CURRENT_LOCATION = None
24
- LAST_LOCATION = None
25
- LAST_TRANSITION = None # Stores transition coordinates
26
- MAP_STITCHER_SAVE_CALLBACK = None # Callback to save map stitcher when location connections change
27
- MAP_STITCHER_INSTANCE = None # Reference to the MapStitcher instance
28
-
29
- def _get_location_connections_from_cache():
30
- """Read location connections from MapStitcher's cache file"""
31
- try:
32
- cache_file = '.pokeagent_cache/map_stitcher_data.json'
33
- if os.path.exists(cache_file):
34
- with open(cache_file, 'r') as f:
35
- data = json.load(f)
36
- return data.get('location_connections', {})
37
- except Exception as e:
38
- print(f"Failed to read location connections from cache: {e}")
39
- return {}
40
-
41
- def detect_dialogue_on_frame(screenshot_base64=None, frame_array=None):
42
- """
43
- Detect if dialogue is visible on the game frame by analyzing the lower portion.
44
-
45
- Args:
46
- screenshot_base64: Base64 encoded screenshot string
47
- frame_array: numpy array of the frame (240x160 for GBA)
48
-
49
- Returns:
50
- dict: {
51
- 'has_dialogue': bool,
52
- 'confidence': float (0-1),
53
- 'reason': str
54
- }
55
- """
56
- try:
57
- # Convert base64 to image if needed
58
- if screenshot_base64 and not frame_array:
59
- image_data = base64.b64decode(screenshot_base64)
60
- image = Image.open(io.BytesIO(image_data))
61
- frame_array = np.array(image)
62
-
63
- if frame_array is None:
64
- return {'has_dialogue': False, 'confidence': 0.0, 'reason': 'No frame data'}
65
-
66
- # GBA resolution is 240x160
67
- height, width = frame_array.shape[:2]
68
-
69
- # Dialogue typically appears in the bottom 40-50 pixels
70
- dialogue_region = frame_array[height-50:, :] # Bottom 50 pixels
71
-
72
- # Convert to grayscale for analysis
73
- if len(dialogue_region.shape) == 3:
74
- # Convert RGB to grayscale
75
- gray = np.dot(dialogue_region[...,:3], [0.299, 0.587, 0.114]).astype(np.uint8)
76
- else:
77
- gray = dialogue_region
78
-
79
- # Dialogue boxes in Pokemon are typically:
80
- # 1. Have a distinct blue/white color scheme
81
- # 2. Have high contrast text on background
82
- # 3. Have consistent borders
83
-
84
- # Check for dialogue box characteristics
85
- # 1. Check for blue dialogue box (typical color range)
86
- if len(dialogue_region.shape) == 3:
87
- # Blue dialogue box detection (Pokemon dialogue boxes are often blue-ish)
88
- blue_mask = (
89
- (dialogue_region[:,:,2] > 100) & # High blue channel
90
- (dialogue_region[:,:,2] > dialogue_region[:,:,0] * 1.2) & # More blue than red
91
- (dialogue_region[:,:,2] > dialogue_region[:,:,1] * 1.2) # More blue than green
92
- )
93
- blue_percentage = np.sum(blue_mask) / blue_mask.size
94
-
95
- # White/light regions (text areas)
96
- white_mask = (
97
- (dialogue_region[:,:,0] > 200) &
98
- (dialogue_region[:,:,1] > 200) &
99
- (dialogue_region[:,:,2] > 200)
100
- )
101
- white_percentage = np.sum(white_mask) / white_mask.size
102
- else:
103
- blue_percentage = 0
104
- white_percentage = 0
105
-
106
- # 2. Check for high contrast (text on background)
107
- std_dev = np.std(gray)
108
-
109
- # 3. Check for horizontal lines (dialogue box borders)
110
- # Detect horizontal edges
111
- vertical_diff = np.abs(np.diff(gray, axis=0))
112
- horizontal_edges = np.sum(vertical_diff > 50) / vertical_diff.size
113
-
114
- # 4. Check for consistent patterns (not random pixels)
115
- # Calculate local variance to detect structured content
116
- local_variance = []
117
- for i in range(0, gray.shape[0]-5, 5):
118
- for j in range(0, gray.shape[1]-5, 5):
119
- patch = gray[i:i+5, j:j+5]
120
- local_variance.append(np.var(patch))
121
-
122
- avg_local_variance = np.mean(local_variance) if local_variance else 0
123
-
124
- # Scoring system
125
- confidence = 0.0
126
- reasons = []
127
-
128
- # Blue/white dialogue box detection
129
- if blue_percentage > 0.3:
130
- confidence += 0.3
131
- reasons.append("blue dialogue box detected")
132
-
133
- if white_percentage > 0.1 and white_percentage < 0.5:
134
- confidence += 0.2
135
- reasons.append("text area detected")
136
-
137
- # High contrast for text
138
- if std_dev > 30 and std_dev < 100:
139
- confidence += 0.2
140
- reasons.append("text contrast detected")
141
-
142
- # Horizontal edges (box borders)
143
- if horizontal_edges > 0.01 and horizontal_edges < 0.1:
144
- confidence += 0.2
145
- reasons.append("dialogue box borders detected")
146
-
147
- # Structured content (not random)
148
- if avg_local_variance > 100 and avg_local_variance < 2000:
149
- confidence += 0.1
150
- reasons.append("structured content")
151
-
152
- # Determine if dialogue is present
153
- has_dialogue = confidence >= 0.5
154
-
155
- return {
156
- 'has_dialogue': has_dialogue,
157
- 'confidence': min(confidence, 1.0),
158
- 'reason': ', '.join(reasons) if reasons else 'no dialogue indicators'
159
- }
160
-
161
- except Exception as e:
162
- logger.warning(f"Failed to detect dialogue on frame: {e}")
163
- return {'has_dialogue': False, 'confidence': 0.0, 'reason': f'error: {e}'}
164
-
165
- def format_state(state_data, format_type="summary", include_debug_info=False, include_npcs=True):
166
- """
167
- Format comprehensive state data into readable text.
168
-
169
- Args:
170
- state_data (dict): The comprehensive state from /state endpoint
171
- format_type (str): "summary" for one-line summary, "detailed" for multi-line LLM format
172
- include_debug_info (bool): Whether to include extra debug information (for detailed format)
173
- include_npcs (bool): Whether to include NPC information in the state
174
-
175
- Returns:
176
- str: Formatted state text
177
- """
178
- if format_type == "summary":
179
- return _format_state_summary(state_data)
180
- elif format_type == "detailed":
181
- return _format_state_detailed(state_data, include_debug_info, include_npcs)
182
- else:
183
- raise ValueError(f"Unknown format_type: {format_type}. Use 'summary' or 'detailed'")
184
-
185
- def format_state_for_llm(state_data, include_debug_info=False, include_npcs=True):
186
- """
187
- Format comprehensive state data into a readable context for the VLM.
188
-
189
- Args:
190
- state_data (dict): The comprehensive state from /state endpoint
191
- include_debug_info (bool): Whether to include extra debug information
192
- include_npcs (bool): Whether to include NPC information in the state
193
-
194
- Returns:
195
- str: Formatted state context for LLM prompts
196
- """
197
- return format_state(state_data, format_type="detailed", include_debug_info=include_debug_info, include_npcs=include_npcs)
198
-
199
- def format_state_summary(state_data):
200
- """
201
- Create a concise one-line summary of the current state for logging.
202
-
203
- Args:
204
- state_data (dict): The comprehensive state from /state endpoint
205
-
206
- Returns:
207
- str: Concise state summary
208
- """
209
- return format_state(state_data, format_type="summary")
210
-
211
- def _format_state_summary(state_data):
212
- """
213
- Internal function to create a concise one-line summary of the current state.
214
- """
215
- player_data = state_data.get('player', {})
216
- game_data = state_data.get('game', {})
217
-
218
- summary_parts = []
219
-
220
- # Player name (don't show during title sequence)
221
- player_location = player_data.get('location', '')
222
- if player_data.get('name') and player_location != 'TITLE_SEQUENCE':
223
- summary_parts.append(f"Player: {player_data['name']}")
224
-
225
- # Location
226
- location = player_data.get('location')
227
- if location:
228
- summary_parts.append(f"Location: {location}")
229
-
230
- # Position
231
- position = player_data.get('position')
232
- if position and isinstance(position, dict):
233
- summary_parts.append(f"Pos: ({position.get('x', '?')}, {position.get('y', '?')})")
234
-
235
- # Facing direction - removed as it's often unreliable
236
- # facing = player_data.get('facing')
237
- # if facing:
238
- # summary_parts.append(f"Facing: {facing}")
239
-
240
- # Game state
241
- game_state = game_data.get('game_state')
242
- if game_state:
243
- summary_parts.append(f"State: {game_state}")
244
-
245
- # Battle status
246
- if game_data.get('is_in_battle'):
247
- summary_parts.append("In Battle")
248
-
249
- # Money
250
- money = game_data.get('money')
251
- if money is not None:
252
- summary_parts.append(f"Money: ${money}")
253
-
254
- # Party information
255
- party_data = player_data.get('party')
256
- if party_data:
257
- party_size = len(party_data)
258
- if party_size > 0:
259
- # Get first Pokemon details
260
- first_pokemon = party_data[0]
261
- species = first_pokemon.get('species_name', 'Unknown')
262
- level = first_pokemon.get('level', '?')
263
- hp = first_pokemon.get('current_hp', '?')
264
- max_hp = first_pokemon.get('max_hp', '?')
265
- status = first_pokemon.get('status', 'OK')
266
-
267
- summary_parts.append(f"Party: {party_size} pokemon")
268
- summary_parts.append(f"Lead: {species} Lv{level} HP:{hp}/{max_hp} {status}")
269
-
270
- # Pokedex information
271
- pokedex_seen = game_data.get('pokedex_seen')
272
- pokedex_caught = game_data.get('pokedex_caught')
273
- if pokedex_seen is not None:
274
- summary_parts.append(f"Pokedex: {pokedex_caught or 0} caught, {pokedex_seen} seen")
275
-
276
- # Badges
277
- badges = game_data.get('badges')
278
- if badges:
279
- if isinstance(badges, list):
280
- badge_count = len(badges)
281
- else:
282
- badge_count = badges
283
- summary_parts.append(f"Badges: {badge_count}")
284
-
285
- # Items
286
- item_count = game_data.get('item_count')
287
- if item_count is not None:
288
- summary_parts.append(f"Items: {item_count}")
289
-
290
- # Game time
291
- time_data = game_data.get('time')
292
- if time_data and isinstance(time_data, (list, tuple)) and len(time_data) >= 3:
293
- hours, minutes, seconds = time_data[:3]
294
- summary_parts.append(f"Time: {hours:02d}:{minutes:02d}:{seconds:02d}")
295
-
296
- # Dialog text (if any)
297
- dialog_text = game_data.get('dialog_text')
298
- dialogue_detected = game_data.get('dialogue_detected', {})
299
- if dialog_text and dialogue_detected.get('has_dialogue', True):
300
- # Only show dialogue if frame detection confirms it (or if detection wasn't run)
301
- # Truncate dialog text to first 50 characters
302
- dialog_preview = dialog_text[:50].replace('\n', ' ').strip()
303
- if len(dialog_text) > 50:
304
- dialog_preview += "..."
305
- summary_parts.append(f"Dialog: {dialog_preview}")
306
-
307
- # Progress context (if available)
308
- progress_context = game_data.get('progress_context')
309
- if progress_context:
310
- badges_obtained = progress_context.get('badges_obtained', 0)
311
- visited_locations = progress_context.get('visited_locations', [])
312
- if badges_obtained > 0:
313
- summary_parts.append(f"Progress: {badges_obtained} badges, {len(visited_locations)} locations")
314
-
315
- return " | ".join(summary_parts) if summary_parts else "No state data"
316
-
317
- def _format_state_detailed(state_data, include_debug_info=False, include_npcs=True):
318
- """
319
- Internal function to create detailed multi-line state format for LLM prompts.
320
- """
321
- context_parts = []
322
-
323
- # Check both player and game sections for data
324
- player_data = state_data.get('player', {})
325
- game_data = state_data.get('game', {})
326
-
327
- # Check if we're in battle to determine formatting mode
328
- is_in_battle = game_data.get('is_in_battle', False) or game_data.get('in_battle', False)
329
-
330
- if is_in_battle:
331
- # BATTLE MODE: Focus on battle-relevant information
332
- context_parts.append("=== BATTLE MODE ===")
333
- context_parts.append("Currently in battle - map and dialogue information hidden")
334
-
335
- # Battle information first
336
- if 'battle_info' in game_data and game_data['battle_info']:
337
- battle = game_data['battle_info']
338
- context_parts.append("\n=== BATTLE STATUS ===")
339
-
340
- # Battle type and context
341
- battle_type = battle.get('battle_type', 'unknown')
342
- context_parts.append(f"Battle Type: {battle_type.title()}")
343
- if battle.get('is_capturable'):
344
- context_parts.append("🟢 Wild Pokémon - CAN BE CAPTURED")
345
- if battle.get('can_escape'):
346
- context_parts.append("🟡 Can escape from battle")
347
-
348
- # Player's active Pokémon
349
- if 'player_pokemon' in battle and battle['player_pokemon']:
350
- player_pkmn = battle['player_pokemon']
351
- context_parts.append(f"\n--- YOUR POKÉMON ---")
352
- context_parts.append(f"{player_pkmn.get('nickname', player_pkmn.get('species', 'Unknown'))} (Lv.{player_pkmn.get('level', '?')})")
353
-
354
- # Health display with percentage
355
- current_hp = player_pkmn.get('current_hp', 0)
356
- max_hp = player_pkmn.get('max_hp', 1)
357
- hp_pct = player_pkmn.get('hp_percentage', 0)
358
- health_bar = "🟢" if hp_pct > 50 else "🟡" if hp_pct > 25 else "🔴"
359
- context_parts.append(f" HP: {current_hp}/{max_hp} ({hp_pct}%) {health_bar}")
360
-
361
- # Status condition
362
- status = player_pkmn.get('status', 'Normal')
363
- if status != 'Normal':
364
- context_parts.append(f" Status: {status}")
365
-
366
- # Types
367
- types = player_pkmn.get('types', [])
368
- if types:
369
- context_parts.append(f" Type: {'/'.join(types)}")
370
-
371
- # Available moves with PP
372
- moves = player_pkmn.get('moves', [])
373
- move_pp = player_pkmn.get('move_pp', [])
374
- if moves:
375
- context_parts.append(f" Moves:")
376
- for i, move in enumerate(moves):
377
- if move and move.strip():
378
- pp = move_pp[i] if i < len(move_pp) else '?'
379
- context_parts.append(f" {i+1}. {move} (PP: {pp})")
380
-
381
- # Opponent Pokémon
382
- if 'opponent_pokemon' in battle:
383
- if battle['opponent_pokemon']:
384
- opp_pkmn = battle['opponent_pokemon']
385
- context_parts.append(f"\n--- OPPONENT POKÉMON ---")
386
- context_parts.append(f"{opp_pkmn.get('species', 'Unknown')} (Lv.{opp_pkmn.get('level', '?')})")
387
-
388
- # Health display with percentage
389
- current_hp = opp_pkmn.get('current_hp', 0)
390
- max_hp = opp_pkmn.get('max_hp', 1)
391
- hp_pct = opp_pkmn.get('hp_percentage', 0)
392
- health_bar = "🟢" if hp_pct > 50 else "🟡" if hp_pct > 25 else "🔴"
393
- context_parts.append(f" HP: {current_hp}/{max_hp} ({hp_pct}%) {health_bar}")
394
-
395
- # Status condition
396
- status = opp_pkmn.get('status', 'Normal')
397
- if status != 'Normal':
398
- context_parts.append(f" Status: {status}")
399
-
400
- # Types
401
- types = opp_pkmn.get('types', [])
402
- if types:
403
- context_parts.append(f" Type: {'/'.join(types)}")
404
-
405
- # Moves (for wild Pokémon, showing moves can help with strategy)
406
- moves = opp_pkmn.get('moves', [])
407
- if moves and any(move.strip() for move in moves):
408
- context_parts.append(f" Known Moves:")
409
- for i, move in enumerate(moves):
410
- if move and move.strip():
411
- context_parts.append(f" • {move}")
412
-
413
- # Stats (helpful for battle strategy)
414
- stats = opp_pkmn.get('stats', {})
415
- if stats:
416
- context_parts.append(f" Battle Stats: ATK:{stats.get('attack', '?')} DEF:{stats.get('defense', '?')} SPD:{stats.get('speed', '?')}")
417
-
418
- # Special indicators
419
- if opp_pkmn.get('is_shiny'):
420
- context_parts.append(f" ✨ SHINY POKÉMON!")
421
- else:
422
- # Opponent data not ready
423
- context_parts.append(f"\n--- OPPONENT POKÉMON ---")
424
- opponent_status = battle.get('opponent_status', 'Opponent data not available')
425
- context_parts.append(f"⏳ {opponent_status}")
426
- context_parts.append(" (Battle may be in initialization phase)")
427
-
428
- # Battle interface info
429
- interface = battle.get('battle_interface', {})
430
- available_actions = interface.get('available_actions', [])
431
- if available_actions:
432
- context_parts.append(f"\n--- AVAILABLE ACTIONS ---")
433
- context_parts.append(f"Options: {', '.join(available_actions)}")
434
-
435
- # Trainer battle specific info
436
- if battle.get('is_trainer_battle'):
437
- remaining = battle.get('opponent_team_remaining', 1)
438
- if remaining > 1:
439
- context_parts.append(f"\nTrainer has {remaining} Pokémon remaining")
440
-
441
- # Battle phase info
442
- battle_phase = battle.get('battle_phase_name')
443
- if battle_phase:
444
- context_parts.append(f"\nBattle Phase: {battle_phase}")
445
-
446
- # Party information (important for switching decisions)
447
- context_parts.append("\n=== PARTY STATUS ===")
448
- party_context = _format_party_info(player_data, game_data)
449
- context_parts.extend(party_context)
450
-
451
- # Trainer info if available
452
- if 'name' in player_data and player_data['name']:
453
- context_parts.append(f"\nTrainer: {player_data['name']}")
454
-
455
- # Money/badges might be relevant
456
- money = player_data.get('money') or game_data.get('money')
457
- if money is not None:
458
- context_parts.append(f"Money: ${money}")
459
-
460
- else:
461
- # NORMAL MODE: Full state information
462
- context_parts.append("=== PLAYER INFO ===")
463
-
464
- # Player name and basic info (don't show during title sequence as it hasn't been set yet)
465
- player_location = player_data.get('location', '')
466
- if 'name' in player_data and player_data['name'] and player_location != 'TITLE_SEQUENCE':
467
- context_parts.append(f"Player Name: {player_data['name']}")
468
-
469
- # Position information
470
- position = _get_player_position(player_data)
471
- if position:
472
- context_parts.append(f"Position: X={position.get('x', 'unknown')}, Y={position.get('y', 'unknown')}")
473
-
474
- # Facing direction - removed as it's often unreliable
475
- # if 'facing' in player_data and player_data['facing']:
476
- # context_parts.append(f"Facing: {player_data['facing']}")
477
-
478
- # Money (check both player and game sections)
479
- money = player_data.get('money') or game_data.get('money')
480
- if money is not None:
481
- context_parts.append(f"Money: ${money}")
482
-
483
- # Pokemon Party (check both player and game sections)
484
- party_context = _format_party_info(player_data, game_data)
485
- context_parts.extend(party_context)
486
-
487
- # Map/Location information with traversability (NOT shown in battle)
488
- map_context = _format_map_info(state_data.get('map', {}), player_data, include_debug_info, include_npcs, state_data)
489
- context_parts.extend(map_context)
490
-
491
- # Game state information (including dialogue if not in battle)
492
- game_context = _format_game_state(game_data, state_data)
493
- context_parts.extend(game_context)
494
-
495
- # Debug information if requested (shown in both modes)
496
- if include_debug_info:
497
- debug_context = _format_debug_info(state_data)
498
- context_parts.extend(debug_context)
499
-
500
- return "\n".join(context_parts)
501
-
502
- def format_state_for_debug(state_data):
503
- """
504
- Format state data for detailed debugging output.
505
-
506
- Args:
507
- state_data (dict): The comprehensive state from /state endpoint
508
-
509
- Returns:
510
- str: Detailed debug information
511
- """
512
- debug_parts = []
513
- debug_parts.append("=" * 60)
514
- debug_parts.append("COMPREHENSIVE STATE DEBUG")
515
- debug_parts.append("=" * 60)
516
-
517
- # Raw structure overview
518
- debug_parts.append("\n--- STRUCTURE OVERVIEW ---")
519
- for key, value in state_data.items():
520
- if isinstance(value, dict):
521
- debug_parts.append(f"{key}: dict with {len(value)} keys")
522
- elif isinstance(value, list):
523
- debug_parts.append(f"{key}: list with {len(value)} items")
524
- else:
525
- debug_parts.append(f"{key}: {type(value).__name__} = {value}")
526
-
527
- # Detailed formatted state
528
- debug_parts.append("\n--- FORMATTED STATE ---")
529
- debug_parts.append(format_state_for_llm(state_data, include_debug_info=True))
530
-
531
- # Raw JSON (truncated if too long)
532
- debug_parts.append("\n--- RAW JSON (truncated) ---")
533
- raw_json = json.dumps(state_data, indent=2)
534
- if len(raw_json) > 2000:
535
- debug_parts.append(raw_json[:2000] + "\n... (truncated)")
536
- else:
537
- debug_parts.append(raw_json)
538
-
539
- debug_parts.append("=" * 60)
540
- return "\n".join(debug_parts)
541
-
542
- # Helper functions for state formatting
543
-
544
- def _get_player_position(player_data):
545
- """Extract player position from various possible locations in player data."""
546
- if 'coordinates' in player_data:
547
- return player_data['coordinates']
548
- elif 'position' in player_data:
549
- pos = player_data['position']
550
- # print( _get_player_position found position: {pos}, type: {type(pos)}")
551
- # Check if it's a valid position dict with x,y keys
552
- if pos and isinstance(pos, dict) and 'x' in pos and 'y' in pos:
553
- return pos
554
- # print( position invalid - missing x,y or not dict")
555
- return None
556
-
557
- def _get_party_size(party_data):
558
- """Get party size from party data regardless of format."""
559
- if isinstance(party_data, dict):
560
- return party_data.get('size', len(party_data.get('pokemon', [])))
561
- elif isinstance(party_data, list):
562
- return len(party_data)
563
- return 0
564
-
565
- def _format_party_info(player_data, game_data):
566
- """Format pokemon party information."""
567
- context_parts = []
568
-
569
- # Pokemon Party (check both player and game sections)
570
- party_data = player_data.get('party') or game_data.get('party')
571
- if party_data:
572
- pokemon_list = []
573
- if isinstance(party_data, dict) and party_data.get('pokemon'):
574
- # Format: {"size": X, "pokemon": [...]}
575
- pokemon_list = party_data.get('pokemon', [])
576
- party_size = party_data.get('size', len(pokemon_list))
577
- elif isinstance(party_data, list):
578
- # Format: [pokemon1, pokemon2, ...]
579
- pokemon_list = party_data
580
- party_size = len(pokemon_list)
581
- else:
582
- party_size = 0
583
-
584
- if party_size > 0:
585
- context_parts.append(f"Pokemon Party ({party_size} pokemon):")
586
- for i, pokemon in enumerate(pokemon_list[:6]):
587
- if pokemon:
588
- species = pokemon.get('species_name', pokemon.get('species', 'Unknown'))
589
- level = pokemon.get('level', '?')
590
- hp = pokemon.get('current_hp', '?')
591
- max_hp = pokemon.get('max_hp', '?')
592
- status = pokemon.get('status', 'Normal')
593
- context_parts.append(f" {i+1}. {species} (Lv.{level}) HP: {hp}/{max_hp} Status: {status}")
594
- else:
595
- context_parts.append("No Pokemon in party")
596
- else:
597
- context_parts.append("No Pokemon in party")
598
-
599
- return context_parts
600
-
601
- def _format_map_info(map_info, player_data=None, include_debug_info=False, include_npcs=True, full_state_data=None):
602
- """Format map and traversability information using MapStitcher."""
603
- context_parts = []
604
-
605
- if not map_info:
606
- return context_parts
607
-
608
- # Get location name from player data
609
- location_name = None
610
- if player_data and 'location' in player_data and player_data['location']:
611
- location = player_data['location']
612
- if isinstance(location, dict):
613
- location_name = location.get('map_name', str(location))
614
- else:
615
- location_name = str(location)
616
-
617
- # Special handling for title sequence - don't show map
618
- if location_name == 'TITLE_SEQUENCE':
619
- context_parts.append("\n=== LOCATION INFO ===")
620
- context_parts.append(f"Current Location: {location_name}")
621
- context_parts.append("No map available during title sequence")
622
- return context_parts
623
-
624
- context_parts.append("\n=== LOCATION & MAP INFO ===")
625
- if location_name:
626
- context_parts.append(f"Current Location: {location_name}")
627
-
628
- # Also add current map if different
629
- if 'current_map' in map_info and map_info['current_map'] != location_name:
630
- context_parts.append(f"Current Map: {map_info['current_map']}")
631
-
632
- # Get player coordinates
633
- player_coords = None
634
- player_coords_dict = map_info.get('player_coords', {})
635
- if isinstance(player_coords_dict, dict) and player_coords_dict.get('x') is not None:
636
- player_coords = (player_coords_dict.get('x', 0), player_coords_dict.get('y', 0))
637
- elif player_coords_dict and not isinstance(player_coords_dict, dict):
638
- player_coords = player_coords_dict
639
- elif player_data and 'position' in player_data:
640
- pos = player_data['position']
641
- player_coords = (pos.get('x', 0), pos.get('y', 0))
642
-
643
- # Get MapStitcher instance - prefer the one from memory_reader if available
644
- # This ensures we use the instance that has the actual map data
645
- map_stitcher = map_info.get('_map_stitcher_instance') if map_info else None
646
- if not map_stitcher:
647
- map_stitcher = _get_map_stitcher_instance()
648
-
649
- # Get NPCs if available
650
- npcs = []
651
- if include_npcs and 'object_events' in map_info:
652
- npcs = map_info.get('object_events', [])
653
-
654
- # Get connections from current area
655
- connections = []
656
- if map_info.get('stitched_map_info'):
657
- current_area = map_info['stitched_map_info'].get('current_area', {})
658
- connections = current_area.get('connections', [])
659
-
660
- # Check for pre-generated visual map first (from memory_reader)
661
- if map_info.get('visual_map'):
662
- # Use the pre-generated map visualization
663
- context_parts.append(map_info['visual_map'])
664
- elif location_name:
665
- # Generate map display using MapStitcher
666
- # print( Attempting to generate map for location: '{location_name}'")
667
- # print( MapStitcher exists: {map_stitcher is not None}")
668
- if map_stitcher:
669
- # print( MapStitcher has {len(map_stitcher.map_areas)} areas")
670
- for map_id in list(map_stitcher.map_areas.keys())[:3]:
671
- area = map_stitcher.map_areas[map_id]
672
- # print( Area {map_id}: '{area.location_name}'")
673
-
674
- map_lines = map_stitcher.generate_location_map_display(
675
- location_name=location_name,
676
- player_pos=player_coords,
677
- npcs=npcs,
678
- connections=connections
679
- )
680
-
681
- if map_lines:
682
- # print( Generated {len(map_lines)} map lines from MapStitcher")
683
- context_parts.extend(map_lines)
684
- # Add exploration statistics
685
- location_grid = map_stitcher.get_location_grid(location_name)
686
- if location_grid:
687
- total_tiles = len(location_grid)
688
- context_parts.append("")
689
- context_parts.append(f"Total explored: {total_tiles} tiles")
690
- else:
691
- # print( MapStitcher returned empty, falling back to memory tiles")
692
- # Fallback if MapStitcher doesn't have data for this location - use memory tiles
693
- pass
694
- if 'tiles' in map_info and map_info['tiles']:
695
- context_parts.append(f"\n--- MAP: {location_name.upper()} (from memory) ---")
696
- _add_local_map_fallback(context_parts, map_info, include_npcs)
697
- else:
698
- context_parts.append(f"\n--- MAP: {location_name.upper()} ---")
699
- context_parts.append("No map data available")
700
- else:
701
- # No location name - use local map fallback
702
- context_parts.append("\n--- LOCAL MAP (Location unknown) ---")
703
- if 'tiles' in map_info and map_info['tiles']:
704
- _add_local_map_fallback(context_parts, map_info, include_npcs)
705
-
706
- # NPC information removed - unreliable detection with incorrect positions
707
-
708
- # Add stitched map information if available
709
- stitched_info = _format_stitched_map_info(map_info)
710
- if stitched_info:
711
- context_parts.extend(stitched_info)
712
-
713
- return context_parts
714
-
715
- def _add_local_map_fallback(context_parts, map_info, include_npcs):
716
- """Helper function to add local map display as fallback"""
717
- if 'tiles' in map_info and map_info['tiles']:
718
- raw_tiles = map_info['tiles']
719
- # Use default facing direction since memory-based facing is unreliable
720
- facing = "South" # default
721
-
722
- # Get player coordinates
723
- player_coords = map_info.get('player_coords')
724
-
725
- # Get NPCs if available and include_npcs is True
726
- npcs = []
727
- if include_npcs and 'object_events' in map_info:
728
- npcs = map_info.get('object_events', [])
729
-
730
- # Use unified LLM formatter for consistency with NPCs if available
731
- map_display = format_map_for_llm(raw_tiles, facing, npcs, player_coords)
732
- context_parts.append(map_display)
733
-
734
- # Add dynamic legend based on symbols in the map
735
- grid = format_map_grid(raw_tiles, facing, npcs, player_coords)
736
- legend = generate_dynamic_legend(grid)
737
- context_parts.append(f"\n{legend}")
738
-
739
- def _format_world_map_display(stitched_data, full_state_data=None):
740
- """Format location-specific map display"""
741
- try:
742
- # Build separate map for each location
743
- return _build_stitched_world_map(stitched_data, full_state_data)
744
-
745
- except Exception as e:
746
- logger.warning(f"World map generation failed: {e}")
747
- return []
748
-
749
- def _get_map_stitcher_instance():
750
- """Get the MapStitcher instance - always reload from cache for multiprocess compatibility"""
751
- from utils.map_stitcher import MapStitcher
752
- # Always create fresh instance to read latest cache
753
- # This is needed because server and client run in different processes
754
- return MapStitcher()
755
-
756
- def save_persistent_world_map(file_path=None):
757
- """Deprecated - MapStitcher handles all persistence now"""
758
- # MapStitcher auto-saves, nothing to do here
759
- pass
760
-
761
- def load_persistent_world_map(file_path=None):
762
- """Deprecated - MapStitcher handles all persistence now"""
763
- # MapStitcher auto-loads, nothing to do here
764
- pass
765
-
766
- def clear_persistent_world_map():
767
- """Clear the MapStitcher's data for testing"""
768
- global CURRENT_LOCATION, LAST_LOCATION, LAST_TRANSITION
769
- CURRENT_LOCATION = None
770
- LAST_LOCATION = None
771
- LAST_TRANSITION = None
772
- # Clear MapStitcher data if instance exists
773
- if MAP_STITCHER_INSTANCE:
774
- MAP_STITCHER_INSTANCE.map_areas.clear()
775
- MAP_STITCHER_INSTANCE.warp_connections.clear()
776
- MAP_STITCHER_INSTANCE.save_to_file()
777
- # print( Cleared map stitcher data")
778
-
779
- # Helper function removed - now handled by MapStitcher._is_explorable_edge()
780
-
781
-
782
- def _build_stitched_world_map(stitched_data, full_state_data=None):
783
- """Build map display using MapStitcher"""
784
-
785
- # Get the current area and location info
786
- current_area = stitched_data.get('current_area', {})
787
- location_name = current_area.get('name')
788
-
789
- # Get NPCs if available
790
- npcs = stitched_data.get('object_events', [])
791
-
792
- # Use fallback location name if current_area has 'Unknown' or empty name
793
- fallback_name = stitched_data.get('location_name_fallback')
794
- if not location_name or location_name == 'Unknown':
795
- if fallback_name and fallback_name != 'Unknown':
796
- location_name = fallback_name
797
- else:
798
- location_name = 'Unknown'
799
- elif fallback_name and fallback_name != location_name and fallback_name != 'Unknown':
800
- location_name = fallback_name
801
-
802
- # Track location changes and transition coordinates
803
- global CURRENT_LOCATION, LAST_LOCATION, LAST_TRANSITION
804
- LAST_TRANSITION = None # Will store transition info
805
-
806
- # Get MapStitcher instance - prefer the one from memory_reader if available
807
- # This ensures we use the instance that has the actual map data
808
- map_stitcher = map_info.get('_map_stitcher_instance') if map_info else None
809
- if not map_stitcher:
810
- map_stitcher = _get_map_stitcher_instance()
811
-
812
- # Get player position for transition tracking
813
- player_local_pos = stitched_data.get('player_local_pos', (0, 0))
814
-
815
- # Check if we've changed locations
816
- if CURRENT_LOCATION != location_name:
817
- # Store transition information
818
- if CURRENT_LOCATION is not None and CURRENT_LOCATION != location_name:
819
- LAST_TRANSITION = {
820
- 'from_location': CURRENT_LOCATION,
821
- 'from_coords': player_local_pos, # Use current position as it's the exit point
822
- 'to_location': location_name,
823
- 'to_coords': player_local_pos
824
- }
825
-
826
- # Trigger MapStitcher save if callback is available
827
- if MAP_STITCHER_SAVE_CALLBACK:
828
- try:
829
- # print( Location transition detected, triggering MapStitcher save...")
830
- MAP_STITCHER_SAVE_CALLBACK()
831
- # print( MapStitcher save completed")
832
- except Exception as e:
833
- print("Failed to save via MapStitcher callback: {e}")
834
-
835
- # print( Location transition: {CURRENT_LOCATION} → {location_name} at position {player_local_pos}")
836
-
837
- LAST_LOCATION = CURRENT_LOCATION
838
- CURRENT_LOCATION = location_name
839
-
840
- # Get player position
841
- player_local_pos = stitched_data.get('player_local_pos')
842
- if not player_local_pos:
843
- # Fallback to extracting from current area
844
- player_local_pos = current_area.get('player_pos', (5, 5)) # Default to center
845
-
846
- # Get connection info from current_area
847
- connections = current_area.get('connections', [])
848
-
849
- # Build the display for this location
850
- lines = []
851
-
852
- # Add location transition indicator if we just changed locations
853
- if LAST_LOCATION and LAST_LOCATION != location_name:
854
- lines.append(f"\n⚡ LOCATION TRANSITION: {LAST_LOCATION} → {location_name}")
855
- # Add transition coordinates if available
856
- if LAST_TRANSITION and LAST_TRANSITION['to_location'] == location_name:
857
- from_coords = LAST_TRANSITION['from_coords']
858
- to_coords = LAST_TRANSITION['to_coords']
859
- from_location = LAST_TRANSITION['from_location']
860
- lines.append(f"📍 Exited {from_location} at ({from_coords[0]}, {from_coords[1]})")
861
- lines.append(f"📍 Entered {location_name} at ({to_coords[0]}, {to_coords[1]})")
862
- # Clear the LAST_LOCATION after showing transition
863
- LAST_LOCATION = None
864
- lines.append("")
865
-
866
- # Use MapStitcher to generate the map display
867
- map_stitcher = full_state_data.get('map_stitcher') if full_state_data else None
868
- if not map_stitcher and MAP_STITCHER_INSTANCE:
869
- map_stitcher = MAP_STITCHER_INSTANCE
870
-
871
- if map_stitcher:
872
- # Generate map display using MapStitcher
873
- map_lines = map_stitcher.generate_location_map_display(
874
- location_name=location_name,
875
- player_pos=player_local_pos,
876
- npcs=npcs,
877
- connections=connections
878
- )
879
- lines.extend(map_lines)
880
-
881
- # Add exploration statistics
882
- location_grid = map_stitcher.get_location_grid(location_name)
883
- if location_grid:
884
- total_tiles = len(location_grid)
885
- lines.append("")
886
- lines.append(f"Total explored in {location_name}: {total_tiles} tiles")
887
- else:
888
- # Fallback if no MapStitcher available
889
- lines.append(f"\n--- MAP: {location_name.upper()} ---")
890
- lines.append("Map data not available")
891
-
892
- # Add discovered connection points from our transition tracking
893
- # print( Checking portal coordinates for location: {location_name}")
894
- portal_connections_found = False
895
-
896
- # Check location connections from MapStitcher cache first
897
- location_connections = _get_location_connections_from_cache()
898
- if location_connections and location_name in location_connections:
899
- if not portal_connections_found:
900
- lines.append("")
901
- lines.append("Known Portal Coordinates:")
902
- portal_connections_found = True
903
- for other_loc, my_coords, their_coords in location_connections[location_name]:
904
- lines.append(f" At ({my_coords[0]}, {my_coords[1]}) → {other_loc} ({their_coords[0]}, {their_coords[1]})")
905
-
906
- # Also check MAP_ID_CONNECTIONS if available (from loaded MapStitcher data or HTTP response)
907
- # print( About to check MAP_ID_CONNECTIONS...")
908
- # print( full_state_data is: {type(full_state_data)} with keys: {list(full_state_data.keys()) if full_state_data else 'None'}")
909
- try:
910
- # Try multiple ways to find MAP_ID_CONNECTIONS
911
- map_id_connections = None
912
-
913
- # Method 0: Check if passed via HTTP response (NEW - preferred method)
914
- # First try location_connections (more accurate), then fall back to portal_connections
915
- if full_state_data and 'location_connections' in full_state_data:
916
- location_connections = full_state_data['location_connections']
917
- # print( Found location_connections in HTTP response: {location_connections}")
918
-
919
- # Use the location connections display logic
920
- if location_name in location_connections:
921
- if not portal_connections_found:
922
- lines.append("")
923
- lines.append("Known Portal Coordinates:")
924
- portal_connections_found = True
925
- for other_loc, my_coords, their_coords in location_connections[location_name]:
926
- lines.append(f" At ({my_coords[0]}, {my_coords[1]}) → {other_loc} ({their_coords[0]}, {their_coords[1]})")
927
- # print( Added location connection: At ({my_coords[0]}, {my_coords[1]}) → {other_loc} ({their_coords[0]}, {their_coords[1]})")
928
-
929
- elif full_state_data and 'portal_connections' in full_state_data:
930
- map_id_connections = full_state_data['portal_connections']
931
- # print( Found MAP_ID_CONNECTIONS in HTTP response: {map_id_connections}")
932
-
933
- # Get current map ID to find relevant portals
934
- current_map_id = None
935
- if stitched_data and 'current_area' in stitched_data:
936
- area_id = stitched_data['current_area'].get('id', '')
937
- if area_id:
938
- try:
939
- current_map_id = int(area_id, 16) if isinstance(area_id, str) else area_id
940
- # print( Current map ID: {current_map_id}")
941
- except ValueError:
942
- print("Could not parse map ID from: {area_id}")
943
-
944
- # Try to find current map ID by matching current location name
945
- if not current_map_id:
946
- current_location = location_name # Use the location_name parameter
947
- # print( Trying to find map ID for location: {current_location}")
948
-
949
- # Look through server's map data to find current map ID
950
- if 'map' in full_state_data:
951
- map_info = full_state_data.get('map', {})
952
- map_location = map_info.get('location_name', '')
953
- if map_location:
954
- current_location = map_location
955
- # print( Using map location: {current_location}")
956
-
957
- # Match location with portal connections - try all map IDs
958
- for map_id in map_id_connections.keys():
959
- # Convert string keys to int if needed
960
- try:
961
- test_map_id = int(map_id) if isinstance(map_id, str) else map_id
962
- # print( Testing map ID {test_map_id} for location '{current_location}'")
963
-
964
- # For LITTLEROOT TOWN MAYS HOUSE 2F, map_id should be 259
965
- if current_location == "LITTLEROOT TOWN MAYS HOUSE 2F" and test_map_id == 259:
966
- current_map_id = test_map_id
967
- # print( Found matching map ID: {current_map_id}")
968
- break
969
- elif current_location == "LITTLEROOT TOWN MAYS HOUSE 1F" and test_map_id == 258:
970
- current_map_id = test_map_id
971
- # print( Found matching map ID: {current_map_id}")
972
- break
973
- except (ValueError, TypeError):
974
- continue
975
-
976
- # Display portal coordinates if we found them
977
- if current_map_id and current_map_id in map_id_connections:
978
- if not portal_connections_found:
979
- lines.append("")
980
- lines.append("Known Portal Coordinates:")
981
- portal_connections_found = True
982
-
983
- for conn in map_id_connections[current_map_id]:
984
- to_name = conn.get('to_name', 'Unknown Location')
985
- from_pos = conn.get('from_pos', [0, 0])
986
- to_pos = conn.get('to_pos', [0, 0])
987
- lines.append(f" At ({from_pos[0]}, {from_pos[1]}) → {to_name} ({to_pos[0]}, {to_pos[1]})")
988
- # print( Added portal: At ({from_pos[0]}, {from_pos[1]}) → {to_name} ({to_pos[0]}, {to_pos[1]})")
989
-
990
- elif stitched_data and 'portal_connections' in stitched_data:
991
- map_id_connections = stitched_data['portal_connections']
992
- # print( Found MAP_ID_CONNECTIONS in stitched_data: {map_id_connections}")
993
-
994
- # Method 1: Check current module (fallback)
995
- if not map_id_connections:
996
- current_module = sys.modules[__name__]
997
- # print( Checking current module for MAP_ID_CONNECTIONS attribute...")
998
- # print( hasattr(current_module, 'MAP_ID_CONNECTIONS'): {hasattr(current_module, 'MAP_ID_CONNECTIONS')}")
999
- if hasattr(current_module, 'MAP_ID_CONNECTIONS') and current_module.MAP_ID_CONNECTIONS:
1000
- map_id_connections = current_module.MAP_ID_CONNECTIONS
1001
- # print( Found MAP_ID_CONNECTIONS in current module: {map_id_connections}")
1002
-
1003
- # Method 2: Check global variable (fallback)
1004
- if not map_id_connections:
1005
- # print( Checking globals for MAP_ID_CONNECTIONS...")
1006
- # print( 'MAP_ID_CONNECTIONS' in globals(): {'MAP_ID_CONNECTIONS' in globals()}")
1007
- try:
1008
- if 'MAP_ID_CONNECTIONS' in globals():
1009
- # print( globals()['MAP_ID_CONNECTIONS']: {globals()['MAP_ID_CONNECTIONS']}")
1010
- if 'MAP_ID_CONNECTIONS' in globals() and globals()['MAP_ID_CONNECTIONS']:
1011
- map_id_connections = globals()['MAP_ID_CONNECTIONS']
1012
- # print( Found MAP_ID_CONNECTIONS in globals: {map_id_connections}")
1013
- except Exception as e:
1014
- print("Error checking globals: {e}")
1015
-
1016
- # Method 3: Re-import state_formatter and check (fallback)
1017
- if not map_id_connections:
1018
- try:
1019
- # print( Attempting to re-import state_formatter...")
1020
- # print( hasattr(sf, 'MAP_ID_CONNECTIONS'): {hasattr(sf, 'MAP_ID_CONNECTIONS')}")
1021
- if hasattr(sf, 'MAP_ID_CONNECTIONS'):
1022
- # print( sf.MAP_ID_CONNECTIONS: {sf.MAP_ID_CONNECTIONS}")
1023
- if hasattr(sf, 'MAP_ID_CONNECTIONS') and sf.MAP_ID_CONNECTIONS:
1024
- map_id_connections = sf.MAP_ID_CONNECTIONS
1025
- # print( Found MAP_ID_CONNECTIONS in imported module: {map_id_connections}")
1026
- except Exception as e:
1027
- print(f" Error re-importing state_formatter: {e}")
1028
-
1029
- if map_id_connections:
1030
- # print( MAP_ID_CONNECTIONS available with {len(map_id_connections)} maps")
1031
- # print( Available map IDs: {list(map_id_connections.keys())}")
1032
-
1033
- # Get current map ID from stitched data
1034
- current_map_id = None
1035
- # print( stitched_data structure: {stitched_data}")
1036
- if stitched_data and stitched_data.get('available'):
1037
- # Try to get map ID from current area
1038
- current_area = stitched_data.get('current_area', {})
1039
- map_id_str = current_area.get('id')
1040
- # print( Current area ID from stitched_data: {map_id_str}")
1041
- # print( Full current_area: {current_area}")
1042
- if map_id_str:
1043
- try:
1044
- current_map_id = int(map_id_str, 16) # Convert from hex string
1045
- print(f" Converted to map ID: {current_map_id}")
1046
- except ValueError:
1047
- print(f" Failed to convert map ID: {map_id_str}")
1048
- else:
1049
- print("No map ID found in current_area: {current_area}")
1050
- # else:
1051
- # print( No stitched_data available or not available")
1052
- # print( stitched_data type: {type(stitched_data)}")
1053
- # if stitched_data:
1054
- # print( stitched_data keys: {list(stitched_data.keys()) if isinstance(stitched_data, dict) else 'not a dict'}")
1055
- # print( stitched_data.get('available'): {stitched_data.get('available') if isinstance(stitched_data, dict) else 'N/A'}")
1056
-
1057
- if current_map_id and current_map_id in map_id_connections:
1058
- # print( Found connections for map ID {current_map_id}")
1059
- if not portal_connections_found:
1060
- lines.append("")
1061
- lines.append("Known Portal Coordinates:")
1062
- portal_connections_found = True
1063
- for conn in map_id_connections[current_map_id]:
1064
- to_name = conn['to_name']
1065
- from_pos = conn['from_pos']
1066
- to_pos = conn['to_pos']
1067
- lines.append(f" At ({from_pos[0]}, {from_pos[1]}) → {to_name} ({to_pos[0]}, {to_pos[1]})")
1068
- else:
1069
- print("No connections found for map ID {current_map_id} (available: {list(map_id_connections.keys()) if map_id_connections else 'None'})")
1070
- else:
1071
- print("MAP_ID_CONNECTIONS not found in any location")
1072
- except Exception as e:
1073
- print(f" Error checking MAP_ID_CONNECTIONS: {e}")
1074
-
1075
- return lines
1076
-
1077
-
1078
- def _format_stitched_map_info(map_info):
1079
- """Format stitched map information for the agent"""
1080
- context_parts = []
1081
-
1082
- # Check if stitched map info is available
1083
- stitched_data = map_info.get('stitched_map_info')
1084
- if not stitched_data or not stitched_data.get('available'):
1085
- return context_parts
1086
-
1087
- # Check if world map display with terrain was already shown
1088
- # Old world map knowledge system removed - replaced by location-based maps with portal coordinates
1089
- return context_parts
1090
-
1091
- def _format_game_state(game_data, state_data=None):
1092
- """Format game state information (for non-battle mode)."""
1093
- context_parts = []
1094
-
1095
- if not game_data:
1096
- return context_parts
1097
-
1098
- context_parts.append("\n=== GAME STATE ===")
1099
-
1100
- # Note: Battle info is handled separately in battle mode
1101
- # This is for showing game state when NOT in battle
1102
-
1103
- # Dialogue detection and validation (only show when not in battle)
1104
- is_in_battle = game_data.get('is_in_battle', False) or game_data.get('in_battle', False)
1105
-
1106
- if not is_in_battle:
1107
- dialog_text = game_data.get('dialog_text')
1108
- dialogue_detected = game_data.get('dialogue_detected', {})
1109
-
1110
- if dialog_text and dialogue_detected.get('has_dialogue', False):
1111
- # Only show dialogue if it's actually visible and active
1112
- context_parts.append(f"\n--- DIALOGUE ---")
1113
- if dialogue_detected.get('confidence') is not None:
1114
- context_parts.append(f"Detection confidence: {dialogue_detected['confidence']:.1%}")
1115
- context_parts.append(f"Text: {dialog_text}")
1116
- # Note: Residual/invisible dialogue text is completely hidden from agent
1117
-
1118
- # Check if we're in title sequence and override game state
1119
- player_location = state_data.get('player', {}).get('location', '') if state_data else ''
1120
- if player_location == 'TITLE_SEQUENCE':
1121
- context_parts.append(f"Game State: title")
1122
- elif 'game_state' in game_data:
1123
- context_parts.append(f"Game State: {game_data['game_state']}")
1124
-
1125
- # Get player data from state_data if provided
1126
- player_data = state_data.get('player', {}) if state_data else {}
1127
-
1128
- # Add helpful prompt for title sequence
1129
- player_location = player_data.get('location', '')
1130
- if player_location == 'TITLE_SEQUENCE':
1131
- context_parts.append("")
1132
- context_parts.append("💡 TIP: Make sure to choose a fun name for your character!")
1133
- context_parts.append("Be creative and have fun with the naming!")
1134
-
1135
- # Add movement preview for overworld navigation (but not during title sequence)
1136
- if (state_data and not is_in_battle and
1137
- game_data.get('game_state') == 'overworld' and
1138
- player_location != 'TITLE_SEQUENCE'):
1139
- movement_preview = format_movement_preview_for_llm(state_data)
1140
- if movement_preview:
1141
- context_parts.append("")
1142
- context_parts.append(movement_preview)
1143
-
1144
- return context_parts
1145
-
1146
- def _format_debug_info(state_data):
1147
- """Format additional debug information."""
1148
- context_parts = []
1149
-
1150
- context_parts.append("\n=== DEBUG INFO ===")
1151
-
1152
- # Step information
1153
- if 'step_number' in state_data:
1154
- context_parts.append(f"Step Number: {state_data['step_number']}")
1155
-
1156
- if 'status' in state_data:
1157
- context_parts.append(f"Status: {state_data['status']}")
1158
-
1159
- # Visual data info
1160
- if 'visual' in state_data:
1161
- visual = state_data['visual']
1162
- if 'resolution' in visual:
1163
- context_parts.append(f"Resolution: {visual['resolution']}")
1164
- if 'screenshot_base64' in visual:
1165
- context_parts.append(f"Screenshot: Available ({len(visual['screenshot_base64'])} chars)")
1166
-
1167
- return context_parts
1168
-
1169
- # Convenience functions for specific use cases
1170
-
1171
- def get_movement_options(state_data):
1172
- """
1173
- Extract movement options from traversability data.
1174
-
1175
- Returns:
1176
- dict: Direction -> description mapping
1177
- """
1178
- map_info = state_data.get('map', {})
1179
- if 'traversability' not in map_info or not map_info['traversability']:
1180
- return {}
1181
-
1182
- traversability = map_info['traversability']
1183
- center_y = len(traversability) // 2
1184
- center_x = len(traversability[0]) // 2
1185
-
1186
- directions = {
1187
- 'UP': (0, -1), 'DOWN': (0, 1),
1188
- 'LEFT': (-1, 0), 'RIGHT': (1, 0)
1189
- }
1190
-
1191
- movement_options = {}
1192
- for direction, (dx, dy) in directions.items():
1193
- new_x, new_y = center_x + dx, center_y + dy
1194
- if 0 <= new_y < len(traversability) and 0 <= new_x < len(traversability[new_y]):
1195
- cell = str(traversability[new_y][new_x])
1196
- if cell == "0":
1197
- movement_options[direction] = "BLOCKED"
1198
- elif cell == ".":
1199
- movement_options[direction] = "Normal path"
1200
- elif "TALL" in cell:
1201
- movement_options[direction] = "Tall grass (wild encounters)"
1202
- elif "WATER" in cell:
1203
- movement_options[direction] = "Water (need Surf)"
1204
- else:
1205
- movement_options[direction] = cell
1206
- else:
1207
- movement_options[direction] = "Out of bounds"
1208
-
1209
- return movement_options
1210
-
1211
-
1212
- def get_movement_preview(state_data):
1213
- """
1214
- Get detailed preview of what happens with each directional movement.
1215
- Shows new coordinates and tile information for each direction.
1216
-
1217
- Args:
1218
- state_data: Complete game state data
1219
-
1220
- Returns:
1221
- dict: Direction -> preview info mapping
1222
- """
1223
- # Get current player position
1224
- player_data = state_data.get('player', {})
1225
- player_position = _get_player_position(player_data)
1226
-
1227
- if not player_position or 'x' not in player_position or 'y' not in player_position:
1228
- # print( Movement preview - No player position. player_position={player_position}")
1229
- return {}
1230
-
1231
- current_x = int(player_position['x'])
1232
- current_y = int(player_position['y'])
1233
-
1234
- # Get map and tile data
1235
- map_info = state_data.get('map', {})
1236
- raw_tiles = map_info.get('tiles', [])
1237
-
1238
- if not raw_tiles:
1239
- # print( Movement preview - No tiles. map_info keys: {list(map_info.keys()) if map_info else 'None'}")
1240
- return {}
1241
-
1242
- directions = {
1243
- 'UP': (0, -1),
1244
- 'DOWN': (0, 1),
1245
- 'LEFT': (-1, 0),
1246
- 'RIGHT': (1, 0)
1247
- }
1248
-
1249
- movement_preview = {}
1250
-
1251
- # Player is at center of the 15x15 grid
1252
- center_x = len(raw_tiles[0]) // 2 if raw_tiles and raw_tiles[0] else 7
1253
- center_y = len(raw_tiles) // 2 if raw_tiles else 7
1254
-
1255
- # Get the tile the player is currently standing on
1256
- current_tile_symbol = None
1257
- if (0 <= center_y < len(raw_tiles) and
1258
- 0 <= center_x < len(raw_tiles[center_y]) and
1259
- raw_tiles[center_y] and raw_tiles[center_y][center_x]):
1260
- current_tile = raw_tiles[center_y][center_x]
1261
- current_tile_symbol = format_tile_to_symbol(current_tile)
1262
-
1263
- for direction, (dx, dy) in directions.items():
1264
- # Calculate new world coordinates
1265
- new_world_x = current_x + dx
1266
- new_world_y = current_y + dy
1267
-
1268
- # Calculate grid position in the tile array
1269
- grid_x = center_x + dx
1270
- grid_y = center_y + dy
1271
-
1272
- preview_info = {
1273
- 'new_coords': (new_world_x, new_world_y),
1274
- 'blocked': True,
1275
- 'tile_symbol': '#',
1276
- 'tile_description': 'BLOCKED - Out of bounds'
1277
- }
1278
-
1279
- # Check if the target position is within the grid bounds
1280
- if (0 <= grid_y < len(raw_tiles) and
1281
- 0 <= grid_x < len(raw_tiles[grid_y]) and
1282
- raw_tiles[grid_y]):
1283
-
1284
- try:
1285
- # Get the tile at the target position
1286
- target_tile = raw_tiles[grid_y][grid_x]
1287
-
1288
- # Get tile symbol and check if walkable
1289
- tile_symbol = format_tile_to_symbol(target_tile)
1290
-
1291
- # Determine if movement is blocked
1292
- is_blocked = tile_symbol in ['#', 'W'] # Walls and water block movement
1293
-
1294
- # SPECIAL CASE: If player is standing on stairs/door, don't block the warp direction
1295
- # Stairs and doors often require moving in a specific direction to activate
1296
- if current_tile_symbol in ['S', 'D']:
1297
- # When on stairs/doors, typically you need to move forward to activate them
1298
- # Don't block any direction when on these tiles to allow proper navigation
1299
- # This ensures the agent can properly use warps/doors even if the destination
1300
- # tile might normally be considered blocked
1301
- if tile_symbol in ['#', 'W']:
1302
- # Override the blocking for navigation tiles but KEEP original symbol
1303
- is_blocked = False
1304
- # DO NOT change tile_symbol - preserve S, D, #, W, etc.
1305
-
1306
- # Special handling for jump ledges - they're only walkable in their direction
1307
- if tile_symbol in ['↓', '↑', '←', '→', '↗', '↖', '↘', '↙']:
1308
- # Map directions to tile symbols
1309
- ledge_direction_map = {
1310
- 'UP': '↑',
1311
- 'DOWN': '↓',
1312
- 'LEFT': '←',
1313
- 'RIGHT': '→'
1314
- }
1315
-
1316
- # Only allow movement if we're going in the direction the ledge points
1317
- if direction in ledge_direction_map:
1318
- allowed_symbol = ledge_direction_map[direction]
1319
- if tile_symbol != allowed_symbol:
1320
- is_blocked = True # Block movement in wrong direction
1321
- else:
1322
- is_blocked = True # Block diagonal movements for basic directional ledges
1323
-
1324
- # Get tile description
1325
- if len(target_tile) >= 2:
1326
- tile_id, behavior = target_tile[:2]
1327
-
1328
- # Convert behavior to readable description
1329
- if hasattr(behavior, 'name'):
1330
- behavior_name = behavior.name
1331
- elif isinstance(behavior, int):
1332
- try:
1333
- behavior_enum = MetatileBehavior(behavior)
1334
- behavior_name = behavior_enum.name
1335
- except (ValueError, ImportError):
1336
- behavior_name = f"BEHAVIOR_{behavior}"
1337
- else:
1338
- behavior_name = str(behavior)
1339
-
1340
- # Create human-readable description
1341
- # Check if we're overriding blocking due to being on stairs/door
1342
- is_override = current_tile_symbol in ['S', 'D'] and not is_blocked and tile_symbol in ['#', 'W']
1343
-
1344
- if is_override:
1345
- # We're on stairs/door and this normally blocked tile is walkable
1346
- if tile_symbol == '#':
1347
- tile_description = f"Walkable - Warp/Door exit (normally blocked) (ID: {tile_id})"
1348
- elif tile_symbol == 'W':
1349
- tile_description = f"Walkable - Warp/Door exit over water (ID: {tile_id})"
1350
- elif tile_symbol == '.':
1351
- tile_description = f"Walkable path (ID: {tile_id})"
1352
- elif tile_symbol == '#':
1353
- tile_description = f"BLOCKED - Wall/Obstacle (ID: {tile_id}, {behavior_name})"
1354
- elif tile_symbol == 'W':
1355
- tile_description = f"BLOCKED - Water (need Surf) (ID: {tile_id})"
1356
- elif tile_symbol == '~':
1357
- tile_description = f"Walkable - Tall grass (wild encounters) (ID: {tile_id})"
1358
- elif tile_symbol == 'D':
1359
- tile_description = f"Walkable - Door/Entrance (ID: {tile_id})"
1360
- elif tile_symbol == 'S':
1361
- tile_description = f"Walkable - Stairs/Warp (ID: {tile_id})"
1362
- elif tile_symbol in ['↓', '↑', '←', '→', '↗', '↖', '↘', '↙']:
1363
- # Ledge description based on whether movement is allowed
1364
- if is_blocked:
1365
- tile_description = f"BLOCKED - Jump ledge {tile_symbol} (wrong direction) (ID: {tile_id})"
1366
- else:
1367
- tile_description = f"Walkable - Jump ledge {tile_symbol} (correct direction) (ID: {tile_id})"
1368
- else:
1369
- tile_description = f"Walkable - {behavior_name} (ID: {tile_id})"
1370
- else:
1371
- tile_description = "Unknown tile"
1372
-
1373
- preview_info.update({
1374
- 'blocked': is_blocked,
1375
- 'tile_symbol': tile_symbol,
1376
- 'tile_description': tile_description
1377
- })
1378
-
1379
- except (IndexError, TypeError) as e:
1380
- logger.warning(f"Error analyzing tile at {grid_x}, {grid_y}: {e}")
1381
- # Keep default blocked values
1382
- pass
1383
-
1384
- movement_preview[direction] = preview_info
1385
-
1386
- return movement_preview
1387
-
1388
-
1389
- def format_movement_preview_for_llm(state_data):
1390
- """
1391
- Format movement preview in a concise format suitable for LLM prompts.
1392
-
1393
- Args:
1394
- state_data: Complete game state data
1395
-
1396
- Returns:
1397
- str: Formatted movement preview text
1398
- """
1399
- preview = get_movement_preview(state_data)
1400
-
1401
- if not preview:
1402
- return "Movement preview: Not available"
1403
-
1404
- lines = ["MOVEMENT PREVIEW:"]
1405
-
1406
- for direction in ['UP', 'DOWN', 'LEFT', 'RIGHT']:
1407
- if direction in preview:
1408
- info = preview[direction]
1409
- new_x, new_y = info['new_coords']
1410
- symbol = info['tile_symbol']
1411
- status = "BLOCKED" if info['blocked'] else "WALKABLE"
1412
-
1413
- lines.append(f" {direction:5}: ({new_x:3},{new_y:3}) [{symbol}] {status}")
1414
- # Add brief description for tiles
1415
- desc = info['tile_description']
1416
- if info['blocked']:
1417
- # Special messages for blocked tiles
1418
- if 'Jump ledge' in desc and 'wrong direction' in desc:
1419
- lines[-1] += " - Can only jump in arrow direction"
1420
- elif 'Water' in desc:
1421
- lines[-1] += " - Need Surf to cross"
1422
- elif 'Wall' in desc or 'Obstacle' in desc:
1423
- lines[-1] += " - Impassable"
1424
- else:
1425
- # Add brief description for walkable tiles
1426
- if 'Tall grass' in desc:
1427
- lines[-1] += " - Tall grass (wild encounters)"
1428
- elif 'Stairs' in desc or 'Warp' in desc:
1429
- lines[-1] += " - Stairs/Warp"
1430
- elif 'Door' in desc or 'Entrance' in desc:
1431
- lines[-1] += " - Door/Entrance"
1432
- elif 'Jump ledge' in desc and 'correct direction' in desc:
1433
- lines[-1] += " - Jump ledge (can jump this way)"
1434
-
1435
- return "\n".join(lines)
1436
-
1437
-
1438
- def get_party_health_summary(state_data):
1439
- """
1440
- Get a summary of party health status.
1441
-
1442
- Returns:
1443
- dict: Summary with healthy_count, total_count, critical_pokemon
1444
- """
1445
- player_data = state_data.get('player', {})
1446
- game_data = state_data.get('game', {})
1447
- party_data = player_data.get('party') or game_data.get('party')
1448
-
1449
- if not party_data:
1450
- return {"healthy_count": 0, "total_count": 0, "critical_pokemon": []}
1451
-
1452
- pokemon_list = []
1453
- if isinstance(party_data, dict) and party_data.get('pokemon'):
1454
- pokemon_list = party_data.get('pokemon', [])
1455
- elif isinstance(party_data, list):
1456
- pokemon_list = party_data
1457
-
1458
- healthy_count = 0
1459
- critical_pokemon = []
1460
-
1461
- for i, pokemon in enumerate(pokemon_list[:6]):
1462
- if pokemon:
1463
- hp = pokemon.get('current_hp', 0)
1464
- max_hp = pokemon.get('max_hp', 1)
1465
- status = pokemon.get('status', 'OK')
1466
- species = pokemon.get('species_name', pokemon.get('species', 'Unknown Pokemon'))
1467
-
1468
- # Check if healthy: has HP and no negative status (OK or Normal are both healthy)
1469
- if hp > 0 and status in ['OK', 'Normal']:
1470
- healthy_count += 1
1471
-
1472
- hp_percent = (hp / max_hp * 100) if max_hp > 0 else 0
1473
- # Mark as critical if low HP or has a status condition
1474
- if hp_percent < 25 or status not in ['OK', 'Normal']:
1475
- critical_pokemon.append(f"{species} ({hp_percent:.0f}% HP, {status})")
1476
-
1477
- return {
1478
- "healthy_count": healthy_count,
1479
- "total_count": len(pokemon_list),
1480
- "critical_pokemon": critical_pokemon
1481
- }