synth-ai 0.2.16__py3-none-any.whl โ 0.2.17__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/blog_posts/pokemon_vl/README.md +98 -0
- examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +25 -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 +42 -0
- examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
- examples/blog_posts/warming_up_to_rl/README.md +158 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -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/train_rl_from_sft.toml +41 -0
- examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
- examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
- examples/multi_step/configs/crafter_rl_outcome.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +65 -107
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +1 -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 +5 -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 -4
- examples/swe/task_app/morph_backend.py +178 -0
- examples/task_apps/crafter/task_app/README.md +1 -1
- examples/task_apps/crafter/task_app/grpo_crafter.py +66 -3
- 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/inference/openai_client.py +17 -49
- examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +13 -5
- examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +15 -1
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
- examples/task_apps/math/README.md +1 -2
- examples/task_apps/pokemon_red/README.md +3 -4
- 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 +36 -5
- 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 +2 -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 +134 -3
- 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/inference/openai_client.py +4 -4
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +6 -3
- examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +5 -0
- synth_ai/api/train/builders.py +9 -3
- synth_ai/api/train/cli.py +125 -10
- synth_ai/api/train/configs/__init__.py +8 -1
- synth_ai/api/train/configs/rl.py +32 -7
- synth_ai/api/train/configs/sft.py +6 -2
- synth_ai/api/train/configs/shared.py +59 -2
- synth_ai/auth/credentials.py +119 -0
- synth_ai/cli/__init__.py +12 -4
- synth_ai/cli/commands/__init__.py +17 -0
- synth_ai/cli/commands/demo/__init__.py +6 -0
- synth_ai/cli/commands/demo/core.py +163 -0
- synth_ai/cli/commands/deploy/__init__.py +23 -0
- synth_ai/cli/commands/deploy/core.py +614 -0
- synth_ai/cli/commands/deploy/errors.py +72 -0
- synth_ai/cli/commands/deploy/validation.py +11 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1109 -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 +388 -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 +73 -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/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -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 +199 -0
- synth_ai/cli/commands/train/judge_validation.py +304 -0
- synth_ai/cli/commands/train/validation.py +443 -0
- synth_ai/cli/demo.py +2 -162
- synth_ai/cli/deploy/__init__.py +28 -0
- synth_ai/cli/deploy/core.py +5 -0
- synth_ai/cli/deploy/errors.py +23 -0
- synth_ai/cli/deploy/validation.py +5 -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/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/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 +58 -1487
- 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/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 -11
- synth_ai/learning/rl/client.py +3 -1
- synth_ai/streaming/__init__.py +29 -0
- synth_ai/streaming/config.py +94 -0
- synth_ai/streaming/handlers.py +469 -0
- synth_ai/streaming/streamer.py +301 -0
- synth_ai/streaming/types.py +95 -0
- synth_ai/task/validators.py +2 -2
- synth_ai/tracing_v3/migration_helper.py +1 -2
- synth_ai/utils/env.py +25 -18
- synth_ai/utils/http.py +4 -1
- synth_ai/utils/modal.py +2 -2
- {synth_ai-0.2.16.dist-info โ synth_ai-0.2.17.dist-info}/METADATA +8 -3
- {synth_ai-0.2.16.dist-info โ synth_ai-0.2.17.dist-info}/RECORD +184 -109
- 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.17.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.16.dist-info โ synth_ai-0.2.17.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.16.dist-info โ synth_ai-0.2.17.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.16.dist-info โ synth_ai-0.2.17.dist-info}/top_level.txt +0 -0
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# Evaluation config for Qwen2-VL via synth-ai hosted inference
|
|
2
|
-
# Collects vision traces for SFT training
|
|
3
|
-
|
|
4
|
-
[eval]
|
|
5
|
-
model = "Qwen/Qwen2-VL-7B-Instruct"
|
|
6
|
-
provider = "synth" # Use synth-ai hosted inference
|
|
7
|
-
|
|
8
|
-
# Task app endpoint (local or hosted)
|
|
9
|
-
# task_app_url = "http://localhost:8000" # Local
|
|
10
|
-
task_app_url = "https://synth-laboratories--grpo-crafter-task-app.modal.run" # Hosted
|
|
11
|
-
|
|
12
|
-
# Vision settings (auto-detected from "qwen2-vl" in model name)
|
|
13
|
-
use_vision = true
|
|
14
|
-
image_only_mode = false # Include both text + images
|
|
15
|
-
|
|
16
|
-
# Rollout settings
|
|
17
|
-
num_episodes = 100
|
|
18
|
-
max_steps_per_episode = 50
|
|
19
|
-
seeds = "100-199" # Different seeds from gpt-5-nano for comparison
|
|
20
|
-
|
|
21
|
-
# Sampling parameters
|
|
22
|
-
temperature = 0.7
|
|
23
|
-
max_tokens = 512
|
|
24
|
-
|
|
25
|
-
# Trace collection
|
|
26
|
-
collect_traces = true
|
|
27
|
-
trace_db = "traces/qwen2vl_vision/rollouts.db"
|
|
28
|
-
|
|
29
|
-
# Tools
|
|
30
|
-
use_tools = true
|
|
31
|
-
|
|
32
|
-
# Parallel rollouts
|
|
33
|
-
parallel_episodes = 5
|
|
34
|
-
|
|
35
|
-
[task]
|
|
36
|
-
name = "crafter"
|
|
37
|
-
environment = "crafter-classic"
|
|
38
|
-
|
|
39
|
-
# Task-specific settings
|
|
40
|
-
[task.config]
|
|
41
|
-
seed_start = 100
|
|
42
|
-
max_episode_length = 256
|
|
43
|
-
render_size = [64, 64] # 64x64 PNG images
|
|
44
|
-
|
synth_ai/cli/tui.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
CLI: Interactive TUI dashboard for Synth AI.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import importlib
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
from rich.console import Console
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def register(cli):
|
|
14
|
-
@cli.command()
|
|
15
|
-
@click.option(
|
|
16
|
-
"--url",
|
|
17
|
-
"db_url",
|
|
18
|
-
default="sqlite+libsql://http://127.0.0.1:8080",
|
|
19
|
-
help="Database URL (default: sqlite+libsql://http://127.0.0.1:8080)",
|
|
20
|
-
)
|
|
21
|
-
@click.option("--debug", is_flag=True, help="Enable debug logging")
|
|
22
|
-
def tui(db_url: str, debug: bool):
|
|
23
|
-
"""Launch interactive TUI dashboard showing experiments, balance, and active runs."""
|
|
24
|
-
console = Console()
|
|
25
|
-
|
|
26
|
-
# Import here to avoid circular imports and handle optional dependencies
|
|
27
|
-
try:
|
|
28
|
-
module = importlib.import_module("synth_ai.tui.dashboard")
|
|
29
|
-
except (ImportError, ModuleNotFoundError) as e:
|
|
30
|
-
console.print("[red]Error:[/red] TUI dashboard not available.")
|
|
31
|
-
console.print(f"Missing dependencies: {e}")
|
|
32
|
-
console.print("Install with: pip install textual")
|
|
33
|
-
return
|
|
34
|
-
except Exception:
|
|
35
|
-
# Handle other import errors (like missing libsql, type annotation issues, etc.)
|
|
36
|
-
console.print("[red]Error:[/red] TUI dashboard not available.")
|
|
37
|
-
console.print("This may be due to missing dependencies or Python version compatibility.")
|
|
38
|
-
console.print("Try: pip install textual libsql")
|
|
39
|
-
console.print("If using Python < 3.10, you may need to update Python or install eval_type_backport.")
|
|
40
|
-
return
|
|
41
|
-
tui_main = getattr(module, "main", None)
|
|
42
|
-
if not callable(tui_main):
|
|
43
|
-
console.print("[red]Error:[/red] TUI dashboard entrypoint not available.")
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
# Set environment variables for the TUI to use
|
|
47
|
-
os.environ.setdefault("TUI_DB_URL", db_url)
|
|
48
|
-
if debug:
|
|
49
|
-
os.environ["TUI_DEBUG"] = "1"
|
|
50
|
-
|
|
51
|
-
# Run the TUI by calling the module directly with sanitized argv
|
|
52
|
-
try:
|
|
53
|
-
tui_args = ["--url", db_url]
|
|
54
|
-
if debug:
|
|
55
|
-
tui_args.append("--debug")
|
|
56
|
-
tui_main(tui_args)
|
|
57
|
-
except KeyboardInterrupt:
|
|
58
|
-
console.print("\n[blue]TUI closed.[/blue]")
|
|
59
|
-
except Exception as e:
|
|
60
|
-
console.print(f"\n[red]Error running TUI:[/red] {e}")
|
|
61
|
-
if debug:
|
|
62
|
-
raise
|
synth_ai/tui/__init__.py
DELETED
synth_ai/tui/__main__.py
DELETED
synth_ai/tui/cli/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Command Line Interface tools for synth-ai."""
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Query experiments and sessions from Turso/sqld using v3 tracing.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import asyncio
|
|
8
|
-
|
|
9
|
-
from synth_ai.tracing_v3.turso import NativeLibsqlTraceManager
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
async def list_experiments(db_url: str):
|
|
13
|
-
"""List all experiments in the database."""
|
|
14
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
15
|
-
await db.initialize()
|
|
16
|
-
|
|
17
|
-
try:
|
|
18
|
-
df = await db.query_traces("""
|
|
19
|
-
SELECT
|
|
20
|
-
e.experiment_id,
|
|
21
|
-
e.name,
|
|
22
|
-
e.description,
|
|
23
|
-
e.created_at,
|
|
24
|
-
COUNT(DISTINCT st.session_id) as num_sessions,
|
|
25
|
-
COUNT(DISTINCT ev.id) as num_events,
|
|
26
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
|
|
27
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
|
|
28
|
-
FROM experiments e
|
|
29
|
-
LEFT JOIN session_traces st ON e.experiment_id = st.experiment_id
|
|
30
|
-
LEFT JOIN events ev ON st.session_id = ev.session_id
|
|
31
|
-
GROUP BY e.experiment_id, e.name, e.description, e.created_at
|
|
32
|
-
ORDER BY e.created_at DESC
|
|
33
|
-
""")
|
|
34
|
-
|
|
35
|
-
if df.empty:
|
|
36
|
-
print("No experiments found in database.")
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
print(f"\n{'=' * 100}")
|
|
40
|
-
print(f"{'Experiments in ' + db_url:^100}")
|
|
41
|
-
print(f"{'=' * 100}\n")
|
|
42
|
-
|
|
43
|
-
for _, row in df.iterrows():
|
|
44
|
-
print(f"๐งช {row['name']} (id: {row['experiment_id'][:8]}...)")
|
|
45
|
-
print(f" Created: {row['created_at']}")
|
|
46
|
-
print(f" Description: {row['description']}")
|
|
47
|
-
print(f" Sessions: {row['num_sessions']}")
|
|
48
|
-
print(f" Events: {row['num_events']:,}")
|
|
49
|
-
if row["total_cost"] and row["total_cost"] > 0:
|
|
50
|
-
print(f" Cost: ${row['total_cost']:.4f}")
|
|
51
|
-
if row["total_tokens"] and row["total_tokens"] > 0:
|
|
52
|
-
print(f" Tokens: {int(row['total_tokens']):,}")
|
|
53
|
-
print()
|
|
54
|
-
finally:
|
|
55
|
-
await db.close()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async def show_experiment_details(db_url: str, experiment_id: str):
|
|
59
|
-
"""Show detailed information about a specific experiment."""
|
|
60
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
61
|
-
await db.initialize()
|
|
62
|
-
|
|
63
|
-
try:
|
|
64
|
-
# Get experiment info
|
|
65
|
-
exp_df = await db.query_traces(
|
|
66
|
-
"""
|
|
67
|
-
SELECT * FROM experiments WHERE experiment_id LIKE :exp_id
|
|
68
|
-
""",
|
|
69
|
-
{"exp_id": f"{experiment_id}%"},
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
if exp_df.empty:
|
|
73
|
-
print(f"No experiment found matching ID: {experiment_id}")
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
exp = exp_df.iloc[0]
|
|
77
|
-
print(f"\n{'=' * 100}")
|
|
78
|
-
print(f"Experiment: {exp['name']} ({exp['experiment_id']})")
|
|
79
|
-
print(f"{'=' * 100}\n")
|
|
80
|
-
|
|
81
|
-
# Get session statistics
|
|
82
|
-
sessions_df = await db.get_sessions_by_experiment(exp["experiment_id"])
|
|
83
|
-
|
|
84
|
-
if sessions_df:
|
|
85
|
-
print(f"Sessions: {len(sessions_df)}")
|
|
86
|
-
|
|
87
|
-
# Get aggregated stats
|
|
88
|
-
stats_df = await db.query_traces(
|
|
89
|
-
"""
|
|
90
|
-
SELECT
|
|
91
|
-
COUNT(DISTINCT ev.id) as total_events,
|
|
92
|
-
COUNT(DISTINCT m.id) as total_messages,
|
|
93
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
|
|
94
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
|
|
95
|
-
FROM session_traces st
|
|
96
|
-
LEFT JOIN events ev ON st.session_id = ev.session_id
|
|
97
|
-
LEFT JOIN messages m ON st.session_id = m.session_id
|
|
98
|
-
WHERE st.experiment_id = :exp_id
|
|
99
|
-
""",
|
|
100
|
-
{"exp_id": exp["experiment_id"]},
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if not stats_df.empty:
|
|
104
|
-
stats = stats_df.iloc[0]
|
|
105
|
-
print(f"Total events: {int(stats['total_events']):,}")
|
|
106
|
-
print(f"Total messages: {int(stats['total_messages']):,}")
|
|
107
|
-
print(f"Total cost: ${stats['total_cost']:.4f}")
|
|
108
|
-
print(f"Total tokens: {int(stats['total_tokens']):,}")
|
|
109
|
-
|
|
110
|
-
# Show session list
|
|
111
|
-
print("\nSession list:")
|
|
112
|
-
for sess in sessions_df:
|
|
113
|
-
print(f" - {sess['session_id']} ({sess['created_at']})")
|
|
114
|
-
print(
|
|
115
|
-
f" Timesteps: {sess['num_timesteps']}, Events: {sess['num_events']}, Messages: {sess['num_messages']}"
|
|
116
|
-
)
|
|
117
|
-
finally:
|
|
118
|
-
await db.close()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
async def show_model_usage(db_url: str, model_name: str | None = None):
|
|
122
|
-
"""Show model usage statistics."""
|
|
123
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
124
|
-
await db.initialize()
|
|
125
|
-
|
|
126
|
-
try:
|
|
127
|
-
df = await db.get_model_usage(model_name=model_name)
|
|
128
|
-
|
|
129
|
-
if df.empty:
|
|
130
|
-
print("No model usage data found.")
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
print(f"\n{'=' * 100}")
|
|
134
|
-
print(f"{'Model Usage Statistics':^100}")
|
|
135
|
-
print(f"{'=' * 100}\n")
|
|
136
|
-
|
|
137
|
-
print(df.to_string(index=False))
|
|
138
|
-
finally:
|
|
139
|
-
await db.close()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
async def main():
|
|
143
|
-
parser = argparse.ArgumentParser(description="Query experiments from Turso/sqld (v3)")
|
|
144
|
-
parser.add_argument(
|
|
145
|
-
"-u", "--url", default="sqlite+libsql://http://127.0.0.1:8080", help="Turso database URL"
|
|
146
|
-
)
|
|
147
|
-
parser.add_argument(
|
|
148
|
-
"-e", "--experiment", help="Show details for specific experiment ID (can be partial)"
|
|
149
|
-
)
|
|
150
|
-
parser.add_argument("-m", "--model", help="Show usage for specific model")
|
|
151
|
-
parser.add_argument("--usage", action="store_true", help="Show model usage statistics")
|
|
152
|
-
|
|
153
|
-
args = parser.parse_args()
|
|
154
|
-
|
|
155
|
-
if args.usage or args.model:
|
|
156
|
-
await show_model_usage(args.url, args.model)
|
|
157
|
-
elif args.experiment:
|
|
158
|
-
await show_experiment_details(args.url, args.experiment)
|
|
159
|
-
else:
|
|
160
|
-
await list_experiments(args.url)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if __name__ == "__main__":
|
|
164
|
-
asyncio.run(main())
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Query experiments and sessions from Turso/sqld using v3 tracing.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import asyncio
|
|
8
|
-
|
|
9
|
-
from synth_ai.tracing_v3.turso import NativeLibsqlTraceManager
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
async def list_experiments(db_url: str):
|
|
13
|
-
"""List all experiments in the database."""
|
|
14
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
15
|
-
await db.initialize()
|
|
16
|
-
|
|
17
|
-
try:
|
|
18
|
-
df = await db.query_traces("""
|
|
19
|
-
SELECT
|
|
20
|
-
e.experiment_id,
|
|
21
|
-
e.name,
|
|
22
|
-
e.description,
|
|
23
|
-
e.created_at,
|
|
24
|
-
COUNT(DISTINCT st.session_id) as num_sessions,
|
|
25
|
-
COUNT(DISTINCT ev.id) as num_events,
|
|
26
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
|
|
27
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
|
|
28
|
-
FROM experiments e
|
|
29
|
-
LEFT JOIN session_traces st ON e.experiment_id = st.experiment_id
|
|
30
|
-
LEFT JOIN events ev ON st.session_id = ev.session_id
|
|
31
|
-
GROUP BY e.experiment_id, e.name, e.description, e.created_at
|
|
32
|
-
ORDER BY e.created_at DESC
|
|
33
|
-
""")
|
|
34
|
-
|
|
35
|
-
if df.empty:
|
|
36
|
-
print("No experiments found in database.")
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
print(f"\n{'=' * 100}")
|
|
40
|
-
print(f"{'Experiments in ' + db_url:^100}")
|
|
41
|
-
print(f"{'=' * 100}\n")
|
|
42
|
-
|
|
43
|
-
for _, row in df.iterrows():
|
|
44
|
-
print(f"๐งช {row['name']} (id: {row['experiment_id'][:8]}...)")
|
|
45
|
-
print(f" Created: {row['created_at']}")
|
|
46
|
-
print(f" Description: {row['description']}")
|
|
47
|
-
print(f" Sessions: {row['num_sessions']}")
|
|
48
|
-
print(f" Events: {row['num_events']:,}")
|
|
49
|
-
if row["total_cost"] and row["total_cost"] > 0:
|
|
50
|
-
print(f" Cost: ${row['total_cost']:.4f}")
|
|
51
|
-
if row["total_tokens"] and row["total_tokens"] > 0:
|
|
52
|
-
print(f" Tokens: {int(row['total_tokens']):,}")
|
|
53
|
-
print()
|
|
54
|
-
finally:
|
|
55
|
-
await db.close()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async def show_experiment_details(db_url: str, experiment_id: str):
|
|
59
|
-
"""Show detailed information about a specific experiment."""
|
|
60
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
61
|
-
await db.initialize()
|
|
62
|
-
|
|
63
|
-
try:
|
|
64
|
-
# Get experiment info
|
|
65
|
-
exp_df = await db.query_traces(
|
|
66
|
-
"""
|
|
67
|
-
SELECT * FROM experiments WHERE experiment_id LIKE :exp_id
|
|
68
|
-
""",
|
|
69
|
-
{"exp_id": f"{experiment_id}%"},
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
if exp_df.empty:
|
|
73
|
-
print(f"No experiment found matching ID: {experiment_id}")
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
exp = exp_df.iloc[0]
|
|
77
|
-
print(f"\n{'=' * 100}")
|
|
78
|
-
print(f"Experiment: {exp['name']} ({exp['experiment_id']})")
|
|
79
|
-
print(f"{'=' * 100}\n")
|
|
80
|
-
|
|
81
|
-
# Get session statistics
|
|
82
|
-
sessions_df = await db.get_sessions_by_experiment(exp["experiment_id"])
|
|
83
|
-
|
|
84
|
-
if sessions_df:
|
|
85
|
-
print(f"Sessions: {len(sessions_df)}")
|
|
86
|
-
|
|
87
|
-
# Get aggregated stats
|
|
88
|
-
stats_df = await db.query_traces(
|
|
89
|
-
"""
|
|
90
|
-
SELECT
|
|
91
|
-
COUNT(DISTINCT ev.id) as total_events,
|
|
92
|
-
COUNT(DISTINCT m.id) as total_messages,
|
|
93
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
|
|
94
|
-
SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
|
|
95
|
-
FROM session_traces st
|
|
96
|
-
LEFT JOIN events ev ON st.session_id = ev.session_id
|
|
97
|
-
LEFT JOIN messages m ON st.session_id = m.session_id
|
|
98
|
-
WHERE st.experiment_id = :exp_id
|
|
99
|
-
""",
|
|
100
|
-
{"exp_id": exp["experiment_id"]},
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if not stats_df.empty:
|
|
104
|
-
stats = stats_df.iloc[0]
|
|
105
|
-
print(f"Total events: {int(stats['total_events']):,}")
|
|
106
|
-
print(f"Total messages: {int(stats['total_messages']):,}")
|
|
107
|
-
print(f"Total cost: ${stats['total_cost']:.4f}")
|
|
108
|
-
print(f"Total tokens: {int(stats['total_tokens']):,}")
|
|
109
|
-
|
|
110
|
-
# Show session list
|
|
111
|
-
print("\nSession list:")
|
|
112
|
-
for sess in sessions_df:
|
|
113
|
-
print(f" - {sess['session_id']} ({sess['created_at']})")
|
|
114
|
-
print(
|
|
115
|
-
f" Timesteps: {sess['num_timesteps']}, Events: {sess['num_events']}, Messages: {sess['num_messages']}"
|
|
116
|
-
)
|
|
117
|
-
finally:
|
|
118
|
-
await db.close()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
async def show_model_usage(db_url: str, model_name: str | None = None):
|
|
122
|
-
"""Show model usage statistics."""
|
|
123
|
-
db = NativeLibsqlTraceManager(db_url)
|
|
124
|
-
await db.initialize()
|
|
125
|
-
|
|
126
|
-
try:
|
|
127
|
-
df = await db.get_model_usage(model_name=model_name)
|
|
128
|
-
|
|
129
|
-
if df.empty:
|
|
130
|
-
print("No model usage data found.")
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
print(f"\n{'=' * 100}")
|
|
134
|
-
print(f"{'Model Usage Statistics':^100}")
|
|
135
|
-
print(f"{'=' * 100}\n")
|
|
136
|
-
|
|
137
|
-
print(df.to_string(index=False))
|
|
138
|
-
finally:
|
|
139
|
-
await db.close()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
async def main():
|
|
143
|
-
parser = argparse.ArgumentParser(description="Query experiments from Turso/sqld (v3)")
|
|
144
|
-
parser.add_argument(
|
|
145
|
-
"-u", "--url", default="sqlite+libsql://http://127.0.0.1:8080", help="Turso database URL"
|
|
146
|
-
)
|
|
147
|
-
parser.add_argument(
|
|
148
|
-
"-e", "--experiment", help="Show details for specific experiment ID (can be partial)"
|
|
149
|
-
)
|
|
150
|
-
parser.add_argument("-m", "--model", help="Show usage for specific model")
|
|
151
|
-
parser.add_argument("--usage", action="store_true", help="Show model usage statistics")
|
|
152
|
-
|
|
153
|
-
args = parser.parse_args()
|
|
154
|
-
|
|
155
|
-
if args.usage or args.model:
|
|
156
|
-
await show_model_usage(args.url, args.model)
|
|
157
|
-
elif args.experiment:
|
|
158
|
-
await show_experiment_details(args.url, args.experiment)
|
|
159
|
-
else:
|
|
160
|
-
await list_experiments(args.url)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if __name__ == "__main__":
|
|
164
|
-
asyncio.run(main())
|