synth-ai 0.2.16__py3-none-any.whl → 0.2.19__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/analyze_semantic_words.sh +2 -2
- examples/baseline/banking77_baseline.py +204 -0
- examples/baseline/crafter_baseline.py +407 -0
- examples/baseline/pokemon_red_baseline.py +326 -0
- examples/baseline/simple_baseline.py +56 -0
- examples/baseline/warming_up_to_rl_baseline.py +239 -0
- examples/blog_posts/gepa/README.md +355 -0
- examples/blog_posts/gepa/configs/banking77_gepa_local.toml +95 -0
- examples/blog_posts/gepa/configs/banking77_gepa_test.toml +82 -0
- examples/blog_posts/gepa/configs/banking77_mipro_local.toml +52 -0
- examples/blog_posts/gepa/configs/hotpotqa_gepa_local.toml +59 -0
- examples/blog_posts/gepa/configs/hotpotqa_gepa_qwen.toml +36 -0
- examples/blog_posts/gepa/configs/hotpotqa_mipro_local.toml +53 -0
- examples/blog_posts/gepa/configs/hover_gepa_local.toml +59 -0
- examples/blog_posts/gepa/configs/hover_gepa_qwen.toml +36 -0
- examples/blog_posts/gepa/configs/hover_mipro_local.toml +53 -0
- examples/blog_posts/gepa/configs/ifbench_gepa_local.toml +59 -0
- examples/blog_posts/gepa/configs/ifbench_gepa_qwen.toml +36 -0
- examples/blog_posts/gepa/configs/ifbench_mipro_local.toml +53 -0
- examples/blog_posts/gepa/configs/pupa_gepa_local.toml +60 -0
- examples/blog_posts/gepa/configs/pupa_mipro_local.toml +54 -0
- examples/blog_posts/gepa/deploy_banking77_task_app.sh +41 -0
- examples/blog_posts/gepa/gepa_baseline.py +204 -0
- examples/blog_posts/gepa/query_prompts_example.py +97 -0
- examples/blog_posts/gepa/run_gepa_banking77.sh +87 -0
- examples/blog_posts/gepa/task_apps.py +105 -0
- examples/blog_posts/gepa/test_gepa_local.sh +67 -0
- examples/blog_posts/gepa/verify_banking77_setup.sh +123 -0
- examples/blog_posts/pokemon_vl/README.md +98 -0
- examples/blog_posts/pokemon_vl/configs/eval_gpt5nano.toml +26 -0
- examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +27 -0
- examples/blog_posts/pokemon_vl/configs/eval_rl_final.toml +24 -0
- examples/blog_posts/pokemon_vl/configs/filter_high_reward.toml +10 -0
- examples/blog_posts/pokemon_vl/configs/train_rl_from_sft.toml +43 -0
- examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
- examples/blog_posts/pokemon_vl/extract_images.py +239 -0
- examples/blog_posts/pokemon_vl/pokemon_vl_baseline.py +326 -0
- examples/blog_posts/pokemon_vl/run_eval_extract_images.py +209 -0
- examples/blog_posts/pokemon_vl/run_qwen_eval_extract_images.py +212 -0
- examples/blog_posts/pokemon_vl/text_box_analysis.md +106 -0
- examples/blog_posts/warming_up_to_rl/ARCHITECTURE.md +195 -0
- examples/blog_posts/warming_up_to_rl/FINAL_TEST_RESULTS.md +127 -0
- examples/blog_posts/warming_up_to_rl/INFERENCE_SUCCESS.md +132 -0
- examples/blog_posts/warming_up_to_rl/README.md +158 -0
- examples/blog_posts/warming_up_to_rl/SMOKE_TESTING.md +164 -0
- examples/blog_posts/warming_up_to_rl/SMOKE_TEST_COMPLETE.md +253 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_baseline_qwen32b_10x20.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b_10x20.toml +26 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_groq_qwen32b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_openai_gpt_oss_120b.toml +29 -0
- examples/blog_posts/warming_up_to_rl/configs/filter_high_reward_dataset.toml +10 -0
- examples/blog_posts/warming_up_to_rl/configs/smoke_test.toml +75 -0
- examples/blog_posts/warming_up_to_rl/configs/train_rl_from_sft.toml +91 -0
- examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
- examples/blog_posts/warming_up_to_rl/warming_up_to_rl_baseline.py +187 -0
- examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
- examples/multi_step/configs/VERILOG_REWARDS.md +4 -0
- examples/multi_step/configs/VERILOG_RL_CHECKLIST.md +4 -0
- examples/multi_step/configs/crafter_rl_outcome.toml +2 -1
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +65 -107
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +2 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +2 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple_NEW_FORMAT.toml +105 -0
- examples/multi_step/configs/verilog_rl_lora.toml +80 -123
- examples/qwen_coder/configs/coder_lora_30b.toml +1 -3
- examples/qwen_coder/configs/coder_lora_4b.toml +4 -1
- examples/qwen_coder/configs/coder_lora_small.toml +1 -3
- examples/qwen_vl/README.md +10 -12
- examples/qwen_vl/SETUP_COMPLETE.md +7 -8
- examples/qwen_vl/VISION_TESTS_COMPLETE.md +2 -3
- examples/qwen_vl/collect_data_via_cli.md +76 -84
- examples/qwen_vl/collect_vision_traces.py +4 -4
- examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +40 -57
- examples/qwen_vl/configs/crafter_vlm_sft_example.toml +1 -2
- examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +20 -37
- examples/qwen_vl/configs/eval_gpt5nano_vision.toml +21 -40
- examples/qwen_vl/configs/eval_qwen3vl_vision.toml +26 -0
- examples/qwen_vl/configs/{filter_qwen2vl_sft.toml → filter_qwen3vl_sft.toml} +4 -5
- examples/qwen_vl/configs/filter_vision_sft.toml +2 -3
- examples/qwen_vl/crafter_qwen_vl_agent.py +5 -5
- examples/qwen_vl/run_vision_comparison.sh +6 -7
- examples/rl/README.md +5 -5
- examples/rl/configs/rl_from_base_qwen.toml +26 -1
- examples/rl/configs/rl_from_base_qwen17.toml +6 -2
- examples/rl/task_app/README.md +1 -2
- examples/rl/task_app/math_single_step.py +2 -2
- examples/run_crafter_demo.sh +2 -2
- examples/sft/README.md +1 -1
- examples/sft/configs/crafter_fft_qwen0p6b.toml +4 -1
- examples/sft/configs/crafter_lora_qwen0p6b.toml +4 -1
- examples/swe/task_app/README.md +32 -2
- examples/swe/task_app/grpo_swe_mini.py +4 -0
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +1 -1
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +37 -10
- examples/swe/task_app/hosted/inference/openai_client.py +4 -38
- examples/swe/task_app/hosted/policy_routes.py +17 -0
- examples/swe/task_app/hosted/rollout.py +4 -2
- examples/swe/task_app/morph_backend.py +178 -0
- examples/task_apps/banking77/__init__.py +6 -0
- examples/task_apps/banking77/banking77_task_app.py +841 -0
- examples/task_apps/banking77/deploy_wrapper.py +46 -0
- examples/task_apps/crafter/CREATE_SFT_DATASET.md +4 -0
- examples/task_apps/crafter/FILTER_COMMAND_STATUS.md +4 -0
- examples/task_apps/crafter/FILTER_COMMAND_SUCCESS.md +4 -0
- examples/task_apps/crafter/task_app/README.md +1 -1
- examples/task_apps/crafter/task_app/grpo_crafter.py +90 -5
- examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +1 -1
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +4 -26
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -2
- examples/task_apps/crafter/task_app/synth_envs_hosted/hosted_app.py +49 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +372 -107
- examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +81 -12
- examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +82 -11
- examples/task_apps/crafter/task_app/synth_envs_hosted/utils.py +194 -1
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
- examples/task_apps/gepa_benchmarks/__init__.py +7 -0
- examples/task_apps/gepa_benchmarks/common.py +260 -0
- examples/task_apps/gepa_benchmarks/hotpotqa_task_app.py +507 -0
- examples/task_apps/gepa_benchmarks/hover_task_app.py +436 -0
- examples/task_apps/gepa_benchmarks/ifbench_task_app.py +563 -0
- examples/task_apps/gepa_benchmarks/pupa_task_app.py +460 -0
- examples/task_apps/math/README.md +1 -2
- examples/task_apps/pokemon_red/README.md +3 -4
- examples/task_apps/pokemon_red/README_IMAGE_ONLY_EVAL.md +4 -0
- examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +6 -5
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +1 -2
- examples/task_apps/pokemon_red/task_app.py +288 -39
- examples/task_apps/sokoban/README.md +2 -3
- examples/task_apps/verilog/eval_groq_qwen32b.toml +12 -14
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +1 -1
- examples/vlm/configs/crafter_vlm_gpt4o.toml +4 -1
- examples/warming_up_to_rl/configs/crafter_fft.toml +4 -1
- examples/warming_up_to_rl/configs/crafter_fft_4b.toml +0 -2
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +3 -2
- examples/warming_up_to_rl/run_local_rollout_traced.py +1 -1
- examples/warming_up_to_rl/task_app/README.md +1 -1
- examples/warming_up_to_rl/task_app/grpo_crafter.py +185 -5
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +3 -27
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +49 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +156 -45
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +37 -4
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +33 -3
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +67 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +6 -0
- synth_ai/api/train/builders.py +99 -4
- synth_ai/api/train/cli.py +516 -26
- synth_ai/api/train/config_finder.py +13 -2
- synth_ai/api/train/configs/__init__.py +23 -2
- synth_ai/api/train/configs/prompt_learning.py +442 -0
- synth_ai/api/train/configs/rl.py +61 -7
- synth_ai/api/train/configs/sft.py +6 -2
- synth_ai/api/train/configs/shared.py +59 -2
- synth_ai/api/train/task_app.py +1 -1
- synth_ai/api/train/validators.py +277 -0
- synth_ai/auth/credentials.py +119 -0
- synth_ai/baseline/__init__.py +25 -0
- synth_ai/baseline/config.py +209 -0
- synth_ai/baseline/discovery.py +214 -0
- synth_ai/baseline/execution.py +146 -0
- synth_ai/cli/__init__.py +94 -18
- synth_ai/cli/__main__.py +0 -0
- synth_ai/cli/claude.py +70 -0
- synth_ai/cli/codex.py +84 -0
- synth_ai/cli/commands/__init__.py +18 -0
- synth_ai/cli/commands/baseline/__init__.py +12 -0
- synth_ai/cli/commands/baseline/core.py +637 -0
- synth_ai/cli/commands/baseline/list.py +93 -0
- synth_ai/cli/commands/demo/__init__.py +6 -0
- synth_ai/cli/commands/demo/core.py +163 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1112 -0
- synth_ai/cli/commands/eval/errors.py +81 -0
- synth_ai/cli/commands/eval/validation.py +133 -0
- synth_ai/cli/commands/filter/__init__.py +12 -0
- synth_ai/cli/commands/filter/core.py +424 -0
- synth_ai/cli/commands/filter/errors.py +55 -0
- synth_ai/cli/commands/filter/validation.py +77 -0
- synth_ai/cli/commands/help/__init__.py +177 -0
- synth_ai/cli/commands/help/core.py +72 -0
- synth_ai/cli/commands/smoke/__init__.py +7 -0
- synth_ai/cli/commands/smoke/core.py +1436 -0
- synth_ai/cli/commands/status/__init__.py +64 -0
- synth_ai/cli/commands/status/client.py +192 -0
- synth_ai/cli/commands/status/config.py +92 -0
- synth_ai/cli/commands/status/errors.py +20 -0
- synth_ai/cli/commands/status/formatters.py +164 -0
- synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
- synth_ai/cli/commands/status/subcommands/files.py +79 -0
- synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
- synth_ai/cli/commands/status/subcommands/models.py +79 -0
- synth_ai/cli/commands/status/subcommands/pricing.py +22 -0
- synth_ai/cli/commands/status/subcommands/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -0
- synth_ai/cli/commands/status/subcommands/usage.py +203 -0
- synth_ai/cli/commands/status/utils.py +114 -0
- synth_ai/cli/commands/train/__init__.py +53 -0
- synth_ai/cli/commands/train/core.py +21 -0
- synth_ai/cli/commands/train/errors.py +117 -0
- synth_ai/cli/commands/train/judge_schemas.py +200 -0
- synth_ai/cli/commands/train/judge_validation.py +305 -0
- synth_ai/cli/commands/train/validation.py +386 -0
- synth_ai/cli/demo.py +30 -158
- synth_ai/cli/deploy/__init__.py +43 -0
- synth_ai/cli/deploy.py +162 -0
- synth_ai/cli/eval/__init__.py +36 -0
- synth_ai/cli/eval/core.py +5 -0
- synth_ai/cli/eval/errors.py +31 -0
- synth_ai/cli/eval/validation.py +5 -0
- synth_ai/cli/filter/__init__.py +28 -0
- synth_ai/cli/filter/core.py +5 -0
- synth_ai/cli/filter/errors.py +23 -0
- synth_ai/cli/filter/validation.py +5 -0
- synth_ai/cli/legacy_root_backup.py +14 -8
- synth_ai/cli/modal_serve/__init__.py +12 -0
- synth_ai/cli/modal_serve/core.py +14 -0
- synth_ai/cli/modal_serve/errors.py +8 -0
- synth_ai/cli/modal_serve/validation.py +11 -0
- synth_ai/cli/opencode.py +107 -0
- synth_ai/cli/root.py +9 -5
- synth_ai/cli/serve/__init__.py +12 -0
- synth_ai/cli/serve/core.py +14 -0
- synth_ai/cli/serve/errors.py +8 -0
- synth_ai/cli/serve/validation.py +11 -0
- synth_ai/cli/setup.py +20 -265
- synth_ai/cli/status.py +7 -126
- synth_ai/cli/task_app_deploy.py +1 -10
- synth_ai/cli/task_app_modal_serve.py +4 -9
- synth_ai/cli/task_app_serve.py +4 -11
- synth_ai/cli/task_apps.py +51 -1480
- synth_ai/cli/train/__init__.py +12 -0
- synth_ai/cli/train/core.py +21 -0
- synth_ai/cli/train/errors.py +8 -0
- synth_ai/cli/train/validation.py +24 -0
- synth_ai/cli/train.py +1 -14
- synth_ai/demos/crafter/grpo_crafter_task_app.py +1 -1
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +1 -1
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +7 -4
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +9 -5
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +4 -3
- synth_ai/environments/examples/red/engine.py +33 -12
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
- synth_ai/environments/examples/red/environment.py +26 -0
- synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
- synth_ai/http.py +12 -0
- synth_ai/judge_schemas.py +10 -10
- synth_ai/learning/__init__.py +10 -0
- synth_ai/learning/prompt_learning_client.py +276 -0
- synth_ai/learning/prompt_learning_types.py +184 -0
- synth_ai/learning/rl/client.py +3 -1
- synth_ai/pricing/__init__.py +2 -0
- synth_ai/pricing/model_pricing.py +57 -0
- synth_ai/streaming/__init__.py +29 -0
- synth_ai/streaming/config.py +94 -0
- synth_ai/streaming/handlers.py +518 -0
- synth_ai/streaming/streamer.py +320 -0
- synth_ai/streaming/types.py +95 -0
- synth_ai/task/apps/__init__.py +1 -0
- synth_ai/task/config.py +2 -0
- synth_ai/task/tracing_utils.py +25 -25
- synth_ai/task/validators.py +45 -9
- synth_ai/task_app_cfgs.py +21 -0
- synth_ai/tracing_v3/config.py +162 -19
- synth_ai/tracing_v3/constants.py +1 -1
- synth_ai/tracing_v3/db_config.py +24 -38
- synth_ai/tracing_v3/migration_helper.py +1 -2
- synth_ai/tracing_v3/storage/config.py +47 -13
- synth_ai/tracing_v3/storage/factory.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +113 -11
- synth_ai/tracing_v3/turso/native_manager.py +92 -16
- synth_ai/types.py +8 -0
- synth_ai/urls.py +11 -0
- synth_ai/utils/__init__.py +30 -1
- synth_ai/utils/agents.py +74 -0
- synth_ai/utils/bin.py +39 -0
- synth_ai/utils/cli.py +149 -5
- synth_ai/utils/env.py +40 -33
- synth_ai/utils/http.py +4 -1
- synth_ai/utils/json.py +72 -0
- synth_ai/utils/modal.py +285 -3
- synth_ai/utils/paths.py +48 -0
- synth_ai/utils/uvicorn.py +113 -0
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/METADATA +109 -6
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/RECORD +291 -142
- examples/qwen_vl/configs/eval_qwen2vl_vision.toml +0 -44
- synth_ai/cli/tui.py +0 -62
- synth_ai/tui/__init__.py +0 -5
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -164
- synth_ai/tui/cli/query_experiments_v3.py +0 -164
- synth_ai/tui/dashboard.py +0 -911
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.16.dist-info → synth_ai-0.2.19.dist-info}/top_level.txt +0 -0
synth_ai/cli/deploy.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from types import SimpleNamespace
|
|
3
|
+
from typing import Literal, TypeAlias, get_args
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from synth_ai.task_app_cfgs import LocalTaskAppConfig, ModalTaskAppConfig
|
|
7
|
+
from synth_ai.utils.cli import PromptedChoiceOption, PromptedChoiceType, PromptedPathOption
|
|
8
|
+
from synth_ai.utils.modal import deploy_modal_app, get_default_modal_bin_path
|
|
9
|
+
from synth_ai.utils.uvicorn import deploy_uvicorn_app
|
|
10
|
+
|
|
11
|
+
RuntimeType: TypeAlias = Literal[
|
|
12
|
+
"local",
|
|
13
|
+
"modal"
|
|
14
|
+
]
|
|
15
|
+
RUNTIMES = get_args(RuntimeType)
|
|
16
|
+
|
|
17
|
+
MODAL_RUNTIME_OPTIONS = [
|
|
18
|
+
"task_app_name",
|
|
19
|
+
"cmd_arg",
|
|
20
|
+
"modal_bin_path",
|
|
21
|
+
"dry_run",
|
|
22
|
+
"modal_app_path",
|
|
23
|
+
]
|
|
24
|
+
LOCAL_RUNTIME_OPTIONS = [
|
|
25
|
+
"trace",
|
|
26
|
+
"host",
|
|
27
|
+
"port"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
RUNTIME_MSG = SimpleNamespace(
|
|
31
|
+
init="[deploy]",
|
|
32
|
+
local="[deploy --runtime local]",
|
|
33
|
+
modal="[deploy --runtime modal]",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@click.command("deploy")
|
|
38
|
+
# --- Required options ---
|
|
39
|
+
@click.option(
|
|
40
|
+
"--task-app",
|
|
41
|
+
"task_app_path",
|
|
42
|
+
cls=PromptedPathOption,
|
|
43
|
+
type=click.Path(
|
|
44
|
+
exists=True,
|
|
45
|
+
dir_okay=False,
|
|
46
|
+
file_okay=True,
|
|
47
|
+
path_type=Path
|
|
48
|
+
),
|
|
49
|
+
file_type=".py",
|
|
50
|
+
help=f"{RUNTIME_MSG.init} Enter the path to your task app",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--runtime",
|
|
54
|
+
"runtime",
|
|
55
|
+
cls=PromptedChoiceOption,
|
|
56
|
+
type=PromptedChoiceType(RUNTIMES),
|
|
57
|
+
required=True
|
|
58
|
+
)
|
|
59
|
+
# --- Local-only options ---
|
|
60
|
+
@click.option(
|
|
61
|
+
"--trace/--no-trace",
|
|
62
|
+
"trace",
|
|
63
|
+
default=True,
|
|
64
|
+
help=f"{RUNTIME_MSG.local} Enable or disable trace output"
|
|
65
|
+
)
|
|
66
|
+
@click.option(
|
|
67
|
+
"--host",
|
|
68
|
+
"host",
|
|
69
|
+
default="127.0.0.1",
|
|
70
|
+
help=f"{RUNTIME_MSG.local} Host to bind to"
|
|
71
|
+
)
|
|
72
|
+
@click.option(
|
|
73
|
+
"--port",
|
|
74
|
+
"port",
|
|
75
|
+
default=8000,
|
|
76
|
+
type=int,
|
|
77
|
+
help=f"{RUNTIME_MSG.local} Port to bind to"
|
|
78
|
+
)
|
|
79
|
+
# --- Modal-only options ---
|
|
80
|
+
@click.option(
|
|
81
|
+
"--modal-app",
|
|
82
|
+
"modal_app_path",
|
|
83
|
+
cls=PromptedPathOption,
|
|
84
|
+
type=click.Path(
|
|
85
|
+
exists=True,
|
|
86
|
+
dir_okay=False,
|
|
87
|
+
file_okay=True,
|
|
88
|
+
path_type=Path
|
|
89
|
+
),
|
|
90
|
+
file_type=".py",
|
|
91
|
+
prompt_guard=lambda ctx: (ctx.params.get("runtime") != "local"),
|
|
92
|
+
help=f"{RUNTIME_MSG.modal} Enter the path to your Modal app",
|
|
93
|
+
)
|
|
94
|
+
@click.option(
|
|
95
|
+
"--name",
|
|
96
|
+
"task_app_name",
|
|
97
|
+
default=None,
|
|
98
|
+
help=f"{RUNTIME_MSG.modal} Override Modal app name"
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"--modal-mode",
|
|
102
|
+
"cmd_arg",
|
|
103
|
+
default="deploy",
|
|
104
|
+
help=f"{RUNTIME_MSG.modal} Mode: deploy or serve"
|
|
105
|
+
)
|
|
106
|
+
@click.option(
|
|
107
|
+
"--modal-cli",
|
|
108
|
+
"modal_bin_path",
|
|
109
|
+
type=click.Path(
|
|
110
|
+
dir_okay=False,
|
|
111
|
+
file_okay=True,
|
|
112
|
+
exists=True,
|
|
113
|
+
path_type=Path
|
|
114
|
+
),
|
|
115
|
+
default=None,
|
|
116
|
+
help=f"{RUNTIME_MSG.modal} Path to Modal CLI",
|
|
117
|
+
)
|
|
118
|
+
@click.option(
|
|
119
|
+
"--dry-run",
|
|
120
|
+
"dry_run",
|
|
121
|
+
is_flag=True,
|
|
122
|
+
help=f"{RUNTIME_MSG.modal} Print Modal command without executing"
|
|
123
|
+
)
|
|
124
|
+
@click.option(
|
|
125
|
+
"--env-file",
|
|
126
|
+
"env_file",
|
|
127
|
+
multiple=True,
|
|
128
|
+
type=click.Path(exists=True),
|
|
129
|
+
help="Path to .env file(s) to load"
|
|
130
|
+
)
|
|
131
|
+
def deploy_cmd(
|
|
132
|
+
task_app_path: Path,
|
|
133
|
+
runtime: RuntimeType,
|
|
134
|
+
env_file: tuple[str, ...],
|
|
135
|
+
**kwargs
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Deploy a task app to local or Modal runtime."""
|
|
138
|
+
match runtime:
|
|
139
|
+
case "local":
|
|
140
|
+
opts = {k: v for k, v in kwargs.items() if k in LOCAL_RUNTIME_OPTIONS}
|
|
141
|
+
deploy_uvicorn_app(LocalTaskAppConfig(**opts, task_app_path=task_app_path))
|
|
142
|
+
|
|
143
|
+
case "modal":
|
|
144
|
+
opts = {k: v for k, v in kwargs.items() if k in MODAL_RUNTIME_OPTIONS}
|
|
145
|
+
|
|
146
|
+
if "modal_app_path" not in opts or opts["modal_app_path"] is None:
|
|
147
|
+
raise click.ClickException("Modal app path required")
|
|
148
|
+
|
|
149
|
+
if opts["cmd_arg"] == "serve" and opts["dry_run"] is True:
|
|
150
|
+
raise click.ClickException("--modal-mode=serve cannot be combined with --dry-run")
|
|
151
|
+
|
|
152
|
+
modal_bin_path = opts.get("modal_bin_path") or get_default_modal_bin_path()
|
|
153
|
+
if not modal_bin_path:
|
|
154
|
+
raise click.ClickException(
|
|
155
|
+
"Modal CLI not found. Install the `modal` package or pass --modal-cli with its path."
|
|
156
|
+
)
|
|
157
|
+
if isinstance(modal_bin_path, str):
|
|
158
|
+
modal_bin_path = Path(modal_bin_path)
|
|
159
|
+
opts["modal_bin_path"] = modal_bin_path
|
|
160
|
+
deploy_modal_app(ModalTaskAppConfig(**opts, task_app_path=task_app_path))
|
|
161
|
+
|
|
162
|
+
__all__ = ["deploy_cmd"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .core import command, get_command
|
|
4
|
+
from .errors import (
|
|
5
|
+
EvalCliError,
|
|
6
|
+
EvalConfigNotFoundError,
|
|
7
|
+
EvalConfigParseError,
|
|
8
|
+
InvalidEvalConfigError,
|
|
9
|
+
MetadataFilterFormatError,
|
|
10
|
+
MetadataSQLExecutionError,
|
|
11
|
+
MetadataSQLResultError,
|
|
12
|
+
MissingEvalTableError,
|
|
13
|
+
NoSeedsMatchedError,
|
|
14
|
+
SeedParseError,
|
|
15
|
+
TaskInfoUnavailableError,
|
|
16
|
+
TomlUnavailableError,
|
|
17
|
+
)
|
|
18
|
+
from .validation import validate_eval_options
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"command",
|
|
22
|
+
"get_command",
|
|
23
|
+
"EvalCliError",
|
|
24
|
+
"TomlUnavailableError",
|
|
25
|
+
"EvalConfigNotFoundError",
|
|
26
|
+
"EvalConfigParseError",
|
|
27
|
+
"MissingEvalTableError",
|
|
28
|
+
"InvalidEvalConfigError",
|
|
29
|
+
"SeedParseError",
|
|
30
|
+
"MetadataFilterFormatError",
|
|
31
|
+
"TaskInfoUnavailableError",
|
|
32
|
+
"NoSeedsMatchedError",
|
|
33
|
+
"MetadataSQLExecutionError",
|
|
34
|
+
"MetadataSQLResultError",
|
|
35
|
+
"validate_eval_options",
|
|
36
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from synth_ai.cli.commands.eval.errors import (
|
|
4
|
+
EvalCliError,
|
|
5
|
+
EvalConfigNotFoundError,
|
|
6
|
+
EvalConfigParseError,
|
|
7
|
+
InvalidEvalConfigError,
|
|
8
|
+
MetadataFilterFormatError,
|
|
9
|
+
MetadataSQLExecutionError,
|
|
10
|
+
MetadataSQLResultError,
|
|
11
|
+
MissingEvalTableError,
|
|
12
|
+
NoSeedsMatchedError,
|
|
13
|
+
SeedParseError,
|
|
14
|
+
TaskInfoUnavailableError,
|
|
15
|
+
TomlUnavailableError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"EvalCliError",
|
|
20
|
+
"TomlUnavailableError",
|
|
21
|
+
"EvalConfigNotFoundError",
|
|
22
|
+
"EvalConfigParseError",
|
|
23
|
+
"MissingEvalTableError",
|
|
24
|
+
"InvalidEvalConfigError",
|
|
25
|
+
"SeedParseError",
|
|
26
|
+
"MetadataFilterFormatError",
|
|
27
|
+
"TaskInfoUnavailableError",
|
|
28
|
+
"NoSeedsMatchedError",
|
|
29
|
+
"MetadataSQLExecutionError",
|
|
30
|
+
"MetadataSQLResultError",
|
|
31
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .core import command, get_command
|
|
4
|
+
from .errors import (
|
|
5
|
+
FilterCliError,
|
|
6
|
+
FilterConfigNotFoundError,
|
|
7
|
+
FilterConfigParseError,
|
|
8
|
+
InvalidFilterConfigError,
|
|
9
|
+
MissingFilterTableError,
|
|
10
|
+
NoSessionsMatchedError,
|
|
11
|
+
NoTracesFoundError,
|
|
12
|
+
TomlUnavailableError,
|
|
13
|
+
)
|
|
14
|
+
from .validation import validate_filter_options
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"command",
|
|
18
|
+
"get_command",
|
|
19
|
+
"FilterCliError",
|
|
20
|
+
"TomlUnavailableError",
|
|
21
|
+
"FilterConfigNotFoundError",
|
|
22
|
+
"FilterConfigParseError",
|
|
23
|
+
"MissingFilterTableError",
|
|
24
|
+
"InvalidFilterConfigError",
|
|
25
|
+
"NoTracesFoundError",
|
|
26
|
+
"NoSessionsMatchedError",
|
|
27
|
+
"validate_filter_options",
|
|
28
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from synth_ai.cli.commands.filter.errors import (
|
|
4
|
+
FilterCliError,
|
|
5
|
+
FilterConfigNotFoundError,
|
|
6
|
+
FilterConfigParseError,
|
|
7
|
+
InvalidFilterConfigError,
|
|
8
|
+
MissingFilterTableError,
|
|
9
|
+
NoSessionsMatchedError,
|
|
10
|
+
NoTracesFoundError,
|
|
11
|
+
TomlUnavailableError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"FilterCliError",
|
|
16
|
+
"TomlUnavailableError",
|
|
17
|
+
"FilterConfigNotFoundError",
|
|
18
|
+
"FilterConfigParseError",
|
|
19
|
+
"MissingFilterTableError",
|
|
20
|
+
"InvalidFilterConfigError",
|
|
21
|
+
"NoTracesFoundError",
|
|
22
|
+
"NoSessionsMatchedError",
|
|
23
|
+
]
|
|
@@ -253,7 +253,7 @@ def view(url: str):
|
|
|
253
253
|
|
|
254
254
|
@cli.command()
|
|
255
255
|
@click.option("--db-file", default="synth_ai.db", help="Database file path")
|
|
256
|
-
@click.option("--sqld-port", default=8080, type=int, help="Port for sqld HTTP
|
|
256
|
+
@click.option("--sqld-port", default=8080, type=int, help="Port for sqld Hrana WebSocket interface (HTTP API will be port+1)")
|
|
257
257
|
@click.option("--env-port", default=8901, type=int, help="Port for environment service")
|
|
258
258
|
@click.option("--no-sqld", is_flag=True, help="Skip starting sqld daemon")
|
|
259
259
|
@click.option("--no-env", is_flag=True, help="Skip starting environment service")
|
|
@@ -298,32 +298,37 @@ def serve(
|
|
|
298
298
|
|
|
299
299
|
# Start sqld if requested
|
|
300
300
|
if not no_sqld:
|
|
301
|
+
hrana_port = sqld_port
|
|
302
|
+
http_port = sqld_port + 1
|
|
301
303
|
# Check if sqld is already running
|
|
302
304
|
try:
|
|
303
305
|
result = subprocess.run(
|
|
304
|
-
["pgrep", "-f", f"sqld
|
|
306
|
+
["pgrep", "-f", f"sqld.*(--hrana-listen-addr.*:{hrana_port}|--http-listen-addr.*:{http_port})"],
|
|
305
307
|
capture_output=True,
|
|
306
308
|
text=True,
|
|
307
309
|
)
|
|
308
310
|
if result.returncode == 0:
|
|
309
|
-
click.echo(f"✅ sqld already running on port {
|
|
311
|
+
click.echo(f"✅ sqld already running on hrana port {hrana_port}, HTTP API port {http_port}")
|
|
310
312
|
click.echo(f" Database: {db_file}")
|
|
311
|
-
click.echo(f"
|
|
313
|
+
click.echo(f" libsql: libsql://127.0.0.1:{hrana_port}")
|
|
314
|
+
click.echo(f" HTTP API: http://127.0.0.1:{http_port}")
|
|
312
315
|
else:
|
|
313
316
|
# Find or install sqld
|
|
314
317
|
sqld_bin = find_sqld_binary()
|
|
315
318
|
if not sqld_bin:
|
|
316
319
|
sqld_bin = install_sqld()
|
|
317
320
|
|
|
318
|
-
click.echo(f"🗄️ Starting sqld (local only) on port {
|
|
321
|
+
click.echo(f"🗄️ Starting sqld (local only) on hrana port {hrana_port}, HTTP API port {http_port}")
|
|
319
322
|
|
|
320
323
|
# Start sqld
|
|
321
324
|
sqld_cmd = [
|
|
322
325
|
sqld_bin,
|
|
323
326
|
"--db-path",
|
|
324
327
|
db_file,
|
|
328
|
+
"--hrana-listen-addr",
|
|
329
|
+
f"127.0.0.1:{hrana_port}",
|
|
325
330
|
"--http-listen-addr",
|
|
326
|
-
f"127.0.0.1:{
|
|
331
|
+
f"127.0.0.1:{http_port}",
|
|
327
332
|
]
|
|
328
333
|
|
|
329
334
|
# Create log file
|
|
@@ -346,7 +351,8 @@ def serve(
|
|
|
346
351
|
|
|
347
352
|
click.echo("✅ sqld started successfully!")
|
|
348
353
|
click.echo(f" Database: {db_file}")
|
|
349
|
-
click.echo(f"
|
|
354
|
+
click.echo(f" libsql: libsql://127.0.0.1:{hrana_port}")
|
|
355
|
+
click.echo(f" HTTP API: http://127.0.0.1:{http_port}")
|
|
350
356
|
click.echo(f" Log file: {os.path.abspath('sqld.log')}")
|
|
351
357
|
|
|
352
358
|
except FileNotFoundError:
|
|
@@ -417,7 +423,7 @@ def serve(
|
|
|
417
423
|
click.echo(f" Working directory: {os.getcwd()}")
|
|
418
424
|
click.echo("")
|
|
419
425
|
click.echo("🔄 Starting services...")
|
|
420
|
-
click.echo(f" - sqld daemon: http://127.0.0.1:{sqld_port}")
|
|
426
|
+
click.echo(f" - sqld daemon: libsql://127.0.0.1:{sqld_port} (HTTP API: http://127.0.0.1:{sqld_port + 1})")
|
|
421
427
|
click.echo(f" - Environment service: http://127.0.0.1:{env_port}")
|
|
422
428
|
click.echo("")
|
|
423
429
|
click.echo("💡 Tips:")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .core import command, get_command
|
|
4
|
+
from .errors import ModalServeCliError
|
|
5
|
+
from .validation import validate_modal_serve_options
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"command",
|
|
9
|
+
"get_command",
|
|
10
|
+
"ModalServeCliError",
|
|
11
|
+
"validate_modal_serve_options",
|
|
12
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from synth_ai.cli.task_apps import task_app_group
|
|
5
|
+
|
|
6
|
+
__all__ = ["command", "get_command"]
|
|
7
|
+
|
|
8
|
+
command = task_app_group.commands.get("modal-serve")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_command() -> click.Command:
|
|
12
|
+
if command is None:
|
|
13
|
+
raise RuntimeError("modal-serve command is not registered on task_app_group")
|
|
14
|
+
return command
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import MutableMapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
__all__ = ["validate_modal_serve_options"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_modal_serve_options(options: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
|
10
|
+
"""Validate parameters passed to the modal-serve CLI command."""
|
|
11
|
+
return options
|
synth_ai/cli/opencode.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from synth_ai.types import MODEL_NAMES, ModelName
|
|
6
|
+
from synth_ai.urls import BACKEND_URL_SYNTH_RESEARCH_BASE
|
|
7
|
+
from synth_ai.utils import (
|
|
8
|
+
create_and_write_json,
|
|
9
|
+
find_bin_path,
|
|
10
|
+
install_bin,
|
|
11
|
+
load_json_to_dict,
|
|
12
|
+
resolve_env_var,
|
|
13
|
+
verify_bin,
|
|
14
|
+
write_agents_md,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
CONFIG_PATH = Path.home() / ".config" / "opencode" / "opencode.json"
|
|
18
|
+
AUTH_PATH = Path.home() / ".local" / "share" / "opencode" / "auth.json"
|
|
19
|
+
SYNTH_PROVIDER_ID = "synth"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.command("opencode")
|
|
23
|
+
@click.option(
|
|
24
|
+
"--model",
|
|
25
|
+
"model_name",
|
|
26
|
+
type=str,
|
|
27
|
+
default=None
|
|
28
|
+
)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--force",
|
|
31
|
+
is_flag=True,
|
|
32
|
+
help="Prompt for API keys even if cached values exist."
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--url",
|
|
36
|
+
"override_url",
|
|
37
|
+
type=str,
|
|
38
|
+
default=None,
|
|
39
|
+
)
|
|
40
|
+
def opencode_cmd(
|
|
41
|
+
model_name: ModelName | None = None,
|
|
42
|
+
force: bool = False,
|
|
43
|
+
override_url: str | None = None
|
|
44
|
+
) -> None:
|
|
45
|
+
|
|
46
|
+
while True:
|
|
47
|
+
bin_path = find_bin_path("opencode")
|
|
48
|
+
if bin_path:
|
|
49
|
+
break
|
|
50
|
+
if not install_bin(
|
|
51
|
+
"OpenCode",
|
|
52
|
+
[
|
|
53
|
+
"brew install opencode",
|
|
54
|
+
"bun add -g opencode-ai",
|
|
55
|
+
"curl -fsSL https://opencode.ai/install | bash",
|
|
56
|
+
"npm i -g opencode-ai",
|
|
57
|
+
"paru -S opencode"
|
|
58
|
+
]
|
|
59
|
+
):
|
|
60
|
+
print("Failed to find your installed OpenCode")
|
|
61
|
+
print("Please install from: https://opencode.ai")
|
|
62
|
+
return
|
|
63
|
+
print(f"Using OpenCode at {bin_path}")
|
|
64
|
+
|
|
65
|
+
if not verify_bin(bin_path):
|
|
66
|
+
print("Failed to verify OpenCode is runnable")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
write_agents_md()
|
|
70
|
+
|
|
71
|
+
if model_name is not None:
|
|
72
|
+
if model_name not in MODEL_NAMES:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"model_name={model_name} is invalid. Valid values for model_name: {MODEL_NAMES}"
|
|
75
|
+
)
|
|
76
|
+
synth_api_key = resolve_env_var("SYNTH_API_KEY", override_process_env=force)
|
|
77
|
+
data = load_json_to_dict(AUTH_PATH)
|
|
78
|
+
good_entry = {
|
|
79
|
+
"type": "api",
|
|
80
|
+
"key": synth_api_key,
|
|
81
|
+
}
|
|
82
|
+
if data.get(SYNTH_PROVIDER_ID) != good_entry:
|
|
83
|
+
data[SYNTH_PROVIDER_ID] = good_entry
|
|
84
|
+
create_and_write_json(AUTH_PATH, data)
|
|
85
|
+
config = load_json_to_dict(CONFIG_PATH)
|
|
86
|
+
config.setdefault("$schema", "https://opencode.ai/config.json")
|
|
87
|
+
if override_url:
|
|
88
|
+
url = override_url
|
|
89
|
+
print("Using override URL:", url)
|
|
90
|
+
else:
|
|
91
|
+
url = BACKEND_URL_SYNTH_RESEARCH_BASE
|
|
92
|
+
provider_section = config.setdefault("provider", {})
|
|
93
|
+
synth_provider = provider_section.setdefault(SYNTH_PROVIDER_ID, {})
|
|
94
|
+
synth_provider["npm"] = "@ai-sdk/openai-compatible"
|
|
95
|
+
synth_provider.setdefault("name", "Synth")
|
|
96
|
+
models = synth_provider.setdefault("models", {})
|
|
97
|
+
models.setdefault(model_name, {})
|
|
98
|
+
options = synth_provider.setdefault("options", {})
|
|
99
|
+
options["baseURL"] = url
|
|
100
|
+
full_model_name = f"{SYNTH_PROVIDER_ID}/{model_name}"
|
|
101
|
+
config["model"] = full_model_name
|
|
102
|
+
create_and_write_json(CONFIG_PATH, config)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
subprocess.run([str(bin_path)], check=True)
|
|
106
|
+
except subprocess.CalledProcessError:
|
|
107
|
+
print("Failed to launch OpenCode")
|
synth_ai/cli/root.py
CHANGED
|
@@ -158,7 +158,7 @@ def cli():
|
|
|
158
158
|
|
|
159
159
|
@cli.command()
|
|
160
160
|
@click.option("--db-file", default="traces/v3/synth_ai.db", help="Database file path")
|
|
161
|
-
@click.option("--sqld-port", default=8080, type=int, help="Port for sqld HTTP
|
|
161
|
+
@click.option("--sqld-port", default=8080, type=int, help="Port for sqld Hrana WebSocket interface (HTTP API will be port+1)")
|
|
162
162
|
@click.option("--env-port", default=8901, type=int, help="Port for environment service")
|
|
163
163
|
@click.option("--no-sqld", is_flag=True, help="Skip starting sqld daemon")
|
|
164
164
|
@click.option("--no-env", is_flag=True, help="Skip starting environment service")
|
|
@@ -204,21 +204,25 @@ def serve_deprecated(
|
|
|
204
204
|
|
|
205
205
|
if not no_sqld:
|
|
206
206
|
try:
|
|
207
|
+
hrana_port = sqld_port
|
|
208
|
+
http_port = sqld_port + 1
|
|
207
209
|
result = subprocess.run(
|
|
208
|
-
["pgrep", "-f", f"sqld
|
|
210
|
+
["pgrep", "-f", f"sqld.*(--hrana-listen-addr.*:{hrana_port}|--http-listen-addr.*:{http_port})"],
|
|
209
211
|
capture_output=True,
|
|
210
212
|
text=True,
|
|
211
213
|
)
|
|
212
214
|
if result.returncode != 0:
|
|
213
215
|
sqld_bin = find_sqld_binary() or install_sqld()
|
|
214
|
-
click.echo(f"🗄️ Starting sqld (local only) on port {
|
|
216
|
+
click.echo(f"🗄️ Starting sqld (local only) on hrana port {hrana_port}, HTTP API port {http_port}")
|
|
215
217
|
proc = subprocess.Popen(
|
|
216
218
|
[
|
|
217
219
|
sqld_bin,
|
|
218
220
|
"--db-path",
|
|
219
221
|
db_file,
|
|
222
|
+
"--hrana-listen-addr",
|
|
223
|
+
f"127.0.0.1:{hrana_port}",
|
|
220
224
|
"--http-listen-addr",
|
|
221
|
-
f"127.0.0.1:{
|
|
225
|
+
f"127.0.0.1:{http_port}",
|
|
222
226
|
],
|
|
223
227
|
stdout=open("sqld.log", "w"), # noqa: SIM115
|
|
224
228
|
stderr=subprocess.STDOUT,
|
|
@@ -274,7 +278,7 @@ def serve_deprecated(
|
|
|
274
278
|
click.echo(f" Working directory: {os.getcwd()}")
|
|
275
279
|
click.echo("")
|
|
276
280
|
click.echo("🔄 Starting services...")
|
|
277
|
-
click.echo(f" - sqld daemon: http://127.0.0.1:{sqld_port}")
|
|
281
|
+
click.echo(f" - sqld daemon: libsql://127.0.0.1:{sqld_port} (HTTP API: http://127.0.0.1:{sqld_port + 1})")
|
|
278
282
|
click.echo(f" - Environment service: http://127.0.0.1:{env_port}")
|
|
279
283
|
click.echo("")
|
|
280
284
|
click.echo("💡 Tips:")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .core import command, get_command
|
|
4
|
+
from .errors import ServeCliError
|
|
5
|
+
from .validation import validate_serve_options
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"command",
|
|
9
|
+
"get_command",
|
|
10
|
+
"ServeCliError",
|
|
11
|
+
"validate_serve_options",
|
|
12
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from synth_ai.cli.task_apps import task_app_group
|
|
5
|
+
|
|
6
|
+
__all__ = ["command", "get_command"]
|
|
7
|
+
|
|
8
|
+
command = task_app_group.commands.get("serve")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_command() -> click.Command:
|
|
12
|
+
if command is None:
|
|
13
|
+
raise RuntimeError("Serve command is not registered on task_app_group")
|
|
14
|
+
return command
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import MutableMapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
__all__ = ["validate_serve_options"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_serve_options(options: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
|
10
|
+
"""Validate parameters passed to the serve CLI command."""
|
|
11
|
+
return options
|