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
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""`synth jobs` command group implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ..client import StatusAPIClient
|
|
11
|
+
from ..errors import StatusAPIError
|
|
12
|
+
from ..formatters import (
|
|
13
|
+
console,
|
|
14
|
+
events_panel,
|
|
15
|
+
job_panel,
|
|
16
|
+
jobs_table,
|
|
17
|
+
metrics_table,
|
|
18
|
+
print_json,
|
|
19
|
+
runs_table,
|
|
20
|
+
)
|
|
21
|
+
from ..utils import bail, common_options, parse_relative_time, resolve_context_config
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group("jobs", help="Manage training jobs.")
|
|
25
|
+
@click.pass_context
|
|
26
|
+
def jobs_group(ctx: click.Context) -> None: # pragma: no cover - Click wiring
|
|
27
|
+
ctx.ensure_object(dict)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _print_or_json(items: Any, output_json: bool) -> None:
|
|
31
|
+
if output_json:
|
|
32
|
+
print_json(items)
|
|
33
|
+
elif isinstance(items, list):
|
|
34
|
+
console.print(jobs_table(items))
|
|
35
|
+
else:
|
|
36
|
+
console.print(job_panel(items))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@jobs_group.command("list")
|
|
40
|
+
@common_options()
|
|
41
|
+
@click.option(
|
|
42
|
+
"--status",
|
|
43
|
+
type=click.Choice(["queued", "running", "succeeded", "failed", "cancelled"]),
|
|
44
|
+
help="Filter by job status.",
|
|
45
|
+
)
|
|
46
|
+
@click.option(
|
|
47
|
+
"--type",
|
|
48
|
+
"job_type",
|
|
49
|
+
type=click.Choice(["sft_offline", "sft_online", "rl_online", "dpo", "sft"]),
|
|
50
|
+
help="Filter by training job type.",
|
|
51
|
+
)
|
|
52
|
+
@click.option("--created-after", help="Filter by creation date (ISO8601 or relative like '24h').")
|
|
53
|
+
@click.option("--limit", default=20, show_default=True, type=int)
|
|
54
|
+
@click.option("--json", "output_json", is_flag=True, help="Emit raw JSON.")
|
|
55
|
+
@click.pass_context
|
|
56
|
+
def list_jobs(
|
|
57
|
+
ctx: click.Context,
|
|
58
|
+
base_url: str | None,
|
|
59
|
+
api_key: str | None,
|
|
60
|
+
timeout: float,
|
|
61
|
+
status: str | None,
|
|
62
|
+
job_type: str | None,
|
|
63
|
+
created_after: str | None,
|
|
64
|
+
limit: int,
|
|
65
|
+
output_json: bool,
|
|
66
|
+
) -> None:
|
|
67
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
68
|
+
created_filter = parse_relative_time(created_after)
|
|
69
|
+
|
|
70
|
+
async def _run() -> None:
|
|
71
|
+
try:
|
|
72
|
+
async with StatusAPIClient(cfg) as client:
|
|
73
|
+
jobs = await client.list_jobs(
|
|
74
|
+
status=status,
|
|
75
|
+
job_type=job_type,
|
|
76
|
+
created_after=created_filter,
|
|
77
|
+
limit=limit,
|
|
78
|
+
)
|
|
79
|
+
_print_or_json(jobs, output_json)
|
|
80
|
+
except StatusAPIError as exc:
|
|
81
|
+
bail(f"Backend error: {exc}")
|
|
82
|
+
|
|
83
|
+
asyncio.run(_run())
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@jobs_group.command("get")
|
|
87
|
+
@common_options()
|
|
88
|
+
@click.argument("job_id")
|
|
89
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
90
|
+
@click.pass_context
|
|
91
|
+
def get_job(
|
|
92
|
+
ctx: click.Context,
|
|
93
|
+
base_url: str | None,
|
|
94
|
+
api_key: str | None,
|
|
95
|
+
timeout: float,
|
|
96
|
+
job_id: str,
|
|
97
|
+
output_json: bool,
|
|
98
|
+
) -> None:
|
|
99
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
100
|
+
|
|
101
|
+
async def _run() -> None:
|
|
102
|
+
try:
|
|
103
|
+
async with StatusAPIClient(cfg) as client:
|
|
104
|
+
job = await client.get_job(job_id)
|
|
105
|
+
_print_or_json(job, output_json)
|
|
106
|
+
except StatusAPIError as exc:
|
|
107
|
+
bail(f"Backend error: {exc}")
|
|
108
|
+
|
|
109
|
+
asyncio.run(_run())
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@jobs_group.command("history")
|
|
113
|
+
@common_options()
|
|
114
|
+
@click.argument("job_id")
|
|
115
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
116
|
+
@click.pass_context
|
|
117
|
+
def job_history(
|
|
118
|
+
ctx: click.Context,
|
|
119
|
+
base_url: str | None,
|
|
120
|
+
api_key: str | None,
|
|
121
|
+
timeout: float,
|
|
122
|
+
job_id: str,
|
|
123
|
+
output_json: bool,
|
|
124
|
+
) -> None:
|
|
125
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
126
|
+
|
|
127
|
+
async def _run() -> None:
|
|
128
|
+
try:
|
|
129
|
+
async with StatusAPIClient(cfg) as client:
|
|
130
|
+
runs = await client.list_job_runs(job_id)
|
|
131
|
+
if output_json:
|
|
132
|
+
print_json(runs)
|
|
133
|
+
else:
|
|
134
|
+
console.print(runs_table(runs))
|
|
135
|
+
except StatusAPIError as exc:
|
|
136
|
+
bail(f"Backend error: {exc}")
|
|
137
|
+
|
|
138
|
+
asyncio.run(_run())
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@jobs_group.command("timeline")
|
|
142
|
+
@common_options()
|
|
143
|
+
@click.argument("job_id")
|
|
144
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
145
|
+
@click.pass_context
|
|
146
|
+
def job_timeline(
|
|
147
|
+
ctx: click.Context,
|
|
148
|
+
base_url: str | None,
|
|
149
|
+
api_key: str | None,
|
|
150
|
+
timeout: float,
|
|
151
|
+
job_id: str,
|
|
152
|
+
output_json: bool,
|
|
153
|
+
) -> None:
|
|
154
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
155
|
+
|
|
156
|
+
async def _run() -> None:
|
|
157
|
+
try:
|
|
158
|
+
async with StatusAPIClient(cfg) as client:
|
|
159
|
+
timeline = await client.get_job_timeline(job_id)
|
|
160
|
+
if output_json:
|
|
161
|
+
print_json(timeline)
|
|
162
|
+
else:
|
|
163
|
+
console.print(events_panel(timeline))
|
|
164
|
+
except StatusAPIError as exc:
|
|
165
|
+
bail(f"Backend error: {exc}")
|
|
166
|
+
|
|
167
|
+
asyncio.run(_run())
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@jobs_group.command("metrics")
|
|
171
|
+
@common_options()
|
|
172
|
+
@click.argument("job_id")
|
|
173
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
174
|
+
@click.pass_context
|
|
175
|
+
def job_metrics(
|
|
176
|
+
ctx: click.Context,
|
|
177
|
+
base_url: str | None,
|
|
178
|
+
api_key: str | None,
|
|
179
|
+
timeout: float,
|
|
180
|
+
job_id: str,
|
|
181
|
+
output_json: bool,
|
|
182
|
+
) -> None:
|
|
183
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
184
|
+
|
|
185
|
+
async def _run() -> None:
|
|
186
|
+
try:
|
|
187
|
+
async with StatusAPIClient(cfg) as client:
|
|
188
|
+
metrics = await client.get_job_metrics(job_id)
|
|
189
|
+
if output_json:
|
|
190
|
+
print_json(metrics)
|
|
191
|
+
else:
|
|
192
|
+
console.print(metrics_table(metrics))
|
|
193
|
+
except StatusAPIError as exc:
|
|
194
|
+
bail(f"Backend error: {exc}")
|
|
195
|
+
|
|
196
|
+
asyncio.run(_run())
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@jobs_group.command("config")
|
|
200
|
+
@common_options()
|
|
201
|
+
@click.argument("job_id")
|
|
202
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
203
|
+
@click.pass_context
|
|
204
|
+
def job_config(
|
|
205
|
+
ctx: click.Context,
|
|
206
|
+
base_url: str | None,
|
|
207
|
+
api_key: str | None,
|
|
208
|
+
timeout: float,
|
|
209
|
+
job_id: str,
|
|
210
|
+
output_json: bool,
|
|
211
|
+
) -> None:
|
|
212
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
213
|
+
|
|
214
|
+
async def _run() -> None:
|
|
215
|
+
try:
|
|
216
|
+
async with StatusAPIClient(cfg) as client:
|
|
217
|
+
config = await client.get_job_config(job_id)
|
|
218
|
+
if output_json:
|
|
219
|
+
print_json(config)
|
|
220
|
+
else:
|
|
221
|
+
console.print(job_panel({"job_id": job_id, "config": config}))
|
|
222
|
+
except StatusAPIError as exc:
|
|
223
|
+
bail(f"Backend error: {exc}")
|
|
224
|
+
|
|
225
|
+
asyncio.run(_run())
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@jobs_group.command("status")
|
|
229
|
+
@common_options()
|
|
230
|
+
@click.argument("job_id")
|
|
231
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
232
|
+
@click.pass_context
|
|
233
|
+
def job_status(
|
|
234
|
+
ctx: click.Context,
|
|
235
|
+
base_url: str | None,
|
|
236
|
+
api_key: str | None,
|
|
237
|
+
timeout: float,
|
|
238
|
+
job_id: str,
|
|
239
|
+
output_json: bool,
|
|
240
|
+
) -> None:
|
|
241
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
242
|
+
|
|
243
|
+
async def _run() -> None:
|
|
244
|
+
try:
|
|
245
|
+
async with StatusAPIClient(cfg) as client:
|
|
246
|
+
status = await client.get_job_status(job_id)
|
|
247
|
+
if output_json:
|
|
248
|
+
print_json(status)
|
|
249
|
+
else:
|
|
250
|
+
console.print(f"[bold]{job_id}[/bold]: {status.get('status', 'unknown')}")
|
|
251
|
+
except StatusAPIError as exc:
|
|
252
|
+
bail(f"Backend error: {exc}")
|
|
253
|
+
|
|
254
|
+
asyncio.run(_run())
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@jobs_group.command("cancel")
|
|
258
|
+
@common_options()
|
|
259
|
+
@click.argument("job_id")
|
|
260
|
+
@click.pass_context
|
|
261
|
+
def cancel_job(
|
|
262
|
+
ctx: click.Context,
|
|
263
|
+
base_url: str | None,
|
|
264
|
+
api_key: str | None,
|
|
265
|
+
timeout: float,
|
|
266
|
+
job_id: str,
|
|
267
|
+
) -> None:
|
|
268
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
269
|
+
|
|
270
|
+
async def _run() -> None:
|
|
271
|
+
try:
|
|
272
|
+
async with StatusAPIClient(cfg) as client:
|
|
273
|
+
resp = await client.cancel_job(job_id)
|
|
274
|
+
console.print(resp.get("message") or f"[yellow]Cancellation requested for {job_id}[/yellow]")
|
|
275
|
+
except StatusAPIError as exc:
|
|
276
|
+
bail(f"Backend error: {exc}")
|
|
277
|
+
|
|
278
|
+
asyncio.run(_run())
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@jobs_group.command("logs")
|
|
282
|
+
@common_options()
|
|
283
|
+
@click.argument("job_id")
|
|
284
|
+
@click.option("--since", help="Only show events emitted after the provided timestamp/relative offset.")
|
|
285
|
+
@click.option("--tail", type=int, help="Show only the last N events.")
|
|
286
|
+
@click.option("--follow/--no-follow", default=False, help="Poll for new events.")
|
|
287
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
288
|
+
@click.pass_context
|
|
289
|
+
def job_logs(
|
|
290
|
+
ctx: click.Context,
|
|
291
|
+
base_url: str | None,
|
|
292
|
+
api_key: str | None,
|
|
293
|
+
timeout: float,
|
|
294
|
+
job_id: str,
|
|
295
|
+
since: str | None,
|
|
296
|
+
tail: int | None,
|
|
297
|
+
follow: bool,
|
|
298
|
+
output_json: bool,
|
|
299
|
+
) -> None:
|
|
300
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
301
|
+
since_filter = parse_relative_time(since)
|
|
302
|
+
|
|
303
|
+
async def _loop() -> None:
|
|
304
|
+
seen_ids: set[str] = set()
|
|
305
|
+
cursor: str | None = None
|
|
306
|
+
try:
|
|
307
|
+
async with StatusAPIClient(cfg) as client:
|
|
308
|
+
while True:
|
|
309
|
+
events = await client.get_job_events(
|
|
310
|
+
job_id,
|
|
311
|
+
since=cursor or since_filter,
|
|
312
|
+
limit=tail,
|
|
313
|
+
after=cursor,
|
|
314
|
+
)
|
|
315
|
+
new_events: list[dict[str, Any]] = []
|
|
316
|
+
for event in events:
|
|
317
|
+
event_id = str(event.get("event_id") or event.get("id") or event.get("timestamp"))
|
|
318
|
+
if event_id in seen_ids:
|
|
319
|
+
continue
|
|
320
|
+
seen_ids.add(event_id)
|
|
321
|
+
new_events.append(event)
|
|
322
|
+
if new_events:
|
|
323
|
+
cursor = str(new_events[-1].get("event_id") or new_events[-1].get("id") or "")
|
|
324
|
+
if output_json:
|
|
325
|
+
print_json(new_events)
|
|
326
|
+
else:
|
|
327
|
+
console.print(events_panel(new_events))
|
|
328
|
+
if not follow:
|
|
329
|
+
break
|
|
330
|
+
await asyncio.sleep(2.0)
|
|
331
|
+
except StatusAPIError as exc:
|
|
332
|
+
bail(f"Backend error: {exc}")
|
|
333
|
+
|
|
334
|
+
asyncio.run(_loop())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""`synth models` command group."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.json import JSON
|
|
9
|
+
|
|
10
|
+
from ..client import StatusAPIClient
|
|
11
|
+
from ..errors import StatusAPIError
|
|
12
|
+
from ..formatters import console, models_table, print_json
|
|
13
|
+
from ..utils import bail, common_options, resolve_context_config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group("models", help="Inspect fine-tuned models.")
|
|
17
|
+
@click.pass_context
|
|
18
|
+
def models_group(ctx: click.Context) -> None: # pragma: no cover - Click wiring
|
|
19
|
+
ctx.ensure_object(dict)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@models_group.command("list")
|
|
23
|
+
@common_options()
|
|
24
|
+
@click.option("--limit", type=int, default=None, help="Maximum number of models to return.")
|
|
25
|
+
@click.option("--type", "model_type", type=click.Choice(["rl", "sft"]), default=None, help="Filter by model type.")
|
|
26
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
27
|
+
@click.pass_context
|
|
28
|
+
def list_models(
|
|
29
|
+
ctx: click.Context,
|
|
30
|
+
base_url: str | None,
|
|
31
|
+
api_key: str | None,
|
|
32
|
+
timeout: float,
|
|
33
|
+
limit: int | None,
|
|
34
|
+
model_type: str | None,
|
|
35
|
+
output_json: bool,
|
|
36
|
+
) -> None:
|
|
37
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
38
|
+
|
|
39
|
+
async def _run() -> None:
|
|
40
|
+
try:
|
|
41
|
+
async with StatusAPIClient(cfg) as client:
|
|
42
|
+
models = await client.list_models(limit=limit, model_type=model_type)
|
|
43
|
+
if output_json:
|
|
44
|
+
print_json(models)
|
|
45
|
+
else:
|
|
46
|
+
console.print(models_table(models))
|
|
47
|
+
except StatusAPIError as exc:
|
|
48
|
+
bail(f"Backend error: {exc}")
|
|
49
|
+
|
|
50
|
+
asyncio.run(_run())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@models_group.command("get")
|
|
54
|
+
@common_options()
|
|
55
|
+
@click.argument("model_id")
|
|
56
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
57
|
+
@click.pass_context
|
|
58
|
+
def get_model(
|
|
59
|
+
ctx: click.Context,
|
|
60
|
+
base_url: str | None,
|
|
61
|
+
api_key: str | None,
|
|
62
|
+
timeout: float,
|
|
63
|
+
model_id: str,
|
|
64
|
+
output_json: bool,
|
|
65
|
+
) -> None:
|
|
66
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
67
|
+
|
|
68
|
+
async def _run() -> None:
|
|
69
|
+
try:
|
|
70
|
+
async with StatusAPIClient(cfg) as client:
|
|
71
|
+
model = await client.get_model(model_id)
|
|
72
|
+
if output_json:
|
|
73
|
+
print_json(model)
|
|
74
|
+
else:
|
|
75
|
+
console.print(JSON.from_data(model))
|
|
76
|
+
except StatusAPIError as exc:
|
|
77
|
+
bail(f"Backend error: {exc}")
|
|
78
|
+
|
|
79
|
+
asyncio.run(_run())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
from synth_ai.pricing.model_pricing import MODEL_PRICES
|
|
6
|
+
|
|
7
|
+
from ..formatters import console
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command("pricing", help="List supported provider/model rates (SDK static table).")
|
|
11
|
+
def pricing_command() -> None:
|
|
12
|
+
table = Table(title="Supported Models and Rates (USD/token)")
|
|
13
|
+
table.add_column("Provider", style="cyan", no_wrap=True)
|
|
14
|
+
table.add_column("Model", style="magenta")
|
|
15
|
+
table.add_column("Input USD", justify="right")
|
|
16
|
+
table.add_column("Output USD", justify="right")
|
|
17
|
+
for provider, models in MODEL_PRICES.items():
|
|
18
|
+
for model, rates in models.items():
|
|
19
|
+
table.add_row(provider, model, f"{rates.input_usd:.9f}", f"{rates.output_usd:.9f}")
|
|
20
|
+
console.print(table)
|
|
21
|
+
|
|
22
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""`synth runs` command group."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..client import StatusAPIClient
|
|
10
|
+
from ..errors import StatusAPIError
|
|
11
|
+
from ..formatters import console, events_panel, print_json, runs_table
|
|
12
|
+
from ..utils import bail, common_options, parse_relative_time, resolve_context_config
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group("runs", help="Inspect individual job runs/attempts.")
|
|
16
|
+
@click.pass_context
|
|
17
|
+
def runs_group(ctx: click.Context) -> None: # pragma: no cover - Click wiring
|
|
18
|
+
ctx.ensure_object(dict)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@runs_group.command("list")
|
|
22
|
+
@common_options()
|
|
23
|
+
@click.argument("job_id")
|
|
24
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
25
|
+
@click.pass_context
|
|
26
|
+
def list_runs(
|
|
27
|
+
ctx: click.Context,
|
|
28
|
+
base_url: str | None,
|
|
29
|
+
api_key: str | None,
|
|
30
|
+
timeout: float,
|
|
31
|
+
job_id: str,
|
|
32
|
+
output_json: bool,
|
|
33
|
+
) -> None:
|
|
34
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
35
|
+
|
|
36
|
+
async def _run() -> None:
|
|
37
|
+
try:
|
|
38
|
+
async with StatusAPIClient(cfg) as client:
|
|
39
|
+
runs = await client.list_job_runs(job_id)
|
|
40
|
+
if output_json:
|
|
41
|
+
print_json(runs)
|
|
42
|
+
else:
|
|
43
|
+
console.print(runs_table(runs))
|
|
44
|
+
except StatusAPIError as exc:
|
|
45
|
+
bail(f"Backend error: {exc}")
|
|
46
|
+
|
|
47
|
+
asyncio.run(_run())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@runs_group.command("logs")
|
|
51
|
+
@common_options()
|
|
52
|
+
@click.argument("job_id")
|
|
53
|
+
@click.option("--run", "run_id", required=True, help="Run identifier (number or ID) to inspect.")
|
|
54
|
+
@click.option("--since", help="Filter events after the supplied timestamp/relative offset.")
|
|
55
|
+
@click.option("--json", "output_json", is_flag=True)
|
|
56
|
+
@click.pass_context
|
|
57
|
+
def run_logs(
|
|
58
|
+
ctx: click.Context,
|
|
59
|
+
base_url: str | None,
|
|
60
|
+
api_key: str | None,
|
|
61
|
+
timeout: float,
|
|
62
|
+
job_id: str,
|
|
63
|
+
run_id: str,
|
|
64
|
+
since: str | None,
|
|
65
|
+
output_json: bool,
|
|
66
|
+
) -> None:
|
|
67
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
68
|
+
since_filter = parse_relative_time(since)
|
|
69
|
+
|
|
70
|
+
async def _run() -> None:
|
|
71
|
+
try:
|
|
72
|
+
async with StatusAPIClient(cfg) as client:
|
|
73
|
+
events = await client.get_job_events(job_id, since=since_filter, run_id=run_id)
|
|
74
|
+
if output_json:
|
|
75
|
+
print_json(events)
|
|
76
|
+
else:
|
|
77
|
+
console.print(events_panel(events))
|
|
78
|
+
except StatusAPIError as exc:
|
|
79
|
+
bail(f"Backend error: {exc}")
|
|
80
|
+
|
|
81
|
+
asyncio.run(_run())
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""`synth status summary` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..client import StatusAPIClient
|
|
10
|
+
from ..errors import StatusAPIError
|
|
11
|
+
from ..formatters import console, files_table, jobs_table, models_table
|
|
12
|
+
from ..utils import common_options, resolve_context_config
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("summary", help="Show a condensed overview of recent jobs, models, and files.")
|
|
16
|
+
@common_options()
|
|
17
|
+
@click.option("--limit", default=5, show_default=True, type=int, help="Rows per section.")
|
|
18
|
+
@click.pass_context
|
|
19
|
+
def summary_command(
|
|
20
|
+
ctx: click.Context,
|
|
21
|
+
base_url: str | None,
|
|
22
|
+
api_key: str | None,
|
|
23
|
+
timeout: float,
|
|
24
|
+
limit: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
cfg = resolve_context_config(ctx, base_url=base_url, api_key=api_key, timeout=timeout)
|
|
27
|
+
|
|
28
|
+
async def _run() -> tuple[list[dict[str, object]], list[dict[str, object]], list[dict[str, object]]]:
|
|
29
|
+
async with StatusAPIClient(cfg) as client:
|
|
30
|
+
try:
|
|
31
|
+
jobs = await client.list_jobs(limit=limit)
|
|
32
|
+
except StatusAPIError:
|
|
33
|
+
jobs = []
|
|
34
|
+
try:
|
|
35
|
+
models = await client.list_models(limit=limit)
|
|
36
|
+
except StatusAPIError:
|
|
37
|
+
models = []
|
|
38
|
+
try:
|
|
39
|
+
files = await client.list_files(limit=limit)
|
|
40
|
+
except StatusAPIError:
|
|
41
|
+
files = []
|
|
42
|
+
return jobs, models, files
|
|
43
|
+
|
|
44
|
+
jobs, models, files = asyncio.run(_run())
|
|
45
|
+
console.print(jobs_table(jobs[:limit]))
|
|
46
|
+
console.print(models_table(models[:limit]))
|
|
47
|
+
console.print(files_table(files[:limit]))
|