synth-ai 0.2.4.dev6__py3-none-any.whl → 0.2.4.dev8__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.
Files changed (256) hide show
  1. synth_ai/__init__.py +18 -9
  2. synth_ai/cli/__init__.py +10 -5
  3. synth_ai/cli/balance.py +25 -32
  4. synth_ai/cli/calc.py +2 -3
  5. synth_ai/cli/demo.py +3 -5
  6. synth_ai/cli/legacy_root_backup.py +58 -32
  7. synth_ai/cli/man.py +22 -19
  8. synth_ai/cli/recent.py +9 -8
  9. synth_ai/cli/root.py +58 -13
  10. synth_ai/cli/status.py +13 -6
  11. synth_ai/cli/traces.py +45 -21
  12. synth_ai/cli/watch.py +40 -37
  13. synth_ai/config/base_url.py +47 -2
  14. synth_ai/core/experiment.py +1 -2
  15. synth_ai/environments/__init__.py +2 -6
  16. synth_ai/environments/environment/artifacts/base.py +3 -1
  17. synth_ai/environments/environment/db/sqlite.py +1 -1
  18. synth_ai/environments/environment/registry.py +19 -20
  19. synth_ai/environments/environment/resources/sqlite.py +2 -3
  20. synth_ai/environments/environment/rewards/core.py +3 -2
  21. synth_ai/environments/environment/tools/__init__.py +6 -4
  22. synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
  23. synth_ai/environments/examples/crafter_classic/engine.py +13 -13
  24. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
  25. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
  26. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
  27. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
  28. synth_ai/environments/examples/crafter_classic/environment.py +16 -15
  29. synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
  30. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
  31. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
  32. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
  33. synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
  34. synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
  35. synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
  36. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
  37. synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
  38. synth_ai/environments/examples/crafter_custom/environment.py +13 -13
  39. synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
  40. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
  41. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
  42. synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
  43. synth_ai/environments/examples/enron/engine.py +18 -14
  44. synth_ai/environments/examples/enron/environment.py +12 -11
  45. synth_ai/environments/examples/enron/taskset.py +7 -7
  46. synth_ai/environments/examples/minigrid/__init__.py +6 -6
  47. synth_ai/environments/examples/minigrid/engine.py +6 -6
  48. synth_ai/environments/examples/minigrid/environment.py +6 -6
  49. synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
  50. synth_ai/environments/examples/minigrid/taskset.py +13 -13
  51. synth_ai/environments/examples/nethack/achievements.py +1 -1
  52. synth_ai/environments/examples/nethack/engine.py +8 -7
  53. synth_ai/environments/examples/nethack/environment.py +10 -9
  54. synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
  55. synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
  56. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
  57. synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
  58. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
  59. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
  60. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
  61. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
  62. synth_ai/environments/examples/nethack/taskset.py +5 -5
  63. synth_ai/environments/examples/red/engine.py +9 -8
  64. synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
  65. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
  66. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
  67. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
  68. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
  69. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
  70. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
  71. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
  72. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
  73. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
  74. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
  75. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
  76. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
  77. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
  78. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
  79. synth_ai/environments/examples/red/environment.py +18 -15
  80. synth_ai/environments/examples/red/taskset.py +5 -3
  81. synth_ai/environments/examples/sokoban/engine.py +16 -13
  82. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
  83. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
  84. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
  85. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
  86. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
  87. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
  88. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
  89. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
  90. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
  91. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
  92. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
  93. synth_ai/environments/examples/sokoban/environment.py +15 -14
  94. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
  95. synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
  96. synth_ai/environments/examples/sokoban/taskset.py +13 -10
  97. synth_ai/environments/examples/tictactoe/engine.py +6 -6
  98. synth_ai/environments/examples/tictactoe/environment.py +8 -7
  99. synth_ai/environments/examples/tictactoe/taskset.py +6 -5
  100. synth_ai/environments/examples/verilog/engine.py +4 -3
  101. synth_ai/environments/examples/verilog/environment.py +11 -10
  102. synth_ai/environments/examples/verilog/taskset.py +14 -12
  103. synth_ai/environments/examples/wordle/__init__.py +5 -5
  104. synth_ai/environments/examples/wordle/engine.py +32 -25
  105. synth_ai/environments/examples/wordle/environment.py +21 -16
  106. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
  107. synth_ai/environments/examples/wordle/taskset.py +20 -12
  108. synth_ai/environments/reproducibility/core.py +1 -1
  109. synth_ai/environments/reproducibility/tree.py +21 -21
  110. synth_ai/environments/service/app.py +3 -2
  111. synth_ai/environments/service/core_routes.py +104 -110
  112. synth_ai/environments/service/external_registry.py +1 -2
  113. synth_ai/environments/service/registry.py +1 -1
  114. synth_ai/environments/stateful/core.py +1 -2
  115. synth_ai/environments/stateful/engine.py +1 -1
  116. synth_ai/environments/tasks/api.py +4 -4
  117. synth_ai/environments/tasks/core.py +14 -12
  118. synth_ai/environments/tasks/filters.py +6 -4
  119. synth_ai/environments/tasks/utils.py +13 -11
  120. synth_ai/evals/base.py +2 -3
  121. synth_ai/experimental/synth_oss.py +4 -4
  122. synth_ai/http.py +102 -0
  123. synth_ai/inference/__init__.py +7 -0
  124. synth_ai/inference/client.py +20 -0
  125. synth_ai/jobs/client.py +246 -0
  126. synth_ai/learning/__init__.py +24 -0
  127. synth_ai/learning/client.py +149 -0
  128. synth_ai/learning/config.py +43 -0
  129. synth_ai/learning/constants.py +29 -0
  130. synth_ai/learning/ft_client.py +59 -0
  131. synth_ai/learning/gateway.py +1 -3
  132. synth_ai/learning/health.py +43 -0
  133. synth_ai/learning/jobs.py +205 -0
  134. synth_ai/learning/prompts/banking77_injection_eval.py +15 -10
  135. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
  136. synth_ai/learning/prompts/mipro.py +61 -52
  137. synth_ai/learning/prompts/random_search.py +42 -43
  138. synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
  139. synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
  140. synth_ai/learning/rl_client.py +256 -0
  141. synth_ai/learning/sse.py +58 -0
  142. synth_ai/learning/validators.py +48 -0
  143. synth_ai/lm/__init__.py +5 -5
  144. synth_ai/lm/caching/ephemeral.py +9 -9
  145. synth_ai/lm/caching/handler.py +20 -20
  146. synth_ai/lm/caching/persistent.py +10 -10
  147. synth_ai/lm/config.py +3 -3
  148. synth_ai/lm/constants.py +7 -7
  149. synth_ai/lm/core/all.py +17 -3
  150. synth_ai/lm/core/exceptions.py +0 -2
  151. synth_ai/lm/core/main.py +26 -41
  152. synth_ai/lm/core/main_v3.py +33 -10
  153. synth_ai/lm/core/synth_models.py +48 -0
  154. synth_ai/lm/core/vendor_clients.py +26 -22
  155. synth_ai/lm/injection.py +7 -8
  156. synth_ai/lm/overrides.py +21 -19
  157. synth_ai/lm/provider_support/__init__.py +1 -1
  158. synth_ai/lm/provider_support/anthropic.py +15 -15
  159. synth_ai/lm/provider_support/openai.py +23 -21
  160. synth_ai/lm/structured_outputs/handler.py +34 -32
  161. synth_ai/lm/structured_outputs/inject.py +24 -27
  162. synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
  163. synth_ai/lm/tools/base.py +17 -16
  164. synth_ai/lm/unified_interface.py +17 -18
  165. synth_ai/lm/vendors/base.py +20 -18
  166. synth_ai/lm/vendors/core/anthropic_api.py +36 -27
  167. synth_ai/lm/vendors/core/gemini_api.py +31 -36
  168. synth_ai/lm/vendors/core/mistral_api.py +19 -19
  169. synth_ai/lm/vendors/core/openai_api.py +42 -13
  170. synth_ai/lm/vendors/openai_standard.py +158 -101
  171. synth_ai/lm/vendors/openai_standard_responses.py +74 -61
  172. synth_ai/lm/vendors/retries.py +9 -1
  173. synth_ai/lm/vendors/supported/custom_endpoint.py +38 -28
  174. synth_ai/lm/vendors/supported/deepseek.py +10 -10
  175. synth_ai/lm/vendors/supported/grok.py +8 -8
  176. synth_ai/lm/vendors/supported/ollama.py +2 -1
  177. synth_ai/lm/vendors/supported/openrouter.py +11 -9
  178. synth_ai/lm/vendors/synth_client.py +425 -75
  179. synth_ai/lm/warmup.py +8 -7
  180. synth_ai/rl/__init__.py +30 -0
  181. synth_ai/rl/contracts.py +32 -0
  182. synth_ai/rl/env_keys.py +137 -0
  183. synth_ai/rl/secrets.py +19 -0
  184. synth_ai/scripts/verify_rewards.py +100 -0
  185. synth_ai/task/__init__.py +10 -0
  186. synth_ai/task/contracts.py +120 -0
  187. synth_ai/task/health.py +28 -0
  188. synth_ai/task/validators.py +12 -0
  189. synth_ai/tracing/__init__.py +22 -10
  190. synth_ai/tracing_v1/__init__.py +22 -20
  191. synth_ai/tracing_v3/__init__.py +7 -7
  192. synth_ai/tracing_v3/abstractions.py +56 -52
  193. synth_ai/tracing_v3/config.py +4 -2
  194. synth_ai/tracing_v3/db_config.py +6 -8
  195. synth_ai/tracing_v3/decorators.py +29 -30
  196. synth_ai/tracing_v3/examples/basic_usage.py +12 -12
  197. synth_ai/tracing_v3/hooks.py +24 -22
  198. synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
  199. synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
  200. synth_ai/tracing_v3/migration_helper.py +3 -5
  201. synth_ai/tracing_v3/replica_sync.py +30 -32
  202. synth_ai/tracing_v3/session_tracer.py +158 -31
  203. synth_ai/tracing_v3/storage/__init__.py +1 -1
  204. synth_ai/tracing_v3/storage/base.py +8 -7
  205. synth_ai/tracing_v3/storage/config.py +4 -4
  206. synth_ai/tracing_v3/storage/factory.py +4 -4
  207. synth_ai/tracing_v3/storage/utils.py +9 -9
  208. synth_ai/tracing_v3/turso/__init__.py +3 -3
  209. synth_ai/tracing_v3/turso/daemon.py +9 -9
  210. synth_ai/tracing_v3/turso/manager.py +278 -48
  211. synth_ai/tracing_v3/turso/models.py +77 -19
  212. synth_ai/tracing_v3/utils.py +5 -5
  213. synth_ai/v0/tracing/abstractions.py +28 -28
  214. synth_ai/v0/tracing/base_client.py +9 -9
  215. synth_ai/v0/tracing/client_manager.py +7 -7
  216. synth_ai/v0/tracing/config.py +7 -7
  217. synth_ai/v0/tracing/context.py +6 -6
  218. synth_ai/v0/tracing/decorators.py +6 -5
  219. synth_ai/v0/tracing/events/manage.py +1 -1
  220. synth_ai/v0/tracing/events/store.py +5 -4
  221. synth_ai/v0/tracing/immediate_client.py +4 -5
  222. synth_ai/v0/tracing/local.py +3 -3
  223. synth_ai/v0/tracing/log_client_base.py +4 -5
  224. synth_ai/v0/tracing/retry_queue.py +5 -6
  225. synth_ai/v0/tracing/trackers.py +25 -25
  226. synth_ai/v0/tracing/upload.py +6 -0
  227. synth_ai/v0/tracing_v1/__init__.py +1 -1
  228. synth_ai/v0/tracing_v1/abstractions.py +28 -28
  229. synth_ai/v0/tracing_v1/base_client.py +9 -9
  230. synth_ai/v0/tracing_v1/client_manager.py +7 -7
  231. synth_ai/v0/tracing_v1/config.py +7 -7
  232. synth_ai/v0/tracing_v1/context.py +6 -6
  233. synth_ai/v0/tracing_v1/decorators.py +7 -6
  234. synth_ai/v0/tracing_v1/events/manage.py +1 -1
  235. synth_ai/v0/tracing_v1/events/store.py +5 -4
  236. synth_ai/v0/tracing_v1/immediate_client.py +4 -5
  237. synth_ai/v0/tracing_v1/local.py +3 -3
  238. synth_ai/v0/tracing_v1/log_client_base.py +4 -5
  239. synth_ai/v0/tracing_v1/retry_queue.py +5 -6
  240. synth_ai/v0/tracing_v1/trackers.py +25 -25
  241. synth_ai/v0/tracing_v1/upload.py +25 -24
  242. synth_ai/zyk/__init__.py +1 -0
  243. synth_ai-0.2.4.dev8.dist-info/METADATA +635 -0
  244. synth_ai-0.2.4.dev8.dist-info/RECORD +317 -0
  245. synth_ai/tui/__init__.py +0 -1
  246. synth_ai/tui/__main__.py +0 -13
  247. synth_ai/tui/cli/__init__.py +0 -1
  248. synth_ai/tui/cli/query_experiments.py +0 -165
  249. synth_ai/tui/cli/query_experiments_v3.py +0 -165
  250. synth_ai/tui/dashboard.py +0 -329
  251. synth_ai-0.2.4.dev6.dist-info/METADATA +0 -203
  252. synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
  253. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/WHEEL +0 -0
  254. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/entry_points.txt +0 -0
  255. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/licenses/LICENSE +0 -0
  256. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/top_level.txt +0 -0
