synth-ai 0.2.14__py3-none-any.whl → 0.2.16__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 (236) hide show
  1. examples/README.md +1 -0
  2. examples/multi_step/SFT_README.md +147 -0
  3. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +9 -9
  4. examples/multi_step/configs/crafter_sft_qwen30b_lora.toml +62 -0
  5. examples/multi_step/convert_traces_to_sft.py +84 -0
  6. examples/multi_step/run_sft_qwen30b.sh +45 -0
  7. examples/qwen_coder/configs/coder_lora_30b.toml +2 -1
  8. examples/qwen_coder/configs/coder_lora_4b.toml +2 -1
  9. examples/qwen_coder/configs/coder_lora_small.toml +2 -1
  10. examples/qwen_vl/BUGS_AND_FIXES.md +232 -0
  11. examples/qwen_vl/IMAGE_VALIDATION_COMPLETE.md +271 -0
  12. examples/qwen_vl/IMAGE_VALIDATION_SUMMARY.md +260 -0
  13. examples/qwen_vl/INFERENCE_SFT_TESTS.md +412 -0
  14. examples/qwen_vl/NEXT_STEPS_2B.md +325 -0
  15. examples/qwen_vl/QUICKSTART.md +327 -0
  16. examples/qwen_vl/QUICKSTART_RL_VISION.md +110 -0
  17. examples/qwen_vl/README.md +154 -0
  18. examples/qwen_vl/RL_VISION_COMPLETE.md +475 -0
  19. examples/qwen_vl/RL_VISION_TESTING.md +333 -0
  20. examples/qwen_vl/SDK_VISION_INTEGRATION.md +328 -0
  21. examples/qwen_vl/SETUP_COMPLETE.md +275 -0
  22. examples/qwen_vl/VISION_TESTS_COMPLETE.md +490 -0
  23. examples/qwen_vl/VLM_PIPELINE_COMPLETE.md +242 -0
  24. examples/qwen_vl/__init__.py +2 -0
  25. examples/qwen_vl/collect_data_via_cli.md +423 -0
  26. examples/qwen_vl/collect_vision_traces.py +368 -0
  27. examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +127 -0
  28. examples/qwen_vl/configs/crafter_vlm_sft_example.toml +60 -0
  29. examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +43 -0
  30. examples/qwen_vl/configs/eval_gpt4o_vision_proper.toml +29 -0
  31. examples/qwen_vl/configs/eval_gpt5nano_vision.toml +45 -0
  32. examples/qwen_vl/configs/eval_qwen2vl_vision.toml +44 -0
  33. examples/qwen_vl/configs/filter_qwen2vl_sft.toml +50 -0
  34. examples/qwen_vl/configs/filter_vision_sft.toml +53 -0
  35. examples/qwen_vl/configs/filter_vision_test.toml +8 -0
  36. examples/qwen_vl/configs/sft_qwen3_vl_2b_test.toml +54 -0
  37. examples/qwen_vl/crafter_gpt5nano_agent.py +308 -0
  38. examples/qwen_vl/crafter_qwen_vl_agent.py +300 -0
  39. examples/qwen_vl/run_vision_comparison.sh +62 -0
  40. examples/qwen_vl/run_vision_sft_pipeline.sh +175 -0
  41. examples/qwen_vl/test_image_validation.py +201 -0
  42. examples/qwen_vl/test_sft_vision_data.py +110 -0
  43. examples/rl/README.md +1 -1
  44. examples/rl/configs/eval_base_qwen.toml +17 -0
  45. examples/rl/configs/eval_rl_qwen.toml +13 -0
  46. examples/rl/configs/rl_from_base_qwen.toml +37 -0
  47. examples/rl/configs/rl_from_base_qwen17.toml +76 -0
  48. examples/rl/configs/rl_from_ft_qwen.toml +37 -0
  49. examples/rl/run_eval.py +436 -0
  50. examples/rl/run_rl_and_save.py +111 -0
  51. examples/rl/task_app/README.md +22 -0
  52. examples/rl/task_app/math_single_step.py +990 -0
  53. examples/rl/task_app/math_task_app.py +111 -0
  54. examples/sft/README.md +5 -5
  55. examples/sft/configs/crafter_fft_qwen0p6b.toml +4 -2
  56. examples/sft/configs/crafter_lora_qwen0p6b.toml +4 -3
  57. examples/sft/evaluate.py +2 -4
  58. examples/sft/export_dataset.py +7 -4
  59. examples/swe/task_app/README.md +1 -1
  60. examples/swe/task_app/grpo_swe_mini.py +0 -1
  61. examples/swe/task_app/grpo_swe_mini_task_app.py +0 -12
  62. examples/swe/task_app/hosted/envs/mini_swe/environment.py +13 -13
  63. examples/swe/task_app/hosted/policy_routes.py +0 -2
  64. examples/swe/task_app/hosted/rollout.py +0 -8
  65. examples/task_apps/crafter/task_app/grpo_crafter.py +4 -7
  66. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +59 -1
  67. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +30 -0
  68. examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +62 -31
  69. examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +16 -14
  70. examples/task_apps/enron/__init__.py +1 -0
  71. examples/vlm/README.md +3 -3
  72. examples/vlm/configs/crafter_vlm_gpt4o.toml +2 -0
  73. examples/vlm/crafter_openai_vlm_agent.py +3 -5
  74. examples/vlm/filter_image_rows.py +1 -1
  75. examples/vlm/run_crafter_vlm_benchmark.py +2 -2
  76. examples/warming_up_to_rl/_utils.py +92 -0
  77. examples/warming_up_to_rl/analyze_trace_db.py +1 -1
  78. examples/warming_up_to_rl/configs/crafter_fft.toml +2 -0
  79. examples/warming_up_to_rl/configs/crafter_fft_4b.toml +2 -0
  80. examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +2 -0
  81. examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +2 -0
  82. examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +2 -1
  83. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +2 -1
  84. examples/warming_up_to_rl/configs/rl_from_ft.toml +2 -0
  85. examples/warming_up_to_rl/export_trace_sft.py +174 -60
  86. examples/warming_up_to_rl/readme.md +63 -132
  87. examples/warming_up_to_rl/run_fft_and_save.py +1 -1
  88. examples/warming_up_to_rl/run_rl_and_save.py +1 -1
  89. examples/warming_up_to_rl/task_app/README.md +42 -0
  90. examples/warming_up_to_rl/task_app/grpo_crafter.py +696 -0
  91. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +135 -0
  92. examples/warming_up_to_rl/task_app/synth_envs_hosted/README.md +173 -0
  93. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +5 -0
  94. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +143 -0
  95. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +1226 -0
  96. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -0
  97. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +6 -0
  98. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -0
  99. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +522 -0
  100. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +478 -0
  101. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +108 -0
  102. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +305 -0
  103. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +47 -0
  104. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +204 -0
  105. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +5 -0
  106. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +618 -0
  107. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +100 -0
  108. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +1081 -0
  109. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +195 -0
  110. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +1861 -0
  111. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +5 -0
  112. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +211 -0
  113. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +161 -0
  114. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +137 -0
  115. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +62 -0
  116. synth_ai/__init__.py +44 -30
  117. synth_ai/_utils/__init__.py +47 -0
  118. synth_ai/_utils/base_url.py +10 -0
  119. synth_ai/_utils/http.py +10 -0
  120. synth_ai/_utils/prompts.py +10 -0
  121. synth_ai/_utils/task_app_state.py +12 -0
  122. synth_ai/_utils/user_config.py +10 -0
  123. synth_ai/api/models/supported.py +144 -7
  124. synth_ai/api/train/__init__.py +13 -1
  125. synth_ai/api/train/cli.py +30 -7
  126. synth_ai/api/train/config_finder.py +18 -11
  127. synth_ai/api/train/env_resolver.py +13 -10
  128. synth_ai/cli/__init__.py +62 -78
  129. synth_ai/cli/_modal_wrapper.py +7 -5
  130. synth_ai/cli/_typer_patch.py +0 -2
  131. synth_ai/cli/_validate_task_app.py +22 -4
  132. synth_ai/cli/legacy_root_backup.py +3 -1
  133. synth_ai/cli/lib/__init__.py +10 -0
  134. synth_ai/cli/lib/task_app_discovery.py +7 -0
  135. synth_ai/cli/lib/task_app_env.py +518 -0
  136. synth_ai/cli/recent.py +2 -1
  137. synth_ai/cli/setup.py +266 -0
  138. synth_ai/cli/status.py +1 -1
  139. synth_ai/cli/task_app_deploy.py +16 -0
  140. synth_ai/cli/task_app_list.py +25 -0
  141. synth_ai/cli/task_app_modal_serve.py +16 -0
  142. synth_ai/cli/task_app_serve.py +18 -0
  143. synth_ai/cli/task_apps.py +71 -31
  144. synth_ai/cli/traces.py +1 -1
  145. synth_ai/cli/train.py +18 -0
  146. synth_ai/cli/tui.py +7 -2
  147. synth_ai/cli/turso.py +1 -1
  148. synth_ai/cli/watch.py +1 -1
  149. synth_ai/demos/__init__.py +10 -0
  150. synth_ai/demos/core/__init__.py +28 -1
  151. synth_ai/demos/crafter/__init__.py +1 -0
  152. synth_ai/demos/crafter/crafter_fft_4b.toml +55 -0
  153. synth_ai/demos/crafter/grpo_crafter_task_app.py +185 -0
  154. synth_ai/demos/crafter/rl_from_base_qwen4b.toml +74 -0
  155. synth_ai/demos/demo_registry.py +176 -0
  156. synth_ai/demos/math/__init__.py +1 -0
  157. synth_ai/demos/math/_common.py +16 -0
  158. synth_ai/demos/math/app.py +38 -0
  159. synth_ai/demos/math/config.toml +76 -0
  160. synth_ai/demos/math/deploy_modal.py +54 -0
  161. synth_ai/demos/math/modal_task_app.py +702 -0
  162. synth_ai/demos/math/task_app_entry.py +51 -0
  163. synth_ai/environments/environment/core.py +7 -1
  164. synth_ai/environments/examples/bandit/engine.py +0 -1
  165. synth_ai/environments/examples/bandit/environment.py +0 -1
  166. synth_ai/environments/examples/wordle/environment.py +0 -1
  167. synth_ai/evals/base.py +16 -5
  168. synth_ai/evals/client.py +1 -1
  169. synth_ai/inference/client.py +1 -1
  170. synth_ai/judge_schemas.py +8 -8
  171. synth_ai/learning/client.py +1 -1
  172. synth_ai/learning/health.py +1 -1
  173. synth_ai/learning/jobs.py +1 -1
  174. synth_ai/learning/rl/client.py +1 -1
  175. synth_ai/learning/rl/env_keys.py +1 -1
  176. synth_ai/learning/rl/secrets.py +1 -1
  177. synth_ai/learning/sft/client.py +1 -1
  178. synth_ai/learning/sft/data.py +407 -4
  179. synth_ai/learning/validators.py +4 -1
  180. synth_ai/task/apps/__init__.py +4 -2
  181. synth_ai/task/config.py +6 -4
  182. synth_ai/task/rubrics/__init__.py +1 -2
  183. synth_ai/task/rubrics/loaders.py +14 -10
  184. synth_ai/task/rubrics.py +219 -0
  185. synth_ai/task/trace_correlation_helpers.py +24 -11
  186. synth_ai/task/tracing_utils.py +14 -3
  187. synth_ai/task/validators.py +2 -3
  188. synth_ai/tracing_v3/abstractions.py +3 -3
  189. synth_ai/tracing_v3/config.py +15 -13
  190. synth_ai/tracing_v3/constants.py +21 -0
  191. synth_ai/tracing_v3/db_config.py +3 -1
  192. synth_ai/tracing_v3/decorators.py +10 -7
  193. synth_ai/tracing_v3/llm_call_record_helpers.py +5 -5
  194. synth_ai/tracing_v3/session_tracer.py +7 -7
  195. synth_ai/tracing_v3/storage/base.py +29 -29
  196. synth_ai/tracing_v3/storage/config.py +3 -3
  197. synth_ai/tracing_v3/turso/daemon.py +8 -9
  198. synth_ai/tracing_v3/turso/native_manager.py +80 -72
  199. synth_ai/tracing_v3/utils.py +2 -2
  200. synth_ai/tui/cli/query_experiments.py +4 -4
  201. synth_ai/tui/cli/query_experiments_v3.py +4 -4
  202. synth_ai/tui/dashboard.py +14 -9
  203. synth_ai/utils/__init__.py +101 -0
  204. synth_ai/utils/base_url.py +94 -0
  205. synth_ai/utils/cli.py +131 -0
  206. synth_ai/utils/env.py +287 -0
  207. synth_ai/utils/http.py +169 -0
  208. synth_ai/utils/modal.py +308 -0
  209. synth_ai/utils/process.py +212 -0
  210. synth_ai/utils/prompts.py +39 -0
  211. synth_ai/utils/sqld.py +122 -0
  212. synth_ai/utils/task_app_discovery.py +882 -0
  213. synth_ai/utils/task_app_env.py +186 -0
  214. synth_ai/utils/task_app_state.py +318 -0
  215. synth_ai/utils/user_config.py +137 -0
  216. synth_ai/v0/config/__init__.py +1 -5
  217. synth_ai/v0/config/base_url.py +1 -7
  218. synth_ai/v0/tracing/config.py +1 -1
  219. synth_ai/v0/tracing/decorators.py +1 -1
  220. synth_ai/v0/tracing/upload.py +1 -1
  221. synth_ai/v0/tracing_v1/config.py +1 -1
  222. synth_ai/v0/tracing_v1/decorators.py +1 -1
  223. synth_ai/v0/tracing_v1/upload.py +1 -1
  224. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/METADATA +85 -31
  225. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/RECORD +229 -117
  226. synth_ai/cli/man.py +0 -106
  227. synth_ai/compound/cais.py +0 -0
  228. synth_ai/core/experiment.py +0 -13
  229. synth_ai/core/system.py +0 -15
  230. synth_ai/demo_registry.py +0 -295
  231. synth_ai/handshake.py +0 -109
  232. synth_ai/http.py +0 -26
  233. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/WHEEL +0 -0
  234. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/entry_points.txt +0 -0
  235. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/licenses/LICENSE +0 -0
  236. {synth_ai-0.2.14.dist-info → synth_ai-0.2.16.dist-info}/top_level.txt +0 -0
