synth-ai 0.2.8.dev5__py3-none-any.whl → 0.2.8.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.

@@ -763,7 +763,10 @@ def cmd_deploy(args: argparse.Namespace) -> int:
763
763
  raise FileNotFoundError(f"App file not found: {app_path}")
764
764
  # Surface the app path before asking for the name
765
765
  print(f"Using task app: {app_path}")
766
- suggested_name = args.name or f"synth-{os.path.splitext(os.path.basename(app_path))[0]}"
766
+ existing_name = (args.name or env.task_app_name or "").strip()
767
+ if not existing_name:
768
+ existing_name = f"synth-{os.path.splitext(os.path.basename(app_path))[0]}"
769
+ suggested_name = existing_name
767
770
  name_in = input(f"Modal app name [{suggested_name}]: ").strip() or suggested_name
768
771
  app_name = name_in
769
772
  print("\nAbout to deploy with:")
@@ -774,7 +777,11 @@ def cmd_deploy(args: argparse.Namespace) -> int:
774
777
  print("Aborted by user.")
775
778
  return 1
776
779
 
777
- secret_name = (env.task_app_secret_name or "").strip() or f"{name_in}-secret"
780
+ prev_secret = (env.task_app_secret_name or "").strip()
781
+ default_secret = f"{name_in}-secret"
782
+ secret_name = default_secret if not prev_secret else prev_secret
783
+ if prev_secret and prev_secret != default_secret:
784
+ secret_name = default_secret
778
785
  existing_env_key = (env.env_api_key or "").strip()
779
786
  env_key: str | None = existing_env_key or None
780
787
  if existing_env_key:
@@ -799,24 +806,24 @@ def cmd_deploy(args: argparse.Namespace) -> int:
799
806
  print("[deploy] Minted new ENVIRONMENT_API_KEY")
800
807
 
801
808
  # Optionally upload the new key to the backend using sealed box helper
802
- backend_base = env.dev_backend_url or ""
809
+ backend_base = (env.dev_backend_url or "").rstrip("/")
803
810
  synth_key = (env.synth_api_key or os.environ.get("SYNTH_API_KEY") or local_env.get("SYNTH_API_KEY") or "").strip()
804
811
  if backend_base and synth_key:
805
- backend_base = backend_base.rstrip("/")
806
- if not backend_base.endswith("/api"):
807
- backend_base = f"{backend_base}/api"
812
+ # Pass a base WITHOUT trailing /api to setup_environment_api_key,
813
+ # since it appends /api/v1/... internally.
814
+ non_api_base = backend_base[:-4] if backend_base.endswith("/api") else backend_base
808
815
  try:
809
816
  choice = input(
810
- f"Upload ENVIRONMENT_API_KEY to backend {backend_base}? [Y/n]: "
817
+ f"Upload ENVIRONMENT_API_KEY to backend {non_api_base}? [Y/n]: "
811
818
  ).strip().lower() or "y"
812
819
  except Exception:
813
820
  choice = "y"
814
821
  if choice.startswith("y"):
815
822
  try:
816
- print(f"[deploy] Uploading ENVIRONMENT_API_KEY to {backend_base} …")
823
+ print(f"[deploy] Uploading ENVIRONMENT_API_KEY to {non_api_base} …")
817
824
  from synth_ai.rl.env_keys import setup_environment_api_key
818
825
 
819
- setup_environment_api_key(backend_base.rstrip("/"), synth_key, token=env_key)
826
+ setup_environment_api_key(non_api_base, synth_key, token=env_key)
820
827
  print("[deploy] Backend sealed-box upload complete.")
821
828
  except Exception as upload_err:
822
829
  print(f"[deploy] Failed to upload ENVIRONMENT_API_KEY: {upload_err}")
@@ -917,7 +924,7 @@ def cmd_deploy(args: argparse.Namespace) -> int:
917
924
  print(f" export TASK_APP_NAME={app_name}")
918
925
  print(f" export TASK_APP_SECRET_NAME={app_name}-secret")
