synth-ai 0.2.4.dev7__py3-none-any.whl → 0.2.4.dev9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synth-ai might be problematic. Click here for more details.

Files changed (154) hide show
  1. synth_ai/__init__.py +1 -1
  2. synth_ai/cli/__init__.py +6 -0
  3. synth_ai/cli/balance.py +3 -15
  4. synth_ai/cli/demo.py +68 -9
  5. synth_ai/cli/rl_demo.py +137 -0
  6. synth_ai/cli/root.py +65 -0
  7. synth_ai/config/base_url.py +47 -0
  8. synth_ai/demos/core/__init__.py +1 -0
  9. synth_ai/demos/core/cli.py +621 -0
  10. synth_ai/demos/demo_task_apps/__init__.py +1 -0
  11. synth_ai/demos/demo_task_apps/core.py +374 -0
  12. synth_ai/demos/demo_task_apps/math/__init__.py +1 -0
  13. synth_ai/demos/demo_task_apps/math/app.py +37 -0
  14. synth_ai/demos/demo_task_apps/math/config.toml +44 -0
  15. synth_ai/demos/demo_task_apps/math/deploy_modal.py +60 -0
  16. synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +22 -0
  17. synth_ai/environments/examples/bandit/__init__.py +33 -0
  18. synth_ai/environments/examples/bandit/engine.py +294 -0
  19. synth_ai/environments/examples/bandit/environment.py +194 -0
  20. synth_ai/environments/examples/bandit/taskset.py +200 -0
  21. synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +250 -0
  22. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +59 -0
  23. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +152 -0
  24. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +24 -0
  25. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +1194 -0
  26. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +56 -0
  27. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +32 -0
  28. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +724 -0
  29. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +384 -0
  30. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +53 -0
  31. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +178 -0
  32. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +222 -0
  33. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +183 -0
  34. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +210 -0
  35. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +206 -0
  36. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +49 -0
  37. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +64 -0
  38. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +88 -0
  39. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +77 -0
  40. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +324 -0
  41. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +580 -0
  42. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +362 -0
  43. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +49 -0
  44. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +332 -0
  45. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +97 -0
  46. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +217 -0
  47. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +87 -0
  48. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +88 -0
  49. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +195 -0
  50. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +400 -0
  51. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +195 -0
  52. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +56 -0
  53. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +858 -0
  54. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +52 -0
  55. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +874 -0
  56. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +1412 -0
  57. synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +216 -0
  58. synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +296 -0
  59. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +58 -0
  60. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +464 -0
  61. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +152 -0
  62. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +51 -0
  63. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +1412 -0
  64. synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +112 -0
  65. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +203 -0
  66. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +305 -0
  67. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +126 -0
  68. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +94 -0
  69. synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +142 -0
  70. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +26 -0
  71. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +984 -0
  72. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +724 -0
  73. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +386 -0
  74. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +205 -0
  75. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +150 -0
  76. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +283 -0
  77. synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +280 -0
  78. synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +456 -0
  79. synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +166 -0
  80. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +102 -0
  81. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +128 -0
  82. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +655 -0
  83. synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +202 -0
  84. synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +166 -0
  85. synth_ai/environments/examples/crafter_classic/environment.py +41 -2
  86. synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +1 -0
  87. synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +202 -0
  88. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +159 -0
  89. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +158 -0
  90. synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +71 -0
  91. synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +105 -0
  92. synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +119 -0
  93. synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +52 -0
  94. synth_ai/environments/examples/enron/units/keyword_stats.py +112 -0
  95. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +1188 -0
  96. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +48 -0
  97. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +562 -0
  98. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +221 -0
  99. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +981 -0
  100. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +74 -0
  101. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +831 -0
  102. synth_ai/environments/examples/red/agent_demos/__init__.py +1 -0
  103. synth_ai/environments/examples/red/units/__init__.py +1 -0
  104. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +899 -0
  105. synth_ai/environments/examples/sokoban/units/astar_common.py +95 -0
  106. synth_ai/environments/service/app.py +8 -0
  107. synth_ai/http.py +102 -0
  108. synth_ai/inference/__init__.py +7 -0
  109. synth_ai/inference/client.py +20 -0
  110. synth_ai/install_sqld.sh +40 -0
  111. synth_ai/jobs/client.py +246 -0
  112. synth_ai/learning/__init__.py +24 -0
  113. synth_ai/learning/client.py +149 -0
  114. synth_ai/learning/config.py +43 -0
  115. synth_ai/learning/constants.py +29 -0
  116. synth_ai/learning/ft_client.py +59 -0
  117. synth_ai/learning/health.py +43 -0
  118. synth_ai/learning/jobs.py +205 -0
  119. synth_ai/learning/rl_client.py +256 -0
  120. synth_ai/learning/sse.py +58 -0
  121. synth_ai/learning/validators.py +48 -0
  122. synth_ai/lm/core/main_v3.py +13 -0
  123. synth_ai/lm/core/synth_models.py +48 -0
  124. synth_ai/lm/core/vendor_clients.py +9 -6
  125. synth_ai/lm/vendors/core/openai_api.py +31 -3
  126. synth_ai/lm/vendors/openai_standard.py +45 -14
  127. synth_ai/lm/vendors/supported/custom_endpoint.py +12 -2
  128. synth_ai/lm/vendors/synth_client.py +372 -28
  129. synth_ai/rl/__init__.py +30 -0
  130. synth_ai/rl/contracts.py +32 -0
  131. synth_ai/rl/env_keys.py +137 -0
  132. synth_ai/rl/secrets.py +19 -0
  133. synth_ai/scripts/verify_rewards.py +100 -0
  134. synth_ai/task/__init__.py +10 -0
  135. synth_ai/task/contracts.py +120 -0
  136. synth_ai/task/health.py +28 -0
  137. synth_ai/task/validators.py +12 -0
  138. synth_ai/tracing_v3/hooks.py +3 -1
  139. synth_ai/tracing_v3/session_tracer.py +123 -2
  140. synth_ai/tracing_v3/turso/manager.py +218 -0
  141. synth_ai/tracing_v3/turso/models.py +53 -0
  142. synth_ai-0.2.4.dev9.dist-info/METADATA +91 -0
  143. {synth_ai-0.2.4.dev7.dist-info → synth_ai-0.2.4.dev9.dist-info}/RECORD +147 -30
  144. {synth_ai-0.2.4.dev7.dist-info → synth_ai-0.2.4.dev9.dist-info}/entry_points.txt +1 -0
  145. synth_ai/tui/__init__.py +0 -1
  146. synth_ai/tui/__main__.py +0 -13
  147. synth_ai/tui/cli/__init__.py +0 -1
  148. synth_ai/tui/cli/query_experiments.py +0 -164
  149. synth_ai/tui/cli/query_experiments_v3.py +0 -164
  150. synth_ai/tui/dashboard.py +0 -340
  151. synth_ai-0.2.4.dev7.dist-info/METADATA +0 -193
  152. {synth_ai-0.2.4.dev7.dist-info → synth_ai-0.2.4.dev9.dist-info}/WHEEL +0 -0
  153. {synth_ai-0.2.4.dev7.dist-info → synth_ai-0.2.4.dev9.dist-info}/licenses/LICENSE +0 -0
  154. {synth_ai-0.2.4.dev7.dist-info → synth_ai-0.2.4.dev9.dist-info}/top_level.txt +0 -0
