synth-ai 0.2.4.dev5__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 +21 -17
  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 +29 -0
  104. synth_ai/environments/examples/wordle/engine.py +398 -0
  105. synth_ai/environments/examples/wordle/environment.py +159 -0
  106. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +75 -0
  107. synth_ai/environments/examples/wordle/taskset.py +230 -0
  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 +11 -2
  111. synth_ai/environments/service/core_routes.py +137 -105
  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 +168 -0
  124. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +213 -0
  125. synth_ai/learning/prompts/mipro.py +282 -1
  126. synth_ai/learning/prompts/random_search.py +246 -0
  127. synth_ai/learning/prompts/run_mipro_banking77.py +172 -0
  128. synth_ai/learning/prompts/run_random_search_banking77.py +324 -0
  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 +80 -0
  141. synth_ai/lm/overrides.py +206 -0
  142. synth_ai/lm/provider_support/__init__.py +1 -1
  143. synth_ai/lm/provider_support/anthropic.py +51 -24
  144. synth_ai/lm/provider_support/openai.py +51 -22
  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 +50 -25
  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 +144 -88
  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.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +2 -11
  224. synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
  225. synth_ai-0.2.4.dev5.dist-info/RECORD +0 -287
  226. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
  227. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
  228. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
  229. {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/top_level.txt +0 -0
@@ -1,22 +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.constants import SPECIAL_BASE_TEMPS
17
- from synth_ai.lm.vendors.retries import MAX_BACKOFF
18
24
  from synth_ai.lm.vendors.openai_standard_responses import OpenAIResponsesAPIMixin
19
- import backoff
25
+ from synth_ai.lm.vendors.retries import MAX_BACKOFF
20
26
 
21
27
  DEFAULT_EXCEPTIONS_TO_RETRY = (
22
28
  pydantic_core._pydantic_core.ValidationError,
@@ -28,14 +34,14 @@ DEFAULT_EXCEPTIONS_TO_RETRY = (
28
34
  )
29
35
 
30
36
 
31
- 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]]:
32
38
  """
33
39
  Transform messages for O1 series models which don't support system messages.
34
-
40
+
35
41
  Args:
36
42
  model: Model name to check
37
43
  messages: Original messages list
38
-
44
+
39
45
  Returns:
40
46
  Transformed messages list with system content merged into user message
41
47
  """
@@ -57,18 +63,19 @@ def _silent_backoff_handler(_details):
57
63
  class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
58
64
  """
59
65
  Standard OpenAI-compatible vendor implementation.
60
-
66
+
61
67
  This class provides a standard implementation for OpenAI-compatible APIs,
62
68
  including proper retry logic, caching, and support for various model features.
63
-
69
+
64
70
  Attributes:
65
71
  used_for_structured_outputs: Whether this client supports structured outputs
66
72
  exceptions_to_retry: List of exceptions that trigger automatic retries
67
73
  sync_client: Synchronous API client
68
74
  async_client: Asynchronous API client
69
75
  """
76
+
70
77
  used_for_structured_outputs: bool = True
71
- exceptions_to_retry: List = DEFAULT_EXCEPTIONS_TO_RETRY
78
+ exceptions_to_retry: list = DEFAULT_EXCEPTIONS_TO_RETRY
72
79
  sync_client: Any
73
80
  async_client: Any
74
81
 
@@ -76,19 +83,20 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
76
83
  self,
77
84
  sync_client: Any,
78
85
  async_client: Any,
79
- exceptions_to_retry: List[Exception] = DEFAULT_EXCEPTIONS_TO_RETRY,
86
+ exceptions_to_retry: list[Exception] = DEFAULT_EXCEPTIONS_TO_RETRY,
80
87
  used_for_structured_outputs: bool = False,
81
88
  ):
82
89
  self.sync_client = sync_client
83
90
  self.async_client = async_client
84
91
  self.used_for_structured_outputs = used_for_structured_outputs
85
92
  self.exceptions_to_retry = exceptions_to_retry
86
-
93
+
87
94
  # Initialize Harmony support for OSS models
88
95
  self.harmony_available = False
89
96
  self.harmony_enc = None
90
97
  try:
91
- from openai_harmony import load_harmony_encoding, HarmonyEncodingName
98
+ from openai_harmony import HarmonyEncodingName, load_harmony_encoding
99
+
92
100
  self.harmony_available = True
93
101
  self.harmony_enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
94
102
  except ImportError:
@@ -104,45 +112,49 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
104
112
  async def _hit_api_async(
105
113
  self,
106
114
  model: str,
107
- messages: List[Dict[str, Any]],
108
- lm_config: Dict[str, Any],
115
+ messages: list[dict[str, Any]],
116
+ lm_config: dict[str, Any],
109
117
  use_ephemeral_cache_only: bool = False,
110
118
  reasoning_effort: str = "high",
111
- tools: Optional[List[BaseTool]] = None,
119
+ tools: list[BaseTool] | None = None,
112
120
  ) -> BaseLMResponse:
113
- assert lm_config.get("response_model", None) is None, (
121
+ assert lm_config.get("response_model") is None, (
114
122
  "response_model is not supported for standard calls"
115
123
  )
116
-
117
- DEBUG = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
118
- if DEBUG:
119
- 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:")
120
128
  print(f" Model: {model}")
121
- print(f" Messages: {len(messages)} messages")
129
+ print(f" Messages: {len(messages)} messages")
122
130
  print(f" Tools: {len(tools) if tools else 0} tools")
123
131
  print(f" LM config: {lm_config}")
124
-
132
+
125
133
  messages = special_orion_transform(model, messages)
134
+ # Apply context-scoped overrides and prompt injection just before building API params
135
+ with use_overrides_for_messages(messages):
136
+ messages = apply_injection(messages)
126
137
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
127
138
  lm_config["reasoning_effort"] = reasoning_effort
128
139
  cache_result = used_cache_handler.hit_managed_cache(
129
140
  model, messages, lm_config=lm_config, tools=tools
130
141
  )
131
- if cache_result:
132
- if DEBUG:
133
- print(f"🔍 OPENAI DEBUG: Cache hit! Returning cached result")
134
- print(f" Cache result type: {type(cache_result)}")
135
- print(f"🔍 OPENAI DEBUG: DISABLING CACHE FOR DEBUGGING - forcing API call")
136
- # return cache_result # Commented out to force API call
137
-
138
- if DEBUG:
139
- 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")
140
150
 
141
151
  # Common API call params
142
152
  api_params = {
143
153
  "model": model,
144
154
  "messages": messages,
145
155
  }
156
+ with use_overrides_for_messages(messages):
157
+ api_params = apply_param_overrides(api_params)
146
158
 
147
159
  # Add tools if provided
148
160
  if tools and all(isinstance(tool, BaseTool) for tool in tools):
@@ -150,8 +162,11 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
150
162
  elif tools:
151
163
  api_params["tools"] = tools
152
164
 
153
- # Only add temperature for non o1/o3 models
154
- if not any(prefix in model for prefix in ["o1-", "o3-"]):
165
+ # Only add temperature for non o1/o3 models, and do not override if already set via overrides
166
+ if (
167
+ not any(prefix in model for prefix in ["o1-", "o3-"])
168
+ and "temperature" not in api_params
169
+ ):
155
170
  api_params["temperature"] = lm_config.get(
156
171
  "temperature", SPECIAL_BASE_TEMPS.get(model, 0)
157
172
  )
@@ -187,6 +202,11 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
187
202
  api_params["extra_headers"] = hdrs
188
203
  except Exception:
189
204
  pass
205
+ # Apply overrides (tools and params) from context after building baseline params
206
+ with use_overrides_for_messages(messages):
207
+ api_params = apply_tool_overrides(api_params)
208
+ api_params = apply_param_overrides(api_params)
209
+
190
210
  # Forward Qwen3 chat template kwargs via extra_body when requested
191
211
  if lm_config.get("enable_thinking") is not None:
192
212
  api_params["extra_body"] = api_params.get("extra_body", {})
@@ -196,7 +216,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
196
216
  # Forward arbitrary extra_body from lm_config if provided (merge)
197
217
  if lm_config.get("extra_body") is not None:
198
218
  # Shallow-merge top-level keys; nested keys (like chat_template_kwargs) should be provided whole
199
- 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
+ }
200
223
  # Forward Qwen3 chat template kwargs via extra_body when requested