919
926
  print(f"Persisted to {dotenv_path}")
920
- print("Next: uvx synth-ai run")
927
+ print("\nNext step:\n$ uvx synth-ai run")
921
928
  return 0
922
929
  except Exception as e:
923
930
  print(f"Deploy error: {e}")
@@ -1070,11 +1077,7 @@ fi
1070
1077
  if os.path.exists(dst_cfg):
1071
1078
  print(f" - {dst_cfg} (seeded)")
1072
1079
  print("")
1073
- print("Next steps:")
1074
- print(" 1) cd synth_demo && put your ENVIRONMENT_API_KEY in ./.env")
1075
- print(" 2) Deploy to Modal:")
1076
- print(" uvx bash ./deploy_task_app.sh")
1077
- print(" 3) From project root, run: uvx synth-ai run")
1080
+ print("\nNext step:\n$ uvx synth-ai setup")
1078
1081
  return 0
1079
1082
  except Exception as e:
1080
1083
  print(f"Init error: {e}")
@@ -1363,7 +1366,7 @@ def main(argv: list[str] | None = None) -> int:
1363
1366
  def _deploy_opts(parser):
1364
1367
  parser.add_argument("--local", action="store_true", help="Run local FastAPI instead of Modal deploy")
1365
1368
  parser.add_argument("--app", type=str, default=None, help="Path to Modal app.py for uv run modal deploy")
1366
- parser.add_argument("--name", type=str, default="synth-math-demo", help="Modal app name")
1369
+ parser.add_argument("--name", type=str, default=None, help="Modal app name")
1367
1370
  parser.add_argument("--script", type=str, default=None, help="Path to deploy_task_app.sh (optional legacy)")
1368
1371
  parser.set_defaults(func=cmd_deploy)
1369
1372
 
@@ -43,7 +43,15 @@ if _SYNTH_HOSTED is not None:
43
43
  # No extra local dirs required; app is self-contained
44
44
 
45
45
  app = App("hendrycks-math-task-app")
46
- _SECRET_NAME = "synth-math-demo-secret"
46
+ _SECRET_NAME = (
47
+ os.getenv("TASK_APP_SECRET_NAME")
48
+ or os.getenv("MATH_TASK_APP_SECRET")
49
+ or os.getenv("TASK_APP_NAME", "").strip()
50
+ )
51
+ if not _SECRET_NAME:
52
+ _SECRET_NAME = "synth-math-demo-secret"
53
+ elif not _SECRET_NAME.endswith("-secret"):
54
+ _SECRET_NAME = f"{_SECRET_NAME}-secret"
47
55
 
48
56
 
49
57
  @app.function(
@@ -411,5 +419,3 @@ def fastapi_app():
411
419
  }
412
420
 
413
421
  return api
414
-
415
-
synth_ai/handshake.py CHANGED
@@ -1,10 +1,9 @@
1
1
  from __future__ import annotations
2
-
3
- import json
4
2
  import os
5
3
  import time
6
4
  import webbrowser
7
5
  from typing import Any, Dict, Tuple
6
+ from urllib.parse import urljoin, urlsplit, urlunsplit
8
7
 
9
8
  import requests
10
9
 
@@ -13,18 +12,58 @@ class HandshakeError(Exception):
13
12
  pass
14
13
 
15
14
 
15
+ _TRUTHY = {"1", "true", "yes", "on"}
16
+
17
+
16
18
  def _origin() -> str:
