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
@@ -1,24 +1,28 @@
1
- from typing import Any, Dict, List, Optional, Union
2
1
  import asyncio
2
+ import os
3
3
  import time
4
+ from typing import Any
4
5
 
6
+ import backoff
5
7
  import groq
6
8
  import openai
7
- import os
8
9
  import pydantic_core
9
10
  from pydantic import BaseModel
10
11
 
11
12
  from synth_ai.lm.caching.initialize import (
12
13
  get_cache_handler,
13
14
  )
15
+ from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
16
+ from synth_ai.lm.injection import apply_injection
17
+ from synth_ai.lm.overrides import (
18
+ apply_param_overrides,
19
+ apply_tool_overrides,
20
+ use_overrides_for_messages,
21
+ )
14
22
  from synth_ai.lm.tools.base import BaseTool
15
23
  from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
16
- from synth_ai.lm.injection import apply_injection
17
- from synth_ai.lm.overrides import use_overrides_for_messages, apply_param_overrides, apply_tool_overrides
18
- from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
19
- from synth_ai.lm.vendors.retries import MAX_BACKOFF
20
24
  from synth_ai.lm.vendors.openai_standard_responses import OpenAIResponsesAPIMixin
21
- import backoff
25
+ from synth_ai.lm.vendors.retries import MAX_BACKOFF
22
26
 
23
27
  DEFAULT_EXCEPTIONS_TO_RETRY = (
24
28
  pydantic_core._pydantic_core.ValidationError,
@@ -30,14 +34,14 @@ DEFAULT_EXCEPTIONS_TO_RETRY = (
30
34
  )
31
35
 
32
36
 
33
- def special_orion_transform(model: str, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
37
+ def special_orion_transform(model: str, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
34
38
  """
35
39
  Transform messages for O1 series models which don't support system messages.
36
-
40
+
37
41
  Args:
38
42
  model: Model name to check
39
43
  messages: Original messages list
40
-
44
+
41
45
  Returns:
42
46
  Transformed messages list with system content merged into user message
43
47
  """
@@ -59,18 +63,19 @@ def _silent_backoff_handler(_details):
59
63
  class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
60
64
  """
61
65
  Standard OpenAI-compatible vendor implementation.
62
-
66
+
63
67
  This class provides a standard implementation for OpenAI-compatible APIs,
64
68
  including proper retry logic, caching, and support for various model features.
65
-
69
+
66
70
  Attributes:
67
71
  used_for_structured_outputs: Whether this client supports structured outputs
68
72
  exceptions_to_retry: List of exceptions that trigger automatic retries
69
73
  sync_client: Synchronous API client
70
74
  async_client: Asynchronous API client
71
75
  """
76
+
72
77
  used_for_structured_outputs: bool = True
73
- exceptions_to_retry: List = DEFAULT_EXCEPTIONS_TO_RETRY
78
+ exceptions_to_retry: list = DEFAULT_EXCEPTIONS_TO_RETRY
74
79
  sync_client: Any
75
80
  async_client: Any
76
81
 
@@ -78,19 +83,20 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
78
83
  self,
79
84
  sync_client: Any,
80
85
  async_client: Any,
81
- exceptions_to_retry: List[Exception] = DEFAULT_EXCEPTIONS_TO_RETRY,
86
+ exceptions_to_retry: list[Exception] = DEFAULT_EXCEPTIONS_TO_RETRY,
82
87
  used_for_structured_outputs: bool = False,
83
88
  ):
84
89
  self.sync_client = sync_client
85
90
  self.async_client = async_client
86
91
  self.used_for_structured_outputs = used_for_structured_outputs
87
92
  self.exceptions_to_retry = exceptions_to_retry
88
-
93
+
89
94
  # Initialize Harmony support for OSS models
90
95
  self.harmony_available = False
91
96
  self.harmony_enc = None
92
97
  try:
93
- from openai_harmony import load_harmony_encoding, HarmonyEncodingName
98
+ from openai_harmony import HarmonyEncodingName, load_harmony_encoding
99
+
94
100
  self.harmony_available = True
95
101
  self.harmony_enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
96
102
  except ImportError:
@@ -106,24 +112,24 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
106
112
  async def _hit_api_async(
107
113
  self,
108
114
  model: str,
109
- messages: List[Dict[str, Any]],
110
- lm_config: Dict[str, Any],
115
+ messages: list[dict[str, Any]],
116
+ lm_config: dict[str, Any],
111
117
  use_ephemeral_cache_only: bool = False,
112
118
  reasoning_effort: str = "high",
113
- tools: Optional[List[BaseTool]] = None,
119
+ tools: list[BaseTool] | None = None,
114
120
  ) -> BaseLMResponse:
115
- assert lm_config.get("response_model", None) is None, (
121
+ assert lm_config.get("response_model") is None, (
116
122
  "response_model is not supported for standard calls"
117
123
  )
118
-
119
- DEBUG = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
120
- if DEBUG:
121
- print(f"🔍 OPENAI DEBUG: _hit_api_async called with:")
124
+
125
+ debug = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
126
+ if debug:
127
+ print("🔍 OPENAI DEBUG: _hit_api_async called with:")
122
128
  print(f" Model: {model}")
123
- print(f" Messages: {len(messages)} messages")
129
+ print(f" Messages: {len(messages)} messages")
124
130
  print(f" Tools: {len(tools) if tools else 0} tools")
125
131
  print(f" LM config: {lm_config}")
126
-
132
+
127
133
  messages = special_orion_transform(model, messages)
128
134
  # Apply context-scoped overrides and prompt injection just before building API params
129
135
  with use_overrides_for_messages(messages):
@@ -133,15 +139,14 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
133
139
  cache_result = used_cache_handler.hit_managed_cache(
134
140
  model, messages, lm_config=lm_config, tools=tools
135
141
  )
136
- if cache_result:
137
- if DEBUG:
138
- print(f"🔍 OPENAI DEBUG: Cache hit! Returning cached result")
139
- print(f" Cache result type: {type(cache_result)}")
140
- print(f"🔍 OPENAI DEBUG: DISABLING CACHE FOR DEBUGGING - forcing API call")
141
- # return cache_result # Commented out to force API call
142
-
143
- if DEBUG:
144
- print(f"🔍 OPENAI DEBUG: Cache miss, making actual API call")
142
+ if cache_result and debug:
143
+ print("🔍 OPENAI DEBUG: Cache hit! Returning cached result")
144
+ print(f" Cache result type: {type(cache_result)}")
145
+ print("🔍 OPENAI DEBUG: DISABLING CACHE FOR DEBUGGING - forcing API call")
146
+ # return cache_result # Commented out intentionally when debug is on
147
+
148
+ if debug:
149
+ print("🔍 OPENAI DEBUG: Cache miss, making actual API call")
145
150
 
146
151
  # Common API call params
147
152
  api_params = {
@@ -211,7 +216,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
211
216
  # Forward arbitrary extra_body from lm_config if provided (merge)
212
217
  if lm_config.get("extra_body") is not None:
213
218
  # Shallow-merge top-level keys; nested keys (like chat_template_kwargs) should be provided whole
214
- api_params["extra_body"] = {**api_params.get("extra_body", {}), **(lm_config.get("extra_body") or {})}
219
+ api_params["extra_body"] = {
220
+ **api_params.get("extra_body", {}),
221
+ **(lm_config.get("extra_body") or {}),
222
+ }
215
223
  # Forward Qwen3 chat template kwargs via extra_body when requested
216
224
  if lm_config.get("enable_thinking") is not None:
217
225
  api_params["extra_body"] = api_params.get("extra_body", {})
@@ -233,9 +241,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
233
241
  except Exception:
234
242
  base_url_str = ""
235
243
 
236
- is_external_provider = (
237
- "openai.com" in base_url_str or "api.groq.com" in base_url_str
238
- )
244
+ is_external_provider = "openai.com" in base_url_str or "api.groq.com" in base_url_str
239
245
 
240
246
  if is_external_provider:
241
247
  # Remove extra_body entirely; this is Synth-specific plumbing
@@ -257,18 +263,18 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
257
263
  api_params.pop("temperature", None)
258
264
 
259
265
  # Call API with better auth error reporting
260
- #try:
261
- if DEBUG:
262
- print(f"🔍 OPENAI DEBUG: Making request with params:")
266
+ # try:
267
+ if debug:
268
+ print("🔍 OPENAI DEBUG: Making request with params:")
263
269
  print(f" Model: {api_params.get('model')}")
264
270
  print(f" Messages: {len(api_params.get('messages', []))} messages")
265
271
  print(f" Tools: {len(api_params.get('tools', []))} tools")
266
272
  print(f" Max tokens: {api_params.get('max_tokens', 'NOT SET')}")
267
273
  print(f" Temperature: {api_params.get('temperature', 'NOT SET')}")
268
- if 'tools' in api_params:
274
+ if "tools" in api_params:
269
275
  print(f" First tool: {api_params['tools'][0]}")
270
276
  print(f" FULL API PARAMS: {api_params}")
271
-
277
+
272
278
  # Quiet targeted retry for OpenAI 400 tool_use_failed during tool-calling
273
279
  try:
274
280
  max_attempts_for_tool_use = int(os.getenv("SYNTH_TOOL_USE_RETRIES", "5"))
@@ -294,7 +300,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
294
300
  err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
295
301
  code_val = err_obj.get("code")
296
302
  msg_val = err_obj.get("message")
297
- if code_val == "tool_use_failed" or (isinstance(msg_val, str) and "Failed to call a function" in msg_val):
303
+ if code_val == "tool_use_failed" or (
304
+ isinstance(msg_val, str) and "Failed to call a function" in msg_val
305
+ ):
298
306
  should_retry = True
299
307
  except Exception:
300
308
  pass
@@ -308,7 +316,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
308
316
  err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
309
317
  code_val = err_obj.get("code")
310
318
  msg_val = err_obj.get("message")
311
- if code_val == "tool_use_failed" or (isinstance(msg_val, str) and "Failed to call a function" in msg_val):
319
+ if code_val == "tool_use_failed" or (
320
+ isinstance(msg_val, str)
321
+ and "Failed to call a function" in msg_val
322
+ ):
312
323
  should_retry = True
313
324
  except Exception:
314
325
  pass
@@ -323,33 +334,37 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
323
334
  attempt_index += 1
324
335
  continue
325
336
  raise
326
-
327
- if DEBUG:
328
- print(f"🔍 OPENAI DEBUG: Response received:")
337
+
338
+ if debug:
339
+ print("🔍 OPENAI DEBUG: Response received:")
329
340
  print(f" Type: {type(output)}")
330
341
  print(f" Choices: {len(output.choices) if hasattr(output, 'choices') else 'N/A'}")
331
- if hasattr(output, 'choices') and output.choices:
342
+ if hasattr(output, "choices") and output.choices:
332
343
  choice = output.choices[0]
333
344
  print(f" Choice type: {type(choice)}")
334
- if hasattr(choice, 'message'):
345
+ if hasattr(choice, "message"):
335
346
  message = choice.message
336
347
  print(f" Message type: {type(message)}")
337
348
  print(f" Has tool_calls: {hasattr(message, 'tool_calls')}")
338
- if hasattr(message, 'tool_calls'):
349
+ if hasattr(message, "tool_calls"):
339
350
  print(f" Tool calls: {message.tool_calls}")
340
- print(f" Content: {message.content[:200] if hasattr(message, 'content') and message.content else 'None'}...")
351
+ print(
352
+ f" Content: {message.content[:200] if hasattr(message, 'content') and message.content else 'None'}..."
353
+ )
341
354
  # Show finish_reason and usage if available
342
355
  try:
343
356
  print(f" finish_reason: {getattr(choice, 'finish_reason', None)}")
344
- usage = getattr(output, 'usage', None)
357
+ usage = getattr(output, "usage", None)
345
358
  if usage:
346
- print(f" usage: prompt_tokens={getattr(usage, 'prompt_tokens', None)}, completion_tokens={getattr(usage, 'completion_tokens', None)}, total_tokens={getattr(usage, 'total_tokens', None)}")
359
+ print(
360
+ f" usage: prompt_tokens={getattr(usage, 'prompt_tokens', None)}, completion_tokens={getattr(usage, 'completion_tokens', None)}, total_tokens={getattr(usage, 'total_tokens', None)}"
361
+ )
347
362
  except Exception:
348
363
  pass
349
-
350
- if DEBUG:
351
- print(f"🔍 OPENAI DEBUG: FULL RAW RESPONSE:")
352
- if hasattr(output.choices[0].message, 'content') and output.choices[0].message.content:
364
+
365
+ if debug:
366
+ print("🔍 OPENAI DEBUG: FULL RAW RESPONSE:")
367
+ if hasattr(output.choices[0].message, "content") and output.choices[0].message.content:
353
368
  print(f" FULL CONTENT:\n{output.choices[0].message.content}")
354
369
  print(f" Raw choice: {choice}")
355
370
  print(f" Raw message: {message}")
@@ -390,12 +405,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
390
405
  # Attach basic usage if available
391
406
  usage_dict = None
392
407
  try:
393
- usage_obj = getattr(output, 'usage', None)
408
+ usage_obj = getattr(output, "usage", None)
394
409
  if usage_obj is not None:
395
410
  usage_dict = {
396
- "prompt_tokens": getattr(usage_obj, 'prompt_tokens', None),
397
- "completion_tokens": getattr(usage_obj, 'completion_tokens', None),
398
- "total_tokens": getattr(usage_obj, 'total_tokens', None),
411
+ "prompt_tokens": getattr(usage_obj, "prompt_tokens", None),
412
+ "completion_tokens": getattr(usage_obj, "completion_tokens", None),
413
+ "total_tokens": getattr(usage_obj, "total_tokens", None),
399
414
  }
400
415
  except Exception:
401
416
  usage_dict = None
@@ -422,13 +437,13 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
422
437
  def _hit_api_sync(
423
438
  self,
424
439
  model: str,
425
- messages: List[Dict[str, Any]],
426
- lm_config: Dict[str, Any],
440
+ messages: list[dict[str, Any]],
441
+ lm_config: dict[str, Any],
427
442
  use_ephemeral_cache_only: bool = False,
428
443
  reasoning_effort: str = "high",
429
- tools: Optional[List[BaseTool]] = None,
444
+ tools: list[BaseTool] | None = None,
430
445
  ) -> BaseLMResponse:
431
- assert lm_config.get("response_model", None) is None, (
446
+ assert lm_config.get("response_model") is None, (
432
447
  "response_model is not supported for standard calls"
433
448
  )
434
449
  messages = special_orion_transform(model, messages)
@@ -441,8 +456,8 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
441
456
  model, messages, lm_config=lm_config, tools=tools
442
457
  )
443
458
  # During pytest runs, bypass returning cache to allow tests to inspect outgoing params
444
- IN_PYTEST = os.getenv("PYTEST_CURRENT_TEST") is not None
445
- if cache_result and not IN_PYTEST:
459
+ in_pytest = os.getenv("PYTEST_CURRENT_TEST") is not None
460
+ if cache_result and not in_pytest:
446
461
  return cache_result
447
462
 
448
463
  # Common API call params
@@ -514,7 +529,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
514
529
  err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
515
530
  code_val = err_obj.get("code")
516
531
  msg_val = err_obj.get("message")
517
- if code_val == "tool_use_failed" or (isinstance(msg_val, str) and "Failed to call a function" in msg_val):
532
+ if code_val == "tool_use_failed" or (
533
+ isinstance(msg_val, str) and "Failed to call a function" in msg_val
534
+ ):
518
535
  should_retry = True
519
536
  except Exception:
520
537
  pass
@@ -527,7 +544,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
527
544
  err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
528
545
  code_val = err_obj.get("code")
529
546
  msg_val = err_obj.get("message")
530
- if code_val == "tool_use_failed" or (isinstance(msg_val, str) and "Failed to call a function" in msg_val):
547
+ if code_val == "tool_use_failed" or (
548
+ isinstance(msg_val, str)
549
+ and "Failed to call a function" in msg_val
550
+ ):
531
551
  should_retry = True
532
552
  except Exception:
533
553
  pass
@@ -542,13 +562,17 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
542
562
  continue
543
563
  raise
544
564
  message = output.choices[0].message
545
- DEBUG = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
546
- if DEBUG:
565
+ debug_sync = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
566
+ if debug_sync:
547
567
  try:
548
- print(f"🔍 OPENAI DEBUG (sync): finish_reason={getattr(output.choices[0], 'finish_reason', None)}")
549
- usage = getattr(output, 'usage', None)
568
+ print(
569
+ f"🔍 OPENAI DEBUG (sync): finish_reason={getattr(output.choices[0], 'finish_reason', None)}"
570
+ )
571
+ usage = getattr(output, "usage", None)
550
572
  if usage:
551
- print(f"🔍 OPENAI DEBUG (sync): usage prompt_tokens={getattr(usage, 'prompt_tokens', None)}, completion_tokens={getattr(usage, 'completion_tokens', None)}, total_tokens={getattr(usage, 'total_tokens', None)}")
573
+ print(
574
+ f"🔍 OPENAI DEBUG (sync): usage prompt_tokens={getattr(usage, 'prompt_tokens', None)}, completion_tokens={getattr(usage, 'completion_tokens', None)}, total_tokens={getattr(usage, 'total_tokens', None)}"
575
+ )
552
576
  except Exception:
553
577
  pass
554
578
 
@@ -570,12 +594,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
570
594
  # Attach basic usage if available
571
595
  usage_dict = None
572
596
  try:
573
- usage_obj = getattr(output, 'usage', None)
597
+ usage_obj = getattr(output, "usage", None)
574
598
  if usage_obj is not None:
575
599
  usage_dict = {
576
- "prompt_tokens": getattr(usage_obj, 'prompt_tokens', None),
577
- "completion_tokens": getattr(usage_obj, 'completion_tokens', None),
578
- "total_tokens": getattr(usage_obj, 'total_tokens', None),
600
+ "prompt_tokens": getattr(usage_obj, "prompt_tokens", None),
601
+ "completion_tokens": getattr(usage_obj, "completion_tokens", None),
602
+ "total_tokens": getattr(usage_obj, "total_tokens", None),
579
603
  }
580
604
  except Exception:
581
605
  usage_dict = None
@@ -595,12 +619,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
595
619
  async def _hit_api_async_structured_output(
596
620
  self,
597
621
  model: str,
598
- messages: List[Dict[str, Any]],
622
+ messages: list[dict[str, Any]],
599
623
  response_model: BaseModel,
600
624
  temperature: float,
601
625
  use_ephemeral_cache_only: bool = False,
602
626
  reasoning_effort: str = "high",
603
- tools: Optional[List[BaseTool]] = None,
627
+ tools: list[BaseTool] | None = None,
604
628
  ) -> BaseLMResponse:
605
629
  lm_config = {
606
630
  "temperature": temperature,
@@ -608,7 +632,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
608
632
  "reasoning_effort": reasoning_effort,
609
633
  }
610
634
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
611
- cache_result: Union[BaseLMResponse, None] = used_cache_handler.hit_managed_cache(
635
+ cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
612
636
  model, messages, lm_config=lm_config, tools=tools
613
637
  )
614
638
  if cache_result is not None:
@@ -654,12 +678,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
654
678
  def _hit_api_sync_structured_output(
655
679
  self,
656
680
  model: str,
657
- messages: List[Dict[str, Any]],
681
+ messages: list[dict[str, Any]],
658
682
  response_model: BaseModel,
659
683
  temperature: float,
660
684
  use_ephemeral_cache_only: bool = False,
661
685
  reasoning_effort: str = "high",
662
- tools: Optional[List[BaseTool]] = None,
686
+ tools: list[BaseTool] | None = None,
663
687
  ) -> BaseLMResponse:
664
688
  lm_config = {
665
689
  "temperature": temperature,
@@ -667,7 +691,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
667
691
  "reasoning_effort": reasoning_effort,
668
692
  }
669
693
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
670
- cache_result: Union[BaseLMResponse, None] = used_cache_handler.hit_managed_cache(
694
+ cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
671
695
  model, messages, lm_config=lm_config, tools=tools
672
696
  )
673
697
  if cache_result is not None:
@@ -701,7 +725,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
701
725
  base_url_str_sync = str(base_url_obj) if base_url_obj is not None else ""
702
726
  except Exception:
703
727
  base_url_str_sync = ""
704
- if ("openai.com" in base_url_str_sync or "api.groq.com" in base_url_str_sync) and model.startswith("gpt-5"):
728
+ if (
729
+ "openai.com" in base_url_str_sync or "api.groq.com" in base_url_str_sync
730
+ ) and model.startswith("gpt-5"):
705
731
  if "max_tokens" in api_params:
706
732
  api_params["max_completion_tokens"] = api_params.pop("max_tokens")
707
733
  if "temperature" in api_params: