synth-ai 0.4.1__py3-none-any.whl → 0.4.4__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.
- synth_ai/__init__.py +13 -13
- synth_ai/cli/__init__.py +6 -15
- synth_ai/cli/commands/eval/__init__.py +6 -15
- synth_ai/cli/commands/eval/config.py +338 -0
- synth_ai/cli/commands/eval/core.py +236 -1091
- synth_ai/cli/commands/eval/runner.py +704 -0
- synth_ai/cli/commands/eval/validation.py +44 -117
- synth_ai/cli/commands/filter/core.py +7 -7
- synth_ai/cli/commands/filter/validation.py +2 -2
- synth_ai/cli/commands/smoke/core.py +7 -17
- synth_ai/cli/commands/status/__init__.py +1 -64
- synth_ai/cli/commands/status/client.py +50 -151
- synth_ai/cli/commands/status/config.py +3 -83
- synth_ai/cli/commands/status/errors.py +4 -13
- synth_ai/cli/commands/status/subcommands/__init__.py +2 -8
- synth_ai/cli/commands/status/subcommands/config.py +13 -0
- synth_ai/cli/commands/status/subcommands/files.py +18 -63
- synth_ai/cli/commands/status/subcommands/jobs.py +28 -311
- synth_ai/cli/commands/status/subcommands/models.py +18 -62
- synth_ai/cli/commands/status/subcommands/runs.py +16 -63
- synth_ai/cli/commands/status/subcommands/session.py +67 -172
- synth_ai/cli/commands/status/subcommands/summary.py +24 -32
- synth_ai/cli/commands/status/subcommands/utils.py +41 -0
- synth_ai/cli/commands/status/utils.py +16 -107
- synth_ai/cli/commands/train/__init__.py +18 -20
- synth_ai/cli/commands/train/errors.py +3 -3
- synth_ai/cli/commands/train/prompt_learning_validation.py +15 -16
- synth_ai/cli/commands/train/validation.py +7 -7
- synth_ai/cli/commands/train/{judge_schemas.py → verifier_schemas.py} +33 -34
- synth_ai/cli/commands/train/verifier_validation.py +235 -0
- synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +0 -1
- synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +2 -6
- synth_ai/cli/demo_apps/math/config.toml +0 -1
- synth_ai/cli/demo_apps/math/modal_task_app.py +2 -6
- synth_ai/cli/demo_apps/mipro/task_app.py +25 -47
- synth_ai/cli/lib/apps/task_app.py +12 -13
- synth_ai/cli/lib/task_app_discovery.py +6 -6
- synth_ai/cli/lib/train_cfgs.py +10 -10
- synth_ai/cli/task_apps/__init__.py +11 -0
- synth_ai/cli/task_apps/commands.py +7 -15
- synth_ai/core/env.py +12 -1
- synth_ai/core/errors.py +1 -2
- synth_ai/core/integrations/cloudflare.py +209 -33
- synth_ai/core/tracing_v3/abstractions.py +46 -0
- synth_ai/data/__init__.py +3 -30
- synth_ai/data/enums.py +1 -20
- synth_ai/data/rewards.py +100 -3
- synth_ai/products/graph_evolve/__init__.py +1 -2
- synth_ai/products/graph_evolve/config.py +16 -16
- synth_ai/products/graph_evolve/converters/__init__.py +3 -3
- synth_ai/products/graph_evolve/converters/openai_sft.py +7 -7
- synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +1 -1
- synth_ai/products/graph_gepa/__init__.py +23 -0
- synth_ai/products/graph_gepa/converters/__init__.py +19 -0
- synth_ai/products/graph_gepa/converters/openai_sft.py +29 -0
- synth_ai/sdk/__init__.py +45 -35
- synth_ai/sdk/api/eval/__init__.py +33 -0
- synth_ai/sdk/api/eval/job.py +732 -0
- synth_ai/sdk/api/research_agent/__init__.py +276 -66
- synth_ai/sdk/api/train/builders.py +181 -0
- synth_ai/sdk/api/train/cli.py +41 -33
- synth_ai/sdk/api/train/configs/__init__.py +6 -4
- synth_ai/sdk/api/train/configs/prompt_learning.py +127 -33
- synth_ai/sdk/api/train/configs/rl.py +264 -16
- synth_ai/sdk/api/train/configs/sft.py +165 -1
- synth_ai/sdk/api/train/graph_validators.py +12 -12
- synth_ai/sdk/api/train/graphgen.py +169 -51
- synth_ai/sdk/api/train/graphgen_models.py +95 -45
- synth_ai/sdk/api/train/local_api.py +10 -0
- synth_ai/sdk/api/train/pollers.py +36 -0
- synth_ai/sdk/api/train/prompt_learning.py +390 -60
- synth_ai/sdk/api/train/rl.py +41 -5
- synth_ai/sdk/api/train/sft.py +2 -0
- synth_ai/sdk/api/train/task_app.py +20 -0
- synth_ai/sdk/api/train/validators.py +17 -17
- synth_ai/sdk/graphs/completions.py +239 -33
- synth_ai/sdk/{judging/schemas.py → graphs/verifier_schemas.py} +23 -23
- synth_ai/sdk/learning/__init__.py +35 -5
- synth_ai/sdk/learning/context_learning_client.py +531 -0
- synth_ai/sdk/learning/context_learning_types.py +294 -0
- synth_ai/sdk/learning/prompt_learning_client.py +1 -1
- synth_ai/sdk/learning/prompt_learning_types.py +2 -1
- synth_ai/sdk/learning/rl/__init__.py +0 -4
- synth_ai/sdk/learning/rl/contracts.py +0 -4
- synth_ai/sdk/localapi/__init__.py +40 -0
- synth_ai/sdk/localapi/apps/__init__.py +28 -0
- synth_ai/sdk/localapi/client.py +10 -0
- synth_ai/sdk/localapi/contracts.py +10 -0
- synth_ai/sdk/localapi/helpers.py +519 -0
- synth_ai/sdk/localapi/rollouts.py +93 -0
- synth_ai/sdk/localapi/server.py +29 -0
- synth_ai/sdk/localapi/template.py +49 -0
- synth_ai/sdk/streaming/handlers.py +6 -6
- synth_ai/sdk/streaming/streamer.py +10 -6
- synth_ai/sdk/task/__init__.py +18 -5
- synth_ai/sdk/task/apps/__init__.py +37 -1
- synth_ai/sdk/task/client.py +9 -1
- synth_ai/sdk/task/config.py +6 -11
- synth_ai/sdk/task/contracts.py +137 -95
- synth_ai/sdk/task/in_process.py +32 -22
- synth_ai/sdk/task/in_process_runner.py +9 -4
- synth_ai/sdk/task/rubrics/__init__.py +2 -3
- synth_ai/sdk/task/rubrics/loaders.py +4 -4
- synth_ai/sdk/task/rubrics/strict.py +3 -4
- synth_ai/sdk/task/server.py +76 -16
- synth_ai/sdk/task/trace_correlation_helpers.py +190 -139
- synth_ai/sdk/task/validators.py +34 -49
- synth_ai/sdk/training/__init__.py +7 -16
- synth_ai/sdk/tunnels/__init__.py +118 -0
- synth_ai/sdk/tunnels/cleanup.py +83 -0
- synth_ai/sdk/tunnels/ports.py +120 -0
- synth_ai/sdk/tunnels/tunneled_api.py +363 -0
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/METADATA +71 -4
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/RECORD +118 -128
- synth_ai/cli/commands/baseline/__init__.py +0 -12
- synth_ai/cli/commands/baseline/core.py +0 -636
- synth_ai/cli/commands/baseline/list.py +0 -94
- synth_ai/cli/commands/eval/errors.py +0 -81
- synth_ai/cli/commands/status/formatters.py +0 -164
- synth_ai/cli/commands/status/subcommands/pricing.py +0 -23
- synth_ai/cli/commands/status/subcommands/usage.py +0 -203
- synth_ai/cli/commands/train/judge_validation.py +0 -305
- synth_ai/cli/usage.py +0 -159
- synth_ai/data/specs.py +0 -36
- synth_ai/sdk/api/research_agent/cli.py +0 -428
- synth_ai/sdk/api/research_agent/config.py +0 -357
- synth_ai/sdk/api/research_agent/job.py +0 -717
- synth_ai/sdk/baseline/__init__.py +0 -25
- synth_ai/sdk/baseline/config.py +0 -209
- synth_ai/sdk/baseline/discovery.py +0 -216
- synth_ai/sdk/baseline/execution.py +0 -154
- synth_ai/sdk/judging/__init__.py +0 -15
- synth_ai/sdk/judging/base.py +0 -24
- synth_ai/sdk/judging/client.py +0 -191
- synth_ai/sdk/judging/types.py +0 -42
- synth_ai/sdk/research_agent/__init__.py +0 -34
- synth_ai/sdk/research_agent/container_builder.py +0 -328
- synth_ai/sdk/research_agent/container_spec.py +0 -198
- synth_ai/sdk/research_agent/defaults.py +0 -34
- synth_ai/sdk/research_agent/results_collector.py +0 -69
- synth_ai/sdk/specs/__init__.py +0 -46
- synth_ai/sdk/specs/dataclasses.py +0 -149
- synth_ai/sdk/specs/loader.py +0 -144
- synth_ai/sdk/specs/serializer.py +0 -199
- synth_ai/sdk/specs/validation.py +0 -250
- synth_ai/sdk/tracing/__init__.py +0 -39
- synth_ai/sdk/usage/__init__.py +0 -37
- synth_ai/sdk/usage/client.py +0 -171
- synth_ai/sdk/usage/models.py +0 -261
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/WHEEL +0 -0
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.4.1.dist-info → synth_ai-0.4.4.dist-info}/top_level.txt +0 -0
|
@@ -1,92 +1,12 @@
|
|
|
1
|
-
"""Configuration
|
|
2
|
-
|
|
3
|
-
Provides helpers to resolve backend URLs, API keys, and request timeouts
|
|
4
|
-
from CLI options and environment variables.
|
|
5
|
-
"""
|
|
1
|
+
"""Configuration helpers for status commands."""
|
|
6
2
|
|
|
7
3
|
from __future__ import annotations
|
|
8
4
|
|
|
9
|
-
import importlib
|
|
10
|
-
import os
|
|
11
|
-
from collections.abc import Callable
|
|
12
5
|
from dataclasses import dataclass
|
|
13
6
|
|
|
14
|
-
DEFAULT_TIMEOUT = 30.0
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _load_backend_helpers() -> tuple[str, Callable[[], tuple[str, str]] | None]:
|
|
18
|
-
"""Attempt to load shared backend helpers from synth_ai.core.env."""
|
|
19
|
-
try:
|
|
20
|
-
module = importlib.import_module("synth_ai.core.env")
|
|
21
|
-
except Exception:
|
|
22
|
-
return "https://api.usesynth.ai", None
|
|
23
|
-
|
|
24
|
-
default = getattr(module, "PROD_BASE_URL_DEFAULT", "https://api.usesynth.ai")
|
|
25
|
-
getter = getattr(module, "get_backend_from_env", None)
|
|
26
|
-
return str(default), getter if callable(getter) else None
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
PROD_BASE_URL_DEFAULT, _GET_BACKEND_FROM_ENV = _load_backend_helpers()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _normalize_base_url(raw: str) -> str:
|
|
33
|
-
"""Ensure the configured base URL includes the /api/v1 prefix."""
|
|
34
|
-
base = raw.rstrip("/") if raw else ""
|
|
35
|
-
if not base:
|
|
36
|
-
return raw
|
|
37
|
-
if base.endswith("/api") or base.endswith("/api/v1") or "/api/" in base:
|
|
38
|
-
return base
|
|
39
|
-
return f"{base}/api/v1"
|
|
40
|
-
|
|
41
7
|
|
|
42
|
-
|
|
43
|
-
"""Compute the default backend base URL using env vars or helper module."""
|
|
44
|
-
for var in ("SYNTH_BACKEND_BASE_URL", "BACKEND_BASE_URL", "SYNTH_BASE_URL"):
|
|
45
|
-
val = os.getenv(var)
|
|
46
|
-
if val:
|
|
47
|
-
return _normalize_base_url(val)
|
|
48
|
-
if _GET_BACKEND_FROM_ENV:
|
|
49
|
-
try:
|
|
50
|
-
base, _ = _GET_BACKEND_FROM_ENV()
|
|
51
|
-
return _normalize_base_url(base)
|
|
52
|
-
except Exception:
|
|
53
|
-
pass
|
|
54
|
-
return _normalize_base_url(PROD_BASE_URL_DEFAULT)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _resolve_api_key(cli_key: str | None) -> tuple[str | None, str | None]:
|
|
58
|
-
"""Resolve the API key from CLI input or known environment variables."""
|
|
59
|
-
if cli_key:
|
|
60
|
-
return cli_key, "--api-key"
|
|
61
|
-
for var in ("SYNTH_BACKEND_API_KEY", "SYNTH_API_KEY", "DEFAULT_DEV_API_KEY"):
|
|
62
|
-
val = os.getenv(var)
|
|
63
|
-
if val:
|
|
64
|
-
return val, var
|
|
65
|
-
return None, None
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@dataclass()
|
|
8
|
+
@dataclass
|
|
69
9
|
class BackendConfig:
|
|
70
|
-
"""Configuration bundle shared across status commands."""
|
|
71
|
-
|
|
72
10
|
base_url: str
|
|
73
11
|
api_key: str | None
|
|
74
|
-
timeout: float =
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
def headers(self) -> dict[str, str]:
|
|
78
|
-
if not self.api_key:
|
|
79
|
-
return {}
|
|
80
|
-
return {"Authorization": f"Bearer {self.api_key}"}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def resolve_backend_config(
|
|
84
|
-
*,
|
|
85
|
-
base_url: str | None,
|
|
86
|
-
api_key: str | None,
|
|
87
|
-
timeout: float | None = None,
|
|
88
|
-
) -> BackendConfig:
|
|
89
|
-
"""Resolve the backend configuration from CLI options/environment."""
|
|
90
|
-
resolved_url = _normalize_base_url(base_url) if base_url else _default_base_url()
|
|
91
|
-
key, _ = _resolve_api_key(api_key)
|
|
92
|
-
return BackendConfig(base_url=resolved_url, api_key=key, timeout=timeout or DEFAULT_TIMEOUT)
|
|
12
|
+
timeout: float = 30.0
|
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"""
|
|
4
|
-
Custom error hierarchy for status CLI commands.
|
|
5
|
-
"""
|
|
1
|
+
"""Errors for status CLI helpers."""
|
|
6
2
|
|
|
3
|
+
from __future__ import annotations
|
|
7
4
|
|
|
8
5
|
|
|
9
6
|
class StatusAPIError(RuntimeError):
|
|
10
|
-
"""Raised when
|
|
7
|
+
"""Raised when status API calls fail."""
|
|
11
8
|
|
|
12
|
-
def __init__(self, message: str, status_code: int | None = None):
|
|
9
|
+
def __init__(self, message: str, *, status_code: int | None = None) -> None:
|
|
13
10
|
super().__init__(message)
|
|
14
11
|
self.status_code = status_code
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class StatusCLIError(RuntimeError):
|
|
18
|
-
"""Raised for client-side validation errors."""
|
|
19
|
-
|
|
20
|
-
pass
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Subcommands for the status CLI namespace.
|
|
3
|
-
"""
|
|
1
|
+
"""Status subcommands."""
|
|
4
2
|
|
|
5
|
-
from
|
|
6
|
-
from .jobs import jobs_group # noqa: F401
|
|
7
|
-
from .models import models_group # noqa: F401
|
|
8
|
-
from .runs import runs_group # noqa: F401
|
|
9
|
-
from .summary import summary_command # noqa: F401
|
|
3
|
+
from __future__ import annotations
|
|
@@ -1,79 +1,34 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Status files subcommand."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
|
-
from rich.json import JSON
|
|
9
8
|
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from ..formatters import console, files_table, print_json
|
|
13
|
-
from ..utils import bail, common_options, resolve_context_config
|
|
9
|
+
from .config import StatusConfig
|
|
10
|
+
from .utils import StatusAPIClient, print_json
|
|
14
11
|
|
|
15
12
|
|
|
16
|
-
@click.group("files"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ctx.ensure_object(dict)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@files_group.command("list")
|
|
23
|
-
@common_options()
|
|
24
|
-
@click.option("--purpose", type=click.Choice(["fine-tune", "validation"]))
|
|
25
|
-
@click.option("--limit", type=int, default=20, show_default=True)
|
|
26
|
-
@click.option("--json", "output_json", is_flag=True)
|
|
27
|
-
@click.pass_context
|
|
28
|
-
def list_files(
|
|
29
|
-
ctx: click.Context,
|
|
30
|
-
base_url: str | None,
|
|
31
|
-
api_key: str | None,
|
|
32
|
-
timeout: float,
|
|
33
|
-
purpose: str | None,
|
|
34
|
-
limit: int,
|
|
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
|
-
files = await client.list_files(purpose=purpose, limit=limit)
|
|
43
|
-
if output_json:
|
|
44
|
-
print_json(files)
|
|
45
|
-
else:
|
|
46
|
-
console.print(files_table(files))
|
|
47
|
-
except StatusAPIError as exc:
|
|
48
|
-
bail(f"Backend error: {exc}")
|
|
49
|
-
|
|
50
|
-
asyncio.run(_run())
|
|
13
|
+
@click.group("files")
|
|
14
|
+
def files_group() -> None:
|
|
15
|
+
return None
|
|
51
16
|
|
|
52
17
|
|
|
53
18
|
@files_group.command("get")
|
|
54
|
-
@common_options()
|
|
55
19
|
@click.argument("file_id")
|
|
56
|
-
@click.option("--json", "
|
|
57
|
-
|
|
58
|
-
def get_file(
|
|
59
|
-
ctx: click.Context,
|
|
60
|
-
base_url: str | None,
|
|
61
|
-
api_key: str | None,
|
|
62
|
-
timeout: float,
|
|
63
|
-
file_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
|
-
|
|
20
|
+
@click.option("--json", "as_json", is_flag=True, default=False)
|
|
21
|
+
def get_file(file_id: str, as_json: bool) -> None:
|
|
68
22
|
async def _run() -> None:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
except StatusAPIError as exc:
|
|
77
|
-
bail(f"Backend error: {exc}")
|
|
23
|
+
config = StatusConfig()
|
|
24
|
+
async with StatusAPIClient(config) as client:
|
|
25
|
+
data = await client.get_file(file_id) # type: ignore[attr-defined]
|
|
26
|
+
if as_json:
|
|
27
|
+
print_json(data)
|
|
28
|
+
else:
|
|
29
|
+
click.echo(data)
|
|
78
30
|
|
|
79
31
|
asyncio.run(_run())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = ["files_group"]
|
|
@@ -1,334 +1,51 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Status jobs subcommand."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
-
from typing import Any
|
|
7
6
|
|
|
8
7
|
import click
|
|
9
8
|
|
|
10
|
-
from
|
|
11
|
-
from
|
|
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
|
|
9
|
+
from .config import StatusConfig
|
|
10
|
+
from .utils import StatusAPIClient, print_json
|
|
22
11
|
|
|
23
12
|
|
|
24
|
-
@click.group("jobs"
|
|
25
|
-
|
|
26
|
-
|
|
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))
|
|
13
|
+
@click.group("jobs")
|
|
14
|
+
def jobs_group() -> None:
|
|
15
|
+
return None
|
|
37
16
|
|
|
38
17
|
|
|
39
18
|
@jobs_group.command("list")
|
|
40
|
-
@
|
|
41
|
-
@click.option(
|
|
42
|
-
|
|
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
|
-
|
|
19
|
+
@click.option("--json", "as_json", is_flag=True, default=False)
|
|
20
|
+
@click.option("--status", default=None)
|
|
21
|
+
def list_jobs(as_json: bool, status: str | None) -> None:
|
|
127
22
|
async def _run() -> None:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
except StatusAPIError as exc:
|
|
136
|
-
bail(f"Backend error: {exc}")
|
|
23
|
+
config = StatusConfig()
|
|
24
|
+
async with StatusAPIClient(config) as client:
|
|
25
|
+
data = await client.list_jobs(status=status) # type: ignore[attr-defined]
|
|
26
|
+
if as_json:
|
|
27
|
+
print_json(data)
|
|
28
|
+
else:
|
|
29
|
+
click.echo(data)
|
|
137
30
|
|
|
138
31
|
asyncio.run(_run())
|
|
139
32
|
|
|
140
33
|
|
|
141
|
-
@jobs_group.command("
|
|
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()
|
|
34
|
+
@jobs_group.command("logs")
|
|
259
35
|
@click.argument("job_id")
|
|
260
|
-
@click.
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
36
|
+
@click.option("--json", "as_json", is_flag=True, default=False)
|
|
37
|
+
@click.option("--tail", default=None, type=int)
|
|
38
|
+
def logs(job_id: str, as_json: bool, tail: int | None) -> None:
|
|
270
39
|
async def _run() -> None:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
40
|
+
config = StatusConfig()
|
|
41
|
+
async with StatusAPIClient(config) as client:
|
|
42
|
+
data = await client.get_job_events(job_id, limit=tail) # type: ignore[attr-defined]
|
|
43
|
+
if as_json:
|
|
44
|
+
print_json(data)
|
|
45
|
+
else:
|
|
46
|
+
click.echo(data)
|
|
277
47
|
|
|
278
48
|
asyncio.run(_run())
|
|
279
49
|
|
|
280
50
|
|
|
281
|
-
|
|
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())
|
|
51
|
+
__all__ = ["jobs_group"]
|