synth-ai 0.1.9__py3-none-any.whl → 0.2.1.dev0__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 +28 -2
- synth_ai/core/system.py +4 -0
- synth_ai/environments/__init__.py +35 -0
- synth_ai/environments/environment/__init__.py +1 -0
- synth_ai/environments/environment/artifacts/__init__.py +1 -0
- synth_ai/environments/environment/artifacts/base.py +50 -0
- synth_ai/environments/environment/core.py +22 -0
- synth_ai/environments/environment/db/__init__.py +1 -0
- synth_ai/environments/environment/db/sqlite.py +45 -0
- synth_ai/environments/environment/registry.py +24 -0
- synth_ai/environments/environment/resources/sqlite.py +46 -0
- synth_ai/environments/environment/results.py +1 -0
- synth_ai/environments/environment/rewards/__init__.py +1 -0
- synth_ai/environments/environment/rewards/core.py +28 -0
- synth_ai/environments/environment/shared_engine.py +26 -0
- synth_ai/environments/environment/tools/__init__.py +34 -0
- synth_ai/environments/examples/__init__.py +1 -0
- synth_ai/environments/examples/crafter_classic/__init__.py +8 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +58 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +152 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +1194 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +51 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +872 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +1412 -0
- synth_ai/environments/examples/crafter_classic/agent_demos/test_crafter_react_agent.py +1110 -0
- synth_ai/environments/examples/crafter_classic/config_logging.py +111 -0
- synth_ai/environments/examples/crafter_classic/engine.py +502 -0
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +63 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +5 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +74 -0
- synth_ai/environments/examples/crafter_classic/environment.py +255 -0
- synth_ai/environments/examples/crafter_classic/taskset.py +228 -0
- synth_ai/environments/examples/enron/agent_demos/test_synth_react.py +535 -0
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +156 -0
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +280 -0
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +24 -0
- synth_ai/environments/examples/enron/engine.py +291 -0
- synth_ai/environments/examples/enron/environment.py +165 -0
- synth_ai/environments/examples/enron/taskset.py +112 -0
- synth_ai/environments/examples/enron/units/keyword_stats.py +111 -0
- synth_ai/environments/examples/enron/units/test_email_index.py +8 -0
- synth_ai/environments/examples/minigrid/__init__.py +48 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +1188 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +47 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +562 -0
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +220 -0
- synth_ai/environments/examples/minigrid/agent_demos/test_minigrid_react_agent.py +393 -0
- synth_ai/environments/examples/minigrid/engine.py +589 -0
- synth_ai/environments/examples/minigrid/environment.py +274 -0
- synth_ai/environments/examples/minigrid/environment_mapping.py +242 -0
- synth_ai/environments/examples/minigrid/puzzle_loader.py +416 -0
- synth_ai/environments/examples/minigrid/taskset.py +583 -0
- synth_ai/environments/examples/minigrid/units/test_action_behavior.py +226 -0
- synth_ai/environments/examples/minigrid/units/test_debug_messages.py +83 -0
- synth_ai/environments/examples/minigrid/units/test_exploration.py +120 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_engine.py +214 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_environment.py +238 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_environment_mapping.py +301 -0
- synth_ai/environments/examples/minigrid/units/test_minigrid_taskset.py +210 -0
- synth_ai/environments/examples/nethack/__init__.py +7 -0
- synth_ai/environments/examples/nethack/achievements.py +337 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +981 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +74 -0
- synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +832 -0
- synth_ai/environments/examples/nethack/agent_demos/test_nethack_react_agent.py +1112 -0
- synth_ai/environments/examples/nethack/engine.py +738 -0
- synth_ai/environments/examples/nethack/environment.py +255 -0
- synth_ai/environments/examples/nethack/helpers/__init__.py +42 -0
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +301 -0
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +401 -0
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +433 -0
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +201 -0
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +268 -0
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +308 -0
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +430 -0
- synth_ai/environments/examples/nethack/taskset.py +323 -0
- synth_ai/environments/examples/nethack/units/test_nethack_engine.py +277 -0
- synth_ai/environments/examples/nethack/units/test_nethack_environment.py +281 -0
- synth_ai/environments/examples/nethack/units/test_nethack_taskset.py +213 -0
- synth_ai/environments/examples/nethack/units/test_recording.py +307 -0
- synth_ai/environments/examples/red/__init__.py +7 -0
- synth_ai/environments/examples/red/agent_demos/__init__.py +1 -0
- synth_ai/environments/examples/red/agent_demos/test_synth_react.py +1471 -0
- synth_ai/environments/examples/red/config_logging.py +110 -0
- synth_ai/environments/examples/red/engine.py +693 -0
- synth_ai/environments/examples/red/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +28 -0
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +275 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +142 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +283 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +149 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +137 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +330 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +120 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +558 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +312 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +147 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +246 -0
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +367 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +139 -0
- synth_ai/environments/examples/red/environment.py +235 -0
- synth_ai/environments/examples/red/taskset.py +77 -0
- synth_ai/environments/examples/red/test_fixes.py +125 -0
- synth_ai/environments/examples/red/test_fixes_mock.py +148 -0
- synth_ai/environments/examples/red/units/__init__.py +1 -0
- synth_ai/environments/examples/red/units/test_basic_functionality.py +97 -0
- synth_ai/environments/examples/red/units/test_button_press_requirements.py +217 -0
- synth_ai/environments/examples/red/units/test_engine.py +192 -0
- synth_ai/environments/examples/red/units/test_environment.py +455 -0
- synth_ai/environments/examples/red/units/test_exploration_strategy.py +227 -0
- synth_ai/environments/examples/red/units/test_integration.py +217 -0
- synth_ai/environments/examples/red/units/test_memory_extraction.py +111 -0
- synth_ai/environments/examples/red/units/test_menu_bug_reproduction.py +1100 -0
- synth_ai/environments/examples/red/units/test_movement_debug.py +255 -0
- synth_ai/environments/examples/red/units/test_pokemon_mcts_debug.py +163 -0
- synth_ai/environments/examples/red/units/test_pokemon_mcts_verbose.py +117 -0
- synth_ai/environments/examples/red/units/test_red_basic.py +145 -0
- synth_ai/environments/examples/red/units/test_red_comprehensive.py +323 -0
- synth_ai/environments/examples/red/units/test_retry_movement.py +195 -0
- synth_ai/environments/examples/red/units/test_reward_components.py +186 -0
- synth_ai/environments/examples/red/units/test_rom_integration.py +260 -0
- synth_ai/environments/examples/red/units/test_taskset.py +116 -0
- synth_ai/environments/examples/red/units/test_tree.py +448 -0
- synth_ai/environments/examples/sokoban/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +900 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_dspy_react.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_sokoban_react_agent.py +498 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_lats.py +1 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_locally.py +748 -0
- synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_service.py +296 -0
- synth_ai/environments/examples/sokoban/engine.py +675 -0
- synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +656 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +17 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +3 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +129 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +370 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +331 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +305 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +66 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +114 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +122 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +394 -0
- synth_ai/environments/examples/sokoban/environment.py +228 -0
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +438 -0
- synth_ai/environments/examples/sokoban/puzzle_loader.py +311 -0
- synth_ai/environments/examples/sokoban/taskset.py +425 -0
- synth_ai/environments/examples/sokoban/units/astar_common.py +94 -0
- synth_ai/environments/examples/sokoban/units/test_building_task_set.py +49 -0
- synth_ai/environments/examples/sokoban/units/test_false_positive.py +120 -0
- synth_ai/environments/examples/sokoban/units/test_simple_run_through_environment.py +119 -0
- synth_ai/environments/examples/sokoban/units/test_sokoban_environment.py +98 -0
- synth_ai/environments/examples/sokoban/units/test_tree.py +364 -0
- synth_ai/environments/examples/tictactoe/__init__.py +1 -0
- synth_ai/environments/examples/tictactoe/agent_demos/test_synth_react.py +266 -0
- synth_ai/environments/examples/tictactoe/agent_demos/test_tictactoe_react_agent.py +470 -0
- synth_ai/environments/examples/tictactoe/engine.py +368 -0
- synth_ai/environments/examples/tictactoe/environment.py +239 -0
- synth_ai/environments/examples/tictactoe/taskset.py +214 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_engine.py +393 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_environment.py +493 -0
- synth_ai/environments/examples/tictactoe/units/test_tictactoe_taskset.py +191 -0
- synth_ai/environments/examples/verilog/__init__.py +10 -0
- synth_ai/environments/examples/verilog/agent_demos/test_synth_react.py +520 -0
- synth_ai/environments/examples/verilog/engine.py +328 -0
- synth_ai/environments/examples/verilog/environment.py +349 -0
- synth_ai/environments/examples/verilog/taskset.py +418 -0
- synth_ai/environments/examples/verilog/units/test_verilog_engine.py +466 -0
- synth_ai/environments/examples/verilog/units/test_verilog_environment.py +585 -0
- synth_ai/environments/examples/verilog/units/test_verilog_integration.py +383 -0
- synth_ai/environments/examples/verilog/units/test_verilog_taskset.py +457 -0
- synth_ai/environments/reproducibility/core.py +42 -0
- synth_ai/environments/reproducibility/tree.py +364 -0
- synth_ai/environments/service/app.py +78 -0
- synth_ai/environments/service/core_routes.py +775 -0
- synth_ai/environments/service/external_registry.py +57 -0
- synth_ai/environments/service/registry.py +9 -0
- synth_ai/environments/stateful/__init__.py +1 -0
- synth_ai/environments/stateful/core.py +28 -0
- synth_ai/environments/stateful/engine.py +21 -0
- synth_ai/environments/stateful/state.py +7 -0
- synth_ai/environments/tasks/api.py +19 -0
- synth_ai/environments/tasks/core.py +78 -0
- synth_ai/environments/tasks/filters.py +39 -0
- synth_ai/environments/tasks/utils.py +89 -0
- synth_ai/environments/v0_observability/history.py +3 -0
- synth_ai/environments/v0_observability/log.py +2 -0
- synth_ai/lm/caching/constants.py +1 -0
- synth_ai/{zyk/lms → lm}/caching/ephemeral.py +4 -8
- synth_ai/{zyk/lms → lm}/caching/handler.py +15 -15
- synth_ai/{zyk/lms → lm}/caching/initialize.py +2 -4
- synth_ai/{zyk/lms → lm}/caching/persistent.py +4 -10
- synth_ai/{zyk/lms → lm}/config.py +2 -1
- synth_ai/{zyk/lms → lm}/constants.py +2 -2
- synth_ai/{zyk/lms → lm}/core/all.py +10 -10
- synth_ai/{zyk/lms → lm}/core/main.py +57 -33
- synth_ai/{zyk/lms → lm}/core/vendor_clients.py +12 -10
- synth_ai/lm/cost/monitor.py +1 -0
- synth_ai/lm/cost/statefulness.py +1 -0
- synth_ai/lm/provider_support/__init__.py +8 -0
- synth_ai/lm/provider_support/anthropic.py +945 -0
- synth_ai/lm/provider_support/openai.py +1115 -0
- synth_ai/lm/provider_support/suppress_logging.py +31 -0
- synth_ai/{zyk/lms → lm}/structured_outputs/handler.py +58 -80
- synth_ai/{zyk/lms → lm}/structured_outputs/inject.py +6 -20
- synth_ai/{zyk/lms → lm}/structured_outputs/rehabilitate.py +6 -12
- synth_ai/{zyk/lms → lm}/vendors/core/anthropic_api.py +21 -30
- synth_ai/{zyk/lms → lm}/vendors/core/gemini_api.py +37 -32
- synth_ai/{zyk/lms → lm}/vendors/core/mistral_api.py +19 -28
- synth_ai/{zyk/lms → lm}/vendors/core/openai_api.py +26 -36
- synth_ai/{zyk/lms → lm}/vendors/openai_standard.py +29 -33
- synth_ai/{zyk/lms → lm}/vendors/retries.py +1 -1
- synth_ai/lm/vendors/supported/__init__.py +0 -0
- synth_ai/{zyk/lms → lm}/vendors/supported/custom_endpoint.py +131 -118
- synth_ai/{zyk/lms → lm}/vendors/supported/deepseek.py +4 -8
- synth_ai/{zyk/lms → lm}/vendors/supported/grok.py +6 -8
- synth_ai/{zyk/lms → lm}/vendors/supported/groq.py +1 -1
- synth_ai/{zyk/lms → lm}/vendors/supported/ollama.py +2 -2
- synth_ai/{zyk/lms → lm}/vendors/supported/openrouter.py +18 -16
- synth_ai/{zyk/lms → lm}/vendors/supported/together.py +1 -1
- synth_ai/tracing/__init__.py +0 -0
- synth_ai/tracing/abstractions.py +224 -0
- synth_ai/tracing/base_client.py +91 -0
- synth_ai/tracing/client_manager.py +131 -0
- synth_ai/tracing/config.py +140 -0
- synth_ai/tracing/context.py +146 -0
- synth_ai/tracing/decorators.py +679 -0
- synth_ai/tracing/events/__init__.py +0 -0
- synth_ai/tracing/events/manage.py +147 -0
- synth_ai/tracing/events/scope.py +86 -0
- synth_ai/tracing/events/store.py +227 -0
- synth_ai/tracing/immediate_client.py +152 -0
- synth_ai/tracing/local.py +18 -0
- synth_ai/tracing/log_client_base.py +74 -0
- synth_ai/tracing/retry_queue.py +187 -0
- synth_ai/tracing/trackers.py +515 -0
- synth_ai/tracing/upload.py +504 -0
- synth_ai/tracing/utils.py +9 -0
- synth_ai/zyk/__init__.py +28 -2
- synth_ai-0.2.1.dev0.dist-info/METADATA +349 -0
- synth_ai-0.2.1.dev0.dist-info/RECORD +261 -0
- synth_ai/zyk/lms/caching/constants.py +0 -1
- synth_ai/zyk/lms/cost/monitor.py +0 -1
- synth_ai/zyk/lms/cost/statefulness.py +0 -1
- synth_ai-0.1.9.dist-info/METADATA +0 -37
- synth_ai-0.1.9.dist-info/RECORD +0 -50
- /synth_ai/{zyk/lms/__init__.py → environments/reproducibility/helpers.py} +0 -0
- /synth_ai/{zyk/lms/caching → lm}/__init__.py +0 -0
- /synth_ai/{zyk/lms/core → lm/caching}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/caching/dbs.py +0 -0
- /synth_ai/{zyk/lms/cost → lm/core}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/core/exceptions.py +0 -0
- /synth_ai/{zyk/lms/structured_outputs → lm/cost}/__init__.py +0 -0
- /synth_ai/{zyk/lms/vendors → lm/structured_outputs}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/tools/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/tools/base.py +0 -0
- /synth_ai/{zyk/lms/vendors/core → lm/vendors}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/vendors/base.py +0 -0
- /synth_ai/{zyk/lms/vendors/local → lm/vendors/core}/__init__.py +0 -0
- /synth_ai/{zyk/lms/vendors/supported → lm/vendors/local}/__init__.py +0 -0
- /synth_ai/{zyk/lms → lm}/vendors/local/ollama.py +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/WHEEL +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/top_level.txt +0 -0
File without changes
|
@@ -0,0 +1,224 @@
|
|
1
|
+
import logging
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from datetime import datetime
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
5
|
+
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class MessageInputs:
|
13
|
+
messages: List[Dict[str, str]] # {"role": "", "content": ""}
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass
|
17
|
+
class ArbitraryInputs:
|
18
|
+
inputs: Dict[str, Any]
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass
|
22
|
+
class MessageOutputs:
|
23
|
+
messages: List[Dict[str, str]]
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class ArbitraryOutputs:
|
28
|
+
outputs: Dict[str, Any]
|
29
|
+
|
30
|
+
|
31
|
+
@dataclass
|
32
|
+
class ComputeStep:
|
33
|
+
event_order: int
|
34
|
+
compute_ended: datetime # time step
|
35
|
+
compute_began: datetime # time step
|
36
|
+
compute_input: Dict[str, Any] # {variable_name: value}
|
37
|
+
compute_output: Dict[str, Any] # {variable_name: value}
|
38
|
+
|
39
|
+
def to_dict(self):
|
40
|
+
# Serialize compute_input
|
41
|
+
serializable_input = [
|
42
|
+
input_item.__dict__
|
43
|
+
for input_item in self.compute_input
|
44
|
+
if isinstance(input_item, (MessageInputs, ArbitraryInputs))
|
45
|
+
]
|
46
|
+
|
47
|
+
# Serialize compute_output
|
48
|
+
serializable_output = [
|
49
|
+
output_item.__dict__
|
50
|
+
for output_item in self.compute_output
|
51
|
+
if isinstance(output_item, (MessageOutputs, ArbitraryOutputs))
|
52
|
+
]
|
53
|
+
|
54
|
+
# Warn about non-serializable inputs/outputs
|
55
|
+
for item in self.compute_input:
|
56
|
+
if not isinstance(item, (MessageInputs, ArbitraryInputs)):
|
57
|
+
logger.warning(f"Skipping non-serializable input: {item}")
|
58
|
+
for item in self.compute_output:
|
59
|
+
if not isinstance(item, (MessageOutputs, ArbitraryOutputs)):
|
60
|
+
logger.warning(f"Skipping non-serializable output: {item}")
|
61
|
+
|
62
|
+
return {
|
63
|
+
"event_order": self.event_order,
|
64
|
+
"compute_ended": self.compute_ended.isoformat()
|
65
|
+
if isinstance(self.compute_ended, datetime)
|
66
|
+
else self.compute_ended,
|
67
|
+
"compute_began": self.compute_began.isoformat()
|
68
|
+
if isinstance(self.compute_began, datetime)
|
69
|
+
else self.compute_began,
|
70
|
+
"compute_input": serializable_input,
|
71
|
+
"compute_output": serializable_output,
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
@dataclass
|
76
|
+
class AgentComputeStep(ComputeStep):
|
77
|
+
model_name: Optional[str] = None
|
78
|
+
model_params: Optional[Dict[str, Any]] = None
|
79
|
+
should_learn: Optional[bool] = None
|
80
|
+
compute_input: List[Union[MessageInputs, ArbitraryInputs]]
|
81
|
+
compute_output: List[Union[MessageOutputs, ArbitraryOutputs]]
|
82
|
+
|
83
|
+
def to_dict(self):
|
84
|
+
base_dict = super().to_dict() # Get the parent class serialization
|
85
|
+
base_dict["model_name"] = self.model_name # Add model_name
|
86
|
+
return base_dict
|
87
|
+
|
88
|
+
|
89
|
+
@dataclass
|
90
|
+
class EnvironmentComputeStep(ComputeStep):
|
91
|
+
compute_input: List[ArbitraryInputs]
|
92
|
+
compute_output: List[ArbitraryOutputs]
|
93
|
+
|
94
|
+
|
95
|
+
@dataclass
|
96
|
+
class Event:
|
97
|
+
system_instance_id: str
|
98
|
+
event_type: str
|
99
|
+
opened: float
|
100
|
+
closed: Optional[float]
|
101
|
+
partition_index: int
|
102
|
+
agent_compute_step: AgentComputeStep
|
103
|
+
environment_compute_steps: List["EnvironmentComputeStep"]
|
104
|
+
system_name: Optional[str] = None
|
105
|
+
system_id: Optional[str] = None
|
106
|
+
event_metadata: Dict[str, Any] = None # JSON-serializable metadata for this specific event
|
107
|
+
|
108
|
+
def __post_init__(self):
|
109
|
+
if self.event_metadata is None:
|
110
|
+
self.event_metadata = {}
|
111
|
+
|
112
|
+
def to_dict(self):
|
113
|
+
return {
|
114
|
+
"event_type": self.event_type,
|
115
|
+
"opened": self.opened.isoformat() if isinstance(self.opened, datetime) else self.opened,
|
116
|
+
"closed": self.closed.isoformat() if isinstance(self.closed, datetime) else self.closed,
|
117
|
+
"partition_index": self.partition_index,
|
118
|
+
"agent_compute_step": self.agent_compute_step.to_dict()
|
119
|
+
if self.agent_compute_step
|
120
|
+
else None,
|
121
|
+
"environment_compute_steps": [
|
122
|
+
step.to_dict() for step in self.environment_compute_steps
|
123
|
+
],
|
124
|
+
"event_metadata": self.event_metadata,
|
125
|
+
}
|
126
|
+
|
127
|
+
# backwards compatibility
|
128
|
+
@property
|
129
|
+
def agent_compute_steps(self) -> List[AgentComputeStep]:
|
130
|
+
"""Backwards compatibility method that returns a list containing the agent_compute_step."""
|
131
|
+
return [self.agent_compute_step] if self.agent_compute_step is not None else []
|
132
|
+
|
133
|
+
|
134
|
+
@dataclass
|
135
|
+
class EventPartitionElement:
|
136
|
+
partition_index: int
|
137
|
+
events: List[Event]
|
138
|
+
|
139
|
+
def to_dict(self):
|
140
|
+
return {
|
141
|
+
"partition_index": self.partition_index,
|
142
|
+
"events": [event.to_dict() for event in self.events],
|
143
|
+
}
|
144
|
+
|
145
|
+
|
146
|
+
@dataclass
|
147
|
+
class SystemTrace:
|
148
|
+
system_name: str
|
149
|
+
system_id: str
|
150
|
+
system_instance_id: str
|
151
|
+
partition: List[EventPartitionElement]
|
152
|
+
metadata: Optional[Dict[str, Any]] = None # System-level metadata
|
153
|
+
instance_metadata: Dict[str, Any] = (
|
154
|
+
None # JSON-serializable metadata for this specific instance
|
155
|
+
)
|
156
|
+
current_partition_index: int = 0 # Track current partition
|
157
|
+
|
158
|
+
def __post_init__(self):
|
159
|
+
if self.instance_metadata is None:
|
160
|
+
self.instance_metadata = {}
|
161
|
+
|
162
|
+
def to_dict(self):
|
163
|
+
return {
|
164
|
+
"system_name": self.system_name,
|
165
|
+
"system_id": self.system_id,
|
166
|
+
"system_instance_id": self.system_instance_id,
|
167
|
+
"partition": [element.to_dict() for element in self.partition],
|
168
|
+
"current_partition_index": self.current_partition_index,
|
169
|
+
"metadata": self.metadata if self.metadata else None,
|
170
|
+
"instance_metadata": self.instance_metadata,
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
class TrainingQuestion(BaseModel):
|
175
|
+
"""
|
176
|
+
A training question is a question that an agent (system_instance_id) is trying to answer.
|
177
|
+
It contains an intent and criteria that the agent is trying to meet.
|
178
|
+
"""
|
179
|
+
|
180
|
+
id: str
|
181
|
+
intent: str
|
182
|
+
criteria: str
|
183
|
+
|
184
|
+
def to_dict(self):
|
185
|
+
return {
|
186
|
+
"id": self.id,
|
187
|
+
"intent": self.intent,
|
188
|
+
"criteria": self.criteria,
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
class RewardSignal(BaseModel):
|
193
|
+
"""
|
194
|
+
A reward signal tells us how well an agent (system_instance_id) is doing on a particular question (question_id).
|
195
|
+
"""
|
196
|
+
|
197
|
+
question_id: str
|
198
|
+
system_instance_id: str
|
199
|
+
reward: Union[float, int, bool]
|
200
|
+
annotation: Optional[str] = None
|
201
|
+
|
202
|
+
def to_dict(self):
|
203
|
+
return {
|
204
|
+
"question_id": self.question_id,
|
205
|
+
"system_instance_id": self.system_instance_id,
|
206
|
+
"reward": self.reward,
|
207
|
+
"annotation": self.annotation,
|
208
|
+
}
|
209
|
+
|
210
|
+
|
211
|
+
class Dataset(BaseModel):
|
212
|
+
"""
|
213
|
+
A dataset is a collection of training questions and reward signals.
|
214
|
+
This better represents the data that is used to train a model, and gives us more information about the data.
|
215
|
+
"""
|
216
|
+
|
217
|
+
questions: List[TrainingQuestion]
|
218
|
+
reward_signals: List[RewardSignal]
|
219
|
+
|
220
|
+
def to_dict(self):
|
221
|
+
return {
|
222
|
+
"questions": [question.to_dict() for question in self.questions],
|
223
|
+
"reward_signals": [signal.to_dict() for signal in self.reward_signals],
|
224
|
+
}
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import logging
|
2
|
+
import time
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any, Dict, Optional
|
6
|
+
|
7
|
+
from synth_ai.tracing.abstractions import Event
|
8
|
+
from synth_ai.tracing.config import TracingConfig
|
9
|
+
from synth_ai.tracing.events.store import event_store
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class LogResponse:
|
16
|
+
"""Represents the response from a logging attempt"""
|
17
|
+
|
18
|
+
success: bool
|
19
|
+
error: Optional[str] = None
|
20
|
+
retry_after: Optional[float] = None
|
21
|
+
status_code: Optional[int] = None
|
22
|
+
|
23
|
+
|
24
|
+
class BaseLogClient(ABC):
|
25
|
+
"""Abstract base class for logging clients"""
|
26
|
+
|
27
|
+
def __init__(self, config: TracingConfig):
|
28
|
+
self.config = config
|
29
|
+
self._consecutive_failures = 0
|
30
|
+
self._last_failure_time = 0
|
31
|
+
self._circuit_open = False
|
32
|
+
self._circuit_open_time = 0
|
33
|
+
|
34
|
+
def _should_retry(self, attempt: int, status_code: Optional[int] = None) -> bool:
|
35
|
+
"""Determine if a retry should be attempted based on configuration and status"""
|
36
|
+
if attempt >= self.config.max_retries:
|
37
|
+
return False
|
38
|
+
|
39
|
+
# Don't retry on certain status codes
|
40
|
+
if status_code:
|
41
|
+
# Always retry on 429 (Too Many Requests)
|
42
|
+
if status_code == 429:
|
43
|
+
return True
|
44
|
+
|
45
|
+
# Don't retry on client errors except timeout/too many requests
|
46
|
+
if 400 <= status_code < 500 and status_code not in (408, 429):
|
47
|
+
return False
|
48
|
+
|
49
|
+
return True
|
50
|
+
|
51
|
+
def _prepare_payload(self, event: Event, system_info: Dict[str, str]) -> Dict[str, Any]:
|
52
|
+
"""Prepare the payload for sending"""
|
53
|
+
return {
|
54
|
+
"event": event.to_dict(),
|
55
|
+
"system_info": system_info,
|
56
|
+
"timestamp": time.time(),
|
57
|
+
"sdk_version": self.config.sdk_version, # Use SDK version from config
|
58
|
+
}
|
59
|
+
|
60
|
+
def _handle_failure(self, event: Event, system_info: Dict[str, str], error: Exception) -> None:
|
61
|
+
"""Handle logging failure by storing in event_store"""
|
62
|
+
logger.error(f"Logging failed: {str(error)}")
|
63
|
+
self._consecutive_failures += 1
|
64
|
+
self._last_failure_time = time.time()
|
65
|
+
|
66
|
+
# Store in event_store as backup
|
67
|
+
event_store.add_event(
|
68
|
+
system_info["system_name"],
|
69
|
+
system_info["system_id"],
|
70
|
+
system_info["system_instance_id"],
|
71
|
+
event,
|
72
|
+
)
|
73
|
+
|
74
|
+
def _handle_success(self) -> None:
|
75
|
+
"""Handle successful logging attempt"""
|
76
|
+
self._consecutive_failures = 0
|
77
|
+
self._last_failure_time = 0
|
78
|
+
|
79
|
+
@abstractmethod
|
80
|
+
def send_event(self, event: Event, system_info: Dict[str, str]) -> bool:
|
81
|
+
"""Send a single event with retries and fallback"""
|
82
|
+
pass
|
83
|
+
|
84
|
+
|
85
|
+
class BaseAsyncLogClient(BaseLogClient):
|
86
|
+
"""Abstract base class for async logging clients"""
|
87
|
+
|
88
|
+
@abstractmethod
|
89
|
+
async def send_event(self, event: Event, system_info: Dict[str, str]) -> bool:
|
90
|
+
"""Send a single event with retries and fallback (async version)"""
|
91
|
+
pass
|
@@ -0,0 +1,131 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import random
|
5
|
+
from typing import ClassVar, Dict, Optional
|
6
|
+
|
7
|
+
import httpx
|
8
|
+
|
9
|
+
from synth_ai.tracing.config import TracingConfig
|
10
|
+
|
11
|
+
|
12
|
+
class ClientManager:
|
13
|
+
"""Singleton manager for HTTP clients with both sync and async support"""
|
14
|
+
|
15
|
+
_instance: ClassVar[Optional[ClientManager]] = None
|
16
|
+
_lock = asyncio.Lock()
|
17
|
+
|
18
|
+
def __init__(self):
|
19
|
+
self._config: Optional[TracingConfig] = None
|
20
|
+
self._sync_client: Optional[httpx.Client] = None
|
21
|
+
self._async_client: Optional[httpx.AsyncClient] = None
|
22
|
+
self._credentials_cache: Dict[str, str] = {}
|
23
|
+
|
24
|
+
@classmethod
|
25
|
+
async def get_instance(cls) -> ClientManager:
|
26
|
+
"""Get or create the singleton instance asynchronously"""
|
27
|
+
if not cls._instance:
|
28
|
+
async with cls._lock:
|
29
|
+
if not cls._instance:
|
30
|
+
cls._instance = ClientManager()
|
31
|
+
return cls._instance
|
32
|
+
|
33
|
+
@classmethod
|
34
|
+
def initialize(cls, config: TracingConfig) -> ClientManager:
|
35
|
+
"""Initialize or return the singleton instance synchronously"""
|
36
|
+
if cls._instance is None:
|
37
|
+
cls._instance = cls()
|
38
|
+
cls._instance.configure(config)
|
39
|
+
return cls._instance
|
40
|
+
|
41
|
+
def configure(self, config: TracingConfig) -> None:
|
42
|
+
"""Configure the client manager with new settings"""
|
43
|
+
self._config = config
|
44
|
+
self._credentials_cache = {
|
45
|
+
"api_key": config.api_key,
|
46
|
+
"api_secret": getattr(config, "api_secret", None),
|
47
|
+
}
|
48
|
+
self._close_clients()
|
49
|
+
|
50
|
+
def get_sync_client(self) -> httpx.Client:
|
51
|
+
"""Get or create synchronized HTTP client with connection pooling"""
|
52
|
+
if not self._sync_client and self._config:
|
53
|
+
limits = httpx.Limits(
|
54
|
+
max_connections=self._config.max_connections,
|
55
|
+
max_keepalive_connections=self._config.max_connections,
|
56
|
+
)
|
57
|
+
timeout = httpx.Timeout(timeout=self._config.timeout)
|
58
|
+
self._sync_client = httpx.Client(
|
59
|
+
base_url=self._config.base_url,
|
60
|
+
timeout=timeout,
|
61
|
+
limits=limits,
|
62
|
+
headers={"Authorization": f"Bearer {self._config.api_key}"},
|
63
|
+
)
|
64
|
+
return self._sync_client
|
65
|
+
|
66
|
+
async def get_async_client(self) -> httpx.AsyncClient:
|
67
|
+
"""Get or create asynchronous HTTP client with connection pooling"""
|
68
|
+
if not self._async_client and self._config:
|
69
|
+
limits = httpx.Limits(
|
70
|
+
max_connections=self._config.max_connections,
|
71
|
+
max_keepalive_connections=self._config.max_connections,
|
72
|
+
)
|
73
|
+
timeout = httpx.Timeout(timeout=self._config.timeout)
|
74
|
+
self._async_client = httpx.AsyncClient(
|
75
|
+
base_url=self._config.base_url,
|
76
|
+
timeout=timeout,
|
77
|
+
limits=limits,
|
78
|
+
headers={"Authorization": f"Bearer {self._config.api_key}"},
|
79
|
+
)
|
80
|
+
return self._async_client
|
81
|
+
|
82
|
+
def calculate_backoff(self, retry_number: int) -> float:
|
83
|
+
"""Calculate backoff time with exponential backoff and jitter"""
|
84
|
+
if not self._config:
|
85
|
+
raise RuntimeError("ClientManager not configured")
|
86
|
+
|
87
|
+
if retry_number <= 0:
|
88
|
+
return 0
|
89
|
+
|
90
|
+
if not getattr(self._config, "retry_exponential_backoff", True):
|
91
|
+
return self._config.retry_backoff
|
92
|
+
|
93
|
+
# Calculate exponential backoff
|
94
|
+
delay = min(
|
95
|
+
self._config.retry_backoff * (2 ** (retry_number - 1)),
|
96
|
+
60.0, # Cap at 60 seconds
|
97
|
+
)
|
98
|
+
|
99
|
+
# Add jitter (±25% of delay)
|
100
|
+
jitter = delay * 0.25
|
101
|
+
delay = random.uniform(delay - jitter, delay + jitter)
|
102
|
+
|
103
|
+
return max(0.0, delay)
|
104
|
+
|
105
|
+
def close(self) -> None:
|
106
|
+
"""Close the sync client"""
|
107
|
+
if self._sync_client:
|
108
|
+
self._sync_client.close()
|
109
|
+
self._sync_client = None
|
110
|
+
|
111
|
+
async def aclose(self) -> None:
|
112
|
+
"""Close the async client"""
|
113
|
+
if self._async_client:
|
114
|
+
await self._async_client.aclose()
|
115
|
+
self._async_client = None
|
116
|
+
|
117
|
+
def _close_clients(self) -> None:
|
118
|
+
"""Close both sync and async clients"""
|
119
|
+
self.close()
|
120
|
+
if self._async_client:
|
121
|
+
asyncio.create_task(self.aclose())
|
122
|
+
|
123
|
+
@property
|
124
|
+
def config(self) -> Optional[TracingConfig]:
|
125
|
+
"""Get the current configuration"""
|
126
|
+
return self._config
|
127
|
+
|
128
|
+
@config.setter
|
129
|
+
def config(self, value: TracingConfig) -> None:
|
130
|
+
"""Set the configuration and reset clients"""
|
131
|
+
self.configure(value)
|
@@ -0,0 +1,140 @@
|
|
1
|
+
import json
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Any, Dict, List, Sequence, TypedDict, Union
|
4
|
+
|
5
|
+
from opentelemetry import trace
|
6
|
+
from opentelemetry.sdk.trace import ReadableSpan, TracerProvider
|
7
|
+
from opentelemetry.sdk.trace.export import (
|
8
|
+
SimpleSpanProcessor,
|
9
|
+
SpanExporter,
|
10
|
+
SpanExportResult,
|
11
|
+
)
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
13
|
+
from typing_extensions import NotRequired
|
14
|
+
|
15
|
+
|
16
|
+
class InMemoryExporter(SpanExporter):
|
17
|
+
def __init__(self):
|
18
|
+
self.spans: List[Dict[str, Any]] = []
|
19
|
+
|
20
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
21
|
+
for span in spans:
|
22
|
+
self.spans.append(
|
23
|
+
{
|
24
|
+
"name": span.name,
|
25
|
+
"context": {
|
26
|
+
"trace_id": span.context.trace_id,
|
27
|
+
"span_id": span.context.span_id,
|
28
|
+
},
|
29
|
+
"parent_id": span.parent.span_id if span.parent else None,
|
30
|
+
"start_time": span.start_time,
|
31
|
+
"end_time": span.end_time,
|
32
|
+
"attributes": dict(span.attributes),
|
33
|
+
"events": [
|
34
|
+
{
|
35
|
+
"name": event.name,
|
36
|
+
"timestamp": event.timestamp,
|
37
|
+
"attributes": dict(event.attributes),
|
38
|
+
}
|
39
|
+
for event in span.events
|
40
|
+
],
|
41
|
+
}
|
42
|
+
)
|
43
|
+
return SpanExportResult.SUCCESS
|
44
|
+
|
45
|
+
def shutdown(self):
|
46
|
+
pass
|
47
|
+
|
48
|
+
def get_spans(self) -> List[Dict[str, Any]]:
|
49
|
+
return self.spans
|
50
|
+
|
51
|
+
def clear(self):
|
52
|
+
self.spans = []
|
53
|
+
|
54
|
+
def to_json(self) -> str:
|
55
|
+
return json.dumps(self.spans, default=str)
|
56
|
+
|
57
|
+
|
58
|
+
# Initialize the custom exporter
|
59
|
+
in_memory_exporter = InMemoryExporter()
|
60
|
+
|
61
|
+
# Set up the tracer provider
|
62
|
+
tracer_provider = TracerProvider()
|
63
|
+
span_processor = SimpleSpanProcessor(
|
64
|
+
in_memory_exporter
|
65
|
+
) # Use SimpleSpanProcessor for immediate exporting
|
66
|
+
tracer_provider.add_span_processor(span_processor)
|
67
|
+
trace.set_tracer_provider(tracer_provider)
|
68
|
+
|
69
|
+
# Get a tracer
|
70
|
+
tracer = trace.get_tracer(__name__)
|
71
|
+
|
72
|
+
|
73
|
+
def shutdown_tracer_provider():
|
74
|
+
tracer_provider.shutdown()
|
75
|
+
|
76
|
+
|
77
|
+
# Valid types for tracking
|
78
|
+
VALID_TYPES = (str, dict, int, float, bool, list, type(None), BaseModel)
|
79
|
+
|
80
|
+
|
81
|
+
class LoggingMode(Enum):
|
82
|
+
INSTANT = "instant"
|
83
|
+
DEFERRED = "deferred"
|
84
|
+
|
85
|
+
|
86
|
+
class Origin(str, Enum):
|
87
|
+
"""Source of computation in the system."""
|
88
|
+
|
89
|
+
AGENT = "agent"
|
90
|
+
ENVIRONMENT = "environment"
|
91
|
+
|
92
|
+
|
93
|
+
class EventManagement(str, Enum):
|
94
|
+
"""Controls the lifecycle of trace events."""
|
95
|
+
|
96
|
+
CREATE = "create"
|
97
|
+
END = "end"
|
98
|
+
CREATE_AND_END = "create_and_end"
|
99
|
+
|
100
|
+
|
101
|
+
class TracingConfig(BaseModel):
|
102
|
+
mode: LoggingMode = Field(default=LoggingMode.DEFERRED)
|
103
|
+
api_key: str
|
104
|
+
base_url: str = Field(default="https://agent-learning.onrender.com")
|
105
|
+
max_retries: int = Field(default=3)
|
106
|
+
retry_backoff: float = Field(default=1.5) # exponential backoff multiplier
|
107
|
+
batch_size: int = Field(default=1) # for future batching support
|
108
|
+
timeout: float = Field(default=5.0) # seconds
|
109
|
+
sdk_version: str = Field(default="0.1.0") # Added sdk_version field
|
110
|
+
|
111
|
+
# Connection settings
|
112
|
+
max_connections: int = Field(
|
113
|
+
default=100, gt=0, description="Maximum number of concurrent connections"
|
114
|
+
)
|
115
|
+
keepalive_expiry: float = Field(
|
116
|
+
default=30.0, gt=0, description="Connection keepalive time in seconds"
|
117
|
+
)
|
118
|
+
|
119
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid") # Prevent additional fields
|
120
|
+
|
121
|
+
|
122
|
+
class Message(TypedDict):
|
123
|
+
"""A message in a conversation with an AI model."""
|
124
|
+
|
125
|
+
role: str
|
126
|
+
content: str
|
127
|
+
name: NotRequired[str]
|
128
|
+
function_call: NotRequired[Dict[str, str]]
|
129
|
+
|
130
|
+
|
131
|
+
class ModelParams(TypedDict, total=False):
|
132
|
+
"""Parameters for configuring model behavior."""
|
133
|
+
|
134
|
+
temperature: float
|
135
|
+
max_tokens: int
|
136
|
+
top_p: float
|
137
|
+
frequency_penalty: float
|
138
|
+
presence_penalty: float
|
139
|
+
stop: Union[str, List[str]]
|
140
|
+
functions: List[Dict[str, Any]]
|