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.
- synth_ai/__init__.py +18 -9
- synth_ai/cli/__init__.py +10 -5
- synth_ai/cli/balance.py +25 -32
- synth_ai/cli/calc.py +2 -3
- synth_ai/cli/demo.py +3 -5
- synth_ai/cli/legacy_root_backup.py +58 -32
- synth_ai/cli/man.py +22 -19
- synth_ai/cli/recent.py +9 -8
- synth_ai/cli/root.py +58 -13
- synth_ai/cli/status.py +13 -6
- synth_ai/cli/traces.py +45 -21
- synth_ai/cli/watch.py +40 -37
- synth_ai/config/base_url.py +47 -2
- synth_ai/core/experiment.py +1 -2
- synth_ai/environments/__init__.py +2 -6
- synth_ai/environments/environment/artifacts/base.py +3 -1
- synth_ai/environments/environment/db/sqlite.py +1 -1
- synth_ai/environments/environment/registry.py +19 -20
- synth_ai/environments/environment/resources/sqlite.py +2 -3
- synth_ai/environments/environment/rewards/core.py +3 -2
- synth_ai/environments/environment/tools/__init__.py +6 -4
- synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
- synth_ai/environments/examples/crafter_classic/engine.py +13 -13
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
- synth_ai/environments/examples/crafter_classic/environment.py +16 -15
- synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
- synth_ai/environments/examples/crafter_custom/environment.py +13 -13
- synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
- synth_ai/environments/examples/enron/engine.py +18 -14
- synth_ai/environments/examples/enron/environment.py +12 -11
- synth_ai/environments/examples/enron/taskset.py +7 -7
- synth_ai/environments/examples/minigrid/__init__.py +6 -6
- synth_ai/environments/examples/minigrid/engine.py +6 -6
- synth_ai/environments/examples/minigrid/environment.py +6 -6
- synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
- synth_ai/environments/examples/minigrid/taskset.py +13 -13
- synth_ai/environments/examples/nethack/achievements.py +1 -1
- synth_ai/environments/examples/nethack/engine.py +8 -7
- synth_ai/environments/examples/nethack/environment.py +10 -9
- synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
- synth_ai/environments/examples/nethack/taskset.py +5 -5
- synth_ai/environments/examples/red/engine.py +9 -8
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
- synth_ai/environments/examples/red/environment.py +18 -15
- synth_ai/environments/examples/red/taskset.py +5 -3
- synth_ai/environments/examples/sokoban/engine.py +16 -13
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
- synth_ai/environments/examples/sokoban/environment.py +15 -14
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
- synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
- synth_ai/environments/examples/sokoban/taskset.py +13 -10
- synth_ai/environments/examples/tictactoe/engine.py +6 -6
- synth_ai/environments/examples/tictactoe/environment.py +8 -7
- synth_ai/environments/examples/tictactoe/taskset.py +6 -5
- synth_ai/environments/examples/verilog/engine.py +4 -3
- synth_ai/environments/examples/verilog/environment.py +11 -10
- synth_ai/environments/examples/verilog/taskset.py +14 -12
- synth_ai/environments/examples/wordle/__init__.py +5 -5
- synth_ai/environments/examples/wordle/engine.py +32 -25
- synth_ai/environments/examples/wordle/environment.py +21 -16
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
- synth_ai/environments/examples/wordle/taskset.py +20 -12
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +3 -2
- synth_ai/environments/service/core_routes.py +104 -110
- synth_ai/environments/service/external_registry.py +1 -2
- synth_ai/environments/service/registry.py +1 -1
- synth_ai/environments/stateful/core.py +1 -2
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/api.py +4 -4
- synth_ai/environments/tasks/core.py +14 -12
- synth_ai/environments/tasks/filters.py +6 -4
- synth_ai/environments/tasks/utils.py +13 -11
- synth_ai/evals/base.py +2 -3
- synth_ai/experimental/synth_oss.py +4 -4
- synth_ai/http.py +102 -0
- synth_ai/inference/__init__.py +7 -0
- synth_ai/inference/client.py +20 -0
- synth_ai/jobs/client.py +246 -0
- synth_ai/learning/__init__.py +24 -0
- synth_ai/learning/client.py +149 -0
- synth_ai/learning/config.py +43 -0
- synth_ai/learning/constants.py +29 -0
- synth_ai/learning/ft_client.py +59 -0
- synth_ai/learning/gateway.py +1 -3
- synth_ai/learning/health.py +43 -0
- synth_ai/learning/jobs.py +205 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +15 -10
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
- synth_ai/learning/prompts/mipro.py +61 -52
- synth_ai/learning/prompts/random_search.py +42 -43
- synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
- synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
- synth_ai/learning/rl_client.py +256 -0
- synth_ai/learning/sse.py +58 -0
- synth_ai/learning/validators.py +48 -0
- synth_ai/lm/__init__.py +5 -5
- synth_ai/lm/caching/ephemeral.py +9 -9
- synth_ai/lm/caching/handler.py +20 -20
- synth_ai/lm/caching/persistent.py +10 -10
- synth_ai/lm/config.py +3 -3
- synth_ai/lm/constants.py +7 -7
- synth_ai/lm/core/all.py +17 -3
- synth_ai/lm/core/exceptions.py +0 -2
- synth_ai/lm/core/main.py +26 -41
- synth_ai/lm/core/main_v3.py +33 -10
- synth_ai/lm/core/synth_models.py +48 -0
- synth_ai/lm/core/vendor_clients.py +26 -22
- synth_ai/lm/injection.py +7 -8
- synth_ai/lm/overrides.py +21 -19
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +15 -15
- synth_ai/lm/provider_support/openai.py +23 -21
- synth_ai/lm/structured_outputs/handler.py +34 -32
- synth_ai/lm/structured_outputs/inject.py +24 -27
- synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
- synth_ai/lm/tools/base.py +17 -16
- synth_ai/lm/unified_interface.py +17 -18
- synth_ai/lm/vendors/base.py +20 -18
- synth_ai/lm/vendors/core/anthropic_api.py +36 -27
- synth_ai/lm/vendors/core/gemini_api.py +31 -36
- synth_ai/lm/vendors/core/mistral_api.py +19 -19
- synth_ai/lm/vendors/core/openai_api.py +42 -13
- synth_ai/lm/vendors/openai_standard.py +158 -101
- synth_ai/lm/vendors/openai_standard_responses.py +74 -61
- synth_ai/lm/vendors/retries.py +9 -1
- synth_ai/lm/vendors/supported/custom_endpoint.py +38 -28
- synth_ai/lm/vendors/supported/deepseek.py +10 -10
- synth_ai/lm/vendors/supported/grok.py +8 -8
- synth_ai/lm/vendors/supported/ollama.py +2 -1
- synth_ai/lm/vendors/supported/openrouter.py +11 -9
- synth_ai/lm/vendors/synth_client.py +425 -75
- synth_ai/lm/warmup.py +8 -7
- synth_ai/rl/__init__.py +30 -0
- synth_ai/rl/contracts.py +32 -0
- synth_ai/rl/env_keys.py +137 -0
- synth_ai/rl/secrets.py +19 -0
- synth_ai/scripts/verify_rewards.py +100 -0
- synth_ai/task/__init__.py +10 -0
- synth_ai/task/contracts.py +120 -0
- synth_ai/task/health.py +28 -0
- synth_ai/task/validators.py +12 -0
- synth_ai/tracing/__init__.py +22 -10
- synth_ai/tracing_v1/__init__.py +22 -20
- synth_ai/tracing_v3/__init__.py +7 -7
- synth_ai/tracing_v3/abstractions.py +56 -52
- synth_ai/tracing_v3/config.py +4 -2
- synth_ai/tracing_v3/db_config.py +6 -8
- synth_ai/tracing_v3/decorators.py +29 -30
- synth_ai/tracing_v3/examples/basic_usage.py +12 -12
- synth_ai/tracing_v3/hooks.py +24 -22
- synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
- synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
- synth_ai/tracing_v3/migration_helper.py +3 -5
- synth_ai/tracing_v3/replica_sync.py +30 -32
- synth_ai/tracing_v3/session_tracer.py +158 -31
- synth_ai/tracing_v3/storage/__init__.py +1 -1
- synth_ai/tracing_v3/storage/base.py +8 -7
- synth_ai/tracing_v3/storage/config.py +4 -4
- synth_ai/tracing_v3/storage/factory.py +4 -4
- synth_ai/tracing_v3/storage/utils.py +9 -9
- synth_ai/tracing_v3/turso/__init__.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +9 -9
- synth_ai/tracing_v3/turso/manager.py +278 -48
- synth_ai/tracing_v3/turso/models.py +77 -19
- synth_ai/tracing_v3/utils.py +5 -5
- synth_ai/v0/tracing/abstractions.py +28 -28
- synth_ai/v0/tracing/base_client.py +9 -9
- synth_ai/v0/tracing/client_manager.py +7 -7
- synth_ai/v0/tracing/config.py +7 -7
- synth_ai/v0/tracing/context.py +6 -6
- synth_ai/v0/tracing/decorators.py +6 -5
- synth_ai/v0/tracing/events/manage.py +1 -1
- synth_ai/v0/tracing/events/store.py +5 -4
- synth_ai/v0/tracing/immediate_client.py +4 -5
- synth_ai/v0/tracing/local.py +3 -3
- synth_ai/v0/tracing/log_client_base.py +4 -5
- synth_ai/v0/tracing/retry_queue.py +5 -6
- synth_ai/v0/tracing/trackers.py +25 -25
- synth_ai/v0/tracing/upload.py +6 -0
- synth_ai/v0/tracing_v1/__init__.py +1 -1
- synth_ai/v0/tracing_v1/abstractions.py +28 -28
- synth_ai/v0/tracing_v1/base_client.py +9 -9
- synth_ai/v0/tracing_v1/client_manager.py +7 -7
- synth_ai/v0/tracing_v1/config.py +7 -7
- synth_ai/v0/tracing_v1/context.py +6 -6
- synth_ai/v0/tracing_v1/decorators.py +7 -6
- synth_ai/v0/tracing_v1/events/manage.py +1 -1
- synth_ai/v0/tracing_v1/events/store.py +5 -4
- synth_ai/v0/tracing_v1/immediate_client.py +4 -5
- synth_ai/v0/tracing_v1/local.py +3 -3
- synth_ai/v0/tracing_v1/log_client_base.py +4 -5
- synth_ai/v0/tracing_v1/retry_queue.py +5 -6
- synth_ai/v0/tracing_v1/trackers.py +25 -25
- synth_ai/v0/tracing_v1/upload.py +25 -24
- synth_ai/zyk/__init__.py +1 -0
- synth_ai-0.2.4.dev8.dist-info/METADATA +635 -0
- synth_ai-0.2.4.dev8.dist-info/RECORD +317 -0
- synth_ai/tui/__init__.py +0 -1
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -165
- synth_ai/tui/cli/query_experiments_v3.py +0 -165
- synth_ai/tui/dashboard.py +0 -329
- synth_ai-0.2.4.dev6.dist-info/METADATA +0 -203
- synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/top_level.txt +0 -0
@@ -35,19 +35,21 @@ Concepts:
|
|
35
35
|
"""
|
36
36
|
|
37
37
|
from __future__ import annotations
|
38
|
-
|
38
|
+
|
39
|
+
from dataclasses import asdict, dataclass, field
|
39
40
|
from datetime import datetime
|
40
|
-
from typing import Any
|
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:
|
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:
|
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:
|
127
|
-
event_metadata:
|
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:
|
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:
|
169
|
-
system_state_after:
|
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:
|
199
|
-
input_tokens:
|
200
|
-
output_tokens:
|
201
|
-
total_tokens:
|
202
|
-
cost_usd:
|
203
|
-
latency_ms:
|
204
|
-
span_id:
|
205
|
-
trace_id:
|
206
|
-
system_state_before:
|
207
|
-
system_state_after:
|
208
|
-
call_records:
|
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:
|
235
|
-
events:
|
236
|
-
markov_blanket_messages:
|
237
|
-
step_metadata:
|
238
|
-
completed_at:
|
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:
|
269
|
-
event_history:
|
270
|
-
markov_blanket_message_history:
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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.
|
synth_ai/tracing_v3/config.py
CHANGED
@@ -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(
|
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"))
|
synth_ai/tracing_v3/db_config.py
CHANGED
@@ -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:
|
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:
|
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) ->
|
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:
|
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
|
29
|
-
import
|
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
|
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[
|
38
|
+
_session_id_ctx: contextvars.ContextVar[str | None] = contextvars.ContextVar(
|
40
39
|
"session_id", default=None
|
41
40
|
)
|
42
|
-
_turn_number_ctx: 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[
|
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:
|
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() ->
|
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:
|
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() ->
|
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
|
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
|
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()
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
|
synth_ai/tracing_v3/hooks.py
CHANGED
@@ -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
|
36
|
+
from collections.abc import Callable
|
37
|
+
from dataclasses import dataclass
|
38
|
+
from typing import Any
|
39
39
|
|
40
|
-
from .abstractions import
|
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:
|
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:
|
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:
|
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) ->
|
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,20 +187,22 @@ 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:
|
203
|
-
|
202
|
+
async def log_session_start(session_id: str, metadata: dict[str, Any]):
|
203
|
+
import os
|
204
|
+
if os.getenv("SYNTH_TRACE_VERBOSE", "0") in ("1", "true", "True"):
|
205
|
+
print(f"Session started: {session_id}")
|
204
206
|
|
205
207
|
# Example: Validate events before recording - ensures data quality
|
206
208
|
def validate_event(event_obj: BaseEvent) -> bool:
|