17
- # Prefer explicit env; fallback to localhost dashboard
18
- return (os.getenv("SYNTH_CANONICAL_ORIGIN", "") or "http://localhost:3000").rstrip("/")
19
+ """Resolve the dashboard origin for the browser handshake.
20
+
21
+ Priority order:
22
+ 1. Explicit ``SYNTH_CANONICAL_ORIGIN`` override.
23
+ 2. Development flag ``SYNTH_CANONICAL_DEV`` (case-insensitive truthy) → localhost.
24
+ 3. Production dashboard at ``https://www.usesynth.ai/dashboard``.
25
+ """
26
+
27
+ override = (os.getenv("SYNTH_CANONICAL_ORIGIN") or "").strip()
28
+ if override:
29
+ return override.rstrip("/")
30
+
31
+ dev_flag = (os.getenv("SYNTH_CANONICAL_DEV") or "").strip().lower()
32
+ if dev_flag in _TRUTHY:
33
+ print("USING DEV ORIGIN")
34
+ return "http://localhost:3000"
35
+
36
+ return "https://www.usesynth.ai/dashboard"
37
+
38
+
39
+ def _split_origin(origin: str) -> tuple[str, str]:
40
+ parsed = urlsplit(origin)
41
+ bare = urlunsplit((parsed.scheme, parsed.netloc, "", "", ""))
42
+ path = parsed.path.rstrip("/")
43
+ return bare, path
44
+
45
+
46
+ def _ensure_verification_uri(data: Dict[str, Any], base_with_path: str) -> None:
47
+ uri = data.get("verification_uri")
48
+ if not isinstance(uri, str) or not uri:
49
+ return
50
+ if uri.startswith("http://") or uri.startswith("https://"):
51
+ return
52
+ data["verification_uri"] = urljoin(base_with_path.rstrip("/") + "/", uri.lstrip("/"))
19
53
 
20
54
 
21
55
  def start_handshake_session(origin: str | None = None) -> Tuple[str, str, int, int]:
22
56
  base = (origin or _origin()).rstrip("/")
23
- url = f"{base}/api/sdk/handshake/init"
57
+ api_origin, _ = _split_origin(base)
58
+ url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/init")
24
59
  r = requests.post(url, timeout=10)
25
60
  if r.status_code != 200:
26
61
  raise HandshakeError(f"init failed: {r.status_code} {r.text}")