201
224
  if lm_config.get("enable_thinking") is not None:
202
225
  api_params["extra_body"] = api_params.get("extra_body", {})
@@ -218,9 +241,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
218
241
  except Exception:
219
242
  base_url_str = ""
220
243
 
221
- is_external_provider = (
222
- "openai.com" in base_url_str or "api.groq.com" in base_url_str
223
- )
244
+ is_external_provider = "openai.com" in base_url_str or "api.groq.com" in base_url_str
224
245
 
225
246
  if is_external_provider:
226
247
  # Remove extra_body entirely; this is Synth-specific plumbing
@@ -242,18 +263,18 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
242
263
  api_params.pop("temperature", None)
243
264
 
244
265
  # Call API with better auth error reporting
245
- #try:
246
- if DEBUG:
247
- print(f"🔍 OPENAI DEBUG: Making request with params:")
266
+ # try:
267
+ if debug:
268
+ print("🔍 OPENAI DEBUG: Making request with params:")
248
269
  print(f" Model: {api_params.get('model')}")
249
270
  print(f" Messages: {len(api_params.get('messages', []))} messages")
250
271
  print(f" Tools: {len(api_params.get('tools', []))} tools")
251
272
  print(f" Max tokens: {api_params.get('max_tokens', 'NOT SET')}")
252
273
  print(f" Temperature: {api_params.get('temperature', 'NOT SET')}")
253
- if 'tools' in api_params:
274
+ if "tools" in api_params:
254
275
  print(f" First tool: {api_params['tools'][0]}")
255
276
  print(f" FULL API PARAMS: {api_params}")
256
-
277
+
257
278
  # Quiet targeted retry for OpenAI 400 tool_use_failed during tool-calling
258
279
  try:
259
280
  max_attempts_for_tool_use = int(os.getenv("SYNTH_TOOL_USE_RETRIES", "5"))
@@ -279,7 +300,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
279
300
  err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
280
301
  code_val = err_obj.get("code")
281
302
  msg_val = err_obj.get("message")
282
- 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
+ ):
283
306
  should_retry = True
284
307
  except Exception:
285
308
  pass
@@ -293,7 +316,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
293
316
  err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
294
317
  code_val = err_obj.get("code")
295
318
  msg_val = err_obj.get("message")
296
- 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
+ ):
297
323
  should_retry = True
298
324
  except Exception:
299
325
  pass
@@ -308,33 +334,37 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
308
334
  attempt_index += 1
309
335
  continue
310
336
  raise
311
-
312
- if DEBUG:
313
- print(f"🔍 OPENAI DEBUG: Response received:")
337
+
338
+ if debug:
339
+ print("🔍 OPENAI DEBUG: Response received:")
314
340
  print(f" Type: {type(output)}")
315
341
  print(f" Choices: {len(output.choices) if hasattr(output, 'choices') else 'N/A'}")
316
- if hasattr(output, 'choices') and output.choices:
342
+ if hasattr(output, "choices") and output.choices:
317
343
  choice = output.choices[0]
318
344
  print(f" Choice type: {type(choice)}")
319
- if hasattr(choice, 'message'):
345
+ if hasattr(choice, "message"):
320
346
  message = choice.message
321
347
  print(f" Message type: {type(message)}")
322
348
  print(f" Has tool_calls: {hasattr(message, 'tool_calls')}")
323
- if hasattr(message, 'tool_calls'):
349
+ if hasattr(message, "tool_calls"):
324
350
  print(f" Tool calls: {message.tool_calls}")
325
- 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
+ )
326
354
  # Show finish_reason and usage if available
327
355
  try:
328
356
  print(f" finish_reason: {getattr(choice, 'finish_reason', None)}")
329
- usage = getattr(output, 'usage', None)
357
+ usage = getattr(output, "usage", None)
330
358
  if usage:
331
- 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
+ )
332
362
  except Exception:
333
363
  pass
334
-
335
- if DEBUG:
336
- print(f"🔍 OPENAI DEBUG: FULL RAW RESPONSE:")
337
- 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:
338
368
  print(f" FULL CONTENT:\n{output.choices[0].message.content}")
339
369
  print(f" Raw choice: {choice}")
340
370
  print(f" Raw message: {message}")
@@ -375,12 +405,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
375
405
  # Attach basic usage if available
376
406
  usage_dict = None
377
407
  try:
378
- usage_obj = getattr(output, 'usage', None)
408
+ usage_obj = getattr(output, "usage", None)
379
409
  if usage_obj is not None:
380
410
  usage_dict = {
381
- "prompt_tokens": getattr(usage_obj, 'prompt_tokens', None),
382
- "completion_tokens": getattr(usage_obj, 'completion_tokens', None),
383
- "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),
384
414
  }
385
415
  except Exception:
386
416
  usage_dict = None
@@ -407,22 +437,27 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
407
437
  def _hit_api_sync(
408
438
  self,
409
439
  model: str,
410
- messages: List[Dict[str, Any]],
411
- lm_config: Dict[str, Any],
440
+ messages: list[dict[str, Any]],
441
+ lm_config: dict[str, Any],
412
442
  use_ephemeral_cache_only: bool = False,
413
443
  reasoning_effort: str = "high",
414
- tools: Optional[List[BaseTool]] = None,
444
+ tools: list[BaseTool] | None = None,
415
445
  ) -> BaseLMResponse:
416
- assert lm_config.get("response_model", None) is None, (
446
+ assert lm_config.get("response_model") is None, (
417
447
  "response_model is not supported for standard calls"
418
448
  )
419
449
  messages = special_orion_transform(model, messages)
450
+ with use_overrides_for_messages(messages):
451
+ # Apply context-scoped prompt injection just before building API params
452
+ messages = apply_injection(messages)
420
453
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only=use_ephemeral_cache_only)
421
454
  lm_config["reasoning_effort"] = reasoning_effort
422
455
  cache_result = used_cache_handler.hit_managed_cache(
423
456
  model, messages, lm_config=lm_config, tools=tools
424
457
  )
425
- if cache_result:
458
+ # During pytest runs, bypass returning cache to allow tests to inspect outgoing params
459
+ in_pytest = os.getenv("PYTEST_CURRENT_TEST") is not None
460
+ if cache_result and not in_pytest:
426
461
  return cache_result
427
462
 
428
463
  # Common API call params
@@ -430,6 +465,8 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
430
465
  "model": model,
431
466
  "messages": messages,
432
467
  }
468
+ with use_overrides_for_messages(messages):
469
+ api_params = apply_param_overrides(api_params)
433
470
 
434
471
  # Add tools if provided
435
472
  if tools and all(isinstance(tool, BaseTool) for tool in tools):
@@ -437,8 +474,16 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
437
474
  elif tools:
438
475
  api_params["tools"] = tools
439
476
 
440
- # Only add temperature for non o1/o3 models
441
- if not any(prefix in model for prefix in ["o1-", "o3-"]):
477
+ # Apply overrides (tools and params) using module-level imports
478
+ with use_overrides_for_messages(messages):
479
+ api_params = apply_tool_overrides(api_params)
480
+ api_params = apply_param_overrides(api_params)
481
+
482
+ # Only add temperature for non o1/o3 models, and do not override if already set via overrides
483
+ if (
484
+ not any(prefix in model for prefix in ["o1-", "o3-"])
485
+ and "temperature" not in api_params
486
+ ):
442
487
  api_params["temperature"] = lm_config.get(
443
488
  "temperature", SPECIAL_BASE_TEMPS.get(model, 0)
444
489
  )
@@ -484,7 +529,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
484
529
  err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
485
530
  code_val = err_obj.get("code")
486
531
  msg_val = err_obj.get("message")
487
- 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
+ ):
488
535
  should_retry = True
489
536
  except Exception:
490
537
  pass
@@ -497,7 +544,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
497
544
  err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
498
545
  code_val = err_obj.get("code")
499
546
  msg_val = err_obj.get("message")
500
- 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
+ ):
501
551
  should_retry = True
502
552
  except Exception:
503
553
  pass
@@ -512,13 +562,17 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
512
562
  continue
513
563
  raise
514
564
  message = output.choices[0].message
515
- DEBUG = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
516
- if DEBUG:
565
+ debug_sync = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
566
+ if debug_sync:
517
567
  try:
518
- print(f"🔍 OPENAI DEBUG (sync): finish_reason={getattr(output.choices[0], 'finish_reason', None)}")
519
- 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)
520
572
  if usage:
521
- 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
+ )
522
576
  except Exception:
523
577
  pass
524
578
 
@@ -540,12 +594,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
540
594
  # Attach basic usage if available
541
595
  usage_dict = None
542
596
  try:
543
- usage_obj = getattr(output, 'usage', None)
597
+ usage_obj = getattr(output, "usage", None)
544
598
  if usage_obj is not None:
545
599
  usage_dict = {
546
- "prompt_tokens": getattr(usage_obj, 'prompt_tokens', None),
547
- "completion_tokens": getattr(usage_obj, 'completion_tokens', None),
548
- "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),
549
603
  }
550
604
  except Exception:
551
605
  usage_dict = None
@@ -565,12 +619,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
565
619
  async def _hit_api_async_structured_output(
566
620
  self,
567
621
  model: str,
568
- messages: List[Dict[str, Any]],
622
+ messages: list[dict[str, Any]],
569
623
  response_model: BaseModel,
570
624
  temperature: float,
571
625
  use_ephemeral_cache_only: bool = False,
572
626
  reasoning_effort: str = "high",
573
- tools: Optional[List[BaseTool]] = None,
627
+ tools: list[BaseTool] | None = None,
574
628
  ) -> BaseLMResponse:
575
629
  lm_config = {
576
630
  "temperature": temperature,
@@ -578,7 +632,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
578
632
  "reasoning_effort": reasoning_effort,
579
633
  }
580
634
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
581
- cache_result: Union[BaseLMResponse, None] = used_cache_handler.hit_managed_cache(
635
+ cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
582
636
  model, messages, lm_config=lm_config, tools=tools
583
637
  )
584
638
  if cache_result is not None:
@@ -624,12 +678,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
624
678
  def _hit_api_sync_structured_output(
625
679
  self,
626
680
  model: str,
627
- messages: List[Dict[str, Any]],
681
+ messages: list[dict[str, Any]],
628
682
  response_model: BaseModel,
629
683
  temperature: float,
630
684
  use_ephemeral_cache_only: bool = False,
631
685
  reasoning_effort: str = "high",
632
- tools: Optional[List[BaseTool]] = None,
686
+ tools: list[BaseTool] | None = None,
633
687
  ) -> BaseLMResponse:
634
688
  lm_config = {
635
689
  "temperature": temperature,
@@ -637,7 +691,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
637
691
  "reasoning_effort": reasoning_effort,
638
692
  }
639
693
  used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
640
- cache_result: Union[BaseLMResponse, None] = used_cache_handler.hit_managed_cache(
694
+ cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
641
695
  model, messages, lm_config=lm_config, tools=tools
642
696
  )
643
697
  if cache_result is not None:
@@ -671,7 +725,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
671
725
  base_url_str_sync = str(base_url_obj) if base_url_obj is not None else ""
672
726
  except Exception:
673
727
  base_url_str_sync = ""
674
- 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"):
675
731
  if "max_tokens" in api_params:
676
732
  api_params["max_completion_tokens"] = api_params.pop("max_tokens")
677
733
  if "temperature" in api_params: