Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions integrations/langchain-py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ members = [
".",
]

[tool.uv.sources]
braintrust = { path = "../../py", editable = true }

[dependency-groups]
dev = [
"build",
Expand Down
28 changes: 25 additions & 3 deletions integrations/langchain-py/src/braintrust_langchain/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
from .callbacks import BraintrustCallbackHandler
from .context import set_global_handler
"""
DEPRECATED: braintrust-langchain is now part of the main braintrust package.

__all__ = ["BraintrustCallbackHandler", "set_global_handler"]
Install `braintrust` and use `from braintrust.integrations.langchain import BraintrustCallbackHandler` instead.
This package now re-exports from `braintrust.integrations.langchain` for backward compatibility.
"""

import warnings

warnings.warn(
"braintrust-langchain is deprecated. The LangChain integration is now included in the main "
"'braintrust' package. Use 'from braintrust.integrations.langchain import BraintrustCallbackHandler' "
"instead. This package will be removed in a future release.",
DeprecationWarning,
stacklevel=2,
)

# Re-export public API from the new location for backward compatibility
from braintrust.integrations.langchain import ( # noqa: E402, F401
BraintrustCallbackHandler,
BraintrustTracer,
clear_global_handler,
set_global_handler,
)

__all__ = ["BraintrustCallbackHandler", "set_global_handler", "clear_global_handler", "BraintrustTracer"]
25 changes: 1 addition & 24 deletions integrations/langchain-py/src/braintrust_langchain/context.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
from contextvars import ContextVar

from langchain_core.tracers.context import register_configure_hook

from braintrust_langchain.callbacks import BraintrustCallbackHandler
from braintrust.integrations.langchain.context import clear_global_handler, set_global_handler

__all__ = ["set_global_handler", "clear_global_handler"]


braintrust_callback_handler_var: ContextVar[BraintrustCallbackHandler | None] = ContextVar(
"braintrust_callback_handler", default=None
)


def set_global_handler(handler: BraintrustCallbackHandler):
braintrust_callback_handler_var.set(handler)


def clear_global_handler():
braintrust_callback_handler_var.set(None)


register_configure_hook(
context_var=braintrust_callback_handler_var,
inheritable=True,
)
43 changes: 43 additions & 0 deletions integrations/langchain-py/src/tests/test_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Test that braintrust_langchain re-exports the public API from braintrust.integrations.langchain."""

import importlib
import warnings

import pytest


def test_public_api_reexported():
"""All public API symbols should be importable from braintrust_langchain."""
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)

from braintrust_langchain import (
BraintrustCallbackHandler,
BraintrustTracer,
clear_global_handler,
set_global_handler,
)

assert callable(BraintrustCallbackHandler)
assert callable(BraintrustTracer)
assert callable(set_global_handler)
assert callable(clear_global_handler)


def test_context_module_reexported():
"""braintrust_langchain.context should still work for users who imported from there directly."""
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)

from braintrust_langchain.context import clear_global_handler, set_global_handler

assert callable(set_global_handler)
assert callable(clear_global_handler)


def test_deprecation_warning():
"""Importing braintrust_langchain should emit a DeprecationWarning."""
import braintrust_langchain

with pytest.warns(DeprecationWarning, match="braintrust-langchain is deprecated"):
importlib.reload(braintrust_langchain)
24 changes: 22 additions & 2 deletions py/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def _pinned_python_version():
GENAI_VERSIONS = (LATEST,)
DSPY_VERSIONS = (LATEST,)
GOOGLE_ADK_VERSIONS = (LATEST, "1.14.1")
LANGCHAIN_VERSIONS = (LATEST, "0.3.28")
# temporalio 1.19.0+ requires Python >= 3.10; skip Python 3.9 entirely
TEMPORAL_VERSIONS = (LATEST, "1.20.0", "1.19.0")
PYTEST_VERSIONS = (LATEST, "8.4.2")
Expand Down Expand Up @@ -202,6 +203,24 @@ def test_google_adk(session, version):
_run_core_tests(session)


@nox.session()
@nox.parametrize("version", LANGCHAIN_VERSIONS, ids=LANGCHAIN_VERSIONS)
def test_langchain(session, version):
"""Test LangChain integration."""
# langchain requires Python >= 3.10
if sys.version_info < (3, 10):
session.skip("langchain requires Python >= 3.10")
_install_test_deps(session)
_install(session, "langchain-core", version)
_install(session, "langchain-openai")
_install(session, "langchain-anthropic")
_install(session, "langgraph")
_run_tests(session, f"{INTEGRATION_DIR}/langchain/test_callbacks.py")
_run_tests(session, f"{INTEGRATION_DIR}/langchain/test_context.py")
_run_tests(session, f"{INTEGRATION_DIR}/langchain/test_anthropic.py")
_run_core_tests(session)


@nox.session()
@nox.parametrize("version", OPENAI_VERSIONS, ids=OPENAI_VERSIONS)
def test_openai(session, version):
Expand Down Expand Up @@ -336,8 +355,9 @@ def pylint(session):
session.install("pydantic_ai>=1.10.0")
session.install("google-adk")
session.install("opentelemetry.instrumentation.openai")
# langsmith is needed for the wrapper module but not in VENDOR_PACKAGES
session.install("langsmith")
# langsmith is needed for the langsmith_wrapper module but not in VENDOR_PACKAGES
# langchain-core, langchain-openai, langchain-anthropic are needed for the langchain integration
session.install("langsmith", "langchain-core", "langchain-openai", "langchain-anthropic")

result = session.run("git", "ls-files", "**/*.py", silent=True, log=False)
files = [path for path in result.strip().splitlines() if path not in GENERATED_LINT_EXCLUDES]
Expand Down
13 changes: 8 additions & 5 deletions py/requirements-optional.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
anthropic==0.84.0
openai==2.24.0
pydantic_ai==1.66.0
agno==2.5.7
google-genai==1.66.0
google-adk==1.14.1
anthropic==0.84.0
dspy==3.1.3
google-adk==1.14.1
google-genai==1.66.0
langchain-anthropic==1.4.0
langchain-core==1.2.22
langchain-openai==1.1.12
langsmith==0.7.12
litellm==1.82.0
openai==2.24.0
pydantic_ai==1.66.0
2 changes: 1 addition & 1 deletion py/src/braintrust/contrib/temporal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def __init__(self, logger: Any | None = None) -> None:
workflow_runner=_modify_workflow_runner,
)
else:
super().__init__(
super().__init__( # pylint: disable=unexpected-keyword-arg
name="braintrust",
client_interceptors=[interceptor],
worker_interceptors=[interceptor],
Expand Down
20 changes: 20 additions & 0 deletions py/src/braintrust/integrations/langchain/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging

from braintrust.integrations.langchain.callbacks import BraintrustCallbackHandler
from braintrust.integrations.langchain.context import clear_global_handler, set_global_handler


__all__ = ["BraintrustCallbackHandler", "set_global_handler", "clear_global_handler", "BraintrustTracer"]

_logger = logging.getLogger(__name__)


class BraintrustTracer(BraintrustCallbackHandler):
"""Deprecated: use BraintrustCallbackHandler instead."""

def __init__(self, *args, **kwargs):
_logger.warning(
"BraintrustTracer is deprecated, use BraintrustCallbackHandler instead. "
"Update your imports to: from braintrust.integrations.langchain import BraintrustCallbackHandler"
)
super().__init__(*args, **kwargs)
Loading
Loading