27
- data = r.json()
62
+ try:
63
+ data = r.json()
64
+ except ValueError as exc: # pragma: no cover - network dependent
65
+ raise HandshakeError(f"init returned malformed JSON: {exc}") from exc
66
+ _ensure_verification_uri(data, base)
28
67
  return (
29
68
  str(data.get("device_code")),
30
69
  str(data.get("verification_uri")),
@@ -35,7 +74,8 @@ def start_handshake_session(origin: str | None = None) -> Tuple[str, str, int, i
35
74
 
36
75
  def poll_handshake_token(device_code: str, origin: str | None = None, *, timeout_s: int | None = None) -> Dict[str, Any]:
37
76
  base = (origin or _origin()).rstrip("/")
38
- url = f"{base}/api/sdk/handshake/token"
77
+ api_origin, _ = _split_origin(base)
78
+ url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/token")
39
79
  deadline = time.time() + (timeout_s or 600)
40
80
  while True:
41
81
  if time.time() > deadline:
@@ -46,7 +86,12 @@ def poll_handshake_token(device_code: str, origin: str | None = None, *, timeout
46
86
  time.sleep(2)
47
87
  continue
48
88
  if r.status_code == 200:
49
- return r.json()
89
+ try:
90
+ data = r.json()
91
+ except ValueError as exc: # pragma: no cover - network dependent
92
+ raise HandshakeError(f"token returned malformed JSON: {exc}") from exc
93
+ _ensure_verification_uri(data, base)
94
+ return data
50
95
  elif r.status_code in (404, 410):
51
96
  raise HandshakeError(f"handshake failed: {r.status_code}")
52
97
  # 428 authorization_pending or others → wait and retry
@@ -60,4 +105,3 @@ def run_handshake(origin: str | None = None) -> Dict[str, Any]:
60
105
  except Exception:
61
106
  pass
62
107
  return poll_handshake_token(device_code, origin, timeout_s=expires_in)
63
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-ai
3
- Version: 0.2.8.dev5
3
+ Version: 0.2.8.dev7
4
4
  Summary: RL as a service SDK - Core AI functionality and tracing
5
5
  Author-email: Synth AI <josh@usesynth.ai>
6
6
  License-Expression: MIT
@@ -125,5 +125,6 @@ If your browser isn’t already signed in, sign in when prompted and the pairing
125
125
 
126
126
  Environment variables:
127
127
 
128
- - `SYNTH_CANONICAL_ORIGIN` (optional): override the dashboard base URL the SDK uses for the handshake (defaults to `http://localhost:3000`).
128
+ - `SYNTH_CANONICAL_ORIGIN` (optional): override the dashboard base URL the SDK uses for the handshake (defaults to `https://www.usesynth.ai/dashboard`).
129
+ - `SYNTH_CANONICAL_DEV` (optional): set to `1`, `true`, `yes`, or `on` to target the local dashboard at `http://localhost:3000`.
129
130
  - Keys are stored only in your project’s `.env` file, not exported to your shell.
@@ -1,6 +1,6 @@
1
1
  synth_ai/__init__.py,sha256=NixuXddy4lS2Wmj0F8eMt0HS_oYCTnq3iVVq5VYwWIc,1341
2
2
  synth_ai/__main__.py,sha256=Kh1xBKkTE5Vs2qNMtDuuOXerHUptMcOiF3YziOpC6DA,146
3
- synth_ai/handshake.py,sha256=AJR0GFFAkdAga8CXRLYbQl90-s0AQzTGxy5tQzPQb0E,1994
3
+ synth_ai/handshake.py,sha256=uzoTOpkf9JQgsyKWrlx8gjfQmK3HpqFQAZY1gZDtiIo,3735
4
4
  synth_ai/http.py,sha256=lqjFXDmAP_xgfywK_rDSOVxuMy4rDH9S3Rtu9k1tLmk,1028
5
5
  synth_ai/http_client.py,sha256=_9J8rUGoItUMnJLGZw7r0uXiJeLWR939kByRkvtP1XM,4429
6
6
  synth_ai/install_sqld.sh,sha256=AMBhlfq661PxeTTc6D4K_Nei_qwMvA84ei4NhQzmUUk,928
@@ -21,7 +21,7 @@ synth_ai/config/base_url.py,sha256=c85LaABBrvsl8Fp8KH0LNtJJrpnUwlzA5Ywbuth8fHE,3
21
21
  synth_ai/core/experiment.py,sha256=hLkPtzUFA7iY3-QpeJ5K8YjvQeyfqnjab5P2CFaojys,236
22
22
  synth_ai/core/system.py,sha256=s-Z7np2ISYmYc1r9YN-y2yb3cgRlOalrh0iaqnxeo84,206
23
23
  synth_ai/demos/core/__init__.py,sha256=A2FjhY7KXGtyzdQXqeTPCkEhHfrH-eQg6bvP8HaYhZM,36
24
- synth_ai/demos/core/cli.py,sha256=LMw8ExI_1lUQrX0QFJzhegStwLkxqooTjD6r1Nr7fwY,55976
24
+ synth_ai/demos/core/cli.py,sha256=lwkW1j638pOaLpmo6vPtDvEjsWdtLDCTwjbHoSHOdA8,56220
25
25
  synth_ai/demos/demo_task_apps/__init__.py,sha256=8aUGEGpWUw11GRb3wQXRAmQ99yjAt5qd5FCTDJpXWjI,44
26
26
  synth_ai/demos/demo_task_apps/core.py,sha256=ifKxxRKqC-y43MaqLHNuerXAlBHO8MI8ZBo2CzYcOoU,14563
27
27
  synth_ai/demos/demo_task_apps/math/__init__.py,sha256=WBzpZwSn7pRarBmhopQi34i9bEm05-71eM3siboOavY,43
@@ -30,7 +30,7 @@ synth_ai/demos/demo_task_apps/math/app.py,sha256=gNopoAhwM0vzdKuCa7AwQqSwiV2xagr
30
30
  synth_ai/demos/demo_task_apps/math/config.toml,sha256=Kxrzuyj7Az5mvzXaipPIyngKTDqphohf6uSWOHCF5cw,2105
31
31
  synth_ai/demos/demo_task_apps/math/deploy_modal.py,sha256=O4745sFuGEZTsygl-mz6ZOFJ7mog8CquXMgMyjFKr_c,2288
32
32
  synth_ai/demos/demo_task_apps/math/deploy_task_app.sh,sha256=qVffbAmsiCAxzFDzcxNVF4f7yyLWnmqPc1cNydHT5BQ,791
33
- synth_ai/demos/demo_task_apps/math/modal_task_app.py,sha256=OgTtoxlNc504ClAtLamsXL7nhmYptW0Djg4SeeeHcgs,18772
33
+ synth_ai/demos/demo_task_apps/math/modal_task_app.py,sha256=PE6LtRmz-56nbMT0tCInSr5IRUIpq-jVOrhZMTmMna4,19026
34
34
  synth_ai/environments/__init__.py,sha256=BQW0Nc_BFQq_N-pcqTyJVjW56kSEXu7XZyaSer-U95Q,1032
35
35
  synth_ai/environments/environment/__init__.py,sha256=EBol9AKxPTIPXWcbH9Tja-l3yL-N2kB8e5atyf6F66c,31
36
36
  synth_ai/environments/environment/core.py,sha256=0jd0CZ88_s_qqA3d1lOgVsnv-ucw_1lJDAIUj1gTSt0,2201
@@ -412,9 +412,9 @@ synth_ai/v0/tracing_v1/events/manage.py,sha256=ZDXXP-ZwLH9LCsmw7Ru9o55d7bl_diPtJ
412
412
  synth_ai/v0/tracing_v1/events/scope.py,sha256=BuBkhSpVHUJt8iGT9HJZF82rbb88mQcd2vM2shg-w2I,2550
413
413
  synth_ai/v0/tracing_v1/events/store.py,sha256=0342lvAcalyJbVEIzQFaPuMQGgwiFm7M5rE6gr-G0E8,9041
414
414
  synth_ai/zyk/__init__.py,sha256=htVLnzTYQ5rxzYpzSYBm7_o6uNKZ3pB_PrqkBrgTRS4,771
415
- synth_ai-0.2.8.dev5.dist-info/licenses/LICENSE,sha256=ynhjRQUfqA_RdGRATApfFA_fBAy9cno04sLtLUqxVFM,1069
416
- synth_ai-0.2.8.dev5.dist-info/METADATA,sha256=obtSTg2SzuMQvnJvR918ouGdIx48zgW7QIzjEKQkdGA,5011
417
- synth_ai-0.2.8.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
418
- synth_ai-0.2.8.dev5.dist-info/entry_points.txt,sha256=Neq-3bT7TAijjgOIR77pKL-WYg6TWBDeO8pp_nL4vGY,91
419
- synth_ai-0.2.8.dev5.dist-info/top_level.txt,sha256=fBmtZyVHuKaGa29oHBaaUkrUIWTqSpoVMPiVdCDP3k8,9
420
- synth_ai-0.2.8.dev5.dist-info/RECORD,,
415
+ synth_ai-0.2.8.dev7.dist-info/licenses/LICENSE,sha256=ynhjRQUfqA_RdGRATApfFA_fBAy9cno04sLtLUqxVFM,1069
416
+ synth_ai-0.2.8.dev7.dist-info/METADATA,sha256=NhVDsnnUZ2UYfMURfhfdl_ukMaeyB3Zo3xtuvjQlnms,5152
417
+ synth_ai-0.2.8.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
418
+ synth_ai-0.2.8.dev7.dist-info/entry_points.txt,sha256=Neq-3bT7TAijjgOIR77pKL-WYg6TWBDeO8pp_nL4vGY,91
419
+ synth_ai-0.2.8.dev7.dist-info/top_level.txt,sha256=fBmtZyVHuKaGa29oHBaaUkrUIWTqSpoVMPiVdCDP3k8,9
420
+ synth_ai-0.2.8.dev7.dist-info/RECORD,,