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
synth_ai/lm/vendors/base.py
CHANGED
@@ -5,7 +5,7 @@ This module provides abstract base classes for implementing language model vendo
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
from abc import ABC, abstractmethod
|
8
|
-
from typing import Any
|
8
|
+
from typing import Any
|
9
9
|
|
10
10
|
from pydantic import BaseModel
|
11
11
|
|
@@ -13,7 +13,7 @@ from pydantic import BaseModel
|
|
13
13
|
class BaseLMResponse(BaseModel):
|
14
14
|
"""
|
15
15
|
Standard response format from language model API calls.
|
16
|
-
|
16
|
+
|
17
17
|
Attributes:
|
18
18
|
raw_response: The raw text response from the model
|
19
19
|
structured_output: Optional parsed Pydantic model if structured output was requested
|
@@ -22,39 +22,41 @@ class BaseLMResponse(BaseModel):
|
|
22
22
|
reasoning: Optional reasoning trace from the model (o1 models)
|
23
23
|
api_type: Optional API type used ("chat", "responses", or "harmony")
|
24
24
|
"""
|
25
|
+
|
25
26
|
raw_response: str
|
26
|
-
structured_output:
|
27
|
-
tool_calls:
|
28
|
-
response_id:
|
29
|
-
reasoning:
|
30
|
-
api_type:
|
31
|
-
usage:
|
27
|
+
structured_output: BaseModel | None = None
|
28
|
+
tool_calls: list[dict] | None = None
|
29
|
+
response_id: str | None = None
|
30
|
+
reasoning: str | None = None
|
31
|
+
api_type: str | None = None
|
32
|
+
usage: dict[str, Any] | None = None
|
32
33
|
|
33
34
|
|
34
35
|
class VendorBase(ABC):
|
35
36
|
"""
|
36
37
|
Abstract base class for language model vendor implementations.
|
37
|
-
|
38
|
+
|
38
39
|
Attributes:
|
39
40
|
used_for_structured_outputs: Whether this vendor supports structured outputs
|
40
41
|
exceptions_to_retry: List of exceptions that should trigger retries
|
41
42
|
"""
|
43
|
+
|
42
44
|
used_for_structured_outputs: bool = False
|
43
|
-
exceptions_to_retry:
|
45
|
+
exceptions_to_retry: list[Exception] = []
|
44
46
|
|
45
47
|
@abstractmethod
|
46
48
|
async def _hit_api_async(
|
47
49
|
self,
|
48
|
-
messages:
|
49
|
-
response_model_override:
|
50
|
+
messages: list[dict[str, Any]],
|
51
|
+
response_model_override: BaseModel | None = None,
|
50
52
|
) -> str:
|
51
53
|
"""
|
52
54
|
Make an asynchronous API call to the language model.
|
53
|
-
|
55
|
+
|
54
56
|
Args:
|
55
57
|
messages: List of message dictionaries with role and content
|
56
58
|
response_model_override: Optional Pydantic model for structured output
|
57
|
-
|
59
|
+
|
58
60
|
Returns:
|
59
61
|
str: The model's response
|
60
62
|
"""
|
@@ -63,16 +65,16 @@ class VendorBase(ABC):
|
|
63
65
|
@abstractmethod
|
64
66
|
def _hit_api_sync(
|
65
67
|
self,
|
66
|
-
messages:
|
67
|
-
response_model_override:
|
68
|
+
messages: list[dict[str, Any]],
|
69
|
+
response_model_override: BaseModel | None = None,
|
68
70
|
) -> str:
|
69
71
|
"""
|
70
72
|
Make a synchronous API call to the language model.
|
71
|
-
|
73
|
+
|
72
74
|
Args:
|
73
75
|
messages: List of message dictionaries with role and content
|
74
76
|
response_model_override: Optional Pydantic model for structured output
|
75
|
-
|
77
|
+
|
76
78
|
Returns:
|
77
79
|
str: The model's response
|
78
80
|
"""
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any
|
3
3
|
|
4
4
|
import anthropic
|
5
5
|
import pydantic
|
@@ -8,27 +8,32 @@ from pydantic import BaseModel
|
|
8
8
|
from synth_ai.lm.caching.initialize import (
|
9
9
|
get_cache_handler,
|
10
10
|
)
|
11
|
+
from synth_ai.lm.constants import CLAUDE_REASONING_MODELS, SONNET_37_BUDGETS, SPECIAL_BASE_TEMPS
|
12
|
+
from synth_ai.lm.overrides import (
|
13
|
+
apply_injection as apply_injection_overrides,
|
14
|
+
)
|
15
|
+
from synth_ai.lm.overrides import (
|
16
|
+
apply_param_overrides,
|
17
|
+
use_overrides_for_messages,
|
18
|
+
)
|
11
19
|
from synth_ai.lm.tools.base import BaseTool
|
12
20
|
from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
|
13
|
-
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS, CLAUDE_REASONING_MODELS, SONNET_37_BUDGETS
|
14
21
|
from synth_ai.lm.vendors.core.openai_api import OpenAIStructuredOutputClient
|
15
|
-
from synth_ai.lm.overrides import use_overrides_for_messages, apply_injection as apply_injection_overrides, apply_param_overrides
|
16
|
-
from synth_ai.lm.injection import apply_injection
|
17
22
|
|
18
|
-
ANTHROPIC_EXCEPTIONS_TO_RETRY:
|
23
|
+
ANTHROPIC_EXCEPTIONS_TO_RETRY: tuple[type[Exception], ...] = (anthropic.APIError,)
|
19
24
|
|
20
25
|
|
21
26
|
class AnthropicAPI(VendorBase):
|
22
27
|
used_for_structured_outputs: bool = True
|
23
|
-
exceptions_to_retry:
|
28
|
+
exceptions_to_retry: tuple = ANTHROPIC_EXCEPTIONS_TO_RETRY
|
24
29
|
sync_client: Any
|
25
30
|
async_client: Any
|
26
31
|
|
27
32
|
def __init__(
|
28
33
|
self,
|
29
|
-
exceptions_to_retry:
|
34
|
+
exceptions_to_retry: tuple[type[Exception], ...] = ANTHROPIC_EXCEPTIONS_TO_RETRY,
|
30
35
|
used_for_structured_outputs: bool = False,
|
31
|
-
reasoning_effort:
|
36
|
+
reasoning_effort: str | None = "high",
|
32
37
|
):
|
33
38
|
self.sync_client = anthropic.Anthropic()
|
34
39
|
self.async_client = anthropic.AsyncAnthropic()
|
@@ -46,14 +51,14 @@ class AnthropicAPI(VendorBase):
|
|
46
51
|
async def _hit_api_async(
|
47
52
|
self,
|
48
53
|
model: str,
|
49
|
-
messages:
|
50
|
-
lm_config:
|
54
|
+
messages: list[dict[str, Any]],
|
55
|
+
lm_config: dict[str, Any],
|
51
56
|
use_ephemeral_cache_only: bool = False,
|
52
57
|
reasoning_effort: str = "high",
|
53
|
-
tools:
|
54
|
-
**vendor_params:
|
58
|
+
tools: list[BaseTool] | None = None,
|
59
|
+
**vendor_params: dict[str, Any],
|
55
60
|
) -> BaseLMResponse:
|
56
|
-
assert lm_config.get("response_model"
|
61
|
+
assert lm_config.get("response_model") is None, (
|
57
62
|
"response_model is not supported for standard calls"
|
58
63
|
)
|
59
64
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
@@ -77,6 +82,7 @@ class AnthropicAPI(VendorBase):
|
|
77
82
|
}
|
78
83
|
with use_overrides_for_messages(messages):
|
79
84
|
from synth_ai.lm.overrides import apply_tool_overrides
|
85
|
+
|
80
86
|
api_params = apply_tool_overrides(api_params)
|
81
87
|
api_params = apply_param_overrides(api_params)
|
82
88
|
|
@@ -89,8 +95,11 @@ class AnthropicAPI(VendorBase):
|
|
89
95
|
import inspect
|
90
96
|
|
91
97
|
create_sig = inspect.signature(self.async_client.messages.create)
|
92
|
-
if
|
93
|
-
|
98
|
+
if (
|
99
|
+
"thinking" in create_sig.parameters
|
100
|
+
and model in CLAUDE_REASONING_MODELS
|
101
|
+
and reasoning_effort in ["high", "medium"]
|
102
|
+
):
|
94
103
|
budget = SONNET_37_BUDGETS[reasoning_effort]
|
95
104
|
api_params["thinking"] = {
|
96
105
|
"type": "enabled",
|
@@ -144,14 +153,14 @@ class AnthropicAPI(VendorBase):
|
|
144
153
|
def _hit_api_sync(
|
145
154
|
self,
|
146
155
|
model: str,
|
147
|
-
messages:
|
148
|
-
lm_config:
|
156
|
+
messages: list[dict[str, Any]],
|
157
|
+
lm_config: dict[str, Any],
|
149
158
|
use_ephemeral_cache_only: bool = False,
|
150
159
|
reasoning_effort: str = "high",
|
151
|
-
tools:
|
152
|
-
**vendor_params:
|
160
|
+
tools: list[BaseTool] | None = None,
|
161
|
+
**vendor_params: dict[str, Any],
|
153
162
|
) -> BaseLMResponse:
|
154
|
-
assert lm_config.get("response_model"
|
163
|
+
assert lm_config.get("response_model") is None, (
|
155
164
|
"response_model is not supported for standard calls"
|
156
165
|
)
|
157
166
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only=use_ephemeral_cache_only)
|
@@ -175,6 +184,7 @@ class AnthropicAPI(VendorBase):
|
|
175
184
|
}
|
176
185
|
with use_overrides_for_messages(messages):
|
177
186
|
from synth_ai.lm.overrides import apply_tool_overrides
|
187
|
+
|
178
188
|
api_params = apply_tool_overrides(api_params)
|
179
189
|
api_params = apply_param_overrides(api_params)
|
180
190
|
|
@@ -238,12 +248,12 @@ class AnthropicAPI(VendorBase):
|
|
238
248
|
async def _hit_api_async_structured_output(
|
239
249
|
self,
|
240
250
|
model: str,
|
241
|
-
messages:
|
251
|
+
messages: list[dict[str, Any]],
|
242
252
|
response_model: BaseModel,
|
243
253
|
temperature: float,
|
244
254
|
use_ephemeral_cache_only: bool = False,
|
245
255
|
reasoning_effort: str = "high",
|
246
|
-
**vendor_params:
|
256
|
+
**vendor_params: dict[str, Any],
|
247
257
|
) -> BaseLMResponse:
|
248
258
|
try:
|
249
259
|
# First try with Anthropic
|
@@ -297,17 +307,16 @@ class AnthropicAPI(VendorBase):
|
|
297
307
|
def _hit_api_sync_structured_output(
|
298
308
|
self,
|
299
309
|
model: str,
|
300
|
-
messages:
|
310
|
+
messages: list[dict[str, Any]],
|
301
311
|
response_model: BaseModel,
|
302
312
|
temperature: float,
|
303
313
|
use_ephemeral_cache_only: bool = False,
|
304
314
|
reasoning_effort: str = "high",
|
305
|
-
**vendor_params:
|
315
|
+
**vendor_params: dict[str, Any],
|
306
316
|
) -> BaseLMResponse:
|
307
317
|
try:
|
308
318
|
# First try with Anthropic
|
309
319
|
reasoning_effort = vendor_params.get("reasoning_effort", reasoning_effort)
|
310
|
-
import time
|
311
320
|
|
312
321
|
if model in CLAUDE_REASONING_MODELS:
|
313
322
|
if reasoning_effort in ["high", "medium"]:
|
@@ -359,13 +368,13 @@ class AnthropicAPI(VendorBase):
|
|
359
368
|
|
360
369
|
async def _process_call_async(
|
361
370
|
self,
|
362
|
-
messages:
|
371
|
+
messages: list[dict[str, Any]],
|
363
372
|
model: str,
|
364
373
|
response_model: BaseModel,
|
365
374
|
api_call_method,
|
366
375
|
temperature: float = 0.0,
|
367
376
|
use_ephemeral_cache_only: bool = False,
|
368
|
-
vendor_params:
|
377
|
+
vendor_params: dict[str, Any] = None,
|
369
378
|
) -> BaseModel:
|
370
379
|
vendor_params = vendor_params or {}
|
371
380
|
# Each vendor can filter parameters they support
|
@@ -2,22 +2,21 @@ import json
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
import warnings
|
5
|
-
from typing import Any
|
5
|
+
from typing import Any
|
6
6
|
|
7
7
|
import google.genai as genai
|
8
8
|
from google.api_core.exceptions import ResourceExhausted
|
9
9
|
from google.genai import types
|
10
|
+
|
10
11
|
from synth_ai.lm.caching.initialize import get_cache_handler
|
11
|
-
from synth_ai.lm.tools.base import BaseTool
|
12
|
-
from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
|
13
12
|
from synth_ai.lm.constants import (
|
14
|
-
SPECIAL_BASE_TEMPS,
|
15
13
|
GEMINI_REASONING_MODELS,
|
16
14
|
GEMINI_THINKING_BUDGETS,
|
15
|
+
SPECIAL_BASE_TEMPS,
|
17
16
|
)
|
17
|
+
from synth_ai.lm.tools.base import BaseTool
|
18
|
+
from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
|
18
19
|
from synth_ai.lm.vendors.retries import BACKOFF_TOLERANCE, MAX_BACKOFF, backoff
|
19
|
-
import logging
|
20
|
-
|
21
20
|
|
22
21
|
ALIASES = {
|
23
22
|
"gemini-2.5-flash": "gemini-2.5-flash-preview-04-17",
|
@@ -25,7 +24,7 @@ ALIASES = {
|
|
25
24
|
|
26
25
|
logger = logging.getLogger(__name__)
|
27
26
|
_CLIENT = None # Initialize lazily when needed
|
28
|
-
GEMINI_EXCEPTIONS_TO_RETRY:
|
27
|
+
GEMINI_EXCEPTIONS_TO_RETRY: tuple[type[Exception], ...] = (ResourceExhausted,)
|
29
28
|
logging.getLogger("google.genai").setLevel(logging.ERROR)
|
30
29
|
os.environ["GRPC_VERBOSITY"] = "ERROR"
|
31
30
|
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
@@ -49,11 +48,11 @@ def _get_client():
|
|
49
48
|
|
50
49
|
class GeminiAPI(VendorBase):
|
51
50
|
used_for_structured_outputs: bool = True
|
52
|
-
exceptions_to_retry:
|
51
|
+
exceptions_to_retry: tuple[type[Exception], ...] = GEMINI_EXCEPTIONS_TO_RETRY
|
53
52
|
|
54
53
|
def __init__(
|
55
54
|
self,
|
56
|
-
exceptions_to_retry:
|
55
|
+
exceptions_to_retry: tuple[type[Exception], ...] = GEMINI_EXCEPTIONS_TO_RETRY,
|
57
56
|
used_for_structured_outputs: bool = False,
|
58
57
|
):
|
59
58
|
self.used_for_structured_outputs = used_for_structured_outputs
|
@@ -65,7 +64,7 @@ class GeminiAPI(VendorBase):
|
|
65
64
|
return model_name
|
66
65
|
|
67
66
|
@staticmethod
|
68
|
-
def _msg_to_contents(messages:
|
67
|
+
def _msg_to_contents(messages: list[dict[str, Any]]) -> list[types.Content]:
|
69
68
|
# contents, sys_instr = [], None
|
70
69
|
contents = []
|
71
70
|
for m in messages:
|
@@ -82,16 +81,12 @@ class GeminiAPI(VendorBase):
|
|
82
81
|
return contents
|
83
82
|
|
84
83
|
@staticmethod
|
85
|
-
def _tools_to_genai(tools:
|
84
|
+
def _tools_to_genai(tools: list[BaseTool]) -> list[types.Tool]:
|
86
85
|
"""Convert internal BaseTool → genai Tool."""
|
87
|
-
out:
|
86
|
+
out: list[types.Tool] = []
|
88
87
|
for t in tools:
|
89
88
|
# Assume t.to_gemini_tool() now correctly returns a FunctionDeclaration
|
90
|
-
|
91
|
-
if isinstance(t, dict):
|
92
|
-
func_decl = t
|
93
|
-
else:
|
94
|
-
func_decl = t.to_gemini_tool()
|
89
|
+
func_decl = t if isinstance(t, dict) else t.to_gemini_tool()
|
95
90
|
if not isinstance(func_decl, types.FunctionDeclaration):
|
96
91
|
# Or fetch schema parts if to_gemini_tool still returns dict
|
97
92
|
# This depends on BaseTool.to_gemini_tool implementation
|
@@ -106,15 +101,15 @@ class GeminiAPI(VendorBase):
|
|
106
101
|
|
107
102
|
async def _gen_content_async(
|
108
103
|
self,
|
109
|
-
messages:
|
104
|
+
messages: list[dict],
|
110
105
|
temperature: float,
|
111
106
|
model_name: str,
|
112
107
|
reasoning_effort: str,
|
113
|
-
tools:
|
114
|
-
lm_config:
|
115
|
-
) ->
|
108
|
+
tools: list[BaseTool] | None,
|
109
|
+
lm_config: dict[str, Any] | None,
|
110
|
+
) -> tuple[str, list[dict] | None]:
|
116
111
|
model_name = self.get_aliased_model_name(model_name)
|
117
|
-
cfg_kwargs:
|
112
|
+
cfg_kwargs: dict[str, Any] = {"temperature": temperature}
|
118
113
|
if model_name in GEMINI_REASONING_MODELS and reasoning_effort in GEMINI_THINKING_BUDGETS:
|
119
114
|
cfg_kwargs["thinking_config"] = types.ThinkingConfig(
|
120
115
|
thinking_budget=GEMINI_THINKING_BUDGETS[reasoning_effort]
|
@@ -141,15 +136,15 @@ class GeminiAPI(VendorBase):
|
|
141
136
|
|
142
137
|
def _gen_content_sync(
|
143
138
|
self,
|
144
|
-
messages:
|
139
|
+
messages: list[dict],
|
145
140
|
temperature: float,
|
146
141
|
model_name: str,
|
147
142
|
reasoning_effort: str,
|
148
|
-
tools:
|
149
|
-
lm_config:
|
150
|
-
) ->
|
143
|
+
tools: list[BaseTool] | None,
|
144
|
+
lm_config: dict[str, Any] | None,
|
145
|
+
) -> tuple[str, list[dict] | None]:
|
151
146
|
model_name = self.get_aliased_model_name(model_name)
|
152
|
-
cfg_kwargs:
|
147
|
+
cfg_kwargs: dict[str, Any] = {"temperature": temperature}
|
153
148
|
if model_name in GEMINI_REASONING_MODELS and reasoning_effort in GEMINI_THINKING_BUDGETS:
|
154
149
|
cfg_kwargs["thinking_config"] = types.ThinkingConfig(
|
155
150
|
thinking_budget=GEMINI_THINKING_BUDGETS[reasoning_effort]
|
@@ -174,7 +169,7 @@ class GeminiAPI(VendorBase):
|
|
174
169
|
return self._extract(resp)
|
175
170
|
|
176
171
|
@staticmethod
|
177
|
-
def _extract(response) ->
|
172
|
+
def _extract(response) -> tuple[str, list[dict] | None]:
|
178
173
|
# Extract text, handling cases where it might be missing
|
179
174
|
try:
|
180
175
|
text = response.text
|
@@ -208,13 +203,13 @@ class GeminiAPI(VendorBase):
|
|
208
203
|
async def _hit_api_async(
|
209
204
|
self,
|
210
205
|
model: str,
|
211
|
-
messages:
|
212
|
-
lm_config:
|
206
|
+
messages: list[dict[str, Any]],
|
207
|
+
lm_config: dict[str, Any],
|
213
208
|
use_ephemeral_cache_only: bool = False,
|
214
209
|
reasoning_effort: str = "high",
|
215
|
-
tools:
|
210
|
+
tools: list[BaseTool] | None = None,
|
216
211
|
) -> BaseLMResponse:
|
217
|
-
assert lm_config.get("response_model"
|
212
|
+
assert lm_config.get("response_model") is None, (
|
218
213
|
"response_model is not supported for standard calls"
|
219
214
|
)
|
220
215
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
@@ -257,13 +252,13 @@ class GeminiAPI(VendorBase):
|
|
257
252
|
def _hit_api_sync(
|
258
253
|
self,
|
259
254
|
model: str,
|
260
|
-
messages:
|
261
|
-
lm_config:
|
255
|
+
messages: list[dict[str, Any]],
|
256
|
+
lm_config: dict[str, Any],
|
262
257
|
use_ephemeral_cache_only: bool = False,
|
263
258
|
reasoning_effort: str = "high",
|
264
|
-
tools:
|
259
|
+
tools: list[BaseTool] | None = None,
|
265
260
|
) -> BaseLMResponse:
|
266
|
-
assert lm_config.get("response_model"
|
261
|
+
assert lm_config.get("response_model") is None, (
|
267
262
|
"response_model is not supported for standard calls"
|
268
263
|
)
|
269
264
|
used_cache_handler = get_cache_handler(use_ephemeral_cache_only=use_ephemeral_cache_only)
|
@@ -1,30 +1,30 @@
|
|
1
1
|
import json
|
2
2
|
import os
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
|
5
5
|
import pydantic
|
6
6
|
from mistralai import Mistral # use Mistral as both sync and async client
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
9
|
from synth_ai.lm.caching.initialize import get_cache_handler
|
10
|
+
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
|
10
11
|
from synth_ai.lm.tools.base import BaseTool
|
11
12
|
from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
|
12
|
-
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
|
13
13
|
from synth_ai.lm.vendors.core.openai_api import OpenAIStructuredOutputClient
|
14
14
|
|
15
15
|
# Since the mistralai package doesn't expose an exceptions module,
|
16
16
|
# we fallback to catching all Exceptions for retry.
|
17
|
-
MISTRAL_EXCEPTIONS_TO_RETRY:
|
17
|
+
MISTRAL_EXCEPTIONS_TO_RETRY: tuple[type[Exception], ...] = (Exception,)
|
18
18
|
|
19
19
|
|
20
20
|
class MistralAPI(VendorBase):
|
21
21
|
used_for_structured_outputs: bool = True
|
22
|
-
exceptions_to_retry:
|
22
|
+
exceptions_to_retry: tuple = MISTRAL_EXCEPTIONS_TO_RETRY
|
23
23
|
_openai_fallback: Any
|
24
24
|
|
25
25
|
def __init__(
|
26
26
|
self,
|
27
|
-
exceptions_to_retry:
|
27
|
+
exceptions_to_retry: tuple[type[Exception], ...] = MISTRAL_EXCEPTIONS_TO_RETRY,
|
28
28
|
used_for_structured_outputs: bool = False,
|
29
29
|
):
|
30
30
|
self.used_for_structured_outputs = used_for_structured_outputs
|
@@ -40,14 +40,14 @@ class MistralAPI(VendorBase):
|
|
40
40
|
async def _hit_api_async(
|
41
41
|
self,
|
42
42
|
model: str,
|
43
|
-
messages:
|
44
|
-
lm_config:
|
45
|
-
response_model:
|
43
|
+
messages: list[dict[str, Any]],
|
44
|
+
lm_config: dict[str, Any],
|
45
|
+
response_model: BaseModel | None = None,
|
46
46
|
use_ephemeral_cache_only: bool = False,
|
47
47
|
reasoning_effort: str = "high",
|
48
|
-
tools:
|
48
|
+
tools: list[BaseTool] | None = None,
|
49
49
|
) -> BaseLMResponse:
|
50
|
-
assert lm_config.get("response_model"
|
50
|
+
assert lm_config.get("response_model") is None, (
|
51
51
|
"response_model is not supported for standard calls"
|
52
52
|
)
|
53
53
|
assert not (response_model and tools), "Cannot provide both response_model and tools"
|
@@ -63,7 +63,7 @@ class MistralAPI(VendorBase):
|
|
63
63
|
], f"Expected BaseLMResponse or str, got {type(cache_result)}"
|
64
64
|
return (
|
65
65
|
cache_result
|
66
|
-
if
|
66
|
+
if isinstance(cache_result, BaseLMResponse)
|
67
67
|
else BaseLMResponse(
|
68
68
|
raw_response=cache_result, structured_output=None, tool_calls=None
|
69
69
|
)
|
@@ -130,14 +130,14 @@ class MistralAPI(VendorBase):
|
|
130
130
|
def _hit_api_sync(
|
131
131
|
self,
|
132
132
|
model: str,
|
133
|
-
messages:
|
134
|
-
lm_config:
|
135
|
-
response_model:
|
133
|
+
messages: list[dict[str, Any]],
|
134
|
+
lm_config: dict[str, Any],
|
135
|
+
response_model: BaseModel | None = None,
|
136
136
|
use_ephemeral_cache_only: bool = False,
|
137
137
|
reasoning_effort: str = "high",
|
138
|
-
tools:
|
138
|
+
tools: list[BaseTool] | None = None,
|
139
139
|
) -> BaseLMResponse:
|
140
|
-
assert lm_config.get("response_model"
|
140
|
+
assert lm_config.get("response_model") is None, (
|
141
141
|
"response_model is not supported for standard calls"
|
142
142
|
)
|
143
143
|
assert not (response_model and tools), "Cannot provide both response_model and tools"
|
@@ -154,7 +154,7 @@ class MistralAPI(VendorBase):
|
|
154
154
|
], f"Expected BaseLMResponse or str, got {type(cache_result)}"
|
155
155
|
return (
|
156
156
|
cache_result
|
157
|
-
if
|
157
|
+
if isinstance(cache_result, BaseLMResponse)
|
158
158
|
else BaseLMResponse(
|
159
159
|
raw_response=cache_result, structured_output=None, tool_calls=None
|
160
160
|
)
|
@@ -217,7 +217,7 @@ class MistralAPI(VendorBase):
|
|
217
217
|
async def _hit_api_async_structured_output(
|
218
218
|
self,
|
219
219
|
model: str,
|
220
|
-
messages:
|
220
|
+
messages: list[dict[str, Any]],
|
221
221
|
response_model: BaseModel,
|
222
222
|
temperature: float,
|
223
223
|
use_ephemeral_cache_only: bool = False,
|
@@ -256,7 +256,7 @@ class MistralAPI(VendorBase):
|
|
256
256
|
def _hit_api_sync_structured_output(
|
257
257
|
self,
|
258
258
|
model: str,
|
259
|
-
messages:
|
259
|
+
messages: list[dict[str, Any]],
|
260
260
|
response_model: BaseModel,
|
261
261
|
temperature: float,
|
262
262
|
use_ephemeral_cache_only: bool = False,
|
@@ -6,7 +6,8 @@ supporting both standard and structured output modes.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
import json
|
9
|
-
|
9
|
+
import os
|
10
|
+
from typing import Any
|
10
11
|
|
11
12
|
import openai
|
12
13
|
import pydantic_core
|
@@ -15,13 +16,13 @@ import pydantic_core
|
|
15
16
|
from pydantic import BaseModel
|
16
17
|
|
17
18
|
from synth_ai.lm.caching.initialize import get_cache_handler
|
19
|
+
from synth_ai.lm.constants import OPENAI_REASONING_MODELS, SPECIAL_BASE_TEMPS
|
18
20
|
from synth_ai.lm.tools.base import BaseTool
|
19
21
|
from synth_ai.lm.vendors.base import BaseLMResponse
|
20
|
-
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS, OPENAI_REASONING_MODELS
|
21
22
|
from synth_ai.lm.vendors.openai_standard import OpenAIStandard
|
22
23
|
|
23
24
|
# Exceptions that should trigger retry logic for OpenAI API calls
|
24
|
-
OPENAI_EXCEPTIONS_TO_RETRY:
|
25
|
+
OPENAI_EXCEPTIONS_TO_RETRY: tuple[type[Exception], ...] = (
|
25
26
|
pydantic_core._pydantic_core.ValidationError,
|
26
27
|
openai.OpenAIError,
|
27
28
|
openai.APIConnectionError,
|
@@ -36,33 +37,61 @@ OPENAI_EXCEPTIONS_TO_RETRY: Tuple[Type[Exception], ...] = (
|
|
36
37
|
class OpenAIStructuredOutputClient(OpenAIStandard):
|
37
38
|
"""
|
38
39
|
OpenAI client with support for structured outputs.
|
39
|
-
|
40
|
+
|
40
41
|
This client extends the standard OpenAI client to support structured outputs
|
41
42
|
using OpenAI's native structured output feature or response format parameter.
|
42
43
|
"""
|
44
|
+
|
43
45
|
def __init__(self, synth_logging: bool = True):
|
44
|
-
if
|
46
|
+
# Check if we should use Synth clients instead of OpenAI
|
47
|
+
openai_base = os.getenv("OPENAI_API_BASE", "")
|
48
|
+
use_synth = (openai_base.startswith("https://synth") or
|
49
|
+
openai_base.startswith("https://agent-learning") or
|
50
|
+
os.getenv("SYNTH_BASE_URL") or os.getenv("MODAL_BASE_URL"))
|
51
|
+
|
52
|
+
if use_synth:
|
53
|
+
# Use Synth clients for Synth endpoints
|
54
|
+
from synth_ai.lm.vendors.synth_client import AsyncSynthClient, SyncSynthClient
|
55
|
+
from synth_ai.lm.config import SynthConfig
|
56
|
+
|
57
|
+
# Create config from OPENAI_* environment variables if available
|
58
|
+
openai_base = os.getenv("OPENAI_API_BASE")
|
59
|
+
openai_key = os.getenv("OPENAI_API_KEY")
|
60
|
+
|
61
|
+
if openai_base and openai_key:
|
62
|
+
config = SynthConfig(base_url=openai_base, api_key=openai_key)
|
63
|
+
sync_client = SyncSynthClient(config)
|
64
|
+
async_client = AsyncSynthClient(config)
|
65
|
+
else:
|
66
|
+
# Fall back to default config loading
|
67
|
+
sync_client = SyncSynthClient()
|
68
|
+
async_client = AsyncSynthClient()
|
69
|
+
elif synth_logging:
|
45
70
|
# print("Using synth logging - OpenAIStructuredOutputClient")
|
46
71
|
from synth_ai.lm.provider_support.openai import AsyncOpenAI, OpenAI
|
72
|
+
sync_client = OpenAI()
|
73
|
+
async_client = AsyncOpenAI()
|
47
74
|
else:
|
48
75
|
# print("Not using synth logging - OpenAIStructuredOutputClient")
|
49
76
|
from openai import AsyncOpenAI, OpenAI
|
77
|
+
sync_client = OpenAI()
|
78
|
+
async_client = AsyncOpenAI()
|
50
79
|
|
51
80
|
super().__init__(
|
52
81
|
used_for_structured_outputs=True,
|
53
82
|
exceptions_to_retry=OPENAI_EXCEPTIONS_TO_RETRY,
|
54
|
-
sync_client=
|
55
|
-
async_client=
|
83
|
+
sync_client=sync_client,
|
84
|
+
async_client=async_client,
|
56
85
|
)
|
57
86
|
|
58
87
|
async def _hit_api_async_structured_output(
|
59
88
|
self,
|
60
89
|
model: str,
|
61
|
-
messages:
|
90
|
+
messages: list[dict[str, Any]],
|
62
91
|
response_model: BaseModel,
|
63
92
|
temperature: float,
|
64
93
|
use_ephemeral_cache_only: bool = False,
|
65
|
-
tools:
|
94
|
+
tools: list[BaseTool] | None = None,
|
66
95
|
reasoning_effort: str = "high",
|
67
96
|
) -> str:
|
68
97
|
if tools:
|
@@ -81,7 +110,7 @@ class OpenAIStructuredOutputClient(OpenAIStandard):
|
|
81
110
|
dict,
|
82
111
|
BaseLMResponse,
|
83
112
|
], f"Expected dict or BaseLMResponse, got {type(cache_result)}"
|
84
|
-
return cache_result["response"] if
|
113
|
+
return cache_result["response"] if isinstance(cache_result, dict) else cache_result
|
85
114
|
if model in OPENAI_REASONING_MODELS:
|
86
115
|
output = await self.async_client.beta.chat.completions.parse(
|
87
116
|
model=model,
|
@@ -109,11 +138,11 @@ class OpenAIStructuredOutputClient(OpenAIStandard):
|
|
109
138
|
def _hit_api_sync_structured_output(
|
110
139
|
self,
|
111
140
|
model: str,
|
112
|
-
messages:
|
141
|
+
messages: list[dict[str, Any]],
|
113
142
|
response_model: BaseModel,
|
114
143
|
temperature: float,
|
115
144
|
use_ephemeral_cache_only: bool = False,
|
116
|
-
tools:
|
145
|
+
tools: list[BaseTool] | None = None,
|
117
146
|
reasoning_effort: str = "high",
|
118
147
|
) -> str:
|
119
148
|
if tools:
|
@@ -130,7 +159,7 @@ class OpenAIStructuredOutputClient(OpenAIStandard):
|
|
130
159
|
dict,
|
131
160
|
BaseLMResponse,
|
132
161
|
], f"Expected dict or BaseLMResponse, got {type(cache_result)}"
|
133
|
-
return cache_result["response"] if
|
162
|
+
return cache_result["response"] if isinstance(cache_result, dict) else cache_result
|
134
163
|
if model in OPENAI_REASONING_MODELS:
|
135
164
|
output = self.sync_client.beta.chat.completions.parse(
|
136
165
|
model=model,
|