synth-ai 0.2.9.dev5__py3-none-any.whl → 0.2.9.dev7__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.
- examples/common_old/backend.py +0 -1
- examples/crafter_debug_render.py +15 -6
- examples/evals_old/compare_models.py +1 -0
- examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +6 -2
- examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +4 -4
- examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +4 -3
- examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +6 -2
- examples/finetuning_old/synth_qwen_v1/finetune.py +1 -1
- examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +4 -4
- examples/finetuning_old/synth_qwen_v1/infer.py +1 -2
- examples/finetuning_old/synth_qwen_v1/poll.py +4 -2
- examples/finetuning_old/synth_qwen_v1/prepare_data.py +8 -8
- examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +5 -4
- examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +11 -8
- examples/finetuning_old/synth_qwen_v1/run_ft_job.py +17 -12
- examples/finetuning_old/synth_qwen_v1/upload_data.py +1 -1
- examples/finetuning_old/synth_qwen_v1/util.py +7 -2
- examples/rl/configs/eval_base_qwen.toml +1 -1
- examples/rl/configs/rl_from_base_qwen17.toml +1 -1
- examples/rl/download_dataset.py +26 -10
- examples/rl/run_eval.py +17 -15
- examples/rl/run_rl_and_save.py +24 -7
- examples/rl/task_app/math_single_step.py +128 -11
- examples/rl/task_app/math_task_app.py +11 -3
- examples/rl_old/task_app.py +222 -53
- examples/warming_up_to_rl/analyze_trace_db.py +7 -5
- examples/warming_up_to_rl/export_trace_sft.py +141 -16
- examples/warming_up_to_rl/groq_test.py +11 -4
- examples/warming_up_to_rl/manage_secrets.py +15 -6
- examples/warming_up_to_rl/readme.md +9 -2
- examples/warming_up_to_rl/run_eval.py +108 -30
- examples/warming_up_to_rl/run_fft_and_save.py +128 -52
- examples/warming_up_to_rl/run_local_rollout.py +87 -36
- examples/warming_up_to_rl/run_local_rollout_modal.py +113 -25
- examples/warming_up_to_rl/run_local_rollout_parallel.py +80 -16
- examples/warming_up_to_rl/run_local_rollout_traced.py +125 -20
- examples/warming_up_to_rl/run_rl_and_save.py +31 -7
- examples/warming_up_to_rl/run_rollout_remote.py +37 -10
- examples/warming_up_to_rl/task_app/grpo_crafter.py +90 -27
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +9 -27
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +46 -108
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +50 -17
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +35 -21
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +8 -4
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +29 -26
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +17 -13
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +106 -63
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +82 -84
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +76 -59
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +43 -49
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +5 -15
- synth_ai/__init__.py +1 -0
- synth_ai/api/train/builders.py +34 -10
- synth_ai/api/train/cli.py +172 -32
- synth_ai/api/train/config_finder.py +59 -4
- synth_ai/api/train/env_resolver.py +32 -14
- synth_ai/api/train/pollers.py +11 -3
- synth_ai/api/train/task_app.py +4 -1
- synth_ai/api/train/utils.py +20 -4
- synth_ai/cli/__init__.py +11 -4
- synth_ai/cli/balance.py +1 -1
- synth_ai/cli/demo.py +19 -5
- synth_ai/cli/rl_demo.py +75 -16
- synth_ai/cli/root.py +116 -37
- synth_ai/cli/task_apps.py +1276 -186
- synth_ai/cli/traces.py +1 -0
- synth_ai/cli/turso.py +73 -0
- synth_ai/core/experiment.py +0 -2
- synth_ai/demo_registry.py +67 -30
- synth_ai/demos/core/cli.py +493 -164
- synth_ai/demos/demo_task_apps/core.py +50 -6
- synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +2 -3
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +36 -28
- synth_ai/demos/demo_task_apps/math/_common.py +1 -2
- synth_ai/demos/demo_task_apps/math/deploy_modal.py +0 -2
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +168 -65
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
- synth_ai/environments/examples/bandit/engine.py +12 -4
- synth_ai/environments/examples/bandit/taskset.py +4 -4
- synth_ai/environments/reproducibility/tree.py +3 -1
- synth_ai/environments/service/core_routes.py +6 -2
- synth_ai/evals/base.py +0 -2
- synth_ai/experimental/synth_oss.py +11 -12
- synth_ai/handshake.py +3 -1
- synth_ai/http_client.py +31 -7
- synth_ai/inference/__init__.py +0 -2
- synth_ai/inference/client.py +8 -4
- synth_ai/jobs/client.py +40 -10
- synth_ai/learning/client.py +33 -8
- synth_ai/learning/config.py +0 -2
- synth_ai/learning/constants.py +0 -2
- synth_ai/learning/ft_client.py +6 -3
- synth_ai/learning/health.py +9 -2
- synth_ai/learning/jobs.py +17 -5
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +1 -3
- synth_ai/learning/prompts/random_search.py +4 -1
- synth_ai/learning/prompts/run_random_search_banking77.py +6 -1
- synth_ai/learning/rl_client.py +42 -14
- synth_ai/learning/sse.py +0 -2
- synth_ai/learning/validators.py +6 -2
- synth_ai/lm/caching/ephemeral.py +1 -3
- synth_ai/lm/core/exceptions.py +0 -2
- synth_ai/lm/core/main.py +13 -1
- synth_ai/lm/core/synth_models.py +0 -1
- synth_ai/lm/core/vendor_clients.py +4 -2
- synth_ai/lm/overrides.py +2 -2
- synth_ai/lm/vendors/core/anthropic_api.py +7 -7
- synth_ai/lm/vendors/core/openai_api.py +2 -0
- synth_ai/lm/vendors/openai_standard.py +3 -1
- synth_ai/lm/vendors/openai_standard_responses.py +6 -3
- synth_ai/lm/vendors/supported/custom_endpoint.py +1 -3
- synth_ai/lm/vendors/synth_client.py +37 -10
- synth_ai/rl/__init__.py +0 -1
- synth_ai/rl/contracts.py +0 -2
- synth_ai/rl/env_keys.py +6 -1
- synth_ai/task/__init__.py +1 -0
- synth_ai/task/apps/__init__.py +11 -11
- synth_ai/task/auth.py +29 -17
- synth_ai/task/client.py +3 -1
- synth_ai/task/contracts.py +1 -0
- synth_ai/task/datasets.py +3 -1
- synth_ai/task/errors.py +3 -2
- synth_ai/task/health.py +0 -2
- synth_ai/task/json.py +0 -1
- synth_ai/task/proxy.py +2 -5
- synth_ai/task/rubrics.py +9 -3
- synth_ai/task/server.py +31 -5
- synth_ai/task/tracing_utils.py +8 -3
- synth_ai/task/validators.py +0 -1
- synth_ai/task/vendors.py +0 -1
- synth_ai/tracing_v3/db_config.py +26 -1
- synth_ai/tracing_v3/decorators.py +1 -0
- synth_ai/tracing_v3/examples/basic_usage.py +3 -2
- synth_ai/tracing_v3/hooks.py +2 -0
- synth_ai/tracing_v3/replica_sync.py +1 -0
- synth_ai/tracing_v3/session_tracer.py +24 -3
- synth_ai/tracing_v3/storage/base.py +4 -1
- synth_ai/tracing_v3/storage/factory.py +0 -1
- synth_ai/tracing_v3/turso/manager.py +102 -38
- synth_ai/tracing_v3/turso/models.py +4 -1
- synth_ai/tracing_v3/utils.py +1 -0
- synth_ai/v0/tracing/upload.py +32 -135
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/METADATA +1 -1
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/RECORD +154 -154
- synth_ai/install_sqld.sh +0 -40
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/top_level.txt +0 -0
synth_ai/api/train/task_app.py
CHANGED
|
@@ -42,6 +42,7 @@ def _health_response_ok(resp: requests.Response | None) -> tuple[bool, str]:
|
|
|
42
42
|
def check_task_app_health(base_url: str, api_key: str, *, timeout: float = 10.0) -> TaskAppHealth:
|
|
43
43
|
# Send ALL known environment keys so the server can authorize any valid one
|
|
44
44
|
import os
|
|
45
|
+
|
|
45
46
|
headers = {"X-API-Key": api_key}
|
|
46
47
|
aliases = (os.getenv("ENVIRONMENT_API_KEY_ALIASES") or "").strip()
|
|
47
48
|
keys: list[str] = [api_key]
|
|
@@ -146,7 +147,9 @@ def list_modal_secrets(pattern: str | None = None) -> list[str]:
|
|
|
146
147
|
def get_modal_secret_value(name: str) -> str:
|
|
147
148
|
result = _run_modal(["secret", "get", name])
|
|
148
149
|
if result.code != 0:
|
|
149
|
-
raise click.ClickException(
|
|
150
|
+
raise click.ClickException(
|
|
151
|
+
f"modal secret get {name} failed: {result.stderr or result.stdout}"
|
|
152
|
+
)
|
|
150
153
|
value = result.stdout.strip()
|
|
151
154
|
if not value:
|
|
152
155
|
raise click.ClickException(f"Secret {name} is empty")
|
synth_ai/api/train/utils.py
CHANGED
|
@@ -83,7 +83,13 @@ class CLIResult:
|
|
|
83
83
|
stderr: str
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def run_cli(
|
|
86
|
+
def run_cli(
|
|
87
|
+
args: Iterable[str],
|
|
88
|
+
*,
|
|
89
|
+
cwd: Path | None = None,
|
|
90
|
+
env: Mapping[str, str] | None = None,
|
|
91
|
+
timeout: float | None = None,
|
|
92
|
+
) -> CLIResult:
|
|
87
93
|
proc = subprocess.run(
|
|
88
94
|
list(args),
|
|
89
95
|
cwd=cwd,
|
|
@@ -95,17 +101,27 @@ def run_cli(args: Iterable[str], *, cwd: Path | None = None, env: Mapping[str, s
|
|
|
95
101
|
return CLIResult(code=proc.returncode, stdout=proc.stdout.strip(), stderr=proc.stderr.strip())
|
|
96
102
|
|
|
97
103
|
|
|
98
|
-
def http_post(
|
|
104
|
+
def http_post(
|
|
105
|
+
url: str,
|
|
106
|
+
*,
|
|
107
|
+
headers: Mapping[str, str] | None = None,
|
|
108
|
+
json_body: Any | None = None,
|
|
109
|
+
timeout: float = 60.0,
|
|
110
|
+
) -> requests.Response:
|
|
99
111
|
resp = requests.post(url, headers=dict(headers or {}), json=json_body, timeout=timeout)
|
|
100
112
|
return resp
|
|
101
113
|
|
|
102
114
|
|
|
103
|
-
def http_get(
|
|
115
|
+
def http_get(
|
|
116
|
+
url: str, *, headers: Mapping[str, str] | None = None, timeout: float = 30.0
|
|
117
|
+
) -> requests.Response:
|
|
104
118
|
resp = requests.get(url, headers=dict(headers or {}), timeout=timeout)
|
|
105
119
|
return resp
|
|
106
120
|
|
|
107
121
|
|
|
108
|
-
def post_multipart(
|
|
122
|
+
def post_multipart(
|
|
123
|
+
url: str, *, api_key: str, file_field: str, file_path: Path, purpose: str = "fine-tune"
|
|
124
|
+
) -> requests.Response:
|
|
109
125
|
headers = {"Authorization": f"Bearer {api_key}"}
|
|
110
126
|
files = {file_field: (file_path.name, file_path.read_bytes(), "application/jsonl")}
|
|
111
127
|
data = {"purpose": purpose}
|
synth_ai/cli/__init__.py
CHANGED
|
@@ -69,6 +69,12 @@ try:
|
|
|
69
69
|
_demo.register(cli)
|
|
70
70
|
except Exception:
|
|
71
71
|
pass
|
|
72
|
+
try:
|
|
73
|
+
from . import turso as _turso
|
|
74
|
+
|
|
75
|
+
_turso.register(cli)
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
72
78
|
try:
|
|
73
79
|
from . import rl_demo as _rl_demo
|
|
74
80
|
|
|
@@ -83,18 +89,19 @@ except Exception:
|
|
|
83
89
|
pass
|
|
84
90
|
|
|
85
91
|
|
|
86
|
-
|
|
87
92
|
from .task_apps import task_app_group
|
|
93
|
+
|
|
88
94
|
cli.add_command(task_app_group, name="task-app")
|
|
89
95
|
|
|
90
96
|
|
|
91
97
|
try:
|
|
92
98
|
from . import task_apps as _task_apps
|
|
99
|
+
|
|
93
100
|
_task_apps.register(cli)
|
|
94
101
|
except Exception:
|
|
95
102
|
pass
|
|
96
103
|
|
|
97
|
-
cli.add_command(task_app_group.commands[
|
|
98
|
-
cli.add_command(task_app_group.commands[
|
|
104
|
+
cli.add_command(task_app_group.commands["serve"], name="serve")
|
|
105
|
+
cli.add_command(task_app_group.commands["deploy"], name="deploy")
|
|
99
106
|
|
|
100
|
-
cli.add_command(task_app_group.commands[
|
|
107
|
+
cli.add_command(task_app_group.commands["modal-serve"], name="modal-serve")
|
synth_ai/cli/balance.py
CHANGED
synth_ai/cli/demo.py
CHANGED
|
@@ -23,6 +23,7 @@ def _find_demo_scripts(root: Path) -> list[Path]:
|
|
|
23
23
|
|
|
24
24
|
def _forward_to_new(args: list[str]) -> None:
|
|
25
25
|
import sys
|
|
26
|
+
|
|
26
27
|
try:
|
|
27
28
|
from synth_ai.demos.core import cli as demo_cli # type: ignore
|
|
28
29
|
except Exception as e: # pragma: no cover
|
|
@@ -35,7 +36,9 @@ def _forward_to_new(args: list[str]) -> None:
|
|
|
35
36
|
|
|
36
37
|
def register(cli):
|
|
37
38
|
@cli.group("demo", invoke_without_command=True)
|
|
38
|
-
@click.option(
|
|
39
|
+
@click.option(
|
|
40
|
+
"--force", is_flag=True, help="Overwrite existing files in CWD when initializing demo"
|
|
41
|
+
)
|
|
39
42
|
@click.option("--list", "list_only", is_flag=True, help="List available legacy demos and exit")
|
|
40
43
|
@click.option("-f", "filter_term", default="", help="Filter legacy demos by substring")
|
|
41
44
|
@click.pass_context
|
|
@@ -99,13 +102,24 @@ def register(cli):
|
|
|
99
102
|
|
|
100
103
|
# Help pyright understand dynamic Click group attributes
|
|
101
104
|
from typing import Any, cast as _cast
|
|
105
|
+
|
|
102
106
|
_dg = _cast(Any, demo)
|
|
103
107
|
|
|
104
108
|
@_dg.command("deploy")
|
|
105
109
|
@click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
|
|
106
|
-
@click.option(
|
|
110
|
+
@click.option(
|
|
111
|
+
"--app",
|
|
112
|
+
type=click.Path(),
|
|
113
|
+
default=None,
|
|
114
|
+
help="Path to Modal app.py for uv run modal deploy",
|
|
115
|
+
)
|
|
107
116
|
@click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
|
|
108
|
-
@click.option(
|
|
117
|
+
@click.option(
|
|
118
|
+
"--script",
|
|
119
|
+
type=click.Path(),
|
|
120
|
+
default=None,
|
|
121
|
+
help="Path to deploy_task_app.sh (optional legacy)",
|
|
122
|
+
)
|
|
109
123
|
def demo_deploy(local: bool, app: str | None, name: str, script: str | None):
|
|
110
124
|
args: list[str] = ["rl_demo.deploy"]
|
|
111
125
|
if local:
|
|
@@ -120,11 +134,11 @@ def register(cli):
|
|
|
120
134
|
|
|
121
135
|
@_dg.command("configure")
|
|
122
136
|
def demo_configure():
|
|
123
|
-
_forward_to_new(["rl_demo.configure"])
|
|
137
|
+
_forward_to_new(["rl_demo.configure"])
|
|
124
138
|
|
|
125
139
|
@_dg.command("setup")
|
|
126
140
|
def demo_setup():
|
|
127
|
-
_forward_to_new(["rl_demo.setup"])
|
|
141
|
+
_forward_to_new(["rl_demo.setup"])
|
|
128
142
|
|
|
129
143
|
@_dg.command("run")
|
|
130
144
|
@click.option("--batch-size", type=int, default=None)
|
synth_ai/cli/rl_demo.py
CHANGED
|
@@ -20,6 +20,7 @@ import click
|
|
|
20
20
|
|
|
21
21
|
def _forward(args: list[str]) -> None:
|
|
22
22
|
import sys
|
|
23
|
+
|
|
23
24
|
try:
|
|
24
25
|
from synth_ai.demos.core import cli as demo_cli # type: ignore
|
|
25
26
|
except Exception as e: # pragma: no cover
|
|
@@ -37,6 +38,7 @@ def register(cli):
|
|
|
37
38
|
|
|
38
39
|
# Help pyright understand dynamic Click group attributes
|
|
39
40
|
from typing import Any, cast as _cast
|
|
41
|
+
|
|
40
42
|
_rlg = _cast(Any, rl_demo)
|
|
41
43
|
|
|
42
44
|
@_rlg.command("setup")
|
|
@@ -47,9 +49,19 @@ def register(cli):
|
|
|
47
49
|
|
|
48
50
|
@_rlg.command("deploy")
|
|
49
51
|
@click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
|
|
50
|
-
@click.option(
|
|
52
|
+
@click.option(
|
|
53
|
+
"--app",
|
|
54
|
+
type=click.Path(),
|
|
55
|
+
default=None,
|
|
56
|
+
help="Path to Modal app.py for uv run modal deploy",
|
|
57
|
+
)
|
|
51
58
|
@click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
|
|
52
|
-
@click.option(
|
|
59
|
+
@click.option(
|
|
60
|
+
"--script",
|
|
61
|
+
type=click.Path(),
|
|
62
|
+
default=None,
|
|
63
|
+
help="Path to deploy_task_app.sh (optional legacy)",
|
|
64
|
+
)
|
|
53
65
|
def rl_deploy(local: bool, app: str | None, name: str, script: str | None):
|
|
54
66
|
args: list[str] = ["rl_demo.deploy"]
|
|
55
67
|
if local:
|
|
@@ -64,7 +76,7 @@ def register(cli):
|
|
|
64
76
|
|
|
65
77
|
@_rlg.command("configure")
|
|
66
78
|
def rl_configure():
|
|
67
|
-
_forward(["rl_demo.configure"])
|
|
79
|
+
_forward(["rl_demo.configure"])
|
|
68
80
|
|
|
69
81
|
@_rlg.command("init")
|
|
70
82
|
@click.option("--template", type=str, default=None, help="Template id to instantiate")
|
|
@@ -81,13 +93,22 @@ def register(cli):
|
|
|
81
93
|
_forward(args)
|
|
82
94
|
|
|
83
95
|
@_rlg.command("run")
|
|
84
|
-
@click.option(
|
|
96
|
+
@click.option(
|
|
97
|
+
"--config", type=click.Path(), default=None, help="Path to TOML config (skip prompt)"
|
|
98
|
+
)
|
|
85
99
|
@click.option("--batch-size", type=int, default=None)
|
|
86
100
|
@click.option("--group-size", type=int, default=None)
|
|
87
101
|
@click.option("--model", type=str, default=None)
|
|
88
102
|
@click.option("--timeout", type=int, default=600)
|
|
89
103
|
@click.option("--dry-run", is_flag=True, help="Print request body and exit")
|
|
90
|
-
def rl_run(
|
|
104
|
+
def rl_run(
|
|
105
|
+
config: str | None,
|
|
106
|
+
batch_size: int | None,
|
|
107
|
+
group_size: int | None,
|
|
108
|
+
model: str | None,
|
|
109
|
+
timeout: int,
|
|
110
|
+
dry_run: bool,
|
|
111
|
+
):
|
|
91
112
|
args = ["rl_demo.run"]
|
|
92
113
|
if config:
|
|
93
114
|
args.extend(["--config", config])
|
|
@@ -106,19 +127,29 @@ def register(cli):
|
|
|
106
127
|
# Dotted aliases (top-level): legacy check → setup
|
|
107
128
|
@cli.command("rl_demo.check")
|
|
108
129
|
def rl_check_alias():
|
|
109
|
-
_forward(["rl_demo.setup"])
|
|
130
|
+
_forward(["rl_demo.setup"])
|
|
110
131
|
|
|
111
132
|
@cli.command("rl_demo.setup")
|
|
112
133
|
def rl_setup_alias():
|
|
113
|
-
_forward(["rl_demo.setup"])
|
|
134
|
+
_forward(["rl_demo.setup"])
|
|
114
135
|
|
|
115
136
|
# (prepare alias removed)
|
|
116
137
|
|
|
117
138
|
@cli.command("rl_demo.deploy")
|
|
118
139
|
@click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
|
|
119
|
-
@click.option(
|
|
140
|
+
@click.option(
|
|
141
|
+
"--app",
|
|
142
|
+
type=click.Path(),
|
|
143
|
+
default=None,
|
|
144
|
+
help="Path to Modal app.py for uv run modal deploy",
|
|
145
|
+
)
|
|
120
146
|
@click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
|
|
121
|
-
@click.option(
|
|
147
|
+
@click.option(
|
|
148
|
+
"--script",
|
|
149
|
+
type=click.Path(),
|
|
150
|
+
default=None,
|
|
151
|
+
help="Path to deploy_task_app.sh (optional legacy)",
|
|
152
|
+
)
|
|
122
153
|
def rl_deploy_alias(local: bool, app: str | None, name: str, script: str | None):
|
|
123
154
|
args: list[str] = ["rl_demo.deploy"]
|
|
124
155
|
if local:
|
|
@@ -133,7 +164,7 @@ def register(cli):
|
|
|
133
164
|
|
|
134
165
|
@cli.command("rl_demo.configure")
|
|
135
166
|
def rl_configure_alias():
|
|
136
|
-
_forward(["rl_demo.configure"])
|
|
167
|
+
_forward(["rl_demo.configure"])
|
|
137
168
|
|
|
138
169
|
@cli.command("rl_demo.init")
|
|
139
170
|
@click.option("--template", type=str, default=None, help="Template id to instantiate")
|
|
@@ -150,13 +181,22 @@ def register(cli):
|
|
|
150
181
|
_forward(args)
|
|
151
182
|
|
|
152
183
|
@cli.command("rl_demo.run")
|
|
153
|
-
@click.option(
|
|
184
|
+
@click.option(
|
|
185
|
+
"--config", type=click.Path(), default=None, help="Path to TOML config (skip prompt)"
|
|
186
|
+
)
|
|
154
187
|
@click.option("--batch-size", type=int, default=None)
|
|
155
188
|
@click.option("--group-size", type=int, default=None)
|
|
156
189
|
@click.option("--model", type=str, default=None)
|
|
157
190
|
@click.option("--timeout", type=int, default=600)
|
|
158
191
|
@click.option("--dry-run", is_flag=True, help="Print request body and exit")
|
|
159
|
-
def rl_run_alias(
|
|
192
|
+
def rl_run_alias(
|
|
193
|
+
config: str | None,
|
|
194
|
+
batch_size: int | None,
|
|
195
|
+
group_size: int | None,
|
|
196
|
+
model: str | None,
|
|
197
|
+
timeout: int,
|
|
198
|
+
dry_run: bool,
|
|
199
|
+
):
|
|
160
200
|
args = ["rl_demo.run"]
|
|
161
201
|
if config:
|
|
162
202
|
args.extend(["--config", config])
|
|
@@ -175,9 +215,19 @@ def register(cli):
|
|
|
175
215
|
# Top-level convenience alias: `synth-ai deploy`
|
|
176
216
|
@cli.command("demo-deploy")
|
|
177
217
|
@click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
|
|
178
|
-
@click.option(
|
|
218
|
+
@click.option(
|
|
219
|
+
"--app",
|
|
220
|
+
type=click.Path(),
|
|
221
|
+
default=None,
|
|
222
|
+
help="Path to Modal app.py for uv run modal deploy",
|
|
223
|
+
)
|
|
179
224
|
@click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
|
|
180
|
-
@click.option(
|
|
225
|
+
@click.option(
|
|
226
|
+
"--script",
|
|
227
|
+
type=click.Path(),
|
|
228
|
+
default=None,
|
|
229
|
+
help="Path to deploy_task_app.sh (optional legacy)",
|
|
230
|
+
)
|
|
181
231
|
def deploy_demo(local: bool, app: str | None, name: str, script: str | None):
|
|
182
232
|
args: list[str] = ["rl_demo.deploy"]
|
|
183
233
|
if local:
|
|
@@ -191,13 +241,22 @@ def register(cli):
|
|
|
191
241
|
_forward(args)
|
|
192
242
|
|
|
193
243
|
@cli.command("run")
|
|
194
|
-
@click.option(
|
|
244
|
+
@click.option(
|
|
245
|
+
"--config", type=click.Path(), default=None, help="Path to TOML config (skip prompt)"
|
|
246
|
+
)
|
|
195
247
|
@click.option("--batch-size", type=int, default=None)
|
|
196
248
|
@click.option("--group-size", type=int, default=None)
|
|
197
249
|
@click.option("--model", type=str, default=None)
|
|
198
250
|
@click.option("--timeout", type=int, default=600)
|
|
199
251
|
@click.option("--dry-run", is_flag=True, help="Print request body and exit")
|
|
200
|
-
def run_top(
|
|
252
|
+
def run_top(
|
|
253
|
+
config: str | None,
|
|
254
|
+
batch_size: int | None,
|
|
255
|
+
group_size: int | None,
|
|
256
|
+
model: str | None,
|
|
257
|
+
timeout: int,
|
|
258
|
+
dry_run: bool,
|
|
259
|
+
):
|
|
201
260
|
args = ["run"]
|
|
202
261
|
if config:
|
|
203
262
|
args.extend(["--config", config])
|
synth_ai/cli/root.py
CHANGED
|
@@ -9,13 +9,17 @@ import logging
|
|
|
9
9
|
import os
|
|
10
10
|
import shutil
|
|
11
11
|
import signal
|
|
12
|
+
import socket
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
15
|
+
import tempfile
|
|
14
16
|
import time
|
|
15
17
|
|
|
16
18
|
import click
|
|
19
|
+
|
|
17
20
|
try:
|
|
18
21
|
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
22
|
+
|
|
19
23
|
try:
|
|
20
24
|
__pkg_version__ = _pkg_version("synth-ai")
|
|
21
25
|
except PackageNotFoundError:
|
|
@@ -30,6 +34,9 @@ except Exception:
|
|
|
30
34
|
__pkg_version__ = "unknown"
|
|
31
35
|
|
|
32
36
|
|
|
37
|
+
SQLD_VERSION = "v0.26.2"
|
|
38
|
+
|
|
39
|
+
|
|
33
40
|
def find_sqld_binary() -> str | None:
|
|
34
41
|
sqld_path = shutil.which("sqld")
|
|
35
42
|
if sqld_path:
|
|
@@ -39,6 +46,7 @@ def find_sqld_binary() -> str | None:
|
|
|
39
46
|
"/usr/bin/sqld",
|
|
40
47
|
os.path.expanduser("~/.local/bin/sqld"),
|
|
41
48
|
os.path.expanduser("~/bin/sqld"),
|
|
49
|
+
os.path.expanduser("~/.turso/bin/sqld"),
|
|
42
50
|
]
|
|
43
51
|
for path in common_paths:
|
|
44
52
|
if os.path.exists(path) and os.access(path, os.X_OK):
|
|
@@ -47,40 +55,104 @@ def find_sqld_binary() -> str | None:
|
|
|
47
55
|
|
|
48
56
|
|
|
49
57
|
def install_sqld() -> str:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
58
|
+
"""Install sqld via the Turso CLI, installing the CLI via Homebrew if needed."""
|
|
59
|
+
|
|
60
|
+
click.echo("🔧 sqld not found. Attempting automatic install...")
|
|
61
|
+
|
|
62
|
+
turso_cli_path = shutil.which("turso")
|
|
63
|
+
brew_path = shutil.which("brew")
|
|
64
|
+
|
|
65
|
+
if not turso_cli_path:
|
|
66
|
+
if not brew_path:
|
|
67
|
+
raise click.ClickException(
|
|
68
|
+
"Automatic install requires either Homebrew or an existing Turso CLI.\n"
|
|
69
|
+
"Install manually using one of:\n"
|
|
70
|
+
" • brew install tursodatabase/tap/turso\n"
|
|
71
|
+
" • curl -sSfL https://get.tur.so/install.sh | bash\n"
|
|
72
|
+
"Then run 'turso dev' once and re-run this command."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
click.echo("🧰 Installing Turso CLI via Homebrew (tursodatabase/tap/turso)…")
|
|
76
|
+
try:
|
|
77
|
+
subprocess.run(
|
|
78
|
+
[brew_path, "install", "tursodatabase/tap/turso"],
|
|
79
|
+
check=True,
|
|
80
|
+
)
|
|
81
|
+
except subprocess.CalledProcessError as exc:
|
|
82
|
+
raise click.ClickException(
|
|
83
|
+
"Homebrew install failed. Please resolve brew errors and retry."
|
|
84
|
+
) from exc
|
|
85
|
+
|
|
86
|
+
turso_cli_path = shutil.which("turso")
|
|
87
|
+
if not turso_cli_path:
|
|
88
|
+
raise click.ClickException(
|
|
89
|
+
"Homebrew reported success but the 'turso' binary is not on PATH."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
click.echo("📥 Downloading sqld via 'turso dev' (this may take a few seconds)…")
|
|
93
|
+
|
|
94
|
+
temp_db = tempfile.NamedTemporaryFile(prefix="synth_sqld_", suffix=".db", delete=False)
|
|
95
|
+
temp_db_path = temp_db.name
|
|
96
|
+
temp_db.close()
|
|
97
|
+
|
|
98
|
+
env = os.environ.copy()
|
|
99
|
+
env.setdefault("TURSO_NONINTERACTIVE", "1")
|
|
100
|
+
|
|
101
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
102
|
+
sock.bind(("127.0.0.1", 0))
|
|
103
|
+
port = sock.getsockname()[1]
|
|
104
|
+
|
|
105
|
+
cmd = [
|
|
106
|
+
turso_cli_path,
|
|
107
|
+
"dev",
|
|
108
|
+
f"--db-file={temp_db_path}",
|
|
109
|
+
f"--port={port}",
|
|
110
|
+
]
|
|
111
|
+
proc: subprocess.Popen[str] | None = None
|
|
112
|
+
stdout_data = ""
|
|
113
|
+
stderr_data = ""
|
|
114
|
+
try:
|
|
115
|
+
proc = subprocess.Popen(
|
|
116
|
+
cmd,
|
|
117
|
+
stdout=subprocess.PIPE,
|
|
118
|
+
stderr=subprocess.PIPE,
|
|
119
|
+
text=True,
|
|
120
|
+
env=env,
|
|
121
|
+
)
|
|
122
|
+
try:
|
|
123
|
+
stdout_data, stderr_data = proc.communicate(timeout=10)
|
|
124
|
+
except subprocess.TimeoutExpired:
|
|
125
|
+
proc.terminate()
|
|
126
|
+
try:
|
|
127
|
+
stdout_data, stderr_data = proc.communicate(timeout=5)
|
|
128
|
+
except subprocess.TimeoutExpired:
|
|
129
|
+
proc.kill()
|
|
130
|
+
stdout_data, stderr_data = proc.communicate()
|
|
131
|
+
finally:
|
|
132
|
+
if proc and proc.returncode not in (0, None):
|
|
133
|
+
if stdout_data or stderr_data:
|
|
134
|
+
logging.getLogger(__name__).debug(
|
|
135
|
+
"turso dev stdout: %s\nstderr: %s", stdout_data, stderr_data
|
|
136
|
+
)
|
|
137
|
+
try:
|
|
138
|
+
os.unlink(temp_db_path)
|
|
139
|
+
except OSError:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
sqld_path = find_sqld_binary()
|
|
143
|
+
if sqld_path:
|
|
144
|
+
click.echo(f"✅ sqld available at {sqld_path}")
|
|
145
|
+
return sqld_path
|
|
146
|
+
|
|
147
|
+
raise click.ClickException(
|
|
148
|
+
"sqld download did not succeed. Run 'turso dev' manually once, "
|
|
149
|
+
"ensure it downloads sqld, and try again."
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@click.group(
|
|
154
|
+
help=f"Synth AI v{__pkg_version__} - Software for aiding the best and multiplying the will."
|
|
155
|
+
)
|
|
84
156
|
@click.version_option(version=__pkg_version__, prog_name="synth-ai")
|
|
85
157
|
def cli():
|
|
86
158
|
"""Top-level command group for Synth AI."""
|
|
@@ -109,9 +181,13 @@ def _forward_to_demo(args: list[str]) -> None:
|
|
|
109
181
|
|
|
110
182
|
@demo.command()
|
|
111
183
|
@click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
|
|
112
|
-
@click.option(
|
|
184
|
+
@click.option(
|
|
185
|
+
"--app", type=click.Path(), default=None, help="Path to Modal app.py for uv run modal deploy"
|
|
186
|
+
)
|
|
113
187
|
@click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
|
|
114
|
-
@click.option(
|
|
188
|
+
@click.option(
|
|
189
|
+
"--script", type=click.Path(), default=None, help="Path to deploy_task_app.sh (optional legacy)"
|
|
190
|
+
)
|
|
115
191
|
def deploy(local: bool, app: str | None, name: str, script: str | None):
|
|
116
192
|
"""Deploy the Math Task App (Modal by default)."""
|
|
117
193
|
args: list[str] = ["rl_demo.deploy"]
|
|
@@ -205,7 +281,10 @@ def serve_deprecated(
|
|
|
205
281
|
force: bool,
|
|
206
282
|
):
|
|
207
283
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
208
|
-
click.echo(
|
|
284
|
+
click.echo(
|
|
285
|
+
"⚠️ 'synth-ai serve' now targets task apps; use 'synth-ai serve' for task apps or 'synth-ai serve-deprecated' for this legacy service.",
|
|
286
|
+
err=True,
|
|
287
|
+
)
|
|
209
288
|
processes = []
|
|
210
289
|
|
|
211
290
|
def signal_handler(sig, frame):
|