synth-ai 0.2.0__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 +35 -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-0.2.0.dist-info → synth_ai-0.2.1.dev0.dist-info}/WHEEL +1 -1
- 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.2.0.dist-info/METADATA +0 -36
- synth_ai-0.2.0.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.2.0.dist-info → synth_ai-0.2.1.dev0.dist-info/licenses}/LICENSE +0 -0
- {synth_ai-0.2.0.dist-info → synth_ai-0.2.1.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
|
3
|
+
from synth_ai.tracing.abstractions import Event
|
4
|
+
from synth_ai.tracing.config import TracingConfig
|
5
|
+
|
6
|
+
|
7
|
+
class BaseLogClient:
|
8
|
+
"""Base class for synchronous logging clients"""
|
9
|
+
|
10
|
+
def __init__(self, config: TracingConfig):
|
11
|
+
self.config = config
|
12
|
+
self.client_manager = None
|
13
|
+
|
14
|
+
def _prepare_payload(self, event: Event, system_info: Dict[str, str]) -> Dict:
|
15
|
+
"""Prepare the payload for sending."""
|
16
|
+
return {
|
17
|
+
"event": event.to_dict(),
|
18
|
+
"system_info": system_info,
|
19
|
+
"timestamp": event.opened,
|
20
|
+
"sdk_version": self.config.sdk_version,
|
21
|
+
}
|
22
|
+
|
23
|
+
def _should_retry(self, attempt: int, status_code: int = None) -> bool:
|
24
|
+
"""Determine if a retry should be attempted."""
|
25
|
+
if attempt >= self.config.max_retries:
|
26
|
+
return False
|
27
|
+
if status_code and status_code < 500: # Don't retry 4xx errors
|
28
|
+
return False
|
29
|
+
return True
|
30
|
+
|
31
|
+
def _handle_success(self) -> None:
|
32
|
+
"""Handle successful event sending."""
|
33
|
+
pass
|
34
|
+
|
35
|
+
def _handle_failure(
|
36
|
+
self, event: Event, system_info: Dict[str, str], exception: Exception
|
37
|
+
) -> None:
|
38
|
+
"""Handle failed event sending."""
|
39
|
+
pass
|
40
|
+
|
41
|
+
|
42
|
+
class BaseAsyncLogClient:
|
43
|
+
"""Base class for asynchronous logging clients"""
|
44
|
+
|
45
|
+
def __init__(self, config: TracingConfig):
|
46
|
+
self.config = config
|
47
|
+
self.client_manager = None
|
48
|
+
|
49
|
+
def _prepare_payload(self, event: Event, system_info: Dict[str, str]) -> Dict:
|
50
|
+
"""Prepare the payload for sending."""
|
51
|
+
return {
|
52
|
+
"event": event.to_dict(),
|
53
|
+
"system_info": system_info,
|
54
|
+
"timestamp": event.opened,
|
55
|
+
"sdk_version": self.config.sdk_version,
|
56
|
+
}
|
57
|
+
|
58
|
+
def _should_retry(self, attempt: int, status_code: int = None) -> bool:
|
59
|
+
"""Determine if a retry should be attempted."""
|
60
|
+
if attempt >= self.config.max_retries:
|
61
|
+
return False
|
62
|
+
if status_code and status_code < 500: # Don't retry 4xx errors
|
63
|
+
return False
|
64
|
+
return True
|
65
|
+
|
66
|
+
def _handle_success(self) -> None:
|
67
|
+
"""Handle successful event sending."""
|
68
|
+
pass
|
69
|
+
|
70
|
+
def _handle_failure(
|
71
|
+
self, event: Event, system_info: Dict[str, str], exception: Exception
|
72
|
+
) -> None:
|
73
|
+
"""Handle failed event sending."""
|
74
|
+
pass
|
@@ -0,0 +1,187 @@
|
|
1
|
+
import logging
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
from collections import deque
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import Dict, List, Optional, Tuple
|
7
|
+
|
8
|
+
from synth_ai.tracing.abstractions import Event
|
9
|
+
from synth_ai.tracing.config import TracingConfig
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class QueuedEvent:
|
16
|
+
"""Represents an event that failed to upload and needs to be retried."""
|
17
|
+
|
18
|
+
event: Event
|
19
|
+
system_info: Dict[str, str]
|
20
|
+
attempt_count: int = 0
|
21
|
+
last_attempt: float = 0
|
22
|
+
|
23
|
+
|
24
|
+
class RetryQueue:
|
25
|
+
"""Manages failed event uploads with retry capabilities."""
|
26
|
+
|
27
|
+
def __init__(self, config: TracingConfig):
|
28
|
+
self.config = config
|
29
|
+
self.queue: deque[QueuedEvent] = deque()
|
30
|
+
self._lock = threading.Lock()
|
31
|
+
self._is_processing = False
|
32
|
+
self._batch_size = config.batch_size
|
33
|
+
|
34
|
+
def add_failed_event(self, event: Event, system_info: Dict[str, str]) -> None:
|
35
|
+
"""Add a failed event to the retry queue."""
|
36
|
+
with self._lock:
|
37
|
+
# Check if event is already in queue to avoid duplicates
|
38
|
+
for queued in self.queue:
|
39
|
+
if (
|
40
|
+
queued.event.system_instance_id == event.system_instance_id
|
41
|
+
and queued.event.event_type == event.event_type
|
42
|
+
and queued.event.opened == event.opened
|
43
|
+
):
|
44
|
+
return
|
45
|
+
|
46
|
+
self.queue.append(
|
47
|
+
QueuedEvent(
|
48
|
+
event=event,
|
49
|
+
system_info=system_info,
|
50
|
+
attempt_count=0,
|
51
|
+
last_attempt=time.time(),
|
52
|
+
)
|
53
|
+
)
|
54
|
+
logger.debug(f"Added event to retry queue. Queue size: {len(self.queue)}")
|
55
|
+
|
56
|
+
def get_retryable_events(self, max_events: Optional[int] = None) -> List[QueuedEvent]:
|
57
|
+
"""Get events that are ready to be retried."""
|
58
|
+
now = time.time()
|
59
|
+
retryable = []
|
60
|
+
|
61
|
+
with self._lock:
|
62
|
+
for _ in range(len(self.queue)):
|
63
|
+
if max_events and len(retryable) >= max_events:
|
64
|
+
break
|
65
|
+
|
66
|
+
event = self.queue[0]
|
67
|
+
# Use exponential backoff with the configured multiplier
|
68
|
+
backoff = self.config.retry_backoff**event.attempt_count
|
69
|
+
if now - event.last_attempt >= backoff:
|
70
|
+
retryable.append(self.queue.popleft())
|
71
|
+
else:
|
72
|
+
# If this event isn't ready, later ones won't be either
|
73
|
+
break
|
74
|
+
|
75
|
+
return retryable
|
76
|
+
|
77
|
+
def process_sync(self) -> Tuple[int, int]:
|
78
|
+
"""Process the retry queue synchronously.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
Tuple of (success_count, failure_count)
|
82
|
+
"""
|
83
|
+
if self._is_processing:
|
84
|
+
return 0, 0
|
85
|
+
|
86
|
+
self._is_processing = True
|
87
|
+
success_count = 0
|
88
|
+
failure_count = 0
|
89
|
+
|
90
|
+
try:
|
91
|
+
from synth_ai.tracing.immediate_client import (
|
92
|
+
ImmediateLogClient, # Import here to avoid circular import
|
93
|
+
)
|
94
|
+
|
95
|
+
client = ImmediateLogClient(self.config)
|
96
|
+
|
97
|
+
while True:
|
98
|
+
batch = self.get_retryable_events(self._batch_size)
|
99
|
+
if not batch:
|
100
|
+
break
|
101
|
+
|
102
|
+
for queued_event in batch:
|
103
|
+
try:
|
104
|
+
if client.send_event(queued_event.event, queued_event.system_info):
|
105
|
+
success_count += 1
|
106
|
+
logger.debug(
|
107
|
+
f"Successfully retried event: {queued_event.event.event_type}"
|
108
|
+
)
|
109
|
+
else:
|
110
|
+
failure_count += 1
|
111
|
+
queued_event.attempt_count += 1
|
112
|
+
queued_event.last_attempt = time.time()
|
113
|
+
if queued_event.attempt_count < self.config.max_retries:
|
114
|
+
self.add_failed_event(queued_event.event, queued_event.system_info)
|
115
|
+
else:
|
116
|
+
logger.error(
|
117
|
+
f"Event exhausted retry attempts: {queued_event.event.event_type}"
|
118
|
+
)
|
119
|
+
except Exception as e:
|
120
|
+
logger.error(f"Error processing retry queue: {e}")
|
121
|
+
failure_count += 1
|
122
|
+
|
123
|
+
finally:
|
124
|
+
self._is_processing = False
|
125
|
+
|
126
|
+
return success_count, failure_count
|
127
|
+
|
128
|
+
async def process_async(self) -> Tuple[int, int]:
|
129
|
+
"""Process the retry queue asynchronously.
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
Tuple of (success_count, failure_count)
|
133
|
+
"""
|
134
|
+
if self._is_processing:
|
135
|
+
return 0, 0
|
136
|
+
|
137
|
+
self._is_processing = True
|
138
|
+
success_count = 0
|
139
|
+
failure_count = 0
|
140
|
+
|
141
|
+
try:
|
142
|
+
from synth_ai.tracing.immediate_client import (
|
143
|
+
AsyncImmediateLogClient, # Import here to avoid circular import
|
144
|
+
)
|
145
|
+
|
146
|
+
client = AsyncImmediateLogClient(self.config)
|
147
|
+
|
148
|
+
while True:
|
149
|
+
batch = self.get_retryable_events(self._batch_size)
|
150
|
+
if not batch:
|
151
|
+
break
|
152
|
+
|
153
|
+
for queued_event in batch:
|
154
|
+
try:
|
155
|
+
if await client.send_event(queued_event.event, queued_event.system_info):
|
156
|
+
success_count += 1
|
157
|
+
logger.debug(
|
158
|
+
f"Successfully retried event: {queued_event.event.event_type}"
|
159
|
+
)
|
160
|
+
else:
|
161
|
+
failure_count += 1
|
162
|
+
queued_event.attempt_count += 1
|
163
|
+
queued_event.last_attempt = time.time()
|
164
|
+
if queued_event.attempt_count < self.config.max_retries:
|
165
|
+
self.add_failed_event(queued_event.event, queued_event.system_info)
|
166
|
+
else:
|
167
|
+
logger.error(
|
168
|
+
f"Event exhausted retry attempts: {queued_event.event.event_type}"
|
169
|
+
)
|
170
|
+
except Exception as e:
|
171
|
+
logger.error(f"Error processing retry queue: {e}")
|
172
|
+
failure_count += 1
|
173
|
+
|
174
|
+
finally:
|
175
|
+
self._is_processing = False
|
176
|
+
|
177
|
+
return success_count, failure_count
|
178
|
+
|
179
|
+
|
180
|
+
# Global retry queue instance
|
181
|
+
retry_queue = RetryQueue(TracingConfig(api_key="")) # Will be initialized with proper config later
|
182
|
+
|
183
|
+
|
184
|
+
def initialize_retry_queue(config: TracingConfig) -> None:
|
185
|
+
"""Initialize the global retry queue with the given config."""
|
186
|
+
global retry_queue
|
187
|
+
retry_queue = RetryQueue(config)
|