synth-ai 0.2.4.dev5__py3-none-any.whl → 0.2.4.dev7__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 (229) hide show
  1. synth_ai/__init__.py +18 -9
  2. synth_ai/cli/__init__.py +10 -5
  3. synth_ai/cli/balance.py +22 -17
  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 +1 -3
  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 +21 -17
  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 +29 -0
  104. synth_ai/environments/examples/wordle/engine.py +398 -0
  105. synth_ai/environments/examples/wordle/environment.py +159 -0
  106. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +75 -0
  107. synth_ai/environments/examples/wordle/taskset.py +230 -0
  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 +11 -2
  111. synth_ai/environments/service/core_routes.py +137 -105
  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/learning/gateway.py +1 -3
  123. synth_ai/learning/prompts/banking77_injection_eval.py +168 -0
  124. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +213 -0
  125. synth_ai/learning/prompts/mipro.py +282 -1
  126. synth_ai/learning/prompts/random_search.py +246 -0
  127. synth_ai/learning/prompts/run_mipro_banking77.py +172 -0
  128. synth_ai/learning/prompts/run_random_search_banking77.py +324 -0
  129. synth_ai/lm/__init__.py +5 -5
  130. synth_ai/lm/caching/ephemeral.py +9 -9
  131. synth_ai/lm/caching/handler.py +20 -20
  132. synth_ai/lm/caching/persistent.py +10 -10
  133. synth_ai/lm/config.py +3 -3
  134. synth_ai/lm/constants.py +7 -7
  135. synth_ai/lm/core/all.py +17 -3
  136. synth_ai/lm/core/exceptions.py +0 -2
  137. synth_ai/lm/core/main.py +26 -41
  138. synth_ai/lm/core/main_v3.py +20 -10
  139. synth_ai/lm/core/vendor_clients.py +18 -17
  140. synth_ai/lm/injection.py +80 -0
  141. synth_ai/lm/overrides.py +206 -0
  142. synth_ai/lm/provider_support/__init__.py +1 -1
  143. synth_ai/lm/provider_support/anthropic.py +51 -24
  144. synth_ai/lm/provider_support/openai.py +51 -22
  145. synth_ai/lm/structured_outputs/handler.py +34 -32
  146. synth_ai/lm/structured_outputs/inject.py +24 -27
  147. synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
  148. synth_ai/lm/tools/base.py +17 -16
  149. synth_ai/lm/unified_interface.py +17 -18
  150. synth_ai/lm/vendors/base.py +20 -18
  151. synth_ai/lm/vendors/core/anthropic_api.py +50 -25
  152. synth_ai/lm/vendors/core/gemini_api.py +31 -36
  153. synth_ai/lm/vendors/core/mistral_api.py +19 -19
  154. synth_ai/lm/vendors/core/openai_api.py +11 -10
  155. synth_ai/lm/vendors/openai_standard.py +144 -88
  156. synth_ai/lm/vendors/openai_standard_responses.py +74 -61
  157. synth_ai/lm/vendors/retries.py +9 -1
  158. synth_ai/lm/vendors/supported/custom_endpoint.py +26 -26
  159. synth_ai/lm/vendors/supported/deepseek.py +10 -10
  160. synth_ai/lm/vendors/supported/grok.py +8 -8
  161. synth_ai/lm/vendors/supported/ollama.py +2 -1
  162. synth_ai/lm/vendors/supported/openrouter.py +11 -9
  163. synth_ai/lm/vendors/synth_client.py +69 -63
  164. synth_ai/lm/warmup.py +8 -7
  165. synth_ai/tracing/__init__.py +22 -10
  166. synth_ai/tracing_v1/__init__.py +22 -20
  167. synth_ai/tracing_v3/__init__.py +7 -7
  168. synth_ai/tracing_v3/abstractions.py +56 -52
  169. synth_ai/tracing_v3/config.py +4 -2
  170. synth_ai/tracing_v3/db_config.py +6 -8
  171. synth_ai/tracing_v3/decorators.py +29 -30
  172. synth_ai/tracing_v3/examples/basic_usage.py +12 -12
  173. synth_ai/tracing_v3/hooks.py +21 -21
  174. synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
  175. synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
  176. synth_ai/tracing_v3/migration_helper.py +3 -5
  177. synth_ai/tracing_v3/replica_sync.py +30 -32
  178. synth_ai/tracing_v3/session_tracer.py +35 -29
  179. synth_ai/tracing_v3/storage/__init__.py +1 -1
  180. synth_ai/tracing_v3/storage/base.py +8 -7
  181. synth_ai/tracing_v3/storage/config.py +4 -4
  182. synth_ai/tracing_v3/storage/factory.py +4 -4
  183. synth_ai/tracing_v3/storage/utils.py +9 -9
  184. synth_ai/tracing_v3/turso/__init__.py +3 -3
  185. synth_ai/tracing_v3/turso/daemon.py +9 -9
  186. synth_ai/tracing_v3/turso/manager.py +60 -48
  187. synth_ai/tracing_v3/turso/models.py +24 -19
  188. synth_ai/tracing_v3/utils.py +5 -5
  189. synth_ai/tui/__main__.py +1 -1
  190. synth_ai/tui/cli/query_experiments.py +2 -3
  191. synth_ai/tui/cli/query_experiments_v3.py +2 -3
  192. synth_ai/tui/dashboard.py +97 -86
  193. synth_ai/v0/tracing/abstractions.py +28 -28
  194. synth_ai/v0/tracing/base_client.py +9 -9
  195. synth_ai/v0/tracing/client_manager.py +7 -7
  196. synth_ai/v0/tracing/config.py +7 -7
  197. synth_ai/v0/tracing/context.py +6 -6
  198. synth_ai/v0/tracing/decorators.py +6 -5
  199. synth_ai/v0/tracing/events/manage.py +1 -1
  200. synth_ai/v0/tracing/events/store.py +5 -4
  201. synth_ai/v0/tracing/immediate_client.py +4 -5
  202. synth_ai/v0/tracing/local.py +3 -3
  203. synth_ai/v0/tracing/log_client_base.py +4 -5
  204. synth_ai/v0/tracing/retry_queue.py +5 -6
  205. synth_ai/v0/tracing/trackers.py +25 -25
  206. synth_ai/v0/tracing/upload.py +6 -0
  207. synth_ai/v0/tracing_v1/__init__.py +1 -1
  208. synth_ai/v0/tracing_v1/abstractions.py +28 -28
  209. synth_ai/v0/tracing_v1/base_client.py +9 -9
  210. synth_ai/v0/tracing_v1/client_manager.py +7 -7
  211. synth_ai/v0/tracing_v1/config.py +7 -7
  212. synth_ai/v0/tracing_v1/context.py +6 -6
  213. synth_ai/v0/tracing_v1/decorators.py +7 -6
  214. synth_ai/v0/tracing_v1/events/manage.py +1 -1
  215. synth_ai/v0/tracing_v1/events/store.py +5 -4
  216. synth_ai/v0/tracing_v1/immediate_client.py +4 -5
  217. synth_ai/v0/tracing_v1/local.py +3 -3
  218. synth_ai/v0/tracing_v1/log_client_base.py +4 -5
  219. synth_ai/v0/tracing_v1/retry_queue.py +5 -6
  220. synth_ai/v0/tracing_v1/trackers.py +25 -25
  221. synth_ai/v0/tracing_v1/upload.py +25 -24
  222. synth_ai/zyk/__init__.py +1 -0
  223. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +2 -11
  224. synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
  225. synth_ai-0.2.4.dev5.dist-info/RECORD +0 -287
  226. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
  227. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
  228. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
  229. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/top_level.txt +0 -0
