Skip to content

Commit d735951

Browse files
committed
remove patcher config for now
1 parent e0fb5cf commit d735951

8 files changed

Lines changed: 28 additions & 172 deletions

File tree

.agents/skills/sdk-integrations/SKILL.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Patchers must provide:
9292
- existence checks
9393
- idempotence through the base patcher marker
9494

95-
Use `IntegrationPatchConfig` only when users need patcher-level selection. Let `BaseIntegration.resolve_patchers()` reject unknown patcher ids instead of silently ignoring them.
95+
Let `BaseIntegration.resolve_patchers()` reject duplicate patcher ids instead of silently ignoring them.
9696

9797
## Patching Patterns
9898

@@ -116,14 +116,11 @@ Use:
116116

117117
Update `py/src/braintrust/auto.py` only if the integration should be auto-patched.
118118

119-
Use `InstrumentOption` (i.e. `bool | IntegrationPatchConfig`) for all integrations, including those that do not yet use the integrations API. This keeps the signature uniform and avoids a breaking change when the integration is later migrated.
120-
121-
Use `_normalize_instrument_option()` and `_instrument_integration(...)` instead of adding a custom `_instrument_*` function:
119+
All `auto_instrument()` parameters are plain `bool` flags. Use `_instrument_integration(...)` instead of adding a custom `_instrument_*` function:
122120

123121
```python
124-
enabled, config = _normalize_instrument_option("provider", provider)
125-
if enabled:
126-
results["provider"] = _instrument_integration(ProviderIntegration, patch_config=config)
122+
if provider:
123+
results["provider"] = _instrument_integration(ProviderIntegration)
127124
```
128125

129126
Add the integration import near the other integration imports in `auto.py`.
@@ -147,7 +144,7 @@ Cover the surfaces that changed:
147144
- streaming behavior
148145
- idempotence
149146
- failure and error logging
150-
- patcher selection when using `IntegrationPatchConfig`
147+
- patcher resolution and duplicate detection
151148

152149
Keep VCR cassettes in `py/src/braintrust/integrations/<provider>/cassettes/`. Re-record them only for intentional behavior changes.
153150

@@ -173,8 +170,6 @@ cd py && make lint
173170
- Moving provider-specific behavior into shared integration code.
174171
- Combining unrelated targets into one patcher.
175172
- Forgetting async or streaming coverage.
176-
- Adding patcher selection without tests for enabled and disabled cases.
177173
- Re-recording cassettes when behavior did not intentionally change.
178-
- Using `_normalize_bool_option()` instead of `_normalize_instrument_option()` — all integrations should accept `InstrumentOption`.
179174
- Adding a custom `_instrument_*` helper where `_instrument_integration()` already fits.
180175
- Forgetting `target_module` for deep or optional submodule patch targets.

py/src/braintrust/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ def is_equal(expected, output):
6363

6464
from .audit import *
6565
from .auto import (
66-
IntegrationPatchConfig, # noqa: F401 # type: ignore[reportUnusedImport]
6766
auto_instrument, # noqa: F401 # type: ignore[reportUnusedImport]
6867
)
6968
from .framework import *

py/src/braintrust/auto.py