@@ -1,165 +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
- from typing import Optional
9
- from synth_ai.tracing_v3.turso.manager import AsyncSQLTraceManager
10
- import pandas as pd
11
-
12
-
13
- async def list_experiments(db_url: str):
14
- """List all experiments in the database."""
15
- db = AsyncSQLTraceManager(db_url)
16
- await db.initialize()
17
-
18
- try:
19
- df = await db.query_traces("""
20
- SELECT
21
- e.experiment_id,
22
- e.name,
23
- e.description,
24
- e.created_at,
25
- COUNT(DISTINCT st.session_id) as num_sessions,
26
- COUNT(DISTINCT ev.id) as num_events,
27
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
28
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
29
- FROM experiments e
30
- LEFT JOIN session_traces st ON e.experiment_id = st.experiment_id
31
- LEFT JOIN events ev ON st.session_id = ev.session_id
32
- GROUP BY e.experiment_id, e.name, e.description, e.created_at
33
- ORDER BY e.created_at DESC
34
- """)
35
-
36
- if df.empty:
37
- print("No experiments found in database.")
38
- return
39
-
40
- print(f"\n{'=' * 100}")
41
- print(f"{'Experiments in ' + db_url:^100}")
42
- print(f"{'=' * 100}\n")
43
-
44
- for _, row in df.iterrows():
45
- print(f"🧪 {row['name']} (id: {row['experiment_id'][:8]}...)")
46
- print(f" Created: {row['created_at']}")
47
- print(f" Description: {row['description']}")
48
- print(f" Sessions: {row['num_sessions']}")
49
- print(f" Events: {row['num_events']:,}")
50
- if row["total_cost"] and row["total_cost"] > 0:
51
- print(f" Cost: ${row['total_cost']:.4f}")
52
- if row["total_tokens"] and row["total_tokens"] > 0:
53
- print(f" Tokens: {int(row['total_tokens']):,}")
54
- print()
55
- finally:
56
- await db.close()
57
-
58
-
59
- async def show_experiment_details(db_url: str, experiment_id: str):
60
- """Show detailed information about a specific experiment."""
61
- db = AsyncSQLTraceManager(db_url)
62
- await db.initialize()
63
-
64
- try:
65
- # Get experiment info
66
- exp_df = await db.query_traces(
67
- """
68
- SELECT * FROM experiments WHERE experiment_id LIKE :exp_id
69
- """,
70
- {"exp_id": f"{experiment_id}%"},
71
- )
72
-
73
- if exp_df.empty:
74
- print(f"No experiment found matching ID: {experiment_id}")
75
- return
76
-
77
- exp = exp_df.iloc[0]
78
- print(f"\n{'=' * 100}")
79
- print(f"Experiment: {exp['name']} ({exp['experiment_id']})")
80
- print(f"{'=' * 100}\n")
81
-
82
- # Get session statistics
83
- sessions_df = await db.get_sessions_by_experiment(exp["experiment_id"])
84
-
85
- if sessions_df:
86
- print(f"Sessions: {len(sessions_df)}")
87
-
88
- # Get aggregated stats
89
- stats_df = await db.query_traces(
90
- """
91
- SELECT
92
- COUNT(DISTINCT ev.id) as total_events,
93
- COUNT(DISTINCT m.id) as total_messages,
94
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
95
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
96
- FROM session_traces st
97
- LEFT JOIN events ev ON st.session_id = ev.session_id
98
- LEFT JOIN messages m ON st.session_id = m.session_id
99
- WHERE st.experiment_id = :exp_id
100
- """,
101
- {"exp_id": exp["experiment_id"]},
102
- )
103
-
104
- if not stats_df.empty:
105
- stats = stats_df.iloc[0]
106
- print(f"Total events: {int(stats['total_events']):,}")
107
- print(f"Total messages: {int(stats['total_messages']):,}")
108
- print(f"Total cost: ${stats['total_cost']:.4f}")
109
- print(f"Total tokens: {int(stats['total_tokens']):,}")
110
-
111
- # Show session list
112
- print("\nSession list:")
113
- for sess in sessions_df:
114
- print(f" - {sess['session_id']} ({sess['created_at']})")
115
- print(
116
- f" Timesteps: {sess['num_timesteps']}, Events: {sess['num_events']}, Messages: {sess['num_messages']}"
117
- )
118
- finally:
119
- await db.close()
120
-
121
-
122
- async def show_model_usage(db_url: str, model_name: Optional[str] = None):
123
- """Show model usage statistics."""
124
- db = AsyncSQLTraceManager(db_url)
125
- await db.initialize()
126
-
127
- try:
128
- df = await db.get_model_usage(model_name=model_name)
129
-
130
- if df.empty:
131
- print("No model usage data found.")
132
- return
133
-
134
- print(f"\n{'=' * 100}")
135
- print(f"{'Model Usage Statistics':^100}")
136
- print(f"{'=' * 100}\n")
137
-
138
- print(df.to_string(index=False))
139
- finally:
140
- await db.close()
141
-
142
-
143
- async def main():
144
- parser = argparse.ArgumentParser(description="Query experiments from Turso/sqld (v3)")
145
- parser.add_argument(
146
- "-u", "--url", default="sqlite+libsql://http://127.0.0.1:8080", help="Turso database URL"
147
- )
148
- parser.add_argument(
149
- "-e", "--experiment", help="Show details for specific experiment ID (can be partial)"
150
- )
151
- parser.add_argument("-m", "--model", help="Show usage for specific model")
152
- parser.add_argument("--usage", action="store_true", help="Show model usage statistics")
153
-
154
- args = parser.parse_args()
155
-
156
- if args.usage or args.model:
157
- await show_model_usage(args.url, args.model)
158
- elif args.experiment:
159
- await show_experiment_details(args.url, args.experiment)
160
- else:
161
- await list_experiments(args.url)
162
-
163
-
164
- if __name__ == "__main__":
165
- asyncio.run(main())
synth_ai/tui/dashboard.py DELETED
@@ -1,329 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Interactive TUI Dashboard for Synth AI experiments.
4
-
5
- Launch with: python -m synth_ai.tui.dashboard
6
- """
7
-
8
- import asyncio
9
- import logging
10
- from datetime import datetime
11
- from typing import List, Optional, Dict, Any
12
- from urllib.parse import urlparse
13
-
14
- from textual.app import App, ComposeResult
15
- from textual.containers import Container, Horizontal, Vertical
16
- from textual.widgets import (
17
- Header, Footer, DataTable, Static, Input, Button,
18
- TabbedContent, TabPane, Label, ProgressBar
19
- )
20
- from textual.reactive import reactive
21
- from textual.binding import Binding
22
- from textual import on
23
- from textual.timer import Timer
24
-
25
- from ..tracing_v3.turso.manager import AsyncSQLTraceManager
26
-
27
- class ExperimentRow:
28
- """Data structure for experiment display."""
29
- def __init__(self, exp_id: str, name: str, description: str,
30
- created_at: datetime, sessions: int, events: int,
31
- messages: int, cost: float, tokens: int):
32
- self.exp_id = exp_id
33
- self.name = name or "Unnamed"
34
- self.description = description or ""
35
- self.created_at = created_at
36
- self.sessions = sessions
37
- self.events = events
38
- self.messages = messages
39
- self.cost = cost
40
- self.tokens = tokens
41
-
42
- def to_row(self) -> List[str]:
43
- """Convert to table row format."""
44
- return [
45
- self.exp_id[:8], # Shortened ID
46
- self.name[:20], # Truncated name
47
- str(self.sessions),
48
- str(self.events),
49
- str(self.messages),
50
- f"${self.cost:.4f}",
51
- f"{self.tokens:,}",
52
- self.created_at.strftime("%H:%M")
53
- ]
54
-
55
- class ExperimentTable(DataTable):
56
- """Custom DataTable for experiments with refresh capability."""
57
-
58
- def __init__(self, **kwargs):
59
- super().__init__(**kwargs)
60
- self.experiments: List[ExperimentRow] = []
61
- self.selected_exp_id: Optional[str] = None
62
-
63
- def setup_table(self):
64
- """Initialize table columns."""
65
- self.add_columns(
66
- "ID", "Name", "Sessions", "Events",
67
- "Messages", "Cost", "Tokens", "Time"
68
- )
69
-
70
- async def refresh_data(self, db_manager: AsyncSQLTraceManager):
71
- """Refresh experiment data from database."""
72
- try:
73
- # Get experiment list with stats using raw query
74
- df = await db_manager.query_traces("""
75
- SELECT
76
- e.experiment_id,
77
- e.name,
78
- e.description,
79
- e.created_at,
80
- COUNT(DISTINCT st.session_id) as num_sessions,
81
- COUNT(DISTINCT ev.id) as num_events,
82
- COUNT(DISTINCT m.id) as num_messages,
83
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.cost_usd ELSE 0 END) / 100.0 as total_cost,
84
- SUM(CASE WHEN ev.event_type = 'cais' THEN ev.total_tokens ELSE 0 END) as total_tokens
85
- FROM experiments e
86
- LEFT JOIN session_traces st ON e.experiment_id = st.experiment_id
87
- LEFT JOIN events ev ON st.session_id = ev.session_id
88
- LEFT JOIN messages m ON st.session_id = m.session_id
89
- GROUP BY e.experiment_id, e.name, e.description, e.created_at
90
- ORDER BY e.created_at DESC
91
- """)
92
-
93
- self.experiments.clear()
94
- self.clear()
95
-
96
- if not df.empty:
97
- for _, row in df.iterrows():
98
- exp_row = ExperimentRow(
99
- exp_id=row['experiment_id'],
100
- name=row['name'],
101
- description=row['description'],
102
- created_at=row['created_at'],
103
- sessions=int(row['num_sessions'] or 0),
104
- events=int(row['num_events'] or 0),
105
- messages=int(row['num_messages'] or 0),
106
- cost=float(row['total_cost'] or 0.0),
107
- tokens=int(row['total_tokens'] or 0)
108
- )
109
- self.experiments.append(exp_row)
110
- self.add_row(*exp_row.to_row(), key=exp_row.exp_id)
111
-
112
- except Exception as e:
113
- logging.error(f"Failed to refresh experiments: {e}")
114
-
115
- def get_selected_experiment(self) -> Optional[ExperimentRow]:
116
- """Get currently selected experiment."""
117
- if self.cursor_row >= 0 and self.cursor_row < len(self.experiments):
118
- return self.experiments[self.cursor_row]
119
- return None
120
-
121
- class ExperimentDetail(Static):
122
- """Detailed view of selected experiment."""
123
-
124
- def __init__(self, **kwargs):
125
- super().__init__(**kwargs)
126
- self.current_experiment: Optional[ExperimentRow] = None
127
-
128
- def update_experiment(self, experiment: Optional[ExperimentRow]):
129
- """Update the displayed experiment details."""
130
- self.current_experiment = experiment
131
- if experiment:
132
- details = f"""
133
- 🔬 **{experiment.name}**
134
- ID: {experiment.exp_id}
135
- Description: {experiment.description or 'No description'}
136
-
137
- 📊 **Statistics**
138
- Sessions: {experiment.sessions}
139
- Events: {experiment.events}
140
- Messages: {experiment.messages}
141
- Cost: ${experiment.cost:.4f}
142
- Tokens: {experiment.tokens:,}
143
-
144
- 🕒 **Created**: {experiment.created_at.strftime('%Y-%m-%d %H:%M:%S')}
145
- """.strip()
146
- else:
147
- details = "Select an experiment to view details"
148
-
149
- self.update(details)
150
-
151
- class DatabaseStatus(Static):
152
- """Display database connection status."""
153
-
154
- connection_status = reactive("🔴 Disconnected")
155
-
156
- def __init__(self, **kwargs):
157
- super().__init__(**kwargs)
158
-
159
- def render(self) -> str:
160
- return f"Database: {self.connection_status}"
161
-
162
- def set_connected(self, url: str):
163
- parsed = urlparse(url)
164
- host_info = f"{parsed.hostname}:{parsed.port}" if parsed.port else str(parsed.hostname)
165
- self.connection_status = f"🟢 Connected ({host_info})"
166
-
167
- def set_disconnected(self, error: str = ""):
168
- error_text = f" - {error}" if error else ""
169
- self.connection_status = f"🔴 Disconnected{error_text}"
170
-
171
- class SynthDashboard(App):
172
- """Main Synth AI TUI Dashboard application."""
173
-
174
- CSS = """
175
- Screen {
176
- layout: grid;
177
- grid-size: 2 3;
178
- grid-gutter: 1;
179
- }
180
-
181
- #header {
182
- column-span: 2;
183
- height: 3;
184
- }
185
-
186
- #experiments-table {
187
- row-span: 2;
188
- }
189
-
190
- #experiment-detail {
191
- height: 1fr;
192
- }
193
-
194
- #status-bar {
195
- column-span: 2;
196
- height: 3;
197
- }
198
-
199
- ExperimentTable {
200
- height: 100%;
201
- }
202
-
203
- ExperimentDetail {
204
- border: solid $primary;
205
- padding: 1;
206
- height: 100%;
207
- }
208
-
209
- DatabaseStatus {
210
- height: 1;
211
- padding: 0 1;
212
- }
213
- """
214
-
215
- BINDINGS = [
216
- Binding("q", "quit", "Quit"),
217
- Binding("r", "refresh", "Refresh"),
218
- Binding("d", "toggle_debug", "Debug"),
219
- ("ctrl+c", "quit", "Quit"),
220
- ]
221
-
222
- def __init__(self, db_url: str = "sqlite+aiosqlite:///./synth_ai.db/dbs/default/data"):
223
- super().__init__()
224
- self.db_url = db_url
225
- self.db_manager: Optional[AsyncSQLTraceManager] = None
226
- self.refresh_timer: Optional[Timer] = None
227
-
228
- def compose(self) -> ComposeResult:
229
- """Create the UI layout."""
230
- yield Header(show_clock=True)
231
-
232
- with Container(id="experiments-table"):
233
- yield Static("🧪 Experiments", classes="section-title")
234
- yield ExperimentTable(id="experiments")
235
-
236
- with Container(id="experiment-detail"):
237
- yield Static("📋 Details", classes="section-title")
238
- yield ExperimentDetail(id="detail")
239
-
240
- with Container(id="status-bar"):
241
- yield DatabaseStatus(id="db-status")
242
- yield Footer()
243
-
244
- async def on_mount(self) -> None:
245
- """Initialize the app when mounted."""
246
- # Setup database connection
247
- try:
248
- self.db_manager = AsyncSQLTraceManager(self.db_url)
249
- await self.db_manager.initialize()
250
-
251
- db_status = self.query_one("#db-status", DatabaseStatus)
252
- db_status.set_connected(self.db_url)
253
-
254
- except Exception as e:
255
- logging.error(f"Failed to connect to database: {e}")
256
- db_status = self.query_one("#db-status", DatabaseStatus)
257
- db_status.set_disconnected(str(e))
258
-
259
- # Setup experiment table
260
- exp_table = self.query_one("#experiments", ExperimentTable)
261
- exp_table.setup_table()
262
-
263
- # Initial data load
264
- await self.action_refresh()
265
-
266
- # Start auto-refresh timer (every 5 seconds)
267
- self.refresh_timer = self.set_interval(5.0, self._auto_refresh)
268
-
269
- async def _auto_refresh(self) -> None:
270
- """Auto-refresh data periodically."""
271
- if self.db_manager:
272
- exp_table = self.query_one("#experiments", ExperimentTable)
273
- await exp_table.refresh_data(self.db_manager)
274
-
275
- async def action_refresh(self) -> None:
276
- """Manual refresh action."""
277
- if self.db_manager:
278
- exp_table = self.query_one("#experiments", ExperimentTable)
279
- await exp_table.refresh_data(self.db_manager)
280
-
281
- async def action_quit(self) -> None:
282
- """Quit the application."""
283
- if self.refresh_timer:
284
- self.refresh_timer.stop()
285
- if self.db_manager:
286
- await self.db_manager.close()
287
- self.exit()
288
-
289
- def action_toggle_debug(self) -> None:
290
- """Toggle debug mode."""
291
- # Could add debug panel or logging level toggle
292
- pass
293
-
294
- @on(DataTable.RowHighlighted, "#experiments")
295
- def on_experiment_selected(self, event: DataTable.RowHighlighted) -> None:
296
- """Handle experiment selection."""
297
- exp_table = self.query_one("#experiments", ExperimentTable)
298
- selected_exp = exp_table.get_selected_experiment()
299
-
300
- detail_panel = self.query_one("#detail", ExperimentDetail)
301
- detail_panel.update_experiment(selected_exp)
302
-
303
- def main():
304
- """Main entry point for the dashboard."""
305
- import argparse
306
-
307
- parser = argparse.ArgumentParser(description="Synth AI Interactive Dashboard")
308
- parser.add_argument(
309
- "-u", "--url",
310
- default="sqlite+libsql://http://127.0.0.1:8080",
311
- help="Database URL (default: sqlite+libsql://http://127.0.0.1:8080)"
312
- )
313
- parser.add_argument(
314
- "--debug",
315
- action="store_true",
316
- help="Enable debug logging"
317
- )
318
-
319
- args = parser.parse_args()
320
-
321
- if args.debug:
322
- logging.basicConfig(level=logging.DEBUG)
323
-
324
- # Run the dashboard
325
- app = SynthDashboard(db_url=args.url)
326
- app.run()
327
-
328
- if __name__ == "__main__":
329
- main()
@@ -1,203 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: synth-ai
3
- Version: 0.2.4.dev6
4
- Summary: Software for aiding the best and multiplying the will - Core AI functionality and tracing
5
- Author-email: Synth AI <josh@usesynth.ai>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/synth-laboratories/synth-ai
8
- Project-URL: Repository, https://github.com/synth-laboratories/synth-ai
9
- Project-URL: Issues, https://github.com/synth-laboratories/synth-ai/issues
10
- Requires-Python: >=3.11
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: pydantic>=2.0.0
14
- Requires-Dist: python-dotenv>=1.0.1
15
- Requires-Dist: requests>=2.32.3
16
- Requires-Dist: urllib3>=2.3.0
17
- Requires-Dist: tqdm>=4.66.4
18
- Requires-Dist: jsonschema>=4.23.0
19
- Requires-Dist: backoff>=2.0.0
20
- Requires-Dist: typing_extensions>=4.0.0
21
- Requires-Dist: openai>=1.99.0
22
- Requires-Dist: anthropic>=0.42.0
23
- Requires-Dist: langfuse<3.0.0,>=2.53.9
24
- Requires-Dist: opentelemetry-api<1.27.0,>=1.26.0
25
- Requires-Dist: opentelemetry-sdk<1.27.0,>=1.26.0
26
- Requires-Dist: diskcache>=5.6.3
27
- Requires-Dist: groq>=0.30.0
28
- Requires-Dist: google-genai>=1.26.0
29
- Requires-Dist: together>=1.5.21
30
- Requires-Dist: mistralai>=1.9.2
31
- Requires-Dist: fastapi>=0.115.12
32
- Requires-Dist: uvicorn>=0.34.2
33
- Requires-Dist: numpy>=2.2.3
34
- Requires-Dist: networkx>=3.4.2
35
- Requires-Dist: redis>=6.2.0
36
- Requires-Dist: duckdb>=1.0.0
37
- Requires-Dist: pandas>=2.2.3
38
- Requires-Dist: ty>=0.0.1a5
39
- Requires-Dist: toml>=0.10.2
40
- Requires-Dist: sqlalchemy>=2.0.42
41
- Requires-Dist: aiosqlite>=0.21.0
42
- Requires-Dist: greenlet>=3.2.3
43
- Requires-Dist: libsql>=0.1.8
44
- Requires-Dist: google-api-core>=2.25.1
45
- Requires-Dist: google-generativeai>=0.8.5
46
- Requires-Dist: crafter>=1.8.3
47
- Requires-Dist: click>=8.1.0
48
- Requires-Dist: textual>=1.1.0
49
- Requires-Dist: openai-harmony>=0.0.1
50
- Requires-Dist: asyncpg>=0.30.0
51
- Requires-Dist: aiohttp>=3.8.0
52
- Requires-Dist: datasets>=4.0.0
53
- Provides-Extra: dev
54
- Requires-Dist: build>=1.2.2.post1; extra == "dev"
55
- Requires-Dist: twine>=4.0.0; extra == "dev"
56
- Requires-Dist: keyring>=24.0.0; extra == "dev"
57
- Requires-Dist: pytest>=8.3.3; extra == "dev"
58
- Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
59
- Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
60
- Requires-Dist: pyright>=1.1.350; extra == "dev"
61
- Requires-Dist: coverage[toml]>=7.3.0; extra == "dev"
62
- Requires-Dist: ruff>=0.1.0; extra == "dev"
63
- Provides-Extra: google
64
- Requires-Dist: google-api-core>=2.0.0; extra == "google"
65
- Requires-Dist: google-generativeai>=0.8.0; extra == "google"
66
- Requires-Dist: google-genai>=1.0.0; extra == "google"
67
- Provides-Extra: mistral
68
- Requires-Dist: mistralai>=1.0.0; extra == "mistral"
69
- Provides-Extra: research
70
- Requires-Dist: crafter>=1.8.3; extra == "research"
71
- Requires-Dist: datasets>=4.0.0; extra == "research"
72
- Provides-Extra: all
73
- Requires-Dist: google-api-core>=2.0.0; extra == "all"
74
- Requires-Dist: google-generativeai>=0.8.0; extra == "all"
75
- Requires-Dist: google-genai>=1.0.0; extra == "all"
76
- Requires-Dist: mistralai>=1.0.0; extra == "all"
77
- Requires-Dist: crafter>=1.8.3; extra == "all"
78
- Requires-Dist: datasets>=4.0.0; extra == "all"
79
- Dynamic: license-file
80
-
81
- # Synth AI
82
-
83
- Modern Compound AI System Development
84
-
85
- **Comprehensive AI Framework for Language Models, Environments, and Observability**
86
-
87
- [![Python](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/)
88
- [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
89
- [![PyPI](https://img.shields.io/badge/PyPI-0.2.3.dev0-orange)](https://pypi.org/project/synth-ai/)
90
- ![Coverage](https://img.shields.io/badge/coverage-0.0%25-red)
91
- ![Tests](https://img.shields.io/badge/tests-17%2F17%20passing-brightgreen)
92
-
93
- A unified framework combining language model capabilities, synthetic environments, and comprehensive tracing for building and evaluating AI agents.
94
-
95
- ## 🚀 Quick Start
96
-
97
- ### Installation
98
-
99
- ```bash
100
- # Basic installation
101
- pip install synth-ai
102
-
103
- # With research environments (includes game environments)
104
- pip install synth-ai[research]
105
-
106
- # Full installation with all providers
107
- pip install synth-ai[all]
108
- ```
109
-
110
- ### Spinning Up
111
-
112
- Start the Synth AI service daemon (includes sqld database + environment service):
113
-
114
- ```bash
115
- # Start both database daemon (port 8080) and environment service (port 8901)
116
- uvx synth-ai serve
117
- ```
118
-
119
- #### Service Command Options
120
-
121
- ```bash
122
- uvx synth-ai serve [OPTIONS]
123
- ```
124
-
125
- **Available Options:**
126
- - `--db-file` - Database file path (default: "synth_ai.db")
127
- - `--sqld-port` - Port for sqld HTTP interface (default: 8080)
128
- - `--env-port` - Port for environment service (default: 8901)
129
- - `--no-sqld` - Skip starting sqld database daemon
130
- - `--no-env` - Skip starting environment service
131
-
132
- **Examples:**
133
- ```bash
134
- # Start with custom ports
135
- uvx synth-ai serve --sqld-port 8081 --env-port 8902
136
-
137
- # Start only the environment service
138
- uvx synth-ai serve --no-sqld
139
-
140
- # Start only the database service
141
- uvx synth-ai serve --no-env
142
- ```
143
-
144
- #### What the Serve Command Provides
145
-
146
- **sqld Database Service (port 8080)**
147
- - Local SQLite-compatible database server with HTTP API
148
- - Automatically downloads and installs sqld binary if needed
149
- - Provides persistent storage for agent interactions and traces
150
-
151
- **Environment Service (port 8901)**
152
- - FastAPI service for managing AI environments and tasks
153
- - Built-in environments: Crafter, Sokoban, MiniGrid, TicTacToe, Verilog, NetHack, Enron
154
- - RESTful API for environment initialization, stepping, and termination
155
- - Dynamic environment registry for custom environments
156
-
157
- In another terminal, run your first example:
158
-
159
- ```bash
160
- # Run a Crafter agent demo with Gemini
161
- ./examples/run_crafter_demo.sh
162
- ```
163
-
164
- This will:
165
- - Start the sqld database daemon with HTTP API on port 8080
166
- - Launch the environment service API on port 8901
167
- - Run a reactive agent in the Crafter environment using Gemini 1.5 Flash
168
-
169
- #### Demos (Eval + Finetuning)
170
-
171
- You can run interactive demos from the repo without remembering exact commands:
172
-
173
- ```bash
174
- # Lists all available demos under examples/, then prompts you to choose
175
- uvx synth-ai demo
176
- ```
177
-
178
- Today this includes:
179
- - Eval demo: `examples/evals/run_demo.sh`
180
- - Prompts for models, episodes, etc.
181
- - Runs Crafter rollouts with v3 tracing, then analyzes and filters traces
182
- - Writes a JSONL like `ft_data/evals_filtered.jsonl` for downstream use
183
- - Finetuning demo: `examples/finetuning/synth_qwen/run_demo.sh`
184
- - Guides you through: rollouts → filter v3 traces → prepare SFT JSONL
185
- - Pair with `uvpm examples.finetuning.synth_qwen.sft_kickoff` to start an SFT job when ready
186
-
187
- Notes:
188
- - Ensure the service is running (`uvx synth-ai serve`) so v3 traces are recorded locally.
189
- - Set API configuration for finetuning:
190
- - `export LEARNING_V2_BASE_URL="http://localhost:8000/api"` (or your proxy)
191
- - `export SYNTH_API_KEY="sk_live_..."`
192
- - v3 trace data is stored under `traces/v3/synth_ai.db/` by default. Inspect with `uvx synth-ai traces`.
193
- - LM tracing: all model calls (prompts, outputs, tool calls, token usage, latency, cost) are automatically captured via v3 tracing and stored locally; inspect with `uvx synth-ai traces`.
194
-
195
- ### One-Command Demos
196
-
197
- Quickly browse and launch interactive demos under `examples/`:
198
-
199
- ```bash
200
- uvx synth-ai demo
201
- ```
202
-
203
- This lists all `run_demo.sh` scripts found in the repo (e.g., eval comparisons, finetuning flows) and lets you pick one to run.