@@ -35,19 +35,21 @@ Concepts:
35
35
  """
36
36
 
37
37
  from __future__ import annotations
38
- from dataclasses import dataclass, field, asdict
38
+
39
+ from dataclasses import asdict, dataclass, field
39
40
  from datetime import datetime
40
- from typing import Any, Dict, List, Optional
41
+ from typing import Any
42
+
41
43
  from .lm_call_record_abstractions import LLMCallRecord
42
44
 
43
45
 
44
46
  @dataclass
45
47
  class TimeRecord:
46
48
  """Time information for events and messages.
47
-
49
+
48
50
  This class captures timing information with microsecond precision for event
49
51
  correlation and performance analysis.
50
-
52
+
51
53
  Attributes:
52
54
  event_time: Unix timestamp (float) when the event occurred. This is the
53
55
  primary timestamp used for ordering and correlation.
@@ -56,17 +58,17 @@ class TimeRecord:
56
58
  """
57
59
 
58
60
  event_time: float
59
- message_time: Optional[int] = None
61
+ message_time: int | None = None
60
62
 
61
63
 
62
64
  @dataclass
63
65
  class SessionEventMarkovBlanketMessage:
64
66
  """Message crossing Markov blanket boundaries between systems in a session.
65
-
67
+
66
68
  IMPORTANT: This represents information transfer BETWEEN distinct systems/subsystems,
