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.

Files changed (155) hide show
  1. examples/common_old/backend.py +0 -1
  2. examples/crafter_debug_render.py +15 -6
  3. examples/evals_old/compare_models.py +1 -0
  4. examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +6 -2
  5. examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +4 -4
  6. examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +4 -3
  7. examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +6 -2
  8. examples/finetuning_old/synth_qwen_v1/finetune.py +1 -1
  9. examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +4 -4
  10. examples/finetuning_old/synth_qwen_v1/infer.py +1 -2
  11. examples/finetuning_old/synth_qwen_v1/poll.py +4 -2
  12. examples/finetuning_old/synth_qwen_v1/prepare_data.py +8 -8
  13. examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +5 -4
  14. examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +11 -8
  15. examples/finetuning_old/synth_qwen_v1/run_ft_job.py +17 -12
  16. examples/finetuning_old/synth_qwen_v1/upload_data.py +1 -1
  17. examples/finetuning_old/synth_qwen_v1/util.py +7 -2
  18. examples/rl/configs/eval_base_qwen.toml +1 -1
  19. examples/rl/configs/rl_from_base_qwen17.toml +1 -1
  20. examples/rl/download_dataset.py +26 -10
  21. examples/rl/run_eval.py +17 -15
  22. examples/rl/run_rl_and_save.py +24 -7
  23. examples/rl/task_app/math_single_step.py +128 -11
  24. examples/rl/task_app/math_task_app.py +11 -3
  25. examples/rl_old/task_app.py +222 -53
  26. examples/warming_up_to_rl/analyze_trace_db.py +7 -5
  27. examples/warming_up_to_rl/export_trace_sft.py +141 -16
  28. examples/warming_up_to_rl/groq_test.py +11 -4
  29. examples/warming_up_to_rl/manage_secrets.py +15 -6
  30. examples/warming_up_to_rl/readme.md +9 -2
  31. examples/warming_up_to_rl/run_eval.py +108 -30
  32. examples/warming_up_to_rl/run_fft_and_save.py +128 -52
  33. examples/warming_up_to_rl/run_local_rollout.py +87 -36
  34. examples/warming_up_to_rl/run_local_rollout_modal.py +113 -25
  35. examples/warming_up_to_rl/run_local_rollout_parallel.py +80 -16
  36. examples/warming_up_to_rl/run_local_rollout_traced.py +125 -20
  37. examples/warming_up_to_rl/run_rl_and_save.py +31 -7
  38. examples/warming_up_to_rl/run_rollout_remote.py +37 -10
  39. examples/warming_up_to_rl/task_app/grpo_crafter.py +90 -27
  40. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +9 -27
  41. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +46 -108
  42. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -1
  43. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +1 -1
  44. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -1
  45. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +50 -17
  46. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +35 -21
  47. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +8 -4
  48. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +29 -26
  49. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +1 -1
  50. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +17 -13
  51. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +1 -1
  52. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +106 -63
  53. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +82 -84
  54. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +76 -59
  55. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +1 -1
  56. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +43 -49
  57. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +5 -15
  58. synth_ai/__init__.py +1 -0
  59. synth_ai/api/train/builders.py +34 -10
  60. synth_ai/api/train/cli.py +172 -32
  61. synth_ai/api/train/config_finder.py +59 -4
  62. synth_ai/api/train/env_resolver.py +32 -14
  63. synth_ai/api/train/pollers.py +11 -3
  64. synth_ai/api/train/task_app.py +4 -1
  65. synth_ai/api/train/utils.py +20 -4
  66. synth_ai/cli/__init__.py +11 -4
  67. synth_ai/cli/balance.py +1 -1
  68. synth_ai/cli/demo.py +19 -5
  69. synth_ai/cli/rl_demo.py +75 -16
  70. synth_ai/cli/root.py +116 -37
  71. synth_ai/cli/task_apps.py +1276 -186
  72. synth_ai/cli/traces.py +1 -0
  73. synth_ai/cli/turso.py +73 -0
  74. synth_ai/core/experiment.py +0 -2
  75. synth_ai/demo_registry.py +67 -30
  76. synth_ai/demos/core/cli.py +493 -164
  77. synth_ai/demos/demo_task_apps/core.py +50 -6
  78. synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +2 -3
  79. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +36 -28
  80. synth_ai/demos/demo_task_apps/math/_common.py +1 -2
  81. synth_ai/demos/demo_task_apps/math/deploy_modal.py +0 -2
  82. synth_ai/demos/demo_task_apps/math/modal_task_app.py +168 -65
  83. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
  84. synth_ai/environments/examples/bandit/engine.py +12 -4
  85. synth_ai/environments/examples/bandit/taskset.py +4 -4
  86. synth_ai/environments/reproducibility/tree.py +3 -1
  87. synth_ai/environments/service/core_routes.py +6 -2
  88. synth_ai/evals/base.py +0 -2
  89. synth_ai/experimental/synth_oss.py +11 -12
  90. synth_ai/handshake.py +3 -1
  91. synth_ai/http_client.py +31 -7
  92. synth_ai/inference/__init__.py +0 -2
  93. synth_ai/inference/client.py +8 -4
  94. synth_ai/jobs/client.py +40 -10
  95. synth_ai/learning/client.py +33 -8
  96. synth_ai/learning/config.py +0 -2
  97. synth_ai/learning/constants.py +0 -2
  98. synth_ai/learning/ft_client.py +6 -3
  99. synth_ai/learning/health.py +9 -2
  100. synth_ai/learning/jobs.py +17 -5
  101. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +1 -3
  102. synth_ai/learning/prompts/random_search.py +4 -1
  103. synth_ai/learning/prompts/run_random_search_banking77.py +6 -1
  104. synth_ai/learning/rl_client.py +42 -14
  105. synth_ai/learning/sse.py +0 -2
  106. synth_ai/learning/validators.py +6 -2
  107. synth_ai/lm/caching/ephemeral.py +1 -3
  108. synth_ai/lm/core/exceptions.py +0 -2
  109. synth_ai/lm/core/main.py +13 -1
  110. synth_ai/lm/core/synth_models.py +0 -1
  111. synth_ai/lm/core/vendor_clients.py +4 -2
  112. synth_ai/lm/overrides.py +2 -2
  113. synth_ai/lm/vendors/core/anthropic_api.py +7 -7
  114. synth_ai/lm/vendors/core/openai_api.py +2 -0
  115. synth_ai/lm/vendors/openai_standard.py +3 -1
  116. synth_ai/lm/vendors/openai_standard_responses.py +6 -3
  117. synth_ai/lm/vendors/supported/custom_endpoint.py +1 -3
  118. synth_ai/lm/vendors/synth_client.py +37 -10
  119. synth_ai/rl/__init__.py +0 -1
  120. synth_ai/rl/contracts.py +0 -2
  121. synth_ai/rl/env_keys.py +6 -1
  122. synth_ai/task/__init__.py +1 -0
  123. synth_ai/task/apps/__init__.py +11 -11
  124. synth_ai/task/auth.py +29 -17
  125. synth_ai/task/client.py +3 -1
  126. synth_ai/task/contracts.py +1 -0
  127. synth_ai/task/datasets.py +3 -1
  128. synth_ai/task/errors.py +3 -2
  129. synth_ai/task/health.py +0 -2
  130. synth_ai/task/json.py +0 -1
  131. synth_ai/task/proxy.py +2 -5
  132. synth_ai/task/rubrics.py +9 -3
  133. synth_ai/task/server.py +31 -5
  134. synth_ai/task/tracing_utils.py +8 -3
  135. synth_ai/task/validators.py +0 -1
  136. synth_ai/task/vendors.py +0 -1
  137. synth_ai/tracing_v3/db_config.py +26 -1
  138. synth_ai/tracing_v3/decorators.py +1 -0
  139. synth_ai/tracing_v3/examples/basic_usage.py +3 -2
  140. synth_ai/tracing_v3/hooks.py +2 -0
  141. synth_ai/tracing_v3/replica_sync.py +1 -0
  142. synth_ai/tracing_v3/session_tracer.py +24 -3
  143. synth_ai/tracing_v3/storage/base.py +4 -1
  144. synth_ai/tracing_v3/storage/factory.py +0 -1
  145. synth_ai/tracing_v3/turso/manager.py +102 -38
  146. synth_ai/tracing_v3/turso/models.py +4 -1
  147. synth_ai/tracing_v3/utils.py +1 -0
  148. synth_ai/v0/tracing/upload.py +32 -135
  149. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/METADATA +1 -1
  150. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/RECORD +154 -154
  151. synth_ai/install_sqld.sh +0 -40
  152. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/WHEEL +0 -0
  153. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/entry_points.txt +0 -0
  154. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/licenses/LICENSE +0 -0
  155. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev7.dist-info}/top_level.txt +0 -0
@@ -18,4 +18,3 @@ def resolve_backend_url() -> str:
18
18
 
19
19
  if __name__ == "__main__":
20
20
  print(resolve_backend_url())
21
-
@@ -10,6 +10,7 @@ Quick local Crafter observation inspector.
10
10
  Run:
11
11
  uv run python examples/crafter_debug_render.py --base-url http://localhost:8901 --seed 1
12
12
  """
13
+
13
14
  import argparse
14
15
  import math
15
16
  import os
@@ -117,13 +118,17 @@ def format_semantic_map_view(obs: Dict[str, Any], view_size: int = 7) -> str:
117
118
  row_cells.append("void")
118
119
  lines.append(" ".join(row_cells))
119
120
 
120
- legend = f"Visible items: {', '.join(sorted(visible))}" if visible else "No notable items visible"
121
+ legend = (
122
+ f"Visible items: {', '.join(sorted(visible))}" if visible else "No notable items visible"
123
+ )
121
124
  return "\n".join(lines) + "\n" + legend
122
125
 
123
126
 
124
127
  async def main():
125
128
  parser = argparse.ArgumentParser()
126
- parser.add_argument("--base-url", default=os.getenv("CRAFTER_BASE_URL", "http://localhost:8901"))
129
+ parser.add_argument(
130
+ "--base-url", default=os.getenv("CRAFTER_BASE_URL", "http://localhost:8901")
131
+ )
127
132
  parser.add_argument("--seed", type=int, default=1)
128
133
  args = parser.parse_args()
129
134
 
@@ -145,7 +150,11 @@ async def main():
145
150
  print(f"Health: {obs.get('health', 10)}/10")
146
151
  print(f"Hunger: {obs.get('food', 10)}/10")
147
152
  print(f"Energy: {obs.get('energy', 10)}/10")
148
- inv_items = ", ".join([f"{k}: {v}" for k, v in inv.items() if v]) if isinstance(inv, dict) else str(inv)
153
+ inv_items = (
154
+ ", ".join([f"{k}: {v}" for k, v in inv.items() if v])
155
+ if isinstance(inv, dict)
156
+ else str(inv)
157
+ )
149
158
  print(f"Inventory: {inv_items if inv_items else 'empty'}")
150
159
  if isinstance(ach, dict):
151
160
  unlocked = sum(1 for v in ach.values() if v)
@@ -167,7 +176,9 @@ async def main():
167
176
 
168
177
  # Cleanup
169
178
  try:
170
- await client.post(f"{args.base_url}/env/CrafterClassic/terminate", json={"env_id": env_id})
179
+ await client.post(
180
+ f"{args.base_url}/env/CrafterClassic/terminate", json={"env_id": env_id}
181
+ )
171
182
  except Exception:
172
183
  pass
173
184
 
@@ -176,5 +187,3 @@ if __name__ == "__main__":
176
187
  import asyncio
177
188
 
178
189
  asyncio.run(main())
179
-
180
-
@@ -19,6 +19,7 @@ Analyzes and compares:
19
19
  - Performance metrics
20
20
  - Cost analysis
21
21
  """
22
+
22
23
  import os
23
24
  import sys
24
25
  from pathlib import Path
@@ -34,7 +34,8 @@ except Exception: # pragma: no cover
34
34
  )
35
35
  except Exception as _import_err: # pragma: no cover
36
36
  raise ImportError(
37
- "Could not import FinetuningDataExtractorV3 from synth_ai.") from _import_err
37
+ "Could not import FinetuningDataExtractorV3 from synth_ai."
38
+ ) from _import_err
38
39
 
39
40
 
40
41
  def env_list(name: str) -> list[str]:
@@ -133,6 +134,7 @@ async def main() -> None:
133
134
  try:
134
135
  import numpy as _np
135
136
  from collections import Counter as _Counter
137
+
136
138
  async with FinetuningDataExtractorV3(db_url) as _ex:
137
139
  _sessions = await _ex.get_all_sessions()
138
140
  _ach_counts: _Counter[str] = _Counter()
@@ -155,7 +157,9 @@ async def main() -> None:
155
157
  if _rewards:
156
158
  _r = _np.array(_rewards, dtype=float)
157
159
  print("\nReward stats:")
158
- print(f" min={_r.min():.2f} median={_np.median(_r):.2f} mean={_r.mean():.2f} max={_r.max():.2f}")
160
+ print(
161
+ f" min={_r.min():.2f} median={_np.median(_r):.2f} mean={_r.mean():.2f} max={_r.max():.2f}"
162
+ )
159
163
  except Exception:
160
164
  pass
161
165
 
@@ -5,6 +5,7 @@ This demonstrates using the LM class with Synth models through native integratio
5
5
 
6
6
  This version uses the new tracing_v3 system with async Turso/SQLite backend.
