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,428 +0,0 @@
|
|
|
1
|
-
"""CLI for Research Agent jobs.
|
|
2
|
-
|
|
3
|
-
Provides the `synth-ai agent` command group for running research agent jobs.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
import sys
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any, Optional
|
|
11
|
-
|
|
12
|
-
import click
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _print_event(event: dict[str, Any]) -> None:
|
|
16
|
-
"""Print an event to stdout."""
|
|
17
|
-
event_type = event.get("type", "")
|
|
18
|
-
message = event.get("message", "")
|
|
19
|
-
level = event.get("level", "info")
|
|
20
|
-
|
|
21
|
-
# Color based on level/type
|
|
22
|
-
if level == "error" or "failed" in event_type:
|
|
23
|
-
click.secho(f"[{event_type}] {message}", fg="red")
|
|
24
|
-
elif "completed" in event_type or "succeeded" in event_type:
|
|
25
|
-
click.secho(f"[{event_type}] {message}", fg="green")
|
|
26
|
-
elif "warning" in event_type or "budget" in event_type:
|
|
27
|
-
click.secho(f"[{event_type}] {message}", fg="yellow")
|
|
28
|
-
else:
|
|
29
|
-
click.echo(f"[{event_type}] {message}")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@click.group()
|
|
33
|
-
def agent_cmd() -> None:
|
|
34
|
-
"""Research Agent commands for AI-assisted code analysis and optimization."""
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@agent_cmd.command("run")
|
|
39
|
-
@click.option(
|
|
40
|
-
"--config",
|
|
41
|
-
"-c",
|
|
42
|
-
"config_path",
|
|
43
|
-
type=click.Path(exists=True, path_type=Path),
|
|
44
|
-
help="Path to TOML config file",
|
|
45
|
-
)
|
|
46
|
-
@click.option(
|
|
47
|
-
"--algorithm",
|
|
48
|
-
"-a",
|
|
49
|
-
type=click.Choice(["scaffold_tuning", "evaluation", "trace_analysis"]),
|
|
50
|
-
help="Algorithm to run (overrides config)",
|
|
51
|
-
)
|
|
52
|
-
@click.option(
|
|
53
|
-
"--repo",
|
|
54
|
-
"-r",
|
|
55
|
-
"repo_url",
|
|
56
|
-
help="Repository URL (overrides config)",
|
|
57
|
-
)
|
|
58
|
-
@click.option(
|
|
59
|
-
"--branch",
|
|
60
|
-
"-b",
|
|
61
|
-
"repo_branch",
|
|
62
|
-
default="main",
|
|
63
|
-
help="Repository branch",
|
|
64
|
-
)
|
|
65
|
-
@click.option(
|
|
66
|
-
"--backend",
|
|
67
|
-
type=click.Choice(["daytona", "modal", "docker"]),
|
|
68
|
-
default="daytona",
|
|
69
|
-
help="Container backend to use",
|
|
70
|
-
)
|
|
71
|
-
@click.option(
|
|
72
|
-
"--model",
|
|
73
|
-
"-m",
|
|
74
|
-
default="gpt-4o",
|
|
75
|
-
help="Model for the agent to use",
|
|
76
|
-
)
|
|
77
|
-
@click.option(
|
|
78
|
-
"--poll/--no-poll",
|
|
79
|
-
default=True,
|
|
80
|
-
help="Poll for completion and stream events",
|
|
81
|
-
)
|
|
82
|
-
@click.option(
|
|
83
|
-
"--timeout",
|
|
84
|
-
"-t",
|
|
85
|
-
type=float,
|
|
86
|
-
default=3600.0,
|
|
87
|
-
help="Timeout in seconds (for --poll)",
|
|
88
|
-
)
|
|
89
|
-
@click.option(
|
|
90
|
-
"--api-key",
|
|
91
|
-
envvar="SYNTH_API_KEY",
|
|
92
|
-
help="Synth API key",
|
|
93
|
-
)
|
|
94
|
-
@click.option(
|
|
95
|
-
"--backend-url",
|
|
96
|
-
envvar="SYNTH_BACKEND_URL",
|
|
97
|
-
default="https://api.usesynth.ai",
|
|
98
|
-
help="Backend API URL",
|
|
99
|
-
)
|
|
100
|
-
def run_cmd(
|
|
101
|
-
config_path: Optional[Path],
|
|
102
|
-
algorithm: Optional[str],
|
|
103
|
-
repo_url: Optional[str],
|
|
104
|
-
repo_branch: str,
|
|
105
|
-
backend: str,
|
|
106
|
-
model: str,
|
|
107
|
-
poll: bool,
|
|
108
|
-
timeout: float,
|
|
109
|
-
api_key: Optional[str],
|
|
110
|
-
backend_url: str,
|
|
111
|
-
) -> None:
|
|
112
|
-
"""Run a research agent job.
|
|
113
|
-
|
|
114
|
-
You can provide configuration via a TOML file or command-line options.
|
|
115
|
-
|
|
116
|
-
Examples:
|
|
117
|
-
|
|
118
|
-
# From config file
|
|
119
|
-
synth-ai agent run --config my_config.toml --poll
|
|
120
|
-
|
|
121
|
-
# Quick scaffold tuning job
|
|
122
|
-
synth-ai agent run \\
|
|
123
|
-
--algorithm scaffold_tuning \\
|
|
124
|
-
--repo https://github.com/your-org/repo \\
|
|
125
|
-
--backend daytona
|
|
126
|
-
|
|
127
|
-
"""
|
|
128
|
-
from .config import OptimizationTool, ResearchConfig
|
|
129
|
-
from .job import ResearchAgentJob, ResearchAgentJobConfig
|
|
130
|
-
|
|
131
|
-
if not api_key:
|
|
132
|
-
click.secho("Error: SYNTH_API_KEY is required", fg="red", err=True)
|
|
133
|
-
sys.exit(1)
|
|
134
|
-
|
|
135
|
-
# Build config
|
|
136
|
-
if config_path:
|
|
137
|
-
click.echo(f"Loading config from {config_path}...")
|
|
138
|
-
config = ResearchAgentJobConfig.from_toml(config_path)
|
|
139
|
-
# api_key is guaranteed to be str at this point (checked above)
|
|
140
|
-
assert api_key is not None, "api_key should be set by this point"
|
|
141
|
-
config.api_key = api_key
|
|
142
|
-
config.backend_url = backend_url
|
|
143
|
-
|
|
144
|
-
# Apply CLI overrides
|
|
145
|
-
if algorithm:
|
|
146
|
-
# Map algorithm string to optimization tool if needed
|
|
147
|
-
# Note: The algorithm parameter is kept for backward compatibility
|
|
148
|
-
# but the actual optimization is controlled by config.research.tools
|
|
149
|
-
pass # Algorithm is embedded in the research config from TOML
|
|
150
|
-
if repo_url:
|
|
151
|
-
config.repo_url = repo_url
|
|
152
|
-
if repo_branch != "main":
|
|
153
|
-
config.repo_branch = repo_branch
|
|
154
|
-
if backend != "daytona":
|
|
155
|
-
config.backend = backend # type: ignore
|
|
156
|
-
if model != "gpt-4o":
|
|
157
|
-
config.model = model
|
|
158
|
-
else:
|
|
159
|
-
# Build from CLI options
|
|
160
|
-
if not algorithm:
|
|
161
|
-
click.secho("Error: --algorithm is required when not using --config", fg="red", err=True)
|
|
162
|
-
sys.exit(1)
|
|
163
|
-
if not repo_url:
|
|
164
|
-
click.secho("Error: --repo is required when not using --config", fg="red", err=True)
|
|
165
|
-
sys.exit(1)
|
|
166
|
-
|
|
167
|
-
# Create a minimal ResearchConfig
|
|
168
|
-
# The algorithm parameter maps to the optimization tool
|
|
169
|
-
tools = [OptimizationTool.MIPRO] # Default to MIPRO for CLI usage
|
|
170
|
-
research = ResearchConfig(
|
|
171
|
-
task_description=f"Research job via CLI with {algorithm}",
|
|
172
|
-
tools=tools,
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
config = ResearchAgentJobConfig(
|
|
176
|
-
research=research,
|
|
177
|
-
repo_url=repo_url, # type: ignore[arg-type]
|
|
178
|
-
repo_branch=repo_branch,
|
|
179
|
-
backend=backend, # type: ignore
|
|
180
|
-
model=model,
|
|
181
|
-
backend_url=backend_url,
|
|
182
|
-
api_key=api_key, # type: ignore[arg-type]
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
# Create and submit job
|
|
186
|
-
job = ResearchAgentJob(config=config)
|
|
187
|
-
|
|
188
|
-
click.echo("Submitting research job...")
|
|
189
|
-
click.echo(f" Repository: {config.repo_url}")
|
|
190
|
-
click.echo(f" Branch: {config.repo_branch}")
|
|
191
|
-
click.echo(f" Backend: {config.backend}")
|
|
192
|
-
click.echo(f" Model: {config.model}")
|
|
193
|
-
click.echo(f" Tools: {', '.join(t.value for t in config.research.tools)}")
|
|
194
|
-
|
|
195
|
-
try:
|
|
196
|
-
job_id = job.submit()
|
|
197
|
-
click.secho(f"Job submitted: {job_id}", fg="green")
|
|
198
|
-
except Exception as e:
|
|
199
|
-
click.secho(f"Failed to submit job: {e}", fg="red", err=True)
|
|
200
|
-
sys.exit(1)
|
|
201
|
-
|
|
202
|
-
if not poll:
|
|
203
|
-
click.echo(f"\nTo check status: synth-ai agent status {job_id}")
|
|
204
|
-
return
|
|
205
|
-
|
|
206
|
-
# Poll for completion
|
|
207
|
-
click.echo("\nPolling for completion...")
|
|
208
|
-
try:
|
|
209
|
-
result = job.poll_until_complete(
|
|
210
|
-
timeout=timeout,
|
|
211
|
-
poll_interval=5.0,
|
|
212
|
-
on_event=_print_event,
|
|
213
|
-
)
|
|
214
|
-
click.secho(f"\nJob completed: {result.get('status', 'unknown')}", fg="green")
|
|
215
|
-
|
|
216
|
-
# Print summary
|
|
217
|
-
if result.get("best_metric_value") is not None:
|
|
218
|
-
click.echo(f"Best metric value: {result['best_metric_value']}")
|
|
219
|
-
if result.get("current_iteration"):
|
|
220
|
-
click.echo(f"Iterations: {result['current_iteration']}")
|
|
221
|
-
|
|
222
|
-
except TimeoutError:
|
|
223
|
-
click.secho(f"\nJob timed out after {timeout}s", fg="yellow", err=True)
|
|
224
|
-
click.echo(f"Job ID: {job_id}")
|
|
225
|
-
click.echo("Use 'synth-ai agent status' to check progress")
|
|
226
|
-
sys.exit(1)
|
|
227
|
-
except RuntimeError as e:
|
|
228
|
-
click.secho(f"\nJob failed: {e}", fg="red", err=True)
|
|
229
|
-
sys.exit(1)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
@agent_cmd.command("status")
|
|
233
|
-
@click.argument("job_id")
|
|
234
|
-
@click.option(
|
|
235
|
-
"--api-key",
|
|
236
|
-
envvar="SYNTH_API_KEY",
|
|
237
|
-
help="Synth API key",
|
|
238
|
-
)
|
|
239
|
-
@click.option(
|
|
240
|
-
"--backend-url",
|
|
241
|
-
envvar="SYNTH_BACKEND_URL",
|
|
242
|
-
default="https://api.usesynth.ai",
|
|
243
|
-
help="Backend API URL",
|
|
244
|
-
)
|
|
245
|
-
def status_cmd(job_id: str, api_key: Optional[str], backend_url: str) -> None:
|
|
246
|
-
"""Get status of a research agent job."""
|
|
247
|
-
from .job import ResearchAgentJob
|
|
248
|
-
|
|
249
|
-
if not api_key:
|
|
250
|
-
click.secho("Error: SYNTH_API_KEY is required", fg="red", err=True)
|
|
251
|
-
sys.exit(1)
|
|
252
|
-
|
|
253
|
-
job = ResearchAgentJob.from_id(job_id, backend_url=backend_url, api_key=api_key)
|
|
254
|
-
|
|
255
|
-
try:
|
|
256
|
-
status = job.get_status()
|
|
257
|
-
click.echo(f"Job ID: {job_id}")
|
|
258
|
-
click.echo(f"Status: {status.get('status', 'unknown')}")
|
|
259
|
-
click.echo(f"Algorithm: {status.get('algorithm', 'unknown')}")
|
|
260
|
-
click.echo(f"Backend: {status.get('backend', 'unknown')}")
|
|
261
|
-
|
|
262
|
-
if status.get("current_iteration"):
|
|
263
|
-
total = status.get("total_iterations", "?")
|
|
264
|
-
click.echo(f"Progress: {status['current_iteration']}/{total}")
|
|
265
|
-
|
|
266
|
-
if status.get("best_metric_value") is not None:
|
|
267
|
-
click.echo(f"Best metric: {status['best_metric_value']}")
|
|
268
|
-
|
|
269
|
-
if status.get("error"):
|
|
270
|
-
click.secho(f"Error: {status['error']}", fg="red")
|
|
271
|
-
|
|
272
|
-
except Exception as e:
|
|
273
|
-
click.secho(f"Failed to get status: {e}", fg="red", err=True)
|
|
274
|
-
sys.exit(1)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
@agent_cmd.command("events")
|
|
278
|
-
@click.argument("job_id")
|
|
279
|
-
@click.option(
|
|
280
|
-
"--since",
|
|
281
|
-
type=int,
|
|
282
|
-
default=0,
|
|
283
|
-
help="Show events after this sequence number",
|
|
284
|
-
)
|
|
285
|
-
@click.option(
|
|
286
|
-
"--follow",
|
|
287
|
-
"-f",
|
|
288
|
-
is_flag=True,
|
|
289
|
-
help="Follow events in real-time",
|
|
290
|
-
)
|
|
291
|
-
@click.option(
|
|
292
|
-
"--api-key",
|
|
293
|
-
envvar="SYNTH_API_KEY",
|
|
294
|
-
help="Synth API key",
|
|
295
|
-
)
|
|
296
|
-
@click.option(
|
|
297
|
-
"--backend-url",
|
|
298
|
-
envvar="SYNTH_BACKEND_URL",
|
|
299
|
-
default="https://api.usesynth.ai",
|
|
300
|
-
help="Backend API URL",
|
|
301
|
-
)
|
|
302
|
-
def events_cmd(
|
|
303
|
-
job_id: str,
|
|
304
|
-
since: int,
|
|
305
|
-
follow: bool,
|
|
306
|
-
api_key: Optional[str],
|
|
307
|
-
backend_url: str,
|
|
308
|
-
) -> None:
|
|
309
|
-
"""Get events from a research agent job."""
|
|
310
|
-
import time
|
|
311
|
-
|
|
312
|
-
from .job import ResearchAgentJob
|
|
313
|
-
|
|
314
|
-
if not api_key:
|
|
315
|
-
click.secho("Error: SYNTH_API_KEY is required", fg="red", err=True)
|
|
316
|
-
sys.exit(1)
|
|
317
|
-
|
|
318
|
-
job = ResearchAgentJob.from_id(job_id, backend_url=backend_url, api_key=api_key)
|
|
319
|
-
last_seq = since
|
|
320
|
-
|
|
321
|
-
try:
|
|
322
|
-
while True:
|
|
323
|
-
events = job.get_events(since_seq=last_seq)
|
|
324
|
-
for event in events:
|
|
325
|
-
_print_event(event)
|
|
326
|
-
last_seq = max(last_seq, event.get("seq", 0))
|
|
327
|
-
|
|
328
|
-
if not follow:
|
|
329
|
-
break
|
|
330
|
-
|
|
331
|
-
# Check if job is done
|
|
332
|
-
status = job.get_status()
|
|
333
|
-
if status.get("status") in ("succeeded", "failed", "canceled"):
|
|
334
|
-
break
|
|
335
|
-
|
|
336
|
-
time.sleep(2.0)
|
|
337
|
-
|
|
338
|
-
except KeyboardInterrupt:
|
|
339
|
-
click.echo("\nStopped following events")
|
|
340
|
-
except Exception as e:
|
|
341
|
-
click.secho(f"Failed to get events: {e}", fg="red", err=True)
|
|
342
|
-
sys.exit(1)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
@agent_cmd.command("cancel")
|
|
346
|
-
@click.argument("job_id")
|
|
347
|
-
@click.option(
|
|
348
|
-
"--api-key",
|
|
349
|
-
envvar="SYNTH_API_KEY",
|
|
350
|
-
help="Synth API key",
|
|
351
|
-
)
|
|
352
|
-
@click.option(
|
|
353
|
-
"--backend-url",
|
|
354
|
-
envvar="SYNTH_BACKEND_URL",
|
|
355
|
-
default="https://api.usesynth.ai",
|
|
356
|
-
help="Backend API URL",
|
|
357
|
-
)
|
|
358
|
-
def cancel_cmd(job_id: str, api_key: Optional[str], backend_url: str) -> None:
|
|
359
|
-
"""Cancel a running research agent job."""
|
|
360
|
-
from .job import ResearchAgentJob
|
|
361
|
-
|
|
362
|
-
if not api_key:
|
|
363
|
-
click.secho("Error: SYNTH_API_KEY is required", fg="red", err=True)
|
|
364
|
-
sys.exit(1)
|
|
365
|
-
|
|
366
|
-
job = ResearchAgentJob.from_id(job_id, backend_url=backend_url, api_key=api_key)
|
|
367
|
-
|
|
368
|
-
if job.cancel():
|
|
369
|
-
click.secho(f"Cancellation requested for job {job_id}", fg="yellow")
|
|
370
|
-
else:
|
|
371
|
-
click.secho(f"Failed to cancel job {job_id}", fg="red", err=True)
|
|
372
|
-
sys.exit(1)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
@agent_cmd.command("results")
|
|
376
|
-
@click.argument("job_id")
|
|
377
|
-
@click.option(
|
|
378
|
-
"--output",
|
|
379
|
-
"-o",
|
|
380
|
-
type=click.Path(path_type=Path),
|
|
381
|
-
help="Write results to file (JSON)",
|
|
382
|
-
)
|
|
383
|
-
@click.option(
|
|
384
|
-
"--api-key",
|
|
385
|
-
envvar="SYNTH_API_KEY",
|
|
386
|
-
help="Synth API key",
|
|
387
|
-
)
|
|
388
|
-
@click.option(
|
|
389
|
-
"--backend-url",
|
|
390
|
-
envvar="SYNTH_BACKEND_URL",
|
|
391
|
-
default="https://api.usesynth.ai",
|
|
392
|
-
help="Backend API URL",
|
|
393
|
-
)
|
|
394
|
-
def results_cmd(
|
|
395
|
-
job_id: str,
|
|
396
|
-
output: Optional[Path],
|
|
397
|
-
api_key: Optional[str],
|
|
398
|
-
backend_url: str,
|
|
399
|
-
) -> None:
|
|
400
|
-
"""Get results from a completed research agent job."""
|
|
401
|
-
import json
|
|
402
|
-
|
|
403
|
-
from .job import ResearchAgentJob
|
|
404
|
-
|
|
405
|
-
if not api_key:
|
|
406
|
-
click.secho("Error: SYNTH_API_KEY is required", fg="red", err=True)
|
|
407
|
-
sys.exit(1)
|
|
408
|
-
|
|
409
|
-
job = ResearchAgentJob.from_id(job_id, backend_url=backend_url, api_key=api_key)
|
|
410
|
-
|
|
411
|
-
try:
|
|
412
|
-
results = job.get_results()
|
|
413
|
-
|
|
414
|
-
if output:
|
|
415
|
-
with open(output, "w") as f:
|
|
416
|
-
json.dump(results, f, indent=2)
|
|
417
|
-
click.echo(f"Results written to {output}")
|
|
418
|
-
else:
|
|
419
|
-
click.echo(json.dumps(results, indent=2))
|
|
420
|
-
|
|
421
|
-
except Exception as e:
|
|
422
|
-
click.secho(f"Failed to get results: {e}", fg="red", err=True)
|
|
423
|
-
sys.exit(1)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
def register(cli: Any) -> None:
|
|
427
|
-
"""Register the agent command group with the main CLI."""
|
|
428
|
-
cli.add_command(agent_cmd, name="agent")
|