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.

Files changed (192) hide show
  1. examples/analyze_semantic_words.sh +2 -2
  2. examples/blog_posts/pokemon_vl/README.md +98 -0
  3. examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +25 -0
  4. examples/blog_posts/pokemon_vl/configs/eval_rl_final.toml +24 -0
  5. examples/blog_posts/pokemon_vl/configs/filter_high_reward.toml +10 -0
  6. examples/blog_posts/pokemon_vl/configs/train_rl_from_sft.toml +42 -0
  7. examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
  8. examples/blog_posts/warming_up_to_rl/README.md +158 -0
  9. examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -0
  10. examples/blog_posts/warming_up_to_rl/configs/eval_groq_qwen32b.toml +25 -0
  11. examples/blog_posts/warming_up_to_rl/configs/eval_openai_gpt_oss_120b.toml +29 -0
  12. examples/blog_posts/warming_up_to_rl/configs/filter_high_reward_dataset.toml +10 -0
  13. examples/blog_posts/warming_up_to_rl/configs/train_rl_from_sft.toml +41 -0
  14. examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
  15. examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
  16. examples/multi_step/configs/crafter_rl_outcome.toml +1 -1
  17. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +65 -107
  18. examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +1 -1
  19. examples/multi_step/configs/crafter_rl_stepwise_simple.toml +1 -1
  20. examples/multi_step/configs/crafter_rl_stepwise_simple_NEW_FORMAT.toml +105 -0
  21. examples/multi_step/configs/verilog_rl_lora.toml +80 -123
  22. examples/qwen_coder/configs/coder_lora_30b.toml +1 -3
  23. examples/qwen_coder/configs/coder_lora_4b.toml +4 -1
  24. examples/qwen_coder/configs/coder_lora_small.toml +1 -3
  25. examples/qwen_vl/README.md +10 -12
  26. examples/qwen_vl/SETUP_COMPLETE.md +7 -8
  27. examples/qwen_vl/VISION_TESTS_COMPLETE.md +2 -3
  28. examples/qwen_vl/collect_data_via_cli.md +76 -84
  29. examples/qwen_vl/collect_vision_traces.py +4 -4
  30. examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +40 -57
  31. examples/qwen_vl/configs/crafter_vlm_sft_example.toml +1 -2
  32. examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +20 -37
  33. examples/qwen_vl/configs/eval_gpt5nano_vision.toml +21 -40
  34. examples/qwen_vl/configs/eval_qwen3vl_vision.toml +26 -0
  35. examples/qwen_vl/configs/{filter_qwen2vl_sft.toml โ†’ filter_qwen3vl_sft.toml} +4 -5
  36. examples/qwen_vl/configs/filter_vision_sft.toml +2 -3
  37. examples/qwen_vl/crafter_qwen_vl_agent.py +5 -5
  38. examples/qwen_vl/run_vision_comparison.sh +6 -7
  39. examples/rl/README.md +5 -5
  40. examples/rl/configs/rl_from_base_qwen.toml +26 -1
  41. examples/rl/configs/rl_from_base_qwen17.toml +5 -2
  42. examples/rl/task_app/README.md +1 -2
  43. examples/rl/task_app/math_single_step.py +2 -2
  44. examples/run_crafter_demo.sh +2 -2
  45. examples/sft/README.md +1 -1
  46. examples/sft/configs/crafter_fft_qwen0p6b.toml +4 -1
  47. examples/sft/configs/crafter_lora_qwen0p6b.toml +4 -1
  48. examples/swe/task_app/README.md +32 -2
  49. examples/swe/task_app/grpo_swe_mini.py +4 -0
  50. examples/swe/task_app/hosted/envs/crafter/react_agent.py +1 -1
  51. examples/swe/task_app/hosted/envs/mini_swe/environment.py +37 -10
  52. examples/swe/task_app/hosted/inference/openai_client.py +4 -4
  53. examples/swe/task_app/morph_backend.py +178 -0
  54. examples/task_apps/crafter/task_app/README.md +1 -1
  55. examples/task_apps/crafter/task_app/grpo_crafter.py +66 -3
  56. examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +1 -1
  57. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +4 -26
  58. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -2
  59. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +17 -49
  60. examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +13 -5
  61. examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +15 -1
  62. examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
  63. examples/task_apps/math/README.md +1 -2
  64. examples/task_apps/pokemon_red/README.md +3 -4
  65. examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +6 -5
  66. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +1 -2
  67. examples/task_apps/pokemon_red/task_app.py +36 -5
  68. examples/task_apps/sokoban/README.md +2 -3
  69. examples/task_apps/verilog/eval_groq_qwen32b.toml +12 -14
  70. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +1 -1
  71. examples/vlm/configs/crafter_vlm_gpt4o.toml +4 -1
  72. examples/warming_up_to_rl/configs/crafter_fft.toml +4 -1
  73. examples/warming_up_to_rl/configs/crafter_fft_4b.toml +0 -2
  74. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +2 -2
  75. examples/warming_up_to_rl/run_local_rollout_traced.py +1 -1
  76. examples/warming_up_to_rl/task_app/README.md +1 -1
  77. examples/warming_up_to_rl/task_app/grpo_crafter.py +134 -3
  78. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +1 -1
  79. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +3 -27
  80. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -1
  81. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +4 -4
  82. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +6 -3
  83. examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
  84. examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +5 -0
  85. synth_ai/api/train/builders.py +9 -3
  86. synth_ai/api/train/cli.py +125 -10
  87. synth_ai/api/train/configs/__init__.py +8 -1
  88. synth_ai/api/train/configs/rl.py +32 -7
  89. synth_ai/api/train/configs/sft.py +6 -2
  90. synth_ai/api/train/configs/shared.py +59 -2
  91. synth_ai/auth/credentials.py +119 -0
  92. synth_ai/cli/__init__.py +12 -4
  93. synth_ai/cli/commands/__init__.py +17 -0
  94. synth_ai/cli/commands/demo/__init__.py +6 -0
  95. synth_ai/cli/commands/demo/core.py +163 -0
  96. synth_ai/cli/commands/deploy/__init__.py +23 -0
  97. synth_ai/cli/commands/deploy/core.py +614 -0
  98. synth_ai/cli/commands/deploy/errors.py +72 -0
  99. synth_ai/cli/commands/deploy/validation.py +11 -0
  100. synth_ai/cli/commands/eval/__init__.py +19 -0
  101. synth_ai/cli/commands/eval/core.py +1109 -0
  102. synth_ai/cli/commands/eval/errors.py +81 -0
  103. synth_ai/cli/commands/eval/validation.py +133 -0
  104. synth_ai/cli/commands/filter/__init__.py +12 -0
  105. synth_ai/cli/commands/filter/core.py +388 -0
  106. synth_ai/cli/commands/filter/errors.py +55 -0
  107. synth_ai/cli/commands/filter/validation.py +77 -0
  108. synth_ai/cli/commands/help/__init__.py +177 -0
  109. synth_ai/cli/commands/help/core.py +73 -0
  110. synth_ai/cli/commands/status/__init__.py +64 -0
  111. synth_ai/cli/commands/status/client.py +192 -0
  112. synth_ai/cli/commands/status/config.py +92 -0
  113. synth_ai/cli/commands/status/errors.py +20 -0
  114. synth_ai/cli/commands/status/formatters.py +164 -0
  115. synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
  116. synth_ai/cli/commands/status/subcommands/files.py +79 -0
  117. synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
  118. synth_ai/cli/commands/status/subcommands/models.py +79 -0
  119. synth_ai/cli/commands/status/subcommands/runs.py +81 -0
  120. synth_ai/cli/commands/status/subcommands/summary.py +47 -0
  121. synth_ai/cli/commands/status/utils.py +114 -0
  122. synth_ai/cli/commands/train/__init__.py +53 -0
  123. synth_ai/cli/commands/train/core.py +21 -0
  124. synth_ai/cli/commands/train/errors.py +117 -0
  125. synth_ai/cli/commands/train/judge_schemas.py +199 -0
  126. synth_ai/cli/commands/train/judge_validation.py +304 -0
  127. synth_ai/cli/commands/train/validation.py +443 -0
  128. synth_ai/cli/demo.py +2 -162
  129. synth_ai/cli/deploy/__init__.py +28 -0
  130. synth_ai/cli/deploy/core.py +5 -0
  131. synth_ai/cli/deploy/errors.py +23 -0
  132. synth_ai/cli/deploy/validation.py +5 -0
  133. synth_ai/cli/eval/__init__.py +36 -0
  134. synth_ai/cli/eval/core.py +5 -0
  135. synth_ai/cli/eval/errors.py +31 -0
  136. synth_ai/cli/eval/validation.py +5 -0
  137. synth_ai/cli/filter/__init__.py +28 -0
  138. synth_ai/cli/filter/core.py +5 -0
  139. synth_ai/cli/filter/errors.py +23 -0
  140. synth_ai/cli/filter/validation.py +5 -0
  141. synth_ai/cli/modal_serve/__init__.py +12 -0
  142. synth_ai/cli/modal_serve/core.py +14 -0
  143. synth_ai/cli/modal_serve/errors.py +8 -0
  144. synth_ai/cli/modal_serve/validation.py +11 -0
  145. synth_ai/cli/serve/__init__.py +12 -0
  146. synth_ai/cli/serve/core.py +14 -0
  147. synth_ai/cli/serve/errors.py +8 -0
  148. synth_ai/cli/serve/validation.py +11 -0
  149. synth_ai/cli/setup.py +20 -265
  150. synth_ai/cli/status.py +7 -126
  151. synth_ai/cli/task_app_deploy.py +1 -10
  152. synth_ai/cli/task_app_modal_serve.py +4 -9
  153. synth_ai/cli/task_app_serve.py +4 -11
  154. synth_ai/cli/task_apps.py +58 -1487
  155. synth_ai/cli/train/__init__.py +12 -0
  156. synth_ai/cli/train/core.py +21 -0
  157. synth_ai/cli/train/errors.py +8 -0
  158. synth_ai/cli/train/validation.py +24 -0
  159. synth_ai/cli/train.py +1 -14
  160. synth_ai/demos/crafter/grpo_crafter_task_app.py +1 -1
  161. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +1 -1
  162. synth_ai/environments/examples/red/engine.py +33 -12
  163. synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
  164. synth_ai/environments/examples/red/environment.py +26 -0
  165. synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
  166. synth_ai/http.py +12 -0
  167. synth_ai/judge_schemas.py +10 -11
  168. synth_ai/learning/rl/client.py +3 -1
  169. synth_ai/streaming/__init__.py +29 -0
  170. synth_ai/streaming/config.py +94 -0
  171. synth_ai/streaming/handlers.py +469 -0
  172. synth_ai/streaming/streamer.py +301 -0
  173. synth_ai/streaming/types.py +95 -0
  174. synth_ai/task/validators.py +2 -2
  175. synth_ai/tracing_v3/migration_helper.py +1 -2
  176. synth_ai/utils/env.py +25 -18
  177. synth_ai/utils/http.py +4 -1
  178. synth_ai/utils/modal.py +2 -2
  179. {synth_ai-0.2.16.dist-info โ†’ synth_ai-0.2.17.dist-info}/METADATA +8 -3
  180. {synth_ai-0.2.16.dist-info โ†’ synth_ai-0.2.17.dist-info}/RECORD +184 -109
  181. examples/qwen_vl/configs/eval_qwen2vl_vision.toml +0 -44
  182. synth_ai/cli/tui.py +0 -62
  183. synth_ai/tui/__init__.py +0 -5
  184. synth_ai/tui/__main__.py +0 -13
  185. synth_ai/tui/cli/__init__.py +0 -1
  186. synth_ai/tui/cli/query_experiments.py +0 -164
  187. synth_ai/tui/cli/query_experiments_v3.py +0 -164
  188. synth_ai/tui/dashboard.py +0 -911
  189. {synth_ai-0.2.16.dist-info โ†’ synth_ai-0.2.17.dist-info}/WHEEL +0 -0
  190. {synth_ai-0.2.16.dist-info โ†’ synth_ai-0.2.17.dist-info}/entry_points.txt +0 -0
  191. {synth_ai-0.2.16.dist-info โ†’ synth_ai-0.2.17.dist-info}/licenses/LICENSE +0 -0
  192. {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
@@ -1,5 +0,0 @@
1
- """Text User Interface utilities for synth-ai."""
2
-
3
- from .dashboard import main
4
-
5
- __all__ = ["main"]
synth_ai/tui/__main__.py DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Entry point for Synth AI TUI dashboard.
4
-
5
- Usage:
6
- python -m synth_ai.tui
7
- python -m synth_ai.tui --url sqlite+aiosqlite:///path/to/db
8
- """
9
-
10
- from .dashboard import main
11
-
12
- if __name__ == "__main__":
13
- main()
@@ -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())