synth_ai/__init__.py CHANGED
@@ -23,7 +23,7 @@ from synth_ai.tracing_v1.abstractions import (
23
23
  from synth_ai.tracing_v1.decorators import trace_event_async, trace_event_sync
24
24
  from synth_ai.tracing_v1.upload import upload
25
25
 
26
- __version__ = "0.2.4.dev7"
26
+ __version__ = "0.2.4.dev8"
27
27
  __all__ = [
28
28
  "LM",
29
29
  "tracing",
synth_ai/cli/__init__.py CHANGED
@@ -69,3 +69,9 @@ try:
69
69
  _demo.register(cli)
70
70
  except Exception:
71
71
  pass
72
+ try:
73
+ from . import rl_demo as _rl_demo
74
+
75
+ _rl_demo.register(cli)
76
+ except Exception:
77
+ pass
synth_ai/cli/balance.py CHANGED
@@ -19,10 +19,10 @@ PROD_BACKEND_BASE = "https://agent-learning.onrender.com/api/v1"
19
19
 
20
20
 
21
21
  def _get_default_base_url() -> str:
22
- # Prefer explicit backend variables that are NOT modal; else default to prod backend
22
+ # Prefer explicit backend variables; else default to prod backend
23
23
  for var in ("SYNTH_BACKEND_BASE_URL", "BACKEND_BASE_URL", "SYNTH_BASE_URL"):
24
24
  val = os.getenv(var)
25
- if val and ("modal" not in val.lower() and "modal.run" not in val.lower()):
25
+ if val:
26
26
  return val
27
27
  return PROD_BACKEND_BASE
28
28
 
@@ -87,19 +87,7 @@ def register(cli):
87
87
 
88
88
  base = _ensure_api_v1_prefix(base_url)
89
89
 
90
- # Hard guard: never hit Modal URLs for account balance
91
- try:
92
- parsed = urlparse(base)
93
- host = (parsed.hostname or "").lower()
94
- except Exception:
95
- host = ""
96
- if "modal" in host or "modal.run" in base.lower():
97
- # Override to prod backend unconditionally
98
- fallback = PROD_BACKEND_BASE
99
- console.print(
100
- f"[yellow]Detected remote Modal URL ({base}). Using backend instead:[/yellow] {fallback}"
101
- )
102
- base = fallback
90
+ # No special-casing for modal.run domains; honor the provided base URL
103
91
 
104
92
  try:
105
93
  resp: Response = requests.get(
synth_ai/cli/demo.py CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- CLI: interactive launcher for example demos.
3
+ CLI: interactive launcher for example demos and forwarders for new RL demo.
4
4
 
5
- Finds all `run_demo.sh` scripts under `examples/` and lets the user pick one
6
- to run. Intended to be used as: `uvx synth-ai demo` or `synth-ai demo`.
5
+ - `synth-ai demo` (no subcommand) -> legacy examples/ runner (run_demo.sh picker)
6
+ - `synth-ai demo deploy|configure|run` -> forwards to synth_ai.demos.core.cli
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -21,12 +21,32 @@ def _find_demo_scripts(root: Path) -> list[Path]:
21
21
  return sorted([p for p in root.rglob("run_demo.sh") if p.is_file()])
22
22
 
23
23
 
24
+ def _forward_to_new(args: list[str]) -> None:
25
+ import sys
26
+ try:
27
+ from synth_ai.demos.core import cli as demo_cli # type: ignore
28
+ except Exception as e: # pragma: no cover
29
+ click.echo(f"Failed to import demo CLI: {e}")
30
+ sys.exit(1)
31
+ rc = int(demo_cli.main(args) or 0)
32
+ if rc != 0:
33
+ sys.exit(rc)
34
+
35
+
24
36
  def register(cli):
25
- @cli.command()
26
- @click.option("--list", "list_only", is_flag=True, help="List available demos and exit")
27
- @click.option("-f", "filter_term", default="", help="Filter demos by substring")
28
- def demo(list_only: bool, filter_term: str):
29
- """Launch an interactive demo from examples/"""
37
+ @cli.group("demo", invoke_without_command=True)
38
+ @click.option("--list", "list_only", is_flag=True, help="List available legacy demos and exit")
39
+ @click.option("-f", "filter_term", default="", help="Filter legacy demos by substring")
40
+ @click.pass_context
41
+ def demo(ctx: click.Context, list_only: bool, filter_term: str):
42
+ """Demo helpers.
43
+
44
+ - Legacy mode (no subcommand): find and run examples/*/run_demo.sh
45
+ - New RL demo subcommands: deploy, configure, run
46
+ """
47
+ if ctx.invoked_subcommand is not None:
48
+ return
49
+ # Legacy behavior: interactive examples runner
30
50
  repo_root = Path(os.getcwd())
31
51
  examples_dir = repo_root / "examples"
32
52
  demos = _find_demo_scripts(examples_dir)
@@ -63,10 +83,49 @@ def register(cli):
63
83
  click.echo("")
64
84
  click.echo(f"🚀 Running {script.relative_to(repo_root)}\n")
65
85
 
66
- # Run via bash to avoid relying on executable bit; inherit environment
67
86
  try:
68
87
  subprocess.run(["bash", str(script)], check=True)
69
88
  except subprocess.CalledProcessError as e:
70
89
  click.echo(f"❌ Demo exited with non-zero status: {e.returncode}")
71
90
  except KeyboardInterrupt:
72
91
  click.echo("\n🛑 Demo interrupted by user")
92
+
93
+ # (prepare command removed; configure now prepares baseline TOML)
94
+
95
+ @demo.command("deploy")
96
+ @click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
97
+ @click.option("--app", type=click.Path(), default=None, help="Path to Modal app.py for uv run modal deploy")
98
+ @click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
99
+ @click.option("--script", type=click.Path(), default=None, help="Path to deploy_task_app.sh (optional legacy)")
100
+ def demo_deploy(local: bool, app: str | None, name: str, script: str | None):
101
+ args: list[str] = ["rl_demo.deploy"]
102
+ if local:
103
+ args.append("--local")
104
+ if app:
105
+ args.extend(["--app", app])
106
+ if name:
107
+ args.extend(["--name", name])
108
+ if script:
109
+ args.extend(["--script", script])
110
+ _forward_to_new(args)
111
+
112
+ @demo.command("configure")
113
+ def demo_configure():
114
+ _forward_to_new(["rl_demo.configure"])
115
+
116
+ @demo.command("run")
117
+ @click.option("--batch-size", type=int, default=None)
118
+ @click.option("--group-size", type=int, default=None)
119
+ @click.option("--model", type=str, default=None)
120
+ @click.option("--timeout", type=int, default=600)
121
+ def demo_run(batch_size: int | None, group_size: int | None, model: str | None, timeout: int):
122
+ args = ["rl_demo.run"]
123
+ if batch_size is not None:
124
+ args.extend(["--batch-size", str(batch_size)])
125
+ if group_size is not None:
126
+ args.extend(["--group-size", str(group_size)])
127
+ if model:
128
+ args.extend(["--model", model])
129
+ if timeout:
130
+ args.extend(["--timeout", str(timeout)])
131
+ _forward_to_new(args)
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ New RL demo command group kept fully separate from legacy demo.
4
+
5
+ Usage examples:
6
+ uvx synth-ai rl_demo check
7
+ uvx synth-ai rl_demo deploy --app /path/to/math_task_app.py --name synth-math-demo
8
+ uvx synth-ai rl_demo configure
9
+ uvx synth-ai rl_demo run --batch-size 4 --group-size 16 --model Qwen/Qwen3-0.6B
10
+
11
+ For convenience, dotted aliases are also exposed:
12
+ uvx synth-ai rl_demo.check
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import click
18
+
19
+
20
+ def _forward(args: list[str]) -> None:
21
+ import sys
22
+ try:
23
+ from synth_ai.demos.core import cli as demo_cli # type: ignore
24
+ except Exception as e: # pragma: no cover
25
+ click.echo(f"Failed to import RL demo CLI: {e}")
26
+ sys.exit(1)
27
+ rc = int(demo_cli.main(args) or 0)
28
+ if rc != 0:
29
+ sys.exit(rc)
30
+
31
+
32
+ def register(cli):
33
+ @cli.group("rl_demo")
34
+ def rl_demo():
35
+ """RL Demo commands (separate from legacy demo)."""
36
+
37
+ @rl_demo.command("check")
38
+ def rl_check():
39
+ _forward(["rl_demo.check"]) # reuse same implementation
40
+
41
+ # (prepare command removed; consolidated into configure)
42
+
43
+ @rl_demo.command("deploy")
44
+ @click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
45
+ @click.option("--app", type=click.Path(), default=None, help="Path to Modal app.py for uv run modal deploy")
46
+ @click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
47
+ @click.option("--script", type=click.Path(), default=None, help="Path to deploy_task_app.sh (optional legacy)")
48
+ def rl_deploy(local: bool, app: str | None, name: str, script: str | None):
49
+ args: list[str] = ["rl_demo.deploy"]
50
+ if local:
51
+ args.append("--local")
52
+ if app:
53
+ args.extend(["--app", app])
54
+ if name:
55
+ args.extend(["--name", name])
56
+ if script:
57
+ args.extend(["--script", script])
58
+ _forward(args)
59
+
60
+ @rl_demo.command("configure")
61
+ def rl_configure():
62
+ _forward(["rl_demo.configure"])
63
+
64
+ @rl_demo.command("run")
65
+ @click.option("--config", type=click.Path(), default=None, help="Path to TOML config (skip prompt)")
66
+ @click.option("--batch-size", type=int, default=None)
67
+ @click.option("--group-size", type=int, default=None)
68
+ @click.option("--model", type=str, default=None)
69
+ @click.option("--timeout", type=int, default=600)
70
+ @click.option("--dry-run", is_flag=True, help="Print request body and exit")
71
+ def rl_run(config: str | None, batch_size: int | None, group_size: int | None, model: str | None, timeout: int, dry_run: bool):
72
+ args = ["rl_demo.run"]
73
+ if config:
74
+ args.extend(["--config", config])
75
+ if batch_size is not None:
76
+ args.extend(["--batch-size", str(batch_size)])
77
+ if group_size is not None:
78
+ args.extend(["--group-size", str(group_size)])
79
+ if model:
80
+ args.extend(["--model", model])
81
+ if timeout is not None:
82
+ args.extend(["--timeout", str(timeout)])
83
+ if dry_run:
84
+ args.append("--dry-run")
85
+ _forward(args)
86
+
87
+ # Dotted aliases (top-level) for convenience: rl_demo.check etc.
88
+ @cli.command("rl_demo.check")
89
+ def rl_check_alias():
90
+ _forward(["rl_demo.check"])
91
+
92
+ # (prepare alias removed)
93
+
94
+ @cli.command("rl_demo.deploy")
95
+ @click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
96
+ @click.option("--app", type=click.Path(), default=None, help="Path to Modal app.py for uv run modal deploy")
97
+ @click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
98
+ @click.option("--script", type=click.Path(), default=None, help="Path to deploy_task_app.sh (optional legacy)")
99
+ def rl_deploy_alias(local: bool, app: str | None, name: str, script: str | None):
100
+ args: list[str] = ["rl_demo.deploy"]
101
+ if local:
102
+ args.append("--local")
103
+ if app:
104
+ args.extend(["--app", app])
105
+ if name:
106
+ args.extend(["--name", name])
107
+ if script:
108
+ args.extend(["--script", script])
109
+ _forward(args)
110
+
111
+ @cli.command("rl_demo.configure")
112
+ def rl_configure_alias():
113
+ _forward(["rl_demo.configure"])
114
+
115
+ @cli.command("rl_demo.run")
116
+ @click.option("--config", type=click.Path(), default=None, help="Path to TOML config (skip prompt)")
117
+ @click.option("--batch-size", type=int, default=None)
118
+ @click.option("--group-size", type=int, default=None)
119
+ @click.option("--model", type=str, default=None)
120
+ @click.option("--timeout", type=int, default=600)
121
+ @click.option("--dry-run", is_flag=True, help="Print request body and exit")
122
+ def rl_run_alias(config: str | None, batch_size: int | None, group_size: int | None, model: str | None, timeout: int, dry_run: bool):
123
+ args = ["rl_demo.run"]
124
+ if config:
125
+ args.extend(["--config", config])
126
+ if batch_size is not None:
127
+ args.extend(["--batch-size", str(batch_size)])
128
+ if group_size is not None:
129
+ args.extend(["--group-size", str(group_size)])
130
+ if model:
131
+ args.extend(["--model", model])
132
+ if timeout is not None:
133
+ args.extend(["--timeout", str(timeout)])
134
+ if dry_run:
135
+ args.append("--dry-run")
136
+ _forward(args)
137
+
synth_ai/cli/root.py CHANGED
@@ -71,6 +71,71 @@ def cli():
71
71
  """Synth AI - Software for aiding the best and multiplying the will."""
72
72
 
73
73
 
74
+ # === Legacy demo command group (aliases new rl_demo implementation) ===
75
+ @cli.group()
76
+ def demo():
77
+ """Demo helpers (deploy, configure, run)."""
78
+
79
+
80
+ def _forward_to_demo(args: list[str]) -> None:
81
+ # Lazy import to avoid loading demo deps unless needed
82
+ try:
83
+ from synth_ai.demos.core import cli as demo_cli # type: ignore
84
+ except Exception as e: # pragma: no cover
85
+ click.echo(f"Failed to import demo CLI: {e}")
86
+ sys.exit(1)
87
+ rc = int(demo_cli.main(args) or 0)
88
+ if rc != 0:
89
+ sys.exit(rc)
90
+
91
+
92
+ # (prepare command removed; handled by configure)
93
+
94
+
95
+ @demo.command()
96
+ @click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
97
+ @click.option("--app", type=click.Path(), default=None, help="Path to Modal app.py for uv run modal deploy")
98
+ @click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
99
+ @click.option("--script", type=click.Path(), default=None, help="Path to deploy_task_app.sh (optional legacy)")
100
+ def deploy(local: bool, app: str | None, name: str, script: str | None):
101
+ """Deploy the Math Task App (Modal by default)."""
102
+ args: list[str] = ["rl_demo.deploy"]
103
+ if local:
104
+ args.append("--local")
105
+ if app:
106
+ args.extend(["--app", app])
107
+ if name:
108
+ args.extend(["--name", name])
109
+ if script:
110
+ args.extend(["--script", script])
111
+ _forward_to_demo(args)
112
+
113
+
114
+ @demo.command()
115
+ def configure():
116
+ """Print resolved environment and config path."""
117
+ _forward_to_demo(["rl_demo.configure"])
118
+
119
+
120
+ @demo.command()
121
+ @click.option("--batch-size", type=int, default=None)
122
+ @click.option("--group-size", type=int, default=None)
123
+ @click.option("--model", type=str, default=None)
124
+ @click.option("--timeout", type=int, default=600)
125
+ def run(batch_size: int | None, group_size: int | None, model: str | None, timeout: int):
126
+ """Kick off a short RL job using the prepared TOML."""
127
+ args = ["rl_demo.run"]
128
+ if batch_size is not None:
129
+ args.extend(["--batch-size", str(batch_size)])
130
+ if group_size is not None:
131
+ args.extend(["--group-size", str(group_size)])
132
+ if model:
133
+ args.extend(["--model", model])
134
+ if timeout:
135
+ args.extend(["--timeout", str(timeout)])
136
+ _forward_to_demo(args)
137
+
138
+
74
139
  @cli.command()
75
140
  @click.option("--db-file", default="traces/v3/synth_ai.db", help="Database file path")
76
141
  @click.option("--sqld-port", default=8080, type=int, help="Port for sqld HTTP interface")
@@ -49,3 +49,50 @@ def get_learning_v2_base_url(mode: Literal["dev", "prod"] = "prod") -> str:
49
49
  return _normalize_base(dev)
50
50
 
51
51
  raise Exception()
52
+
53
+
54
+ def _resolve_override_mode() -> str:
55
+ """Return one of 'local', 'dev', 'prod' based on SYNTH_BACKEND_URL_OVERRIDE.
56
+
57
+ Defaults to 'prod' when unset or unrecognized.
58
+ """
59
+ ov = (os.getenv("SYNTH_BACKEND_URL_OVERRIDE", "") or "").strip().lower()
60
+ if ov in {"local", "dev", "prod"}:
61
+ return ov
62
+ return "prod"
63
+
64
+
65
+ def get_backend_from_env() -> tuple[str, str]:
66
+ """Resolve (base_url, api_key) using a simple LOCAL/DEV/PROD override scheme.
67
+
68
+ Env vars consulted:
69
+ - SYNTH_BACKEND_URL_OVERRIDE = local|dev|prod (case-insensitive)
70
+ - LOCAL_BACKEND_URL, TESTING_LOCAL_SYNTH_API_KEY
71
+ - DEV_BACKEND_URL, DEV_SYNTH_API_KEY
72
+ - PROD_BACKEND_URL, TESTING_PROD_SYNTH_API_KEY (fallback to SYNTH_API_KEY)
73
+
74
+ Base URL is normalized to end with '/api'.
75
+ Defaults: prod base URL → https://agent-learning.onrender.com/api
76
+ """
77
+ mode = _resolve_override_mode()
78
+ if mode == "local":
79
+ base = os.getenv("LOCAL_BACKEND_URL", "http://localhost:8000")
80
+ key = os.getenv("TESTING_LOCAL_SYNTH_API_KEY", "")
81
+ return base.rstrip("/"), key
82
+ if mode == "dev":
83
+ base = os.getenv("DEV_BACKEND_URL", "") or "http://localhost:8000"
84
+ key = os.getenv("DEV_SYNTH_API_KEY", "")
85
+ return base.rstrip("/"), key
86
+ # prod
87
+ base = os.getenv("PROD_BACKEND_URL", f"{PROD_BASE_URL_DEFAULT}")
88
+ # Ensure we return the root (no trailing /api). If default includes /api, strip it.
89
+ base = base.rstrip("/")
90
+ if base.endswith("/api"):
91
+ base = base[: -len("/api")]
92
+ # Prefer explicit PROD key, then testing key, then generic fallback
93
+ key = (
94
+ os.getenv("PROD_SYNTH_API_KEY", "")
95
+ or os.getenv("TESTING_PROD_SYNTH_API_KEY", "")
96
+ or os.getenv("SYNTH_API_KEY", "")
97
+ )
98
+ return base, key
@@ -0,0 +1 @@
1
+ # Core demo CLI and helpers package