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
synth_ai/lm/overrides.py
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from contextlib import contextmanager
|
4
|
-
from typing import Any, Dict, List, Optional, Tuple
|
5
3
|
import contextvars
|
4
|
+
from contextlib import contextmanager
|
5
|
+
from typing import Any
|
6
6
|
|
7
7
|
from synth_ai.lm.injection import (
|
8
|
-
set_injection_rules,
|
9
|
-
clear_injection_rules,
|
10
8
|
apply_injection as _apply_injection,
|
11
9
|
)
|
10
|
+
from synth_ai.lm.injection import (
|
11
|
+
clear_injection_rules,
|
12
|
+
set_injection_rules,
|
13
|
+
)
|
12
14
|
|
13
15
|
# Context to hold a list of override specs to evaluate per-call
|
14
16
|
# Each spec shape (minimal v1):
|
@@ -18,29 +20,29 @@ from synth_ai.lm.injection import (
|
|
18
20
|
# "params": { ... api params to override ... },
|
19
21
|
# "tools": { ... optional tools overrides ... },
|
20
22
|
# }
|
21
|
-
_override_specs_ctx: contextvars.ContextVar[
|
22
|
-
"override_specs", default=None
|
23
|
+
_override_specs_ctx: contextvars.ContextVar[list[dict[str, Any]] | None] = (
|
24
|
+
contextvars.ContextVar("override_specs", default=None)
|
23
25
|
)
|
24
26
|
|
25
27
|
# ContextVars actually applied for the specific call once matched
|
26
|
-
_param_overrides_ctx: contextvars.ContextVar[
|
28
|
+
_param_overrides_ctx: contextvars.ContextVar[dict[str, Any] | None] = contextvars.ContextVar(
|
27
29
|
"param_overrides", default=None
|
28
30
|
)
|
29
|
-
_tool_overrides_ctx: contextvars.ContextVar[
|
31
|
+
_tool_overrides_ctx: contextvars.ContextVar[dict[str, Any] | None] = contextvars.ContextVar(
|
30
32
|
"tool_overrides", default=None
|
31
33
|
)
|
32
|
-
_current_override_label_ctx: contextvars.ContextVar[
|
34
|
+
_current_override_label_ctx: contextvars.ContextVar[str | None] = contextvars.ContextVar(
|
33
35
|
"override_label", default=None
|
34
36
|
)
|
35
37
|
|
36
38
|
|
37
|
-
def set_override_specs(specs:
|
39
|
+
def set_override_specs(specs: list[dict[str, Any]]):
|
38
40
|
if not isinstance(specs, list):
|
39
41
|
raise ValueError("override specs must be a list of dicts")
|
40
42
|
return _override_specs_ctx.set(specs)
|
41
43
|
|
42
44
|
|
43
|
-
def get_override_specs() ->
|
45
|
+
def get_override_specs() -> list[dict[str, Any]] | None:
|
44
46
|
return _override_specs_ctx.get()
|
45
47
|
|
46
48
|
|
@@ -48,7 +50,7 @@ def clear_override_specs(token) -> None:
|
|
48
50
|
_override_specs_ctx.reset(token)
|
49
51
|
|
50
52
|
|
51
|
-
def _matches(spec:
|
53
|
+
def _matches(spec: dict[str, Any], messages: list[dict[str, Any]]) -> bool:
|
52
54
|
match = spec.get("match") or {}
|
53
55
|
contains = match.get("contains")
|
54
56
|
role = match.get("role") # optional
|
@@ -69,7 +71,7 @@ def _matches(spec: Dict[str, Any], messages: List[Dict[str, Any]]) -> bool:
|
|
69
71
|
return False
|
70
72
|
|
71
73
|
|
72
|
-
def resolve_override_for_messages(messages:
|
74
|
+
def resolve_override_for_messages(messages: list[dict[str, Any]]) -> dict[str, Any] | None:
|
73
75
|
specs = get_override_specs() or []
|
74
76
|
for spec in specs:
|
75
77
|
try:
|
@@ -81,12 +83,12 @@ def resolve_override_for_messages(messages: List[Dict[str, Any]]) -> Optional[Di
|
|
81
83
|
return None
|
82
84
|
|
83
85
|
|
84
|
-
def apply_injection(messages:
|
86
|
+
def apply_injection(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
85
87
|
# Delegate to injection.apply_injection
|
86
88
|
return _apply_injection(messages)
|
87
89
|
|
88
90
|
|
89
|
-
def apply_param_overrides(api_params:
|
91
|
+
def apply_param_overrides(api_params: dict[str, Any]) -> dict[str, Any]:
|
90
92
|
ov = _param_overrides_ctx.get()
|
91
93
|
if not ov:
|
92
94
|
return api_params
|
@@ -96,7 +98,7 @@ def apply_param_overrides(api_params: Dict[str, Any]) -> Dict[str, Any]:
|
|
96
98
|
return api_params
|
97
99
|
|
98
100
|
|
99
|
-
def apply_tool_overrides(api_params:
|
101
|
+
def apply_tool_overrides(api_params: dict[str, Any]) -> dict[str, Any]:
|
100
102
|
"""Apply tool overrides to OpenAI/Anthropic-like api_params in place.
|
101
103
|
|
102
104
|
Supports keys under spec["tools"]:
|
@@ -138,7 +140,7 @@ def apply_tool_overrides(api_params: Dict[str, Any]) -> Dict[str, Any]:
|
|
138
140
|
|
139
141
|
|
140
142
|
@contextmanager
|
141
|
-
def use_overrides_for_messages(messages:
|
143
|
+
def use_overrides_for_messages(messages: list[dict[str, Any]]):
|
142
144
|
"""Resolve an override spec against messages and apply its contexts within the scope.
|
143
145
|
|
144
146
|
- Sets injection rules and param overrides if present on the matched spec.
|
@@ -174,7 +176,7 @@ def use_overrides_for_messages(messages: List[Dict[str, Any]]):
|
|
174
176
|
_current_override_label_ctx.reset(label_tok)
|
175
177
|
|
176
178
|
|
177
|
-
def get_current_override_label() ->
|
179
|
+
def get_current_override_label() -> str | None:
|
178
180
|
return _current_override_label_ctx.get()
|
179
181
|
|
180
182
|
|
@@ -189,7 +191,7 @@ class LMOverridesContext:
|
|
189
191
|
run_pipeline()
|
190
192
|
"""
|
191
193
|
|
192
|
-
def __init__(self, override_specs:
|
194
|
+
def __init__(self, override_specs: list[dict[str, Any]] | None | dict[str, Any] = None):
|
193
195
|
if isinstance(override_specs, dict):
|
194
196
|
override_specs = [override_specs]
|
195
197
|
self._specs = override_specs or []
|
@@ -2,7 +2,7 @@
|
|
2
2
|
Provider support for LLM services with integrated tracing.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from .openai import OpenAI, AsyncOpenAI
|
6
5
|
from .anthropic import Anthropic, AsyncAnthropic
|
6
|
+
from .openai import AsyncOpenAI, OpenAI
|
7
7
|
|
8
8
|
__all__ = ["OpenAI", "AsyncOpenAI", "Anthropic", "AsyncAnthropic"]
|
@@ -6,14 +6,13 @@ Analogous to the modified OpenAI version.
|
|
6
6
|
import logging
|
7
7
|
import types
|
8
8
|
from dataclasses import dataclass
|
9
|
-
from typing import Optional
|
10
9
|
|
11
10
|
try:
|
12
11
|
import anthropic
|
13
|
-
except ImportError:
|
12
|
+
except ImportError as err:
|
14
13
|
raise ModuleNotFoundError(
|
15
14
|
"Please install anthropic to use this feature: 'pip install anthropic'"
|
16
|
-
)
|
15
|
+
) from err
|
17
16
|
|
18
17
|
try:
|
19
18
|
from anthropic import AsyncClient, Client
|
@@ -27,14 +26,15 @@ from langfuse.decorators import langfuse_context
|
|
27
26
|
from langfuse.utils import _get_timestamp
|
28
27
|
from langfuse.utils.langfuse_singleton import LangfuseSingleton
|
29
28
|
from wrapt import wrap_function_wrapper
|
29
|
+
|
30
30
|
from synth_ai.lm.overrides import (
|
31
|
-
use_overrides_for_messages,
|
32
31
|
apply_injection as apply_injection_overrides,
|
32
|
+
)
|
33
|
+
from synth_ai.lm.overrides import (
|
33
34
|
apply_param_overrides,
|
34
35
|
apply_tool_overrides,
|
36
|
+
use_overrides_for_messages,
|
35
37
|
)
|
36
|
-
from synth_ai.lm.injection import apply_injection
|
37
|
-
|
38
38
|
from synth_ai.lm.provider_support.suppress_logging import *
|
39
39
|
from synth_ai.tracing_v1.trackers import (
|
40
40
|
synth_tracker_async,
|
@@ -799,7 +799,7 @@ class LangfuseAnthropicResponseGeneratorAsync:
|
|
799
799
|
|
800
800
|
|
801
801
|
class AnthropicLangfuse:
|
802
|
-
_langfuse:
|
802
|
+
_langfuse: Langfuse | None = None
|
803
803
|
|
804
804
|
def initialize(self):
|
805
805
|
self._langfuse = LangfuseSingleton().get(
|
@@ -946,14 +946,14 @@ class AnthropicLangfuse:
|
|
946
946
|
|
947
947
|
anthropic.AsyncClient.__init__ = new_async_init
|
948
948
|
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
949
|
+
anthropic.langfuse_public_key = None
|
950
|
+
anthropic.langfuse_secret_key = None
|
951
|
+
anthropic.langfuse_host = None
|
952
|
+
anthropic.langfuse_debug = None
|
953
|
+
anthropic.langfuse_enabled = True
|
954
|
+
anthropic.langfuse_sample_rate = None
|
955
|
+
anthropic.langfuse_auth_check = self.langfuse_auth_check
|
956
|
+
anthropic.flush_langfuse = self.flush
|
957
957
|
|
958
958
|
|
959
959
|
modifier = AnthropicLangfuse()
|
@@ -4,7 +4,6 @@ import types
|
|
4
4
|
from collections import defaultdict
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from inspect import isclass
|
7
|
-
from typing import List, Optional
|
8
7
|
|
9
8
|
import openai.resources
|
10
9
|
from langfuse import Langfuse
|
@@ -15,22 +14,25 @@ from langfuse.utils.langfuse_singleton import LangfuseSingleton
|
|
15
14
|
from packaging.version import Version
|
16
15
|
from pydantic import BaseModel
|
17
16
|
from wrapt import wrap_function_wrapper
|
17
|
+
|
18
18
|
from synth_ai.lm.overrides import (
|
19
|
-
use_overrides_for_messages,
|
20
19
|
apply_injection as apply_injection_overrides,
|
20
|
+
)
|
21
|
+
from synth_ai.lm.overrides import (
|
21
22
|
apply_param_overrides,
|
22
23
|
apply_tool_overrides,
|
24
|
+
use_overrides_for_messages,
|
23
25
|
)
|
24
|
-
from synth_ai.lm.injection import apply_injection
|
25
|
-
|
26
26
|
from synth_ai.lm.provider_support.suppress_logging import *
|
27
27
|
from synth_ai.tracing_v1.abstractions import MessageInputs
|
28
28
|
from synth_ai.tracing_v1.trackers import synth_tracker_async, synth_tracker_sync
|
29
29
|
|
30
30
|
try:
|
31
31
|
import openai
|
32
|
-
except ImportError:
|
33
|
-
raise ModuleNotFoundError(
|
32
|
+
except ImportError as err:
|
33
|
+
raise ModuleNotFoundError(
|
34
|
+
"Please install OpenAI to use this feature: 'pip install openai'"
|
35
|
+
) from err
|
34
36
|
|
35
37
|
# CREDIT TO LANGFUSE FOR OPEN-SOURCING THE CODE THAT THIS IS BASED ON
|
36
38
|
# USING WITH MIT LICENSE PERMISSION
|
@@ -59,7 +61,7 @@ class OpenAiDefinition:
|
|
59
61
|
method: str
|
60
62
|
type: str
|
61
63
|
sync: bool
|
62
|
-
min_version:
|
64
|
+
min_version: str | None = None
|
63
65
|
|
64
66
|
|
65
67
|
OPENAI_METHODS_V0 = [
|
@@ -212,7 +214,7 @@ def _extract_chat_response(kwargs: dict):
|
|
212
214
|
Extracts the LLM output from the response.
|
213
215
|
"""
|
214
216
|
response = {
|
215
|
-
"role": kwargs.get("role"
|
217
|
+
"role": kwargs.get("role"),
|
216
218
|
}
|
217
219
|
|
218
220
|
if kwargs.get("function_call") is not None:
|
@@ -221,7 +223,7 @@ def _extract_chat_response(kwargs: dict):
|
|
221
223
|
if kwargs.get("tool_calls") is not None:
|
222
224
|
response.update({"tool_calls": kwargs["tool_calls"]})
|
223
225
|
|
224
|
-
response["content"] = kwargs.get("content"
|
226
|
+
response["content"] = kwargs.get("content")
|
225
227
|
return response
|
226
228
|
|
227
229
|
|
@@ -418,7 +420,7 @@ def _extract_streamed_openai_response(resource, chunks):
|
|
418
420
|
usage = chunk_usage
|
419
421
|
|
420
422
|
# Process choices
|
421
|
-
choices = chunk.get("choices", [])
|
423
|
+
choices = chunk.get("choices", []) # noqa: F841
|
422
424
|
# logger.debug(f"Extracted - model: {model}, choices: {choices}")
|
423
425
|
|
424
426
|
# logger.debug(f"Final completion: {completion}")
|
@@ -762,7 +764,7 @@ async def _wrap_async(open_ai_resource: OpenAiDefinition, initialize, wrapped, a
|
|
762
764
|
|
763
765
|
|
764
766
|
class OpenAILangfuse:
|
765
|
-
_langfuse:
|
767
|
+
_langfuse: Langfuse | None = None
|
766
768
|
|
767
769
|
def initialize(self):
|
768
770
|
self._langfuse = LangfuseSingleton().get(
|
@@ -820,15 +822,15 @@ class OpenAILangfuse:
|
|
820
822
|
else _wrap_async(resource, self.initialize),
|
821
823
|
)
|
822
824
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
825
|
+
openai.langfuse_public_key = None
|
826
|
+
openai.langfuse_secret_key = None
|
827
|
+
openai.langfuse_host = None
|
828
|
+
openai.langfuse_debug = None
|
829
|
+
openai.langfuse_enabled = True
|
830
|
+
openai.langfuse_sample_rate = None
|
831
|
+
openai.langfuse_mask = None
|
832
|
+
openai.langfuse_auth_check = self.langfuse_auth_check
|
833
|
+
openai.flush_langfuse = self.flush
|
832
834
|
|
833
835
|
|
834
836
|
modifier = OpenAILangfuse()
|
@@ -843,7 +845,7 @@ def auth_check():
|
|
843
845
|
return modifier._langfuse.auth_check()
|
844
846
|
|
845
847
|
|
846
|
-
def _filter_image_data(messages:
|
848
|
+
def _filter_image_data(messages: list[dict]):
|
847
849
|
"""https://platform.openai.com/docs/guides/vision?lang=python
|
848
850
|
|
849
851
|
The messages array remains the same, but the 'image_url' is removed from the 'content' array.
|
@@ -8,10 +8,12 @@ in the requested structured format (Pydantic models).
|
|
8
8
|
import logging
|
9
9
|
import time
|
10
10
|
from abc import ABC, abstractmethod
|
11
|
-
from
|
11
|
+
from collections.abc import Callable
|
12
|
+
from typing import Any, Literal
|
12
13
|
|
13
14
|
from pydantic import BaseModel
|
14
15
|
|
16
|
+
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
|
15
17
|
from synth_ai.lm.core.exceptions import StructuredOutputCoercionFailureException
|
16
18
|
from synth_ai.lm.structured_outputs.inject import (
|
17
19
|
inject_structured_output_instructions,
|
@@ -22,7 +24,6 @@ from synth_ai.lm.structured_outputs.rehabilitate import (
|
|
22
24
|
pull_out_structured_output,
|
23
25
|
)
|
24
26
|
from synth_ai.lm.vendors.base import BaseLMResponse, VendorBase
|
25
|
-
from synth_ai.lm.constants import SPECIAL_BASE_TEMPS
|
26
27
|
|
27
28
|
logger = logging.getLogger(__name__)
|
28
29
|
|
@@ -30,26 +31,27 @@ logger = logging.getLogger(__name__)
|
|
30
31
|
class StructuredHandlerBase(ABC):
|
31
32
|
"""
|
32
33
|
Abstract base class for structured output handlers.
|
33
|
-
|
34
|
+
|
34
35
|
Handles the logic for ensuring language models return properly formatted
|
35
36
|
structured outputs, with retry logic and error handling.
|
36
|
-
|
37
|
+
|
37
38
|
Attributes:
|
38
39
|
core_client: Primary vendor client for API calls
|
39
40
|
retry_client: Client used for retry attempts (may use different model)
|
40
41
|
handler_params: Configuration parameters including retry count
|
41
42
|
structured_output_mode: Either "stringified_json" or "forced_json"
|
42
43
|
"""
|
44
|
+
|
43
45
|
core_client: VendorBase
|
44
46
|
retry_client: VendorBase
|
45
|
-
handler_params:
|
47
|
+
handler_params: dict[str, Any]
|
46
48
|
structured_output_mode: Literal["stringified_json", "forced_json"]
|
47
49
|
|
48
50
|
def __init__(
|
49
51
|
self,
|
50
52
|
core_client: VendorBase,
|
51
53
|
retry_client: VendorBase,
|
52
|
-
handler_params:
|
54
|
+
handler_params: dict[str, Any] | None = None,
|
53
55
|
structured_output_mode: Literal["stringified_json", "forced_json"] = "stringified_json",
|
54
56
|
):
|
55
57
|
self.core_client = core_client
|
@@ -59,7 +61,7 @@ class StructuredHandlerBase(ABC):
|
|
59
61
|
|
60
62
|
async def call_async(
|
61
63
|
self,
|
62
|
-
messages:
|
64
|
+
messages: list[dict[str, Any]],
|
63
65
|
model: str,
|
64
66
|
response_model: BaseModel,
|
65
67
|
temperature: float = 0.0,
|
@@ -74,7 +76,7 @@ class StructuredHandlerBase(ABC):
|
|
74
76
|
model=model,
|
75
77
|
response_model=response_model,
|
76
78
|
api_call_method=self.core_client._hit_api_async_structured_output
|
77
|
-
if (
|
79
|
+
if (response_model and self.structured_output_mode == "forced_json")
|
78
80
|
else self.core_client._hit_api_async,
|
79
81
|
temperature=temperature,
|
80
82
|
use_ephemeral_cache_only=use_ephemeral_cache_only,
|
@@ -83,7 +85,7 @@ class StructuredHandlerBase(ABC):
|
|
83
85
|
|
84
86
|
def call_sync(
|
85
87
|
self,
|
86
|
-
messages:
|
88
|
+
messages: list[dict[str, Any]],
|
87
89
|
response_model: BaseModel,
|
88
90
|
model: str,
|
89
91
|
temperature: float = 0.0,
|
@@ -97,7 +99,7 @@ class StructuredHandlerBase(ABC):
|
|
97
99
|
model=model,
|
98
100
|
response_model=response_model,
|
99
101
|
api_call_method=self.core_client._hit_api_sync_structured_output
|
100
|
-
if (
|
102
|
+
if (response_model and self.structured_output_mode == "forced_json")
|
101
103
|
else self.core_client._hit_api_sync,
|
102
104
|
temperature=temperature,
|
103
105
|
use_ephemeral_cache_only=use_ephemeral_cache_only,
|
@@ -107,7 +109,7 @@ class StructuredHandlerBase(ABC):
|
|
107
109
|
@abstractmethod
|
108
110
|
async def _process_call_async(
|
109
111
|
self,
|
110
|
-
messages:
|
112
|
+
messages: list[dict[str, Any]],
|
111
113
|
model: str,
|
112
114
|
response_model: BaseModel,
|
113
115
|
api_call_method,
|
@@ -119,7 +121,7 @@ class StructuredHandlerBase(ABC):
|
|
119
121
|
@abstractmethod
|
120
122
|
def _process_call_sync(
|
121
123
|
self,
|
122
|
-
messages:
|
124
|
+
messages: list[dict[str, Any]],
|
123
125
|
model: str,
|
124
126
|
response_model: BaseModel,
|
125
127
|
api_call_method,
|
@@ -132,24 +134,24 @@ class StructuredHandlerBase(ABC):
|
|
132
134
|
class StringifiedJSONHandler(StructuredHandlerBase):
|
133
135
|
core_client: VendorBase
|
134
136
|
retry_client: VendorBase
|
135
|
-
handler_params:
|
137
|
+
handler_params: dict[str, Any]
|
136
138
|
|
137
139
|
def __init__(
|
138
140
|
self,
|
139
141
|
core_client: VendorBase,
|
140
142
|
retry_client: VendorBase,
|
141
|
-
handler_params:
|
143
|
+
handler_params: dict[str, Any] | None = None,
|
142
144
|
):
|
143
145
|
super().__init__(
|
144
146
|
core_client,
|
145
147
|
retry_client,
|
146
|
-
handler_params,
|
148
|
+
handler_params or {"retries": 3},
|
147
149
|
structured_output_mode="stringified_json",
|
148
150
|
)
|
149
151
|
|
150
152
|
async def _process_call_async(
|
151
153
|
self,
|
152
|
-
messages:
|
154
|
+
messages: list[dict[str, Any]],
|
153
155
|
model: str,
|
154
156
|
response_model: BaseModel,
|
155
157
|
temperature: float,
|
@@ -170,7 +172,7 @@ class StringifiedJSONHandler(StructuredHandlerBase):
|
|
170
172
|
response_model=response_model,
|
171
173
|
previously_failed_error_messages=previously_failed_error_messages,
|
172
174
|
)
|
173
|
-
t0 = time.time()
|
175
|
+
# t0 = time.time() # unused
|
174
176
|
raw_text_response_or_cached_hit = await api_call_method(
|
175
177
|
messages=messages_with_json_formatting_instructions,
|
176
178
|
model=model,
|
@@ -184,7 +186,7 @@ class StringifiedJSONHandler(StructuredHandlerBase):
|
|
184
186
|
assert type(raw_text_response_or_cached_hit) in [str, BaseLMResponse], (
|
185
187
|
f"Expected str or BaseLMResponse, got {type(raw_text_response_or_cached_hit)}"
|
186
188
|
)
|
187
|
-
if
|
189
|
+
if isinstance(raw_text_response_or_cached_hit, BaseLMResponse):
|
188
190
|
# print("Got cached hit, returning directly")
|
189
191
|
raw_text_response = raw_text_response_or_cached_hit.raw_response
|
190
192
|
else:
|
@@ -242,7 +244,7 @@ class StringifiedJSONHandler(StructuredHandlerBase):
|
|
242
244
|
|
243
245
|
def _process_call_sync(
|
244
246
|
self,
|
245
|
-
messages:
|
247
|
+
messages: list[dict[str, Any]],
|
246
248
|
model: str,
|
247
249
|
response_model: BaseModel,
|
248
250
|
temperature: float,
|
@@ -277,7 +279,7 @@ class StringifiedJSONHandler(StructuredHandlerBase):
|
|
277
279
|
assert type(raw_text_response_or_cached_hit) in [str, BaseLMResponse], (
|
278
280
|
f"Expected str or BaseLMResponse, got {type(raw_text_response_or_cached_hit)}"
|
279
281
|
)
|
280
|
-
if
|
282
|
+
if isinstance(raw_text_response_or_cached_hit, BaseLMResponse):
|
281
283
|
logger.info("Got cached hit, returning directly")
|
282
284
|
raw_text_response = raw_text_response_or_cached_hit.raw_response
|
283
285
|
else:
|
@@ -320,26 +322,26 @@ class StringifiedJSONHandler(StructuredHandlerBase):
|
|
320
322
|
class ForcedJSONHandler(StructuredHandlerBase):
|
321
323
|
core_client: VendorBase
|
322
324
|
retry_client: VendorBase
|
323
|
-
handler_params:
|
325
|
+
handler_params: dict[str, Any]
|
324
326
|
|
325
327
|
def __init__(
|
326
328
|
self,
|
327
329
|
core_client: VendorBase,
|
328
330
|
retry_client: VendorBase,
|
329
|
-
handler_params:
|
331
|
+
handler_params: dict[str, Any] | None = None,
|
330
332
|
reasoning_effort: str = "high",
|
331
333
|
):
|
332
334
|
super().__init__(
|
333
335
|
core_client,
|
334
336
|
retry_client,
|
335
|
-
handler_params,
|
337
|
+
handler_params or {"retries": 3},
|
336
338
|
structured_output_mode="forced_json",
|
337
339
|
)
|
338
340
|
self.reasoning_effort = reasoning_effort
|
339
341
|
|
340
342
|
async def _process_call_async(
|
341
343
|
self,
|
342
|
-
messages:
|
344
|
+
messages: list[dict[str, Any]],
|
343
345
|
model: str,
|
344
346
|
response_model: BaseModel,
|
345
347
|
api_call_method: Callable,
|
@@ -360,7 +362,7 @@ class ForcedJSONHandler(StructuredHandlerBase):
|
|
360
362
|
|
361
363
|
def _process_call_sync(
|
362
364
|
self,
|
363
|
-
messages:
|
365
|
+
messages: list[dict[str, Any]],
|
364
366
|
model: str,
|
365
367
|
response_model: BaseModel,
|
366
368
|
api_call_method: Callable,
|
@@ -380,16 +382,16 @@ class ForcedJSONHandler(StructuredHandlerBase):
|
|
380
382
|
|
381
383
|
|
382
384
|
class StructuredOutputHandler:
|
383
|
-
handler:
|
385
|
+
handler: StringifiedJSONHandler | ForcedJSONHandler
|
384
386
|
mode: Literal["stringified_json", "forced_json"]
|
385
|
-
handler_params:
|
387
|
+
handler_params: dict[str, Any]
|
386
388
|
|
387
389
|
def __init__(
|
388
390
|
self,
|
389
391
|
core_client: VendorBase,
|
390
392
|
retry_client: VendorBase,
|
391
393
|
mode: Literal["stringified_json", "forced_json"],
|
392
|
-
handler_params:
|
394
|
+
handler_params: dict[str, Any] = {},
|
393
395
|
):
|
394
396
|
self.mode = mode
|
395
397
|
if self.mode == "stringified_json":
|
@@ -402,11 +404,11 @@ class StructuredOutputHandler:
|
|
402
404
|
|
403
405
|
async def call_async(
|
404
406
|
self,
|
405
|
-
messages:
|
407
|
+
messages: list[dict[str, Any]],
|
406
408
|
model: str,
|
407
409
|
response_model: BaseModel,
|
408
410
|
use_ephemeral_cache_only: bool = False,
|
409
|
-
lm_config:
|
411
|
+
lm_config: dict[str, Any] = {},
|
410
412
|
reasoning_effort: str = "high",
|
411
413
|
) -> BaseLMResponse:
|
412
414
|
# print("Output handler call async")
|
@@ -421,11 +423,11 @@ class StructuredOutputHandler:
|
|
421
423
|
|
422
424
|
def call_sync(
|
423
425
|
self,
|
424
|
-
messages:
|
426
|
+
messages: list[dict[str, Any]],
|
425
427
|
model: str,
|
426
428
|
response_model: BaseModel,
|
427
429
|
use_ephemeral_cache_only: bool = False,
|
428
|
-
lm_config:
|
430
|
+
lm_config: dict[str, Any] = {},
|
429
431
|
reasoning_effort: str = "high",
|
430
432
|
) -> BaseLMResponse:
|
431
433
|
return self.handler.call_sync(
|