7
7
  """
8
+
8
9
  import argparse
9
10
  import asyncio
10
11
  import contextlib
@@ -100,13 +101,13 @@ HTTP_TIMEOUT = (
100
101
  MAX_RETRIES = 3
101
102
  RETRY_DELAY = 1.0
102
103
 
104
+
103
105
  # Use the backend
104
106
  @asynccontextmanager
105
107
  async def _noop_async_context():
106
108
  yield
107
109
 
108
110
 
109
-
110
111
  async def create_experiment_context(
111
112
  db_manager: AsyncSQLTraceManager, experiment_name: str, description: str
112
113
  ) -> dict[str, Any]:
@@ -1266,9 +1267,8 @@ async def run_episode(
1266
1267
  info = step_data.get("info", {})
1267
1268
 
1268
1269
  # Calculate achievement reward if not provided by service
1269
- if (
1270
- (reward == 0 or reward is None)
1271
- and ("achievements_status" in obs and "achievements_status" in prev_obs)
1270
+ if (reward == 0 or reward is None) and (
1271
+ "achievements_status" in obs and "achievements_status" in prev_obs
1272
1272
  ):
1273
1273
  prev_achievements = prev_obs["achievements_status"]
1274
1274
  curr_achievements = obs["achievements_status"]
@@ -67,9 +67,10 @@ async def create_job(file_id: str) -> str:
67
67
  "upload_to_wasabi": bool(scfg.get("upload_to_wasabi", True)),
68
68
  }
69
69
  headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
70
- async with aiohttp.ClientSession() as session, session.post(
71
- f"{API_URL}/fine_tuning/jobs", json=body, headers=headers
72
- ) as resp:
70
+ async with (
71
+ aiohttp.ClientSession() as session,
72
+ session.post(f"{API_URL}/fine_tuning/jobs", json=body, headers=headers) as resp,
73
+ ):
73
74
  assert resp.status == 200, await resp.text()
74
75
  data = await resp.json()
75
76
  return data["id"]
@@ -34,7 +34,8 @@ except Exception: # pragma: no cover
34
34
  )
35
35
  except Exception as _import_err: # pragma: no cover
36
36
  raise ImportError(
37
- "Could not import FinetuningDataExtractorV3 from synth_ai.") from _import_err
37
+ "Could not import FinetuningDataExtractorV3 from synth_ai."
38
+ ) from _import_err
38
39
 
39
40
 
40
41
  def env_list(name: str) -> list[str]:
@@ -133,6 +134,7 @@ async def main() -> None:
133
134
  try:
134
135
  import numpy as _np
135
136
  from collections import Counter as _Counter
137
+
136
138
  async with FinetuningDataExtractorV3(db_url) as _ex:
137
139
  _sessions = await _ex.get_all_sessions()
138
140
  _ach_counts: _Counter[str] = _Counter()
@@ -155,7 +157,9 @@ async def main() -> None:
155
157
  if _rewards:
156
158
  _r = _np.array(_rewards, dtype=float)
157
159
  print("\nReward stats:")
158
- print(f" min={_r.min():.2f} median={_np.median(_r):.2f} mean={_r.mean():.2f} max={_r.max():.2f}")
160
+ print(
161
+ f" min={_r.min():.2f} median={_np.median(_r):.2f} mean={_r.mean():.2f} max={_r.max():.2f}"
162
+ )
159
163
  except Exception:
160
164
  pass
161
165
 
@@ -4,6 +4,7 @@ import asyncio
4
4
 
5
5
  import sys
6
6
  import os
7
+
7
8
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
8
9
 
9
10
  from synth_ai.learning import FtClient
@@ -43,4 +44,3 @@ def main() -> None:
43
44
 
44
45
  if __name__ == "__main__":
45
46
  main()
46
-
@@ -44,6 +44,7 @@ async def main() -> None:
44
44
  print("===== End Response =====\n")
45
45
  except Exception as e: # always print full failure context
46
46
  import traceback
47
+
47
48
  print("\n===== Inference Error =====")
48
49
  print(f"Type: {type(e).__name__}")
49
50
  print(f"Repr: {repr(e)}")
@@ -51,14 +52,15 @@ async def main() -> None:
51
52
  print(traceback.format_exc())
52
53
  try:
53
54
  from synth_ai.http import HTTPError # type: ignore
55
+
54
56
  if isinstance(e, HTTPError):
55
57
  print("HTTPError details:")
56
58
  print(f" status={e.status}")
57
59
  print(f" url={e.url}")
58
60
  print(f" message={e.message}")
59
- if getattr(e, 'detail', None) is not None:
61
+ if getattr(e, "detail", None) is not None:
60
62
  print(f" detail={e.detail}")
61
- if getattr(e, 'body_snippet', None):
63
+ if getattr(e, "body_snippet", None):
62
64
  print(f" body_snippet={e.body_snippet}")
63
65
  except Exception:
64
66
  pass
@@ -67,5 +69,3 @@ async def main() -> None:
67
69
 
68
70
  if __name__ == "__main__":
69
71
  asyncio.run(main())
70
-
71
-
@@ -4,6 +4,7 @@ import asyncio
4
4
 
5
5
  import sys
6
6
  import os
7
+
7
8
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
8
9
 
9
10
  from synth_ai.inference import InferenceClient
@@ -33,5 +34,3 @@ def main() -> None:
33
34
 
34
35
  if __name__ == "__main__":
35
36
  main()
36
-
37
-
@@ -5,6 +5,7 @@ from typing import Dict
5
5
 
6
6
  import sys
7
7
  import os
8
+
8
9
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
9
10
 
10
11
  from synth_ai.learning import JobHandle
@@ -27,7 +28,9 @@ async def _run(mode: str | None) -> None:
27
28
 
28
29
  # Use shared JobHandle poller abstraction (strict=True for FT)
29
30
  handle = JobHandle(base, key, job_id, strict=True)
30
- final = await handle.poll_until_terminal(interval_seconds=2.0, max_seconds=1800, on_event=_print_event)
31
+ final = await handle.poll_until_terminal(
32
+ interval_seconds=2.0, max_seconds=1800, on_event=_print_event
33
+ )
31
34
  print(f"final_status={final.get('status')}")
32
35
  ft = final.get("fine_tuned_model")
33
36
  if ft:
@@ -41,4 +44,3 @@ def main() -> None:
41
44
 
42
45
  if __name__ == "__main__":
43
46
  main()
44
-
@@ -17,12 +17,14 @@ def main() -> None:
17
17
 
18
18
  # Minimal single-example JSONL
19
19
  lines = [
20
- json.dumps({
21
- "messages": [
22
- {"role": "user", "content": "Write a short greeting."},
23
- {"role": "assistant", "content": "Hello there!"},
24
- ]
25
- })
20
+ json.dumps(
21
+ {
22
+ "messages": [
23
+ {"role": "user", "content": "Write a short greeting."},
24
+ {"role": "assistant", "content": "Hello there!"},
25
+ ]
26
+ }
27
+ )
26
28
  ]
27
29
  out_path.write_text("\n".join(lines) + "\n")
28
30
  validate_jsonl(out_path)
@@ -31,5 +33,3 @@ def main() -> None:
31
33
 
32
34
  if __name__ == "__main__":
33
35
  main()
34
-
35
-
@@ -5,6 +5,7 @@ This demonstrates using the LM class with Synth models through native integratio
5
5
 
6
6
  This version uses the new tracing_v3 system with async Turso/SQLite backend.
7
7
  """
