synth-ai 0.2.4.dev6__py3-none-any.whl → 0.2.4.dev7__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 (229) hide show
  1. synth_ai/__init__.py +18 -9
  2. synth_ai/cli/__init__.py +10 -5
  3. synth_ai/cli/balance.py +22 -17
  4. synth_ai/cli/calc.py +2 -3
  5. synth_ai/cli/demo.py +3 -5
  6. synth_ai/cli/legacy_root_backup.py +58 -32
  7. synth_ai/cli/man.py +22 -19
  8. synth_ai/cli/recent.py +9 -8
  9. synth_ai/cli/root.py +58 -13
  10. synth_ai/cli/status.py +13 -6
  11. synth_ai/cli/traces.py +45 -21
  12. synth_ai/cli/watch.py +40 -37
  13. synth_ai/config/base_url.py +1 -3
  14. synth_ai/core/experiment.py +1 -2
  15. synth_ai/environments/__init__.py +2 -6
  16. synth_ai/environments/environment/artifacts/base.py +3 -1
  17. synth_ai/environments/environment/db/sqlite.py +1 -1
  18. synth_ai/environments/environment/registry.py +19 -20
  19. synth_ai/environments/environment/resources/sqlite.py +2 -3
  20. synth_ai/environments/environment/rewards/core.py +3 -2
  21. synth_ai/environments/environment/tools/__init__.py +6 -4
  22. synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
  23. synth_ai/environments/examples/crafter_classic/engine.py +13 -13
  24. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
  25. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
  26. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
  27. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
  28. synth_ai/environments/examples/crafter_classic/environment.py +16 -15
  29. synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
  30. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
  31. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
  32. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
  33. synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
  34. synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
  35. synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
  36. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
  37. synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
  38. synth_ai/environments/examples/crafter_custom/environment.py +13 -13
  39. synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
  40. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
  41. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
  42. synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
  43. synth_ai/environments/examples/enron/engine.py +18 -14
  44. synth_ai/environments/examples/enron/environment.py +12 -11
  45. synth_ai/environments/examples/enron/taskset.py +7 -7
  46. synth_ai/environments/examples/minigrid/__init__.py +6 -6
  47. synth_ai/environments/examples/minigrid/engine.py +6 -6
  48. synth_ai/environments/examples/minigrid/environment.py +6 -6
  49. synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
  50. synth_ai/environments/examples/minigrid/taskset.py +13 -13
  51. synth_ai/environments/examples/nethack/achievements.py +1 -1
  52. synth_ai/environments/examples/nethack/engine.py +8 -7
  53. synth_ai/environments/examples/nethack/environment.py +10 -9
  54. synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
  55. synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
  56. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
  57. synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
  58. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
  59. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
  60. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
  61. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
  62. synth_ai/environments/examples/nethack/taskset.py +5 -5
  63. synth_ai/environments/examples/red/engine.py +9 -8
  64. synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
  65. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
  66. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
  67. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
  68. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
  69. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
  70. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
  71. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
  72. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
  73. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
  74. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
  75. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
  76. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
  77. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
  78. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
  79. synth_ai/environments/examples/red/environment.py +18 -15
  80. synth_ai/environments/examples/red/taskset.py +5 -3
  81. synth_ai/environments/examples/sokoban/engine.py +16 -13
  82. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
  83. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
  84. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
  85. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
  86. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
  87. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
  88. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
  89. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
  90. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
  91. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
  92. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
  93. synth_ai/environments/examples/sokoban/environment.py +15 -14
  94. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
  95. synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
  96. synth_ai/environments/examples/sokoban/taskset.py +13 -10
  97. synth_ai/environments/examples/tictactoe/engine.py +6 -6
  98. synth_ai/environments/examples/tictactoe/environment.py +8 -7
  99. synth_ai/environments/examples/tictactoe/taskset.py +6 -5
  100. synth_ai/environments/examples/verilog/engine.py +4 -3
  101. synth_ai/environments/examples/verilog/environment.py +11 -10
  102. synth_ai/environments/examples/verilog/taskset.py +14 -12
  103. synth_ai/environments/examples/wordle/__init__.py +5 -5
  104. synth_ai/environments/examples/wordle/engine.py +32 -25
  105. synth_ai/environments/examples/wordle/environment.py +21 -16
  106. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
  107. synth_ai/environments/examples/wordle/taskset.py +20 -12
  108. synth_ai/environments/reproducibility/core.py +1 -1
  109. synth_ai/environments/reproducibility/tree.py +21 -21
  110. synth_ai/environments/service/app.py +3 -2
  111. synth_ai/environments/service/core_routes.py +104 -110
  112. synth_ai/environments/service/external_registry.py +1 -2
  113. synth_ai/environments/service/registry.py +1 -1
  114. synth_ai/environments/stateful/core.py +1 -2
  115. synth_ai/environments/stateful/engine.py +1 -1
  116. synth_ai/environments/tasks/api.py +4 -4
  117. synth_ai/environments/tasks/core.py +14 -12
  118. synth_ai/environments/tasks/filters.py +6 -4
  119. synth_ai/environments/tasks/utils.py +13 -11
  120. synth_ai/evals/base.py +2 -3
  121. synth_ai/experimental/synth_oss.py +4 -4
  122. synth_ai/learning/gateway.py +1 -3
  123. synth_ai/learning/prompts/banking77_injection_eval.py +15 -10
  124. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
  125. synth_ai/learning/prompts/mipro.py +61 -52
  126. synth_ai/learning/prompts/random_search.py +42 -43
  127. synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
  128. synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
  129. synth_ai/lm/__init__.py +5 -5
  130. synth_ai/lm/caching/ephemeral.py +9 -9
  131. synth_ai/lm/caching/handler.py +20 -20
  132. synth_ai/lm/caching/persistent.py +10 -10
  133. synth_ai/lm/config.py +3 -3
  134. synth_ai/lm/constants.py +7 -7
  135. synth_ai/lm/core/all.py +17 -3
  136. synth_ai/lm/core/exceptions.py +0 -2
  137. synth_ai/lm/core/main.py +26 -41
  138. synth_ai/lm/core/main_v3.py +20 -10
  139. synth_ai/lm/core/vendor_clients.py +18 -17
  140. synth_ai/lm/injection.py +7 -8
  141. synth_ai/lm/overrides.py +21 -19
  142. synth_ai/lm/provider_support/__init__.py +1 -1
  143. synth_ai/lm/provider_support/anthropic.py +15 -15
  144. synth_ai/lm/provider_support/openai.py +23 -21
  145. synth_ai/lm/structured_outputs/handler.py +34 -32
  146. synth_ai/lm/structured_outputs/inject.py +24 -27
  147. synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
  148. synth_ai/lm/tools/base.py +17 -16
  149. synth_ai/lm/unified_interface.py +17 -18
  150. synth_ai/lm/vendors/base.py +20 -18
  151. synth_ai/lm/vendors/core/anthropic_api.py +36 -27
  152. synth_ai/lm/vendors/core/gemini_api.py +31 -36
  153. synth_ai/lm/vendors/core/mistral_api.py +19 -19
  154. synth_ai/lm/vendors/core/openai_api.py +11 -10
  155. synth_ai/lm/vendors/openai_standard.py +113 -87
  156. synth_ai/lm/vendors/openai_standard_responses.py +74 -61
  157. synth_ai/lm/vendors/retries.py +9 -1
  158. synth_ai/lm/vendors/supported/custom_endpoint.py +26 -26
  159. synth_ai/lm/vendors/supported/deepseek.py +10 -10
  160. synth_ai/lm/vendors/supported/grok.py +8 -8
  161. synth_ai/lm/vendors/supported/ollama.py +2 -1
  162. synth_ai/lm/vendors/supported/openrouter.py +11 -9
  163. synth_ai/lm/vendors/synth_client.py +69 -63
  164. synth_ai/lm/warmup.py +8 -7
  165. synth_ai/tracing/__init__.py +22 -10
  166. synth_ai/tracing_v1/__init__.py +22 -20
  167. synth_ai/tracing_v3/__init__.py +7 -7
  168. synth_ai/tracing_v3/abstractions.py +56 -52
  169. synth_ai/tracing_v3/config.py +4 -2
  170. synth_ai/tracing_v3/db_config.py +6 -8
  171. synth_ai/tracing_v3/decorators.py +29 -30
  172. synth_ai/tracing_v3/examples/basic_usage.py +12 -12
  173. synth_ai/tracing_v3/hooks.py +21 -21
  174. synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
  175. synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
  176. synth_ai/tracing_v3/migration_helper.py +3 -5
  177. synth_ai/tracing_v3/replica_sync.py +30 -32
  178. synth_ai/tracing_v3/session_tracer.py +35 -29
  179. synth_ai/tracing_v3/storage/__init__.py +1 -1
  180. synth_ai/tracing_v3/storage/base.py +8 -7
  181. synth_ai/tracing_v3/storage/config.py +4 -4
  182. synth_ai/tracing_v3/storage/factory.py +4 -4
  183. synth_ai/tracing_v3/storage/utils.py +9 -9
  184. synth_ai/tracing_v3/turso/__init__.py +3 -3
  185. synth_ai/tracing_v3/turso/daemon.py +9 -9
  186. synth_ai/tracing_v3/turso/manager.py +60 -48
  187. synth_ai/tracing_v3/turso/models.py +24 -19
  188. synth_ai/tracing_v3/utils.py +5 -5
  189. synth_ai/tui/__main__.py +1 -1
  190. synth_ai/tui/cli/query_experiments.py +2 -3
  191. synth_ai/tui/cli/query_experiments_v3.py +2 -3
  192. synth_ai/tui/dashboard.py +97 -86
  193. synth_ai/v0/tracing/abstractions.py +28 -28
  194. synth_ai/v0/tracing/base_client.py +9 -9
  195. synth_ai/v0/tracing/client_manager.py +7 -7
  196. synth_ai/v0/tracing/config.py +7 -7
  197. synth_ai/v0/tracing/context.py +6 -6
  198. synth_ai/v0/tracing/decorators.py +6 -5
  199. synth_ai/v0/tracing/events/manage.py +1 -1
  200. synth_ai/v0/tracing/events/store.py +5 -4
  201. synth_ai/v0/tracing/immediate_client.py +4 -5
  202. synth_ai/v0/tracing/local.py +3 -3
  203. synth_ai/v0/tracing/log_client_base.py +4 -5
  204. synth_ai/v0/tracing/retry_queue.py +5 -6
  205. synth_ai/v0/tracing/trackers.py +25 -25
  206. synth_ai/v0/tracing/upload.py +6 -0
  207. synth_ai/v0/tracing_v1/__init__.py +1 -1
  208. synth_ai/v0/tracing_v1/abstractions.py +28 -28
  209. synth_ai/v0/tracing_v1/base_client.py +9 -9
  210. synth_ai/v0/tracing_v1/client_manager.py +7 -7
  211. synth_ai/v0/tracing_v1/config.py +7 -7
  212. synth_ai/v0/tracing_v1/context.py +6 -6
  213. synth_ai/v0/tracing_v1/decorators.py +7 -6
  214. synth_ai/v0/tracing_v1/events/manage.py +1 -1
  215. synth_ai/v0/tracing_v1/events/store.py +5 -4
  216. synth_ai/v0/tracing_v1/immediate_client.py +4 -5
  217. synth_ai/v0/tracing_v1/local.py +3 -3
  218. synth_ai/v0/tracing_v1/log_client_base.py +4 -5
  219. synth_ai/v0/tracing_v1/retry_queue.py +5 -6
  220. synth_ai/v0/tracing_v1/trackers.py +25 -25
  221. synth_ai/v0/tracing_v1/upload.py +25 -24
  222. synth_ai/zyk/__init__.py +1 -0
  223. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +1 -11
  224. synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
  225. synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
  226. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
  227. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
  228. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
  229. {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/top_level.txt +0 -0
@@ -4,10 +4,11 @@ Provides async and sync interfaces matching OpenAI's API.
4
4
  """
5
5
 
6
6
  import asyncio
7
- import httpx
8
7
  import json
9
8
  import logging
10
- from typing import List, Dict, Any, Optional, Union
9
+ from typing import Any
10
+
11
+ import httpx
11
12
 
12
13
  from ..config import SynthConfig
13
14
 
@@ -17,7 +18,7 @@ logger = logging.getLogger(__name__)
17
18
  class AsyncSynthClient:
18
19
  """Async client with OpenAI-compatible interface."""
19
20
 
20
- def __init__(self, config: Optional[SynthConfig] = None):
21
+ def __init__(self, config: SynthConfig | None = None):
21
22
  """Initialize with config from environment if not provided."""
22
23
  self.config = config or SynthConfig.from_env()
23
24
  self._client = None
@@ -50,15 +51,15 @@ class AsyncSynthClient:
50
51
  async def responses_create(
51
52
  self,
52
53
  model: str,
53
- messages: List[Dict[str, Any]],
54
- previous_response_id: Optional[str] = None,
55
- tools: Optional[List[Dict[str, Any]]] = None,
56
- tool_choice: Optional[Union[str, Dict[str, Any]]] = "auto",
54
+ messages: list[dict[str, Any]],
55
+ previous_response_id: str | None = None,
56
+ tools: list[dict[str, Any]] | None = None,
57
+ tool_choice: str | dict[str, Any] | None = "auto",
57
58
  **kwargs,
58
- ) -> Dict[str, Any]:
59
+ ) -> dict[str, Any]:
59
60
  """
60
61
  Create response using Synth Responses API.
61
-
62
+
62
63
  Args:
63
64
  model: Model identifier
64
65
  messages: List of message dicts with 'role' and 'content'
@@ -66,71 +67,71 @@ class AsyncSynthClient:
66
67
  tools: List of available tools
67
68
  tool_choice: How to choose tools
68
69
  **kwargs: Additional parameters
69
-
70
+
70
71
  Returns:
71
72
  Responses API-compatible response dict
72
73
  """
73
74
  await self._ensure_client()
74
-
75
+
75
76
  # Build payload for Responses API
76
77
  payload = {
77
78
  "model": model,
78
79
  "messages": messages,
79
80
  }
80
-
81
+
81
82
  # Add optional parameters
82
83
  if previous_response_id is not None:
83
84
  payload["previous_response_id"] = previous_response_id
84
85
  if tools is not None:
85
86
  payload["tools"] = tools
86
87
  payload["tool_choice"] = tool_choice
87
-
88
+
88
89
  # Add any additional kwargs
89
90
  payload.update(kwargs)
90
-
91
+
91
92
  # Retry logic
92
93
  for attempt in range(self.config.max_retries):
93
94
  try:
94
95
  url = f"{self.config.get_base_url_without_v1()}/v1/responses"
95
96
  response = await self._client.post(url, json=payload)
96
-
97
+
97
98
  if response.status_code == 200:
98
99
  return response.json()
99
-
100
+
100
101
  # Handle rate limits with exponential backoff
101
102
  if response.status_code == 429:
102
103
  wait_time = 2**attempt
103
104
  await asyncio.sleep(wait_time)
104
105
  continue
105
-
106
+
106
107
  # Other errors
107
108
  response.raise_for_status()
108
-
109
+
109
110
  except Exception as e:
110
111
  if attempt == self.config.max_retries - 1:
111
112
  logger.error(f"Failed after {self.config.max_retries} attempts: {e}")
112
113
  raise
113
114
  await asyncio.sleep(2**attempt)
114
-
115
+
115
116
  raise Exception(f"Failed to create response after {self.config.max_retries} attempts")
116
117
 
117
118
  async def chat_completions_create(
118
119
  self,
119
120
  model: str,
120
- messages: List[Dict[str, Any]],
121
+ messages: list[dict[str, Any]],
121
122
  temperature: float = 0.7,
122
- max_tokens: Optional[int] = None,
123
+ max_tokens: int | None = None,
123
124
  top_p: float = 1.0,
124
125
  frequency_penalty: float = 0.0,
125
126
  presence_penalty: float = 0.0,
126
- stop: Optional[Union[str, List[str]]] = None,
127
+ stop: str | list[str] | None = None,
127
128
  stream: bool = False,
128
- tools: Optional[List[Dict[str, Any]]] = None,
129
- tool_choice: Optional[Union[str, Dict[str, Any]]] = "auto",
130
- response_format: Optional[Dict[str, Any]] = None,
131
- seed: Optional[int] = None,
129
+ tools: list[dict[str, Any]] | None = None,
130
+ tool_choice: str | dict[str, Any] | None = "auto",
131
+ response_format: dict[str, Any] | None = None,
132
+ seed: int | None = None,
132
133
  **kwargs,
133
- ) -> Dict[str, Any]:
134
+ ) -> dict[str, Any]:
134
135
  """
135
136
  Create chat completion with OpenAI-compatible API.
136
137
 
@@ -188,28 +189,32 @@ class AsyncSynthClient:
188
189
  url = f"{self.config.get_base_url_without_v1()}/v1/chat/completions"
189
190
  print(f"🔍 SYNTH DEBUG: Making request to URL: {url}")
190
191
  print(f"🔍 SYNTH DEBUG: Payload keys: {list(payload.keys())}")
191
- if 'tools' in payload:
192
+ if "tools" in payload:
192
193
  print(f"🔍 SYNTH DEBUG: Tools in payload: {len(payload['tools'])} tools")
193
- print(f"🔍 SYNTH DEBUG: First tool: {json.dumps(payload['tools'][0], indent=2)}")
194
-
194
+ print(
195
+ f"🔍 SYNTH DEBUG: First tool: {json.dumps(payload['tools'][0], indent=2)}"
196
+ )
197
+
195
198
  response = await self._client.post(url, json=payload)
196
-
199
+
197
200
  print(f"🔍 SYNTH DEBUG: Response status: {response.status_code}")
198
-
201
+
199
202
  if response.status_code == 200:
200
203
  result = response.json()
201
204
  print(f"🔍 SYNTH DEBUG: Response keys: {list(result.keys())}")
202
- if 'choices' in result and result['choices']:
203
- choice = result['choices'][0]
205
+ if "choices" in result and result["choices"]:
206
+ choice = result["choices"][0]
204
207
  print(f"🔍 SYNTH DEBUG: Choice keys: {list(choice.keys())}")
205
- if 'message' in choice:
206
- message = choice['message']
208
+ if "message" in choice:
209
+ message = choice["message"]
207
210
  print(f"🔍 SYNTH DEBUG: Message keys: {list(message.keys())}")
208
- if 'tool_calls' in message:
211
+ if "tool_calls" in message:
209
212
  print(f"🔍 SYNTH DEBUG: Tool calls: {message['tool_calls']}")
210
213
  else:
211
- print(f"🔍 SYNTH DEBUG: No tool_calls in message")
212
- print(f"🔍 SYNTH DEBUG: Message content: {message.get('content', 'N/A')[:200]}...")
214
+ print("🔍 SYNTH DEBUG: No tool_calls in message")
215
+ print(
216
+ f"🔍 SYNTH DEBUG: Message content: {message.get('content', 'N/A')[:200]}..."
217
+ )
213
218
  return result
214
219
 
215
220
  # Handle rate limits with exponential backoff
@@ -248,7 +253,7 @@ class AsyncSynthClient:
248
253
  class SyncSynthClient:
249
254
  """Sync client with OpenAI-compatible interface."""
250
255
 
251
- def __init__(self, config: Optional[SynthConfig] = None):
256
+ def __init__(self, config: SynthConfig | None = None):
252
257
  """Initialize with config from environment if not provided."""
253
258
  self.config = config or SynthConfig.from_env()
254
259
  self._client = None
@@ -281,69 +286,70 @@ class SyncSynthClient:
281
286
  def responses_create(
282
287
  self,
283
288
  model: str,
284
- messages: List[Dict[str, Any]],
285
- previous_response_id: Optional[str] = None,
286
- tools: Optional[List[Dict[str, Any]]] = None,
287
- tool_choice: Optional[Union[str, Dict[str, Any]]] = "auto",
289
+ messages: list[dict[str, Any]],
290
+ previous_response_id: str | None = None,
291
+ tools: list[dict[str, Any]] | None = None,
292
+ tool_choice: str | dict[str, Any] | None = "auto",
288
293
  **kwargs,
289
- ) -> Dict[str, Any]:
294
+ ) -> dict[str, Any]:
290
295
  """
291
296
  Create response using Synth Responses API (sync version).
292
-
297
+
293
298
  See AsyncSynthClient.responses_create for full parameter documentation.
294
299
  """