synth_ai/cli/__init__.py CHANGED
@@ -8,8 +8,9 @@ pyproject entry point `synth_ai.cli:cli`.
8
8
  from __future__ import annotations
9
9
 
10
10
  import importlib
11
+ import sys
11
12
  from collections.abc import Callable
12
- from typing import Any, cast
13
+ from typing import Any
13
14
 
14
15
  # Load environment variables from a local .env if present (repo root)
15
16
  try:
@@ -21,86 +22,69 @@ except Exception:
21
22
  # dotenv is optional at runtime; proceed if unavailable
22
23
  pass
23
24
 
24
- try:
25
- from synth_ai.cli._typer_patch import patch_typer_make_metavar
26
-
27
- patch_typer_make_metavar()
28
- except Exception:
29
- pass
30
-
25
+ def _callable_from(module: Any, attr: str) -> Callable[..., Any] | None:
26
+ candidate = getattr(module, attr, None)
27
+ return candidate if callable(candidate) else None
31
28
 
32
- from synth_ai.cli.root import cli # new canonical CLI entrypoint
33
-
34
- # Register subcommands from this package onto the group
35
- # Deprecated/legacy commands intentionally not registered: watch/experiments, balance, calc,
36
- # man, recent, status, traces
37
- try:
38
- from synth_ai.cli import demo as _demo
39
-
40
- _demo.register(cli)
41
- except Exception:
42
- pass
43
- try:
44
- from synth_ai.cli import turso as _turso
45
-
46
- _turso.register(cli)
47
- except Exception:
48
- pass
49
- try:
50
- _train_module = cast(Any, importlib.import_module("synth_ai.api.train"))
51
- _train_register = cast(Callable[[Any], None], _train_module.register)
52
- _train_register(cli)
53
- except Exception:
54
- pass
55
29
 
