Skip to content

Commit 8424adb

Browse files
authored
feat(integrations): add TypeSafe instrumentation (#777)
https://www.braintrust.dev/app/Braintrust%20SDKs/object?object_type=project_logs&object_id=65b29c05-ac12-4829-ba2c-2a4ae7f834c5&id=6df448c8-32e0-4ec0-9ee5-124ed3ae5e4d <img width="840" height="576" alt="image" src="https://github.com/user-attachments/assets/39270720-81ef-4f38-8bd0-5a3a309b606d" /> Auto instrumentation: ```python import braintrust braintrust.auto_instrument() from typesafe_sdk import TypeSafeClient logger = braintrust.init_logger(project="my-project") client = TypeSafeClient() ``` Explicit client instrumentation: ```python from braintrust.integrations.typesafe import wrap_typesafe from typesafe_sdk import TypeSafeClient client = wrap_typesafe(TypeSafeClient()) ``` Generated span tree: ```text Support ticket triage [task] └── typesafe.systemOne [question] ├── input │ ├── state │ └── questions: category, urgency, duplicate_charge ├── metadata │ ├── provider: typesafe │ └── model: jev-1.13.0 ├── output │ └── answers: category, urgency, duplicate_charge └── metrics ├── prompt_tokens: 380 ├── completion_tokens: 70 └── tokens: 450 ```
1 parent ebddc56 commit 8424adb

16 files changed

Lines changed: 605 additions & 0 deletions

‎py/noxfile.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,18 @@ def test_mistral(session, version):
694694
_run_tests(session, f"{INTEGRATION_DIR}/mistral/test_mistral.py", version=version)
695695

696696

697+
TYPESAFE_VERSIONS = _get_matrix_versions("typesafe-sdk")
698+
699+
700+
@nox.session()
701+
@nox.parametrize("version", TYPESAFE_VERSIONS, ids=TYPESAFE_VERSIONS)
702+
def test_typesafe(session, version):
703+
"""Test the TypeSafe SDK integration."""
704+
_install_test_deps(session)
705+
_install_matrix_dep(session, "typesafe-sdk", version)
706+
_run_tests(session, f"{INTEGRATION_DIR}/typesafe/test_typesafe.py", version=version)
707+
708+
697709
HUGGINGFACE_HUB_VERSIONS = _get_matrix_versions("huggingface-hub")
698710

699711

‎py/pyproject.toml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ lint = [
279279
"huggingface-hub",
280280
"numpy",
281281
"strands-agents",
282+
"typesafe-sdk",
282283
"temporalio",
283284
"pydantic-ai",
284285
"opentelemetry-instrumentation-openai==0.59.1",
@@ -502,6 +503,9 @@ latest = "openrouter==1.1.139"
502503
latest = "mistralai==2.10.0"
503504
"1.12.4" = "mistralai==1.12.4"
504505

506+
[tool.braintrust.matrix.typesafe-sdk]
507+
latest = "typesafe-sdk==0.6.0"
508+
505509
[tool.braintrust.matrix.huggingface-hub]
506510
# Floor pinned to 0.32.0: the earliest release that exposes the
507511
# ``provider="auto"`` routing mode the integration relies on for multi-
@@ -590,6 +594,7 @@ openrouter = ["openrouter"]
590594
pipecat = ["pipecat-ai"]
591595
pydantic_ai = ["pydantic-ai-integration", "pydantic-ai-wrap-openai"]
592596
strands = ["strands-agents"]
597+
typesafe = ["typesafe-sdk"]
593598

594599
[tool.braintrust.vendor-packages]
595600
ai-sdk = "ai"
@@ -621,3 +626,4 @@ pipecat-ai = "pipecat"
621626
strands-agents = "strands"
622627
temporalio = "temporalio"
623628
transformers = "transformers"
629+
typesafe-sdk = "typesafe_sdk"

‎py/src/braintrust/auto.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
StrandsIntegration,
3838
TemporalIntegration,
3939
TransformersIntegration,
40+
TypeSafeIntegration,
4041
)
4142
from braintrust.integrations.base import BaseIntegration
4243

@@ -88,6 +89,7 @@ def auto_instrument(
8889
temporal: bool = True,
8990
livekit_agents: bool = True,
9091
pipecat: bool = True,
92+
typesafe: bool = True,
9193
) -> dict[str, bool]:
9294
"""
9395
Auto-instrument supported AI/ML libraries for Braintrust tracing.
@@ -128,6 +130,7 @@ def auto_instrument(
128130
temporal: Enable Temporal instrumentation (default: True)
129131
livekit_agents: Enable LiveKit Agents instrumentation (default: True)
130132
pipecat: Enable Pipecat AI instrumentation (default: True)
133+
typesafe: Enable TypeSafe instrumentation (default: True)
131134
132135
Returns:
133136
Dict mapping integration name to whether it was successfully instrumented.
@@ -233,6 +236,8 @@ def auto_instrument(
233236
results["livekit_agents"] = _instrument_integration(LiveKitAgentsIntegration)
234237
if pipecat:
235238
results["pipecat"] = _instrument_integration(PipecatIntegration)
239+
if typesafe:
240+
results["typesafe"] = _instrument_integration(TypeSafeIntegration)
236241

237242
return results
238243

‎py/src/braintrust/conftest.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def setup_braintrust():
204204
os.environ.setdefault("OPENAI_API_KEY", "sk-test-dummy-api-key-for-vcr-tests")
205205
os.environ.setdefault("ANTHROPIC_API_KEY", "sk-ant-test-dummy-api-key-for-vcr-tests")
206206
os.environ.setdefault("MISTRAL_API_KEY", "mistral-test-dummy-api-key-for-vcr-tests")
207+
os.environ.setdefault("TYPESAFE_API_KEY", "typesafe-test-dummy-api-key-for-vcr-tests")
207208
os.environ.setdefault("CO_API_KEY", os.getenv("COHERE_API_KEY", "co-test-dummy-api-key-for-vcr-tests"))
208209
os.environ.setdefault("COHERE_API_KEY", os.getenv("CO_API_KEY", "co-test-dummy-api-key-for-vcr-tests"))
209210

‎py/src/braintrust/integrations/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .strands import StrandsIntegration
2828
from .temporal import TemporalIntegration
2929
from .transformers import TransformersIntegration
30+
from .typesafe import TypeSafeIntegration
3031

3132

3233
__all__ = [
@@ -59,4 +60,5 @@
5960
"StrandsIntegration",
6061
"TemporalIntegration",
6162
"TransformersIntegration",
63+
"TypeSafeIntegration",
6264
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Test auto_instrument for TypeSafe."""
2+
3+
import os
4+
5+
from braintrust.auto import auto_instrument
6+
from braintrust.integrations.test_utils import autoinstrument_test_context
7+
from typesafe_sdk import Noul, TypeSafeClient
8+
9+
10+
results = auto_instrument()
11+
assert results.get("typesafe") is True
12+
assert auto_instrument().get("typesafe") is True
13+
14+
with autoinstrument_test_context("test_auto_typesafe", integration="typesafe") as memory_logger:
15+
with TypeSafeClient(api_key=os.environ["TYPESAFE_API_KEY"]) as client:
16+
response = client.system_one(
17+
state="The package arrived intact and on time.",
18+
questions={"positive": Noul(instructions="Is this feedback positive?")},
19+
)
20+
assert 0 <= response.nouls["positive"].noul <= 1
21+
22+
spans = memory_logger.pop()
23+
assert len(spans) == 1
24+
span = spans[0]
25+
assert span["metadata"]["provider"] == "typesafe"
26+
assert span["metadata"]["model"].startswith("jev-")
27+
assert span["input"]["questions"][0]["id"] == "positive"
28+
assert span["output"]["answers"][0]["id"] == "positive"
29+
30+
print("SUCCESS")
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Braintrust integration for the TypeSafe Python SDK."""
2+
3+
from typing import Any
4+
5+
from .integration import TypeSafeIntegration
6+
from .patchers import AsyncSystemOnePatcher, SystemOnePatcher
7+
8+
9+
def setup_typesafe() -> bool:
10+
"""Instrument installed TypeSafe sync and async clients."""
11+
return TypeSafeIntegration.setup()
12+
13+
14+
def wrap_typesafe(client: Any) -> Any:
15+
"""Instrument a TypeSafe sync or async client in place."""
16+
from typesafe_sdk import AsyncTypeSafeClient
17+
18+
patcher = AsyncSystemOnePatcher if isinstance(client, AsyncTypeSafeClient) else SystemOnePatcher
19+
return patcher.wrap_target(client)
20+
21+
22+
__all__ = [
23+
"TypeSafeIntegration",
24+
"setup_typesafe",
25+
"wrap_typesafe",
26+
]

‎py/src/braintrust/integrations/typesafe/cassettes/latest/test_auto_typesafe.yaml‎

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎py/src/braintrust/integrations/typesafe/cassettes/latest/test_setup_typesafe_system_one_async.yaml‎

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎py/src/braintrust/integrations/typesafe/cassettes/latest/test_wrap_typesafe_system_one_sync.yaml‎

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)