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
@@ -6,43 +6,47 @@ based on model names or explicit provider specifications.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
import re
|
9
|
-
from
|
9
|
+
from re import Pattern
|
10
|
+
from typing import Any
|
10
11
|
|
11
12
|
from synth_ai.lm.core.all import (
|
12
13
|
AnthropicClient,
|
14
|
+
CustomEndpointClient,
|
13
15
|
DeepSeekClient,
|
14
16
|
GeminiClient,
|
15
|
-
GroqClient,
|
16
17
|
GrokClient,
|
18
|
+
GroqClient,
|
17
19
|
# OpenAIClient,
|
18
20
|
OpenAIStructuredOutputClient,
|
19
|
-
TogetherClient,
|
20
|
-
CustomEndpointClient,
|
21
21
|
OpenRouterClient,
|
22
|
+
TogetherClient,
|
22
23
|
)
|
24
|
+
from synth_ai.lm.core.synth_models import SYNTH_SUPPORTED_MODELS
|
23
25
|
|
24
26
|
# Regular expressions to match model names to their respective providers
|
25
|
-
openai_naming_regexes:
|
27
|
+
openai_naming_regexes: list[Pattern] = [
|
26
28
|
re.compile(r"^(ft:)?(o[1,3,4](-.*)?|gpt-.*)$"),
|
27
29
|
]
|
28
|
-
openai_formatting_model_regexes:
|
30
|
+
openai_formatting_model_regexes: list[Pattern] = [
|
29
31
|
re.compile(r"^(ft:)?gpt-4o(-.*)?$"),
|
30
32
|
]
|
31
|
-
anthropic_naming_regexes:
|
33
|
+
anthropic_naming_regexes: list[Pattern] = [
|
32
34
|
re.compile(r"^claude-.*$"),
|
33
35
|
]
|
34
|
-
gemini_naming_regexes:
|
36
|
+
gemini_naming_regexes: list[Pattern] = [
|
35
37
|
re.compile(r"^gemini-.*$"),
|
36
38
|
re.compile(r"^gemma[2-9].*$"),
|
37
39
|
]
|
38
|
-
deepseek_naming_regexes:
|
40
|
+
deepseek_naming_regexes: list[Pattern] = [
|
39
41
|
re.compile(r"^deepseek-.*$"),
|
40
42
|
]
|
41
|
-
|
42
|
-
|
43
|
+
# Synth-specific model patterns (Qwen3 and fine-tuned models)
|
44
|
+
synth_naming_regexes: list[Pattern] = [
|
45
|
+
re.compile(r"^ft:.*$"), # Fine-tuned models (ft:model-name)
|
46
|
+
re.compile(r"^Qwen/Qwen3.*$"), # Qwen3 models specifically (Qwen/Qwen3-*)
|
43
47
|
]
|
44
48
|
|
45
|
-
groq_naming_regexes:
|
49
|
+
groq_naming_regexes: list[Pattern] = [
|
46
50
|
re.compile(r"^llama-3.3-70b-versatile$"),
|
47
51
|
re.compile(r"^llama-3.1-8b-instant$"),
|
48
52
|
re.compile(r"^qwen-2.5-32b$"),
|
@@ -60,7 +64,7 @@ groq_naming_regexes: List[Pattern] = [
|
|
60
64
|
re.compile(r"^moonshotai/kimi-k2-instruct$"),
|
61
65
|
]
|
62
66
|
|
63
|
-
grok_naming_regexes:
|
67
|
+
grok_naming_regexes: list[Pattern] = [
|
64
68
|
re.compile(r"^grok-3-beta$"),
|
65
69
|
re.compile(r"^grok-3-mini-beta$"),
|
66
70
|
re.compile(r"^grok-beta$"),
|
@@ -68,25 +72,23 @@ grok_naming_regexes: List[Pattern] = [
|
|
68
72
|
]
|
69
73
|
|
70
74
|
|
71
|
-
openrouter_naming_regexes:
|
75
|
+
openrouter_naming_regexes: list[Pattern] = [
|
72
76
|
re.compile(r"^openrouter/.*$"), # openrouter/model-name pattern
|
73
77
|
]
|
74
78
|
|
75
|
-
openrouter_naming_regexes:
|
79
|
+
openrouter_naming_regexes: list[Pattern] = [
|
76
80
|
re.compile(r"^openrouter/.*$"), # openrouter/model-name pattern
|
77
81
|
]
|
78
82
|
|
79
83
|
# Custom endpoint patterns - check these before generic patterns
|
80
|
-
custom_endpoint_naming_regexes:
|
81
|
-
# Modal endpoints: org--app.modal.run
|
82
|
-
re.compile(r"^[a-zA-Z0-9\-]+--[a-zA-Z0-9\-]+\.modal\.run$"),
|
84
|
+
custom_endpoint_naming_regexes: list[Pattern] = [
|
83
85
|
# Generic domain patterns for custom endpoints
|
84
86
|
re.compile(r"^[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+\.[a-zA-Z]+$"), # domain.tld
|
85
87
|
re.compile(r"^[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+\.[a-zA-Z]+\/[a-zA-Z0-9\-\/]+$"), # domain.tld/path
|
86
88
|
]
|
87
89
|
|
88
90
|
# Provider mapping for explicit provider overrides
|
89
|
-
PROVIDER_MAP:
|
91
|
+
PROVIDER_MAP: dict[str, Any] = {
|
90
92
|
"openai": OpenAIStructuredOutputClient,
|
91
93
|
"anthropic": AnthropicClient,
|
92
94
|
"groq": GroqClient,
|
@@ -104,7 +106,7 @@ def get_client(
|
|
104
106
|
model_name: str,
|
105
107
|
with_formatting: bool = False,
|
106
108
|
synth_logging: bool = True,
|
107
|
-
provider:
|
109
|
+
provider: str | None = None,
|
108
110
|
) -> Any:
|
109
111
|
"""
|
110
112
|
Get a vendor client for the specified model.
|
@@ -178,7 +180,9 @@ def get_client(
|
|
178
180
|
elif any(regex.match(model_name) for regex in custom_endpoint_naming_regexes):
|
179
181
|
# Custom endpoints are passed as the endpoint URL
|
180
182
|
return CustomEndpointClient(endpoint_url=model_name)
|
181
|
-
elif any(regex.match(model_name) for regex in
|
182
|
-
|
183
|
+
elif (any(regex.match(model_name) for regex in synth_naming_regexes) or
|
184
|
+
model_name in SYNTH_SUPPORTED_MODELS):
|
185
|
+
# Synth models use OpenAI-compatible client with custom endpoint
|
186
|
+
return OpenAIStructuredOutputClient(synth_logging=synth_logging)
|
183
187
|
else:
|
184
188
|
raise ValueError(f"Invalid model name: {model_name}")
|
synth_ai/lm/injection.py
CHANGED
@@ -2,16 +2,16 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import contextvars
|
4
4
|
from contextlib import contextmanager
|
5
|
-
from typing import Any
|
5
|
+
from typing import Any
|
6
6
|
|
7
|
-
Rule =
|
7
|
+
Rule = dict[str, Any]
|
8
8
|
|
9
|
-
_rules_ctx: contextvars.ContextVar[
|
9
|
+
_rules_ctx: contextvars.ContextVar[list[Rule] | None] = contextvars.ContextVar(
|
10
10
|
"injection_rules", default=None
|
11
11
|
)
|
12
12
|
|
13
13
|
|
14
|
-
def set_injection_rules(rules:
|
14
|
+
def set_injection_rules(rules: list[Rule]):
|
15
15
|
"""Set prompt-injection rules for the current context and return a reset token.
|
16
16
|
|
17
17
|
Each rule must be a dict with at least keys: "find" and "replace" (strings).
|
@@ -24,7 +24,7 @@ def set_injection_rules(rules: List[Rule]):
|
|
24
24
|
return _rules_ctx.set(rules)
|
25
25
|
|
26
26
|
|
27
|
-
def get_injection_rules() ->
|
27
|
+
def get_injection_rules() -> list[Rule] | None:
|
28
28
|
"""Get the current context's injection rules, if any."""
|
29
29
|
return _rules_ctx.get()
|
30
30
|
|
@@ -34,7 +34,7 @@ def clear_injection_rules(token) -> None:
|
|
34
34
|
_rules_ctx.reset(token)
|
35
35
|
|
36
36
|
|
37
|
-
def apply_injection(messages:
|
37
|
+
def apply_injection(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
38
38
|
"""Apply ordered substring replacements to text parts of messages in place.
|
39
39
|
|
40
40
|
- Only modifies `str` content or list parts where `part["type"] == "text"`.
|
@@ -71,11 +71,10 @@ def apply_injection(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
71
71
|
|
72
72
|
|
73
73
|
@contextmanager
|
74
|
-
def injection_rules_ctx(rules:
|
74
|
+
def injection_rules_ctx(rules: list[Rule]):
|
75
75
|
"""Context manager to temporarily apply injection rules within the block."""
|
76
76
|
tok = set_injection_rules(rules)
|
77
77
|
try:
|
78
78
|
yield
|
79
79
|
finally:
|
80
80
|
clear_injection_rules(tok)
|
81
|
-
|
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.
|