8
+
8
9
  import argparse
9
10
  import asyncio
10
11
  import contextlib
@@ -38,6 +39,7 @@ def _resolve_backend_default() -> str:
38
39
  base = base.rstrip("/")
39
40
  return base if base.endswith("/api") else f"{base}/api"
40
41
 
42
+
41
43
  # Disable httpx logging immediately
42
44
  logging.getLogger("httpx").setLevel(logging.ERROR)
43
45
  logging.getLogger("httpcore").setLevel(logging.ERROR)
@@ -108,13 +110,13 @@ HTTP_TIMEOUT = (
108
110
  MAX_RETRIES = 3
109
111
  RETRY_DELAY = 1.0
110
112
 
113
+
111
114
  # Use the backend
112
115
  @asynccontextmanager
113
116
  async def _noop_async_context():
114
117
  yield
115
118
 
116
119
 
117
-
118
120
  async def create_experiment_context(
119
121
  db_manager: AsyncSQLTraceManager, experiment_name: str, description: str
120
122
  ) -> dict[str, Any]:
@@ -1274,9 +1276,8 @@ async def run_episode(
1274
1276
  info = step_data.get("info", {})
1275
1277
 
1276
1278
  # Calculate achievement reward if not provided by service
1277
- if (
1278
- (reward == 0 or reward is None)
1279
- and ("achievements_status" in obs and "achievements_status" in prev_obs)
1279
+ if (reward == 0 or reward is None) and (
1280
+ "achievements_status" in obs and "achievements_status" in prev_obs
1280
1281
  ):
1281
1282
  prev_achievements = prev_obs["achievements_status"]
1282
1283
  curr_achievements = obs["achievements_status"]
@@ -37,13 +37,12 @@ def parse_args() -> argparse.Namespace:
37
37
  p.add_argument("--mode", choices=["local", "dev", "prod"], default=None)
38
38
  p.add_argument(
39
39
  "--db",
40
- default=str(
41
- Path(__file__).resolve().parents[3]
42
- / "traces/v3/synth_ai.db/dbs/default/data"
43
- ),
40
+ default=str(Path(__file__).resolve().parents[3] / "traces/v3/synth_ai.db/dbs/default/data"),
44
41
  help="Path to sqld internal data file or sqlite+aiosqlite URL",
45
42
  )
46
- p.add_argument("--output", default=str(Path(__file__).parent / "data" / "training_crafter.jsonl"))
43
+ p.add_argument(
44
+ "--output", default=str(Path(__file__).parent / "data" / "training_crafter.jsonl")
45
+ )
47
46
  p.add_argument("--min-achievements", type=int, default=2)
48
47
  p.add_argument("--max-cost", type=float, default=10.0)
49
48
  p.add_argument("--max-tokens", type=int, default=100000)
@@ -72,12 +71,14 @@ async def extract_jsonl_from_traces(db_url: str, output_path: str, cfg: dict[str
72
71
  from synth_ai.environments.examples.crafter_classic.agent_demos.crafter_modal_ft.filter_traces_sft_turso import ( # type: ignore
73
72
  FinetuningDataExtractorV3 as _Ex,
74
73
  )
74
+
75
75
  Extractor = _Ex
76
76
  except Exception:
77
77
  try:
78
78
  from synth_ai.environments.examples.crafter_classic.agent_demos.crafter_openai_ft.filter_traces_sft_turso import ( # type: ignore
79
79
  FinetuningDataExtractorV3 as _Ex,
80
80
  )
81
+
81
82
  Extractor = _Ex
82
83
  except Exception as e:
83
84
  raise ImportError("FinetuningDataExtractorV3 not available in current build") from e
@@ -110,7 +111,11 @@ async def extract_jsonl_from_traces(db_url: str, output_path: str, cfg: dict[str
110
111
  """,
111
112
  {"session_id": sid},
112
113
  )
113
- session_models = model_df["model_name"].tolist() if model_df is not None and not model_df.empty else []
114
+ session_models = (
115
+ model_df["model_name"].tolist()
116
+ if model_df is not None and not model_df.empty
117
+ else []
118
+ )
114
119
  if not any(m in session_models for m in models):
115
120
  continue
116
121
  ach = await ex.get_session_achievements(sid) or []
@@ -203,5 +208,3 @@ def main() -> None:
203
208
 
204
209
  if __name__ == "__main__":
205
210
  main()
206
-
207
-
@@ -65,12 +65,14 @@ def ensure_training_jsonl(path: Path) -> Path:
65
65
  if not path.exists():
66
66
  # Minimal JSONL with a single example
67
67
  lines: list[str] = [
68
- json.dumps({
69
- "messages": [
70
- {"role": "user", "content": "Write a short greeting."},
71
- {"role": "assistant", "content": "Hello there!"},
72
- ]
73
- })
68
+ json.dumps(
69
+ {
70
+ "messages": [
71
+ {"role": "user", "content": "Write a short greeting."},
72
+ {"role": "assistant", "content": "Hello there!"},
73
+ ]
74
+ }
75
+ )
74
76
  ]
75
77
  path.write_text("\n".join(lines) + "\n")
76
78
  # Validate using shared SDK validator
@@ -83,7 +85,9 @@ async def run(args: argparse.Namespace) -> None:
83
85
  base_url, api_key = load_env(args.mode)
84
86
  # Force canonical prod base when prod mode (or override) is selected
85
87
  try:
86
- if (args.mode == "prod") or (os.getenv("SYNTH_BACKEND_URL_OVERRIDE", "").strip().lower() == "prod"):
88
+ if (args.mode == "prod") or (
89
+ os.getenv("SYNTH_BACKEND_URL_OVERRIDE", "").strip().lower() == "prod"
90
+ ):
87
91
  base_url = _resolve_backend_default()
88
92
  # Also export for any downstream helpers that read env
89
93
  os.environ["PROD_BACKEND_URL"] = base_url
@@ -123,8 +127,8 @@ async def run(args: argparse.Namespace) -> None:
123
127
  "gpu_type": "A10G",
124
128
  "container_count": 1,
125
129
  }
126
- }
127
- }
130
+ },
131
+ },
128
132
  }
129
133
 
130
134
  create_resp = await ft.create_sft_job(
@@ -194,6 +198,7 @@ async def run(args: argparse.Namespace) -> None:
194
198
  except Exception as e:
195
199
  # Always print full error details and traceback
196
200
  import traceback
201
+
197
202
  try:
198
203
  from synth_ai.http import HTTPError # type: ignore
199
204
  except Exception: # pragma: no cover - fallback if import shape changes
@@ -206,7 +211,7 @@ async def run(args: argparse.Namespace) -> None:
206
211
  print("Traceback:")
207
212
  print(tb)
208
213
  # If HTTP error from backend, surface structured fields
209
- if 'HTTPError' in str(type(e)) or (isinstance((), tuple) and False):
214
+ if "HTTPError" in str(type(e)) or (isinstance((), tuple) and False):
210
215
  pass
211
216
  try:
212
217
  if HTTPError and isinstance(e, HTTPError): # type: ignore[arg-type]
@@ -214,9 +219,9 @@ async def run(args: argparse.Namespace) -> None:
214
219
  print(f" status={e.status}")
215
220
  print(f" url={e.url}")
216
221
  print(f" message={e.message}")
217
- if getattr(e, 'detail', None) is not None:
222
+ if getattr(e, "detail", None) is not None:
218
223
  print(f" detail={e.detail}")
219
- if getattr(e, 'body_snippet', None):
224
+ if getattr(e, "body_snippet", None):
220
225
  print(f" body_snippet={e.body_snippet}")
221
226
  except Exception:
222
227
  pass
@@ -6,6 +6,7 @@ import asyncio
6
6
 
7
7
  import sys
8
8
  import os
9
+
9
10
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
10
11
 
11
12
  from synth_ai.learning import FtClient, validate_training_jsonl
@@ -31,4 +32,3 @@ def main() -> None:
31
32
 
32
33
  if __name__ == "__main__":
33
34
  main()
34
-
@@ -11,6 +11,7 @@ from synth_ai.config.base_url import get_backend_from_env
11
11
  try:
12
12
  from dotenv import load_dotenv # type: ignore[reportMissingImports]
13
13
  except Exception: # pragma: no cover
14
+
14
15
  def load_dotenv(*args, **kwargs): # type: ignore[no-redef]
15
16
  return False
16
17
 
@@ -71,7 +72,9 @@ def load_env(mode: str | None = None) -> tuple[str, str]:
71
72
  or os.getenv("TESTING_LOCAL_SYNTH_API_KEY", "").strip()
72
73
  )
73
74
  if not base_url or not api_key:
74
- raise RuntimeError("Missing LOCAL_BACKEND_URL or DEV_SYNTH_API_KEY/TESTING_LOCAL_SYNTH_API_KEY in environment/.env")
75
+ raise RuntimeError(
76
+ "Missing LOCAL_BACKEND_URL or DEV_SYNTH_API_KEY/TESTING_LOCAL_SYNTH_API_KEY in environment/.env"
77
+ )
75
78
  elif mode == "dev":
76
79
  base_url = os.getenv("DEV_BACKEND_URL", "").strip()
77
80
  api_key = os.getenv("DEV_SYNTH_API_KEY", "").strip()
@@ -85,7 +88,9 @@ def load_env(mode: str | None = None) -> tuple[str, str]:
85
88
  or os.getenv("SYNTH_API_KEY", "").strip()
86
89
  )
87
90
  if not api_key:
88
- raise RuntimeError("Missing PROD_SYNTH_API_KEY/TESTING_PROD_SYNTH_API_KEY/SYNTH_API_KEY in environment/.env")
91
+ raise RuntimeError(
92
+ "Missing PROD_SYNTH_API_KEY/TESTING_PROD_SYNTH_API_KEY/SYNTH_API_KEY in environment/.env"
93
+ )
89
94
  base_url = base_url.rstrip("/")
90
95
  print(f"SYNTH backend: {base_url} (mode={mode})")
91
96
  # Also print masked API key and source
@@ -6,7 +6,7 @@ num_episodes = 50
6
6
  seed_start = 0
7
7
 
8
8
  [policy]
9
- inference_url = "http://localhost:8000/api/inference"
9
+ inference_url = "https://agent-learning.onrender.com/api/inference"
10
10
  max_tokens = 128
11
11
  temperature = 0.0
12
12
 
@@ -11,7 +11,7 @@ base = "Qwen/Qwen3-1.7B"
11
11
 
12
12
  [policy]
13
13
  model = "Qwen/Qwen3-1.7B"
14
- inference_url = "http://localhost:8000/api/inference"
14
+ inference_url = "https://agent-learning.onrender.com/api/inference"
15
15
  max_tokens = 1028
16
16
  temperature = 0.2
17
17
 
@@ -20,10 +20,12 @@ def extract_examples(dataset: Any, *, limit: int | None) -> list[dict[str, str]]
20
20
  solution = item.get("solution") or ""
21
21
  if isinstance(solution, list):
22
22
  solution = "\n".join(str(part) for part in solution)
23
- examples.append({
24
- "problem": problem,
25
- "solution": solution,
26
- })
23
+ examples.append(
24
+ {
25
+ "problem": problem,
26
+ "solution": solution,
27
+ }
28
+ )
27
29
  return examples
28
30
 
29
31
 
@@ -35,12 +37,26 @@ def write_jsonl(path: Path, rows: list[dict[str, str]]) -> None:
35
37
 
36
38
 
37
39
  def main() -> None:
38
- parser = argparse.ArgumentParser(description="Download MATH dataset splits to JSONL for offline use")
39
- parser.add_argument("--output-dir", default="examples/rl/data", help="Directory to write <split>.jsonl files")
40
- parser.add_argument("--dataset", default="nlile/hendrycks-MATH-benchmark", help="Hugging Face dataset identifier")
41
- parser.add_argument("--config", default="algebra", help="Hugging Face dataset config (if required)")
42
- parser.add_argument("--splits", nargs="*", default=["train", "validation", "test"], help="Splits to download")
43
- parser.add_argument("--limit", type=int, default=None, help="Optional cap on examples per split")
40
+ parser = argparse.ArgumentParser(
41
+ description="Download MATH dataset splits to JSONL for offline use"
42
+ )
43
+ parser.add_argument(
44
+ "--output-dir", default="examples/rl/data", help="Directory to write <split>.jsonl files"
45
+ )
46
+ parser.add_argument(
47
+ "--dataset",
48
+ default="nlile/hendrycks-MATH-benchmark",
49
+ help="Hugging Face dataset identifier",
50
+ )
51
+ parser.add_argument(
52
+ "--config", default="algebra", help="Hugging Face dataset config (if required)"
53
+ )
54
+ parser.add_argument(
55
+ "--splits", nargs="*", default=["train", "validation", "test"], help="Splits to download"
56
+ )
57
+ parser.add_argument(
58
+ "--limit", type=int, default=None, help="Optional cap on examples per split"
59
+ )
44
60
  args = parser.parse_args()
45
61
 
46
62
  output_dir = Path(args.output_dir).expanduser()
examples/rl/run_eval.py CHANGED
@@ -25,7 +25,10 @@ class TaskAppClient:
25
25
  async def __aenter__(self) -> "TaskAppClient":
26
26
  headers = {"X-API-Key": self.api_key} if self.api_key else {}
27
27
  self._client = httpx.AsyncClient(
28
- base_url=self.base_url, headers=headers, timeout=httpx.Timeout(120.0), follow_redirects=True
28
+ base_url=self.base_url,
29
+ headers=headers,
30
+ timeout=httpx.Timeout(120.0),
31
+ follow_redirects=True,
29
32
  )
30
33
  return self
31
34
 
@@ -39,7 +42,10 @@ class TaskAppClient:
39
42
  if self._client is None:
40
43
  headers = {"X-API-Key": self.api_key} if self.api_key else {}
41
44
  self._client = httpx.AsyncClient(
42
- base_url=self.base_url, headers=headers, timeout=httpx.Timeout(120.0), follow_redirects=True
45
+ base_url=self.base_url,
46
+ headers=headers,
47
+ timeout=httpx.Timeout(120.0),
48
+ follow_redirects=True,
43
49
  )
44
50
  return self._client
45
51
 
@@ -103,8 +109,7 @@ def _math_tool_schema() -> List[Dict[str, Any]]:
103
109
  "answer": {
104
110
  "type": "string",
105
111
  "description": "Final answer in simplest form",
106
- }
107
- ,
112
+ },
108
113
  "explanation": {
109
114
  "type": "string",
110
115
  "description": "Optional explanation of reasoning",
@@ -203,9 +208,7 @@ async def _choose_actions(
203
208
 
204
209
  if provider == "groq":
205
210
  # Task app proxies Groq requests; reuse existing headers on the client
206
- response = await client.client.post(
207
- "/proxy/groq/v1/chat/completions", json=payload
208
- )
211
+ response = await client.client.post("/proxy/groq/v1/chat/completions", json=payload)
209
212
  response.raise_for_status()
210
213
  body = response.json()
211
214
  else:
@@ -223,18 +226,15 @@ async def _choose_actions(
223
226
  headers=headers or None,
224
227
  )
225
228
  except httpx.ReadTimeout as exc:
226
- raise RuntimeError(
227
- "Inference request timed out. Check the inference service." ) from exc
229
+ raise RuntimeError("Inference request timed out. Check the inference service.") from exc
228
230
  try:
229
231
  body = response.json()
230
232
  except Exception:
231
233
  body = {"raw": response.text[:800]}
232
234
  if response.status_code >= 500:
233
- raise RuntimeError(
234
- f"Inference server error {response.status_code}: {body}")
235
+ raise RuntimeError(f"Inference server error {response.status_code}: {body}")
235
236
  if response.status_code >= 400:
236
- raise RuntimeError(
237
- f"Inference request invalid ({response.status_code}): {body}")
237
+ raise RuntimeError(f"Inference request invalid ({response.status_code}): {body}")
238
238
  tool_calls = _parse_tool_calls(body)
239
239
  return tool_calls, body
240
240
 
@@ -371,7 +371,9 @@ async def main() -> None:
371
371
  seed_start = int(cfg.get("seed_start") or 0)
372
372
 
373
373
  policy_cfg = _default_policy_cfg(cfg)
374
- provider_hint = cfg.get("provider") or cfg.get("policy", {}).get("provider") or policy_cfg.get("provider")
374
+ provider_hint = (
375
+ cfg.get("provider") or cfg.get("policy", {}).get("provider") or policy_cfg.get("provider")
376
+ )
375
377
  provider = _detect_provider(model, provider_hint)
376
378
  policy_cfg.pop("provider", None)
377
379
 
@@ -412,7 +414,7 @@ async def main() -> None:
412
414
  problem = data.get("problem")
413
415
  tool_calls = data.get("tool_calls") or []
414
416
  print(
415
- f"Episode {episode+1}/{episodes} seed={seed} status={status} reward={data.get('reward')}\n"
417
+ f"Episode {episode + 1}/{episodes} seed={seed} status={status} reward={data.get('reward')}\n"
416
418
  f" problem: {problem!r}\n"
417
419
  f" tool : {tool_calls!r}\n"
418
420
  f" answer : {answer!r}\n expected: {expected!r}",