synth-ai 0.2.8.dev2__py3-none-any.whl → 0.4.3__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.
Files changed (740) hide show
  1. synth_ai/__init__.py +44 -24
  2. synth_ai/__main__.py +30 -3
  3. synth_ai/cli/__init__.py +103 -48
  4. synth_ai/cli/__main__.py +42 -0
  5. synth_ai/cli/_internal/__init__.py +5 -0
  6. synth_ai/cli/_internal/modal_wrapper.py +31 -0
  7. synth_ai/cli/_internal/storage.py +20 -0
  8. synth_ai/cli/_internal/typer_patch.py +47 -0
  9. synth_ai/cli/_internal/validate_task_app.py +29 -0
  10. synth_ai/cli/agents/__init__.py +17 -0
  11. synth_ai/cli/agents/claude.py +77 -0
  12. synth_ai/cli/agents/codex.py +265 -0
  13. synth_ai/cli/agents/opencode.py +253 -0
  14. synth_ai/cli/commands/__init__.py +18 -0
  15. synth_ai/cli/commands/artifacts/__init__.py +13 -0
  16. synth_ai/cli/commands/artifacts/client.py +119 -0
  17. synth_ai/cli/commands/artifacts/config.py +57 -0
  18. synth_ai/cli/commands/artifacts/core.py +24 -0
  19. synth_ai/cli/commands/artifacts/download.py +188 -0
  20. synth_ai/cli/commands/artifacts/export.py +186 -0
  21. synth_ai/cli/commands/artifacts/list.py +156 -0
  22. synth_ai/cli/commands/artifacts/parsing.py +250 -0
  23. synth_ai/cli/commands/artifacts/show.py +336 -0
  24. synth_ai/cli/commands/demo/__init__.py +3 -0
  25. synth_ai/cli/commands/demo/core.py +153 -0
  26. synth_ai/cli/commands/eval/__init__.py +10 -0
  27. synth_ai/cli/commands/eval/config.py +338 -0
  28. synth_ai/cli/commands/eval/core.py +256 -0
  29. synth_ai/cli/commands/eval/runner.py +704 -0
  30. synth_ai/cli/commands/eval/validation.py +60 -0
  31. synth_ai/cli/commands/filter/__init__.py +12 -0
  32. synth_ai/cli/commands/filter/core.py +424 -0
  33. synth_ai/cli/commands/filter/errors.py +55 -0
  34. synth_ai/cli/commands/filter/validation.py +77 -0
  35. synth_ai/cli/commands/help/__init__.py +185 -0
  36. synth_ai/cli/commands/help/core.py +72 -0
  37. synth_ai/cli/commands/scan/__init__.py +19 -0
  38. synth_ai/cli/commands/scan/cloudflare_scanner.py +403 -0
  39. synth_ai/cli/commands/scan/core.py +344 -0
  40. synth_ai/cli/commands/scan/health_checker.py +242 -0
  41. synth_ai/cli/commands/scan/local_scanner.py +278 -0
  42. synth_ai/cli/commands/scan/models.py +83 -0
  43. synth_ai/cli/commands/smoke/__init__.py +7 -0
  44. synth_ai/cli/commands/smoke/core.py +1428 -0
  45. synth_ai/cli/commands/status/__init__.py +3 -0
  46. synth_ai/cli/commands/status/client.py +91 -0
  47. synth_ai/cli/commands/status/config.py +12 -0
  48. synth_ai/cli/commands/status/errors.py +11 -0
  49. synth_ai/cli/commands/status/subcommands/__init__.py +3 -0
  50. synth_ai/cli/commands/status/subcommands/config.py +13 -0
  51. synth_ai/cli/commands/status/subcommands/files.py +34 -0
  52. synth_ai/cli/commands/status/subcommands/jobs.py +51 -0
  53. synth_ai/cli/commands/status/subcommands/models.py +35 -0
  54. synth_ai/cli/commands/status/subcommands/runs.py +34 -0
  55. synth_ai/cli/commands/status/subcommands/session.py +77 -0
  56. synth_ai/cli/commands/status/subcommands/summary.py +39 -0
  57. synth_ai/cli/commands/status/subcommands/utils.py +41 -0
  58. synth_ai/cli/commands/status/utils.py +23 -0
  59. synth_ai/cli/commands/train/__init__.py +53 -0
  60. synth_ai/cli/commands/train/core.py +22 -0
  61. synth_ai/cli/commands/train/errors.py +117 -0
  62. synth_ai/cli/commands/train/judge_schemas.py +201 -0
  63. synth_ai/cli/commands/train/judge_validation.py +305 -0
  64. synth_ai/cli/commands/train/prompt_learning_validation.py +633 -0
  65. synth_ai/cli/commands/train/validation.py +392 -0
  66. synth_ai/cli/demo_apps/__init__.py +10 -0
  67. synth_ai/cli/demo_apps/core/__init__.py +28 -0
  68. synth_ai/{demos → cli/demo_apps}/core/cli.py +783 -441
  69. synth_ai/cli/demo_apps/crafter/__init__.py +1 -0
  70. synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +55 -0
  71. synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +186 -0
  72. synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +74 -0
  73. synth_ai/cli/demo_apps/demo_registry.py +176 -0
  74. synth_ai/cli/demo_apps/demo_task_apps/__init__.py +7 -0
  75. synth_ai/{demos → cli/demo_apps}/demo_task_apps/core.py +75 -37
  76. synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
  77. synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/crafter_fft_4b.toml +53 -0
  78. synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +73 -0
  79. synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
  80. synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/_common.py +1 -2
  81. synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/app.py +2 -1
  82. synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +73 -0
  83. synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +3 -6
  84. synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +738 -0
  85. synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +39 -0
  86. synth_ai/cli/demo_apps/math/__init__.py +1 -0
  87. synth_ai/cli/demo_apps/math/_common.py +16 -0
  88. synth_ai/cli/demo_apps/math/app.py +38 -0
  89. synth_ai/cli/demo_apps/math/config.toml +75 -0
  90. synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
  91. synth_ai/cli/demo_apps/math/modal_task_app.py +698 -0
  92. synth_ai/cli/demo_apps/math/task_app_entry.py +53 -0
  93. synth_ai/cli/demo_apps/mipro/main.py +271 -0
  94. synth_ai/cli/demo_apps/mipro/task_app.py +922 -0
  95. synth_ai/cli/demo_apps/mipro/train_cfg.toml +92 -0
  96. synth_ai/cli/demos/__init__.py +12 -0
  97. synth_ai/cli/demos/demo.py +32 -0
  98. synth_ai/cli/demos/rl_demo.py +254 -0
  99. synth_ai/cli/deploy.py +216 -0
  100. synth_ai/cli/infra/__init__.py +14 -0
  101. synth_ai/cli/{balance.py → infra/balance.py} +16 -4
  102. synth_ai/cli/infra/mcp.py +35 -0
  103. synth_ai/cli/infra/modal_app.py +36 -0
  104. synth_ai/cli/infra/setup.py +69 -0
  105. synth_ai/cli/infra/status.py +16 -0
  106. synth_ai/cli/infra/turso.py +77 -0
  107. synth_ai/cli/lib/__init__.py +10 -0
  108. synth_ai/cli/lib/agents.py +76 -0
  109. synth_ai/cli/lib/apps/modal_app.py +101 -0
  110. synth_ai/cli/lib/apps/task_app.py +642 -0
  111. synth_ai/cli/lib/bin.py +39 -0
  112. synth_ai/cli/lib/env.py +375 -0
  113. synth_ai/cli/lib/errors.py +85 -0
  114. synth_ai/cli/lib/modal.py +315 -0
  115. synth_ai/cli/lib/plotting.py +126 -0
  116. synth_ai/cli/lib/prompt_args.py +39 -0
  117. synth_ai/cli/lib/prompts.py +284 -0
  118. synth_ai/cli/lib/sqld.py +122 -0
  119. synth_ai/cli/lib/task_app_discovery.py +884 -0
  120. synth_ai/cli/lib/task_app_env.py +295 -0
  121. synth_ai/cli/lib/train_cfgs.py +300 -0
  122. synth_ai/cli/lib/tunnel_records.py +207 -0
  123. synth_ai/cli/local/__init__.py +14 -0
  124. synth_ai/cli/local/experiment_queue/__init__.py +72 -0
  125. synth_ai/cli/local/experiment_queue/api_schemas.py +221 -0
  126. synth_ai/cli/local/experiment_queue/celery_app.py +208 -0
  127. synth_ai/cli/local/experiment_queue/config.py +128 -0
  128. synth_ai/cli/local/experiment_queue/config_utils.py +272 -0
  129. synth_ai/cli/local/experiment_queue/database.py +175 -0
  130. synth_ai/cli/local/experiment_queue/dispatcher.py +119 -0
  131. synth_ai/cli/local/experiment_queue/models.py +231 -0
  132. synth_ai/cli/local/experiment_queue/progress_info.py +160 -0
  133. synth_ai/cli/local/experiment_queue/results.py +373 -0
  134. synth_ai/cli/local/experiment_queue/schemas.py +131 -0
  135. synth_ai/cli/local/experiment_queue/service.py +344 -0
  136. synth_ai/cli/local/experiment_queue/status.py +372 -0
  137. synth_ai/cli/local/experiment_queue/status_tracker.py +360 -0
  138. synth_ai/cli/local/experiment_queue/tasks.py +1984 -0
  139. synth_ai/cli/local/experiment_queue/trace_storage.py +65 -0
  140. synth_ai/cli/local/experiment_queue/validation.py +157 -0
  141. synth_ai/cli/local/session/__init__.py +92 -0
  142. synth_ai/cli/local/session/client.py +383 -0
  143. synth_ai/cli/local/session/constants.py +63 -0
  144. synth_ai/cli/local/session/exceptions.py +105 -0
  145. synth_ai/cli/local/session/manager.py +139 -0
  146. synth_ai/cli/local/session/models.py +89 -0
  147. synth_ai/cli/local/session/query.py +110 -0
  148. synth_ai/cli/root.py +150 -108
  149. synth_ai/cli/task_apps/__init__.py +37 -0
  150. synth_ai/cli/task_apps/commands.py +3145 -0
  151. synth_ai/cli/task_apps/deploy.py +7 -0
  152. synth_ai/cli/task_apps/list.py +26 -0
  153. synth_ai/cli/task_apps/main.py +36 -0
  154. synth_ai/cli/task_apps/modal_serve.py +11 -0
  155. synth_ai/cli/task_apps/serve.py +11 -0
  156. synth_ai/cli/training/__init__.py +8 -0
  157. synth_ai/cli/training/train.py +5 -0
  158. synth_ai/cli/training/train_cfg.py +34 -0
  159. synth_ai/cli/{watch.py → training/watch.py} +13 -18
  160. synth_ai/cli/turso.py +52 -0
  161. synth_ai/cli/utils/__init__.py +8 -0
  162. synth_ai/cli/utils/experiments.py +235 -0
  163. synth_ai/cli/utils/queue.py +504 -0
  164. synth_ai/cli/{recent.py → utils/recent.py} +13 -7
  165. synth_ai/cli/{traces.py → utils/traces.py} +9 -5
  166. synth_ai/contracts/__init__.py +67 -0
  167. synth_ai/core/__init__.py +100 -0
  168. synth_ai/core/_utils/__init__.py +54 -0
  169. synth_ai/core/_utils/base_url.py +10 -0
  170. synth_ai/core/_utils/http.py +10 -0
  171. synth_ai/core/_utils/prompts.py +14 -0
  172. synth_ai/core/_utils/task_app_state.py +12 -0
  173. synth_ai/core/_utils/user_config.py +10 -0
  174. synth_ai/core/apps/common.py +116 -0
  175. synth_ai/core/auth.py +95 -0
  176. synth_ai/core/cfgs.py +240 -0
  177. synth_ai/core/config/__init__.py +16 -0
  178. synth_ai/core/config/base.py +168 -0
  179. synth_ai/core/config/resolver.py +89 -0
  180. synth_ai/core/env.py +231 -0
  181. synth_ai/core/errors.py +126 -0
  182. synth_ai/core/http.py +230 -0
  183. synth_ai/core/integrations/__init__.py +11 -0
  184. synth_ai/core/integrations/cloudflare.py +1710 -0
  185. synth_ai/core/integrations/mcp/__init__.py +6 -0
  186. synth_ai/core/integrations/mcp/__main__.py +8 -0
  187. synth_ai/core/integrations/mcp/claude.py +36 -0
  188. synth_ai/core/integrations/mcp/main.py +254 -0
  189. synth_ai/core/integrations/mcp/setup.py +100 -0
  190. synth_ai/core/integrations/modal.py +277 -0
  191. synth_ai/core/json.py +72 -0
  192. synth_ai/core/log_filter.py +99 -0
  193. synth_ai/core/logging.py +82 -0
  194. synth_ai/core/paths.py +107 -0
  195. synth_ai/core/pricing.py +109 -0
  196. synth_ai/core/process.py +233 -0
  197. synth_ai/core/ssl.py +25 -0
  198. synth_ai/core/storage/__init__.py +71 -0
  199. synth_ai/core/task_app_state.py +318 -0
  200. synth_ai/core/telemetry.py +282 -0
  201. synth_ai/{tracing_v3 → core/tracing_v3}/__init__.py +5 -1
  202. synth_ai/{tracing_v3 → core/tracing_v3}/abstractions.py +21 -4
  203. synth_ai/core/tracing_v3/config.py +229 -0
  204. synth_ai/core/tracing_v3/constants.py +21 -0
  205. synth_ai/{tracing_v3 → core/tracing_v3}/db_config.py +42 -29
  206. synth_ai/{tracing_v3 → core/tracing_v3}/decorators.py +80 -45
  207. synth_ai/{tracing_v3 → core/tracing_v3}/examples/basic_usage.py +15 -9
  208. synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +6 -4
  209. synth_ai/{tracing_v3 → core/tracing_v3}/llm_call_record_helpers.py +161 -61
  210. synth_ai/{tracing_v3 → core/tracing_v3}/migration_helper.py +1 -2
  211. synth_ai/{tracing_v3 → core/tracing_v3}/replica_sync.py +12 -7
  212. synth_ai/core/tracing_v3/serialization.py +130 -0
  213. synth_ai/{tracing_v3 → core/tracing_v3}/session_tracer.py +88 -21
  214. synth_ai/{tracing_v3 → core/tracing_v3}/storage/base.py +99 -12
  215. synth_ai/core/tracing_v3/storage/config.py +109 -0
  216. synth_ai/{tracing_v3 → core/tracing_v3}/storage/factory.py +11 -9
  217. synth_ai/{tracing_v3 → core/tracing_v3}/storage/utils.py +15 -11
  218. synth_ai/core/tracing_v3/trace_utils.py +326 -0
  219. synth_ai/core/tracing_v3/turso/__init__.py +12 -0
  220. synth_ai/core/tracing_v3/turso/daemon.py +278 -0
  221. synth_ai/{tracing_v3 → core/tracing_v3}/turso/models.py +7 -3
  222. synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
  223. synth_ai/{tracing_v3 → core/tracing_v3}/utils.py +5 -4
  224. synth_ai/core/urls.py +18 -0
  225. synth_ai/core/user_config.py +137 -0
  226. synth_ai/core/uvicorn.py +222 -0
  227. synth_ai/data/__init__.py +83 -0
  228. synth_ai/data/enums.py +123 -0
  229. synth_ai/data/rewards.py +152 -0
  230. synth_ai/data/traces.py +35 -0
  231. synth_ai/products/__init__.py +6 -0
  232. synth_ai/products/graph_evolve/__init__.py +46 -0
  233. synth_ai/products/graph_evolve/client.py +226 -0
  234. synth_ai/products/graph_evolve/config.py +591 -0
  235. synth_ai/products/graph_evolve/converters/__init__.py +42 -0
  236. synth_ai/products/graph_evolve/converters/openai_sft.py +484 -0
  237. synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +109 -0
  238. synth_ai/products/graph_evolve/run.py +222 -0
  239. synth_ai/products/graph_gepa/__init__.py +23 -0
  240. synth_ai/products/graph_gepa/converters/__init__.py +19 -0
  241. synth_ai/products/graph_gepa/converters/openai_sft.py +29 -0
  242. synth_ai/sdk/__init__.py +123 -0
  243. synth_ai/sdk/api/__init__.py +1 -0
  244. synth_ai/sdk/api/models/supported.py +514 -0
  245. synth_ai/sdk/api/research_agent/__init__.py +296 -0
  246. synth_ai/sdk/api/train/__init__.py +85 -0
  247. synth_ai/sdk/api/train/builders.py +895 -0
  248. synth_ai/sdk/api/train/cli.py +2199 -0
  249. synth_ai/sdk/api/train/config_finder.py +267 -0
  250. synth_ai/sdk/api/train/configs/__init__.py +65 -0
  251. synth_ai/sdk/api/train/configs/prompt_learning.py +1706 -0
  252. synth_ai/sdk/api/train/configs/rl.py +187 -0
  253. synth_ai/sdk/api/train/configs/sft.py +99 -0
  254. synth_ai/sdk/api/train/configs/shared.py +81 -0
  255. synth_ai/sdk/api/train/context_learning.py +312 -0
  256. synth_ai/sdk/api/train/env_resolver.py +418 -0
  257. synth_ai/sdk/api/train/graph_validators.py +216 -0
  258. synth_ai/sdk/api/train/graphgen.py +984 -0
  259. synth_ai/sdk/api/train/graphgen_models.py +823 -0
  260. synth_ai/sdk/api/train/graphgen_validators.py +109 -0
  261. synth_ai/sdk/api/train/local_api.py +10 -0
  262. synth_ai/sdk/api/train/pollers.py +124 -0
  263. synth_ai/sdk/api/train/progress/__init__.py +97 -0
  264. synth_ai/sdk/api/train/progress/dataclasses.py +569 -0
  265. synth_ai/sdk/api/train/progress/events.py +326 -0
  266. synth_ai/sdk/api/train/progress/results.py +428 -0
  267. synth_ai/sdk/api/train/progress/tracker.py +641 -0
  268. synth_ai/sdk/api/train/prompt_learning.py +469 -0
  269. synth_ai/sdk/api/train/rl.py +441 -0
  270. synth_ai/sdk/api/train/sft.py +396 -0
  271. synth_ai/sdk/api/train/summary.py +522 -0
  272. synth_ai/sdk/api/train/supported_algos.py +147 -0
  273. synth_ai/sdk/api/train/task_app.py +351 -0
  274. synth_ai/sdk/api/train/utils.py +279 -0
  275. synth_ai/sdk/api/train/validators.py +2424 -0
  276. synth_ai/sdk/graphs/__init__.py +15 -0
  277. synth_ai/sdk/graphs/completions.py +570 -0
  278. synth_ai/{inference → sdk/inference}/__init__.py +0 -1
  279. synth_ai/sdk/inference/client.py +128 -0
  280. synth_ai/sdk/jobs/__init__.py +16 -0
  281. synth_ai/sdk/jobs/client.py +371 -0
  282. synth_ai/sdk/judging/__init__.py +14 -0
  283. synth_ai/sdk/judging/base.py +24 -0
  284. synth_ai/sdk/judging/client.py +40 -0
  285. synth_ai/sdk/judging/schemas.py +222 -0
  286. synth_ai/sdk/judging/types.py +42 -0
  287. synth_ai/sdk/learning/__init__.py +99 -0
  288. synth_ai/sdk/learning/algorithms.py +14 -0
  289. synth_ai/{learning → sdk/learning}/client.py +121 -30
  290. synth_ai/sdk/learning/config.py +5 -0
  291. synth_ai/{learning → sdk/learning}/constants.py +0 -2
  292. synth_ai/sdk/learning/context_learning_client.py +531 -0
  293. synth_ai/sdk/learning/context_learning_types.py +292 -0
  294. synth_ai/sdk/learning/ft_client.py +7 -0
  295. synth_ai/{learning → sdk/learning}/health.py +15 -9
  296. synth_ai/{learning → sdk/learning}/jobs.py +44 -47
  297. synth_ai/sdk/learning/prompt_extraction.py +334 -0
  298. synth_ai/sdk/learning/prompt_learning_client.py +455 -0
  299. synth_ai/sdk/learning/prompt_learning_types.py +186 -0
  300. synth_ai/{rl → sdk/learning/rl}/__init__.py +13 -8
  301. synth_ai/{learning/rl_client.py → sdk/learning/rl/client.py} +89 -77
  302. synth_ai/sdk/learning/rl/config.py +31 -0
  303. synth_ai/{rl → sdk/learning/rl}/contracts.py +5 -14
  304. synth_ai/{rl → sdk/learning/rl}/env_keys.py +45 -16
  305. synth_ai/sdk/learning/rl/secrets.py +13 -0
  306. synth_ai/sdk/learning/rl_client.py +5 -0
  307. synth_ai/sdk/learning/sft/__init__.py +29 -0
  308. synth_ai/sdk/learning/sft/client.py +95 -0
  309. synth_ai/sdk/learning/sft/config.py +270 -0
  310. synth_ai/sdk/learning/sft/data.py +698 -0
  311. synth_ai/sdk/learning/sse.py +57 -0
  312. synth_ai/sdk/learning/validators.py +52 -0
  313. synth_ai/sdk/localapi/__init__.py +40 -0
  314. synth_ai/sdk/localapi/apps/__init__.py +28 -0
  315. synth_ai/sdk/localapi/client.py +10 -0
  316. synth_ai/sdk/localapi/contracts.py +10 -0
  317. synth_ai/sdk/localapi/helpers.py +519 -0
  318. synth_ai/sdk/localapi/rollouts.py +87 -0
  319. synth_ai/sdk/localapi/server.py +29 -0
  320. synth_ai/sdk/localapi/template.py +70 -0
  321. synth_ai/sdk/streaming/__init__.py +35 -0
  322. synth_ai/sdk/streaming/config.py +94 -0
  323. synth_ai/sdk/streaming/handlers.py +1997 -0
  324. synth_ai/sdk/streaming/streamer.py +713 -0
  325. synth_ai/sdk/streaming/types.py +112 -0
  326. synth_ai/sdk/task/__init__.py +164 -0
  327. synth_ai/sdk/task/apps/__init__.py +169 -0
  328. synth_ai/sdk/task/auth.py +165 -0
  329. synth_ai/sdk/task/client.py +175 -0
  330. synth_ai/sdk/task/config.py +257 -0
  331. synth_ai/sdk/task/contracts.py +219 -0
  332. synth_ai/sdk/task/datasets.py +108 -0
  333. synth_ai/sdk/task/errors.py +50 -0
  334. synth_ai/sdk/task/health.py +34 -0
  335. synth_ai/sdk/task/in_process.py +1190 -0
  336. synth_ai/sdk/task/in_process_runner.py +314 -0
  337. synth_ai/sdk/task/inference_api.py +299 -0
  338. synth_ai/sdk/task/json.py +111 -0
  339. synth_ai/sdk/task/proxy.py +287 -0
  340. synth_ai/sdk/task/rubrics/__init__.py +55 -0
  341. synth_ai/sdk/task/rubrics/loaders.py +156 -0
  342. synth_ai/sdk/task/rubrics/models.py +57 -0
  343. synth_ai/sdk/task/rubrics/scoring.py +116 -0
  344. synth_ai/sdk/task/rubrics/strict.py +149 -0
  345. synth_ai/sdk/task/rubrics.py +219 -0
  346. synth_ai/sdk/task/server.py +631 -0
  347. synth_ai/sdk/task/trace_correlation_helpers.py +539 -0
  348. synth_ai/sdk/task/tracing_utils.py +95 -0
  349. synth_ai/sdk/task/validators.py +441 -0
  350. synth_ai/sdk/task/vendors.py +59 -0
  351. synth_ai/sdk/training/__init__.py +102 -0
  352. synth_ai/sdk/tunnels/__init__.py +83 -0
  353. synth_ai/sdk/tunnels/cleanup.py +83 -0
  354. synth_ai/sdk/tunnels/ports.py +120 -0
  355. synth_ai/utils/__init__.py +213 -0
  356. synth_ai-0.4.3.dist-info/METADATA +262 -0
  357. synth_ai-0.4.3.dist-info/RECORD +370 -0
  358. {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/entry_points.txt +0 -1
  359. synth_ai/cli/calc.py +0 -69
  360. synth_ai/cli/demo.py +0 -144
  361. synth_ai/cli/legacy_root_backup.py +0 -470
  362. synth_ai/cli/man.py +0 -106
  363. synth_ai/cli/rl_demo.py +0 -202
  364. synth_ai/cli/status.py +0 -133
  365. synth_ai/config/base_url.py +0 -107
  366. synth_ai/core/experiment.py +0 -15
  367. synth_ai/core/system.py +0 -15
  368. synth_ai/demos/core/__init__.py +0 -1
  369. synth_ai/demos/demo_task_apps/__init__.py +0 -1
  370. synth_ai/demos/demo_task_apps/math/config.toml +0 -129
  371. synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +0 -22
  372. synth_ai/demos/demo_task_apps/math/modal_task_app.py +0 -415
  373. synth_ai/environments/__init__.py +0 -31
  374. synth_ai/environments/environment/__init__.py +0 -1
  375. synth_ai/environments/environment/artifacts/__init__.py +0 -1
  376. synth_ai/environments/environment/artifacts/base.py +0 -52
  377. synth_ai/environments/environment/core.py +0 -67
  378. synth_ai/environments/environment/db/__init__.py +0 -1
  379. synth_ai/environments/environment/db/sqlite.py +0 -45
  380. synth_ai/environments/environment/registry.py +0 -233
  381. synth_ai/environments/environment/resources/sqlite.py +0 -45
  382. synth_ai/environments/environment/results.py +0 -1
  383. synth_ai/environments/environment/rewards/__init__.py +0 -1
  384. synth_ai/environments/environment/rewards/core.py +0 -29
  385. synth_ai/environments/environment/shared_engine.py +0 -26
  386. synth_ai/environments/environment/tools/__init__.py +0 -200
  387. synth_ai/environments/examples/__init__.py +0 -1
  388. synth_ai/environments/examples/bandit/__init__.py +0 -33
  389. synth_ai/environments/examples/bandit/engine.py +0 -294
  390. synth_ai/environments/examples/bandit/environment.py +0 -194
  391. synth_ai/environments/examples/bandit/taskset.py +0 -200
  392. synth_ai/environments/examples/crafter_classic/__init__.py +0 -8
  393. synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +0 -250
  394. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +0 -59
  395. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +0 -152
  396. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +0 -24
  397. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +0 -1194
  398. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +0 -56
  399. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +0 -32
  400. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  401. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +0 -384
  402. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +0 -53
  403. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +0 -178
  404. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +0 -222
  405. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +0 -183
  406. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +0 -210
  407. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +0 -206
  408. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +0 -49
  409. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +0 -64
  410. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +0 -88
  411. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +0 -77
  412. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +0 -324
  413. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  414. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +0 -362
  415. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +0 -49
  416. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +0 -332
  417. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +0 -97
  418. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +0 -217
  419. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +0 -87
  420. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +0 -88
  421. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +0 -195
  422. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +0 -400
  423. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +0 -195
  424. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +0 -56
  425. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +0 -858
  426. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +0 -52
  427. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +0 -874
  428. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +0 -1412
  429. synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +0 -216
  430. synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +0 -296
  431. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +0 -58
  432. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +0 -464
  433. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +0 -152
  434. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +0 -51
  435. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +0 -1412
  436. synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +0 -112
  437. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +0 -203
  438. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +0 -305
  439. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +0 -126
  440. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +0 -94
  441. synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +0 -142
  442. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +0 -26
  443. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +0 -984
  444. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +0 -724
  445. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +0 -386
  446. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +0 -205
  447. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +0 -150
  448. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +0 -283
  449. synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +0 -280
  450. synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +0 -456
  451. synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +0 -166
  452. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +0 -102
  453. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +0 -128
  454. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +0 -655
  455. synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +0 -202
  456. synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +0 -166
  457. synth_ai/environments/examples/crafter_classic/config_logging.py +0 -111
  458. synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
  459. synth_ai/environments/examples/crafter_classic/engine.py +0 -579
  460. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +0 -64
  461. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +0 -6
  462. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +0 -75
  463. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +0 -267
  464. synth_ai/environments/examples/crafter_classic/environment.py +0 -404
  465. synth_ai/environments/examples/crafter_classic/taskset.py +0 -233
  466. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +0 -228
  467. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +0 -299
  468. synth_ai/environments/examples/crafter_custom/__init__.py +0 -4
  469. synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +0 -1
  470. synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +0 -202
  471. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +0 -7
  472. synth_ai/environments/examples/crafter_custom/crafter/config.py +0 -182
  473. synth_ai/environments/examples/crafter_custom/crafter/constants.py +0 -8
  474. synth_ai/environments/examples/crafter_custom/crafter/engine.py +0 -269
  475. synth_ai/environments/examples/crafter_custom/crafter/env.py +0 -262
  476. synth_ai/environments/examples/crafter_custom/crafter/objects.py +0 -417
  477. synth_ai/environments/examples/crafter_custom/crafter/recorder.py +0 -187
  478. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +0 -118
  479. synth_ai/environments/examples/crafter_custom/dataset_builder.py +0 -373
  480. synth_ai/environments/examples/crafter_custom/environment.py +0 -312
  481. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +0 -159
  482. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +0 -158
  483. synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +0 -71
  484. synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +0 -105
  485. synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +0 -119
  486. synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +0 -52
  487. synth_ai/environments/examples/crafter_custom/run_dataset.py +0 -305
  488. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +0 -156
  489. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +0 -281
  490. synth_ai/environments/examples/enron/art_helpers/types_enron.py +0 -25
  491. synth_ai/environments/examples/enron/engine.py +0 -295
  492. synth_ai/environments/examples/enron/environment.py +0 -166
  493. synth_ai/environments/examples/enron/taskset.py +0 -112
  494. synth_ai/environments/examples/enron/units/keyword_stats.py +0 -112
  495. synth_ai/environments/examples/minigrid/__init__.py +0 -48
  496. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +0 -1188
  497. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +0 -48
  498. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +0 -562
  499. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +0 -221
  500. synth_ai/environments/examples/minigrid/engine.py +0 -589
  501. synth_ai/environments/examples/minigrid/environment.py +0 -274
  502. synth_ai/environments/examples/minigrid/environment_mapping.py +0 -242
  503. synth_ai/environments/examples/minigrid/puzzle_loader.py +0 -417
  504. synth_ai/environments/examples/minigrid/taskset.py +0 -583
  505. synth_ai/environments/examples/nethack/__init__.py +0 -7
  506. synth_ai/environments/examples/nethack/achievements.py +0 -337
  507. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +0 -981
  508. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +0 -74
  509. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +0 -831
  510. synth_ai/environments/examples/nethack/engine.py +0 -739
  511. synth_ai/environments/examples/nethack/environment.py +0 -256
  512. synth_ai/environments/examples/nethack/helpers/__init__.py +0 -41
  513. synth_ai/environments/examples/nethack/helpers/action_mapping.py +0 -301
  514. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +0 -402
  515. synth_ai/environments/examples/nethack/helpers/observation_utils.py +0 -433
  516. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +0 -200
  517. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +0 -269
  518. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +0 -308
  519. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +0 -431
  520. synth_ai/environments/examples/nethack/taskset.py +0 -323
  521. synth_ai/environments/examples/red/__init__.py +0 -7
  522. synth_ai/environments/examples/red/agent_demos/__init__.py +0 -1
  523. synth_ai/environments/examples/red/config_logging.py +0 -110
  524. synth_ai/environments/examples/red/engine.py +0 -694
  525. synth_ai/environments/examples/red/engine_helpers/__init__.py +0 -1
  526. synth_ai/environments/examples/red/engine_helpers/memory_map.py +0 -28
  527. synth_ai/environments/examples/red/engine_helpers/reward_components.py +0 -276
  528. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +0 -142
  529. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +0 -57
  530. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +0 -284
  531. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +0 -150
  532. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +0 -138
  533. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +0 -57
  534. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +0 -331
  535. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +0 -121
  536. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +0 -559
  537. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +0 -313
  538. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +0 -148
  539. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +0 -247
  540. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +0 -368
  541. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +0 -140
  542. synth_ai/environments/examples/red/environment.py +0 -238
  543. synth_ai/environments/examples/red/taskset.py +0 -79
  544. synth_ai/environments/examples/red/units/__init__.py +0 -1
  545. synth_ai/environments/examples/sokoban/__init__.py +0 -1
  546. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +0 -899
  547. synth_ai/environments/examples/sokoban/engine.py +0 -678
  548. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +0 -1
  549. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +0 -657
  550. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +0 -18
  551. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +0 -3
  552. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +0 -131
  553. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +0 -370
  554. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +0 -332
  555. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +0 -306
  556. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +0 -67
  557. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +0 -115
  558. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +0 -123
  559. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +0 -394
  560. synth_ai/environments/examples/sokoban/environment.py +0 -229
  561. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +0 -440
  562. synth_ai/environments/examples/sokoban/puzzle_loader.py +0 -312
  563. synth_ai/environments/examples/sokoban/taskset.py +0 -428
  564. synth_ai/environments/examples/sokoban/units/astar_common.py +0 -95
  565. synth_ai/environments/examples/tictactoe/__init__.py +0 -1
  566. synth_ai/environments/examples/tictactoe/engine.py +0 -368
  567. synth_ai/environments/examples/tictactoe/environment.py +0 -240
  568. synth_ai/environments/examples/tictactoe/taskset.py +0 -215
  569. synth_ai/environments/examples/verilog/__init__.py +0 -10
  570. synth_ai/environments/examples/verilog/engine.py +0 -329
  571. synth_ai/environments/examples/verilog/environment.py +0 -350
  572. synth_ai/environments/examples/verilog/taskset.py +0 -420
  573. synth_ai/environments/examples/wordle/__init__.py +0 -29
  574. synth_ai/environments/examples/wordle/engine.py +0 -398
  575. synth_ai/environments/examples/wordle/environment.py +0 -159
  576. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +0 -75
  577. synth_ai/environments/examples/wordle/taskset.py +0 -230
  578. synth_ai/environments/reproducibility/core.py +0 -42
  579. synth_ai/environments/reproducibility/helpers.py +0 -0
  580. synth_ai/environments/reproducibility/tree.py +0 -364
  581. synth_ai/environments/service/app.py +0 -98
  582. synth_ai/environments/service/core_routes.py +0 -1020
  583. synth_ai/environments/service/external_registry.py +0 -56
  584. synth_ai/environments/service/registry.py +0 -9
  585. synth_ai/environments/stateful/__init__.py +0 -1
  586. synth_ai/environments/stateful/core.py +0 -163
  587. synth_ai/environments/stateful/engine.py +0 -21
  588. synth_ai/environments/stateful/state.py +0 -7
  589. synth_ai/environments/tasks/api.py +0 -19
  590. synth_ai/environments/tasks/core.py +0 -80
  591. synth_ai/environments/tasks/filters.py +0 -41
  592. synth_ai/environments/tasks/utils.py +0 -91
  593. synth_ai/environments/v0_observability/history.py +0 -3
  594. synth_ai/environments/v0_observability/log.py +0 -2
  595. synth_ai/evals/base.py +0 -15
  596. synth_ai/experimental/synth_oss.py +0 -446
  597. synth_ai/handshake.py +0 -63
  598. synth_ai/http.py +0 -26
  599. synth_ai/http_client.py +0 -104
  600. synth_ai/inference/client.py +0 -20
  601. synth_ai/install_sqld.sh +0 -40
  602. synth_ai/jobs/client.py +0 -246
  603. synth_ai/learning/__init__.py +0 -24
  604. synth_ai/learning/config.py +0 -43
  605. synth_ai/learning/filtering.py +0 -0
  606. synth_ai/learning/ft_client.py +0 -59
  607. synth_ai/learning/offline/dpo.py +0 -0
  608. synth_ai/learning/offline/providers.py +0 -7
  609. synth_ai/learning/offline/sft.py +0 -0
  610. synth_ai/learning/offline/shared.py +0 -0
  611. synth_ai/learning/online/grpo.py +0 -0
  612. synth_ai/learning/online/irft.py +0 -0
  613. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  614. synth_ai/learning/prompts/gepa.py +0 -0
  615. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -213
  616. synth_ai/learning/prompts/mipro.py +0 -289
  617. synth_ai/learning/prompts/random_search.py +0 -246
  618. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  619. synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
  620. synth_ai/learning/sse.py +0 -58
  621. synth_ai/learning/validators.py +0 -48
  622. synth_ai/lm/__init__.py +0 -51
  623. synth_ai/lm/caching/constants.py +0 -6
  624. synth_ai/lm/caching/dbs.py +0 -0
  625. synth_ai/lm/caching/ephemeral.py +0 -102
  626. synth_ai/lm/caching/handler.py +0 -137
  627. synth_ai/lm/caching/initialize.py +0 -11
  628. synth_ai/lm/caching/persistent.py +0 -114
  629. synth_ai/lm/config.py +0 -110
  630. synth_ai/lm/constants.py +0 -32
  631. synth_ai/lm/core/__init__.py +0 -8
  632. synth_ai/lm/core/all.py +0 -73
  633. synth_ai/lm/core/exceptions.py +0 -7
  634. synth_ai/lm/core/main.py +0 -319
  635. synth_ai/lm/core/main_v3.py +0 -594
  636. synth_ai/lm/core/synth_models.py +0 -48
  637. synth_ai/lm/core/vendor_clients.py +0 -188
  638. synth_ai/lm/cost/__init__.py +0 -0
  639. synth_ai/lm/cost/monitor.py +0 -1
  640. synth_ai/lm/cost/statefulness.py +0 -1
  641. synth_ai/lm/injection.py +0 -80
  642. synth_ai/lm/overrides.py +0 -206
  643. synth_ai/lm/provider_support/__init__.py +0 -8
  644. synth_ai/lm/provider_support/anthropic.py +0 -972
  645. synth_ai/lm/provider_support/openai.py +0 -1139
  646. synth_ai/lm/provider_support/suppress_logging.py +0 -31
  647. synth_ai/lm/structured_outputs/__init__.py +0 -0
  648. synth_ai/lm/structured_outputs/handler.py +0 -440
  649. synth_ai/lm/structured_outputs/inject.py +0 -297
  650. synth_ai/lm/structured_outputs/rehabilitate.py +0 -185
  651. synth_ai/lm/tools/__init__.py +0 -3
  652. synth_ai/lm/tools/base.py +0 -172
  653. synth_ai/lm/unified_interface.py +0 -202
  654. synth_ai/lm/vendors/__init__.py +0 -0
  655. synth_ai/lm/vendors/base.py +0 -81
  656. synth_ai/lm/vendors/core/__init__.py +0 -0
  657. synth_ai/lm/vendors/core/anthropic_api.py +0 -387
  658. synth_ai/lm/vendors/core/gemini_api.py +0 -292
  659. synth_ai/lm/vendors/core/mistral_api.py +0 -322
  660. synth_ai/lm/vendors/core/openai_api.py +0 -225
  661. synth_ai/lm/vendors/core/synth_dev_api.py +0 -0
  662. synth_ai/lm/vendors/local/__init__.py +0 -0
  663. synth_ai/lm/vendors/local/ollama.py +0 -0
  664. synth_ai/lm/vendors/openai_standard.py +0 -780
  665. synth_ai/lm/vendors/openai_standard_responses.py +0 -256
  666. synth_ai/lm/vendors/retries.py +0 -22
  667. synth_ai/lm/vendors/supported/__init__.py +0 -0
  668. synth_ai/lm/vendors/supported/custom_endpoint.py +0 -417
  669. synth_ai/lm/vendors/supported/deepseek.py +0 -69
  670. synth_ai/lm/vendors/supported/grok.py +0 -75
  671. synth_ai/lm/vendors/supported/groq.py +0 -16
  672. synth_ai/lm/vendors/supported/ollama.py +0 -15
  673. synth_ai/lm/vendors/supported/openrouter.py +0 -74
  674. synth_ai/lm/vendors/supported/together.py +0 -11
  675. synth_ai/lm/vendors/synth_client.py +0 -808
  676. synth_ai/lm/warmup.py +0 -186
  677. synth_ai/rl/secrets.py +0 -19
  678. synth_ai/scripts/verify_rewards.py +0 -100
  679. synth_ai/task/__init__.py +0 -10
  680. synth_ai/task/contracts.py +0 -120
  681. synth_ai/task/health.py +0 -28
  682. synth_ai/task/validators.py +0 -12
  683. synth_ai/tracing/__init__.py +0 -30
  684. synth_ai/tracing_v1/__init__.py +0 -33
  685. synth_ai/tracing_v3/config.py +0 -84
  686. synth_ai/tracing_v3/storage/config.py +0 -62
  687. synth_ai/tracing_v3/turso/__init__.py +0 -25
  688. synth_ai/tracing_v3/turso/daemon.py +0 -144
  689. synth_ai/tracing_v3/turso/manager.py +0 -760
  690. synth_ai/v0/tracing/__init__.py +0 -0
  691. synth_ai/v0/tracing/abstractions.py +0 -224
  692. synth_ai/v0/tracing/base_client.py +0 -91
  693. synth_ai/v0/tracing/client_manager.py +0 -131
  694. synth_ai/v0/tracing/config.py +0 -142
  695. synth_ai/v0/tracing/context.py +0 -146
  696. synth_ai/v0/tracing/decorators.py +0 -682
  697. synth_ai/v0/tracing/events/__init__.py +0 -0
  698. synth_ai/v0/tracing/events/manage.py +0 -147
  699. synth_ai/v0/tracing/events/scope.py +0 -86
  700. synth_ai/v0/tracing/events/store.py +0 -228
  701. synth_ai/v0/tracing/immediate_client.py +0 -151
  702. synth_ai/v0/tracing/local.py +0 -18
  703. synth_ai/v0/tracing/log_client_base.py +0 -73
  704. synth_ai/v0/tracing/retry_queue.py +0 -186
  705. synth_ai/v0/tracing/trackers.py +0 -515
  706. synth_ai/v0/tracing/upload.py +0 -512
  707. synth_ai/v0/tracing/utils.py +0 -9
  708. synth_ai/v0/tracing_v1/__init__.py +0 -16
  709. synth_ai/v0/tracing_v1/abstractions.py +0 -224
  710. synth_ai/v0/tracing_v1/base_client.py +0 -91
  711. synth_ai/v0/tracing_v1/client_manager.py +0 -131
  712. synth_ai/v0/tracing_v1/config.py +0 -142
  713. synth_ai/v0/tracing_v1/context.py +0 -146
  714. synth_ai/v0/tracing_v1/decorators.py +0 -703
  715. synth_ai/v0/tracing_v1/events/__init__.py +0 -0
  716. synth_ai/v0/tracing_v1/events/manage.py +0 -147
  717. synth_ai/v0/tracing_v1/events/scope.py +0 -86
  718. synth_ai/v0/tracing_v1/events/store.py +0 -228
  719. synth_ai/v0/tracing_v1/immediate_client.py +0 -151
  720. synth_ai/v0/tracing_v1/local.py +0 -18
  721. synth_ai/v0/tracing_v1/log_client_base.py +0 -73
  722. synth_ai/v0/tracing_v1/retry_queue.py +0 -186
  723. synth_ai/v0/tracing_v1/trackers.py +0 -515
  724. synth_ai/v0/tracing_v1/upload.py +0 -527
  725. synth_ai/v0/tracing_v1/utils.py +0 -9
  726. synth_ai/zyk/__init__.py +0 -30
  727. synth_ai-0.2.8.dev2.dist-info/METADATA +0 -129
  728. synth_ai-0.2.8.dev2.dist-info/RECORD +0 -420
  729. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/__init__.py +0 -0
  730. /synth_ai/{lm/caching → core/apps}/__init__.py +0 -0
  731. /synth_ai/{tracing_v3 → core/tracing_v3}/lm_call_record_abstractions.py +0 -0
  732. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/__init__.py +0 -0
  733. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/exceptions.py +0 -0
  734. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/types.py +0 -0
  735. /synth_ai/{compound/cais.py → py.typed} +0 -0
  736. /synth_ai/{learning → sdk/learning}/core.py +0 -0
  737. /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
  738. {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/WHEEL +0 -0
  739. {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/licenses/LICENSE +0 -0
  740. {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,713 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import contextlib
5
+ import json
6
+ import logging
7
+ import random
8
+ import sys
9
+ import time
10
+ from dataclasses import dataclass
11
+ from typing import Any, AsyncIterator, Iterable, Sequence
12
+
13
+ import aiohttp
14
+
15
+ from synth_ai.core.http import AsyncHttpClient, sleep
16
+ from synth_ai.core.telemetry import log_info
17
+
18
+ from .config import StreamConfig
19
+ from .handlers import StreamHandler
20
+ from .types import StreamMessage, StreamType
21
+
22
+ TERMINAL_STATUSES = {"succeeded", "failed", "cancelled", "canceled", "completed"}
23
+ TERMINAL_EVENT_SUCCESS = {
24
+ "sft.job.completed",
25
+ "rl.train.completed",
26
+ "rl.job.completed",
27
+ "context.learning.job.completed",
28
+ "workflow.completed",
29
+ "training.completed",
30
+ # GraphGen / Graph Evolve events
31
+ "optimization_completed",
32
+ "graphgen.completed",
33
+ "graph_evolve.completed",
34
+ # GEPA / Prompt Learning events
35
+ "prompt.learning.gepa.complete",
36
+ "prompt.learning.mipro.complete",
37
+ "gepa.complete",
38
+ "mipro.complete",
39
+ }
40
+ TERMINAL_EVENT_FAILURE = {
41
+ "sft.job.failed",
42
+ "rl.train.failed",
43
+ "rl.job.failed",
44
+ "context.learning.job.failed",
45
+ "workflow.failed",
46
+ "training.failed",
47
+ # GraphGen / Graph Evolve events
48
+ "optimization_failed",
49
+ "graphgen.failed",
50
+ "graph_evolve.failed",
51
+ # GEPA / Prompt Learning events
52
+ "prompt.learning.gepa.failed",
53
+ "prompt.learning.mipro.failed",
54
+ "gepa.failed",
55
+ "mipro.failed",
56
+ }
57
+
58
+
59
+ @dataclass(slots=True)
60
+ class StreamEndpoints:
61
+ """Collection of endpoint paths (with optional fallbacks) to poll for a job."""
62
+
63
+ status: str | None
64
+ events: str | None = None
65
+ metrics: str | None = None
66
+ timeline: str | None = None
67
+ status_fallbacks: tuple[str, ...] = ()
68
+ event_fallbacks: tuple[str, ...] = ()
69
+ metric_fallbacks: tuple[str, ...] = ()
70
+ timeline_fallbacks: tuple[str, ...] = ()
71
+
72
+ @classmethod
73
+ def learning(cls, job_id: str) -> StreamEndpoints:
74
+ base = f"/learning/jobs/{job_id}"
75
+ return cls(
76
+ status=base,
77
+ events=f"{base}/events",
78
+ metrics=f"{base}/metrics",
79
+ timeline=f"{base}/timeline",
80
+ )
81
+
82
+ @classmethod
83
+ def prompt_learning(cls, job_id: str) -> StreamEndpoints:
84
+ """Endpoints for prompt learning jobs (MIPRO/GEPA)."""
85
+ base = f"/prompt-learning/online/jobs/{job_id}"
86
+ return cls(
87
+ status=base,
88
+ events=f"{base}/events",
89
+ metrics=f"{base}/metrics",
90
+ timeline=None,
91
+ status_fallbacks=(
92
+ f"/learning/jobs/{job_id}",
93
+ f"/orchestration/jobs/{job_id}",
94
+ ),
95
+ event_fallbacks=(
96
+ f"/learning/jobs/{job_id}/events",
97
+ f"/orchestration/jobs/{job_id}/events",
98
+ ),
99
+ )
100
+
101
+ @classmethod
102
+ def context_learning(cls, job_id: str) -> StreamEndpoints:
103
+ """Endpoints for context learning jobs."""
104
+ base = f"/context-learning/jobs/{job_id}"
105
+ return cls(
106
+ status=base,
107
+ events=f"{base}/events",
108
+ metrics=f"{base}/metrics",
109
+ timeline=None,
110
+ )
111
+
112
+ @property
113
+ def events_stream_url(self) -> str | None:
114
+ """Get the SSE streaming URL for events if available."""
115
+ if self.events:
116
+ if self.events.endswith("/events"):
117
+ return f"{self.events}/stream"
118
+ return None
119
+
120
+ @classmethod
121
+ def rl(cls, job_id: str) -> StreamEndpoints:
122
+ base = f"/rl/jobs/{job_id}"
123
+ return cls(
124
+ status=base,
125
+ events=f"{base}/events",
126
+ metrics=f"{base}/metrics",
127
+ timeline=f"{base}/timeline",
128
+ status_fallbacks=(
129
+ f"/learning/jobs/{job_id}",
130
+ f"/orchestration/jobs/{job_id}",
131
+ ),
132
+ event_fallbacks=(
133
+ f"/learning/jobs/{job_id}/events",
134
+ f"/orchestration/jobs/{job_id}/events",
135
+ ),
136
+ metric_fallbacks=(
137
+ f"/learning/jobs/{job_id}/metrics",
138
+ ),
139
+ timeline_fallbacks=(
140
+ f"/learning/jobs/{job_id}/timeline",
141
+ ),
142
+ )
143
+
144
+ @classmethod
145
+ def graphgen(cls, job_id: str) -> StreamEndpoints:
146
+ """Endpoints for GraphGen workflow optimization jobs.
147
+
148
+ GraphGen jobs use /api/graphgen/jobs/{job_id} endpoints.
149
+ The backend handles GraphGen -> graph_evolve job ID resolution internally using job_relationships.
150
+ No fallbacks needed - GraphGen endpoints resolve everything.
151
+ """
152
+ base = f"/graphgen/jobs/{job_id}"
153
+ return cls(
154
+ status=base,
155
+ events=f"{base}/events",
156
+ metrics=f"{base}/metrics",
157
+ timeline=None,
158
+ )
159
+
160
+ @classmethod
161
+ def adas(cls, job_id: str) -> StreamEndpoints:
162
+ """Legacy alias for GraphGen workflow optimization jobs."""
163
+ return cls.graphgen(job_id)
164
+
165
+
166
+ class JobStreamer:
167
+ """Poll job endpoints and dispatch messages to configured handlers."""
168
+
169
+ def __init__(
170
+ self,
171
+ *,
172
+ base_url: str,
173
+ api_key: str,
174
+ job_id: str,
175
+ endpoints: StreamEndpoints | None = None,
176
+ config: StreamConfig | None = None,
177
+ handlers: Sequence[StreamHandler] | None = None,
178
+ interval_seconds: float = 2.0,
179
+ timeout_seconds: float | None = None,
180
+ http_timeout: float = 60.0,
181
+ http_client: AsyncHttpClient | None = None,
182
+ sleep_fn= sleep,
183
+ ) -> None:
184
+ self.base_url = base_url.rstrip("/")
185
+ self.api_key = api_key
186
+ self.job_id = job_id
187
+ self.endpoints = endpoints or StreamEndpoints.learning(job_id)
188
+ self.config = config or StreamConfig.default()
189
+ self.handlers: list[StreamHandler] = list(handlers or [])
190
+ self.interval_seconds = interval_seconds
191
+ self.timeout_seconds = timeout_seconds
192
+ self.http_timeout = http_timeout
193
+ self._http = http_client
194
+ self._sleep = sleep_fn
195
+
196
+ status_sources: list[str | None] = [self.endpoints.status]
197
+ status_sources.extend(self.endpoints.status_fallbacks)
198
+ self._status_paths = [p for p in status_sources if p]
199
+
200
+ event_sources: list[str | None] = [self.endpoints.events]
201
+ event_sources.extend(self.endpoints.event_fallbacks)
202
+ self._event_paths = [p for p in event_sources if p]
203
+
204
+ metric_sources: list[str | None] = [self.endpoints.metrics]
205
+ metric_sources.extend(self.endpoints.metric_fallbacks)
206
+ self._metric_paths = [p for p in metric_sources if p]
207
+
208
+ timeline_sources: list[str | None] = [self.endpoints.timeline]
209
+ timeline_sources.extend(self.endpoints.timeline_fallbacks)
210
+ self._timeline_paths = [p for p in timeline_sources if p]
211
+
212
+ self._last_seq_by_stream: dict[str, int] = {}
213
+ self._metric_cursors: dict[str, tuple[int | None, str]] = {}
214
+ self._seen_messages: set[str] = set()
215
+ self._last_status_payload: dict[str, Any] | None = None
216
+ self._last_status_value: str | None = None
217
+ self._terminal_seen = False
218
+ self._terminal_event_status: str | None = None
219
+
220
+ if not self.handlers:
221
+ from .handlers import CLIHandler
222
+
223
+ self.handlers = [CLIHandler()]
224
+
225
+ async def stream_until_terminal(self) -> dict[str, Any]:
226
+ """Stream configured endpoints until the job reaches a terminal state."""
227
+ ctx: dict[str, Any] = {"job_id": self.job_id, "base_url": self.base_url}
228
+ log_info("JobStreamer.stream_until_terminal invoked", ctx=ctx)
229
+ http_cm = self._http or AsyncHttpClient(self.base_url, self.api_key, timeout=self.http_timeout)
230
+ async with http_cm as http:
231
+ # Use SSE streaming exclusively for events (prompt learning jobs)
232
+ # SSE provides real-time event delivery from Redis, avoiding empty polling responses
233
+ sse_url = self.endpoints.events_stream_url
234
+ if sse_url and StreamType.EVENTS in self.config.enabled_streams:
235
+ # SSE streaming - real-time event delivery, with concurrent status polling
236
+
237
+ # Create a queue to receive events from SSE stream
238
+ event_queue: asyncio.Queue = asyncio.Queue()
239
+ sse_done = asyncio.Event()
240
+
241
+ async def sse_reader():
242
+ """Read SSE events and put them in the queue."""
243
+ try:
244
+ async for event_msg in self._stream_events_sse(sse_url):
245
+ await event_queue.put(event_msg)
246
+ if self._terminal_seen:
247
+ break
248
+ except Exception as e:
249
+ await event_queue.put(e) # Put exception in queue
250
+ finally:
251
+ sse_done.set()
252
+
253
+ async def status_poller():
254
+ """Periodically poll status while SSE stream is active."""
255
+ while not sse_done.is_set() and not self._terminal_seen:
256
+ await asyncio.sleep(2.0) # Check every 2 seconds
257
+ if self._terminal_seen or sse_done.is_set():
258
+ break
259
+
260
+ status = await self._refresh_status(http)
261
+
262
+ metric_messages = await self._poll_metrics(http)
263
+ timeline_messages = await self._poll_timeline(http)
264
+ self._dispatch(metric_messages + timeline_messages)
265
+
266
+ # Check for terminal status
267
+ if status and status.lower() in TERMINAL_STATUSES:
268
+ self._terminal_seen = True
269
+ break
270
+
271
+ # Start both tasks concurrently
272
+ sse_task = asyncio.create_task(sse_reader())
273
+ status_task = asyncio.create_task(status_poller())
274
+
275
+ try:
276
+ # Process events from queue
277
+ while not self._terminal_seen:
278
+ # Wait for event or timeout
279
+ try:
280
+ item = await asyncio.wait_for(event_queue.get(), timeout=1.0)
281
+ except asyncio.TimeoutError:
282
+ # No event received, check if SSE is done or terminal
283
+ if sse_done.is_set() or self._terminal_seen:
284
+ break
285
+ continue
286
+
287
+ # Handle exception from SSE reader
288
+ if isinstance(item, Exception):
289
+ raise item
290
+
291
+ # Process event
292
+ self._dispatch([item])
293
+
294
+ # Poll metrics/timeline after each event
295
+ metric_messages = await self._poll_metrics(http)
296
+ timeline_messages = await self._poll_timeline(http)
297
+ self._dispatch(metric_messages + timeline_messages)
298
+
299
+ # Check for terminal status
300
+ if self._terminal_seen:
301
+ break
302
+ finally:
303
+ # Cancel tasks
304
+ sse_task.cancel()
305
+ status_task.cancel()
306
+ try:
307
+ await asyncio.gather(sse_task, status_task, return_exceptions=True)
308
+ except Exception:
309
+ pass
310
+ else:
311
+ # No SSE endpoint available - use polling for events
312
+ while True:
313
+ status = await self._refresh_status(http)
314
+
315
+ # Check status FIRST before polling events/metrics
316
+ if status and status.lower() in TERMINAL_STATUSES:
317
+ self._terminal_seen = True
318
+ break
319
+ if self._terminal_seen:
320
+ break
321
+
322
+ event_messages = await self._poll_events(http)
323
+ metric_messages = await self._poll_metrics(http)
324
+ timeline_messages = await self._poll_timeline(http)
325
+
326
+ self._dispatch(event_messages + metric_messages + timeline_messages)
327
+
328
+ # Check again after polling (terminal events might have been received)
329
+ if self._terminal_seen:
330
+ break
331
+ if status and status.lower() in TERMINAL_STATUSES:
332
+ self._terminal_seen = True
333
+ break
334
+
335
+ await self._sleep(self.interval_seconds)
336
+
337
+ for handler in self.handlers:
338
+ with contextlib.suppress(Exception):
339
+ handler.flush()
340
+
341
+ final_status = self._terminal_event_status or self._last_status_value or "unknown"
342
+ if self._last_status_payload:
343
+ self._last_status_payload["status"] = final_status
344
+ return self._last_status_payload
345
+ return {"job_id": self.job_id, "status": final_status}
346
+
347
+ async def _stream_events_sse(self, sse_url: str) -> AsyncIterator[StreamMessage]:
348
+ """Stream events via Server-Sent Events (SSE)."""
349
+ url = f"{self.base_url.rstrip('/')}/{sse_url.lstrip('/')}"
350
+ headers = {
351
+ "Accept": "text/event-stream",
352
+ "Cache-Control": "no-cache",
353
+ "authorization": f"Bearer {self.api_key}",
354
+ }
355
+
356
+ # Create a separate session for SSE (long-lived connection)
357
+ timeout = aiohttp.ClientTimeout(total=None) # No timeout for SSE
358
+ async with aiohttp.ClientSession(headers=headers, timeout=timeout) as session:
359
+ async with session.get(url) as resp:
360
+ if resp.status != 200:
361
+ raise Exception(f"SSE endpoint returned {resp.status}: {await resp.text()}")
362
+
363
+ print(f"[DEBUG] SSE stream connected to {url}, status={resp.status}", file=sys.stderr)
364
+ buffer = ""
365
+ event_count = 0
366
+ last_event_time = time.time()
367
+ no_events_warning_printed = False
368
+
369
+ # Read SSE stream in chunks and parse events
370
+ async for chunk in resp.content.iter_chunked(8192):
371
+ current_time = time.time()
372
+
373
+ # Warn if no events received for 10 seconds (events should be streaming)
374
+ if event_count == 1 and current_time - last_event_time > 10 and not no_events_warning_printed:
375
+ print(f"[DEBUG] WARNING: No events received via SSE for 10s after connection. Backend may not be publishing to Redis (check SSE_USE_REDIS env var).", file=sys.stderr)
376
+ no_events_warning_printed = True
377
+
378
+ buffer += chunk.decode("utf-8", errors="ignore")
379
+
380
+ # SSE events are separated by double newlines
381
+ while "\n\n" in buffer:
382
+ event_block, buffer = buffer.split("\n\n", 1)
383
+ event_block = event_block.strip()
384
+
385
+ if not event_block:
386
+ continue
387
+
388
+ event_data = {}
389
+ event_id = None
390
+ event_type_line = None
391
+
392
+ # Parse SSE event block line by line
393
+ for event_line in event_block.split("\n"):
394
+ event_line = event_line.strip()
395
+ if not event_line or event_line.startswith(":"):
396
+ continue # Skip comments/empty lines
397
+ if event_line.startswith("id:"):
398
+ event_id = event_line[3:].strip()
399
+ elif event_line.startswith("event:"):
400
+ event_type_line = event_line[6:].strip()
401
+ elif event_line.startswith("data:"):
402
+ data_str = event_line[5:].strip()
403
+ try:
404
+ event_data = json.loads(data_str)
405
+ except json.JSONDecodeError as e:
406
+ print(f"[DEBUG] Failed to parse SSE data: {e}, data={data_str[:200]}", file=sys.stderr)
407
+ continue
408
+
409
+ # Debug: log what we parsed
410
+ if event_data:
411
+ event_count += 1
412
+ last_event_time = time.time()
413
+ print(f"[DEBUG] Parsed SSE event #{event_count}: type={event_data.get('type')}, seq={event_data.get('seq')}", file=sys.stderr)
414
+
415
+ if event_data and "type" in event_data:
416
+ # Convert SSE event to StreamMessage
417
+ event_job_id = event_data.get("job_id") or self.job_id
418
+ msg = StreamMessage.from_event(event_job_id, event_data)
419
+
420
+ # Update sequence tracking
421
+ seq = event_data.get("seq")
422
+ if seq is not None:
423
+ try:
424
+ seq_int = int(seq)
425
+ if sse_url not in self._last_seq_by_stream or seq_int > self._last_seq_by_stream[sse_url]:
426
+ self._last_seq_by_stream[sse_url] = seq_int
427
+ except (TypeError, ValueError):
428
+ pass
429
+
430
+ # Check for terminal events
431
+ event_type = str(event_data.get("type", "")).lower()
432
+ if event_type in TERMINAL_EVENT_SUCCESS:
433
+ self._terminal_seen = True
434
+ self._terminal_event_status = "succeeded"
435
+ elif event_type in TERMINAL_EVENT_FAILURE:
436
+ self._terminal_seen = True
437
+ self._terminal_event_status = "failed"
438
+
439
+ yield msg
440
+
441
+ async def _refresh_status(self, http: AsyncHttpClient) -> str:
442
+ status_payload = await self._poll_status(http)
443
+ if status_payload:
444
+ self._last_status_payload = status_payload
445
+ status = str(status_payload.get("status") or status_payload.get("state") or "").lower()
446
+ if status:
447
+ self._last_status_value = status
448
+ if status in TERMINAL_STATUSES:
449
+ self._terminal_seen = True
450
+ print(f"[SDK] Terminal status detected: {status}", flush=True)
451
+ return status
452
+ return self._last_status_value or ""
453
+
454
+ async def _poll_status(self, http: AsyncHttpClient) -> dict[str, Any] | None:
455
+ if StreamType.STATUS not in self.config.enabled_streams or not self._status_paths:
456
+ return None
457
+
458
+ last_error: Exception | None = None
459
+ for path in self._status_paths:
460
+ try:
461
+ # Add cache-busting query param to ensure fresh status
462
+ # Use a timestamp to prevent any caching
463
+ params = {"_t": int(time.time() * 1000)}
464
+ data = await http.get(path, params=params)
465
+ except Exception as exc:
466
+ last_error = exc
467
+ # Try next fallback path
468
+ continue
469
+ if isinstance(data, dict):
470
+ message = StreamMessage.from_status(self.job_id, data)
471
+ self._dispatch([message])
472
+ return data
473
+
474
+ # If all paths failed, log the error for debugging
475
+ if last_error is not None:
476
+ logger = logging.getLogger(__name__)
477
+ logger.debug(f"Status polling failed for all paths: {last_error}")
478
+ return None
479
+
480
+ async def _poll_events(self, http: AsyncHttpClient) -> list[StreamMessage]:
481
+ if StreamType.EVENTS not in self.config.enabled_streams or not self._event_paths:
482
+ return []
483
+ messages: list[StreamMessage] = []
484
+ total = 0
485
+ for path in self._event_paths:
486
+ since = self._last_seq_by_stream.get(path, 0)
487
+ # Increase limit to capture more events per poll
488
+ limit = 1000 if self.config.max_events_per_poll and self.config.max_events_per_poll > 200 else 200
489
+ params = {"since_seq": since, "limit": limit}
490
+ try:
491
+ data = await http.get(path, params=params)
492
+ # Debug: Always log what we got from API
493
+ print(f"[DEBUG] Polling {path} with since_seq={since}, limit={limit}", file=sys.stderr)
494
+ print(f"[DEBUG] Got response from {path}, type={type(data).__name__}, keys={list(data.keys()) if isinstance(data, dict) else 'not dict'}", file=sys.stderr)
495
+ if isinstance(data, dict):
496
+ # Check for next_seq to see if we should update our tracking
497
+ if "next_seq" in data:
498
+ print(f"[DEBUG] Response has next_seq={data.get('next_seq')}, current since={since}", file=sys.stderr)
499
+ # Show what keys are in the response
500
+ for key in data.keys():
501
+ val = data[key]
502
+ if isinstance(val, list):
503
+ print(f"[DEBUG] Response[{key}] is list with {len(val)} items", file=sys.stderr)
504
+ if len(val) > 0:
505
+ print(f"[DEBUG] First item in {key}: {list(val[0].keys()) if isinstance(val[0], dict) else type(val[0])}", file=sys.stderr)
506
+ elif isinstance(val, dict):
507
+ print(f"[DEBUG] Response[{key}] is dict with keys: {list(val.keys())[:5]}", file=sys.stderr)
508
+ except Exception as e:
509
+ error_str = str(e)
510
+ print(f"[DEBUG] Error polling {path}: {e}", file=sys.stderr)
511
+ # Fail fast if we get 404 on both ADAS and fallback endpoints (indicates job ID mapping issue)
512
+ if "404" in error_str and (
513
+ "graphgen" in path.lower()
514
+ or "adas" in path.lower()
515
+ or "prompt-learning" in path.lower()
516
+ ):
517
+ # Check if this is the last fallback path - if so, raise to fail fast
518
+ if path == self._event_paths[-1]: # Last fallback path
519
+ raise RuntimeError(
520
+ f"Failed to poll events: All endpoints returned 404. "
521
+ f"This likely indicates a job ID mapping issue. "
522
+ f"GraphGen endpoints need the GraphGen job ID; GEPA fallback endpoints need the GEPA job ID. "
523
+ f"Last error: {error_str}"
524
+ )
525
+ continue
526
+ raw_events = _extract_list(data, "events")
527
+ # Debug: Always log what we extracted
528
+ print(f"[DEBUG] Extracted {len(raw_events)} events from {path} using _extract_list", file=sys.stderr)
529
+ # Update last_seq using next_seq if available
530
+ if isinstance(data, dict) and "next_seq" in data:
531
+ next_seq = data.get("next_seq")
532
+ if next_seq is not None:
533
+ try:
534
+ next_seq_int = int(next_seq)
535
+ if next_seq_int > since:
536
+ self._last_seq_by_stream[path] = next_seq_int
537
+ print(f"[DEBUG] Updated last_seq for {path} to {next_seq_int}", file=sys.stderr)
538
+ except (TypeError, ValueError):
539
+ pass
540
+ if raw_events and len(raw_events) > 0:
541
+ # Log first event type for debugging
542
+ first_event_type = raw_events[0].get("type", "unknown")
543
+ print(f"[DEBUG] First event type: {first_event_type}", file=sys.stderr)
544
+ for event in raw_events:
545
+ seq_raw = event.get("seq")
546
+ try:
547
+ seq_value = int(seq_raw) # type: ignore[arg-type]
548
+ except (TypeError, ValueError):
549
+ seq_value = None
550
+ last_seq = self._last_seq_by_stream.get(path, 0)
551
+ seq = seq_value if seq_value is not None else last_seq + 1
552
+ if seq <= last_seq:
553
+ continue
554
+ if seq_value is None:
555
+ event["seq"] = seq
556
+ self._last_seq_by_stream[path] = seq
557
+ # Bypass filtering - show ALL events
558
+ # if not self.config.should_include_event(event):
559
+ # continue
560
+ event_job_id = event.get("job_id") or self.job_id
561
+ event_message = StreamMessage.from_event(event_job_id, event)
562
+ event_type = str(event.get("type") or "").lower()
563
+ if event_type in TERMINAL_EVENT_SUCCESS:
564
+ self._terminal_seen = True
565
+ self._terminal_event_status = "succeeded"
566
+ elif event_type in TERMINAL_EVENT_FAILURE:
567
+ self._terminal_seen = True
568
+ self._terminal_event_status = "failed"
569
+ messages.append(event_message)
570
+ total += 1
571
+ if self.config.max_events_per_poll and total >= self.config.max_events_per_poll:
572
+ return messages
573
+ return messages
574
+
575
+ async def _poll_metrics(self, http: AsyncHttpClient) -> list[StreamMessage]:
576
+ if StreamType.METRICS not in self.config.enabled_streams or not self._metric_paths:
577
+ return []
578
+ messages: list[StreamMessage] = []
579
+ for path in self._metric_paths:
580
+ params = {"limit": 200}
581
+ try:
582
+ data = await http.get(path, params=params)
583
+ except Exception:
584
+ continue
585
+ points = _extract_list(data, "points")
586
+ for point in points:
587
+ name = str(point.get("name") or "")
588
+ if not name:
589
+ continue
590
+ step, fingerprint = _metric_cursor(point)
591
+ last_step, last_fingerprint = self._metric_cursors.get(name, (None, ""))
592
+ if step is not None:
593
+ if last_step is not None and step <= last_step:
594
+ continue
595
+ elif fingerprint and fingerprint == last_fingerprint:
596
+ continue
597
+ self._metric_cursors[name] = (step, fingerprint)
598
+ if not self.config.should_include_metric(point):
599
+ continue
600
+ metric_job_id = point.get("job_id") or self.job_id
601
+ messages.append(StreamMessage.from_metric(metric_job_id, point))
602
+ return messages
603
+
604
+ async def _poll_timeline(self, http: AsyncHttpClient) -> list[StreamMessage]:
605
+ if StreamType.TIMELINE not in self.config.enabled_streams or not self._timeline_paths:
606
+ return []
607
+ messages: list[StreamMessage] = []
608
+ for path in self._timeline_paths:
609
+ try:
610
+ data = await http.get(path)
611
+ except Exception:
612
+ continue
613
+
614
+ timeline_entries = _extract_list(data, "events")
615
+ for entry in timeline_entries:
616
+ if not self.config.should_include_timeline(entry):
617
+ continue
618
+ timeline_job_id = entry.get("job_id") or self.job_id
619
+ phase = str(entry.get("phase") or "").lower()
620
+ if phase in TERMINAL_STATUSES:
621
+ self._terminal_seen = True
622
+ if phase in {"failed", "cancelled", "canceled"}:
623
+ self._terminal_event_status = "failed"
624
+ elif phase:
625
+ self._terminal_event_status = "succeeded"
626
+ messages.append(StreamMessage.from_timeline(timeline_job_id, entry))
627
+ return messages
628
+
629
+ def _dispatch(self, messages: Iterable[StreamMessage]) -> None:
630
+ message_list = list(messages)
631
+ for message in message_list:
632
+ if self.config.deduplicate and message.key in self._seen_messages:
633
+ continue
634
+ if self.config.sample_rate < 1.0 and random.random() > self.config.sample_rate:
635
+ continue
636
+ if self.config.deduplicate:
637
+ self._seen_messages.add(message.key)
638
+
639
+ # Check for terminal events in dispatch (belt-and-suspenders)
640
+ if message.stream_type == StreamType.EVENTS and message.data:
641
+ event_type = str(message.data.get("type", "")).lower()
642
+ if event_type in TERMINAL_EVENT_SUCCESS:
643
+ self._terminal_seen = True
644
+ self._terminal_event_status = "succeeded"
645
+ elif event_type in TERMINAL_EVENT_FAILURE:
646
+ self._terminal_seen = True
647
+ self._terminal_event_status = "failed"
648
+
649
+ for handler in self.handlers:
650
+ try:
651
+ if handler.should_handle(message):
652
+ handler.handle(message)
653
+ except Exception:
654
+ pass
655
+
656
+
657
+ def _metric_cursor(point: dict[str, Any]) -> tuple[int | None, str]:
658
+ raw_step = point.get("step")
659
+ step_value: int | None = None
660
+ if raw_step is not None:
661
+ try:
662
+ step_value = int(raw_step) # type: ignore[arg-type]
663
+ except (TypeError, ValueError):
664
+ step_value = None
665
+
666
+ fingerprint = ""
667
+ for key in ("created_at", "updated_at", "timestamp"):
668
+ ts_val = point.get(key)
669
+ if ts_val is not None and ts_val != "":
670
+ fingerprint = str(ts_val)
671
+ break
672
+ if not fingerprint:
673
+ try:
674
+ fingerprint = json.dumps(point, sort_keys=True, default=str)
675
+ except Exception:
676
+ fingerprint = repr(point)
677
+ return step_value, fingerprint
678
+
679
+
680
+ def _extract_list(data: Any, field: str) -> list[dict[str, Any]]:
681
+ results: list[dict[str, Any]] = []
682
+ seen_items: set[int] = set()
683
+ stack: list[Any] = [data]
684
+ seen_containers: set[int] = set()
685
+
686
+ fallback_keys = {"data", "result", "results", "items", "payload", "records", "entries", "values"}
687
+
688
+ while stack:
689
+ current = stack.pop()
690
+ if current is None:
691
+ continue
692
+ current_id = id(current)
693
+ if current_id in seen_containers:
694
+ continue
695
+ seen_containers.add(current_id)
696
+
697
+ if isinstance(current, list):
698
+ for item in current:
699
+ if isinstance(item, dict):
700
+ item_id = id(item)
701
+ if item_id not in seen_items:
702
+ seen_items.add(item_id)
703
+ results.append(item)
704
+ elif isinstance(current, dict):
705
+ if field in current:
706
+ stack.append(current[field])
707
+ for key in fallback_keys:
708
+ if key in current:
709
+ stack.append(current[key])
710
+ return results
711
+
712
+
713
+ __all__ = ["JobStreamer", "StreamEndpoints"]