Lines changed: 17 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,15 @@
44
Provides one-line instrumentation for supported libraries.
55
"""
66

7-
from __future__ import annotations
8-
97
import logging
108
from contextlib import contextmanager
119

12-
from braintrust.integrations import ADKIntegration, AnthropicIntegration, IntegrationPatchConfig
10+
from braintrust.integrations import ADKIntegration, AnthropicIntegration
1311

1412

1513
__all__ = ["auto_instrument"]
1614

1715
logger = logging.getLogger(__name__)
18-
InstrumentOption = bool | IntegrationPatchConfig
1916

2017

2118
@contextmanager
@@ -32,14 +29,14 @@ def _try_patch():
3229
def auto_instrument(
3330
*,
3431
openai: bool = True,
35-
anthropic: InstrumentOption = True,
32+
anthropic: bool = True,
3633
litellm: bool = True,
3734
pydantic_ai: bool = True,
3835
google_genai: bool = True,
3936
agno: bool = True,
4037
claude_agent_sdk: bool = True,
4138
dspy: bool = True,
42-
adk: InstrumentOption = True,
39+
adk: bool = True,
4340
) -> dict[str, bool]:
4441
"""
4542
Auto-instrument supported AI/ML libraries for Braintrust tracing.
@@ -52,8 +49,7 @@ def auto_instrument(
5249
5350
Args:
5451
openai: Enable OpenAI instrumentation (default: True)
55-
anthropic: Enable Anthropic instrumentation (default: True), or pass an
56-
IntegrationPatchConfig to select Anthropic patchers explicitly.
52+
anthropic: Enable Anthropic instrumentation (default: True)
5753
litellm: Enable LiteLLM instrumentation (default: True)
5854
pydantic_ai: Enable Pydantic AI instrumentation (default: True)
5955
google_genai: Enable Google GenAI instrumentation (default: True)
@@ -108,34 +104,24 @@ def auto_instrument(
108104
"""
109105
results = {}
110106

111-
openai_enabled = _normalize_bool_option("openai", openai)
112-
anthropic_enabled, anthropic_config = _normalize_instrument_option("anthropic", anthropic)
113-
litellm_enabled = _normalize_bool_option("litellm", litellm)
114-
pydantic_ai_enabled = _normalize_bool_option("pydantic_ai", pydantic_ai)
115-
google_genai_enabled = _normalize_bool_option("google_genai", google_genai)
116-
agno_enabled = _normalize_bool_option("agno", agno)
117-
claude_agent_sdk_enabled = _normalize_bool_option("claude_agent_sdk", claude_agent_sdk)
118-
dspy_enabled = _normalize_bool_option("dspy", dspy)
119-
adk_enabled, adk_config = _normalize_instrument_option("adk", adk)
120-
121-
if openai_enabled:
107+
if openai:
122108
results["openai"] = _instrument_openai()
123-
if anthropic_enabled:
124-
results["anthropic"] = _instrument_integration(AnthropicIntegration, patch_config=anthropic_config)
125-
if litellm_enabled:
109+
if anthropic:
110+
results["anthropic"] = _instrument_integration(AnthropicIntegration)
111+
if litellm:
126112
results["litellm"] = _instrument_litellm()
127-
if pydantic_ai_enabled:
113+
if pydantic_ai:
128114
results["pydantic_ai"] = _instrument_pydantic_ai()
129-
if google_genai_enabled:
115+
if google_genai:
130116
results["google_genai"] = _instrument_google_genai()
131-
if agno_enabled:
117+
if agno:
132118
results["agno"] = _instrument_agno()
133-
if claude_agent_sdk_enabled:
119+
if claude_agent_sdk:
134120
results["claude_agent_sdk"] = _instrument_claude_agent_sdk()
135-
if dspy_enabled:
121+
if dspy:
136122
results["dspy"] = _instrument_dspy()
137-
if adk_enabled:
138-
results["adk"] = _instrument_integration(ADKIntegration, patch_config=adk_config)
123+
if adk:
124+
results["adk"] = _instrument_integration(ADKIntegration)
139125

140126
return results
141127

@@ -148,34 +134,12 @@ def _instrument_openai() -> bool:
148134
return False
149135

150136

151-
def _instrument_integration(integration, *, patch_config: IntegrationPatchConfig | None = None) -> bool:
137+
def _instrument_integration(integration) -> bool:
152138
with _try_patch():
153-
return integration.setup(
154-
enabled_patchers=patch_config.enabled_patchers if patch_config is not None else None,
155-
disabled_patchers=patch_config.disabled_patchers if patch_config is not None else None,
156-
)
139+
return integration.setup()
157140
return False
158141

159142

160-
def _normalize_bool_option(name: str, option: bool) -> bool:
161-
if isinstance(option, bool):
162-
return option
163-
164-
raise TypeError(f"auto_instrument option {name!r} must be a bool, got {type(option).__name__}")
165-
166-
167-
def _normalize_instrument_option(name: str, option: InstrumentOption) -> tuple[bool, IntegrationPatchConfig | None]:
168-
if isinstance(option, bool):
169-
return option, None
170-
171-
if isinstance(option, IntegrationPatchConfig):
172-
return True, option
173-
174-
raise TypeError(
175-
f"auto_instrument option {name} must be a bool or IntegrationPatchConfig, got {type(option).__name__}"
176-
)
177-
178-
179143
def _instrument_litellm() -> bool:
180144
with _try_patch():
181145
from braintrust.wrappers.litellm import patch_litellm
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from .adk import ADKIntegration
22
from .anthropic import AnthropicIntegration
3-
from .base import IntegrationPatchConfig
43

54

6-
__all__ = ["ADKIntegration", "AnthropicIntegration", "IntegrationPatchConfig"]
5+
__all__ = ["ADKIntegration", "AnthropicIntegration"]

py/src/braintrust/integrations/anthropic/test_anthropic.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,6 @@ def test_available_patchers(self):
512512
"anthropic.init.async",
513513
)
514514

515-
def test_resolve_patchers_honors_enable_disable_filters(self):
516-
selected = AnthropicIntegration.resolve_patchers(
517-
enabled_patchers={"anthropic.init.sync", "anthropic.init.async"},
518-
disabled_patchers={"anthropic.init.async"},
519-
)
520-
521-
assert tuple(patcher.identifier() for patcher in selected) == ("anthropic.init.sync",)
522-
523-
def test_resolve_patchers_rejects_unknown_patchers(self):
524-
with pytest.raises(ValueError, match="Unknown patchers"):
525-
AnthropicIntegration.resolve_patchers(enabled_patchers={"anthropic.init.unknown"})
526-
527515
def test_setup_rejects_unsupported_versions(self):
528516
spec = make_specifier(
529517
min_version=AnthropicIntegration.min_version, max_version=AnthropicIntegration.max_version

py/src/braintrust/integrations/auto_test_scripts/test_auto_anthropic_patch_config.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

py/src/braintrust/integrations/base.py

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,14 @@
44
import inspect
55
import re
66
from abc import ABC, abstractmethod
7-
from collections.abc import Collection, Iterable
8-
from dataclasses import dataclass
7+
from collections.abc import Iterable
98
from typing import Any, ClassVar
109

1110
from wrapt import wrap_function_wrapper
1211

1312
from .versioning import detect_module_version, make_specifier, version_satisfies
1413

1514

16-
@dataclass(frozen=True)
17-
class IntegrationPatchConfig:
18-
"""Per-integration patch selection for instrumentation setup."""
19-
20-
enabled_patchers: Collection[str] | None = None
21-
disabled_patchers: Collection[str] | None = None
22-
23-
2415
class BasePatcher(ABC):
2516
"""Base class for one concrete integration patch strategy."""
2617

@@ -229,13 +220,8 @@ def available_patchers(cls) -> tuple[str, ...]:
229220
return tuple(patcher.identifier() for patcher in cls.patchers)
230221

231222
@classmethod
232-
def resolve_patchers(
233-
cls,
234-
*,
235-
enabled_patchers: Collection[str] | None = None,
236-
disabled_patchers: Collection[str] | None = None,
237-
) -> tuple[type[BasePatcher], ...]:
238-
"""Return the selected patchers after validating explicit selectors."""
223+
def resolve_patchers(cls) -> tuple[type[BasePatcher], ...]:
224+
"""Return all patchers after validating there are no duplicate identifiers."""
239225
patchers_by_id: dict[str, type[BasePatcher]] = {}
240226
for patcher in cls.patchers:
241227
patcher_id = patcher.identifier()
@@ -244,30 +230,13 @@ def resolve_patchers(
244230
raise ValueError(f"Duplicate patcher identifier {patcher_id!r} for integration {cls.name!r}")
245231
patchers_by_id[patcher_id] = patcher
246232

247-
enabled = set(enabled_patchers) if enabled_patchers is not None else None
248-
disabled = set(disabled_patchers or ())
249-
requested = disabled if enabled is None else enabled | disabled
250-
unknown = requested - set(patchers_by_id)
251-
if unknown:
252-
available = ", ".join(sorted(patchers_by_id))
253-
unknown_display = ", ".join(sorted(unknown))
254-
raise ValueError(
255-
f"Unknown patchers for integration {cls.name!r}: {unknown_display}. Available patchers: {available}"
256-
)
257-
258-
return tuple(
259-
patcher
260-
for patcher in cls.patchers
261-
if (enabled is None or patcher.identifier() in enabled) and patcher.identifier() not in disabled
262-
)
233+
return cls.patchers
263234

264235
@classmethod
265236
def setup(
266237
cls,
267238
*,
268239
target: Any | None = None,
269-
enabled_patchers: Collection[str] | None = None,
270-
disabled_patchers: Collection[str] | None = None,
271240
) -> bool:
272241
"""Apply all applicable patchers for this integration."""
273242
module = _import_first_available(cls.import_names)
@@ -278,10 +247,7 @@ def setup(
278247
return False
279248

280249
success = False
281-
selected_patchers = cls.resolve_patchers(
282-
enabled_patchers=enabled_patchers,
283-
disabled_patchers=disabled_patchers,
284-
)
250+
selected_patchers = cls.resolve_patchers()
285251
for patcher in sorted(selected_patchers, key=lambda patcher: patcher.priority):
286252
if not patcher.applies(module, version, target=target):
287253
continue

py/src/braintrust/wrappers/test_anthropic.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -58,45 +58,10 @@ def test_anthropic_integration_setup_is_idempotent(self):
5858
assert result.returncode == 0, f"Failed: {result.stderr}"
5959
assert "SUCCESS" in result.stdout
6060

61-
def test_anthropic_integration_setup_can_disable_specific_patchers(self):
62-
result = run_in_subprocess("""
63-
from braintrust.integrations.anthropic import AnthropicIntegration
64-
import anthropic
65-
66-
AnthropicIntegration.setup(disabled_patchers={"anthropic.init.async"})
67-
patched_sync = anthropic.Anthropic(api_key="test-key")
68-
unpatched_async = anthropic.AsyncAnthropic(api_key="test-key")
69-
70-
assert type(patched_sync.messages).__module__ == "braintrust.integrations.anthropic.tracing"
71-
assert type(unpatched_async.messages).__module__.startswith("anthropic.")
72-
print("SUCCESS")
73-
""")
74-
assert result.returncode == 0, f"Failed: {result.stderr}"
75-
assert "SUCCESS" in result.stdout
76-
7761

7862
class TestAutoInstrumentAnthropic:
7963
"""Tests for auto_instrument() with Anthropic."""
8064

8165
def test_auto_instrument_anthropic(self):
8266
"""Test auto_instrument patches Anthropic, creates spans, and uninstrument works."""
8367
verify_autoinstrument_script("test_auto_anthropic.py")
84-
85-
def test_auto_instrument_anthropic_patch_config(self):
86-
verify_autoinstrument_script("test_auto_anthropic_patch_config.py")
87-
88-
def test_auto_instrument_rejects_non_bool_option_for_openai(self):
89-
result = run_in_subprocess("""
90-
from braintrust.auto import auto_instrument
91-
from braintrust.integrations import IntegrationPatchConfig
92-
93-
try:
94-
auto_instrument(openai=IntegrationPatchConfig())
95-
except TypeError as exc:
96-
assert "must be a bool" in str(exc)
97-
print("SUCCESS")
98-
else:
99-
raise AssertionError("Expected TypeError")
100-
""")
101-
assert result.returncode == 0, f"Failed: {result.stderr}"
102-
assert "SUCCESS" in result.stdout

0 commit comments

Comments
 (0)