295
300
  self._ensure_client()
296
-
301
+
297
302
  # Build payload for Responses API
298
303
  payload = {
299
304
  "model": model,
300
305
  "messages": messages,
301
306
  }
302
-
307
+
303
308
  # Add optional parameters
304
309
  if previous_response_id is not None:
305
310
  payload["previous_response_id"] = previous_response_id
306
311
  if tools is not None:
307
312
  payload["tools"] = tools
308
313
  payload["tool_choice"] = tool_choice
309
-
314
+
310
315
  # Add any additional kwargs
311
316
  payload.update(kwargs)
312
-
317
+
313
318
  # Retry logic
314
319
  for attempt in range(self.config.max_retries):
315
320
  try:
316
321
  response = self._client.post(
317
322
  f"{self.config.get_base_url_without_v1()}/v1/responses", json=payload
318
323
  )
319
-
324
+
320
325
  if response.status_code == 200:
321
326
  return response.json()
322
-
327
+
323
328
  # Handle rate limits
324
329
  if response.status_code == 429:
325
330
  wait_time = 2**attempt
326
331
  logger.warning(f"Rate limited, waiting {wait_time}s...")
327
332
  import time
333
+
328
334
  time.sleep(wait_time)
329
335
  continue
330
-
336
+
331
337
  # Other errors
332
338
  error_msg = f"API error {response.status_code}: {response.text}"
333
339
  logger.error(error_msg)
334
340
  raise Exception(error_msg)
335
-
341
+
336
342
  except httpx.TimeoutException:
337
343
  if attempt < self.config.max_retries - 1:
338
344
  logger.warning(f"Timeout on attempt {attempt + 1}, retrying...")
339
345
  continue
340
346
  raise
341
-
347
+
342
348
  raise Exception(f"Failed after {self.config.max_retries} attempts")
343
349
 
344
350
  def chat_completions_create(
345
- self, model: str, messages: List[Dict[str, Any]], **kwargs
346
- ) -> Dict[str, Any]:
351
+ self, model: str, messages: list[dict[str, Any]], **kwargs
352
+ ) -> dict[str, Any]:
347
353
  """
348
354
  Create chat completion with OpenAI-compatible API (sync version).
349
355
 
@@ -393,7 +399,7 @@ class SyncSynthClient:
393
399
 
394
400
 
395
401
  # Factory functions for easy instantiation
396
- def create_async_client(config: Optional[SynthConfig] = None) -> AsyncSynthClient:
402
+ def create_async_client(config: SynthConfig | None = None) -> AsyncSynthClient:
397
403
  """
398
404
  Create async Synth client.
399
405
 
@@ -406,7 +412,7 @@ def create_async_client(config: Optional[SynthConfig] = None) -> AsyncSynthClien
406
412
  return AsyncSynthClient(config)
407
413
 
408
414
 
409
- def create_sync_client(config: Optional[SynthConfig] = None) -> SyncSynthClient:
415
+ def create_sync_client(config: SynthConfig | None = None) -> SyncSynthClient:
410
416
  """
411
417
  Create sync Synth client.
412
418
 
@@ -421,8 +427,8 @@ def create_sync_client(config: Optional[SynthConfig] = None) -> SyncSynthClient:
421
427
 
422
428
  # Convenience functions for one-off requests
423
429
  async def create_chat_completion_async(
424
- model: str, messages: List[Dict[str, Any]], config: Optional[SynthConfig] = None, **kwargs
425
- ) -> Dict[str, Any]:
430
+ model: str, messages: list[dict[str, Any]], config: SynthConfig | None = None, **kwargs
431
+ ) -> dict[str, Any]:
426
432
  """
427
433
  Create a chat completion with automatic client management.
428
434
 
@@ -440,8 +446,8 @@ async def create_chat_completion_async(
440
446
 
441
447
 
442
448
  def create_chat_completion_sync(
443
- model: str, messages: List[Dict[str, Any]], config: Optional[SynthConfig] = None, **kwargs
444
- ) -> Dict[str, Any]:
449
+ model: str, messages: list[dict[str, Any]], config: SynthConfig | None = None, **kwargs
450
+ ) -> dict[str, Any]:
445
451
  """
446
452
  Create a chat completion with automatic client management (sync version).
447
453
 
synth_ai/lm/warmup.py CHANGED
@@ -3,13 +3,14 @@ Model warmup utilities for Synth backend.
3
3
  Handles model preloading and warmup polling.
4
4
  """
5
5
 
6
- import httpx
7
6
  import asyncio
8
7
  import logging
9
8
  import sys
10
9
  import time
11
- from typing import Optional, Dict, Any
12
10
  from datetime import datetime, timedelta
11
+
12
+ import httpx
13
+
13
14
  from .config import SynthConfig
14
15
 
15
16
  logger = logging.getLogger(__name__)
@@ -19,7 +20,7 @@ class WarmupStatus:
19
20
  """Track warmup status for models with TTL."""
20
21
 
21
22
  def __init__(self):
22
- self._warmed_models: Dict[str, datetime] = {}
23
+ self._warmed_models: dict[str, datetime] = {}
23
24
  self._ttl = timedelta(minutes=10) # Consider models warm for 10 minutes
24
25
 
25
26
  def is_warm(self, model_name: str) -> bool:
@@ -47,11 +48,11 @@ _warmup_status = WarmupStatus()
47
48
 
48
49
  async def warmup_synth_model(
49
50
  model_name: str,
50
- config: Optional[SynthConfig] = None,
51
- max_attempts: Optional[int] = None,
51
+ config: SynthConfig | None = None,
52
+ max_attempts: int | None = None,
52
53
  force: bool = False,
53
54
  verbose: bool = True,
54
- gpu_preference: Optional[str] = None,
55
+ gpu_preference: str | None = None,
55
56
  ) -> bool:
56
57
  """
57
58
  Warm up a model on the Synth backend using fire-and-forget approach.
@@ -161,7 +162,7 @@ async def warmup_synth_model(
161
162
  )
162
163
  sys.stdout.flush()
163
164
  await asyncio.sleep(1.0)
164
- except Exception as e:
165
+ except Exception:
165
166
  # Continue polling; update spinner line with error label
166
167
  elapsed = int(time.time() - start_time)
167
168
  wheel = spinner[spin_idx % len(spinner)]
@@ -1,18 +1,30 @@
1
- import sys as _sys
2
1
  import importlib as _importlib
2
+ import sys as _sys
3
3
 
4
- _pkg = _importlib.import_module('synth_ai.v0.tracing')
4
+ _pkg = _importlib.import_module("synth_ai.v0.tracing")
5
5
  _sys.modules[__name__] = _pkg
6
6
 
7
7
  _SUBMODULES = [
8
- 'abstractions', 'base_client', 'client_manager', 'config', 'context',
9
- 'decorators', 'immediate_client', 'local', 'log_client_base', 'retry_queue',
10
- 'trackers', 'upload', 'utils'
8
+ "abstractions",
9
+ "base_client",
10
+ "client_manager",
11
+ "config",
12
+ "context",
13
+ "decorators",
14
+ "immediate_client",
15
+ "local",
16
+ "log_client_base",
17
+ "retry_queue",
18
+ "trackers",
19
+ "upload",
20
+ "utils",
11
21
  ]
12
22
  for _m in _SUBMODULES:
13
- _sys.modules[f'{__name__}.{_m}'] = _importlib.import_module(f'synth_ai.v0.tracing.{_m}')
23
+ _sys.modules[f"{__name__}.{_m}"] = _importlib.import_module(f"synth_ai.v0.tracing.{_m}")
14
24
 
15
- _events_pkg = _importlib.import_module('synth_ai.v0.tracing.events')
16
- _sys.modules[f'{__name__}.events'] = _events_pkg
17
- for _m in ['manage', 'scope', 'store']:
18
- _sys.modules[f'{__name__}.events.{_m}'] = _importlib.import_module(f'synth_ai.v0.tracing.events.{_m}')
25
+ _events_pkg = _importlib.import_module("synth_ai.v0.tracing.events")
26
+ _sys.modules[f"{__name__}.events"] = _events_pkg
27
+ for _m in ["manage", "scope", "store"]:
28
+ _sys.modules[f"{__name__}.events.{_m}"] = _importlib.import_module(
29
+ f"synth_ai.v0.tracing.events.{_m}"
30
+ )
@@ -1,31 +1,33 @@
1
- import sys as _sys
2
1
  import importlib as _importlib
2
+ import sys as _sys
3
3
 
4
4
  # Forward top-level package
5
- _pkg = _importlib.import_module('synth_ai.v0.tracing_v1')
5
+ _pkg = _importlib.import_module("synth_ai.v0.tracing_v1")
6
6
  _sys.modules[__name__] = _pkg
7
7
 
8
8
  # Explicitly forward submodules so `synth_ai.tracing_v1.X` works
9
9
  _SUBMODULES = [
10
- 'abstractions',
11
- 'base_client',
12
- 'client_manager',
13
- 'config',
14
- 'context',
15
- 'decorators',
16
- 'immediate_client',
17
- 'local',
18
- 'log_client_base',
19
- 'retry_queue',
20
- 'trackers',
21
- 'upload',
22
- 'utils',
10
+ "abstractions",
11
+ "base_client",
12
+ "client_manager",
13
+ "config",
14
+ "context",
15
+ "decorators",
16
+ "immediate_client",
17
+ "local",
18
+ "log_client_base",
19
+ "retry_queue",
20
+ "trackers",
21
+ "upload",
22
+ "utils",
23
23
  ]
24
24
  for _m in _SUBMODULES:
25
- _sys.modules[f'{__name__}.{_m}'] = _importlib.import_module(f'synth_ai.v0.tracing_v1.{_m}')
25
+ _sys.modules[f"{__name__}.{_m}"] = _importlib.import_module(f"synth_ai.v0.tracing_v1.{_m}")
26
26
 
27
27
  # Forward events package and its submodules
28
- _events_pkg = _importlib.import_module('synth_ai.v0.tracing_v1.events')
29
- _sys.modules[f'{__name__}.events'] = _events_pkg
30
- for _m in ['manage', 'scope', 'store']:
31
- _sys.modules[f'{__name__}.events.{_m}'] = _importlib.import_module(f'synth_ai.v0.tracing_v1.events.{_m}')
28
+ _events_pkg = _importlib.import_module("synth_ai.v0.tracing_v1.events")
29
+ _sys.modules[f"{__name__}.events"] = _events_pkg
30
+ for _m in ["manage", "scope", "store"]:
31
+ _sys.modules[f"{__name__}.events.{_m}"] = _importlib.import_module(
32
+ f"synth_ai.v0.tracing_v1.events.{_m}"
33
+ )
@@ -1,7 +1,7 @@
1
1
  """Tracing v3 - Turso/sqld based tracing implementation.
2
2
 
3
3
  This module provides a modern, async-first tracing system for capturing and storing
4
- detailed execution traces from AI systems. It's designed to handle high-throughput
4
+ detailed execution traces from AI systems. It's designed to handle high-throughput
5
5
  scenarios with proper async/await patterns throughout.
6
6
 
7
7
  Architecture Overview:
@@ -49,10 +49,10 @@ Key Features:
49
49
  Usage Example:
50
50
  -------------
51
51
  from synth_ai.tracing_v3 import SessionTracer
52
-
52
+
53
53
  tracer = SessionTracer()
54
54
  await tracer.initialize()
55
-
55
+
56
56
  async with tracer.session() as session_id:
57
57
  async with tracer.timestep("step1", turn_number=1):
58
58
  # Record events during execution
@@ -70,17 +70,17 @@ The system uses environment variables for configuration:
70
70
  See `config.py` for full configuration options.
71
71
  """
72
72
 
73
- from .session_tracer import SessionTracer
74
73
  from .abstractions import (
75
- SessionTrace,
76
- SessionTimeStep,
77
74
  BaseEvent,
78
- RuntimeEvent,
79
75
  EnvironmentEvent,
76
+ RuntimeEvent,
80
77
  SessionEventMarkovBlanketMessage,
78
+ SessionTimeStep,
79
+ SessionTrace,
81
80
  TimeRecord,
82
81
  )
83
82
  from .config import TursoConfig
83
+ from .session_tracer import SessionTracer
84
84
 
85
85
  __all__ = [
86
86
  "SessionTracer",