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.
- synth_ai/__init__.py +18 -9
- synth_ai/cli/__init__.py +10 -5
- synth_ai/cli/balance.py +22 -17
- synth_ai/cli/calc.py +2 -3
- synth_ai/cli/demo.py +3 -5
- synth_ai/cli/legacy_root_backup.py +58 -32
- synth_ai/cli/man.py +22 -19
- synth_ai/cli/recent.py +9 -8
- synth_ai/cli/root.py +58 -13
- synth_ai/cli/status.py +13 -6
- synth_ai/cli/traces.py +45 -21
- synth_ai/cli/watch.py +40 -37
- synth_ai/config/base_url.py +1 -3
- synth_ai/core/experiment.py +1 -2
- synth_ai/environments/__init__.py +2 -6
- synth_ai/environments/environment/artifacts/base.py +3 -1
- synth_ai/environments/environment/db/sqlite.py +1 -1
- synth_ai/environments/environment/registry.py +19 -20
- synth_ai/environments/environment/resources/sqlite.py +2 -3
- synth_ai/environments/environment/rewards/core.py +3 -2
- synth_ai/environments/environment/tools/__init__.py +6 -4
- synth_ai/environments/examples/crafter_classic/__init__.py +1 -1
- synth_ai/environments/examples/crafter_classic/engine.py +21 -17
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +1 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +2 -1
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +3 -2
- synth_ai/environments/examples/crafter_classic/environment.py +16 -15
- synth_ai/environments/examples/crafter_classic/taskset.py +2 -2
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +2 -3
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +2 -1
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/config.py +2 -2
- synth_ai/environments/examples/crafter_custom/crafter/env.py +1 -5
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +1 -2
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +1 -2
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +5 -5
- synth_ai/environments/examples/crafter_custom/environment.py +13 -13
- synth_ai/environments/examples/crafter_custom/run_dataset.py +5 -5
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +2 -2
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +5 -4
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +2 -1
- synth_ai/environments/examples/enron/engine.py +18 -14
- synth_ai/environments/examples/enron/environment.py +12 -11
- synth_ai/environments/examples/enron/taskset.py +7 -7
- synth_ai/environments/examples/minigrid/__init__.py +6 -6
- synth_ai/environments/examples/minigrid/engine.py +6 -6
- synth_ai/environments/examples/minigrid/environment.py +6 -6
- synth_ai/environments/examples/minigrid/puzzle_loader.py +3 -2
- synth_ai/environments/examples/minigrid/taskset.py +13 -13
- synth_ai/environments/examples/nethack/achievements.py +1 -1
- synth_ai/environments/examples/nethack/engine.py +8 -7
- synth_ai/environments/examples/nethack/environment.py +10 -9
- synth_ai/environments/examples/nethack/helpers/__init__.py +8 -9
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +1 -1
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +2 -1
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +1 -1
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +3 -4
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +6 -5
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +5 -5
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +7 -6
- synth_ai/environments/examples/nethack/taskset.py +5 -5
- synth_ai/environments/examples/red/engine.py +9 -8
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +7 -7
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +2 -1
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +3 -2
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +2 -1
- synth_ai/environments/examples/red/environment.py +18 -15
- synth_ai/environments/examples/red/taskset.py +5 -3
- synth_ai/environments/examples/sokoban/engine.py +16 -13
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +7 -5
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +1 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +3 -2
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +2 -1
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +5 -4
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +1 -1
- synth_ai/environments/examples/sokoban/environment.py +15 -14
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +5 -3
- synth_ai/environments/examples/sokoban/puzzle_loader.py +3 -2
- synth_ai/environments/examples/sokoban/taskset.py +13 -10
- synth_ai/environments/examples/tictactoe/engine.py +6 -6
- synth_ai/environments/examples/tictactoe/environment.py +8 -7
- synth_ai/environments/examples/tictactoe/taskset.py +6 -5
- synth_ai/environments/examples/verilog/engine.py +4 -3
- synth_ai/environments/examples/verilog/environment.py +11 -10
- synth_ai/environments/examples/verilog/taskset.py +14 -12
- synth_ai/environments/examples/wordle/__init__.py +29 -0
- synth_ai/environments/examples/wordle/engine.py +398 -0
- synth_ai/environments/examples/wordle/environment.py +159 -0
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +75 -0
- synth_ai/environments/examples/wordle/taskset.py +230 -0
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +11 -2
- synth_ai/environments/service/core_routes.py +137 -105
- synth_ai/environments/service/external_registry.py +1 -2
- synth_ai/environments/service/registry.py +1 -1
- synth_ai/environments/stateful/core.py +1 -2
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/api.py +4 -4
- synth_ai/environments/tasks/core.py +14 -12
- synth_ai/environments/tasks/filters.py +6 -4
- synth_ai/environments/tasks/utils.py +13 -11
- synth_ai/evals/base.py +2 -3
- synth_ai/experimental/synth_oss.py +4 -4
- synth_ai/learning/gateway.py +1 -3
- synth_ai/learning/prompts/banking77_injection_eval.py +168 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +213 -0
- synth_ai/learning/prompts/mipro.py +282 -1
- synth_ai/learning/prompts/random_search.py +246 -0
- synth_ai/learning/prompts/run_mipro_banking77.py +172 -0
- synth_ai/learning/prompts/run_random_search_banking77.py +324 -0
- synth_ai/lm/__init__.py +5 -5
- synth_ai/lm/caching/ephemeral.py +9 -9
- synth_ai/lm/caching/handler.py +20 -20
- synth_ai/lm/caching/persistent.py +10 -10
- synth_ai/lm/config.py +3 -3
- synth_ai/lm/constants.py +7 -7
- synth_ai/lm/core/all.py +17 -3
- synth_ai/lm/core/exceptions.py +0 -2
- synth_ai/lm/core/main.py +26 -41
- synth_ai/lm/core/main_v3.py +20 -10
- synth_ai/lm/core/vendor_clients.py +18 -17
- synth_ai/lm/injection.py +80 -0
- synth_ai/lm/overrides.py +206 -0
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +51 -24
- synth_ai/lm/provider_support/openai.py +51 -22
- synth_ai/lm/structured_outputs/handler.py +34 -32
- synth_ai/lm/structured_outputs/inject.py +24 -27
- synth_ai/lm/structured_outputs/rehabilitate.py +19 -15
- synth_ai/lm/tools/base.py +17 -16
- synth_ai/lm/unified_interface.py +17 -18
- synth_ai/lm/vendors/base.py +20 -18
- synth_ai/lm/vendors/core/anthropic_api.py +50 -25
- synth_ai/lm/vendors/core/gemini_api.py +31 -36
- synth_ai/lm/vendors/core/mistral_api.py +19 -19
- synth_ai/lm/vendors/core/openai_api.py +11 -10
- synth_ai/lm/vendors/openai_standard.py +144 -88
- synth_ai/lm/vendors/openai_standard_responses.py +74 -61
- synth_ai/lm/vendors/retries.py +9 -1
- synth_ai/lm/vendors/supported/custom_endpoint.py +26 -26
- synth_ai/lm/vendors/supported/deepseek.py +10 -10
- synth_ai/lm/vendors/supported/grok.py +8 -8
- synth_ai/lm/vendors/supported/ollama.py +2 -1
- synth_ai/lm/vendors/supported/openrouter.py +11 -9
- synth_ai/lm/vendors/synth_client.py +69 -63
- synth_ai/lm/warmup.py +8 -7
- synth_ai/tracing/__init__.py +22 -10
- synth_ai/tracing_v1/__init__.py +22 -20
- synth_ai/tracing_v3/__init__.py +7 -7
- synth_ai/tracing_v3/abstractions.py +56 -52
- synth_ai/tracing_v3/config.py +4 -2
- synth_ai/tracing_v3/db_config.py +6 -8
- synth_ai/tracing_v3/decorators.py +29 -30
- synth_ai/tracing_v3/examples/basic_usage.py +12 -12
- synth_ai/tracing_v3/hooks.py +21 -21
- synth_ai/tracing_v3/llm_call_record_helpers.py +85 -98
- synth_ai/tracing_v3/lm_call_record_abstractions.py +2 -4
- synth_ai/tracing_v3/migration_helper.py +3 -5
- synth_ai/tracing_v3/replica_sync.py +30 -32
- synth_ai/tracing_v3/session_tracer.py +35 -29
- synth_ai/tracing_v3/storage/__init__.py +1 -1
- synth_ai/tracing_v3/storage/base.py +8 -7
- synth_ai/tracing_v3/storage/config.py +4 -4
- synth_ai/tracing_v3/storage/factory.py +4 -4
- synth_ai/tracing_v3/storage/utils.py +9 -9
- synth_ai/tracing_v3/turso/__init__.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +9 -9
- synth_ai/tracing_v3/turso/manager.py +60 -48
- synth_ai/tracing_v3/turso/models.py +24 -19
- synth_ai/tracing_v3/utils.py +5 -5
- synth_ai/tui/__main__.py +1 -1
- synth_ai/tui/cli/query_experiments.py +2 -3
- synth_ai/tui/cli/query_experiments_v3.py +2 -3
- synth_ai/tui/dashboard.py +97 -86
- synth_ai/v0/tracing/abstractions.py +28 -28
- synth_ai/v0/tracing/base_client.py +9 -9
- synth_ai/v0/tracing/client_manager.py +7 -7
- synth_ai/v0/tracing/config.py +7 -7
- synth_ai/v0/tracing/context.py +6 -6
- synth_ai/v0/tracing/decorators.py +6 -5
- synth_ai/v0/tracing/events/manage.py +1 -1
- synth_ai/v0/tracing/events/store.py +5 -4
- synth_ai/v0/tracing/immediate_client.py +4 -5
- synth_ai/v0/tracing/local.py +3 -3
- synth_ai/v0/tracing/log_client_base.py +4 -5
- synth_ai/v0/tracing/retry_queue.py +5 -6
- synth_ai/v0/tracing/trackers.py +25 -25
- synth_ai/v0/tracing/upload.py +6 -0
- synth_ai/v0/tracing_v1/__init__.py +1 -1
- synth_ai/v0/tracing_v1/abstractions.py +28 -28
- synth_ai/v0/tracing_v1/base_client.py +9 -9
- synth_ai/v0/tracing_v1/client_manager.py +7 -7
- synth_ai/v0/tracing_v1/config.py +7 -7
- synth_ai/v0/tracing_v1/context.py +6 -6
- synth_ai/v0/tracing_v1/decorators.py +7 -6
- synth_ai/v0/tracing_v1/events/manage.py +1 -1
- synth_ai/v0/tracing_v1/events/store.py +5 -4
- synth_ai/v0/tracing_v1/immediate_client.py +4 -5
- synth_ai/v0/tracing_v1/local.py +3 -3
- synth_ai/v0/tracing_v1/log_client_base.py +4 -5
- synth_ai/v0/tracing_v1/retry_queue.py +5 -6
- synth_ai/v0/tracing_v1/trackers.py +25 -25
- synth_ai/v0/tracing_v1/upload.py +25 -24
- synth_ai/zyk/__init__.py +1 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +2 -11
- synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
- synth_ai-0.2.4.dev5.dist-info/RECORD +0 -287
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev5.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
- {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
|
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:
|
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:
|
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:
|
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
|
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:
|
108
|
-
lm_config:
|
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:
|
119
|
+
tools: list[BaseTool] | None = None,
|
112
120
|
) -> BaseLMResponse:
|
113
|
-
assert lm_config.get("response_model"
|
121
|
+
assert lm_config.get("response_model") is None, (
|
114
122
|
"response_model is not supported for standard calls"
|
115
123
|
)
|
116
|
-
|
117
|
-
|
118
|
-
if
|
119
|
-
print(
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
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"] = {
|
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
|
247
|
-
print(
|
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
|
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 (
|
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 (
|
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
|
313
|
-
print(
|
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,
|
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,
|
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,
|
349
|
+
if hasattr(message, "tool_calls"):
|
324
350
|
print(f" Tool calls: {message.tool_calls}")
|
325
|
-
print(
|
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,
|
357
|
+
usage = getattr(output, "usage", None)
|
330
358
|
if usage:
|
331
|
-
print(
|
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
|
336
|
-
print(
|
337
|
-
if hasattr(output.choices[0].message,
|
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,
|
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,
|
382
|
-
"completion_tokens": getattr(usage_obj,
|
383
|
-
"total_tokens": getattr(usage_obj,
|
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:
|
411
|
-
lm_config:
|
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:
|
444
|
+
tools: list[BaseTool] | None = None,
|
415
445
|
) -> BaseLMResponse:
|
416
|
-
assert lm_config.get("response_model"
|
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
|
-
|
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
|
-
#
|
441
|
-
|
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 (
|
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 (
|
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
|
-
|
516
|
-
if
|
565
|
+
debug_sync = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
|
566
|
+
if debug_sync:
|
517
567
|
try:
|
518
|
-
print(
|
519
|
-
|
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(
|
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,
|
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,
|
547
|
-
"completion_tokens": getattr(usage_obj,
|
548
|
-
"total_tokens": getattr(usage_obj,
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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 (
|
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:
|