67
69
  where each system is conceptualized as having a Markov blanket that separates its
68
70
  internal states from the external environment. These messages cross those boundaries.
69
-
71
+
70
72
  This is NOT for chat messages within an LLM conversation (those belong in LLMCallRecord).
71
73
  Instead, this captures inter-system communication such as:
72
74
  - Human -> Agent system (user providing instructions)
@@ -75,11 +77,11 @@ class SessionEventMarkovBlanketMessage:
75
77
  - Environment -> Runtime (returning results)
76
78
  - Runtime -> Agent (passing back results)
77
79
  - Agent -> Human (final response)
78
-
80
+
79
81
  Each system maintains its own internal state and processing, but can only influence
80
82
  other systems through these explicit boundary-crossing messages. This follows the
81
83
  Free Energy Principle where systems minimize surprise by maintaining boundaries.
82
-
84
+
83
85
  Attributes:
84
86
  content: The actual message content crossing the boundary (text, JSON, etc.)
85
87
  message_type: Type of boundary crossing (e.g., 'observation', 'action', 'result')
@@ -98,17 +100,17 @@ class SessionEventMarkovBlanketMessage:
98
100
  content: str
99
101
  message_type: str
100
102
  time_record: TimeRecord
101
- metadata: Dict[str, Any] = field(default_factory=dict)
103
+ metadata: dict[str, Any] = field(default_factory=dict)
102
104
 
103
105
 
104
106
  @dataclass
105
107
  class BaseEvent:
106
108
  """Base class for all event types.
107
-
109
+
108
110
  This is the foundation for all events in the tracing system. Every event must
109
111
  have a system identifier and timing information. Events are intra-system facts
110
112
  (they occur within a subsystem) and are not necessarily direct communications.
111
-
113
+
112
114
  Attributes:
113
115
  system_instance_id: Identifier for the system/component that generated
114
116
  this event (e.g., 'llm', 'environment', 'tool_executor')
@@ -123,37 +125,37 @@ class BaseEvent:
123
125
 
124
126
  system_instance_id: str
125
127
  time_record: TimeRecord
126
- metadata: Dict[str, Any] = field(default_factory=dict)
127
- event_metadata: Optional[List[Any]] = None
128
+ metadata: dict[str, Any] = field(default_factory=dict)
129
+ event_metadata: list[Any] | None = None
128
130
 
129
131
 
130
132
  @dataclass
131
133
  class RuntimeEvent(BaseEvent):
132
134
  """Event from runtime system.
133
-
135
+
134
136
  Captures events from the AI system's runtime, typically representing decisions
135
137
  or actions taken by the system (e.g., selecting a tool with arguments).
136
138
  Use paired SessionEventMessages to record the communication of this choice to
137
139
  the environment.
138
-
140
+
139
141
  Attributes:
140
142
  actions: List of action identifiers or indices. The interpretation
141
143
  depends on the system (e.g., discrete action indices for RL,
142
144
  tool selection IDs for agents, etc.)
143
145
  """
144
146
 
145
- actions: List[int] = field(default_factory=list)
147
+ actions: list[int] = field(default_factory=list)
146
148
 
147
149
 
148
150
  @dataclass
149
151
  class EnvironmentEvent(BaseEvent):
150
152
  """Event from environment.
151
-
153
+
152
154
  Captures feedback from the environment in response to system actions (e.g.,
153
155
  command output, exit codes, observations). Use a paired SessionEventMessage
154
156
  to record the environment-to-agent communication of the result.
155
157
  Follows the Gymnasium/OpenAI Gym convention for compatibility.
156
-
158
+
157
159
  Attributes:
158
160
  reward: Scalar reward signal from the environment
159
161
  terminated: Whether the episode ended due to reaching a terminal state
@@ -165,19 +167,19 @@ class EnvironmentEvent(BaseEvent):
165
167
  reward: float = 0.0
166
168
  terminated: bool = False
167
169
  truncated: bool = False
168
- system_state_before: Optional[Dict[str, Any]] = None
169
- system_state_after: Optional[Dict[str, Any]] = None
170
+ system_state_before: dict[str, Any] | None = None
171
+ system_state_after: dict[str, Any] | None = None
170
172
 
171
173
 
172
174
  @dataclass
173
175
  class LMCAISEvent(BaseEvent):
174
176
  """Extended CAIS event for language model interactions.
175
-
177
+
176
178
  CAIS (Claude AI System) events capture detailed information about LLM calls,
177
179
  including performance metrics, cost tracking, and distributed tracing support.
178
180
  Treat provider-specific prompt/completion structures as part of this event's
179
181
  data. Do not emit them as SessionEventMessages.
180
-
182
+
181
183
  Attributes:
182
184
  model_name: The specific model used (e.g., 'gpt-4', 'claude-3-opus')
183
185
  provider: LLM provider (e.g., 'openai', 'anthropic', 'local')
@@ -195,27 +197,27 @@ class LMCAISEvent(BaseEvent):
195
197
  """
196
198
 
197
199
  model_name: str = ""
198
- provider: Optional[str] = None
199
- input_tokens: Optional[int] = None
200
- output_tokens: Optional[int] = None
201
- total_tokens: Optional[int] = None
202
- cost_usd: Optional[float] = None
203
- latency_ms: Optional[int] = None
204
- span_id: Optional[str] = None
205
- trace_id: Optional[str] = None
206
- system_state_before: Optional[Dict[str, Any]] = None
207
- system_state_after: Optional[Dict[str, Any]] = None
208
- call_records: List[LLMCallRecord] = field(default_factory=list)
200
+ provider: str | None = None
201
+ input_tokens: int | None = None
202
+ output_tokens: int | None = None
203
+ total_tokens: int | None = None
204
+ cost_usd: float | None = None
205
+ latency_ms: int | None = None
206
+ span_id: str | None = None
207
+ trace_id: str | None = None
208
+ system_state_before: dict[str, Any] | None = None
209
+ system_state_after: dict[str, Any] | None = None
210
+ call_records: list[LLMCallRecord] = field(default_factory=list)
209
211
 
210
212
 
211
213
  @dataclass
212
214
  class SessionTimeStep:
213
215
  """A logical timestep within a session.
214
-
216
+
215
217
  Represents a discrete step in the session timeline. In conversational AI,
216
218
  this often corresponds to a single turn of dialogue. In RL systems, it
217
219
  might represent a single environment step.
218
-
220
+
219
221
  Attributes:
220
222
  step_id: Unique identifier for this step (e.g., 'turn_1', 'step_42')
221
223
  step_index: Sequential index of this step within the session
@@ -231,21 +233,21 @@ class SessionTimeStep:
231
233
  step_id: str = ""
232
234
  step_index: int = 0
233
235
  timestamp: datetime = field(default_factory=datetime.utcnow)
234
- turn_number: Optional[int] = None
235
- events: List[BaseEvent] = field(default_factory=list)
236
- markov_blanket_messages: List[SessionEventMarkovBlanketMessage] = field(default_factory=list)
237
- step_metadata: Dict[str, Any] = field(default_factory=dict)
238
- completed_at: Optional[datetime] = None
236
+ turn_number: int | None = None
237
+ events: list[BaseEvent] = field(default_factory=list)
238
+ markov_blanket_messages: list[SessionEventMarkovBlanketMessage] = field(default_factory=list)
239
+ step_metadata: dict[str, Any] = field(default_factory=dict)
240
+ completed_at: datetime | None = None
239
241
 
240
242
 
241
243
  @dataclass
242
244
  class SessionTrace:
243
245
  """Complete trace of a session.
244
-
246
+
245
247
  The top-level container that holds all data for a single execution session.
246
248
  This could represent a complete conversation, an RL episode, or any other
247
249
  bounded interaction sequence.
248
-
250
+
249
251
  Attributes:
250
252
  session_id: Unique identifier for this session
251
253
  created_at: When the session started (UTC)
@@ -256,7 +258,7 @@ class SessionTrace:
256
258
  'model_config', 'environment_name')
257
259
  session_metadata: Optional list of structured metadata entries that
258
260
  don't fit the dictionary format
259
-
261
+
260
262
  Note:
261
263
  Both event_history and message_history contain the complete record,
262
264
  while individual timesteps also reference their specific events/messages.
@@ -265,15 +267,17 @@ class SessionTrace:
265
267
 
266
268
  session_id: str = ""
267
269
  created_at: datetime = field(default_factory=datetime.utcnow)
268
- session_time_steps: List[SessionTimeStep] = field(default_factory=list)
269
- event_history: List[BaseEvent] = field(default_factory=list)
270
- markov_blanket_message_history: List[SessionEventMarkovBlanketMessage] = field(default_factory=list)
271
- metadata: Dict[str, Any] = field(default_factory=dict)
272
- session_metadata: Optional[List[Dict[str, Any]]] = None
273
-
274
- def to_dict(self) -> Dict[str, Any]:
270
+ session_time_steps: list[SessionTimeStep] = field(default_factory=list)
271
+ event_history: list[BaseEvent] = field(default_factory=list)
272
+ markov_blanket_message_history: list[SessionEventMarkovBlanketMessage] = field(
273
+ default_factory=list
274
+ )
275
+ metadata: dict[str, Any] = field(default_factory=dict)
276
+ session_metadata: list[dict[str, Any]] | None = None
277
+
278
+ def to_dict(self) -> dict[str, Any]:
275
279
  """Convert to dictionary representation.
276
-
280
+
277
281
  Returns:
278
282
  A dictionary containing all session data, suitable for
279
283
  JSON serialization or database storage.
@@ -1,7 +1,7 @@
1
1
  """Configuration for tracing v3 with Turso/sqld."""
2
2
 
3
- from dataclasses import dataclass
4
3
  import os
4
+ from dataclasses import dataclass
5
5
 
6
6
 
7
7
  @dataclass
@@ -28,7 +28,9 @@ class TursoConfig:
28
28
  # Remote database sync configuration
29
29
  sync_url: str = os.getenv("TURSO_DATABASE_URL", "")
30
30
  auth_token: str = os.getenv("TURSO_AUTH_TOKEN", "")
31
- sync_interval: int = int(os.getenv("TURSO_SYNC_SECONDS", "2")) # 2 seconds for responsive local development
31
+ sync_interval: int = int(
32
+ os.getenv("TURSO_SYNC_SECONDS", "2")
33
+ ) # 2 seconds for responsive local development
32
34
 
33
35
  # Connection pool settings
34
36
  pool_size: int = int(os.getenv("TURSO_POOL_SIZE", "8"))
@@ -2,11 +2,9 @@
2
2
  Centralized database configuration for v3 tracing.
3
3
  """
4
4
 
5
- import os
6
- import tempfile
7
- from pathlib import Path
8
- from typing import Optional, Tuple, TYPE_CHECKING
9
5
  import logging
6
+ import os
7
+ from typing import TYPE_CHECKING, Optional
10
8
 
11
9
  if TYPE_CHECKING:
12
10
  from .turso.daemon import SqldDaemon
@@ -22,7 +20,7 @@ class DatabaseConfig:
22
20
  DEFAULT_HTTP_PORT = 8080
23
21
 
24
22
  def __init__(
25
- self, db_path: Optional[str] = None, http_port: Optional[int] = None, use_sqld: bool = True
23
+ self, db_path: str | None = None, http_port: int | None = None, use_sqld: bool = True
26
24
  ):
27
25
  """
28
26
  Initialize database configuration.
@@ -34,7 +32,7 @@ class DatabaseConfig:
34
32
  """
35
33
  self.use_sqld = use_sqld
36
34
  self.http_port = http_port or int(os.getenv("SQLD_HTTP_PORT", self.DEFAULT_HTTP_PORT))
37
- self._daemon: Optional["SqldDaemon"] = None
35
+ self._daemon: SqldDaemon | None = None
38
36
 
39
37
  # Set up database path to match serve.sh configuration
40
38
  if db_path is None:
@@ -106,7 +104,7 @@ class DatabaseConfig:
106
104
  self._daemon.stop()
107
105
  self._daemon = None
108
106
 
109
- def get_daemon_and_url(self, wait_time: float = 2.0) -> Tuple[Optional["SqldDaemon"], str]:
107
+ def get_daemon_and_url(self, wait_time: float = 2.0) -> tuple[Optional["SqldDaemon"], str]:
110
108
  """
111
109
  Get daemon (starting if needed) and database URL.
112
110
 
@@ -121,7 +119,7 @@ class DatabaseConfig:
121
119
 
122
120
 
123
121
  # Global default configuration
124
- _default_config: Optional[DatabaseConfig] = None
122
+ _default_config: DatabaseConfig | None = None
125
123
 
126
124
 
127
125
  def get_default_db_config() -> DatabaseConfig:
@@ -22,58 +22,57 @@ The decorators support both sync and async functions where appropriate,
22
22
  though async is preferred for consistency with the rest of the system.
23
23
  """
24
24
 
25
+ import asyncio
25
26
  import contextvars
26
27
  import functools
27
28
  import time
28
- from typing import Callable, Any, Optional, TypeVar, Union
29
- import asyncio
30
- import inspect
29
+ from collections.abc import Callable
30
+ from typing import Any, TypeVar
31
31
 
32
32
  from .abstractions import LMCAISEvent, TimeRecord
33
- from .utils import detect_provider, calculate_cost
34
-
33
+ from .utils import calculate_cost, detect_provider
35
34
 
36
35
  # Context variables for session and turn tracking
37
36
  # These variables automatically propagate across async call boundaries,
38
37
  # allowing deeply nested code to access tracing context without explicit passing
39
- _session_id_ctx: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
38
+ _session_id_ctx: contextvars.ContextVar[str | None] = contextvars.ContextVar(
40
39
  "session_id", default=None
41
40
  )
42
- _turn_number_ctx: contextvars.ContextVar[Optional[int]] = contextvars.ContextVar(
41
+ _turn_number_ctx: contextvars.ContextVar[int | None] = contextvars.ContextVar(
43
42
  "turn_number", default=None
44
43
  )
45
- _session_tracer_ctx: contextvars.ContextVar[Optional[Any]] = contextvars.ContextVar(
44
+ _session_tracer_ctx: contextvars.ContextVar[Any | None] = contextvars.ContextVar(
46
45
  "session_tracer", default=None
47
46
  )
48
47
 
49
48
 
50
- def set_session_id(session_id: Optional[str]) -> None:
49
+ def set_session_id(session_id: str | None) -> None:
51
50
  """Set the current session ID in context.
52
-
51
+
53
52
  This ID will be available to all async tasks spawned from the current context.
54
53
  Setting to None clears the session context.
55
-
54
+
56
55
  Args:
57
56
  session_id: The session ID to set, or None to clear
58
57
  """
59
58
  _session_id_ctx.set(session_id)
60
59
 
61
60
 
62
- def get_session_id() -> Optional[str]:
61
+ def get_session_id() -> str | None:
63
62
  """Get the current session ID from context.
64
-
63
+
65
64
  Returns:
66
65
  The current session ID if one is set, None otherwise
67
66
  """
68
67
  return _session_id_ctx.get()
69
68
 
70
69
 
71
- def set_turn_number(turn: Optional[int]) -> None:
70
+ def set_turn_number(turn: int | None) -> None:
72
71
  """Set the current turn number in context."""
73
72
  _turn_number_ctx.set(turn)
74
73
 
75
74
 
76
- def get_turn_number() -> Optional[int]:
75
+ def get_turn_number() -> int | None:
77
76
  """Get the current turn number from context."""
78
77
  return _turn_number_ctx.get()
79
78
 
@@ -93,15 +92,15 @@ T = TypeVar("T")
93
92
 
94
93
  def with_session(require: bool = True):
95
94
  """Decorator that ensures a session is active.
96
-
95
+
97
96
  This decorator checks if a session is active before allowing the decorated
98
97
  function to execute. It supports both sync and async functions.
99
-
98
+
100
99
  Args:
101
100
  require: If True, raises RuntimeError when no session is active.
102
101
  If False, allows execution without a session (useful for
103
102
  optional tracing).
104
-
103
+
105
104
  Example:
106
105
  ```python
107
106
  @with_session()
@@ -144,21 +143,21 @@ def trace_llm_call(
144
143
  extract_cost: bool = True,
145
144
  ):
146
145
  """Decorator to trace LLM API calls.
147
-
146
+
148
147
  Automatically records LLM API calls as LMCAISEvent instances. Extracts token
149
148
  counts, calculates costs, and measures latency. Only works with async functions.
150
-
149
+
151
150
  Args:
152
151
  model_name: Model name to record (can be overridden by actual response)
153
152
  system_id: System identifier for the event (default: "llm")
154
153
  extract_tokens: Whether to extract token counts from response
155
154
  extract_cost: Whether to calculate USD cost from token counts
156
-
155
+
157
156
  Expected Response Format:
158
157
  The decorated function should return a dict with:
159
158
  - 'usage': dict with 'prompt_tokens', 'completion_tokens', 'total_tokens'
160
159
  - 'model': actual model name (optional, falls back to model_name param)
161
-
160
+
162
161
  Example:
163
162
  ```python
164
163
  @trace_llm_call(model_name="gpt-4")
@@ -251,14 +250,14 @@ def trace_llm_call(
251
250
 
252
251
  def trace_method(event_type: str = "runtime", system_id: str = None):
253
252
  """Generic method tracing decorator.
254
-
253
+
255
254
  Traces any method call by recording it as a RuntimeEvent. Supports both
256
255
  sync and async methods, though async is preferred.
257
-
256
+
258
257
  Args:
259
258
  event_type: Type of event to create (default: "runtime")
260
259
  system_id: System identifier (defaults to class name)
261
-
260
+
262
261
  Example:
263
262
  ```python
264
263
  class Agent:
@@ -278,7 +277,7 @@ def trace_method(event_type: str = "runtime", system_id: str = None):
278
277
  if not tracer:
279
278
  return await fn(self, *args, **kwargs)
280
279
 
281
- from .abstractions import BaseEvent, RuntimeEvent
280
+ from .abstractions import RuntimeEvent
282
281
 
283
282
  # Use class name as system_id if not provided
284
283
  actual_system_id = system_id or self.__class__.__name__
@@ -314,20 +313,20 @@ def trace_method(event_type: str = "runtime", system_id: str = None):
314
313
 
315
314
  class SessionContext:
316
315
  """Context manager for session tracking.
317
-
316
+
318
317
  Provides a way to temporarily set session context, useful for testing
319
318
  or when you need to manually manage context outside of SessionTracer.
320
-
319
+
321
320
  This context manager properly handles both sync and async contexts,
322
321
  and ensures the previous context is restored on exit.
323
-
322
+
324
323
  Example:
325
324
  ```python
326
325
  # Sync usage
327
326
  with SessionContext("test_session_123", tracer):
328
327
  # Code here sees the test session
329
328
  process_data()
330
-
329
+
331
330
  # Async usage
332
331
  async with SessionContext("test_session_123", tracer):
333
332
  # Async code here sees the test session
@@ -2,10 +2,9 @@
2
2
 
3
3
  import asyncio
4
4
  import time
5
- from datetime import datetime
6
5
 
7
6
  from synth_ai.tracing_v3 import SessionTracer
8
- from synth_ai.tracing_v3.abstractions import LMCAISEvent, EnvironmentEvent, RuntimeEvent, TimeRecord
7
+ from synth_ai.tracing_v3.abstractions import EnvironmentEvent, LMCAISEvent, RuntimeEvent, TimeRecord
9
8
  from synth_ai.tracing_v3.turso.daemon import SqldDaemon
10
9
 
11
10
 
@@ -31,7 +30,7 @@ async def main():
31
30
  print("Starting tracing v3 example...")
32
31
 
33
32
  # Option 1: Start sqld daemon programmatically
34
- with SqldDaemon() as daemon:
33
+ with SqldDaemon():
35
34
  print("✓ Started sqld daemon")
36
35
 
37
36
  # Wait for daemon to be ready
@@ -167,15 +166,16 @@ async def main():
167
166
 
168
167
  tracer.hooks.register("event_recorded", count_events, name="event_counter")
169
168
 
170
- async with tracer.session(metadata={"example": "hooks"}) as session_id:
171
- async with tracer.timestep("hook_test"):
172
- for i in range(3):
173
- event = RuntimeEvent(
174
- system_instance_id="hook_test",
175
- time_record=TimeRecord(event_time=time.time()),
176
- actions=[i],
177
- )
178
- await tracer.record_event(event)
169
+ async with tracer.session(metadata={"example": "hooks"}) as session_id, tracer.timestep(
170
+ "hook_test"
171
+ ):
172
+ for i in range(3):
173
+ event = RuntimeEvent(
174
+ system_instance_id="hook_test",
175
+ time_record=TimeRecord(event_time=time.time()),
176
+ actions=[i],
177
+ )
178
+ await tracer.record_event(event)
179
179
 
180
180
  print(f"✓ Hook called {call_count['count']} times")
181
181
 
@@ -32,18 +32,18 @@ Common Use Cases:
32
32
  - Custom filtering and sampling
33
33
  """
34
34
 
35
- from typing import Any, Callable, Dict, List, Optional
36
- from dataclasses import dataclass
37
35
  import asyncio
38
- import inspect
36
+ from collections.abc import Callable
37
+ from dataclasses import dataclass
38
+ from typing import Any
39
39
 
40
- from .abstractions import SessionTrace, SessionTimeStep, BaseEvent, SessionEventMarkovBlanketMessage
40
+ from .abstractions import BaseEvent
41
41
 
42
42
 
43
43
  @dataclass
44
44
  class Hook:
45
45
  """A hook that can be registered with the tracer.
46
-
46
+
47
47
  Attributes:
48
48
  name: Unique identifier for the hook
49
49
  callback: Function to call when hook is triggered. Can be sync or async.
@@ -54,25 +54,25 @@ class Hook:
54
54
 
55
55
  name: str
56
56
  callback: Callable
57
- event_types: Optional[List[str]] = None
57
+ event_types: list[str] | None = None
58
58
  priority: int = 0
59
59
  enabled: bool = True
60
60
 
61
61
 
62
62
  class HookManager:
63
63
  """Manages hooks for session tracing.
64
-
64
+
65
65
  The HookManager maintains collections of hooks for each hook point and
66
66
  handles their execution. It ensures hooks are called in priority order
67
67
  and handles both sync and async callbacks appropriately.
68
-
68
+
69
69
  Thread Safety:
70
70
  The HookManager is designed to be thread-safe for registration and
71
71
  execution. Multiple async tasks can trigger hooks concurrently.
72
72
  """
73
73
 
74
74
  def __init__(self):
75
- self.hooks: Dict[str, List[Hook]] = {
75
+ self.hooks: dict[str, list[Hook]] = {
76
76
  "session_start": [],
77
77
  "session_end": [],
78
78
  "timestep_start": [],
@@ -89,10 +89,10 @@ class HookManager:
89
89
  callback: Callable,
90
90
  name: str = None,
91
91
  priority: int = 0,
92
- event_types: List[str] = None,
92
+ event_types: list[str] = None,
93
93
  ) -> Hook:
94
94
  """Register a new hook.
95
-
95
+
96
96
  Args:
97
97
  event: Hook point name (e.g., 'session_start', 'event_recorded')
98
98
  callback: Function to call. Signature depends on hook point:
@@ -102,10 +102,10 @@ class HookManager:
102
102
  name: Optional name for the hook (defaults to callback.__name__)
103
103
  priority: Execution priority (higher = earlier execution)
104
104
  event_types: For 'event_recorded' hook, filter to specific event types
105
-
105
+
106
106
  Returns:
107
107
  The created Hook instance
108
-
108
+
109
109
  Raises:
110
110
  ValueError: If the event name is not a valid hook point
111
111
  """
@@ -126,7 +126,7 @@ class HookManager:
126
126
 
127
127
  def unregister(self, event: str, name: str):
128
128
  """Unregister a hook by name.
129
-
129
+
130
130
  Args:
131
131
  event: Hook point name
132
132
  name: Name of the hook to remove
@@ -136,18 +136,18 @@ class HookManager:
136
136
 
137
137
  self.hooks[event] = [h for h in self.hooks[event] if h.name != name]
138
138
 
139
- async def trigger(self, event: str, *args, **kwargs) -> List[Any]:
139
+ async def trigger(self, event: str, *args, **kwargs) -> list[Any]:
140
140
  """Trigger all hooks for an event.
141
-
141
+
142
142
  Executes all registered hooks for the given event in priority order.
143
143
  Handles both sync and async callbacks appropriately. Exceptions in
144
144
  hooks are caught and logged but don't stop execution of other hooks.
145
-
145
+
146
146
  Args:
147
147
  event: Hook point name
148
148
  *args: Positional arguments passed to hook callbacks
149
149
  **kwargs: Keyword arguments passed to hook callbacks
150
-
150
+
151
151
  Returns:
152
152
  List of return values from all executed hooks
153
153
  """
@@ -187,19 +187,19 @@ class HookManager:
187
187
  # Default hooks for common use cases
188
188
  def create_default_hooks() -> HookManager:
189
189
  """Create hook manager with default hooks.
190
-
190
+
191
191
  Sets up a basic set of hooks that provide common functionality:
192
192
  - Session start logging
193
193
  - Event validation
194
194
  - Automatic event enrichment
195
-
195
+
196
196
  Returns:
197
197
  HookManager with default hooks registered
198
198
  """
199
199
  manager = HookManager()
200
200
 
201
201
  # Example: Log session starts - useful for debugging and monitoring
202
- async def log_session_start(session_id: str, metadata: Dict[str, Any]):
202
+ async def log_session_start(session_id: str, metadata: dict[str, Any]):
203
203
  print(f"Session started: {session_id}")
204
204
 
205
205
  # Example: Validate events before recording - ensures data quality