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.
- 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 +13 -13
- 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 +5 -5
- synth_ai/environments/examples/wordle/engine.py +32 -25
- synth_ai/environments/examples/wordle/environment.py +21 -16
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +6 -6
- synth_ai/environments/examples/wordle/taskset.py +20 -12
- synth_ai/environments/reproducibility/core.py +1 -1
- synth_ai/environments/reproducibility/tree.py +21 -21
- synth_ai/environments/service/app.py +3 -2
- synth_ai/environments/service/core_routes.py +104 -110
- 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 +15 -10
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +26 -14
- synth_ai/learning/prompts/mipro.py +61 -52
- synth_ai/learning/prompts/random_search.py +42 -43
- synth_ai/learning/prompts/run_mipro_banking77.py +32 -20
- synth_ai/learning/prompts/run_random_search_banking77.py +71 -52
- 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 +7 -8
- synth_ai/lm/overrides.py +21 -19
- synth_ai/lm/provider_support/__init__.py +1 -1
- synth_ai/lm/provider_support/anthropic.py +15 -15
- synth_ai/lm/provider_support/openai.py +23 -21
- 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 +36 -27
- 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 +113 -87
- 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.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/METADATA +1 -11
- synth_ai-0.2.4.dev7.dist-info/RECORD +299 -0
- synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev7.dist-info}/licenses/LICENSE +0 -0
- {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
|
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:
|
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:
|
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:
|
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
|
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:
|
110
|
-
lm_config:
|
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:
|
119
|
+
tools: list[BaseTool] | None = None,
|
114
120
|
) -> BaseLMResponse:
|
115
|
-
assert lm_config.get("response_model"
|
121
|
+
assert lm_config.get("response_model") is None, (
|
116
122
|
"response_model is not supported for standard calls"
|
117
123
|
)
|
118
|
-
|
119
|
-
|
120
|
-
if
|
121
|
-
print(
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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"] = {
|
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
|
262
|
-
print(
|
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
|
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 (
|
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 (
|
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
|
328
|
-
print(
|
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,
|
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,
|
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,
|
349
|
+
if hasattr(message, "tool_calls"):
|
339
350
|
print(f" Tool calls: {message.tool_calls}")
|
340
|
-
print(
|
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,
|
357
|
+
usage = getattr(output, "usage", None)
|
345
358
|
if usage:
|
346
|
-
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
|
+
)
|
347
362
|
except Exception:
|
348
363
|
pass
|
349
|
-
|
350
|
-
if
|
351
|
-
print(
|
352
|
-
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:
|
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,
|
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,
|
397
|
-
"completion_tokens": getattr(usage_obj,
|
398
|
-
"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),
|
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:
|
426
|
-
lm_config:
|
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:
|
444
|
+
tools: list[BaseTool] | None = None,
|
430
445
|
) -> BaseLMResponse:
|
431
|
-
assert lm_config.get("response_model"
|
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
|
-
|
445
|
-
if cache_result and not
|
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 (
|
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 (
|
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
|
-
|
546
|
-
if
|
565
|
+
debug_sync = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
|
566
|
+
if debug_sync:
|
547
567
|
try:
|
548
|
-
print(
|
549
|
-
|
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(
|
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,
|
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,
|
577
|
-
"completion_tokens": getattr(usage_obj,
|
578
|
-
"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),
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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 (
|
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:
|