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.
Files changed (266) hide show
  1. synth_ai/__init__.py +28 -2
  2. synth_ai/core/system.py +4 -0
  3. synth_ai/environments/__init__.py +35 -0
  4. synth_ai/environments/environment/__init__.py +1 -0
  5. synth_ai/environments/environment/artifacts/__init__.py +1 -0
  6. synth_ai/environments/environment/artifacts/base.py +50 -0
  7. synth_ai/environments/environment/core.py +22 -0
  8. synth_ai/environments/environment/db/__init__.py +1 -0
  9. synth_ai/environments/environment/db/sqlite.py +45 -0
  10. synth_ai/environments/environment/registry.py +24 -0
  11. synth_ai/environments/environment/resources/sqlite.py +46 -0
  12. synth_ai/environments/environment/results.py +1 -0
  13. synth_ai/environments/environment/rewards/__init__.py +1 -0
  14. synth_ai/environments/environment/rewards/core.py +28 -0
  15. synth_ai/environments/environment/shared_engine.py +26 -0
  16. synth_ai/environments/environment/tools/__init__.py +34 -0
  17. synth_ai/environments/examples/__init__.py +1 -0
  18. synth_ai/environments/examples/crafter_classic/__init__.py +8 -0
  19. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +58 -0
  20. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +152 -0
  21. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +1194 -0
  22. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +51 -0
  23. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +872 -0
  24. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +1412 -0
  25. synth_ai/environments/examples/crafter_classic/agent_demos/test_crafter_react_agent.py +1110 -0
  26. synth_ai/environments/examples/crafter_classic/config_logging.py +111 -0
  27. synth_ai/environments/examples/crafter_classic/engine.py +502 -0
  28. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +63 -0
  29. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +5 -0
  30. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +74 -0
  31. synth_ai/environments/examples/crafter_classic/environment.py +255 -0
  32. synth_ai/environments/examples/crafter_classic/taskset.py +228 -0
  33. synth_ai/environments/examples/enron/agent_demos/test_synth_react.py +535 -0
  34. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +156 -0
  35. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +280 -0
  36. synth_ai/environments/examples/enron/art_helpers/types_enron.py +24 -0
  37. synth_ai/environments/examples/enron/engine.py +291 -0
  38. synth_ai/environments/examples/enron/environment.py +165 -0
  39. synth_ai/environments/examples/enron/taskset.py +112 -0
  40. synth_ai/environments/examples/enron/units/keyword_stats.py +111 -0
  41. synth_ai/environments/examples/enron/units/test_email_index.py +8 -0
  42. synth_ai/environments/examples/minigrid/__init__.py +48 -0
  43. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +1188 -0
  44. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +47 -0
  45. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +562 -0
  46. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +220 -0
  47. synth_ai/environments/examples/minigrid/agent_demos/test_minigrid_react_agent.py +393 -0
  48. synth_ai/environments/examples/minigrid/engine.py +589 -0
  49. synth_ai/environments/examples/minigrid/environment.py +274 -0
  50. synth_ai/environments/examples/minigrid/environment_mapping.py +242 -0
  51. synth_ai/environments/examples/minigrid/puzzle_loader.py +416 -0
  52. synth_ai/environments/examples/minigrid/taskset.py +583 -0
  53. synth_ai/environments/examples/minigrid/units/test_action_behavior.py +226 -0
  54. synth_ai/environments/examples/minigrid/units/test_debug_messages.py +83 -0
  55. synth_ai/environments/examples/minigrid/units/test_exploration.py +120 -0
  56. synth_ai/environments/examples/minigrid/units/test_minigrid_engine.py +214 -0
  57. synth_ai/environments/examples/minigrid/units/test_minigrid_environment.py +238 -0
  58. synth_ai/environments/examples/minigrid/units/test_minigrid_environment_mapping.py +301 -0
  59. synth_ai/environments/examples/minigrid/units/test_minigrid_taskset.py +210 -0
  60. synth_ai/environments/examples/nethack/__init__.py +7 -0
  61. synth_ai/environments/examples/nethack/achievements.py +337 -0
  62. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +981 -0
  63. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +74 -0
  64. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +832 -0
  65. synth_ai/environments/examples/nethack/agent_demos/test_nethack_react_agent.py +1112 -0
  66. synth_ai/environments/examples/nethack/engine.py +738 -0
  67. synth_ai/environments/examples/nethack/environment.py +255 -0
  68. synth_ai/environments/examples/nethack/helpers/__init__.py +42 -0
  69. synth_ai/environments/examples/nethack/helpers/action_mapping.py +301 -0
  70. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +401 -0
  71. synth_ai/environments/examples/nethack/helpers/observation_utils.py +433 -0
  72. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +201 -0
  73. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +268 -0
  74. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +308 -0
  75. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +430 -0
  76. synth_ai/environments/examples/nethack/taskset.py +323 -0
  77. synth_ai/environments/examples/nethack/units/test_nethack_engine.py +277 -0
  78. synth_ai/environments/examples/nethack/units/test_nethack_environment.py +281 -0
  79. synth_ai/environments/examples/nethack/units/test_nethack_taskset.py +213 -0
  80. synth_ai/environments/examples/nethack/units/test_recording.py +307 -0
  81. synth_ai/environments/examples/red/__init__.py +7 -0
  82. synth_ai/environments/examples/red/agent_demos/__init__.py +1 -0
  83. synth_ai/environments/examples/red/agent_demos/test_synth_react.py +1471 -0
  84. synth_ai/environments/examples/red/config_logging.py +110 -0
  85. synth_ai/environments/examples/red/engine.py +693 -0
  86. synth_ai/environments/examples/red/engine_helpers/__init__.py +1 -0
  87. synth_ai/environments/examples/red/engine_helpers/memory_map.py +28 -0
  88. synth_ai/environments/examples/red/engine_helpers/reward_components.py +275 -0
  89. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +142 -0
  90. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +56 -0
  91. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +283 -0
  92. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +149 -0
  93. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +137 -0
  94. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +56 -0
  95. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +330 -0
  96. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +120 -0
  97. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +558 -0
  98. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +312 -0
  99. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +147 -0
  100. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +246 -0
  101. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +367 -0
  102. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +139 -0
  103. synth_ai/environments/examples/red/environment.py +235 -0
  104. synth_ai/environments/examples/red/taskset.py +77 -0
  105. synth_ai/environments/examples/red/test_fixes.py +125 -0
  106. synth_ai/environments/examples/red/test_fixes_mock.py +148 -0
  107. synth_ai/environments/examples/red/units/__init__.py +1 -0
  108. synth_ai/environments/examples/red/units/test_basic_functionality.py +97 -0
  109. synth_ai/environments/examples/red/units/test_button_press_requirements.py +217 -0
  110. synth_ai/environments/examples/red/units/test_engine.py +192 -0
  111. synth_ai/environments/examples/red/units/test_environment.py +455 -0
  112. synth_ai/environments/examples/red/units/test_exploration_strategy.py +227 -0
  113. synth_ai/environments/examples/red/units/test_integration.py +217 -0
  114. synth_ai/environments/examples/red/units/test_memory_extraction.py +111 -0
  115. synth_ai/environments/examples/red/units/test_menu_bug_reproduction.py +1100 -0
  116. synth_ai/environments/examples/red/units/test_movement_debug.py +255 -0
  117. synth_ai/environments/examples/red/units/test_pokemon_mcts_debug.py +163 -0
  118. synth_ai/environments/examples/red/units/test_pokemon_mcts_verbose.py +117 -0
  119. synth_ai/environments/examples/red/units/test_red_basic.py +145 -0
  120. synth_ai/environments/examples/red/units/test_red_comprehensive.py +323 -0
  121. synth_ai/environments/examples/red/units/test_retry_movement.py +195 -0
  122. synth_ai/environments/examples/red/units/test_reward_components.py +186 -0
  123. synth_ai/environments/examples/red/units/test_rom_integration.py +260 -0
  124. synth_ai/environments/examples/red/units/test_taskset.py +116 -0
  125. synth_ai/environments/examples/red/units/test_tree.py +448 -0
  126. synth_ai/environments/examples/sokoban/__init__.py +1 -0
  127. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +900 -0
  128. synth_ai/environments/examples/sokoban/agent_demos/test_dspy_react.py +1 -0
  129. synth_ai/environments/examples/sokoban/agent_demos/test_sokoban_react_agent.py +498 -0
  130. synth_ai/environments/examples/sokoban/agent_demos/test_synth_lats.py +1 -0
  131. synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_locally.py +748 -0
  132. synth_ai/environments/examples/sokoban/agent_demos/test_synth_react_service.py +296 -0
  133. synth_ai/environments/examples/sokoban/engine.py +675 -0
  134. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +1 -0
  135. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +656 -0
  136. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +17 -0
  137. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +3 -0
  138. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +129 -0
  139. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +370 -0
  140. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +331 -0
  141. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +305 -0
  142. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +66 -0
  143. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +114 -0
  144. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +122 -0
  145. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +394 -0
  146. synth_ai/environments/examples/sokoban/environment.py +228 -0
  147. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +438 -0
  148. synth_ai/environments/examples/sokoban/puzzle_loader.py +311 -0
  149. synth_ai/environments/examples/sokoban/taskset.py +425 -0
  150. synth_ai/environments/examples/sokoban/units/astar_common.py +94 -0
  151. synth_ai/environments/examples/sokoban/units/test_building_task_set.py +49 -0
  152. synth_ai/environments/examples/sokoban/units/test_false_positive.py +120 -0
  153. synth_ai/environments/examples/sokoban/units/test_simple_run_through_environment.py +119 -0
  154. synth_ai/environments/examples/sokoban/units/test_sokoban_environment.py +98 -0
  155. synth_ai/environments/examples/sokoban/units/test_tree.py +364 -0
  156. synth_ai/environments/examples/tictactoe/__init__.py +1 -0
  157. synth_ai/environments/examples/tictactoe/agent_demos/test_synth_react.py +266 -0
  158. synth_ai/environments/examples/tictactoe/agent_demos/test_tictactoe_react_agent.py +470 -0
  159. synth_ai/environments/examples/tictactoe/engine.py +368 -0
  160. synth_ai/environments/examples/tictactoe/environment.py +239 -0
  161. synth_ai/environments/examples/tictactoe/taskset.py +214 -0
  162. synth_ai/environments/examples/tictactoe/units/test_tictactoe_engine.py +393 -0
  163. synth_ai/environments/examples/tictactoe/units/test_tictactoe_environment.py +493 -0
  164. synth_ai/environments/examples/tictactoe/units/test_tictactoe_taskset.py +191 -0
  165. synth_ai/environments/examples/verilog/__init__.py +10 -0
  166. synth_ai/environments/examples/verilog/agent_demos/test_synth_react.py +520 -0
  167. synth_ai/environments/examples/verilog/engine.py +328 -0
  168. synth_ai/environments/examples/verilog/environment.py +349 -0
  169. synth_ai/environments/examples/verilog/taskset.py +418 -0
  170. synth_ai/environments/examples/verilog/units/test_verilog_engine.py +466 -0
  171. synth_ai/environments/examples/verilog/units/test_verilog_environment.py +585 -0
  172. synth_ai/environments/examples/verilog/units/test_verilog_integration.py +383 -0
  173. synth_ai/environments/examples/verilog/units/test_verilog_taskset.py +457 -0
  174. synth_ai/environments/reproducibility/core.py +42 -0
  175. synth_ai/environments/reproducibility/tree.py +364 -0
  176. synth_ai/environments/service/app.py +78 -0
  177. synth_ai/environments/service/core_routes.py +775 -0
  178. synth_ai/environments/service/external_registry.py +57 -0
  179. synth_ai/environments/service/registry.py +9 -0
  180. synth_ai/environments/stateful/__init__.py +1 -0
  181. synth_ai/environments/stateful/core.py +28 -0
  182. synth_ai/environments/stateful/engine.py +21 -0
  183. synth_ai/environments/stateful/state.py +7 -0
  184. synth_ai/environments/tasks/api.py +19 -0
  185. synth_ai/environments/tasks/core.py +78 -0
  186. synth_ai/environments/tasks/filters.py +39 -0
  187. synth_ai/environments/tasks/utils.py +89 -0
  188. synth_ai/environments/v0_observability/history.py +3 -0
  189. synth_ai/environments/v0_observability/log.py +2 -0
  190. synth_ai/lm/caching/constants.py +1 -0
  191. synth_ai/{zyk/lms → lm}/caching/ephemeral.py +4 -8
  192. synth_ai/{zyk/lms → lm}/caching/handler.py +15 -15
  193. synth_ai/{zyk/lms → lm}/caching/initialize.py +2 -4
  194. synth_ai/{zyk/lms → lm}/caching/persistent.py +4 -10
  195. synth_ai/{zyk/lms → lm}/config.py +2 -1
  196. synth_ai/{zyk/lms → lm}/constants.py +2 -2
  197. synth_ai/{zyk/lms → lm}/core/all.py +10 -10
  198. synth_ai/{zyk/lms → lm}/core/main.py +57 -33
  199. synth_ai/{zyk/lms → lm}/core/vendor_clients.py +12 -10
  200. synth_ai/lm/cost/monitor.py +1 -0
  201. synth_ai/lm/cost/statefulness.py +1 -0
  202. synth_ai/lm/provider_support/__init__.py +8 -0
  203. synth_ai/lm/provider_support/anthropic.py +945 -0
  204. synth_ai/lm/provider_support/openai.py +1115 -0
  205. synth_ai/lm/provider_support/suppress_logging.py +31 -0
  206. synth_ai/{zyk/lms → lm}/structured_outputs/handler.py +58 -80
  207. synth_ai/{zyk/lms → lm}/structured_outputs/inject.py +6 -20
  208. synth_ai/{zyk/lms → lm}/structured_outputs/rehabilitate.py +6 -12
  209. synth_ai/{zyk/lms → lm}/vendors/core/anthropic_api.py +21 -30
  210. synth_ai/{zyk/lms → lm}/vendors/core/gemini_api.py +37 -32
  211. synth_ai/{zyk/lms → lm}/vendors/core/mistral_api.py +19 -28
  212. synth_ai/{zyk/lms → lm}/vendors/core/openai_api.py +26 -36
  213. synth_ai/{zyk/lms → lm}/vendors/openai_standard.py +29 -33
  214. synth_ai/{zyk/lms → lm}/vendors/retries.py +1 -1
  215. synth_ai/lm/vendors/supported/__init__.py +0 -0
  216. synth_ai/{zyk/lms → lm}/vendors/supported/custom_endpoint.py +131 -118
  217. synth_ai/{zyk/lms → lm}/vendors/supported/deepseek.py +4 -8
  218. synth_ai/{zyk/lms → lm}/vendors/supported/grok.py +6 -8
  219. synth_ai/{zyk/lms → lm}/vendors/supported/groq.py +1 -1
  220. synth_ai/{zyk/lms → lm}/vendors/supported/ollama.py +2 -2
  221. synth_ai/{zyk/lms → lm}/vendors/supported/openrouter.py +18 -16
  222. synth_ai/{zyk/lms → lm}/vendors/supported/together.py +1 -1
  223. synth_ai/tracing/__init__.py +0 -0
  224. synth_ai/tracing/abstractions.py +224 -0
  225. synth_ai/tracing/base_client.py +91 -0
  226. synth_ai/tracing/client_manager.py +131 -0
  227. synth_ai/tracing/config.py +140 -0
  228. synth_ai/tracing/context.py +146 -0
  229. synth_ai/tracing/decorators.py +679 -0
  230. synth_ai/tracing/events/__init__.py +0 -0
  231. synth_ai/tracing/events/manage.py +147 -0
  232. synth_ai/tracing/events/scope.py +86 -0
  233. synth_ai/tracing/events/store.py +227 -0
  234. synth_ai/tracing/immediate_client.py +152 -0
  235. synth_ai/tracing/local.py +18 -0
  236. synth_ai/tracing/log_client_base.py +74 -0
  237. synth_ai/tracing/retry_queue.py +187 -0
  238. synth_ai/tracing/trackers.py +515 -0
  239. synth_ai/tracing/upload.py +504 -0
  240. synth_ai/tracing/utils.py +9 -0
  241. synth_ai/zyk/__init__.py +28 -2
  242. synth_ai-0.2.1.dev0.dist-info/METADATA +349 -0
  243. synth_ai-0.2.1.dev0.dist-info/RECORD +261 -0
  244. synth_ai/zyk/lms/caching/constants.py +0 -1
  245. synth_ai/zyk/lms/cost/monitor.py +0 -1
  246. synth_ai/zyk/lms/cost/statefulness.py +0 -1
  247. synth_ai-0.1.9.dist-info/METADATA +0 -37
  248. synth_ai-0.1.9.dist-info/RECORD +0 -50
  249. /synth_ai/{zyk/lms/__init__.py → environments/reproducibility/helpers.py} +0 -0
  250. /synth_ai/{zyk/lms/caching → lm}/__init__.py +0 -0
  251. /synth_ai/{zyk/lms/core → lm/caching}/__init__.py +0 -0
  252. /synth_ai/{zyk/lms → lm}/caching/dbs.py +0 -0
  253. /synth_ai/{zyk/lms/cost → lm/core}/__init__.py +0 -0
  254. /synth_ai/{zyk/lms → lm}/core/exceptions.py +0 -0
  255. /synth_ai/{zyk/lms/structured_outputs → lm/cost}/__init__.py +0 -0
  256. /synth_ai/{zyk/lms/vendors → lm/structured_outputs}/__init__.py +0 -0
  257. /synth_ai/{zyk/lms → lm}/tools/__init__.py +0 -0
  258. /synth_ai/{zyk/lms → lm}/tools/base.py +0 -0
  259. /synth_ai/{zyk/lms/vendors/core → lm/vendors}/__init__.py +0 -0
  260. /synth_ai/{zyk/lms → lm}/vendors/base.py +0 -0
  261. /synth_ai/{zyk/lms/vendors/local → lm/vendors/core}/__init__.py +0 -0
  262. /synth_ai/{zyk/lms/vendors/supported → lm/vendors/local}/__init__.py +0 -0
  263. /synth_ai/{zyk/lms → lm}/vendors/local/ollama.py +0 -0
  264. {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/WHEEL +0 -0
  265. {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/licenses/LICENSE +0 -0
  266. {synth_ai-0.1.9.dist-info → synth_ai-0.2.1.dev0.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ import os
2
2
 
3
3
  from together import AsyncTogether, Together
4
4
 
5
- from synth_ai.zyk.lms.vendors.openai_standard import OpenAIStandard
5
+ from synth_ai.lm.vendors.openai_standard import OpenAIStandard
6
6
 
7
7
 
8
8
  class TogetherAPI(OpenAIStandard):
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]]