56
-
57
- # Import task_app_group conditionally
58
- try:
59
- from synth_ai.cli.task_apps import task_app_group
60
- cli.add_command(task_app_group, name="task-app")
61
- except Exception:
62
- # Task app functionality not available
63
- pass
64
-
65
-
66
- try:
67
- # Make task_apps import more robust to handle missing optional dependencies
68
- import importlib
69
- task_apps_module = importlib.import_module('synth_ai.cli.task_apps')
70
- task_apps_module.register(cli)
71
- except (ImportError, ModuleNotFoundError, TypeError, RuntimeError) as e:
72
- # Task apps module not available (missing optional dependencies)
73
- # This is expected - silently skip
74
- pass
75
-
76
- # Register TUI command - make import completely isolated
77
- def _register_tui_command():
78
- """Register TUI command only when called, not during CLI startup."""
30
+ def _maybe_import(module_path: str) -> Any | None:
79
31
  try:
80
- # Import TUI only when the command is actually used
81
- from synth_ai.cli.tui import register as tui_register
82
- tui_register(cli)
32
+ return importlib.import_module(module_path)
83
33
  except Exception:
84
- # TUI not available - this is expected if dependencies are missing
85
- pass
86
-
87
- # Add TUI command as a lazy-registered command
88
- try:
89
- # Try to import and register immediately for normal cases
90
- from synth_ai.cli.tui import register as tui_register
91
- tui_register(cli)
92
- except Exception:
93
- # If that fails, add a lazy registration that will only happen when called
94
- # For now, just skip - the command won't be available but CLI won't crash
95
- pass
34
+ return None
35
+
36
+
37
+ def _maybe_call(module_path: str, attr: str, *args: Any, **kwargs: Any) -> None:
38
+ module = _maybe_import(module_path)
39
+ if not module:
40
+ return
41
+ fn = _callable_from(module, attr)
42
+ if fn:
43
+ fn(*args, **kwargs)
44
+
45
+
46
+ # Apply Typer patch if available
47
+ _maybe_call("synth_ai.cli._typer_patch", "patch_typer_make_metavar")
48
+
49
+
50
+ _cli_module = _maybe_import("synth_ai.cli.root")
51
+ if not _cli_module:
52
+ raise ImportError("synth_ai.cli.root is required for CLI entrypoint")
53
+ cli = _cli_module.cli # type: ignore[attr-defined]
54
+
55
+
56
+ # Register optional subcommands packaged under synth_ai.cli.*
57
+ for _module_path in ("synth_ai.cli.demo", "synth_ai.cli.turso"):
58
+ module = _maybe_import(_module_path)
59
+ if not module:
60
+ continue
61
+ sub_name = _module_path.rsplit(".", 1)[-1]
62
+ setattr(sys.modules[__name__], sub_name, module)
63
+ fn = _callable_from(module, "register")
64
+ if fn:
65
+ fn(cli)
66
+
67
+ # Train CLI lives under synth_ai.api.train
68
+ _maybe_call("synth_ai.api.train", "register", cli)
69
+
70
+ # Task app group/commands are optional and have richer API surface
71
+ _task_apps_module = _maybe_import("synth_ai.cli.task_apps")
72
+ if _task_apps_module:
73
+ task_app_group = getattr(_task_apps_module, "task_app_group", None)
74
+ if task_app_group is not None:
75
+ cli.add_command(task_app_group, name="task-app")
76
+ # Expose common aliases when present
77
+ commands = getattr(task_app_group, "commands", None)
78
+ if isinstance(commands, dict):
79
+ for alias, name in (("serve", "serve"), ("deploy", "deploy"), ("modal-serve", "modal-serve")):
80
+ command = commands.get(name)
81
+ if command is not None:
82
+ cli.add_command(command, name=alias)
83
+ register_task_apps = _callable_from(_task_apps_module, "register")
84
+ if register_task_apps:
85
+ register_task_apps(cli)
86
+
87
+ # Register TUI command if dependencies allow
88
+ _maybe_call("synth_ai.cli.tui", "register", cli)
96
89
 
97
- # Add task app commands if available
98
- try:
99
- if 'task_app_group' in locals() and hasattr(task_app_group, 'commands'):
100
- cli.add_command(task_app_group.commands["serve"], name="serve")
101
- cli.add_command(task_app_group.commands["deploy"], name="deploy")
102
- cli.add_command(task_app_group.commands["modal-serve"], name="modal-serve")
103
- except Exception:
104
- # Task app commands not available
105
- pass
106
90
  # Top-level 'info' alias removed; use `synth-ai task-app info` instead
@@ -1,16 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib
3
4
  import sys
4
5
 
5
6
 
6
7
  def main() -> int:
7
8
  # Apply Typer compatibility patch before Modal CLI bootstraps Click/Typer internals.
8
9
  try:
9
- from synth_ai.cli._typer_patch import patch_typer_make_metavar
10
-
11
- patch_typer_make_metavar()
10
+ module = importlib.import_module("synth_ai.cli._typer_patch")
12
11
  except Exception:
13
- pass
12
+ module = None
13
+ if module is not None:
14
+ patch = getattr(module, "patch_typer_make_metavar", None)
15
+ if callable(patch):
16
+ patch()
14
17
 
15
18
  from modal.__main__ import main as modal_main
16
19
 
@@ -26,4 +29,3 @@ def main() -> int:
26
29
 
27
30
  if __name__ == "__main__":
28
31
  sys.exit(main())
29
-
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from click import Parameter
4
2
 
5
3
 
@@ -3,9 +3,27 @@
3
3
  # This module provides the validate_task_app function for CLI use
4
4
  # The actual implementation is imported from the task module
5
5
 
6
- from synth_ai.task.validators import (
7
- validate_task_app_endpoint as validate_task_app, # type: ignore[attr-defined]
8
- )
6
+ import importlib
7
+ from collections.abc import Callable
8
+ from typing import Any
9
9
 
10
- __all__ = ["validate_task_app"]
10
+ _validators_module: Any | None = None
11
+ validate_task_app: Callable[..., Any] | None = None
12
+
13
+ try:
14
+ _validators_module = importlib.import_module("synth_ai.task.validators")
15
+ except Exception:
16
+ _validators_module = None
17
+
18
+ if _validators_module is not None:
19
+ candidate = getattr(_validators_module, "validate_task_app_endpoint", None)
20
+ if callable(candidate):
21
+ validate_task_app = candidate
11
22
 
23
+ if validate_task_app is None:
24
+ def _missing_validate_task_app(*_args: Any, **_kwargs: Any) -> None:
25
+ raise RuntimeError("task validation utilities are unavailable in this environment")
26
+
27
+ validate_task_app = _missing_validate_task_app
28
+
29
+ __all__ = ["validate_task_app"]
@@ -235,7 +235,9 @@ def view(url: str):
235
235
  """Launch the interactive TUI dashboard."""
236
236
  try:
237
237
  module = importlib.import_module(".tui.dashboard", __package__)
238
- synth_dashboard_cls = getattr(module, "SynthDashboard")
238
+ synth_dashboard_cls = getattr(module, "SynthDashboard", None)
239
+ if synth_dashboard_cls is None:
240
+ raise RuntimeError("SynthDashboard class not available")
239
241
  app = synth_dashboard_cls(db_url=url)
240
242
  app.run()
241
243
  except ImportError:
@@ -0,0 +1,10 @@
1
+ """
2
+ Support utilities reused across CLI subcommands.
3
+
4
+ This module currently exposes helpers for env handling and discovery by
5
+ re-exporting the maintained implementations under ``synth_ai.utils``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from .task_app_env import * # noqa: F401,F403
@@ -0,0 +1,7 @@
1
+ """
2
+ Backwards-compatible re-export of task app discovery helpers.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from synth_ai.utils.task_app_discovery import * # noqa: F401,F403