synth-ai 0.3.1.dev1__tar.gz → 0.3.1.dev3__tar.gz

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 (362) hide show
  1. synth_ai-0.3.1.dev3/PKG-INFO +154 -0
  2. synth_ai-0.3.1.dev3/README.md +74 -0
  3. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/pyproject.toml +1 -1
  4. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/smoke/core.py +1 -1
  5. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +1 -1
  6. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +1 -1
  7. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +2 -5
  8. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/modal_task_app.py +2 -5
  9. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/turso.py +1 -1
  10. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/task_app_env.py +108 -0
  11. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/tasks.py +1 -1
  12. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/client.py +5 -6
  13. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/__init__.py +2 -1
  14. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/commands.py +4 -2
  15. synth_ai-0.3.1.dev3/synth_ai/cli/turso.py +52 -0
  16. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/trace_utils.py +2 -2
  17. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/turso/daemon.py +26 -0
  18. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/turso/native_manager.py +5 -5
  19. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/__init__.py +11 -6
  20. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/configs/prompt_learning.py +1 -1
  21. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/task_app.py +11 -1
  22. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/validators.py +1 -1
  23. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/sft/client.py +26 -0
  24. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/__init__.py +12 -0
  25. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/apps/__init__.py +2 -2
  26. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/datasets.py +2 -2
  27. synth_ai-0.3.1.dev3/synth_ai/sdk/task/in_process.py +876 -0
  28. synth_ai-0.3.1.dev3/synth_ai/sdk/task/in_process_runner.py +281 -0
  29. synth_ai-0.3.1.dev3/synth_ai.egg-info/PKG-INFO +154 -0
  30. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai.egg-info/SOURCES.txt +2 -0
  31. synth_ai-0.3.1.dev1/PKG-INFO +0 -404
  32. synth_ai-0.3.1.dev1/README.md +0 -324
  33. synth_ai-0.3.1.dev1/synth_ai/sdk/task/in_process.py +0 -477
  34. synth_ai-0.3.1.dev1/synth_ai.egg-info/PKG-INFO +0 -404
  35. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/LICENSE +0 -0
  36. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/MANIFEST.in +0 -0
  37. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/setup.cfg +0 -0
  38. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/__init__.py +0 -0
  39. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/__main__.py +0 -0
  40. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/__init__.py +0 -0
  41. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/__main__.py +0 -0
  42. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/_internal/__init__.py +0 -0
  43. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/_internal/modal_wrapper.py +0 -0
  44. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/_internal/storage.py +0 -0
  45. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/_internal/typer_patch.py +0 -0
  46. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/_internal/validate_task_app.py +0 -0
  47. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/agents/__init__.py +0 -0
  48. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/agents/claude.py +0 -0
  49. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/agents/codex.py +0 -0
  50. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/agents/opencode.py +0 -0
  51. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/__init__.py +0 -0
  52. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/__init__.py +0 -0
  53. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/client.py +0 -0
  54. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/config.py +0 -0
  55. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/core.py +0 -0
  56. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/download.py +0 -0
  57. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/export.py +0 -0
  58. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/list.py +0 -0
  59. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/parsing.py +0 -0
  60. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/artifacts/show.py +0 -0
  61. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/baseline/__init__.py +0 -0
  62. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/baseline/core.py +0 -0
  63. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/baseline/list.py +0 -0
  64. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/demo/__init__.py +0 -0
  65. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/demo/core.py +0 -0
  66. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/eval/__init__.py +0 -0
  67. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/eval/core.py +0 -0
  68. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/eval/errors.py +0 -0
  69. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/eval/validation.py +0 -0
  70. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/filter/__init__.py +0 -0
  71. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/filter/core.py +0 -0
  72. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/filter/errors.py +0 -0
  73. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/filter/validation.py +0 -0
  74. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/help/__init__.py +0 -0
  75. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/help/core.py +0 -0
  76. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/__init__.py +0 -0
  77. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/cloudflare_scanner.py +0 -0
  78. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/core.py +0 -0
  79. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/health_checker.py +0 -0
  80. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/local_scanner.py +0 -0
  81. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/scan/models.py +0 -0
  82. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/smoke/__init__.py +0 -0
  83. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/__init__.py +0 -0
  84. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/client.py +0 -0
  85. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/config.py +0 -0
  86. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/errors.py +0 -0
  87. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/formatters.py +0 -0
  88. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/__init__.py +0 -0
  89. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/files.py +0 -0
  90. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/jobs.py +0 -0
  91. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/models.py +0 -0
  92. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/pricing.py +0 -0
  93. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/runs.py +0 -0
  94. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/session.py +0 -0
  95. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/summary.py +0 -0
  96. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/subcommands/usage.py +0 -0
  97. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/status/utils.py +0 -0
  98. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/__init__.py +0 -0
  99. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/core.py +0 -0
  100. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/errors.py +0 -0
  101. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/judge_schemas.py +0 -0
  102. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/judge_validation.py +0 -0
  103. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/commands/train/validation.py +0 -0
  104. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/__init__.py +0 -0
  105. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/core/__init__.py +0 -0
  106. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/core/cli.py +0 -0
  107. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/crafter/__init__.py +0 -0
  108. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +0 -0
  109. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +0 -0
  110. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_registry.py +0 -0
  111. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/__init__.py +0 -0
  112. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/core.py +0 -0
  113. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +0 -0
  114. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/crafter_fft_4b.toml +0 -0
  115. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +0 -0
  116. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/__init__.py +0 -0
  117. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/_common.py +0 -0
  118. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/app.py +0 -0
  119. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +0 -0
  120. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/deploy_modal.py +0 -0
  121. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +0 -0
  122. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/__init__.py +0 -0
  123. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/_common.py +0 -0
  124. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/app.py +0 -0
  125. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/config.toml +0 -0
  126. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/deploy_modal.py +0 -0
  127. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/math/task_app_entry.py +0 -0
  128. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/mipro/main.py +0 -0
  129. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/mipro/task_app.py +0 -0
  130. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demo_apps/mipro/train_cfg.toml +0 -0
  131. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demos/__init__.py +0 -0
  132. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demos/demo.py +0 -0
  133. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/demos/rl_demo.py +0 -0
  134. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/deploy.py +0 -0
  135. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/__init__.py +0 -0
  136. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/balance.py +0 -0
  137. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/mcp.py +0 -0
  138. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/modal_app.py +0 -0
  139. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/setup.py +0 -0
  140. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/infra/status.py +0 -0
  141. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/__init__.py +0 -0
  142. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/agents.py +0 -0
  143. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/apps/modal_app.py +0 -0
  144. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/apps/task_app.py +0 -0
  145. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/bin.py +0 -0
  146. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/env.py +0 -0
  147. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/errors.py +0 -0
  148. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/modal.py +0 -0
  149. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/plotting.py +0 -0
  150. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/prompt_args.py +0 -0
  151. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/prompts.py +0 -0
  152. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/sqld.py +0 -0
  153. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/task_app_discovery.py +0 -0
  154. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/train_cfgs.py +0 -0
  155. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/lib/tunnel_records.py +0 -0
  156. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/__init__.py +0 -0
  157. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/__init__.py +0 -0
  158. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/api_schemas.py +0 -0
  159. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/celery_app.py +0 -0
  160. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/config.py +0 -0
  161. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/config_utils.py +0 -0
  162. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/database.py +0 -0
  163. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/dispatcher.py +0 -0
  164. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/models.py +0 -0
  165. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/progress_info.py +0 -0
  166. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/results.py +0 -0
  167. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/schemas.py +0 -0
  168. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/service.py +0 -0
  169. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/status.py +0 -0
  170. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/status_tracker.py +0 -0
  171. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/trace_storage.py +0 -0
  172. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/experiment_queue/validation.py +0 -0
  173. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/__init__.py +0 -0
  174. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/constants.py +0 -0
  175. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/exceptions.py +0 -0
  176. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/manager.py +0 -0
  177. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/models.py +0 -0
  178. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/local/session/query.py +0 -0
  179. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/root.py +0 -0
  180. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/deploy.py +0 -0
  181. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/list.py +0 -0
  182. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/main.py +0 -0
  183. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/modal_serve.py +0 -0
  184. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/task_apps/serve.py +0 -0
  185. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/training/__init__.py +0 -0
  186. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/training/train.py +0 -0
  187. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/training/train_cfg.py +0 -0
  188. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/training/watch.py +0 -0
  189. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/utils/__init__.py +0 -0
  190. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/utils/experiments.py +0 -0
  191. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/utils/queue.py +0 -0
  192. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/utils/recent.py +0 -0
  193. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/cli/utils/traces.py +0 -0
  194. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/contracts/__init__.py +0 -0
  195. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/__init__.py +0 -0
  196. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/__init__.py +0 -0
  197. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/base_url.py +0 -0
  198. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/http.py +0 -0
  199. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/prompts.py +0 -0
  200. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/task_app_state.py +0 -0
  201. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/_utils/user_config.py +0 -0
  202. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/apps/__init__.py +0 -0
  203. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/apps/common.py +0 -0
  204. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/auth.py +0 -0
  205. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/cfgs.py +0 -0
  206. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/config/__init__.py +0 -0
  207. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/config/base.py +0 -0
  208. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/config/resolver.py +0 -0
  209. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/env.py +0 -0
  210. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/errors.py +0 -0
  211. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/http.py +0 -0
  212. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/__init__.py +0 -0
  213. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/cloudflare.py +0 -0
  214. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/mcp/__init__.py +0 -0
  215. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/mcp/__main__.py +0 -0
  216. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/mcp/claude.py +0 -0
  217. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/mcp/main.py +0 -0
  218. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/mcp/setup.py +0 -0
  219. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/integrations/modal.py +0 -0
  220. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/json.py +0 -0
  221. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/log_filter.py +0 -0
  222. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/logging.py +0 -0
  223. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/paths.py +0 -0
  224. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/pricing.py +0 -0
  225. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/process.py +0 -0
  226. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/ssl.py +0 -0
  227. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/storage/__init__.py +0 -0
  228. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/task_app_state.py +0 -0
  229. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/telemetry.py +0 -0
  230. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/__init__.py +0 -0
  231. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/abstractions.py +0 -0
  232. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/config.py +0 -0
  233. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/constants.py +0 -0
  234. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/db_config.py +0 -0
  235. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/decorators.py +0 -0
  236. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/examples/basic_usage.py +0 -0
  237. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/hooks.py +0 -0
  238. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/llm_call_record_helpers.py +0 -0
  239. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/lm_call_record_abstractions.py +0 -0
  240. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/migration_helper.py +0 -0
  241. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/replica_sync.py +0 -0
  242. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/serialization.py +0 -0
  243. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/session_tracer.py +0 -0
  244. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/__init__.py +0 -0
  245. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/base.py +0 -0
  246. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/config.py +0 -0
  247. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/exceptions.py +0 -0
  248. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/factory.py +0 -0
  249. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/types.py +0 -0
  250. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/storage/utils.py +0 -0
  251. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/turso/__init__.py +0 -0
  252. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/turso/models.py +0 -0
  253. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/tracing_v3/utils.py +0 -0
  254. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/urls.py +0 -0
  255. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/user_config.py +0 -0
  256. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/core/uvicorn.py +0 -0
  257. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/data/__init__.py +0 -0
  258. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/data/enums.py +0 -0
  259. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/data/rewards.py +0 -0
  260. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/data/specs.py +0 -0
  261. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/data/traces.py +0 -0
  262. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/py.typed +0 -0
  263. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/models/supported.py +0 -0
  264. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/research_agent/__init__.py +0 -0
  265. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/research_agent/cli.py +0 -0
  266. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/research_agent/job.py +0 -0
  267. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/__init__.py +0 -0
  268. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/builders.py +0 -0
  269. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/cli.py +0 -0
  270. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/config_finder.py +0 -0
  271. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/configs/__init__.py +0 -0
  272. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/configs/rl.py +0 -0
  273. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/configs/sft.py +0 -0
  274. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/configs/shared.py +0 -0
  275. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/env_resolver.py +0 -0
  276. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/pollers.py +0 -0
  277. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/prompt_learning.py +0 -0
  278. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/rl.py +0 -0
  279. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/sft.py +0 -0
  280. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/summary.py +0 -0
  281. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/supported_algos.py +0 -0
  282. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/api/train/utils.py +0 -0
  283. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/baseline/__init__.py +0 -0
  284. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/baseline/config.py +0 -0
  285. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/baseline/discovery.py +0 -0
  286. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/baseline/execution.py +0 -0
  287. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/inference/__init__.py +0 -0
  288. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/inference/client.py +0 -0
  289. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/jobs/__init__.py +0 -0
  290. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/jobs/client.py +0 -0
  291. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/judging/__init__.py +0 -0
  292. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/judging/base.py +0 -0
  293. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/judging/client.py +0 -0
  294. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/judging/schemas.py +0 -0
  295. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/judging/types.py +0 -0
  296. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/__init__.py +0 -0
  297. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/algorithms.py +0 -0
  298. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/client.py +0 -0
  299. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/config.py +0 -0
  300. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/constants.py +0 -0
  301. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/core.py +0 -0
  302. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/ft_client.py +0 -0
  303. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/gateway.py +0 -0
  304. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/health.py +0 -0
  305. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/jobs.py +0 -0
  306. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/prompt_extraction.py +0 -0
  307. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/prompt_learning_client.py +0 -0
  308. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/prompt_learning_types.py +0 -0
  309. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/__init__.py +0 -0
  310. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/client.py +0 -0
  311. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/config.py +0 -0
  312. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/contracts.py +0 -0
  313. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/env_keys.py +0 -0
  314. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl/secrets.py +0 -0
  315. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/rl_client.py +0 -0
  316. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/sft/__init__.py +0 -0
  317. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/sft/config.py +0 -0
  318. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/sft/data.py +0 -0
  319. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/sse.py +0 -0
  320. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/learning/validators.py +0 -0
  321. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/research_agent/__init__.py +0 -0
  322. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/research_agent/container_builder.py +0 -0
  323. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/research_agent/container_spec.py +0 -0
  324. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/research_agent/defaults.py +0 -0
  325. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/research_agent/results_collector.py +0 -0
  326. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/specs/__init__.py +0 -0
  327. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/specs/dataclasses.py +0 -0
  328. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/specs/loader.py +0 -0
  329. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/specs/serializer.py +0 -0
  330. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/specs/validation.py +0 -0
  331. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/streaming/__init__.py +0 -0
  332. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/streaming/config.py +0 -0
  333. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/streaming/handlers.py +0 -0
  334. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/streaming/streamer.py +0 -0
  335. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/streaming/types.py +0 -0
  336. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/auth.py +0 -0
  337. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/client.py +0 -0
  338. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/config.py +0 -0
  339. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/contracts.py +0 -0
  340. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/errors.py +0 -0
  341. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/health.py +0 -0
  342. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/inference_api.py +0 -0
  343. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/json.py +0 -0
  344. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/proxy.py +0 -0
  345. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics/__init__.py +0 -0
  346. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics/loaders.py +0 -0
  347. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics/models.py +0 -0
  348. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics/scoring.py +0 -0
  349. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics/strict.py +0 -0
  350. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/rubrics.py +0 -0
  351. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/server.py +0 -0
  352. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/trace_correlation_helpers.py +0 -0
  353. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/tracing_utils.py +0 -0
  354. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/validators.py +0 -0
  355. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/task/vendors.py +0 -0
  356. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/tracing/__init__.py +0 -0
  357. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/sdk/training/__init__.py +0 -0
  358. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai/utils/__init__.py +0 -0
  359. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai.egg-info/dependency_links.txt +0 -0
  360. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai.egg-info/entry_points.txt +0 -0
  361. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai.egg-info/requires.txt +0 -0
  362. {synth_ai-0.3.1.dev1 → synth_ai-0.3.1.dev3}/synth_ai.egg-info/top_level.txt +0 -0
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: synth-ai
3
+ Version: 0.3.1.dev3
4
+ Summary: Serverless Posttraining for Agents - Core AI functionality and tracing
5
+ Author-email: Synth AI <josh@usesynth.ai>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/synth-laboratories/synth-ai
8
+ Project-URL: Repository, https://github.com/synth-laboratories/synth-ai
9
+ Project-URL: Issues, https://github.com/synth-laboratories/synth-ai/issues
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: pydantic>=2.0.0
14
+ Requires-Dist: python-dotenv>=1.0.1
15
+ Requires-Dist: requests>=2.32.3
16
+ Requires-Dist: tqdm>=4.66.4
17
+ Requires-Dist: typing_extensions>=4.0.0
18
+ Requires-Dist: rich>=13.9.0
19
+ Requires-Dist: openai>=1.99.0
20
+ Requires-Dist: anthropic>=0.42.0
21
+ Requires-Dist: langfuse<3.0.0,>=2.53.9
22
+ Requires-Dist: opentelemetry-api>=1.26.0
23
+ Requires-Dist: opentelemetry-sdk>=1.26.0
24
+ Requires-Dist: groq>=0.30.0
25
+ Requires-Dist: google-genai>=1.26.0
26
+ Requires-Dist: together>=1.5.21
27
+ Requires-Dist: mistralai>=1.9.2
28
+ Requires-Dist: fastapi>=0.115.12
29
+ Requires-Dist: uvicorn>=0.34.2
30
+ Requires-Dist: numpy>=2.2.3
31
+ Requires-Dist: networkx>=3.4.2
32
+ Requires-Dist: sqlalchemy>=2.0.42
33
+ Requires-Dist: celery>=5.4.0
34
+ Requires-Dist: redis>=6.2.0
35
+ Requires-Dist: aiosqlite>=0.21.0
36
+ Requires-Dist: libsql>=0.1.8
37
+ Requires-Dist: pynacl>=1.5.0
38
+ Requires-Dist: click<8.2,>=8.1.7
39
+ Requires-Dist: aiohttp>=3.8.0
40
+ Requires-Dist: httpx>=0.28.1
41
+ Requires-Dist: modal<2.0.0,>=1.1.4
42
+ Requires-Dist: docker>=7.0.0
43
+ Requires-Dist: mcp>=1.21.0
44
+ Requires-Dist: ruff>=0.12.9
45
+ Requires-Dist: tomli_w>=1.0.0
46
+ Requires-Dist: dspy>=3.0.4
47
+ Requires-Dist: setuptools>=80.9.0
48
+ Requires-Dist: gymnasium>=0.26.2
49
+ Requires-Dist: gepa>=0.0.17
50
+ Requires-Dist: datasets>=4.0.0
51
+ Provides-Extra: dev
52
+ Requires-Dist: build>=1.2.2.post1; extra == "dev"
53
+ Requires-Dist: twine>=4.0.0; extra == "dev"
54
+ Requires-Dist: keyring>=24.0.0; extra == "dev"
55
+ Requires-Dist: pytest>=8.3.3; extra == "dev"
56
+ Requires-Dist: pytest-xdist>=3.6.1; extra == "dev"
57
+ Requires-Dist: pytest-timeout>=2.3.1; extra == "dev"
58
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
59
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
60
+ Requires-Dist: pyright>=1.1.350; extra == "dev"
61
+ Requires-Dist: coverage[toml]>=7.3.0; extra == "dev"
62
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
63
+ Provides-Extra: research
64
+ Requires-Dist: crafter>=1.8.3; extra == "research"
65
+ Requires-Dist: datasets>=4.0.0; extra == "research"
66
+ Provides-Extra: swe
67
+ Requires-Dist: morphcloud>=0.1.3; extra == "swe"
68
+ Requires-Dist: swebench>=2.3.0; extra == "swe"
69
+ Provides-Extra: all
70
+ Requires-Dist: crafter>=1.8.3; extra == "all"
71
+ Requires-Dist: datasets>=4.0.0; extra == "all"
72
+ Requires-Dist: morphcloud>=0.1.3; extra == "all"
73
+ Requires-Dist: swebench>=2.3.0; extra == "all"
74
+ Requires-Dist: pyboy>=2.6.0; extra == "all"
75
+ Requires-Dist: transformers>=4.56.1; extra == "all"
76
+ Requires-Dist: redis>=6.2.0; extra == "all"
77
+ Provides-Extra: analytics
78
+ Requires-Dist: pandas>=2.2.3; extra == "analytics"
79
+ Dynamic: license-file
80
+
81
+ # Synth
82
+
83
+ [![Python](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/)
84
+ [![PyPI](https://img.shields.io/pypi/v/synth-ai.svg)](https://pypi.org/project/synth-ai/)
85
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
86
+ ![Coverage](https://img.shields.io/badge/coverage-28.65%25-yellow)
87
+ ![Tests](https://img.shields.io/badge/tests-847%20passing-brightgreen)
88
+
89
+ Serverless Posttraining APIs for Developers
90
+
91
+ <p align="center">
92
+ <picture align="center">
93
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_dark.png">
94
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_light.png">
95
+ <img alt="Shows a bar chart comparing prompt optimization performance across Synth GEPA, Synth MIPRO, GEPA (lib), DSPy MIPRO, and DSPy GEPA with baseline vs optimized." src="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_light.png">
96
+ </picture>
97
+ </p>
98
+
99
+ <p align="center">
100
+ <i>Average accuracy on <a href="https://arxiv.org/abs/2502.20315">LangProBe</a> prompt optimization benchmarks.</i>
101
+ </p>
102
+
103
+ ## Highlights
104
+
105
+ - 🚀 Train across sft, RL, and prompt opt by standing up a single cloudflared Fastapi wrapper around your code. No production code churn.
106
+ - ⚡️ Parallelize training and achieve 80% GPU util. via PipelineRL
107
+ - 🗂️ Train prompts and models across multiple experiments
108
+ - 🛠️ Spin up experiment queues and datastores locally for dev work
109
+ - 🔩 Run serverless training via cli or programmatically
110
+ - 🏢 Scales gpu-based model training to 64 H100s seemlessly
111
+ - 💾 Use GEPA-calibrated judges for fast, accurate rubric scoring
112
+ - 🖥️ Supports HTTP-based training across all programming languages
113
+ - 🤖 CLI utilities tuned for use with Claude Code, Codex, Opencode
114
+
115
+ ## Getting Started
116
+
117
+ ```bash
118
+ # Use with OpenAI Codex
119
+ uvx synth-ai codex
120
+ ```
121
+
122
+ ```bash
123
+ # Use with Opencode
124
+ uvx synth-ai opencode
125
+ ```
126
+
127
+ Synth is maintained by devs behind the [MIPROv2](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=jauNVA8AAAAJ&citation_for_view=jauNVA8AAAAJ:u5HHmVD_uO8C) prompt optimizer.
128
+
129
+ ## Documentation
130
+
131
+ Docs available at [docs.usesynth.ai](https://docs.usesynth.ai/overview).
132
+
133
+ ## In-Process Runner (SDK)
134
+
135
+ Run GEPA/MIPRO/RL jobs against a tunneled task app without the CLI:
136
+
137
+ ```python
138
+ import asyncio
139
+ import os
140
+ from synth_ai.sdk.task import run_in_process_job
141
+
142
+ result = asyncio.run(
143
+ run_in_process_job(
144
+ job_type="prompt_learning",
145
+ config_path="configs/style_matching_gepa.toml",
146
+ task_app_path="task_apps/style_matching_task_app.py",
147
+ overrides={"prompt_learning.gepa.rollout.budget": 4},
148
+ backend_url=os.getenv("TARGET_BACKEND_BASE_URL"), # resolves envs automatically
149
+ )
150
+ )
151
+ print(result.job_id, result.status.get("status"))
152
+ ```
153
+
154
+ Env priority for the backend URL: `TARGET_BACKEND_BASE_URL` → `BACKEND_OVERRIDE` → `SYNTH_BACKEND_URL` → `BACKEND_BASE_URL` → `NEXT_PUBLIC_API_URL` → fallback to `get_backend_from_env()`. Required keys: `SYNTH_API_KEY`, `ENVIRONMENT_API_KEY`, plus any model keys used by your task app.
@@ -0,0 +1,74 @@
1
+ # Synth
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/)
4
+ [![PyPI](https://img.shields.io/pypi/v/synth-ai.svg)](https://pypi.org/project/synth-ai/)
5
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
+ ![Coverage](https://img.shields.io/badge/coverage-28.65%25-yellow)
7
+ ![Tests](https://img.shields.io/badge/tests-847%20passing-brightgreen)
8
+
9
+ Serverless Posttraining APIs for Developers
10
+
11
+ <p align="center">
12
+ <picture align="center">
13
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_dark.png">
14
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_light.png">
15
+ <img alt="Shows a bar chart comparing prompt optimization performance across Synth GEPA, Synth MIPRO, GEPA (lib), DSPy MIPRO, and DSPy GEPA with baseline vs optimized." src="https://raw.githubusercontent.com/synth-laboratories/synth-ai/main/langprobe_v2_light.png">
16
+ </picture>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <i>Average accuracy on <a href="https://arxiv.org/abs/2502.20315">LangProBe</a> prompt optimization benchmarks.</i>
21
+ </p>
22
+
23
+ ## Highlights
24
+
25
+ - 🚀 Train across sft, RL, and prompt opt by standing up a single cloudflared Fastapi wrapper around your code. No production code churn.
26
+ - ⚡️ Parallelize training and achieve 80% GPU util. via PipelineRL
27
+ - 🗂️ Train prompts and models across multiple experiments
28
+ - 🛠️ Spin up experiment queues and datastores locally for dev work
29
+ - 🔩 Run serverless training via cli or programmatically
30
+ - 🏢 Scales gpu-based model training to 64 H100s seemlessly
31
+ - 💾 Use GEPA-calibrated judges for fast, accurate rubric scoring
32
+ - 🖥️ Supports HTTP-based training across all programming languages
33
+ - 🤖 CLI utilities tuned for use with Claude Code, Codex, Opencode
34
+
35
+ ## Getting Started
36
+
37
+ ```bash
38
+ # Use with OpenAI Codex
39
+ uvx synth-ai codex
40
+ ```
41
+
42
+ ```bash
43
+ # Use with Opencode
44
+ uvx synth-ai opencode
45
+ ```
46
+
47
+ Synth is maintained by devs behind the [MIPROv2](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=jauNVA8AAAAJ&citation_for_view=jauNVA8AAAAJ:u5HHmVD_uO8C) prompt optimizer.
48
+
49
+ ## Documentation
50
+
51
+ Docs available at [docs.usesynth.ai](https://docs.usesynth.ai/overview).
52
+
53
+ ## In-Process Runner (SDK)
54
+
55
+ Run GEPA/MIPRO/RL jobs against a tunneled task app without the CLI:
56
+
57
+ ```python
58
+ import asyncio
59
+ import os
60
+ from synth_ai.sdk.task import run_in_process_job
61
+
62
+ result = asyncio.run(
63
+ run_in_process_job(
64
+ job_type="prompt_learning",
65
+ config_path="configs/style_matching_gepa.toml",
66
+ task_app_path="task_apps/style_matching_task_app.py",
67
+ overrides={"prompt_learning.gepa.rollout.budget": 4},
68
+ backend_url=os.getenv("TARGET_BACKEND_BASE_URL"), # resolves envs automatically
69
+ )
70
+ )
71
+ print(result.job_id, result.status.get("status"))
72
+ ```
73
+
74
+ Env priority for the backend URL: `TARGET_BACKEND_BASE_URL` → `BACKEND_OVERRIDE` → `SYNTH_BACKEND_URL` → `BACKEND_BASE_URL` → `NEXT_PUBLIC_API_URL` → fallback to `get_backend_from_env()`. Required keys: `SYNTH_API_KEY`, `ENVIRONMENT_API_KEY`, plus any model keys used by your task app.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-ai"
3
- version = "0.3.1.dev1"
3
+ version = "0.3.1.dev3"
4
4
  description = "Serverless Posttraining for Agents - Core AI functionality and tracing"
5
5
  authors = [{name = "Synth AI", email = "josh@usesynth.ai"}]
6
6
  license = "MIT"
@@ -194,7 +194,7 @@ def _start_task_app_server(
194
194
 
195
195
  # Resolve the synth-ai root directory
196
196
  import synth_ai
197
- synth_ai_root = Path(synth_ai.__file__).resolve().parent.parent
197
+ synth_ai_root = Path(synth_ai.__file__ or Path(__file__).resolve()).resolve().parent.parent
198
198
 
199
199
  click.echo(f"[smoke] Starting task app '{task_app_name}' on port {port}...", err=True)
200
200
  click.echo(f"[smoke] Command: {' '.join(cmd)}", err=True)
@@ -25,7 +25,7 @@ from synth_ai.sdk.task.server import TaskAppConfig, create_task_app, run_task_ap
25
25
  def _load_build_config():
26
26
  import synth_ai
27
27
 
28
- synth_ai_path = Path(synth_ai.__file__).resolve().parent.parent
28
+ synth_ai_path = Path(synth_ai.__file__ or Path(__file__).resolve()).resolve().parent.parent
29
29
  module_path = synth_ai_path / "examples" / "warming_up_to_rl" / "task_app" / "grpo_crafter.py"
30
30
 
31
31
  if not module_path.exists():
@@ -33,7 +33,7 @@ def _load_build_config():
33
33
 
34
34
  import synth_ai
35
35
 
36
- synth_ai_path = Path(synth_ai.__file__).resolve().parent.parent
36
+ synth_ai_path = Path(synth_ai.__file__ or Path(__file__).resolve()).resolve().parent.parent
37
37
  module_path = (
38
38
  synth_ai_path / "examples" / "task_apps" / "crafter" / "task_app" / "grpo_crafter.py"
39
39
  )
@@ -499,11 +499,8 @@ def fastapi_app():
499
499
  seed_val = 0
500
500
  if seed_val == 0:
501
501
  try:
502
- seed_val = (
503
- int(env_cfg.get("seed"))
504
- if isinstance(env_cfg, dict) and env_cfg.get("seed") is not None
505
- else 0
506
- )
502
+ raw_seed = env_cfg.get("seed") if isinstance(env_cfg, dict) else None
503
+ seed_val = int(raw_seed) if raw_seed is not None else 0
507
504
  except Exception:
508
505
  seed_val = 0
509
506
  subject = (env_cfg.get("subject") if isinstance(env_cfg, dict) else None) or os.getenv(
@@ -487,11 +487,8 @@ def fastapi_app():
487
487
  seed_val = 0
488
488
  if seed_val == 0:
489
489
  try:
490
- seed_val = (
491
- int(env_cfg.get("seed"))
492
- if isinstance(env_cfg, dict) and env_cfg.get("seed") is not None
493
- else 0
494
- )
490
+ raw_seed = env_cfg.get("seed") if isinstance(env_cfg, dict) else None
491
+ seed_val = int(raw_seed) if raw_seed is not None else 0
495
492
  except Exception:
496
493
  seed_val = 0
497
494
  subject = (env_cfg.get("subject") if isinstance(env_cfg, dict) else None) or os.getenv(
@@ -6,7 +6,7 @@ import subprocess
6
6
 
7
7
  import click
8
8
 
9
- from .root import SQLD_VERSION, find_sqld_binary, install_sqld # type: ignore[import-untyped]
9
+ from synth_ai.cli.root import SQLD_VERSION, find_sqld_binary, install_sqld # type: ignore[import-untyped]
10
10
 
11
11
 
12
12
  def register(cli: click.Group) -> None:
@@ -1,4 +1,7 @@
1
1
  import os
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Dict
2
5
 
3
6
  import click
4
7
 
@@ -13,8 +16,12 @@ __all__ = [
13
16
  "ensure_env_credentials",
14
17
  "ensure_port_free",
15
18
  "preflight_env_key",
19
+ "interactive_fill_env",
20
+ "save_to_env_file",
16
21
  ]
17
22
 
23
+ _ENV_LINE = re.compile(r"^\s*(?:export\s+)?(?P<key>[A-Za-z0-9_]+)\s*=\s*(?P<value>.*)$")
24
+
18
25
 
19
26
  def ensure_env_credentials(*, require_synth: bool = False, prompt: bool = True) -> None:
20
27
  """Ensure required API keys are present in the process environment."""
@@ -185,3 +192,104 @@ def preflight_env_key(*, crash_on_failure: bool = False) -> None:
185
192
  if crash_on_failure:
186
193
  raise click.ClickException(f"[CRITICAL] {message}") from exc
187
194
  click.echo(f"[WARN] {message}; proceeding anyway")
195
+
196
+
197
+ def _parse_env_file(path: Path) -> dict[str, str]:
198
+ data: dict[str, str] = {}
199
+ try:
200
+ for line in path.read_text(encoding="utf-8").splitlines():
201
+ if not line or line.lstrip().startswith("#") or "=" not in line:
202
+ continue
203
+ key, value = line.split("=", 1)
204
+ data[key.strip()] = value.strip()
205
+ except FileNotFoundError:
206
+ pass
207
+ return data
208
+
209
+
210
+ def _merge_env_content(original: str, updates: Dict[str, str]) -> str:
211
+ """Merge env updates into existing content while preserving comments/spacing."""
212
+ lines = original.splitlines(keepends=True)
213
+ seen: set[str] = set()
214
+ newline_default = "\n"
215
+ if lines and lines[-1].endswith("\r\n"):
216
+ newline_default = "\r\n"
217
+
218
+ merged: list[str] = []
219
+ for line in lines:
220
+ stripped = line.rstrip("\r\n")
221
+ m = _ENV_LINE.match(stripped)
222
+ key = m.group("key") if m else None
223
+ if key and key in updates and key not in seen:
224
+ end = "\r\n" if line.endswith("\r\n") else "\n"
225
+ merged.append(f"{key}={updates[key]}{end}")
226
+ seen.add(key)
227
+ else:
228
+ merged.append(line)
229
+
230
+ for key, val in updates.items():
231
+ if key not in seen:
232
+ merged.append(f"{key}={val}{newline_default}")
233
+
234
+ if merged and not merged[-1].endswith(("\n", "\r\n")):
235
+ merged[-1] = merged[-1] + newline_default
236
+
237
+ return "".join(merged)
238
+
239
+
240
+ def save_to_env_file(env_path: Path, key: str, value: str) -> Path:
241
+ """Update a single key in .env preserving comments/formatting."""
242
+ existing = env_path.read_text(encoding="utf-8", errors="ignore") if env_path.exists() else ""
243
+ merged = _merge_env_content(existing, {key: value})
244
+ env_path.parent.mkdir(parents=True, exist_ok=True)
245
+ env_path.write_text(merged, encoding="utf-8")
246
+ return env_path
247
+
248
+
249
+ def interactive_fill_env(env_path: Path) -> Path | None:
250
+ """Fill ENVIRONMENT_API_KEY/SYNTH_API_KEY/OPENAI_API_KEY while preserving file content."""
251
+ existing = _parse_env_file(env_path) if env_path.exists() else {}
252
+
253
+ def _prompt(label: str, *, default: str = "", required: bool) -> str | None:
254
+ while True:
255
+ try:
256
+ value = click.prompt(
257
+ label, default=default, show_default=bool(default) or not required
258
+ ).strip()
259
+ except (click.Abort, EOFError, KeyboardInterrupt): # pragma: no cover - interactive paths
260
+ click.echo("Aborted env creation.")
261
+ return None
262
+ if value or not required:
263
+ return value
264
+ click.echo("This field is required.")
265
+
266
+ # Allow non-interactive tests to run; skip TTY enforcement.
267
+ env_api_key = _prompt(
268
+ "ENVIRONMENT_API_KEY",
269
+ default=existing.get("ENVIRONMENT_API_KEY", ""),
270
+ required=True,
271
+ )
272
+ if env_api_key is None:
273
+ return None
274
+ synth_key = _prompt(
275
+ "SYNTH_API_KEY (optional)",
276
+ default=existing.get("SYNTH_API_KEY", ""),
277
+ required=False,
278
+ ) or ""
279
+ openai_key = _prompt(
280
+ "OPENAI_API_KEY (optional)",
281
+ default=existing.get("OPENAI_API_KEY", ""),
282
+ required=False,
283
+ ) or ""
284
+
285
+ updates = {
286
+ "ENVIRONMENT_API_KEY": env_api_key,
287
+ "SYNTH_API_KEY": synth_key,
288
+ "OPENAI_API_KEY": openai_key,
289
+ }
290
+
291
+ content = env_path.read_text(encoding="utf-8", errors="ignore") if env_path.exists() else ""
292
+ merged = _merge_env_content(content, updates)
293
+ env_path.parent.mkdir(parents=True, exist_ok=True)
294
+ env_path.write_text(merged, encoding="utf-8")
295
+ return env_path
@@ -97,7 +97,7 @@ def _find_venv_python() -> str:
97
97
  try:
98
98
  import synth_ai
99
99
 
100
- package_path = Path(synth_ai.__file__).parent.parent.parent
100
+ package_path = Path(synth_ai.__file__ or Path(__file__).resolve()).parent.parent.parent
101
101
  pkg_venv = package_path / ".venv" / "bin" / "python"
102
102
  if pkg_venv.exists() and os.access(pkg_venv, os.X_OK):
103
103
  return str(pkg_venv)
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from datetime import datetime
6
6
  from decimal import Decimal
7
- from typing import Any, Optional
7
+ from typing import Any, List, Optional
8
8
  from uuid import UUID
9
9
 
10
10
  from synth_ai.core.http import AsyncHttpClient, HTTPError
@@ -98,7 +98,7 @@ class AgentSessionClient:
98
98
  async def create(
99
99
  self,
100
100
  org_id: Optional[UUID] = None,
101
- limits: Optional[list[dict[str, Any]]] = None,
101
+ limits: Optional[List[dict[str, Any]]] = None,
102
102
  tracing_session_id: Optional[str] = None,
103
103
  session_type: Optional[str] = None,
104
104
  expires_at: Optional[datetime] = None,
@@ -295,7 +295,7 @@ class AgentSessionClient:
295
295
  raise InvalidLimitError(str(e)) from e
296
296
  raise
297
297
 
298
- async def list_limits(self, session_id: str) -> list[AgentSessionLimit]:
298
+ async def list_limits(self, session_id: str) -> List[AgentSessionLimit]:
299
299
  """Get all limits for a session."""
300
300
  try:
301
301
  async with self._http:
@@ -334,7 +334,7 @@ class AgentSessionClient:
334
334
  status: Optional[str] = None,
335
335
  limit: int = 100,
336
336
  offset: int = 0,
337
- ) -> list[AgentSession]:
337
+ ) -> List[AgentSession]:
338
338
  """List sessions for the authenticated organization."""
339
339
  params: dict[str, Any] = {"limit": limit, "offset": offset}
340
340
  if status:
@@ -353,7 +353,7 @@ class AgentSessionClient:
353
353
  metric_type: Optional[str] = None,
354
354
  limit: int = 100,
355
355
  offset: int = 0,
356
- ) -> list[SessionUsageRecord]:
356
+ ) -> List[SessionUsageRecord]:
357
357
  """Get usage records for a session."""
358
358
  params: dict[str, Any] = {"limit": limit, "offset": offset}
359
359
  if metric_type:
@@ -381,4 +381,3 @@ class AgentSessionClient:
381
381
  if e.status == 404:
382
382
  raise SessionNotFoundError(session_id) from e
383
383
  raise
384
-
@@ -7,6 +7,7 @@ validation, and discovery.
7
7
  from synth_ai.cli.task_apps.commands import (
8
8
  AppChoice,
9
9
  TaskAppEntryType,
10
+ _markov_message_from_dict,
10
11
  register,
11
12
  serve_command,
12
13
  task_app_group,
@@ -21,5 +22,5 @@ __all__ = [
21
22
  "task_app_group",
22
23
  "serve_command",
23
24
  "register",
25
+ "_markov_message_from_dict",
24
26
  ]
25
-
@@ -209,7 +209,9 @@ def _parse_datetime_for_trace(value: Any) -> datetime | None:
209
209
  def _time_record_from_dict(payload: dict[str, Any] | None) -> TimeRecord:
210
210
  payload = payload or {}
211
211
  event_time = payload.get("event_time")
212
- if not isinstance(event_time, int | float):
212
+ if event_time is None:
213
+ event_time = float(time.time())
214
+ elif not isinstance(event_time, int | float):
213
215
  try:
214
216
  event_time = float(event_time)
215
217
  except Exception:
@@ -924,7 +926,7 @@ def _build_modal_config_from_ast(modal_call: ast.Call) -> ModalDeploymentConfigT
924
926
  if isinstance(value_node, ast.List | ast.Tuple):
925
927
  for elt in value_node.elts:
926
928
  if isinstance(elt, ast.Constant):
927
- packages.append(elt.value)
929
+ packages.append(str(elt.value))
928
930
  kwargs[kw.arg] = tuple(packages)
929
931
  elif kw.arg == "extra_local_dirs" and isinstance(kw.value, ast.List | ast.Tuple):
930
932
  # Handle extra_local_dirs list/tuple of tuples
@@ -0,0 +1,52 @@
1
+ """Backwards-compatible Turso CLI entry point.
2
+
3
+ This module re-exports the infra.turso helpers so existing imports
4
+ (`synth_ai.cli.turso`) continue to work for tests and downstream users.
5
+ It also proxies infra functions so monkeypatching works in tests.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import synth_ai.cli.infra.turso as _infra_turso
11
+ from synth_ai.cli.infra.turso import turso # noqa: F401
12
+ from synth_ai.cli.root import SQLD_VERSION, find_sqld_binary, install_sqld # noqa: F401
13
+
14
+ _get_sqld_version_inner = _infra_turso._get_sqld_version
15
+
16
+
17
+ def _get_sqld_version(binary: str) -> str | None: # noqa: F401
18
+ """Delegates to the original infra implementation (monkeypatchable)."""
19
+ return _get_sqld_version_inner(binary)
20
+
21
+
22
+ def _proxy_get_sqld_version(binary: str) -> str | None:
23
+ # Dynamic lookup so monkeypatching synth_ai.cli.turso._get_sqld_version is honored
24
+ import synth_ai.cli.turso as mod
25
+
26
+ return mod._get_sqld_version(binary)
27
+
28
+
29
+ def _proxy_find_sqld_binary() -> str | None:
30
+ import synth_ai.cli.turso as mod
31
+
32
+ return mod.find_sqld_binary()
33
+
34
+
35
+ def _proxy_install_sqld():
36
+ import synth_ai.cli.turso as mod
37
+
38
+ return mod.install_sqld()
39
+
40
+
41
+ # Point infra at proxies so tests that monkeypatch this module influence the CLI command.
42
+ _infra_turso._get_sqld_version = _proxy_get_sqld_version # type: ignore[assignment]
43
+ _infra_turso.find_sqld_binary = _proxy_find_sqld_binary # type: ignore[assignment]
44
+ _infra_turso.install_sqld = _proxy_install_sqld # type: ignore[assignment]
45
+
46
+ __all__ = [
47
+ "turso",
48
+ "_get_sqld_version",
49
+ "find_sqld_binary",
50
+ "install_sqld",
51
+ "SQLD_VERSION",
52
+ ]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import os
4
5
  import sqlite3
5
6
  from collections.abc import Sequence
6
7
  from dataclasses import dataclass
@@ -9,7 +10,7 @@ from typing import Any
9
10
  Row = sqlite3.Row
10
11
 
11
12
 
12
- def connect(db_path: str | bytes | int) -> sqlite3.Connection:
13
+ def connect(db_path: str | bytes | os.PathLike[str] | os.PathLike[bytes]) -> sqlite3.Connection:
13
14
  conn = sqlite3.connect(db_path)
14
15
  conn.row_factory = sqlite3.Row
15
16
  return conn
@@ -314,4 +315,3 @@ __all__ = [
314
315
  "load_session_trace",
315
316
  ]
316
317
 
317
-
@@ -4,6 +4,7 @@ import logging
4
4
  import os
5
5
  import pathlib
6
6
  import shutil
7
+ import socket
7
8
  import subprocess
8
9
  import sys
9
10
  import time
@@ -124,6 +125,15 @@ class SqldDaemon:
124
125
  if self.process and self.process.poll() is None:
125
126
  return self.process
126
127
 
128
+ # Avoid port conflicts by selecting free ports when needed
129
+ if not self._port_available(self.hrana_port):
130
+ self.hrana_port = self._find_free_port()
131
+ if not self._port_available(self.http_port) or self.http_port == self.hrana_port:
132
+ self.http_port = self._find_free_port()
133
+ # Ensure distinct ports
134
+ if self.http_port == self.hrana_port:
135
+ self.http_port = self._find_free_port()
136
+
127
137
  db_file = pathlib.Path(self.db_path).resolve()
128
138
  db_file.parent.mkdir(parents=True, exist_ok=True)
129
139
 
@@ -174,6 +184,22 @@ class SqldDaemon:
174
184
 
175
185
  raise TimeoutError(f"sqld daemon did not become ready within {timeout} seconds")
176
186
 
187
+ @staticmethod
188
+ def _port_available(port: int) -> bool:
189
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
190
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
191
+ try:
192
+ s.bind(("127.0.0.1", port))
193
+ return True
194
+ except OSError:
195
+ return False
196
+
197
+ @staticmethod
198
+ def _find_free_port() -> int:
199
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
200
+ s.bind(("127.0.0.1", 0))
201
+ return s.getsockname()[1]
202
+
177
203
  def stop(self, timeout: float = 5.0):
178
204
  """Stop the sqld daemon gracefully."""
179
205
  if not self.process: