synth-ai 0.2.4.dev6__py3-none-any.whl → 0.2.4.dev8__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 +25 -32
- 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 +47 -2
- 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/http.py +102 -0
- synth_ai/inference/__init__.py +7 -0
- synth_ai/inference/client.py +20 -0
- synth_ai/jobs/client.py +246 -0
- synth_ai/learning/__init__.py +24 -0
- synth_ai/learning/client.py +149 -0
- synth_ai/learning/config.py +43 -0
- synth_ai/learning/constants.py +29 -0
- synth_ai/learning/ft_client.py +59 -0
- synth_ai/learning/gateway.py +1 -3
- synth_ai/learning/health.py +43 -0
- synth_ai/learning/jobs.py +205 -0
- 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/learning/rl_client.py +256 -0
- synth_ai/learning/sse.py +58 -0
- synth_ai/learning/validators.py +48 -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 +33 -10
- synth_ai/lm/core/synth_models.py +48 -0
- synth_ai/lm/core/vendor_clients.py +26 -22
- 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 +42 -13
- synth_ai/lm/vendors/openai_standard.py +158 -101
- 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 +38 -28
- 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 +425 -75
- synth_ai/lm/warmup.py +8 -7
- synth_ai/rl/__init__.py +30 -0
- synth_ai/rl/contracts.py +32 -0
- synth_ai/rl/env_keys.py +137 -0
- synth_ai/rl/secrets.py +19 -0
- synth_ai/scripts/verify_rewards.py +100 -0
- synth_ai/task/__init__.py +10 -0
- synth_ai/task/contracts.py +120 -0
- synth_ai/task/health.py +28 -0
- synth_ai/task/validators.py +12 -0
- 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 +24 -22
- 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 +158 -31
- 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 +278 -48
- synth_ai/tracing_v3/turso/models.py +77 -19
- synth_ai/tracing_v3/utils.py +5 -5
- 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.dev8.dist-info/METADATA +635 -0
- synth_ai-0.2.4.dev8.dist-info/RECORD +317 -0
- synth_ai/tui/__init__.py +0 -1
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -165
- synth_ai/tui/cli/query_experiments_v3.py +0 -165
- synth_ai/tui/dashboard.py +0 -329
- synth_ai-0.2.4.dev6.dist-info/METADATA +0 -203
- synth_ai-0.2.4.dev6.dist-info/RECORD +0 -299
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev6.dist-info → synth_ai-0.2.4.dev8.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 = {
|
@@ -202,7 +207,22 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
202
207
|
api_params = apply_tool_overrides(api_params)
|
203
208
|
api_params = apply_param_overrides(api_params)
|
204
209
|
|
205
|
-
#
|
210
|
+
# Thinking controls: route via extra_body.chat_template_kwargs for compatibility
|
211
|
+
thinking_mode_val = lm_config.get("thinking_mode")
|
212
|
+
thinking_budget_val = lm_config.get("thinking_budget")
|
213
|
+
if thinking_mode_val is not None or thinking_budget_val is not None:
|
214
|
+
api_params["extra_body"] = api_params.get("extra_body", {})
|
215
|
+
ctk = api_params["extra_body"].get("chat_template_kwargs", {})
|
216
|
+
if thinking_mode_val is not None:
|
217
|
+
ctk["thinking_mode"] = thinking_mode_val
|
218
|
+
if thinking_budget_val is not None:
|
219
|
+
try:
|
220
|
+
ctk["thinking_budget"] = int(thinking_budget_val)
|
221
|
+
except Exception:
|
222
|
+
ctk["thinking_budget"] = thinking_budget_val
|
223
|
+
api_params["extra_body"]["chat_template_kwargs"] = ctk
|
224
|
+
|
225
|
+
# Backward-compatible: forward legacy enable_thinking only via extra_body for callers still using it
|
206
226
|
if lm_config.get("enable_thinking") is not None:
|
207
227
|
api_params["extra_body"] = api_params.get("extra_body", {})
|
208
228
|
ctk = api_params["extra_body"].get("chat_template_kwargs", {})
|
@@ -211,8 +231,11 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
211
231
|
# Forward arbitrary extra_body from lm_config if provided (merge)
|
212
232
|
if lm_config.get("extra_body") is not None:
|
213
233
|
# Shallow-merge top-level keys; nested keys (like chat_template_kwargs) should be provided whole
|
214
|
-
api_params["extra_body"] = {
|
215
|
-
|
234
|
+
api_params["extra_body"] = {
|
235
|
+
**api_params.get("extra_body", {}),
|
236
|
+
**(lm_config.get("extra_body") or {}),
|
237
|
+
}
|
238
|
+
# Ensure legacy extra_body flag remains merged (do not override top-level fields)
|
216
239
|
if lm_config.get("enable_thinking") is not None:
|
217
240
|
api_params["extra_body"] = api_params.get("extra_body", {})
|
218
241
|
ctk = api_params["extra_body"].get("chat_template_kwargs", {})
|
@@ -233,9 +256,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
233
256
|
except Exception:
|
234
257
|
base_url_str = ""
|
235
258
|
|
236
|
-
is_external_provider =
|
237
|
-
"openai.com" in base_url_str or "api.groq.com" in base_url_str
|
238
|
-
)
|
259
|
+
is_external_provider = "openai.com" in base_url_str or "api.groq.com" in base_url_str
|
239
260
|
|
240
261
|
if is_external_provider:
|
241
262
|
# Remove extra_body entirely; this is Synth-specific plumbing
|
@@ -257,18 +278,18 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
257
278
|
api_params.pop("temperature", None)
|
258
279
|
|
259
280
|
# Call API with better auth error reporting
|
260
|
-
#try:
|
261
|
-
if
|
262
|
-
print(
|
281
|
+
# try:
|
282
|
+
if debug:
|
283
|
+
print("🔍 OPENAI DEBUG: Making request with params:")
|
263
284
|
print(f" Model: {api_params.get('model')}")
|
264
285
|
print(f" Messages: {len(api_params.get('messages', []))} messages")
|
265
286
|
print(f" Tools: {len(api_params.get('tools', []))} tools")
|
266
287
|
print(f" Max tokens: {api_params.get('max_tokens', 'NOT SET')}")
|
267
288
|
print(f" Temperature: {api_params.get('temperature', 'NOT SET')}")
|
268
|
-
if
|
289
|
+
if "tools" in api_params:
|
269
290
|
print(f" First tool: {api_params['tools'][0]}")
|
270
291
|
print(f" FULL API PARAMS: {api_params}")
|
271
|
-
|
292
|
+
|
272
293
|
# Quiet targeted retry for OpenAI 400 tool_use_failed during tool-calling
|
273
294
|
try:
|
274
295
|
max_attempts_for_tool_use = int(os.getenv("SYNTH_TOOL_USE_RETRIES", "5"))
|
@@ -294,7 +315,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
294
315
|
err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
|
295
316
|
code_val = err_obj.get("code")
|
296
317
|
msg_val = err_obj.get("message")
|
297
|
-
if code_val == "tool_use_failed" or (
|
318
|
+
if code_val == "tool_use_failed" or (
|
319
|
+
isinstance(msg_val, str) and "Failed to call a function" in msg_val
|
320
|
+
):
|
298
321
|
should_retry = True
|
299
322
|
except Exception:
|
300
323
|
pass
|
@@ -308,7 +331,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
308
331
|
err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
|
309
332
|
code_val = err_obj.get("code")
|
310
333
|
msg_val = err_obj.get("message")
|
311
|
-
if code_val == "tool_use_failed" or (
|
334
|
+
if code_val == "tool_use_failed" or (
|
335
|
+
isinstance(msg_val, str)
|
336
|
+
and "Failed to call a function" in msg_val
|
337
|
+
):
|
312
338
|
should_retry = True
|
313
339
|
except Exception:
|
314
340
|
pass
|
@@ -323,33 +349,37 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
323
349
|
attempt_index += 1
|
324
350
|
continue
|
325
351
|
raise
|
326
|
-
|
327
|
-
if
|
328
|
-
print(
|
352
|
+
|
353
|
+
if debug:
|
354
|
+
print("🔍 OPENAI DEBUG: Response received:")
|
329
355
|
print(f" Type: {type(output)}")
|
330
356
|
print(f" Choices: {len(output.choices) if hasattr(output, 'choices') else 'N/A'}")
|
331
|
-
if hasattr(output,
|
357
|
+
if hasattr(output, "choices") and output.choices:
|
332
358
|
choice = output.choices[0]
|
333
359
|
print(f" Choice type: {type(choice)}")
|
334
|
-
if hasattr(choice,
|
360
|
+
if hasattr(choice, "message"):
|
335
361
|
message = choice.message
|
336
362
|
print(f" Message type: {type(message)}")
|
337
363
|
print(f" Has tool_calls: {hasattr(message, 'tool_calls')}")
|
338
|
-
if hasattr(message,
|
364
|
+
if hasattr(message, "tool_calls"):
|
339
365
|
print(f" Tool calls: {message.tool_calls}")
|
340
|
-
print(
|
366
|
+
print(
|
367
|
+
f" Content: {message.content[:200] if hasattr(message, 'content') and message.content else 'None'}..."
|
368
|
+
)
|
341
369
|
# Show finish_reason and usage if available
|
342
370
|
try:
|
343
371
|
print(f" finish_reason: {getattr(choice, 'finish_reason', None)}")
|
344
|
-
usage = getattr(output,
|
372
|
+
usage = getattr(output, "usage", None)
|
345
373
|
if usage:
|
346
|
-
print(
|
374
|
+
print(
|
375
|
+
f" usage: prompt_tokens={getattr(usage, 'prompt_tokens', None)}, completion_tokens={getattr(usage, 'completion_tokens', None)}, total_tokens={getattr(usage, 'total_tokens', None)}"
|
376
|
+
)
|
347
377
|
except Exception:
|
348
378
|
pass
|
349
|
-
|
350
|
-
if
|
351
|
-
print(
|
352
|
-
if hasattr(output.choices[0].message,
|
379
|
+
|
380
|
+
if debug:
|
381
|
+
print("🔍 OPENAI DEBUG: FULL RAW RESPONSE:")
|
382
|
+
if hasattr(output.choices[0].message, "content") and output.choices[0].message.content:
|
353
383
|
print(f" FULL CONTENT:\n{output.choices[0].message.content}")
|
354
384
|
print(f" Raw choice: {choice}")
|
355
385
|
print(f" Raw message: {message}")
|
@@ -372,30 +402,46 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
372
402
|
# raise
|
373
403
|
message = output.choices[0].message
|
374
404
|
|
375
|
-
# Convert tool calls to dict format
|
405
|
+
# Convert tool calls to dict format, preferring dict-shaped entries first
|
376
406
|
tool_calls = None
|
377
407
|
if message.tool_calls:
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
"
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
408
|
+
converted: list[dict] = []
|
409
|
+
for tc in message.tool_calls:
|
410
|
+
if isinstance(tc, dict):
|
411
|
+
fn = tc.get("function") or {}
|
412
|
+
converted.append(
|
413
|
+
{
|
414
|
+
"id": tc.get("id"),
|
415
|
+
"type": tc.get("type", "function"),
|
416
|
+
"function": {
|
417
|
+
"name": fn.get("name") or tc.get("name"),
|
418
|
+
"arguments": fn.get("arguments") or tc.get("arguments"),
|
419
|
+
},
|
420
|
+
}
|
421
|
+
)
|
422
|
+
else:
|
423
|
+
# SDK object path
|
424
|
+
converted.append(
|
425
|
+
{
|
426
|
+
"id": getattr(tc, "id", None),
|
427
|
+
"type": getattr(tc, "type", "function"),
|
428
|
+
"function": {
|
429
|
+
"name": getattr(getattr(tc, "function", None), "name", None),
|
430
|
+
"arguments": getattr(getattr(tc, "function", None), "arguments", None),
|
431
|
+
},
|
432
|
+
}
|
433
|
+
)
|
434
|
+
tool_calls = converted or None
|
389
435
|
|
390
436
|
# Attach basic usage if available
|
391
437
|
usage_dict = None
|
392
438
|
try:
|
393
|
-
usage_obj = getattr(output,
|
439
|
+
usage_obj = getattr(output, "usage", None)
|
394
440
|
if usage_obj is not None:
|
395
441
|
usage_dict = {
|
396
|
-
"prompt_tokens": getattr(usage_obj,
|
397
|
-
"completion_tokens": getattr(usage_obj,
|
398
|
-
"total_tokens": getattr(usage_obj,
|
442
|
+
"prompt_tokens": getattr(usage_obj, "prompt_tokens", None),
|
443
|
+
"completion_tokens": getattr(usage_obj, "completion_tokens", None),
|
444
|
+
"total_tokens": getattr(usage_obj, "total_tokens", None),
|
399
445
|
}
|
400
446
|
except Exception:
|
401
447
|
usage_dict = None
|
@@ -422,13 +468,13 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
422
468
|
def _hit_api_sync(
|
423
469
|
self,
|
424
470
|
model: str,
|
425
|
-
messages:
|
426
|
-
lm_config:
|
471
|
+
messages: list[dict[str, Any]],
|
472
|
+
lm_config: dict[str, Any],
|
427
473
|
use_ephemeral_cache_only: bool = False,
|
428
474
|
reasoning_effort: str = "high",
|
429
|
-
tools:
|
475
|
+
tools: list[BaseTool] | None = None,
|
430
476
|
) -> BaseLMResponse:
|
431
|
-
assert lm_config.get("response_model"
|
477
|
+
assert lm_config.get("response_model") is None, (
|
432
478
|
"response_model is not supported for standard calls"
|
433
479
|
)
|
434
480
|
messages = special_orion_transform(model, messages)
|
@@ -441,8 +487,8 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
441
487
|
model, messages, lm_config=lm_config, tools=tools
|
442
488
|
)
|
443
489
|
# During pytest runs, bypass returning cache to allow tests to inspect outgoing params
|
444
|
-
|
445
|
-
if cache_result and not
|
490
|
+
in_pytest = os.getenv("PYTEST_CURRENT_TEST") is not None
|
491
|
+
if cache_result and not in_pytest:
|
446
492
|
return cache_result
|
447
493
|
|
448
494
|
# Common API call params
|
@@ -514,7 +560,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
514
560
|
err_obj = body.get("error") if isinstance(body.get("error"), dict) else {}
|
515
561
|
code_val = err_obj.get("code")
|
516
562
|
msg_val = err_obj.get("message")
|
517
|
-
if code_val == "tool_use_failed" or (
|
563
|
+
if code_val == "tool_use_failed" or (
|
564
|
+
isinstance(msg_val, str) and "Failed to call a function" in msg_val
|
565
|
+
):
|
518
566
|
should_retry = True
|
519
567
|
except Exception:
|
520
568
|
pass
|
@@ -527,7 +575,10 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
527
575
|
err_obj = j.get("error") if isinstance(j.get("error"), dict) else {}
|
528
576
|
code_val = err_obj.get("code")
|
529
577
|
msg_val = err_obj.get("message")
|
530
|
-
if code_val == "tool_use_failed" or (
|
578
|
+
if code_val == "tool_use_failed" or (
|
579
|
+
isinstance(msg_val, str)
|
580
|
+
and "Failed to call a function" in msg_val
|
581
|
+
):
|
531
582
|
should_retry = True
|
532
583
|
except Exception:
|
533
584
|
pass
|
@@ -542,13 +593,17 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
542
593
|
continue
|
543
594
|
raise
|
544
595
|
message = output.choices[0].message
|
545
|
-
|
546
|
-
if
|
596
|
+
debug_sync = os.getenv("SYNTH_OPENAI_DEBUG") == "1"
|
597
|
+
if debug_sync:
|
547
598
|
try:
|
548
|
-
print(
|
549
|
-
|
599
|
+
print(
|
600
|
+
f"🔍 OPENAI DEBUG (sync): finish_reason={getattr(output.choices[0], 'finish_reason', None)}"
|
601
|
+
)
|
602
|
+
usage = getattr(output, "usage", None)
|
550
603
|
if usage:
|
551
|
-
print(
|
604
|
+
print(
|
605
|
+
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)}"
|
606
|
+
)
|
552
607
|
except Exception:
|
553
608
|
pass
|
554
609
|
|
@@ -570,12 +625,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
570
625
|
# Attach basic usage if available
|
571
626
|
usage_dict = None
|
572
627
|
try:
|
573
|
-
usage_obj = getattr(output,
|
628
|
+
usage_obj = getattr(output, "usage", None)
|
574
629
|
if usage_obj is not None:
|
575
630
|
usage_dict = {
|
576
|
-
"prompt_tokens": getattr(usage_obj,
|
577
|
-
"completion_tokens": getattr(usage_obj,
|
578
|
-
"total_tokens": getattr(usage_obj,
|
631
|
+
"prompt_tokens": getattr(usage_obj, "prompt_tokens", None),
|
632
|
+
"completion_tokens": getattr(usage_obj, "completion_tokens", None),
|
633
|
+
"total_tokens": getattr(usage_obj, "total_tokens", None),
|
579
634
|
}
|
580
635
|
except Exception:
|
581
636
|
usage_dict = None
|
@@ -595,12 +650,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
595
650
|
async def _hit_api_async_structured_output(
|
596
651
|
self,
|
597
652
|
model: str,
|
598
|
-
messages:
|
653
|
+
messages: list[dict[str, Any]],
|
599
654
|
response_model: BaseModel,
|
600
655
|
temperature: float,
|
601
656
|
use_ephemeral_cache_only: bool = False,
|
602
657
|
reasoning_effort: str = "high",
|
603
|
-
tools:
|
658
|
+
tools: list[BaseTool] | None = None,
|
604
659
|
) -> BaseLMResponse:
|
605
660
|
lm_config = {
|
606
661
|
"temperature": temperature,
|
@@ -608,7 +663,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
608
663
|
"reasoning_effort": reasoning_effort,
|
609
664
|
}
|
610
665
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
611
|
-
cache_result:
|
666
|
+
cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
|
612
667
|
model, messages, lm_config=lm_config, tools=tools
|
613
668
|
)
|
614
669
|
if cache_result is not None:
|
@@ -654,12 +709,12 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
654
709
|
def _hit_api_sync_structured_output(
|
655
710
|
self,
|
656
711
|
model: str,
|
657
|
-
messages:
|
712
|
+
messages: list[dict[str, Any]],
|
658
713
|
response_model: BaseModel,
|
659
714
|
temperature: float,
|
660
715
|
use_ephemeral_cache_only: bool = False,
|
661
716
|
reasoning_effort: str = "high",
|
662
|
-
tools:
|
717
|
+
tools: list[BaseTool] | None = None,
|
663
718
|
) -> BaseLMResponse:
|
664
719
|
lm_config = {
|
665
720
|
"temperature": temperature,
|
@@ -667,7 +722,7 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
667
722
|
"reasoning_effort": reasoning_effort,
|
668
723
|
}
|
669
724
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
670
|
-
cache_result:
|
725
|
+
cache_result: BaseLMResponse | None = used_cache_handler.hit_managed_cache(
|
671
726
|
model, messages, lm_config=lm_config, tools=tools
|
672
727
|
)
|
673
728
|
if cache_result is not None:
|
@@ -701,7 +756,9 @@ class OpenAIStandard(VendorBase, OpenAIResponsesAPIMixin):
|
|
701
756
|
base_url_str_sync = str(base_url_obj) if base_url_obj is not None else ""
|
702
757
|
except Exception:
|
703
758
|
base_url_str_sync = ""
|
704
|
-
if (
|
759
|
+
if (
|
760
|
+
"openai.com" in base_url_str_sync or "api.groq.com" in base_url_str_sync
|
761
|
+
) and model.startswith("gpt-5"):
|
705
762
|
if "max_tokens" in api_params:
|
706
763
|
api_params["max_completion_tokens"] = api_params.pop("max_tokens")
|
707
764
|
if "temperature" in api_params:
|