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
@@ -0,0 +1,147 @@
|
|
1
|
+
import time
|
2
|
+
from typing import Literal, Optional
|
3
|
+
|
4
|
+
from synth_ai.tracing.abstractions import Event
|
5
|
+
from synth_ai.tracing.events.store import event_store
|
6
|
+
from synth_ai.tracing.local import _local, logger
|
7
|
+
|
8
|
+
|
9
|
+
def get_current_event(event_type: str) -> "Event":
|
10
|
+
"""
|
11
|
+
Get the current active event of the specified type.
|
12
|
+
Raises ValueError if no such event exists.
|
13
|
+
"""
|
14
|
+
events = getattr(_local, "active_events", {})
|
15
|
+
if event_type not in events:
|
16
|
+
raise ValueError(f"No active event of type '{event_type}' found")
|
17
|
+
return events[event_type]
|
18
|
+
|
19
|
+
|
20
|
+
def set_current_event(event: Optional["Event"], decorator_type: Literal["sync", "async"] = None):
|
21
|
+
"""
|
22
|
+
Set the current event, ending any existing events of the same type.
|
23
|
+
If event is None, it clears the current event of that type.
|
24
|
+
"""
|
25
|
+
if event is None:
|
26
|
+
raise ValueError("Event cannot be None when setting current event.")
|
27
|
+
|
28
|
+
# Check if we're in an async context
|
29
|
+
try:
|
30
|
+
import asyncio
|
31
|
+
|
32
|
+
asyncio.get_running_loop()
|
33
|
+
is_async = True
|
34
|
+
except RuntimeError:
|
35
|
+
is_async = False
|
36
|
+
|
37
|
+
if decorator_type == "sync" or not is_async:
|
38
|
+
# Original thread-local storage logic
|
39
|
+
if not hasattr(_local, "active_events"):
|
40
|
+
_local.active_events = {}
|
41
|
+
logger.debug("Initialized active_events in thread local storage")
|
42
|
+
|
43
|
+
# If there's an existing event of the same type, end it
|
44
|
+
if event.event_type in _local.active_events:
|
45
|
+
if (
|
46
|
+
_local.active_events[event.event_type].system_instance_id
|
47
|
+
== event.system_instance_id
|
48
|
+
):
|
49
|
+
logger.debug(f"Found existing event of type {event.event_type}")
|
50
|
+
existing_event = _local.active_events[event.event_type]
|
51
|
+
existing_event.closed = time.time()
|
52
|
+
logger.debug(
|
53
|
+
f"Closed existing event of type {event.event_type} at {existing_event.closed}"
|
54
|
+
)
|
55
|
+
|
56
|
+
# Store the closed event if system_instance_id is present
|
57
|
+
if hasattr(_local, "system_instance_id"):
|
58
|
+
logger.debug(f"Storing closed event for system {_local.system_instance_id}")
|
59
|
+
try:
|
60
|
+
event_store.add_event(
|
61
|
+
_local.system_name,
|
62
|
+
_local.system_id,
|
63
|
+
_local.system_instance_id,
|
64
|
+
existing_event,
|
65
|
+
)
|
66
|
+
logger.debug("Successfully stored closed event")
|
67
|
+
except Exception as e:
|
68
|
+
logger.error(f"Failed to store closed event: {str(e)}")
|
69
|
+
raise
|
70
|
+
|
71
|
+
# Set the new event with both keys
|
72
|
+
_local.active_events[event.event_type] = event # Plain key
|
73
|
+
if is_async:
|
74
|
+
unique_key = f"{event.event_type}-{time.time()}"
|
75
|
+
_local.active_events[unique_key] = event # Unique key for async
|
76
|
+
|
77
|
+
else:
|
78
|
+
from synth_ai.tracing.local import (
|
79
|
+
active_events_var,
|
80
|
+
system_id_var,
|
81
|
+
system_instance_id_var,
|
82
|
+
system_name_var,
|
83
|
+
)
|
84
|
+
|
85
|
+
# Get current active events from context var
|
86
|
+
active_events = active_events_var.get()
|
87
|
+
# print("Active events:", active_events)
|
88
|
+
# If there's an existing event of the same type, end it
|
89
|
+
if event and event.event_type in active_events:
|
90
|
+
existing_event = active_events[event.event_type]
|
91
|
+
|
92
|
+
# Check that the active event has the same system_instance_id as the one we're settting
|
93
|
+
if existing_event.system_instance_id == event.system_instance_id:
|
94
|
+
logger.debug(f"Found existing event of type {event.event_type}")
|
95
|
+
existing_event.closed = time.time()
|
96
|
+
logger.debug(
|
97
|
+
f"Closed existing event of type {event.event_type} at {existing_event.closed}"
|
98
|
+
)
|
99
|
+
|
100
|
+
# Store the closed event if system_instance_id is present
|
101
|
+
system_instance_id = system_instance_id_var.get()
|
102
|
+
if system_instance_id:
|
103
|
+
logger.debug(f"Storing closed event for system {system_instance_id}")
|
104
|
+
try:
|
105
|
+
event_store.add_event(
|
106
|
+
system_name_var.get(),
|
107
|
+
system_id_var.get(),
|
108
|
+
system_instance_id,
|
109
|
+
existing_event,
|
110
|
+
)
|
111
|
+
logger.debug("Successfully stored closed event")
|
112
|
+
except Exception as e:
|
113
|
+
logger.error(f"Failed to store closed event: {str(e)}")
|
114
|
+
raise
|
115
|
+
elif not event:
|
116
|
+
logger.debug(f"No event found for type {event.event_type}")
|
117
|
+
# Set the new event with both keys
|
118
|
+
active_events[event.event_type] = event # Plain key
|
119
|
+
unique_key = f"{event.event_type}-{time.time()}"
|
120
|
+
active_events[unique_key] = event # Unique key for async
|
121
|
+
active_events_var.set(active_events)
|
122
|
+
logger.debug(
|
123
|
+
f"New event set as current in context vars with keys '{event.event_type}' and '{unique_key}'"
|
124
|
+
)
|
125
|
+
|
126
|
+
|
127
|
+
def clear_current_event(event_type: str):
|
128
|
+
if hasattr(_local, "active_events"):
|
129
|
+
_local.active_events.pop(event_type, None)
|
130
|
+
logger.debug(f"Cleared current event of type {event_type}")
|
131
|
+
|
132
|
+
|
133
|
+
def end_event(event_type: str) -> Optional[Event]:
|
134
|
+
"""End the current event and store it."""
|
135
|
+
current_event = get_current_event(event_type)
|
136
|
+
if current_event:
|
137
|
+
current_event.closed = time.time()
|
138
|
+
# Store the event
|
139
|
+
if hasattr(_local, "system_instance_id"):
|
140
|
+
event_store.add_event(
|
141
|
+
_local.system_name,
|
142
|
+
_local.system_id,
|
143
|
+
_local.system_instance_id,
|
144
|
+
current_event,
|
145
|
+
)
|
146
|
+
clear_current_event(event_type)
|
147
|
+
return current_event
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import time
|
2
|
+
from contextlib import contextmanager
|
3
|
+
|
4
|
+
from synth_ai.tracing.abstractions import Event
|
5
|
+
from synth_ai.tracing.config import LoggingMode
|
6
|
+
from synth_ai.tracing.decorators import (
|
7
|
+
_local,
|
8
|
+
clear_current_event,
|
9
|
+
get_tracing_config,
|
10
|
+
set_current_event,
|
11
|
+
)
|
12
|
+
from synth_ai.tracing.events.store import event_store
|
13
|
+
from synth_ai.tracing.immediate_client import (
|
14
|
+
AsyncImmediateLogClient,
|
15
|
+
ImmediateLogClient,
|
16
|
+
)
|
17
|
+
from synth_ai.tracing.local import (
|
18
|
+
system_id_var,
|
19
|
+
system_instance_id_var,
|
20
|
+
system_name_var,
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
@contextmanager
|
25
|
+
def event_scope(event_type: str):
|
26
|
+
"""
|
27
|
+
Context manager for creating and managing events.
|
28
|
+
|
29
|
+
Usage:
|
30
|
+
with event_scope("my_event_type"):
|
31
|
+
# do stuff
|
32
|
+
"""
|
33
|
+
# Check if we're in an async context
|
34
|
+
try:
|
35
|
+
import asyncio
|
36
|
+
|
37
|
+
asyncio.get_running_loop()
|
38
|
+
is_async = True
|
39
|
+
except RuntimeError:
|
40
|
+
is_async = False
|
41
|
+
|
42
|
+
# Get system_instance_id from appropriate source
|
43
|
+
system_instance_id = (
|
44
|
+
system_instance_id_var.get() if is_async else getattr(_local, "system_instance_id", None)
|
45
|
+
)
|
46
|
+
system_id = system_id_var.get() if is_async else getattr(_local, "system_id", None)
|
47
|
+
system_name = system_name_var.get() if is_async else getattr(_local, "system_name", None)
|
48
|
+
|
49
|
+
event = Event(
|
50
|
+
system_instance_id=system_instance_id,
|
51
|
+
event_type=event_type,
|
52
|
+
opened=time.time(),
|
53
|
+
closed=None,
|
54
|
+
partition_index=0,
|
55
|
+
agent_compute_step=None,
|
56
|
+
environment_compute_steps=[],
|
57
|
+
)
|
58
|
+
set_current_event(event)
|
59
|
+
|
60
|
+
try:
|
61
|
+
yield event
|
62
|
+
finally:
|
63
|
+
event.closed = time.time()
|
64
|
+
clear_current_event(event_type)
|
65
|
+
|
66
|
+
# Get the config to determine logging mode
|
67
|
+
config = get_tracing_config()
|
68
|
+
|
69
|
+
# If immediate logging is enabled and we have system info, send the event now
|
70
|
+
if config.mode == LoggingMode.INSTANT and system_instance_id:
|
71
|
+
system_info = {
|
72
|
+
"system_name": system_name,
|
73
|
+
"system_id": system_id,
|
74
|
+
"system_instance_id": system_instance_id,
|
75
|
+
}
|
76
|
+
if is_async:
|
77
|
+
client = AsyncImmediateLogClient(config)
|
78
|
+
# Note: Since we can't use await in a finally block,
|
79
|
+
# we'll have to rely on the event store as primary storage in async context
|
80
|
+
else:
|
81
|
+
client = ImmediateLogClient(config)
|
82
|
+
client.send_event(event, system_info)
|
83
|
+
|
84
|
+
# Always store in event_store
|
85
|
+
if system_instance_id:
|
86
|
+
event_store.add_event(system_name, system_id, system_instance_id, event)
|
@@ -0,0 +1,227 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
import time
|
4
|
+
from threading import RLock # Change this import
|
5
|
+
from typing import Any, Dict, List
|
6
|
+
|
7
|
+
from synth_ai.tracing.abstractions import Event, EventPartitionElement, SystemTrace
|
8
|
+
from synth_ai.tracing.local import ( # Import context variables
|
9
|
+
_local,
|
10
|
+
active_events_var,
|
11
|
+
)
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class EventStore:
|
17
|
+
def __init__(self) -> None:
|
18
|
+
self._traces: Dict[str, SystemTrace] = {}
|
19
|
+
self._lock = RLock() # Use RLock instead of Lock
|
20
|
+
self.logger = logging.getLogger(__name__)
|
21
|
+
|
22
|
+
def get_or_create_system_trace(
|
23
|
+
self,
|
24
|
+
system_name: str,
|
25
|
+
system_id: str,
|
26
|
+
system_instance_id: str,
|
27
|
+
_already_locked: bool = False,
|
28
|
+
) -> SystemTrace:
|
29
|
+
"""Get or create a SystemTrace for the given system_instance_id."""
|
30
|
+
logger = logging.getLogger(__name__)
|
31
|
+
# logger.debug(f"Starting get_or_create_system_trace for {system_instance_id}")
|
32
|
+
|
33
|
+
def _get_or_create():
|
34
|
+
# logger.debug("Inside _get_or_create")
|
35
|
+
if system_instance_id not in self._traces:
|
36
|
+
# Get system_instance_metadata from context if available
|
37
|
+
system_instance_metadata = {}
|
38
|
+
try:
|
39
|
+
from synth_ai.tracing.context import get_current_context
|
40
|
+
|
41
|
+
context = get_current_context()
|
42
|
+
if (
|
43
|
+
"system_instance_metadata" in context
|
44
|
+
and context["system_instance_metadata"]
|
45
|
+
):
|
46
|
+
system_instance_metadata = context["system_instance_metadata"]
|
47
|
+
except Exception as e:
|
48
|
+
self.logger.warning(
|
49
|
+
f"Failed to get system_instance_metadata from context: {str(e)}"
|
50
|
+
)
|
51
|
+
|
52
|
+
# logger.debug(f"Creating new system trace for {system_instance_id}")
|
53
|
+
self._traces[system_instance_id] = SystemTrace(
|
54
|
+
system_name=system_name,
|
55
|
+
system_id=system_id,
|
56
|
+
system_instance_id=system_instance_id,
|
57
|
+
metadata={},
|
58
|
+
partition=[], # EventPartitionElement(partition_index=0, events=[])
|
59
|
+
current_partition_index=0,
|
60
|
+
instance_metadata=system_instance_metadata,
|
61
|
+
)
|
62
|
+
# logger.debug("Returning system trace")
|
63
|
+
return self._traces[system_instance_id]
|
64
|
+
|
65
|
+
if _already_locked:
|
66
|
+
return _get_or_create()
|
67
|
+
else:
|
68
|
+
with self._lock:
|
69
|
+
# logger.debug("Lock acquired in get_or_create_system_trace")
|
70
|
+
return _get_or_create()
|
71
|
+
|
72
|
+
def increment_partition(self, system_name: str, system_id: str, system_instance_id: str) -> int:
|
73
|
+
"""Increment the partition index for a system and create new partition element."""
|
74
|
+
logger = logging.getLogger(__name__)
|
75
|
+
# logger.debug(f"Starting increment_partition for system {system_instance_id}")
|
76
|
+
|
77
|
+
with self._lock:
|
78
|
+
# logger.debug("Lock acquired in increment_partition")
|
79
|
+
system_trace = self.get_or_create_system_trace(
|
80
|
+
system_name, system_id, system_instance_id, _already_locked=True
|
81
|
+
)
|
82
|
+
# logger.debug(
|
83
|
+
# f"Got system trace, current index: {system_trace.current_partition_index}"
|
84
|
+
# )
|
85
|
+
|
86
|
+
system_trace.current_partition_index += 1
|
87
|
+
# logger.debug(
|
88
|
+
# f"Incremented index to: {system_trace.current_partition_index}"
|
89
|
+
# )
|
90
|
+
|
91
|
+
system_trace.partition.append(
|
92
|
+
EventPartitionElement(
|
93
|
+
partition_index=system_trace.current_partition_index, events=[]
|
94
|
+
)
|
95
|
+
)
|
96
|
+
# logger.debug("Added new partition element")
|
97
|
+
|
98
|
+
return system_trace.current_partition_index
|
99
|
+
|
100
|
+
def add_event(self, system_name: str, system_id: str, system_instance_id: str, event: Event):
|
101
|
+
"""Add an event to the appropriate partition of the system trace."""
|
102
|
+
# self.#logger.debug(f"Adding event type {event.event_type} to system {system_instance_id}")
|
103
|
+
# self.#logger.debug(
|
104
|
+
# f"Event details: opened={event.opened}, closed={event.closed}, partition={event.partition_index}"
|
105
|
+
# )
|
106
|
+
# print("Adding event: ", event)
|
107
|
+
|
108
|
+
# try:
|
109
|
+
if not self._lock.acquire(timeout=5):
|
110
|
+
self.logger.error("Failed to acquire lock within timeout period")
|
111
|
+
return
|
112
|
+
|
113
|
+
try:
|
114
|
+
system_trace = self.get_or_create_system_trace(
|
115
|
+
system_name, system_id, system_instance_id, _already_locked=True
|
116
|
+
)
|
117
|
+
# self.#logger.debug(
|
118
|
+
# f"Got system trace with {len(system_trace.partition)} partitions"
|
119
|
+
# )
|
120
|
+
|
121
|
+
current_partition = next(
|
122
|
+
(p for p in system_trace.partition if p.partition_index == event.partition_index),
|
123
|
+
None,
|
124
|
+
)
|
125
|
+
|
126
|
+
if current_partition is None:
|
127
|
+
self.logger.error(
|
128
|
+
f"No partition found for index {event.partition_index} - existing partitions: {set([p.partition_index for p in system_trace.partition])}"
|
129
|
+
)
|
130
|
+
raise ValueError(f"No partition found for index {event.partition_index}")
|
131
|
+
|
132
|
+
current_partition.events.append(event)
|
133
|
+
finally:
|
134
|
+
self._lock.release()
|
135
|
+
# except Exception as e:
|
136
|
+
# self.logger.error(f"Error in add_event: {str(e)}", exc_info=True)
|
137
|
+
# raise
|
138
|
+
|
139
|
+
def get_system_traces(self) -> List[SystemTrace]:
|
140
|
+
"""Get all system traces."""
|
141
|
+
with self._lock:
|
142
|
+
self.end_all_active_events()
|
143
|
+
|
144
|
+
return list(self._traces.values())
|
145
|
+
|
146
|
+
def end_all_active_events(self):
|
147
|
+
"""End all active events and store them."""
|
148
|
+
# self.#logger.debug("Ending all active events")
|
149
|
+
|
150
|
+
# For synchronous code
|
151
|
+
if hasattr(_local, "active_events"):
|
152
|
+
for _, event in list(_local.active_events.items()):
|
153
|
+
if event.closed is None:
|
154
|
+
event.closed = time.time()
|
155
|
+
self.add_event(
|
156
|
+
event.system_name,
|
157
|
+
event.system_id,
|
158
|
+
event.system_instance_id,
|
159
|
+
event,
|
160
|
+
)
|
161
|
+
_local.active_events.clear()
|
162
|
+
|
163
|
+
else:
|
164
|
+
# For asynchronous code
|
165
|
+
active_events_async = active_events_var.get()
|
166
|
+
if active_events_async:
|
167
|
+
for _, event in list(active_events_async.items()):
|
168
|
+
if event.closed is None:
|
169
|
+
event.closed = time.time()
|
170
|
+
self.add_event(
|
171
|
+
event.system_name,
|
172
|
+
event.system_id,
|
173
|
+
event.system_instance_id,
|
174
|
+
event,
|
175
|
+
)
|
176
|
+
active_events_var.set({})
|
177
|
+
|
178
|
+
def get_system_traces_json(self) -> str:
|
179
|
+
"""Get all system traces as JSON."""
|
180
|
+
with self._lock:
|
181
|
+
return json.dumps(
|
182
|
+
[
|
183
|
+
{
|
184
|
+
"system_instance_id": trace.system_instance_id,
|
185
|
+
"current_partition_index": trace.current_partition_index,
|
186
|
+
"partition": [
|
187
|
+
{
|
188
|
+
"partition_index": p.partition_index,
|
189
|
+
"events": [self._event_to_dict(event) for event in p.events],
|
190
|
+
}
|
191
|
+
for p in trace.partition
|
192
|
+
],
|
193
|
+
}
|
194
|
+
for trace in self._traces.values()
|
195
|
+
],
|
196
|
+
default=str,
|
197
|
+
)
|
198
|
+
|
199
|
+
def _event_to_dict(self, event: Event) -> Dict[str, Any]:
|
200
|
+
"""Convert an Event object to a dictionary."""
|
201
|
+
return {
|
202
|
+
"event_type": event.event_type,
|
203
|
+
"opened": event.opened,
|
204
|
+
"closed": event.closed,
|
205
|
+
"partition_index": event.partition_index,
|
206
|
+
"agent_compute_step": {
|
207
|
+
"event_order": event.agent_compute_step.event_order,
|
208
|
+
"compute_began": event.agent_compute_step.compute_began,
|
209
|
+
"compute_ended": event.agent_compute_step.compute_ended,
|
210
|
+
"compute_input": event.agent_compute_step.compute_input,
|
211
|
+
"compute_output": event.agent_compute_step.compute_output,
|
212
|
+
},
|
213
|
+
"environment_compute_steps": [
|
214
|
+
{
|
215
|
+
"event_order": step.event_order,
|
216
|
+
"compute_began": step.compute_began,
|
217
|
+
"compute_ended": step.compute_ended,
|
218
|
+
"compute_input": step.compute_input,
|
219
|
+
"compute_output": step.compute_output,
|
220
|
+
}
|
221
|
+
for step in event.environment_compute_steps
|
222
|
+
],
|
223
|
+
}
|
224
|
+
|
225
|
+
|
226
|
+
# Global event store instance
|
227
|
+
event_store = EventStore()
|
@@ -0,0 +1,152 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import time
|
4
|
+
from typing import Dict
|
5
|
+
|
6
|
+
import httpx
|
7
|
+
|
8
|
+
from synth_ai.tracing.abstractions import Event
|
9
|
+
from synth_ai.tracing.client_manager import ClientManager
|
10
|
+
from synth_ai.tracing.config import TracingConfig
|
11
|
+
from synth_ai.tracing.log_client_base import BaseAsyncLogClient, BaseLogClient
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class ImmediateLogClient(BaseLogClient):
|
17
|
+
"""Synchronous client for immediate logging of events"""
|
18
|
+
|
19
|
+
def __init__(self, config: TracingConfig):
|
20
|
+
super().__init__(config)
|
21
|
+
self.client_manager = ClientManager.initialize(config)
|
22
|
+
|
23
|
+
def send_event(self, event: Event, system_info: Dict[str, str]) -> bool:
|
24
|
+
"""Send a single event with retries and fallback"""
|
25
|
+
from synth_ai.tracing.retry_queue import (
|
26
|
+
retry_queue, # Import here to avoid circular import
|
27
|
+
)
|
28
|
+
|
29
|
+
payload = self._prepare_payload(event, system_info)
|
30
|
+
headers = {"Authorization": f"Bearer {self.config.api_key}"}
|
31
|
+
last_exception = None
|
32
|
+
|
33
|
+
for attempt in range(self.config.max_retries):
|
34
|
+
try:
|
35
|
+
response = self.client_manager.sync_client.post(
|
36
|
+
f"{self.config.base_url}/v1/uploads/stream",
|
37
|
+
json=payload,
|
38
|
+
headers=headers,
|
39
|
+
timeout=self.config.timeout,
|
40
|
+
)
|
41
|
+
logger.info(f"Event upload response status: {response.status_code}")
|
42
|
+
response.raise_for_status()
|
43
|
+
response_data = response.json()
|
44
|
+
event.id = response_data.get("event_id") # Store the event_id
|
45
|
+
self._handle_success()
|
46
|
+
return True
|
47
|
+
except Exception as e:
|
48
|
+
last_exception = e
|
49
|
+
status_code = getattr(e, "response", None)
|
50
|
+
if status_code:
|
51
|
+
status_code = status_code.status_code
|
52
|
+
|
53
|
+
if not self._should_retry(attempt, status_code):
|
54
|
+
break
|
55
|
+
|
56
|
+
backoff = self.client_manager.calculate_backoff(attempt)
|
57
|
+
time.sleep(backoff)
|
58
|
+
|
59
|
+
# If we get here, all immediate retries failed
|
60
|
+
self._handle_failure(event, system_info, last_exception)
|
61
|
+
|
62
|
+
# Add to retry queue for later processing
|
63
|
+
retry_queue.add_failed_event(event, system_info)
|
64
|
+
return False
|
65
|
+
|
66
|
+
|
67
|
+
class AsyncImmediateLogClient(BaseAsyncLogClient):
|
68
|
+
"""Asynchronous client for immediate logging of events"""
|
69
|
+
|
70
|
+
def __init__(self, config: TracingConfig):
|
71
|
+
super().__init__(config)
|
72
|
+
self.client_manager = ClientManager.initialize(config)
|
73
|
+
|
74
|
+
async def send_event(self, event: Event, system_info: Dict[str, str]) -> bool:
|
75
|
+
"""Send a single event with retries and fallback (async version)"""
|
76
|
+
from synth_ai.tracing.retry_queue import retry_queue
|
77
|
+
|
78
|
+
if not self.config.api_key:
|
79
|
+
logger.error("No API key provided")
|
80
|
+
return False
|
81
|
+
|
82
|
+
# First get JWT token
|
83
|
+
async with httpx.AsyncClient(timeout=self.config.timeout) as client:
|
84
|
+
try:
|
85
|
+
auth_response = await client.get(
|
86
|
+
f"{self.config.base_url}/v1/auth/token",
|
87
|
+
headers={"customer_specific_api_key": self.config.api_key.strip()},
|
88
|
+
)
|
89
|
+
auth_response.raise_for_status()
|
90
|
+
auth_data = auth_response.json() # This is synchronous in httpx
|
91
|
+
logger.debug(f"Auth response data: {auth_data}")
|
92
|
+
|
93
|
+
token = auth_data.get("access_token")
|
94
|
+
if not token:
|
95
|
+
logger.error(
|
96
|
+
f"No access token received from auth endpoint. Response data: {auth_data}"
|
97
|
+
)
|
98
|
+
return False
|
99
|
+
except Exception as e:
|
100
|
+
# logger.error(f"Failed to get auth token: {e}")
|
101
|
+
return False
|
102
|
+
|
103
|
+
# Now send the event with the JWT token
|
104
|
+
payload = self._prepare_payload(event, system_info)
|
105
|
+
headers = {
|
106
|
+
"Authorization": f"Bearer {token}",
|
107
|
+
"Content-Type": "application/json",
|
108
|
+
"Accept": "application/json",
|
109
|
+
}
|
110
|
+
|
111
|
+
logger.debug(f"Request URL: {self.config.base_url}/v1/uploads/stream")
|
112
|
+
logger.debug("Using JWT token for authentication")
|
113
|
+
logger.debug(f"Headers: {headers}")
|
114
|
+
logger.debug(f"Payload size: {len(str(payload))} bytes")
|
115
|
+
|
116
|
+
async with httpx.AsyncClient(timeout=self.config.timeout) as client:
|
117
|
+
for attempt in range(self.config.max_retries + 1):
|
118
|
+
try:
|
119
|
+
response = await client.post(
|
120
|
+
f"{self.config.base_url}/v1/uploads/stream",
|
121
|
+
json=payload,
|
122
|
+
headers=headers,
|
123
|
+
timeout=self.config.timeout,
|
124
|
+
)
|
125
|
+
logger.info(f"Event upload response status: {response.status_code}")
|
126
|
+
|
127
|
+
if response.status_code >= 500:
|
128
|
+
error_body = response.text
|
129
|
+
logger.error(f"Server error. Response: {error_body}")
|
130
|
+
response.raise_for_status()
|
131
|
+
|
132
|
+
if response.status_code == 401:
|
133
|
+
error_body = response.text
|
134
|
+
logger.error(f"Authentication failed. Response: {error_body}")
|
135
|
+
response.raise_for_status()
|
136
|
+
|
137
|
+
response_data = response.json() # This is synchronous in httpx
|
138
|
+
event.id = response_data.get("event_id")
|
139
|
+
return True
|
140
|
+
|
141
|
+
except Exception as e:
|
142
|
+
# last_exception = e
|
143
|
+
# logger.error(f"Upload attempt {attempt + 1} failed: {str(e)}")
|
144
|
+
if attempt < self.config.max_retries:
|
145
|
+
backoff = self.client_manager.calculate_backoff(attempt)
|
146
|
+
await asyncio.sleep(backoff)
|
147
|
+
|
148
|
+
# logger.error(
|
149
|
+
# f"All upload attempts failed. Last error: {last_exception}"
|
150
|
+
# )
|
151
|
+
retry_queue.add_failed_event(event, system_info)
|
152
|
+
return False
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import logging
|
2
|
+
import threading
|
3
|
+
from contextvars import ContextVar
|
4
|
+
from typing import Any, Dict
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
# Thread-local storage for active events and system_instance_id
|
9
|
+
# Used for synchronous tracing
|
10
|
+
_local = threading.local()
|
11
|
+
# Used for asynchronous tracing
|
12
|
+
system_name_var: ContextVar[str] = ContextVar("system_name", default=None)
|
13
|
+
system_id_var: ContextVar[str] = ContextVar("system_id", default=None)
|
14
|
+
system_instance_id_var: ContextVar[str] = ContextVar("system_instance_id", default=None)
|
15
|
+
system_instance_metadata_var: ContextVar[Dict[str, Any]] = ContextVar(
|
16
|
+
"system_instance_metadata", default={}
|
17
|
+
)
|
18
|
+
active_events_var: ContextVar[Dict[str, Any]] = ContextVar("active_events", default={})
|