synth-ai 0.2.14__py3-none-any.whl → 0.4.1__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 (1091) hide show
  1. synth_ai/__init__.py +19 -40
  2. synth_ai/__main__.py +30 -3
  3. synth_ai/cli/__init__.py +105 -70
  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/baseline/__init__.py +12 -0
  25. synth_ai/cli/commands/baseline/core.py +636 -0
  26. synth_ai/cli/commands/baseline/list.py +94 -0
  27. synth_ai/cli/commands/demo/__init__.py +3 -0
  28. synth_ai/cli/commands/demo/core.py +153 -0
  29. synth_ai/cli/commands/eval/__init__.py +19 -0
  30. synth_ai/cli/commands/eval/core.py +1113 -0
  31. synth_ai/cli/commands/eval/errors.py +81 -0
  32. synth_ai/cli/commands/eval/validation.py +133 -0
  33. synth_ai/cli/commands/filter/__init__.py +12 -0
  34. synth_ai/cli/commands/filter/core.py +424 -0
  35. synth_ai/cli/commands/filter/errors.py +55 -0
  36. synth_ai/cli/commands/filter/validation.py +77 -0
  37. synth_ai/cli/commands/help/__init__.py +185 -0
  38. synth_ai/cli/commands/help/core.py +72 -0
  39. synth_ai/cli/commands/scan/__init__.py +19 -0
  40. synth_ai/cli/commands/scan/cloudflare_scanner.py +403 -0
  41. synth_ai/cli/commands/scan/core.py +344 -0
  42. synth_ai/cli/commands/scan/health_checker.py +242 -0
  43. synth_ai/cli/commands/scan/local_scanner.py +278 -0
  44. synth_ai/cli/commands/scan/models.py +83 -0
  45. synth_ai/cli/commands/smoke/__init__.py +7 -0
  46. synth_ai/cli/commands/smoke/core.py +1438 -0
  47. synth_ai/cli/commands/status/__init__.py +66 -0
  48. synth_ai/cli/commands/status/client.py +192 -0
  49. synth_ai/cli/commands/status/config.py +92 -0
  50. synth_ai/cli/commands/status/errors.py +20 -0
  51. synth_ai/cli/commands/status/formatters.py +164 -0
  52. synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
  53. synth_ai/cli/commands/status/subcommands/files.py +79 -0
  54. synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
  55. synth_ai/cli/commands/status/subcommands/models.py +79 -0
  56. synth_ai/cli/commands/status/subcommands/pricing.py +23 -0
  57. synth_ai/cli/commands/status/subcommands/runs.py +81 -0
  58. synth_ai/cli/commands/status/subcommands/session.py +182 -0
  59. synth_ai/cli/commands/status/subcommands/summary.py +47 -0
  60. synth_ai/cli/commands/status/subcommands/usage.py +203 -0
  61. synth_ai/cli/commands/status/utils.py +114 -0
  62. synth_ai/cli/commands/train/__init__.py +53 -0
  63. synth_ai/cli/commands/train/core.py +22 -0
  64. synth_ai/cli/commands/train/errors.py +117 -0
  65. synth_ai/cli/commands/train/judge_schemas.py +201 -0
  66. synth_ai/cli/commands/train/judge_validation.py +305 -0
  67. synth_ai/cli/commands/train/prompt_learning_validation.py +633 -0
  68. synth_ai/cli/commands/train/validation.py +392 -0
  69. synth_ai/cli/demo_apps/__init__.py +10 -0
  70. synth_ai/cli/demo_apps/core/__init__.py +28 -0
  71. synth_ai/cli/demo_apps/core/cli.py +1735 -0
  72. synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +55 -0
  73. synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +186 -0
  74. synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +74 -0
  75. synth_ai/cli/demo_apps/demo_registry.py +176 -0
  76. synth_ai/cli/demo_apps/demo_task_apps/core.py +440 -0
  77. synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
  78. synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
  79. synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +742 -0
  80. synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +39 -0
  81. synth_ai/cli/demo_apps/math/__init__.py +1 -0
  82. synth_ai/cli/demo_apps/math/_common.py +16 -0
  83. synth_ai/cli/demo_apps/math/app.py +38 -0
  84. synth_ai/cli/demo_apps/math/config.toml +76 -0
  85. synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
  86. synth_ai/cli/demo_apps/math/modal_task_app.py +702 -0
  87. synth_ai/cli/demo_apps/math/task_app_entry.py +53 -0
  88. synth_ai/cli/demo_apps/mipro/main.py +271 -0
  89. synth_ai/cli/demo_apps/mipro/task_app.py +933 -0
  90. synth_ai/cli/demo_apps/mipro/train_cfg.toml +92 -0
  91. synth_ai/cli/demos/__init__.py +12 -0
  92. synth_ai/cli/demos/demo.py +32 -0
  93. synth_ai/cli/demos/rl_demo.py +254 -0
  94. synth_ai/cli/deploy.py +216 -0
  95. synth_ai/cli/infra/__init__.py +14 -0
  96. synth_ai/cli/infra/balance.py +216 -0
  97. synth_ai/cli/infra/mcp.py +35 -0
  98. synth_ai/cli/infra/modal_app.py +36 -0
  99. synth_ai/cli/infra/setup.py +69 -0
  100. synth_ai/cli/infra/status.py +16 -0
  101. synth_ai/cli/infra/turso.py +77 -0
  102. synth_ai/cli/lib/__init__.py +10 -0
  103. synth_ai/cli/lib/agents.py +76 -0
  104. synth_ai/cli/lib/apps/modal_app.py +101 -0
  105. synth_ai/cli/lib/apps/task_app.py +643 -0
  106. synth_ai/cli/lib/bin.py +39 -0
  107. synth_ai/cli/lib/env.py +375 -0
  108. synth_ai/cli/lib/errors.py +85 -0
  109. synth_ai/cli/lib/modal.py +315 -0
  110. synth_ai/cli/lib/plotting.py +126 -0
  111. synth_ai/cli/lib/prompt_args.py +39 -0
  112. synth_ai/cli/lib/prompts.py +284 -0
  113. synth_ai/cli/lib/sqld.py +122 -0
  114. synth_ai/cli/lib/task_app_discovery.py +884 -0
  115. synth_ai/cli/lib/task_app_env.py +295 -0
  116. synth_ai/cli/lib/train_cfgs.py +300 -0
  117. synth_ai/cli/lib/tunnel_records.py +207 -0
  118. synth_ai/cli/local/__init__.py +14 -0
  119. synth_ai/cli/local/experiment_queue/__init__.py +72 -0
  120. synth_ai/cli/local/experiment_queue/api_schemas.py +221 -0
  121. synth_ai/cli/local/experiment_queue/celery_app.py +208 -0
  122. synth_ai/cli/local/experiment_queue/config.py +128 -0
  123. synth_ai/cli/local/experiment_queue/config_utils.py +272 -0
  124. synth_ai/cli/local/experiment_queue/database.py +175 -0
  125. synth_ai/cli/local/experiment_queue/dispatcher.py +119 -0
  126. synth_ai/cli/local/experiment_queue/models.py +231 -0
  127. synth_ai/cli/local/experiment_queue/progress_info.py +160 -0
  128. synth_ai/cli/local/experiment_queue/results.py +373 -0
  129. synth_ai/cli/local/experiment_queue/schemas.py +131 -0
  130. synth_ai/cli/local/experiment_queue/service.py +344 -0
  131. synth_ai/cli/local/experiment_queue/status.py +372 -0
  132. synth_ai/cli/local/experiment_queue/status_tracker.py +360 -0
  133. synth_ai/cli/local/experiment_queue/tasks.py +1984 -0
  134. synth_ai/cli/local/experiment_queue/trace_storage.py +65 -0
  135. synth_ai/cli/local/experiment_queue/validation.py +157 -0
  136. synth_ai/cli/local/session/__init__.py +92 -0
  137. synth_ai/cli/local/session/client.py +383 -0
  138. synth_ai/cli/local/session/constants.py +63 -0
  139. synth_ai/cli/local/session/exceptions.py +105 -0
  140. synth_ai/cli/local/session/manager.py +139 -0
  141. synth_ai/cli/local/session/models.py +89 -0
  142. synth_ai/cli/local/session/query.py +110 -0
  143. synth_ai/cli/root.py +30 -6
  144. synth_ai/cli/task_apps/__init__.py +26 -0
  145. synth_ai/cli/task_apps/commands.py +3153 -0
  146. synth_ai/cli/task_apps/deploy.py +7 -0
  147. synth_ai/cli/task_apps/list.py +26 -0
  148. synth_ai/cli/task_apps/main.py +36 -0
  149. synth_ai/cli/task_apps/modal_serve.py +11 -0
  150. synth_ai/cli/task_apps/serve.py +11 -0
  151. synth_ai/cli/training/__init__.py +8 -0
  152. synth_ai/cli/training/train.py +5 -0
  153. synth_ai/cli/training/train_cfg.py +34 -0
  154. synth_ai/cli/training/watch.py +506 -0
  155. synth_ai/cli/turso.py +34 -55
  156. synth_ai/cli/usage.py +159 -0
  157. synth_ai/cli/utils/__init__.py +8 -0
  158. synth_ai/cli/utils/experiments.py +235 -0
  159. synth_ai/cli/utils/queue.py +504 -0
  160. synth_ai/cli/utils/recent.py +133 -0
  161. synth_ai/cli/utils/traces.py +164 -0
  162. synth_ai/contracts/__init__.py +67 -0
  163. synth_ai/core/__init__.py +100 -0
  164. synth_ai/core/_utils/__init__.py +54 -0
  165. synth_ai/core/_utils/base_url.py +10 -0
  166. synth_ai/core/_utils/http.py +10 -0
  167. synth_ai/core/_utils/prompts.py +14 -0
  168. synth_ai/core/_utils/task_app_state.py +12 -0
  169. synth_ai/core/_utils/user_config.py +10 -0
  170. synth_ai/core/apps/common.py +116 -0
  171. synth_ai/core/auth.py +95 -0
  172. synth_ai/core/cfgs.py +240 -0
  173. synth_ai/core/config/__init__.py +16 -0
  174. synth_ai/core/config/base.py +168 -0
  175. synth_ai/core/config/resolver.py +89 -0
  176. synth_ai/core/env.py +220 -0
  177. synth_ai/core/errors.py +126 -0
  178. synth_ai/core/http.py +230 -0
  179. synth_ai/core/integrations/__init__.py +11 -0
  180. synth_ai/core/integrations/cloudflare.py +1710 -0
  181. synth_ai/core/integrations/mcp/__init__.py +6 -0
  182. synth_ai/core/integrations/mcp/__main__.py +8 -0
  183. synth_ai/core/integrations/mcp/claude.py +36 -0
  184. synth_ai/core/integrations/mcp/main.py +254 -0
  185. synth_ai/core/integrations/mcp/setup.py +100 -0
  186. synth_ai/core/integrations/modal.py +277 -0
  187. synth_ai/core/json.py +72 -0
  188. synth_ai/core/log_filter.py +99 -0
  189. synth_ai/core/logging.py +82 -0
  190. synth_ai/core/paths.py +107 -0
  191. synth_ai/core/pricing.py +109 -0
  192. synth_ai/core/process.py +233 -0
  193. synth_ai/core/ssl.py +25 -0
  194. synth_ai/core/storage/__init__.py +71 -0
  195. synth_ai/core/task_app_state.py +318 -0
  196. synth_ai/core/telemetry.py +282 -0
  197. synth_ai/core/tracing_v3/__init__.py +99 -0
  198. synth_ai/core/tracing_v3/abstractions.py +302 -0
  199. synth_ai/core/tracing_v3/config.py +229 -0
  200. synth_ai/core/tracing_v3/constants.py +21 -0
  201. synth_ai/core/tracing_v3/db_config.py +182 -0
  202. synth_ai/core/tracing_v3/decorators.py +401 -0
  203. synth_ai/core/tracing_v3/llm_call_record_helpers.py +437 -0
  204. synth_ai/core/tracing_v3/migration_helper.py +119 -0
  205. synth_ai/core/tracing_v3/session_tracer.py +542 -0
  206. synth_ai/core/tracing_v3/storage/base.py +211 -0
  207. synth_ai/core/tracing_v3/storage/config.py +109 -0
  208. synth_ai/core/tracing_v3/storage/factory.py +39 -0
  209. synth_ai/core/tracing_v3/trace_utils.py +326 -0
  210. synth_ai/core/tracing_v3/turso/daemon.py +278 -0
  211. synth_ai/core/tracing_v3/turso/models.py +470 -0
  212. synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
  213. synth_ai/core/tracing_v3/utils.py +108 -0
  214. synth_ai/core/urls.py +18 -0
  215. synth_ai/core/user_config.py +137 -0
  216. synth_ai/core/uvicorn.py +222 -0
  217. synth_ai/data/__init__.py +110 -0
  218. synth_ai/data/enums.py +141 -0
  219. synth_ai/data/rewards.py +152 -0
  220. synth_ai/data/specs.py +36 -0
  221. synth_ai/data/traces.py +35 -0
  222. synth_ai/products/__init__.py +6 -0
  223. synth_ai/products/graph_evolve/__init__.py +46 -0
  224. synth_ai/products/graph_evolve/client.py +226 -0
  225. synth_ai/products/graph_evolve/config.py +591 -0
  226. synth_ai/products/graph_evolve/converters/__init__.py +42 -0
  227. synth_ai/products/graph_evolve/converters/openai_sft.py +484 -0
  228. synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +109 -0
  229. synth_ai/products/graph_evolve/run.py +222 -0
  230. synth_ai/sdk/__init__.py +119 -0
  231. synth_ai/sdk/api/__init__.py +1 -0
  232. synth_ai/sdk/api/models/supported.py +514 -0
  233. synth_ai/sdk/api/research_agent/__init__.py +86 -0
  234. synth_ai/sdk/api/research_agent/cli.py +428 -0
  235. synth_ai/sdk/api/research_agent/config.py +357 -0
  236. synth_ai/sdk/api/research_agent/job.py +717 -0
  237. synth_ai/sdk/api/train/__init__.py +85 -0
  238. synth_ai/sdk/api/train/builders.py +895 -0
  239. synth_ai/sdk/api/train/cli.py +2188 -0
  240. synth_ai/sdk/api/train/config_finder.py +267 -0
  241. synth_ai/sdk/api/train/configs/__init__.py +65 -0
  242. synth_ai/sdk/api/train/configs/prompt_learning.py +1706 -0
  243. synth_ai/sdk/api/train/configs/rl.py +188 -0
  244. synth_ai/sdk/api/train/configs/sft.py +99 -0
  245. synth_ai/sdk/api/train/configs/shared.py +81 -0
  246. synth_ai/sdk/api/train/context_learning.py +312 -0
  247. synth_ai/sdk/api/train/env_resolver.py +418 -0
  248. synth_ai/sdk/api/train/graph_validators.py +216 -0
  249. synth_ai/sdk/api/train/graphgen.py +984 -0
  250. synth_ai/sdk/api/train/graphgen_models.py +823 -0
  251. synth_ai/sdk/api/train/graphgen_validators.py +109 -0
  252. synth_ai/sdk/api/train/pollers.py +124 -0
  253. synth_ai/sdk/api/train/progress/__init__.py +97 -0
  254. synth_ai/sdk/api/train/progress/dataclasses.py +569 -0
  255. synth_ai/sdk/api/train/progress/events.py +326 -0
  256. synth_ai/sdk/api/train/progress/results.py +428 -0
  257. synth_ai/sdk/api/train/progress/tracker.py +641 -0
  258. synth_ai/sdk/api/train/prompt_learning.py +470 -0
  259. synth_ai/sdk/api/train/rl.py +442 -0
  260. synth_ai/sdk/api/train/sft.py +396 -0
  261. synth_ai/sdk/api/train/summary.py +522 -0
  262. synth_ai/sdk/api/train/supported_algos.py +147 -0
  263. synth_ai/sdk/api/train/task_app.py +331 -0
  264. synth_ai/sdk/api/train/utils.py +279 -0
  265. synth_ai/sdk/api/train/validators.py +2424 -0
  266. synth_ai/sdk/baseline/__init__.py +25 -0
  267. synth_ai/sdk/baseline/config.py +209 -0
  268. synth_ai/sdk/baseline/discovery.py +216 -0
  269. synth_ai/sdk/baseline/execution.py +154 -0
  270. synth_ai/sdk/graphs/__init__.py +15 -0
  271. synth_ai/sdk/graphs/completions.py +570 -0
  272. synth_ai/sdk/inference/__init__.py +6 -0
  273. synth_ai/sdk/inference/client.py +128 -0
  274. synth_ai/sdk/jobs/__init__.py +16 -0
  275. synth_ai/sdk/jobs/client.py +371 -0
  276. synth_ai/sdk/judging/__init__.py +15 -0
  277. synth_ai/sdk/judging/base.py +24 -0
  278. synth_ai/sdk/judging/client.py +191 -0
  279. synth_ai/sdk/judging/schemas.py +222 -0
  280. synth_ai/sdk/learning/__init__.py +69 -0
  281. synth_ai/sdk/learning/client.py +240 -0
  282. synth_ai/sdk/learning/ft_client.py +7 -0
  283. synth_ai/sdk/learning/health.py +49 -0
  284. synth_ai/sdk/learning/jobs.py +202 -0
  285. synth_ai/sdk/learning/prompt_extraction.py +334 -0
  286. synth_ai/sdk/learning/prompt_learning_client.py +455 -0
  287. synth_ai/sdk/learning/prompt_learning_types.py +185 -0
  288. synth_ai/sdk/learning/rl/client.py +268 -0
  289. synth_ai/sdk/learning/rl/contracts.py +27 -0
  290. synth_ai/sdk/learning/rl/env_keys.py +166 -0
  291. synth_ai/sdk/learning/rl/secrets.py +13 -0
  292. synth_ai/sdk/learning/sft/client.py +95 -0
  293. synth_ai/sdk/learning/sft/config.py +270 -0
  294. synth_ai/sdk/learning/sft/data.py +698 -0
  295. synth_ai/sdk/learning/validators.py +52 -0
  296. synth_ai/sdk/research_agent/__init__.py +34 -0
  297. synth_ai/sdk/research_agent/container_builder.py +328 -0
  298. synth_ai/sdk/research_agent/container_spec.py +198 -0
  299. synth_ai/sdk/research_agent/defaults.py +34 -0
  300. synth_ai/sdk/research_agent/results_collector.py +69 -0
  301. synth_ai/sdk/specs/__init__.py +46 -0
  302. synth_ai/sdk/specs/dataclasses.py +149 -0
  303. synth_ai/sdk/specs/loader.py +144 -0
  304. synth_ai/sdk/specs/serializer.py +199 -0
  305. synth_ai/sdk/specs/validation.py +250 -0
  306. synth_ai/sdk/streaming/__init__.py +35 -0
  307. synth_ai/sdk/streaming/config.py +94 -0
  308. synth_ai/sdk/streaming/handlers.py +1997 -0
  309. synth_ai/sdk/streaming/streamer.py +704 -0
  310. synth_ai/sdk/streaming/types.py +112 -0
  311. synth_ai/sdk/task/__init__.py +151 -0
  312. synth_ai/sdk/task/apps/__init__.py +133 -0
  313. synth_ai/sdk/task/config.py +261 -0
  314. synth_ai/sdk/task/contracts.py +298 -0
  315. synth_ai/sdk/task/datasets.py +108 -0
  316. synth_ai/sdk/task/in_process.py +1190 -0
  317. synth_ai/sdk/task/in_process_runner.py +309 -0
  318. synth_ai/sdk/task/inference_api.py +299 -0
  319. synth_ai/sdk/task/proxy.py +287 -0
  320. synth_ai/sdk/task/rubrics/__init__.py +55 -0
  321. synth_ai/sdk/task/rubrics/loaders.py +156 -0
  322. synth_ai/sdk/task/rubrics.py +219 -0
  323. synth_ai/sdk/task/server.py +580 -0
  324. synth_ai/sdk/task/trace_correlation_helpers.py +506 -0
  325. synth_ai/sdk/task/tracing_utils.py +95 -0
  326. synth_ai/sdk/task/validators.py +456 -0
  327. synth_ai/sdk/tracing/__init__.py +39 -0
  328. synth_ai/sdk/training/__init__.py +102 -0
  329. synth_ai/sdk/usage/__init__.py +37 -0
  330. synth_ai/sdk/usage/client.py +171 -0
  331. synth_ai/sdk/usage/models.py +261 -0
  332. synth_ai/utils/__init__.py +213 -0
  333. synth_ai-0.4.1.dist-info/METADATA +195 -0
  334. synth_ai-0.4.1.dist-info/RECORD +379 -0
  335. synth_ai-0.4.1.dist-info/top_level.txt +1 -0
  336. examples/__init__.py +0 -16
  337. examples/analyze_semantic_words.sh +0 -17
  338. examples/crafter_debug_render.py +0 -186
  339. examples/dev/qwen3_32b_qlora_4xh100.toml +0 -40
  340. examples/multi_step/configs/README_verilog_rl.md +0 -77
  341. examples/multi_step/configs/VERILOG_REWARDS.md +0 -90
  342. examples/multi_step/configs/VERILOG_RL_CHECKLIST.md +0 -183
  343. examples/multi_step/configs/crafter_eval_synth_qwen4b.toml +0 -35
  344. examples/multi_step/configs/crafter_eval_text_only_groq_qwen32b.toml +0 -36
  345. examples/multi_step/configs/crafter_rl_outcome.toml +0 -74
  346. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +0 -187
  347. examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +0 -83
  348. examples/multi_step/configs/crafter_rl_stepwise_simple.toml +0 -78
  349. examples/multi_step/configs/crafter_synth_backend.md +0 -40
  350. examples/multi_step/configs/verilog_eval_groq_qwen32b.toml +0 -31
  351. examples/multi_step/configs/verilog_eval_synth_qwen8b.toml +0 -33
  352. examples/multi_step/configs/verilog_rl_lora.toml +0 -190
  353. examples/multi_step/crafter_rl_lora.md +0 -70
  354. examples/multi_step/judges/crafter_backend_judge.py +0 -220
  355. examples/multi_step/judges/verilog_backend_judge.py +0 -234
  356. examples/multi_step/readme.md +0 -48
  357. examples/multi_step/sse_metrics_streaming_notes.md +0 -357
  358. examples/multi_step/task_app_config_notes.md +0 -494
  359. examples/multi_step/verilog_rl_lora.md +0 -218
  360. examples/qwen_coder/README.md +0 -102
  361. examples/qwen_coder/_shared.py +0 -113
  362. examples/qwen_coder/configs/coder_lora_30b.toml +0 -61
  363. examples/qwen_coder/configs/coder_lora_4b.toml +0 -57
  364. examples/qwen_coder/configs/coder_lora_small.toml +0 -58
  365. examples/qwen_coder/generate_dataset.py +0 -98
  366. examples/qwen_coder/infer_ft_smoke.py +0 -65
  367. examples/qwen_coder/infer_prod_proxy.py +0 -73
  368. examples/qwen_coder/infer_via_synth.py +0 -87
  369. examples/qwen_coder/scripts/infer_coder.sh +0 -19
  370. examples/qwen_coder/scripts/train_coder_30b.sh +0 -22
  371. examples/qwen_coder/sft_full_17b.py +0 -103
  372. examples/qwen_coder/sft_lora_30b.py +0 -110
  373. examples/qwen_coder/subset_jsonl.py +0 -39
  374. examples/qwen_coder/todos.md +0 -38
  375. examples/qwen_coder/validate_jsonl.py +0 -60
  376. examples/rl/README.md +0 -169
  377. examples/rl/download_dataset.py +0 -80
  378. examples/run_crafter_demo.sh +0 -10
  379. examples/sft/README.md +0 -139
  380. examples/sft/configs/crafter_fft_qwen0p6b.toml +0 -44
  381. examples/sft/configs/crafter_lora_qwen0p6b.toml +0 -45
  382. examples/sft/evaluate.py +0 -119
  383. examples/sft/export_dataset.py +0 -117
  384. examples/sft/generate_traces.py +0 -164
  385. examples/swe/__init__.py +0 -12
  386. examples/swe/task_app/README.md +0 -105
  387. examples/swe/task_app/__init__.py +0 -2
  388. examples/swe/task_app/grpo_swe_mini.py +0 -601
  389. examples/swe/task_app/grpo_swe_mini_task_app.py +0 -136
  390. examples/swe/task_app/hosted/README.md +0 -173
  391. examples/swe/task_app/hosted/__init__.py +0 -5
  392. examples/swe/task_app/hosted/branching.py +0 -143
  393. examples/swe/task_app/hosted/environment_routes.py +0 -1289
  394. examples/swe/task_app/hosted/envs/__init__.py +0 -1
  395. examples/swe/task_app/hosted/envs/crafter/__init__.py +0 -6
  396. examples/swe/task_app/hosted/envs/crafter/app.py +0 -1
  397. examples/swe/task_app/hosted/envs/crafter/environment.py +0 -522
  398. examples/swe/task_app/hosted/envs/crafter/policy.py +0 -478
  399. examples/swe/task_app/hosted/envs/crafter/react_agent.py +0 -108
  400. examples/swe/task_app/hosted/envs/crafter/shared.py +0 -305
  401. examples/swe/task_app/hosted/envs/crafter/tools.py +0 -47
  402. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +0 -8
  403. examples/swe/task_app/hosted/envs/mini_swe/environment.py +0 -1164
  404. examples/swe/task_app/hosted/envs/mini_swe/policy.py +0 -355
  405. examples/swe/task_app/hosted/envs/mini_swe/shared.py +0 -83
  406. examples/swe/task_app/hosted/envs/mini_swe/tools.py +0 -96
  407. examples/swe/task_app/hosted/hosted_app.py +0 -204
  408. examples/swe/task_app/hosted/inference/__init__.py +0 -5
  409. examples/swe/task_app/hosted/inference/openai_client.py +0 -618
  410. examples/swe/task_app/hosted/main.py +0 -100
  411. examples/swe/task_app/hosted/policy_routes.py +0 -1079
  412. examples/swe/task_app/hosted/registry.py +0 -195
  413. examples/swe/task_app/hosted/rollout.py +0 -1911
  414. examples/swe/task_app/hosted/storage/__init__.py +0 -5
  415. examples/swe/task_app/hosted/storage/volume.py +0 -211
  416. examples/swe/task_app/hosted/test_agents.py +0 -161
  417. examples/swe/task_app/hosted/test_service.py +0 -136
  418. examples/swe/task_app/hosted/utils.py +0 -62
  419. examples/task_apps/IMAGE_ONLY_EVAL_QUICKSTART.md +0 -258
  420. examples/task_apps/TESTING.md +0 -275
  421. examples/task_apps/crafter/CREATE_SFT_DATASET.md +0 -273
  422. examples/task_apps/crafter/EVAL_IMAGE_ONLY_RESULTS.md +0 -152
  423. examples/task_apps/crafter/FILTER_COMMAND_STATUS.md +0 -174
  424. examples/task_apps/crafter/FILTER_COMMAND_SUCCESS.md +0 -268
  425. examples/task_apps/crafter/QUERY_EXAMPLES.md +0 -203
  426. examples/task_apps/crafter/README_IMAGE_ONLY_EVAL.md +0 -316
  427. examples/task_apps/crafter/__init__.py +0 -0
  428. examples/task_apps/crafter/eval_image_only_gpt4o.toml +0 -28
  429. examples/task_apps/crafter/eval_text_only_groq_llama.toml +0 -36
  430. examples/task_apps/crafter/filter_sft_dataset.toml +0 -16
  431. examples/task_apps/crafter/task_app/README.md +0 -42
  432. examples/task_apps/crafter/task_app/__init__.py +0 -5
  433. examples/task_apps/crafter/task_app/grpo_crafter.py +0 -973
  434. examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +0 -146
  435. examples/task_apps/crafter/task_app/synth_envs_hosted/README.md +0 -173
  436. examples/task_apps/crafter/task_app/synth_envs_hosted/__init__.py +0 -5
  437. examples/task_apps/crafter/task_app/synth_envs_hosted/branching.py +0 -143
  438. examples/task_apps/crafter/task_app/synth_envs_hosted/environment_routes.py +0 -1226
  439. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/__init__.py +0 -1
  440. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -6
  441. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/app.py +0 -1
  442. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -532
  443. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +0 -547
  444. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -123
  445. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -305
  446. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -47
  447. examples/task_apps/crafter/task_app/synth_envs_hosted/hosted_app.py +0 -204
  448. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/__init__.py +0 -5
  449. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +0 -704
  450. examples/task_apps/crafter/task_app/synth_envs_hosted/main.py +0 -100
  451. examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +0 -1152
  452. examples/task_apps/crafter/task_app/synth_envs_hosted/registry.py +0 -195
  453. examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +0 -2160
  454. examples/task_apps/crafter/task_app/synth_envs_hosted/storage/__init__.py +0 -5
  455. examples/task_apps/crafter/task_app/synth_envs_hosted/storage/volume.py +0 -211
  456. examples/task_apps/crafter/task_app/synth_envs_hosted/test_agents.py +0 -161
  457. examples/task_apps/crafter/task_app/synth_envs_hosted/test_service.py +0 -136
  458. examples/task_apps/crafter/task_app/synth_envs_hosted/utils.py +0 -218
  459. examples/task_apps/dev/pokemon_emerald/__init__.py +0 -2
  460. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +0 -811
  461. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +0 -120
  462. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +0 -160
  463. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +0 -155
  464. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +0 -69
  465. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +0 -96
  466. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +0 -1502
  467. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +0 -4
  468. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +0 -68
  469. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +0 -216
  470. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +0 -35
  471. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +0 -631
  472. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +0 -1544
  473. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +0 -1428
  474. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +0 -4848
  475. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +0 -41
  476. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +0 -298
  477. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +0 -95
  478. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +0 -204
  479. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
  480. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +0 -2152
  481. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +0 -429
  482. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +0 -155
  483. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +0 -78
  484. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
  485. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +0 -122
  486. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +0 -76
  487. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +0 -413
  488. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +0 -204
  489. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +0 -133
  490. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +0 -229
  491. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +0 -300
  492. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +0 -205
  493. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +0 -200
  494. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +0 -284
  495. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +0 -468
  496. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +0 -575
  497. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +0 -311
  498. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +0 -259
  499. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
  500. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +0 -372
  501. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +0 -296
  502. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +0 -275
  503. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +0 -22
  504. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +0 -44
  505. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +0 -514
  506. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +0 -415
  507. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +0 -1763
  508. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +0 -33
  509. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +0 -106
  510. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +0 -334
  511. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +0 -1020
  512. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +0 -188
  513. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +0 -1481
  514. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +0 -862
  515. examples/task_apps/dev/pokemon_emerald/modal_app.py +0 -114
  516. examples/task_apps/dev/pokemon_emerald/task_app/README.md +0 -81
  517. examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +0 -6
  518. examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +0 -685
  519. examples/task_apps/enron/__init__.py +0 -1
  520. examples/task_apps/enron/eval_groq_qwen32.toml +0 -16
  521. examples/task_apps/enron/filter_sft.toml +0 -5
  522. examples/task_apps/enron/task_app/README.md +0 -14
  523. examples/task_apps/enron/task_app/__init__.py +0 -1
  524. examples/task_apps/enron/task_app/grpo_enron.py +0 -906
  525. examples/task_apps/enron/task_app/grpo_enron_task_app.py +0 -146
  526. examples/task_apps/enron/tests/__init__.py +0 -4
  527. examples/task_apps/enron/tests/conftest.py +0 -115
  528. examples/task_apps/enron/tests/integration/__init__.py +0 -4
  529. examples/task_apps/enron/tests/integration/test_enron_eval.py +0 -179
  530. examples/task_apps/enron/tests/integration/test_enron_rollout.py +0 -135
  531. examples/task_apps/enron/tests/unit/__init__.py +0 -4
  532. examples/task_apps/enron/tests/unit/test_enron_environment.py +0 -126
  533. examples/task_apps/math/README.md +0 -22
  534. examples/task_apps/math/__init__.py +0 -0
  535. examples/task_apps/math/math_single_step.py +0 -1000
  536. examples/task_apps/math/math_task_app.py +0 -115
  537. examples/task_apps/pokemon_battle/__init__.py +0 -2
  538. examples/task_apps/pokemon_battle/modal_app.py +0 -104
  539. examples/task_apps/pokemon_battle/task_app/README.md +0 -68
  540. examples/task_apps/pokemon_battle/task_app/__init__.py +0 -6
  541. examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +0 -932
  542. examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_COMPLETE.md +0 -283
  543. examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_STATUS.md +0 -155
  544. examples/task_apps/pokemon_red/README.md +0 -357
  545. examples/task_apps/pokemon_red/README_IMAGE_ONLY_EVAL.md +0 -415
  546. examples/task_apps/pokemon_red/__init__.py +0 -3
  547. examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +0 -29
  548. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +0 -225
  549. examples/task_apps/pokemon_red/pallet_town_rl_config.toml +0 -75
  550. examples/task_apps/pokemon_red/task_app.py +0 -799
  551. examples/task_apps/pokemon_red/test_pallet_town_rewards.py +0 -193
  552. examples/task_apps/sokoban/README.md +0 -307
  553. examples/task_apps/sokoban/__init__.py +0 -3
  554. examples/task_apps/sokoban/eval_groq_qwen32.toml +0 -16
  555. examples/task_apps/sokoban/eval_openai_gpt5.toml +0 -16
  556. examples/task_apps/sokoban/filter_sft.toml +0 -5
  557. examples/task_apps/sokoban/task_app.py +0 -1058
  558. examples/task_apps/sokoban/tests/__init__.py +0 -4
  559. examples/task_apps/sokoban/tests/conftest.py +0 -113
  560. examples/task_apps/sokoban/tests/integration/__init__.py +0 -4
  561. examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +0 -57
  562. examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +0 -198
  563. examples/task_apps/sokoban/tests/unit/__init__.py +0 -4
  564. examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +0 -114
  565. examples/task_apps/verilog/__init__.py +0 -1
  566. examples/task_apps/verilog/eval_groq_qwen32b.toml +0 -24
  567. examples/task_apps/verilog/filter_sft.toml +0 -5
  568. examples/task_apps/verilog/task_app/README.md +0 -12
  569. examples/task_apps/verilog/task_app/__init__.py +0 -1
  570. examples/task_apps/verilog/task_app/grpo_verilog.py +0 -1166
  571. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +0 -145
  572. examples/task_apps/verilog/tests/__init__.py +0 -4
  573. examples/task_apps/verilog/tests/conftest.py +0 -115
  574. examples/task_apps/verilog/tests/integration/__init__.py +0 -4
  575. examples/task_apps/verilog/tests/integration/test_verilog_eval.py +0 -181
  576. examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +0 -55
  577. examples/task_apps/verilog/tests/unit/__init__.py +0 -4
  578. examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +0 -118
  579. examples/vlm/PROPOSAL.md +0 -53
  580. examples/vlm/README.md +0 -68
  581. examples/vlm/configs/crafter_vlm_gpt4o.toml +0 -44
  582. examples/vlm/crafter_image_only_agent.py +0 -207
  583. examples/vlm/crafter_openai_vlm_agent.py +0 -277
  584. examples/vlm/filter_image_rows.py +0 -63
  585. examples/vlm/run_crafter_vlm_benchmark.py +0 -316
  586. examples/warming_up_to_rl/analyze_trace_db.py +0 -422
  587. examples/warming_up_to_rl/configs/crafter_fft.toml +0 -48
  588. examples/warming_up_to_rl/configs/crafter_fft_4b.toml +0 -54
  589. examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +0 -20
  590. examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +0 -13
  591. examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +0 -23
  592. examples/warming_up_to_rl/configs/eval_stepwise_complex.toml +0 -35
  593. examples/warming_up_to_rl/configs/eval_stepwise_consistent.toml +0 -26
  594. examples/warming_up_to_rl/configs/eval_stepwise_per_achievement.toml +0 -36
  595. examples/warming_up_to_rl/configs/eval_stepwise_simple.toml +0 -32
  596. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +0 -83
  597. examples/warming_up_to_rl/configs/rl_from_ft.toml +0 -56
  598. examples/warming_up_to_rl/export_trace_sft.py +0 -723
  599. examples/warming_up_to_rl/groq_test.py +0 -97
  600. examples/warming_up_to_rl/manage_secrets.py +0 -131
  601. examples/warming_up_to_rl/old/event_rewards.md +0 -234
  602. examples/warming_up_to_rl/old/notes.md +0 -73
  603. examples/warming_up_to_rl/readme.md +0 -179
  604. examples/warming_up_to_rl/run_eval.py +0 -736
  605. examples/warming_up_to_rl/run_fft_and_save.py +0 -380
  606. examples/warming_up_to_rl/run_local_rollout.py +0 -239
  607. examples/warming_up_to_rl/run_local_rollout_modal.py +0 -248
  608. examples/warming_up_to_rl/run_local_rollout_parallel.py +0 -405
  609. examples/warming_up_to_rl/run_local_rollout_traced.py +0 -477
  610. examples/warming_up_to_rl/run_rl_and_save.py +0 -124
  611. examples/warming_up_to_rl/run_rollout_remote.py +0 -156
  612. examples/workflows/__init__.py +0 -0
  613. examples/workflows/math_rl/__init__.py +0 -0
  614. examples/workflows/math_rl/configs/eval_base_qwen.toml +0 -15
  615. examples/workflows/math_rl/configs/eval_rl_qwen.toml +0 -11
  616. examples/workflows/math_rl/configs/rl_from_base_qwen.toml +0 -35
  617. examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +0 -74
  618. examples/workflows/math_rl/configs/rl_from_ft_qwen.toml +0 -35
  619. examples/workflows/math_rl/download_dataset.py +0 -80
  620. examples/workflows/math_rl/run_eval.py +0 -436
  621. examples/workflows/math_rl/run_rl_and_save.py +0 -111
  622. synth_ai/api/models/supported.py +0 -377
  623. synth_ai/api/train/__init__.py +0 -5
  624. synth_ai/api/train/builders.py +0 -351
  625. synth_ai/api/train/cli.py +0 -635
  626. synth_ai/api/train/config_finder.py +0 -228
  627. synth_ai/api/train/configs/__init__.py +0 -44
  628. synth_ai/api/train/configs/rl.py +0 -134
  629. synth_ai/api/train/configs/sft.py +0 -95
  630. synth_ai/api/train/configs/shared.py +0 -24
  631. synth_ai/api/train/env_resolver.py +0 -349
  632. synth_ai/api/train/pollers.py +0 -75
  633. synth_ai/api/train/supported_algos.py +0 -147
  634. synth_ai/api/train/task_app.py +0 -195
  635. synth_ai/api/train/utils.py +0 -225
  636. synth_ai/cli/_modal_wrapper.py +0 -29
  637. synth_ai/cli/_storage.py +0 -20
  638. synth_ai/cli/_typer_patch.py +0 -49
  639. synth_ai/cli/_validate_task_app.py +0 -11
  640. synth_ai/cli/balance.py +0 -216
  641. synth_ai/cli/calc.py +0 -84
  642. synth_ai/cli/demo.py +0 -165
  643. synth_ai/cli/legacy_root_backup.py +0 -468
  644. synth_ai/cli/man.py +0 -106
  645. synth_ai/cli/recent.py +0 -132
  646. synth_ai/cli/rl_demo.py +0 -254
  647. synth_ai/cli/status.py +0 -134
  648. synth_ai/cli/task_apps.py +0 -4523
  649. synth_ai/cli/traces.py +0 -164
  650. synth_ai/cli/tui.py +0 -57
  651. synth_ai/cli/watch.py +0 -506
  652. synth_ai/compound/cais.py +0 -0
  653. synth_ai/config/base_url.py +0 -107
  654. synth_ai/core/experiment.py +0 -13
  655. synth_ai/core/system.py +0 -15
  656. synth_ai/demo_registry.py +0 -295
  657. synth_ai/demos/core/__init__.py +0 -1
  658. synth_ai/demos/core/cli.py +0 -1718
  659. synth_ai/demos/demo_task_apps/core.py +0 -440
  660. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +0 -184
  661. synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +0 -22
  662. synth_ai/demos/demo_task_apps/math/modal_task_app.py +0 -739
  663. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -37
  664. synth_ai/environments/__init__.py +0 -31
  665. synth_ai/environments/environment/__init__.py +0 -1
  666. synth_ai/environments/environment/artifacts/__init__.py +0 -1
  667. synth_ai/environments/environment/artifacts/base.py +0 -52
  668. synth_ai/environments/environment/core.py +0 -67
  669. synth_ai/environments/environment/db/__init__.py +0 -1
  670. synth_ai/environments/environment/db/sqlite.py +0 -45
  671. synth_ai/environments/environment/registry.py +0 -233
  672. synth_ai/environments/environment/resources/sqlite.py +0 -45
  673. synth_ai/environments/environment/results.py +0 -1
  674. synth_ai/environments/environment/rewards/__init__.py +0 -1
  675. synth_ai/environments/environment/rewards/core.py +0 -29
  676. synth_ai/environments/environment/shared_engine.py +0 -26
  677. synth_ai/environments/environment/tools/__init__.py +0 -200
  678. synth_ai/environments/examples/__init__.py +0 -1
  679. synth_ai/environments/examples/bandit/__init__.py +0 -33
  680. synth_ai/environments/examples/bandit/engine.py +0 -302
  681. synth_ai/environments/examples/bandit/environment.py +0 -194
  682. synth_ai/environments/examples/bandit/taskset.py +0 -200
  683. synth_ai/environments/examples/crafter_classic/__init__.py +0 -8
  684. synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +0 -250
  685. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +0 -59
  686. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +0 -152
  687. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +0 -24
  688. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +0 -1194
  689. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +0 -56
  690. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +0 -32
  691. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  692. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +0 -384
  693. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +0 -53
  694. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +0 -178
  695. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +0 -222
  696. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +0 -183
  697. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +0 -210
  698. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +0 -206
  699. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +0 -49
  700. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +0 -64
  701. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +0 -88
  702. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +0 -77
  703. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +0 -324
  704. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  705. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +0 -362
  706. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +0 -49
  707. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +0 -332
  708. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +0 -97
  709. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +0 -217
  710. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +0 -87
  711. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +0 -88
  712. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +0 -195
  713. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +0 -400
  714. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +0 -195
  715. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +0 -56
  716. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +0 -858
  717. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +0 -52
  718. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +0 -874
  719. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +0 -1412
  720. synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +0 -216
  721. synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +0 -296
  722. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +0 -58
  723. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +0 -464
  724. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +0 -152
  725. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +0 -51
  726. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +0 -1412
  727. synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +0 -112
  728. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +0 -203
  729. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +0 -305
  730. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +0 -126
  731. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +0 -94
  732. synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +0 -142
  733. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +0 -26
  734. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +0 -984
  735. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +0 -724
  736. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +0 -386
  737. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +0 -205
  738. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +0 -150
  739. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +0 -283
  740. synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +0 -280
  741. synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +0 -456
  742. synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +0 -166
  743. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +0 -102
  744. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +0 -128
  745. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +0 -655
  746. synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +0 -202
  747. synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +0 -166
  748. synth_ai/environments/examples/crafter_classic/config_logging.py +0 -111
  749. synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
  750. synth_ai/environments/examples/crafter_classic/engine.py +0 -579
  751. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +0 -64
  752. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +0 -6
  753. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +0 -75
  754. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +0 -267
  755. synth_ai/environments/examples/crafter_classic/environment.py +0 -495
  756. synth_ai/environments/examples/crafter_classic/taskset.py +0 -233
  757. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +0 -228
  758. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +0 -299
  759. synth_ai/environments/examples/crafter_custom/__init__.py +0 -4
  760. synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +0 -1
  761. synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +0 -202
  762. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +0 -7
  763. synth_ai/environments/examples/crafter_custom/crafter/config.py +0 -182
  764. synth_ai/environments/examples/crafter_custom/crafter/constants.py +0 -8
  765. synth_ai/environments/examples/crafter_custom/crafter/engine.py +0 -269
  766. synth_ai/environments/examples/crafter_custom/crafter/env.py +0 -262
  767. synth_ai/environments/examples/crafter_custom/crafter/objects.py +0 -417
  768. synth_ai/environments/examples/crafter_custom/crafter/recorder.py +0 -187
  769. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +0 -118
  770. synth_ai/environments/examples/crafter_custom/dataset_builder.py +0 -373
  771. synth_ai/environments/examples/crafter_custom/environment.py +0 -312
  772. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +0 -159
  773. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +0 -158
  774. synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +0 -71
  775. synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +0 -105
  776. synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +0 -119
  777. synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +0 -52
  778. synth_ai/environments/examples/crafter_custom/run_dataset.py +0 -305
  779. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +0 -156
  780. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +0 -281
  781. synth_ai/environments/examples/enron/art_helpers/types_enron.py +0 -25
  782. synth_ai/environments/examples/enron/engine.py +0 -300
  783. synth_ai/environments/examples/enron/environment.py +0 -234
  784. synth_ai/environments/examples/enron/taskset.py +0 -112
  785. synth_ai/environments/examples/enron/units/keyword_stats.py +0 -112
  786. synth_ai/environments/examples/minigrid/__init__.py +0 -48
  787. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +0 -1188
  788. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +0 -48
  789. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +0 -562
  790. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +0 -221
  791. synth_ai/environments/examples/minigrid/engine.py +0 -589
  792. synth_ai/environments/examples/minigrid/environment.py +0 -274
  793. synth_ai/environments/examples/minigrid/environment_mapping.py +0 -242
  794. synth_ai/environments/examples/minigrid/puzzle_loader.py +0 -417
  795. synth_ai/environments/examples/minigrid/taskset.py +0 -583
  796. synth_ai/environments/examples/nethack/__init__.py +0 -7
  797. synth_ai/environments/examples/nethack/achievements.py +0 -337
  798. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +0 -981
  799. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +0 -74
  800. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +0 -831
  801. synth_ai/environments/examples/nethack/engine.py +0 -739
  802. synth_ai/environments/examples/nethack/environment.py +0 -256
  803. synth_ai/environments/examples/nethack/helpers/__init__.py +0 -41
  804. synth_ai/environments/examples/nethack/helpers/action_mapping.py +0 -301
  805. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +0 -402
  806. synth_ai/environments/examples/nethack/helpers/observation_utils.py +0 -433
  807. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +0 -200
  808. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +0 -269
  809. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +0 -308
  810. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +0 -431
  811. synth_ai/environments/examples/nethack/taskset.py +0 -323
  812. synth_ai/environments/examples/red/__init__.py +0 -7
  813. synth_ai/environments/examples/red/agent_demos/__init__.py +0 -1
  814. synth_ai/environments/examples/red/config_logging.py +0 -110
  815. synth_ai/environments/examples/red/engine.py +0 -721
  816. synth_ai/environments/examples/red/engine_helpers/__init__.py +0 -1
  817. synth_ai/environments/examples/red/engine_helpers/memory_map.py +0 -35
  818. synth_ai/environments/examples/red/engine_helpers/reward_components.py +0 -276
  819. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +0 -142
  820. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +0 -57
  821. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +0 -284
  822. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +0 -150
  823. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +0 -138
  824. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +0 -57
  825. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +0 -331
  826. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +0 -121
  827. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +0 -477
  828. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +0 -559
  829. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +0 -313
  830. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +0 -148
  831. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +0 -247
  832. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +0 -368
  833. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +0 -172
  834. synth_ai/environments/examples/red/environment.py +0 -298
  835. synth_ai/environments/examples/red/taskset.py +0 -79
  836. synth_ai/environments/examples/red/units/__init__.py +0 -1
  837. synth_ai/environments/examples/sokoban/__init__.py +0 -1
  838. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +0 -899
  839. synth_ai/environments/examples/sokoban/engine.py +0 -678
  840. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +0 -1
  841. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +0 -657
  842. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +0 -18
  843. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +0 -3
  844. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +0 -131
  845. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +0 -370
  846. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +0 -332
  847. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +0 -306
  848. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +0 -67
  849. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +0 -115
  850. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +0 -123
  851. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +0 -394
  852. synth_ai/environments/examples/sokoban/environment.py +0 -229
  853. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +0 -440
  854. synth_ai/environments/examples/sokoban/puzzle_loader.py +0 -312
  855. synth_ai/environments/examples/sokoban/taskset.py +0 -544
  856. synth_ai/environments/examples/tictactoe/__init__.py +0 -1
  857. synth_ai/environments/examples/tictactoe/engine.py +0 -368
  858. synth_ai/environments/examples/tictactoe/environment.py +0 -240
  859. synth_ai/environments/examples/tictactoe/taskset.py +0 -215
  860. synth_ai/environments/examples/verilog/__init__.py +0 -10
  861. synth_ai/environments/examples/verilog/engine.py +0 -421
  862. synth_ai/environments/examples/verilog/environment.py +0 -350
  863. synth_ai/environments/examples/verilog/taskset.py +0 -420
  864. synth_ai/environments/examples/wordle/__init__.py +0 -29
  865. synth_ai/environments/examples/wordle/engine.py +0 -398
  866. synth_ai/environments/examples/wordle/environment.py +0 -159
  867. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +0 -75
  868. synth_ai/environments/examples/wordle/taskset.py +0 -230
  869. synth_ai/environments/reproducibility/core.py +0 -42
  870. synth_ai/environments/reproducibility/helpers.py +0 -0
  871. synth_ai/environments/reproducibility/tree.py +0 -363
  872. synth_ai/environments/service/app.py +0 -97
  873. synth_ai/environments/service/core_routes.py +0 -1021
  874. synth_ai/environments/service/external_registry.py +0 -56
  875. synth_ai/environments/service/registry.py +0 -9
  876. synth_ai/environments/stateful/__init__.py +0 -1
  877. synth_ai/environments/stateful/core.py +0 -163
  878. synth_ai/environments/stateful/engine.py +0 -21
  879. synth_ai/environments/stateful/state.py +0 -7
  880. synth_ai/environments/tasks/api.py +0 -19
  881. synth_ai/environments/tasks/core.py +0 -81
  882. synth_ai/environments/tasks/filters.py +0 -40
  883. synth_ai/environments/tasks/utils.py +0 -90
  884. synth_ai/environments/v0_observability/history.py +0 -3
  885. synth_ai/environments/v0_observability/log.py +0 -2
  886. synth_ai/evals/__init__.py +0 -15
  887. synth_ai/evals/base.py +0 -13
  888. synth_ai/evals/client.py +0 -82
  889. synth_ai/handshake.py +0 -109
  890. synth_ai/http.py +0 -26
  891. synth_ai/http_client.py +0 -136
  892. synth_ai/inference/__init__.py +0 -5
  893. synth_ai/inference/client.py +0 -34
  894. synth_ai/jobs/client.py +0 -295
  895. synth_ai/judge_schemas.py +0 -127
  896. synth_ai/learning/__init__.py +0 -59
  897. synth_ai/learning/client.py +0 -241
  898. synth_ai/learning/ft_client.py +0 -7
  899. synth_ai/learning/health.py +0 -49
  900. synth_ai/learning/jobs.py +0 -201
  901. synth_ai/learning/rl/client.py +0 -267
  902. synth_ai/learning/rl/contracts.py +0 -27
  903. synth_ai/learning/rl/env_keys.py +0 -166
  904. synth_ai/learning/rl/secrets.py +0 -13
  905. synth_ai/learning/sft/client.py +0 -68
  906. synth_ai/learning/sft/config.py +0 -270
  907. synth_ai/learning/sft/data.py +0 -295
  908. synth_ai/learning/validators.py +0 -49
  909. synth_ai/lm/__init__.py +0 -25
  910. synth_ai/task/__init__.py +0 -121
  911. synth_ai/task/apps/__init__.py +0 -129
  912. synth_ai/task/config.py +0 -257
  913. synth_ai/task/contracts.py +0 -236
  914. synth_ai/task/datasets.py +0 -108
  915. synth_ai/task/proxy.py +0 -251
  916. synth_ai/task/rubrics/__init__.py +0 -56
  917. synth_ai/task/rubrics/loaders.py +0 -152
  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/config.toml +0 -0
  1057. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +0 -0
  1058. {examples/task_apps → synth_ai/core/apps}/__init__.py +0 -0
  1059. /synth_ai/{tracing_v3 → core/tracing_v3}/examples/basic_usage.py +0 -0
  1060. /synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +0 -0
  1061. /synth_ai/{tracing_v3 → core/tracing_v3}/lm_call_record_abstractions.py +0 -0
  1062. /synth_ai/{tracing_v3 → core/tracing_v3}/replica_sync.py +0 -0
  1063. /synth_ai/{tracing_v3 → core/tracing_v3}/serialization.py +0 -0
  1064. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/__init__.py +0 -0
  1065. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/exceptions.py +0 -0
  1066. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/types.py +0 -0
  1067. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/utils.py +0 -0
  1068. /synth_ai/{tracing_v3 → core/tracing_v3}/turso/__init__.py +0 -0
  1069. /synth_ai/{evals → sdk/judging}/types.py +0 -0
  1070. /synth_ai/{learning → sdk/learning}/algorithms.py +0 -0
  1071. /synth_ai/{learning → sdk/learning}/config.py +0 -0
  1072. /synth_ai/{learning → sdk/learning}/constants.py +0 -0
  1073. /synth_ai/{learning → sdk/learning}/core.py +0 -0
  1074. /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
  1075. /synth_ai/{learning → sdk/learning}/rl/__init__.py +0 -0
  1076. /synth_ai/{learning → sdk/learning}/rl/config.py +0 -0
  1077. /synth_ai/{learning → sdk/learning}/rl_client.py +0 -0
  1078. /synth_ai/{learning → sdk/learning}/sft/__init__.py +0 -0
  1079. /synth_ai/{learning → sdk/learning}/sse.py +0 -0
  1080. /synth_ai/{task → sdk/task}/auth.py +0 -0
  1081. /synth_ai/{task → sdk/task}/client.py +0 -0
  1082. /synth_ai/{task → sdk/task}/errors.py +0 -0
  1083. /synth_ai/{task → sdk/task}/health.py +0 -0
  1084. /synth_ai/{task → sdk/task}/json.py +0 -0
  1085. /synth_ai/{task → sdk/task}/rubrics/models.py +0 -0
  1086. /synth_ai/{task → sdk/task}/rubrics/scoring.py +0 -0
  1087. /synth_ai/{task → sdk/task}/rubrics/strict.py +0 -0
  1088. /synth_ai/{task → sdk/task}/vendors.py +0 -0
  1089. {synth_ai-0.2.14.dist-info → synth_ai-0.4.1.dist-info}/WHEEL +0 -0
  1090. {synth_ai-0.2.14.dist-info → synth_ai-0.4.1.dist-info}/entry_points.txt +0 -0
  1091. {synth_ai-0.2.14.dist-info → synth_ai-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,1020 +0,0 @@
1
- """
2
- OCR-based dialogue detection for Pokemon Emerald.
3
- Provides fallback text detection when memory reading fails or returns stale data.
4
- """
5
-
6
- import cv2
7
- import numpy as np
8
- from PIL import Image
9
- from typing import Optional, List, Tuple
10
- import re
11
- import logging
12
-
13
- try:
14
- import pytesseract
15
- OCR_AVAILABLE = True
16
- except ImportError:
17
- OCR_AVAILABLE = False
18
- logging.warning("pytesseract not available - OCR dialogue detection disabled")
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
- class OCRDialogueDetector:
23
- """OCR-based dialogue detection for Pokemon Emerald"""
24
-
25
- # Pokemon Emerald dialogue box coordinates (corrected for actual dialogue position)
26
- DIALOGUE_BOX_COORDS = {
27
- 'x': 0, # Full width - dialogue spans entire bottom
28
- 'y': 104, # Dialogue starts around row 104 (from debug analysis)
29
- 'width': 240, # Full screen width
30
- 'height': 56 # Bottom portion height (160-104=56)
31
- }
32
-
33
- # Tighter OCR coordinates - just the text area inside the border (adjusted lower)
34
- OCR_TEXT_COORDS = {
35
- 'x': 8, # Skip left border
36
- 'y': 116, # Moved down 4px for better text alignment (104 + 12)
37
- 'width': 224, # Skip both side borders (240 - 16)
38
- 'height': 36 # Reduced height to maintain bottom margin (56 - 20)
39
- }
40
-
41
- # Pokemon Emerald dialogue text colors (based on actual RGB values)
42
- DIALOGUE_TEXT_COLORS = [
43
- # Exact text color from user
44
- (99, 99, 99), # Exact text color
45
- # Close variations for anti-aliasing and slight rendering differences
46
- (95, 95, 95), # Slightly darker
47
- (103, 103, 103), # Slightly lighter
48
- (91, 91, 91), # Darker variant
49
- (107, 107, 107), # Lighter variant
50
- (99, 99, 95), # Slight color shift
51
- (99, 95, 99), # Slight color shift
52
- (95, 99, 99), # Slight color shift
53
- # Additional gray tones that might appear due to rendering
54
- (87, 87, 87), # Darker gray
55
- (111, 111, 111), # Lighter gray
56
- (79, 79, 79), # Much darker
57
- (119, 119, 119), # Much lighter
58
- # Shadow colors (darker, often with slight offset)
59
- (64, 64, 64), # Dark shadow
60
- (72, 72, 72), # Medium shadow
61
- (56, 56, 56), # Darker shadow
62
- (48, 48, 48), # Very dark shadow
63
- # Possible highlighting/special text colors
64
- (99, 99, 128), # Blue-tinted for names
65
- (128, 99, 99), # Red-tinted for special text
66
- (99, 128, 99), # Green-tinted for special text
67
- ]
68
-
69
- # Color tolerance for matching (RGB distance) - increased to capture more text pixels
70
- COLOR_TOLERANCE = 40
71
-
72
- # Pokemon Emerald dialogue box background colors (based on actual RGB values)
73
- DIALOGUE_BOX_BACKGROUND_COLORS = [
74
- # Exact green line/border color from user
75
- (85, 204, 128), # Exact green border color
76
- # Variations of the green border for anti-aliasing and shadows
77
- (80, 199, 123), # Slightly darker green
78
- (90, 209, 133), # Slightly lighter green
79
- (85, 204, 128), # Exact match (duplicate for emphasis)
80
- (75, 194, 118), # Darker green variant
81
- (95, 214, 138), # Lighter green variant
82
- # Exact white text background from user
83
- (255, 255, 255), # Exact white text background
84
- # Close variations for anti-aliasing and compression artifacts
85
- (254, 254, 254), # Very close to white
86
- (253, 253, 253), # Slightly off white
87
- (252, 252, 252), # Light gray-white
88
- (248, 248, 248), # Near white
89
- (240, 240, 240), # Light off-white
90
- (255, 255, 254), # Slight yellow tint
91
- (254, 255, 255), # Slight cyan tint
92
- ]
93
-
94
- # How much of the dialogue box should be background color to consider it "active"
95
- DIALOGUE_BOX_BACKGROUND_THRESHOLD = 0.4 # 40% of dialogue area should be box color (mostly off-white background)
96
-
97
- # Battle text area (different position)
98
- BATTLE_TEXT_COORDS = {
99
- 'x': 8,
100
- 'y': 120,
101
- 'width': 224,
102
- 'height': 40
103
- }
104
-
105
- def __init__(self):
106
- self.last_detected_text = ""
107
- self.text_stability_threshold = 2 # Frames text must be stable
108
- self.stable_text_count = 0
109
- self.debug_color_detection = False # Set to True for color debugging
110
- self.use_full_frame_scan = False # Set to True to enable full-frame scanning (may pick up noise)
111
- self.skip_dialogue_box_detection = False # Set to True to temporarily bypass dialogue box detection
112
-
113
- def detect_dialogue_from_screenshot(self, screenshot: Image.Image) -> Optional[str]:
114
- """
115
- Detect dialogue text from Pokemon Emerald dialogue regions only.
116
- First verifies dialogue box is visible to prevent false positives.
117
-
118
- Args:
119
- screenshot: PIL Image of the game screen
120
-
121
- Returns:
122
- Detected dialogue text or None if no text found
123
- """
124
- if not OCR_AVAILABLE:
125
- return None
126
-
127
- try:
128
- screenshot_np = np.array(screenshot)
129
-
130
- # STEP 1: Check if dialogue box is actually visible (unless bypassed)
131
- if not self.skip_dialogue_box_detection and not self.is_dialogue_box_visible(screenshot):
132
- logger.debug("No dialogue box detected - skipping OCR")
133
- return None
134
-
135
- # STEP 2: Primary dialogue box area (most common) - use tighter text coordinates
136
- dialogue_text = self._extract_text_from_region(
137
- screenshot_np,
138
- self.OCR_TEXT_COORDS
139
- )
140
-
141
- if dialogue_text:
142
- validated = self._validate_and_clean_text(dialogue_text)
143
- if validated:
144
- return validated
145
-
146
- # Method 2: Battle text area (different position)
147
- battle_text = self._extract_text_from_region(
148
- screenshot_np,
149
- self.BATTLE_TEXT_COORDS
150
- )
151
-
152
- if battle_text:
153
- validated = self._validate_and_clean_text(battle_text)
154
- if validated:
155
- return validated
156
-
157
- # Method 3: Full frame scan (only if explicitly enabled - can pick up noise)
158
- if self.use_full_frame_scan:
159
- full_frame_text = self._extract_text_from_full_frame(screenshot)
160
- if full_frame_text:
161
- validated = self._validate_and_clean_text(full_frame_text)
162
- if validated:
163
- return validated
164
-
165
- return None
166
-
167
- except Exception as e:
168
- logger.debug(f"OCR dialogue detection failed: {e}")
169
- return None
170
-
171
- def _extract_text_from_full_frame(self, screenshot: Image.Image) -> Optional[str]:
172
- """
173
- Extract text from the entire screenshot using OCR
174
- This is more comprehensive than region-specific detection
175
- """
176
- try:
177
- # Convert PIL to numpy array
178
- screenshot_np = np.array(screenshot)
179
-
180
- # Preprocess the entire frame for better OCR
181
- processed_frame = self._preprocess_full_frame_for_ocr(screenshot_np)
182
-
183
- # OCR configuration optimized for Pokemon text detection
184
- # Use different settings for full frame vs regions
185
- full_frame_config = r'--oem 3 --psm 6' # Assume uniform block of text
186
-
187
- # Extract text from entire frame
188
- full_text = pytesseract.image_to_string(processed_frame, config=full_frame_config)
189
-
190
- # Clean and validate the text
191
- cleaned_text = self._clean_full_frame_text(full_text)
192
-
193
- if cleaned_text:
194
- return cleaned_text
195
-
196
- # If that fails, try with different PSM mode
197
- alt_config = r'--oem 3 --psm 11' # Sparse text, find as much as possible
198
- alt_text = pytesseract.image_to_string(processed_frame, config=alt_config)
199
- alt_cleaned = self._clean_full_frame_text(alt_text)
200
-
201
- return alt_cleaned if alt_cleaned else None
202
-
203
- except Exception as e:
204
- logger.debug(f"Full frame OCR failed: {e}")
205
- return None
206
-
207
- def _preprocess_full_frame_for_ocr(self, image_np: np.ndarray) -> np.ndarray:
208
- """Preprocess entire frame using Pokemon-specific dialogue color matching"""
209
- # Ensure we have color information
210
- if len(image_np.shape) != 3:
211
- # Convert grayscale to color by duplicating channels
212
- image_np = cv2.cvtColor(image_np, cv2.COLOR_GRAY2RGB)
213
-
214
- # Scale up for better color detection precision
215
- scaled = cv2.resize(image_np, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC)
216
-
217
- # Create mask for dialogue text colors across entire frame
218
- text_mask = self._create_dialogue_color_mask(scaled)
219
-
220
- # Apply color mask - black text on white background (better for OCR)
221
- binary = np.where(text_mask, 0, 255).astype(np.uint8)
222
-
223
- # Enhanced morphological operations for full frame
224
- # Close gaps and thicken text
225
- kernel_close = np.ones((2, 2), np.uint8)
226
- cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel_close)
227
-
228
- # Dilate to make text more readable
229
- kernel_dilate = np.ones((1, 1), np.uint8)
230
- cleaned = cv2.dilate(cleaned, kernel_dilate, iterations=1)
231
-
232
- return cleaned
233
-
234
- def _clean_full_frame_text(self, raw_text: str) -> Optional[str]:
235
- """Clean and validate text extracted from full frame"""
236
- if not raw_text:
237
- return None
238
-
239
- # Remove excessive whitespace and special characters
240
- lines = []
241
- for line in raw_text.split('\n'):
242
- # Clean each line
243
- cleaned_line = re.sub(r'\s+', ' ', line.strip())
244
-
245
- # Filter out lines that are likely noise
246
- if len(cleaned_line) >= 2: # Minimum meaningful length
247
- # Check if line has reasonable character content
248
- alpha_ratio = sum(c.isalpha() for c in cleaned_line) / len(cleaned_line)
249
- if alpha_ratio >= 0.3: # At least 30% alphabetic characters
250
- lines.append(cleaned_line)
251
-
252
- if not lines:
253
- return None
254
-
255
- # Join lines and do final cleanup
256
- full_text = ' '.join(lines)
257
-
258
- # Remove common OCR artifacts for Pokemon games
259
- # These are characters commonly misread by OCR
260
- ocr_artifacts = [
261
- r'[|\\/_]', # Common line artifacts
262
- r'^\W+', # Leading non-word characters
263
- r'\W+$', # Trailing non-word characters
264
- ]
265
-
266
- for artifact in ocr_artifacts:
267
- full_text = re.sub(artifact, ' ', full_text)
268
-
269
- # Final cleanup
270
- full_text = re.sub(r'\s+', ' ', full_text).strip()
271
-
272
- # Validate final result
273
- if len(full_text) < 3:
274
- return None
275
-
276
- # Check for reasonable content (not just numbers/symbols)
277
- alpha_count = sum(c.isalpha() for c in full_text)
278
- if alpha_count < 3: # Need at least 3 letters
279
- return None
280
-
281
- return full_text
282
-
283
- def detect_all_text_regions(self, screenshot: Image.Image) -> List[dict]:
284
- """
285
- Detect all text regions in the screenshot with their locations
286
- Useful for debugging and comprehensive text detection
287
- """
288
- if not OCR_AVAILABLE:
289
- return []
290
-
291
- try:
292
- # Convert to numpy array
293
- screenshot_np = np.array(screenshot)
294
- processed = self._preprocess_full_frame_for_ocr(screenshot_np)
295
-
296
- # Use pytesseract to get text with bounding boxes
297
- data = pytesseract.image_to_data(processed, output_type=pytesseract.Output.DICT)
298
-
299
- text_regions = []
300
- n_boxes = len(data['level'])
301
-
302
- for i in range(n_boxes):
303
- # Get confidence and text
304
- confidence = int(data['conf'][i])
305
- text = data['text'][i].strip()
306
-
307
- # Only include text with reasonable confidence and content
308
- if confidence > 30 and len(text) > 1:
309
- # Get bounding box (scale back from 2x preprocessing)
310
- x = data['left'][i] // 2 # Scale back from 2x
311
- y = data['top'][i] // 2
312
- w = data['width'][i] // 2
313
- h = data['height'][i] // 2
314
-
315
- # Validate text content
316
- alpha_ratio = sum(c.isalpha() for c in text) / len(text)
317
- if alpha_ratio >= 0.3: # At least 30% letters
318
- text_regions.append({
319
- 'text': text,
320
- 'confidence': confidence,
321
- 'bbox': (x, y, w, h),
322
- 'area': w * h
323
- })
324
-
325
- # Sort by confidence and area (larger, more confident regions first)
326
- text_regions.sort(key=lambda r: (r['confidence'], r['area']), reverse=True)
327
-
328
- return text_regions
329
-
330
- except Exception as e:
331
- logger.debug(f"Text region detection failed: {e}")
332
- return []
333
-
334
- def _extract_text_from_region(self, image_np: np.ndarray, coords: dict) -> str:
335
- """Extract text from a specific region of the image"""
336
- # Extract region of interest
337
- y1 = coords['y']
338
- y2 = y1 + coords['height']
339
- x1 = coords['x']
340
- x2 = x1 + coords['width']
341
-
342
- roi = image_np[y1:y2, x1:x2]
343
-
344
- # Preprocessing for better OCR accuracy
345
- roi = self._preprocess_for_ocr(roi)
346
-
347
- # OCR configuration optimized for Pokemon Emerald text
348
- custom_config = r'--oem 3 --psm 6'
349
-
350
- # Extract text
351
- text = pytesseract.image_to_string(roi, config=custom_config)
352
- return text.strip()
353
-
354
- def _preprocess_for_ocr(self, roi: np.ndarray) -> np.ndarray:
355
- """Preprocess image region using Pokemon-specific dialogue color matching"""
356
- # Keep original color information for color matching
357
- if len(roi.shape) != 3:
358
- # Convert grayscale back to color for processing (duplicate channels)
359
- roi = cv2.cvtColor(roi, cv2.COLOR_GRAY2RGB)
360
-
361
- # Scale up first for better color detection precision
362
- roi = cv2.resize(roi, None, fx=4, fy=4, interpolation=cv2.INTER_CUBIC)
363
-
364
- # Create mask for dialogue text colors
365
- text_mask = self._create_dialogue_color_mask(roi)
366
-
367
- # Apply color mask to create clean binary image
368
- # Black text on white background (better for OCR)
369
- binary_roi = np.where(text_mask, 0, 255).astype(np.uint8)
370
-
371
- # Ensure we have a proper binary image (pure black and white only)
372
- binary_roi = np.where(binary_roi > 127, 255, 0).astype(np.uint8)
373
-
374
- # Enhanced morphological operations to thicken and connect text
375
- # Close gaps in letters
376
- kernel_close = np.ones((2, 2), np.uint8)
377
- binary_roi = cv2.morphologyEx(binary_roi, cv2.MORPH_CLOSE, kernel_close)
378
-
379
- # Dilate to make text thicker and more readable (balanced approach)
380
- kernel_dilate = np.ones((2, 2), np.uint8)
381
- binary_roi = cv2.dilate(binary_roi, kernel_dilate, iterations=2)
382
-
383
- # Remove small noise while preserving text
384
- kernel_open = np.ones((1, 1), np.uint8)
385
- binary_roi = cv2.morphologyEx(binary_roi, cv2.MORPH_OPEN, kernel_open)
386
-
387
- return binary_roi
388
-
389
- def _create_dialogue_color_mask(self, image: np.ndarray) -> np.ndarray:
390
- """Create binary mask for pixels matching Pokemon dialogue text colors"""
391
- if len(image.shape) != 3:
392
- return np.zeros(image.shape[:2], dtype=bool)
393
-
394
- mask = np.zeros(image.shape[:2], dtype=bool)
395
- matched_pixels_per_color = []
396
-
397
- # Check each dialogue color
398
- for i, target_color in enumerate(self.DIALOGUE_TEXT_COLORS):
399
- # Calculate color distance for all pixels
400
- color_diff = np.sqrt(np.sum((image - target_color) ** 2, axis=2))
401
-
402
- # Add pixels within tolerance to mask
403
- color_mask = (color_diff <= self.COLOR_TOLERANCE)
404
- mask |= color_mask
405
-
406
- # Debug information
407
- if self.debug_color_detection:
408
- matched_count = np.sum(color_mask)
409
- matched_pixels_per_color.append(matched_count)
410
-
411
- # Log color detection results for debugging
412
- if self.debug_color_detection and any(matched_pixels_per_color):
413
- total_matched = np.sum(mask)
414
- logger.debug(f"Color matching: {total_matched} pixels matched dialogue colors")
415
- for i, count in enumerate(matched_pixels_per_color):
416
- if count > 0:
417
- color = self.DIALOGUE_TEXT_COLORS[i]
418
- logger.debug(f" Color {color}: {count} pixels")
419
-
420
- return mask
421
-
422
- def is_dialogue_box_visible(self, screenshot: Image.Image) -> bool:
423
- """
424
- Check if a dialogue box is actually visible by looking for green horizontal border lines.
425
- Searches for the characteristic green lines above and below the dialogue text.
426
-
427
- Args:
428
- screenshot: PIL Image of the game screen
429
-
430
- Returns:
431
- True if dialogue box is detected, False otherwise
432
- """
433
- if not screenshot:
434
- return False
435
-
436
- try:
437
- # Convert to numpy array
438
- image_np = np.array(screenshot)
439
- if len(image_np.shape) != 3:
440
- return False
441
-
442
- # Extract extended dialogue region to catch border lines
443
- coords = self.DIALOGUE_BOX_COORDS
444
- # Extend the search area to catch top and bottom borders
445
- extended_region = image_np[
446
- max(0, coords['y'] - 5):min(image_np.shape[0], coords['y'] + coords['height'] + 5),
447
- coords['x']:coords['x'] + coords['width']
448
- ]
449
-
450
- if extended_region.size == 0:
451
- return False
452
-
453
- # Look for horizontal border lines using actual dialogue border colors
454
- border_colors = [
455
- (66, 181, 132), # Main teal border color from debug analysis
456
- (24, 165, 107), # Secondary border color
457
- (57, 140, 49), # Darker border variant
458
- (0, 255, 156), # Bright border accent
459
- (115, 198, 165) # Light border variant
460
- ]
461
- border_tolerance = 20 # Tolerance for color matching
462
-
463
- # Check each row for horizontal border lines
464
- border_line_rows = []
465
- height, width = extended_region.shape[:2]
466
-
467
- for row_idx in range(height):
468
- row_pixels = extended_region[row_idx]
469
-
470
- # Count border color pixels in this row
471
- border_pixels_in_row = 0
472
- for pixel in row_pixels:
473
- # Check if pixel matches any of the border colors
474
- for border_color in border_colors:
475
- color_diff = np.sqrt(np.sum((pixel - np.array(border_color)) ** 2))
476
- if color_diff <= border_tolerance:
477
- border_pixels_in_row += 1
478
- break # Don't double-count pixels
479
-
480
- # If significant portion of row has border colors, it's likely a border line
481
- border_percentage = border_pixels_in_row / width
482
- if border_percentage > 0.2: # 20% of row width has border colors (lower threshold)
483
- border_line_rows.append(row_idx)
484
-
485
- # VERY strict detection to avoid false positives from environment colors
486
-
487
- # Require many border lines for robust detection
488
- has_sufficient_border_lines = len(border_line_rows) >= 5 # Need at least 5 border lines
489
-
490
- # MUST have top AND bottom border lines (no exceptions for false positive prevention)
491
- has_top_and_bottom_lines = False
492
- if len(border_line_rows) >= 3:
493
- # Check if we have lines at different heights (top and bottom)
494
- min_line = min(border_line_rows)
495
- max_line = max(border_line_rows)
496
- if max_line - min_line > 15: # Lines must be at least 15 pixels apart (very strict)
497
- has_top_and_bottom_lines = True
498
-
499
- # Additional check: look for proper dialogue box pattern (rectangular border)
500
- has_rectangular_pattern = False
501
- if len(border_line_rows) >= 5:
502
- # Check if we have border lines spread across the dialogue region
503
- height_quarter = height // 4
504
- top_lines = [r for r in border_line_rows if r < height_quarter]
505
- middle_lines = [r for r in border_line_rows if height_quarter <= r <= 3 * height_quarter]
506
- bottom_lines = [r for r in border_line_rows if r > 3 * height_quarter]
507
-
508
- # Must have lines in top AND bottom, and some in middle for a proper box
509
- if len(top_lines) >= 2 and len(bottom_lines) >= 2 and len(middle_lines) >= 1:
510
- has_rectangular_pattern = True
511
-
512
- # Extra check: ensure lines are actually horizontal (consistent across width)
513
- has_proper_horizontal_lines = False
514
- if len(border_line_rows) >= 3:
515
- # Check that border lines extend across significant width (not just scattered pixels)
516
- proper_lines = 0
517
- for row_idx in border_line_rows[:10]: # Check first 10 lines
518
- row_pixels = extended_region[row_idx]
519
- border_pixels_in_row = 0
520
- for pixel in row_pixels:
521
- for border_color in border_colors:
522
- color_diff = np.sqrt(np.sum((pixel - np.array(border_color)) ** 2))
523
- if color_diff <= border_tolerance:
524
- border_pixels_in_row += 1
525
- break
526
-
527
- # Line must span at least 50% of width to be considered a proper horizontal line
528
- if border_pixels_in_row / width > 0.5:
529
- proper_lines += 1
530
-
531
- if proper_lines >= 3: # Need at least 3 proper horizontal lines
532
- has_proper_horizontal_lines = True
533
-
534
- # Log detection results
535
- if self.debug_color_detection:
536
- logger.debug(f"Border line detection: Found {len(border_line_rows)} border horizontal lines")
537
- logger.debug(f"Line rows: {border_line_rows[:5]}") # Show first 5
538
- logger.debug(f"Has sufficient lines (≥5): {has_sufficient_border_lines}")
539
- logger.debug(f"Has top+bottom lines (≥15px apart): {has_top_and_bottom_lines}")
540
- logger.debug(f"Has rectangular pattern: {has_rectangular_pattern}")
541
- logger.debug(f"Has proper horizontal lines (≥50% width): {has_proper_horizontal_lines}")
542
-
543
- # Final check: look for actual dialogue box background (light/white area inside borders)
544
- has_dialogue_background = False
545
- if len(border_line_rows) >= 3:
546
- # Check middle area for dialogue background colors (light colors)
547
- middle_start = height // 4
548
- middle_end = 3 * height // 4
549
- middle_region = extended_region[middle_start:middle_end, width//4:3*width//4]
550
-
551
- if middle_region.size > 0:
552
- # Look for light background colors typical of dialogue boxes
553
- light_pixels = 0
554
- total_pixels = middle_region.size // 3 # Divide by 3 for RGB
555
-
556
- for pixel in middle_region.reshape(-1, 3):
557
- # Light colors: high brightness (sum of RGB > 400) or white-ish
558
- brightness = np.sum(pixel)
559
- if brightness > 400 or (pixel[0] > 200 and pixel[1] > 200 and pixel[2] > 200):
560
- light_pixels += 1
561
-
562
- light_percentage = light_pixels / total_pixels
563
- if light_percentage > 0.3: # At least 30% of middle area should be light (dialogue background)
564
- has_dialogue_background = True
565
-
566
- # Log all criteria
567
- if self.debug_color_detection:
568
- logger.debug(f"Has dialogue background (light area): {has_dialogue_background}")
569
-
570
- # Use simplified detection method to avoid false positives
571
- # Check for white background in center area
572
- center_h = extended_region.shape[0] // 2
573
- center_w = extended_region.shape[1] // 2
574
- margin = 20
575
-
576
- center_area = extended_region[
577
- max(0, center_h - margin):min(extended_region.shape[0], center_h + margin),
578
- max(0, center_w - margin):min(extended_region.shape[1], center_w + margin)
579
- ]
580
-
581
- if center_area.size > 0:
582
- # Count white/light pixels (dialogue background)
583
- light_mask = (center_area[:,:,0] > 200) & (center_area[:,:,1] > 200) & (center_area[:,:,2] > 200)
584
- light_percentage = np.sum(light_mask) / light_mask.size
585
-
586
- # Count text-like colors (dark gray)
587
- text_mask = ((center_area[:,:,0] > 80) & (center_area[:,:,0] < 130) &
588
- (center_area[:,:,1] > 80) & (center_area[:,:,1] < 130) &
589
- (center_area[:,:,2] > 80) & (center_area[:,:,2] < 130))
590
- text_percentage = np.sum(text_mask) / text_mask.size
591
-
592
- # Simple, robust criteria
593
- is_visible = light_percentage > 0.3 and text_percentage > 0.02
594
-
595
- if self.debug_color_detection:
596
- logger.debug(f"Simplified detection - Light bg: {light_percentage:.1%}, Text: {text_percentage:.1%}")
597
- else:
598
- is_visible = False
599
-
600
- if self.debug_color_detection:
601
- logger.debug(f"Dialogue box {'VISIBLE' if is_visible else 'NOT VISIBLE'} "
602
- f"(found {len(border_line_rows)} border lines)")
603
-
604
- return is_visible
605
-
606
- except Exception as e:
607
- logger.debug(f"Dialogue box detection error: {e}")
608
- return False
609
-
610
- def enable_color_debug(self, enabled: bool = True):
611
- """Enable/disable color detection debugging"""
612
- self.debug_color_detection = enabled
613
- if enabled:
614
- logger.info("OCR color detection debugging enabled")
615
- else:
616
- logger.info("OCR color detection debugging disabled")
617
-
618
- def analyze_dialogue_colors(self, screenshot: Image.Image) -> dict:
619
- """
620
- Analyze a screenshot to find the actual colors used in the dialogue box.
621
- This helps fine-tune the DIALOGUE_TEXT_COLORS list.
622
- """
623
- if not screenshot:
624
- return {}
625
-
626
- # Convert to numpy array
627
- image_np = np.array(screenshot)
628
- if len(image_np.shape) != 3:
629
- return {}
630
-
631
- # Extract dialogue region
632
- coords = self.DIALOGUE_BOX_COORDS
633
- dialogue_region = image_np[
634
- coords['y']:coords['y'] + coords['height'],
635
- coords['x']:coords['x'] + coords['width']
636
- ]
637
-
638
- if dialogue_region.size == 0:
639
- return {}
640
-
641
- # Find unique colors and their frequencies
642
- pixels = dialogue_region.reshape(-1, 3)
643
- unique_colors, counts = np.unique(pixels, axis=0, return_counts=True)
644
-
645
- # Sort by frequency (most common first)
646
- sorted_indices = np.argsort(counts)[::-1]
647
-
648
- # Analyze the most common colors
649
- color_analysis = {
650
- 'total_pixels': len(pixels),
651
- 'unique_colors': len(unique_colors),
652
- 'top_colors': []
653
- }
654
-
655
- # Show top 20 most common colors
656
- for i in range(min(20, len(unique_colors))):
657
- idx = sorted_indices[i]
658
- color = tuple(unique_colors[idx])
659
- count = counts[idx]
660
- percentage = (count / len(pixels)) * 100
661
-
662
- color_analysis['top_colors'].append({
663
- 'rgb': color,
664
- 'count': int(count),
665
- 'percentage': round(percentage, 2)
666
- })
667
-
668
- return color_analysis
669
-
670
- def print_color_analysis(self, screenshot: Image.Image):
671
- """Print color analysis in a readable format"""
672
- analysis = self.analyze_dialogue_colors(screenshot)
673
-
674
- if not analysis:
675
- print("❌ Could not analyze colors")
676
- return
677
-
678
- print(f"\n🎨 DIALOGUE COLOR ANALYSIS")
679
- print(f"={'='*50}")
680
- print(f"Total pixels: {analysis['total_pixels']:,}")
681
- print(f"Unique colors: {analysis['unique_colors']:,}")
682
- print(f"\nTop Colors (most frequent first):")
683
- print(f"{'Rank':<4} {'RGB Color':<20} {'Count':<8} {'%':<6} {'Color Type':<15}")
684
- print(f"{'-'*70}")
685
-
686
- for i, color_info in enumerate(analysis['top_colors'][:15], 1):
687
- rgb = color_info['rgb']
688
- count = color_info['count']
689
- pct = color_info['percentage']
690
-
691
- # Classify the color
692
- if rgb[0] > 240 and rgb[1] > 240 and rgb[2] > 240:
693
- color_type = "Background"
694
- elif rgb[0] < 120 and rgb[1] < 120 and rgb[2] < 120:
695
- color_type = "Text/Shadow"
696
- elif abs(rgb[0] - rgb[1]) < 10 and abs(rgb[1] - rgb[2]) < 10:
697
- color_type = "Gray text"
698
- else:
699
- color_type = "Other"
700
-
701
- print(f"{i:<4} {str(rgb):<20} {count:<8} {pct:<6.1f} {color_type:<15}")
702
-
703
- print(f"\n💡 Suggested dialogue colors to add:")
704
- suggested = []
705
- for color_info in analysis['top_colors'][:10]:
706
- rgb = color_info['rgb']
707
- # Suggest colors that look like text (not pure white background)
708
- if rgb[0] < 200 and color_info['percentage'] > 0.5:
709
- suggested.append(rgb)
710
-
711
- for color in suggested[:5]: # Show top 5 suggestions
712
- print(f" {color},")
713
-
714
- print(f"{'='*50}")
715
-
716
- def update_dialogue_colors_from_analysis(self, screenshot: Image.Image, threshold_percentage: float = 1.0):
717
- """
718
- Update DIALOGUE_TEXT_COLORS based on analysis of actual screenshot.
719
- Only adds colors that appear frequently enough (above threshold_percentage).
720
- """
721
- analysis = self.analyze_dialogue_colors(screenshot)
722
-
723
- if not analysis:
724
- logger.warning("Could not analyze colors to update dialogue colors")
725
- return
726
-
727
- # Find colors that appear frequently and look like text
728
- new_colors = []
729
- for color_info in analysis['top_colors']:
730
- rgb = color_info['rgb']
731
- pct = color_info['percentage']
732
-
733
- # Only consider colors that:
734
- # 1. Appear frequently enough
735
- # 2. Are not pure white (background)
736
- # 3. Are not already in our color list
737
- if (pct >= threshold_percentage and
738
- not (rgb[0] > 240 and rgb[1] > 240 and rgb[2] > 240) and
739
- rgb not in self.DIALOGUE_TEXT_COLORS):
740
- new_colors.append(rgb)
741
-
742
- if new_colors:
743
- logger.info(f"Adding {len(new_colors)} new dialogue colors from analysis")
744
- for color in new_colors[:5]: # Limit to top 5 new colors
745
- logger.info(f" Added color: {color}")
746
-
747
- # Add new colors to the existing list
748
- self.DIALOGUE_TEXT_COLORS.extend(new_colors[:5])
749
- else:
750
- logger.info("No new dialogue colors found to add")
751
-
752
- def analyze_dialogue_box_background(self, screenshot: Image.Image):
753
- """
754
- Analyze dialogue box region to find actual background colors.
755
- Useful for fine-tuning DIALOGUE_BOX_BACKGROUND_COLORS.
756
- """
757
- analysis = self.analyze_dialogue_colors(screenshot)
758
-
759
- if not analysis:
760
- print("❌ Could not analyze dialogue box background")
761
- return
762
-
763
- print(f"\n📦 DIALOGUE BOX BACKGROUND ANALYSIS")
764
- print(f"{'='*50}")
765
- print(f"Total pixels: {analysis['total_pixels']:,}")
766
- print(f"Unique colors: {analysis['unique_colors']:,}")
767
- print(f"\nTop Background Colors (most frequent first):")
768
- print(f"{'Rank':<4} {'RGB Color':<20} {'Count':<8} {'%':<6} {'Type':<15}")
769
- print(f"{'-'*70}")
770
-
771
- for i, color_info in enumerate(analysis['top_colors'][:15], 1):
772
- rgb = color_info['rgb']
773
- count = color_info['count']
774
- pct = color_info['percentage']
775
-
776
- # Classify as likely background vs text
777
- if pct > 10: # Very common = likely background
778
- color_type = "Background"
779
- elif rgb[0] < 150 and rgb[1] < 150 and rgb[2] < 150:
780
- color_type = "Text/Shadow"
781
- else:
782
- color_type = "Other"
783
-
784
- print(f"{i:<4} {str(rgb):<20} {count:<8} {pct:<6.1f} {color_type:<15}")
785
-
786
- print(f"\n💡 Suggested background colors (>5% pixels):")
787
- for color_info in analysis['top_colors'][:10]:
788
- rgb = color_info['rgb']
789
- pct = color_info['percentage']
790
- # Suggest colors that are common and not text-like
791
- if pct > 5.0 and not (rgb[0] < 150 and rgb[1] < 150 and rgb[2] < 150):
792
- print(f" {rgb},")
793
-
794
- print(f"{'='*50}")
795
-
796
- def test_dialogue_box_detection(self, screenshot: Image.Image):
797
- """Test dialogue box detection with detailed output for green line method"""
798
- print(f"\n🔍 DIALOGUE BOX DETECTION TEST (Green Line Method)")
799
- print(f"{'='*50}")
800
-
801
- # Enable debug mode for detailed output
802
- old_debug = self.debug_color_detection
803
- self.debug_color_detection = True
804
-
805
- is_visible = self.is_dialogue_box_visible(screenshot)
806
-
807
- # Get detailed green line analysis
808
- image_np = np.array(screenshot)
809
- coords = self.DIALOGUE_BOX_COORDS
810
-
811
- # Extended region for border detection
812
- extended_region = image_np[
813
- max(0, coords['y'] - 5):min(image_np.shape[0], coords['y'] + coords['height'] + 5),
814
- coords['x']:coords['x'] + coords['width']
815
- ]
816
-
817
- height, width = extended_region.shape[:2]
818
- green_border_color = (85, 204, 128)
819
- green_tolerance = 15
820
-
821
- print(f"Search region: {coords['x']},{coords['y']-5} {coords['width']}x{height+10}")
822
- print(f"Green border color: {green_border_color}")
823
- print(f"Green tolerance: ±{green_tolerance}")
824
-
825
- # Analyze each row
826
- green_line_rows = []
827
- for row_idx in range(height):
828
- row_pixels = extended_region[row_idx]
829
-
830
- green_pixels_in_row = 0
831
- for pixel in row_pixels:
832
- color_diff = np.sqrt(np.sum((pixel - green_border_color) ** 2))
833
- if color_diff <= green_tolerance:
834
- green_pixels_in_row += 1
835
-
836
- green_percentage = green_pixels_in_row / width
837
- if green_percentage > 0.3: # 30% threshold
838
- green_line_rows.append({
839
- 'row': row_idx,
840
- 'green_pixels': green_pixels_in_row,
841
- 'percentage': green_percentage * 100
842
- })
843
-
844
- print(f"Found {len(green_line_rows)} green horizontal lines:")
845
- for line_info in green_line_rows[:5]: # Show first 5
846
- row = line_info['row']
847
- pixels = line_info['green_pixels']
848
- pct = line_info['percentage']
849
- print(f" Row {row}: {pixels}/{width} pixels ({pct:.1f}% green)")
850
-
851
- print(f"\nResult: {'✅ DIALOGUE BOX VISIBLE' if is_visible else '❌ NOT VISIBLE'}")
852
- print(f"{'='*50}")
853
-
854
- # Restore debug setting
855
- self.debug_color_detection = old_debug
856
-
857
- return is_visible
858
-
859
- def _validate_and_clean_text(self, text: str) -> Optional[str]:
860
- """Validate and clean detected text"""
861
- if not text or len(text.strip()) < 3:
862
- return None
863
-
864
- # Clean up common OCR errors
865
- text = re.sub(r'\n+', ' ', text) # Replace newlines with spaces
866
- text = re.sub(r'\s+', ' ', text) # Normalize whitespace
867
- text = text.strip()
868
-
869
- # Filter out obviously wrong detections
870
- if len(text) < 3 or len(text) > 200:
871
- return None
872
-
873
- # Check for minimum alphabetic content (avoid detecting UI elements)
874
- alpha_ratio = sum(c.isalpha() for c in text) / len(text)
875
- if alpha_ratio < 0.5:
876
- return None
877
-
878
- # Comprehensive random letter filtering - catch ANY nonsense patterns
879
- if self._is_random_nonsense(text):
880
- logger.debug(f"OCR validation: Rejected as random nonsense: '{text[:50]}...'")
881
- return None
882
-
883
- return text
884
-
885
- def _is_random_nonsense(self, text: str) -> bool:
886
- """
887
- Comprehensive detection of random letter sequences and nonsense text.
888
- Catches any type of random letters that don't form meaningful dialogue.
889
- """
890
- if not text or len(text.strip()) < 3:
891
- return True
892
-
893
- text_lower = text.lower().strip()
894
- words = text_lower.split()
895
-
896
- if len(words) == 0:
897
- return True
898
-
899
- # Pattern 1: Excessive single/double character "words"
900
- short_words = [w for w in words if len(w) <= 2]
901
- if len(short_words) > len(words) * 0.6: # More than 60% are very short
902
- return True
903
-
904
- # Pattern 2: Repetitive patterns (like "a a a a a")
905
- word_counts = {}
906
- for word in words:
907
- word_counts[word] = word_counts.get(word, 0) + 1
908
- for word, count in word_counts.items():
909
- if len(word) <= 2 and count >= 3: # Short word repeated 3+ times
910
- return True
911
-
912
- # Pattern 3: Too many words (dialogue is usually concise)
913
- if len(words) > 30:
914
- return True
915
-
916
- # Pattern 4: Check for valid English-like words
917
- valid_words = 0
918
- dialogue_words = {
919
- 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by',
920
- 'you', 'i', 'we', 'they', 'he', 'she', 'it', 'this', 'that', 'these', 'those',
921
- 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did',
922
- 'will', 'would', 'could', 'should', 'can', 'may', 'might', 'must',
923
- 'get', 'got', 'give', 'take', 'go', 'come', 'see', 'look', 'want', 'need', 'know', 'think',
924
- 'pokemon', 'trainer', 'battle', 'items', 'store', 'pc', 'computer', 'use', 'hello', 'hi'
925
- }
926
-
927
- for word in words:
928
- clean_word = ''.join(c for c in word if c.isalnum()).lower()
929
- if len(clean_word) >= 2:
930
- # Check if it's a known good word
931
- if clean_word in dialogue_words:
932
- valid_words += 1
933
- # Check if it has reasonable letter patterns
934
- elif self._has_valid_letter_pattern(clean_word):
935
- valid_words += 1
936
-
937
- # Need at least 30% valid words
938
- valid_ratio = valid_words / len(words) if len(words) > 0 else 0
939
- if valid_ratio < 0.3:
940
- return True
941
-
942
- # Pattern 5: Detect excessive mixed case (OCR noise pattern)
943
- mixed_case_words = 0
944
- for word in words:
945
- if len(word) >= 3:
946
- has_lower = any(c.islower() for c in word)
947
- has_upper = any(c.isupper() for c in word)
948
- if has_lower and has_upper and not word[0].isupper(): # Not normal capitalization
949
- mixed_case_words += 1
950
-
951
- if mixed_case_words > len(words) * 0.4: # More than 40% have weird capitalization
952
- return True
953
-
954
- return False
955
-
956
- def _has_valid_letter_pattern(self, word: str) -> bool:
957
- """Check if word has valid English-like letter patterns"""
958
- if len(word) < 2:
959
- return False
960
-
961
- # Must have at least one vowel (unless very short)
962
- vowels = 'aeiou'
963
- has_vowel = any(c in vowels for c in word.lower())
964
- if len(word) >= 3 and not has_vowel:
965
- return False
966
-
967
- # Check for reasonable consonant clusters
968
- consonants = 'bcdfghjklmnpqrstvwxyz'
969
- consonant_streak = 0
970
- max_consonant_streak = 0
971
-
972
- for char in word.lower():
973
- if char in consonants:
974
- consonant_streak += 1
975
- max_consonant_streak = max(max_consonant_streak, consonant_streak)
976
- else:
977
- consonant_streak = 0
978
-
979
- # Too many consonants in a row suggests OCR noise
980
- if max_consonant_streak > 4:
981
- return False
982
-
983
- # Check for excessive repeated characters
984
- repeated = 0
985
- for i in range(len(word) - 1):
986
- if word[i] == word[i + 1]:
987
- repeated += 1
988
-
989
- if repeated > len(word) * 0.4: # More than 40% repeated chars
990
- return False
991
-
992
- return True
993
-
994
- def get_stable_dialogue_text(self, screenshot: Image.Image) -> Optional[str]:
995
- """
996
- Get dialogue text that has been stable across multiple frames.
997
- This helps avoid detecting transitional/partial text.
998
- """
999
- current_text = self.detect_dialogue_from_screenshot(screenshot)
1000
-
1001
- if current_text == self.last_detected_text:
1002
- self.stable_text_count += 1
1003
- else:
1004
- self.stable_text_count = 0
1005
- self.last_detected_text = current_text
1006
-
1007
- # Return text only if it's been stable for threshold frames
1008
- if self.stable_text_count >= self.text_stability_threshold and current_text:
1009
- return current_text
1010
-
1011
- return None
1012
-
1013
-
1014
- def create_ocr_detector() -> Optional[OCRDialogueDetector]:
1015
- """Factory function to create OCR detector if available"""
1016
- if OCR_AVAILABLE:
1017
- return OCRDialogueDetector()
1018
- else:
1019
- logger.warning("OCR not available - install pytesseract and tesseract-ocr system package")
1020
- return None