Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-langchain-core for
openSUSE:Factory checked in at 2026-10-17 21:26:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langchain-core (Old)
and /work/SRC/openSUSE:Factory/.python-langchain-core.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-langchain-core"
Sat Oct 17 21:26:57 2026 rev:21 rq:1385352 version:1.6.12
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-langchain-core/python-langchain-core.changes
2026-10-15 12:22:02.601845800 +0200
+++
/work/SRC/openSUSE:Factory/.python-langchain-core.new.3698736/python-langchain-core.changes
2026-10-17 21:26:58.864831975 +0200
@@ -1,0 +2,11 @@
+Sat Oct 17 06:23:51 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 1.6.12:
+ * Trace to a beta agent address: tracing(), LangChainTracer
+ and the callback manager accept an AgentAddress
+ destination (needs langsmith >= 0.14.9)
+ * Compat bridge keeps block extras and ids on text and
+ reasoning skeletons
+ * Tracer run construction goes through a _get_run_kwargs hook
+
+-------------------------------------------------------------------
Old:
----
langchain_core-1.6.10.tar.gz
New:
----
langchain_core-1.6.12.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-langchain-core.spec ++++++
--- /var/tmp/diff_new_pack.102HzY/_old 2026-10-17 21:26:59.661865229 +0200
+++ /var/tmp/diff_new_pack.102HzY/_new 2026-10-17 21:26:59.663865313 +0200
@@ -17,7 +17,7 @@
Name: python-langchain-core
-Version: 1.6.10
+Version: 1.6.12
Release: 0
Summary: Building applications with LLMs through composability
License: MIT
@@ -33,7 +33,7 @@
Requires: python-httpx >= 0.23.0
Requires: python-jsonpatch >= 1.33
Requires: python-langchain-protocol >= 0.0.17
-Requires: python-langsmith >= 0.3.45
+Requires: python-langsmith >= 0.14.9
Requires: python-packaging >= 23.2
Requires: python-pydantic >= 2.7.4
Requires: python-tenacity >= 8.1.0
@@ -47,7 +47,7 @@
BuildRequires: %{python_module httpx >= 0.23.0}
BuildRequires: %{python_module jsonpatch >= 1.33}
BuildRequires: %{python_module langchain-protocol >= 0.0.17}
-BuildRequires: %{python_module langsmith >= 0.3.45}
+BuildRequires: %{python_module langsmith >= 0.14.9}
BuildRequires: %{python_module numpy}
BuildRequires: %{python_module packaging >= 23.2}
BuildRequires: %{python_module pydantic >= 2.7.4}
++++++ langchain_core-1.6.10.tar.gz -> langchain_core-1.6.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langchain_core-1.6.10/PKG-INFO
new/langchain_core-1.6.12/PKG-INFO
--- old/langchain_core-1.6.10/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.5
Name: langchain-core
-Version: 1.6.10
+Version: 1.6.12
Summary: Building applications with LLMs through composability
Project-URL: Homepage, https://docs.langchain.com/
Project-URL: Documentation,
https://reference.langchain.com/python/langchain_core/
@@ -27,7 +27,7 @@
Requires-Dist: httpx<1.0.0,>=0.23.0
Requires-Dist: jsonpatch<2.0.0,>=1.33.0
Requires-Dist: langchain-protocol>=0.0.17
-Requires-Dist: langsmith<1.0.0,>=0.3.45
+Requires-Dist: langsmith<1.0.0,>=0.14.9
Requires-Dist: packaging>=23.2.0
Requires-Dist: pydantic<3.0.0,>=2.7.4
Requires-Dist: pyyaml<7.0.0,>=5.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/langchain_core/callbacks/manager.py
new/langchain_core-1.6.12/langchain_core/callbacks/manager.py
--- old/langchain_core-1.6.10/langchain_core/callbacks/manager.py
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/callbacks/manager.py
2020-02-02 01:00:00.000000000 +0100
@@ -37,6 +37,7 @@
from uuid import UUID
from langchain_protocol.protocol import MessagesData
+ from langsmith.address import AgentAddress
from tenacity import RetryCallState
from langchain_core.agents import AgentAction, AgentFinish
@@ -59,6 +60,7 @@
*,
inputs: dict[str, Any] | None = None,
project_name: str | None = None,
+ address: AgentAddress | None = None,
example_id: str | UUID | None = None,
run_id: UUID | None = None,
tags: list[str] | None = None,
@@ -74,6 +76,10 @@
callback_manager: The callback manager to use.
inputs: The inputs to the chain group.
project_name: The name of the project.
+ address: The agent address to send traces to instead of a project.
+
+ !!! warning "Beta"
+ Agent addressing must be enabled for the LangSmith workspace.
example_id: The ID of the example.
run_id: The ID of the run.
tags: The inheritable tags to apply to all runs.
@@ -101,7 +107,7 @@
)
cb = _get_trace_callbacks(
- project_name, example_id, callback_manager=callback_manager
+ project_name, example_id, callback_manager=callback_manager,
address=address
)
cm = CallbackManager.configure(
inheritable_callbacks=cb,
@@ -139,6 +145,7 @@
*,
inputs: dict[str, Any] | None = None,
project_name: str | None = None,
+ address: AgentAddress | None = None,
example_id: str | UUID | None = None,
run_id: UUID | None = None,
tags: list[str] | None = None,
@@ -155,6 +162,10 @@
other callback behavior.
inputs: The inputs to the chain group.
project_name: The name of the project.
+ address: The agent address to send traces to instead of a project.
+
+ !!! warning "Beta"
+ Agent addressing must be enabled for the LangSmith workspace.
example_id: The ID of the example.
run_id: The ID of the run.
tags: The inheritable tags to apply to all runs.
@@ -184,7 +195,7 @@
)
cb = _get_trace_callbacks(
- project_name, example_id, callback_manager=callback_manager
+ project_name, example_id, callback_manager=callback_manager,
address=address
)
cm = AsyncCallbackManager.configure(
inheritable_callbacks=cb, inheritable_tags=tags,
inheritable_metadata=metadata
@@ -2427,7 +2438,7 @@
from langchain_core.tracers.context import ( # noqa: PLC0415
_configure_hooks,
- _get_tracer_project,
+ _get_tracer_kwargs,
_tracing_v2_is_enabled,
tracing_v2_callback_var,
)
@@ -2505,7 +2516,6 @@
)
raise RuntimeError(msg)
- tracer_project = _get_tracer_project()
debug = _get_debug()
if verbose or debug or tracing_v2_enabled_:
if verbose and not any(
@@ -2529,16 +2539,7 @@
callback_manager.add_handler(tracer_v2)
else:
try:
- handler = LangChainTracer(
- project_name=tracer_project,
- client=(
- run_tree.client
- if run_tree is not None
- else tracing_context["client"]
- ),
- tags=tracing_tags,
- metadata=tracing_metadata,
- )
+ handler = LangChainTracer(**_get_tracer_kwargs())
callback_manager.add_handler(handler)
except Exception as e:
logger.warning(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/langchain_core/language_models/_compat_bridge.py
new/langchain_core-1.6.12/langchain_core/language_models/_compat_bridge.py
--- old/langchain_core-1.6.10/langchain_core/language_models/_compat_bridge.py
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/language_models/_compat_bridge.py
2020-02-02 01:00:00.000000000 +0100
@@ -47,10 +47,8 @@
MessageMetadata,
MessagesData,
MessageStartData,
- ReasoningContentBlock,
ServerToolCall,
ServerToolCallChunk,
- TextContentBlock,
ToolCall,
ToolCallChunk,
UsageInfo,
@@ -242,13 +240,21 @@
audio, video, file, non_standard, finalized tool calls) drop their
heavy payload fields; those are carried by `content-block-finish`.
Correlation fields (id, name, toolCallId) and small metadata
- (mime_type, url, status, …) are preserved on the start event.
+ (mime_type, url, status, phase, …) are preserved on the start event.
"""
btype = block.get("type", "text")
if btype == "text":
- return TextContentBlock(type="text", text="")
+ # extras and id
+ text_skel = {
+ k: v for k, v in block.items() if k not in {"text", "index",
"annotations"}
+ }
+ text_skel["text"] = ""
+ return _to_protocol_block(text_skel)
if btype == "reasoning":
- return ReasoningContentBlock(type="reasoning", reasoning="")
+ # extras and id
+ r_skel = {k: v for k, v in block.items() if k not in {"reasoning",
"index"}}
+ r_skel["reasoning"] = ""
+ return _to_protocol_block(r_skel)
if btype == "tool_call_chunk":
return ToolCallChunk(
type="tool_call_chunk",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/langchain_core/tracers/context.py
new/langchain_core-1.6.12/langchain_core/tracers/context.py
--- old/langchain_core-1.6.10/langchain_core/tracers/context.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/tracers/context.py 2020-02-02
01:00:00.000000000 +0100
@@ -22,6 +22,7 @@
from collections.abc import Generator
from langsmith import Client as LangSmithClient
+ from langsmith.address import AgentAddress
from langchain_core.callbacks.base import BaseCallbackHandler, Callbacks
from langchain_core.callbacks.manager import AsyncCallbackManager,
CallbackManager
@@ -43,16 +44,22 @@
example_id: str | UUID | None = None,
tags: list[str] | None = None,
client: LangSmithClient | None = None,
+ address: AgentAddress | None = None,
) -> Generator[LangChainTracer, None, None]:
"""Instruct LangChain to log all runs in context to LangSmith.
Args:
project_name: The name of the project.
- Defaults to `'default'`.
+ Resolved together with `address` from LangSmith config and the
+ environment when no destination is supplied.
example_id: The ID of the example.
tags: The tags to add to the run.
client: The client of the langsmith.
+ address: The agent address to send traces to instead of a project.
+
+ !!! warning "Beta"
+ Agent addressing must be enabled for the LangSmith workspace.
Yields:
The LangChain tracer.
@@ -72,6 +79,7 @@
cb = LangChainTracer(
example_id=example_id,
project_name=project_name,
+ address=address,
tags=tags,
client=client,
)
@@ -106,12 +114,13 @@
project_name: str | None = None,
example_id: str | UUID | None = None,
callback_manager: CallbackManager | AsyncCallbackManager | None = None,
+ *,
+ address: AgentAddress | None = None,
) -> Callbacks:
if _tracing_v2_is_enabled():
- project_name_ = project_name or _get_tracer_project()
tracer = tracing_v2_callback_var.get() or LangChainTracer(
- project_name=project_name_,
example_id=example_id,
+ **_get_tracer_kwargs(project_name, address=address),
)
if callback_manager is None:
cb = cast("Callbacks", [tracer])
@@ -135,27 +144,29 @@
return ls_utils.tracing_is_enabled()
-def _get_tracer_project() -> str:
- tracing_context = ls_rh.get_tracing_context()
- run_tree = tracing_context["parent"]
- if run_tree is None and tracing_context["project_name"] is not None:
- return cast("str", tracing_context["project_name"])
- return getattr(
- run_tree,
- "session_name",
- getattr(
- # Note, if people are trying to nest @traceable functions and the
- # tracing_v2_enabled context manager, this will likely mess up the
- # tree structure.
- tracing_v2_callback_var.get(),
- "project",
- # Have to set this to a string even though it always will return
- # a string because `get_tracer_project` technically can return
- # None, but only when a specific argument is supplied.
- # Therefore, this just tricks the mypy type checker
- str(ls_utils.get_tracer_project()),
- ),
- )
+def _get_tracer_kwargs(
+ project_name: str | None = None, *, address: AgentAddress | None = None
+) -> dict[str, Any]:
+ """Get tracer defaults from the current LangSmith context.
+
+ Forward project and address together so an addressed parent never picks up
+ an ambient project. Leave environment defaults and validation to RunTree.
+ """
+ context = ls_rh.get_tracing_context()
+ parent = context["parent"]
+ if project_name is not None or address is not None:
+ project = project_name
+ elif parent is not None:
+ project, address = parent.session_name, parent.address
+ else:
+ project, address = context["project_name"], context["address"]
+ return {
+ "project_name": project,
+ "address": address,
+ "client": parent.client if parent is not None else context["client"],
+ "tags": context["tags"],
+ "metadata": context["metadata"],
+ }
_configure_hooks: list[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langchain_core-1.6.10/langchain_core/tracers/core.py
new/langchain_core-1.6.12/langchain_core/tracers/core.py
--- old/langchain_core-1.6.10/langchain_core/tracers/core.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/tracers/core.py 2020-02-02
01:00:00.000000000 +0100
@@ -174,6 +174,14 @@
self.order_map[run.id] = (run.trace_id, run.dotted_order)
self.run_map[str(run.id)] = run
+ def _get_run_kwargs(
+ self,
+ parent_run_id: UUID | None, # noqa: ARG002
+ extra: dict[str, Any],
+ ) -> dict[str, Any]:
+ """Return construction arguments for a run."""
+ return {"extra": extra}
+
def _get_run(self, run_id: UUID, run_type: str | set[str] | None = None)
-> Run:
try:
run = self.run_map[str(run_id)]
@@ -226,7 +234,7 @@
parent_run_id=parent_run_id,
serialized=serialized,
inputs={"messages": [[dumpd(msg) for msg in batch] for batch in
messages]},
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
# WARNING: This is valid ONLY for streaming_events.
@@ -258,7 +266,7 @@
serialized=serialized,
# TODO: Figure out how to expose kwargs here
inputs={"prompts": prompts},
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
run_type="llm",
@@ -408,7 +416,7 @@
parent_run_id=parent_run_id,
serialized=serialized,
inputs=self._get_chain_inputs(inputs),
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
child_runs=[],
@@ -504,7 +512,7 @@
serialized=serialized,
# Wrapping in dict since Run requires a dict object.
inputs=inputs,
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
child_runs=[],
@@ -561,7 +569,7 @@
parent_run_id=parent_run_id,
serialized=serialized,
inputs={"query": query},
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
tags=tags,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/langchain_core/tracers/langchain.py
new/langchain_core-1.6.12/langchain_core/tracers/langchain.py
--- old/langchain_core-1.6.10/langchain_core/tracers/langchain.py
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/tracers/langchain.py
2020-02-02 01:00:00.000000000 +0100
@@ -9,6 +9,7 @@
from uuid import UUID
from langsmith import Client, get_tracing_context
+from langsmith import address as ls_address
from langsmith import run_trees as rt
from langsmith import utils as ls_utils
from tenacity import (
@@ -29,6 +30,8 @@
if TYPE_CHECKING:
from collections.abc import Mapping
+ from langsmith.address import AgentAddress
+
from langchain_core.messages import BaseMessage
from langchain_core.outputs import ChatGenerationChunk, GenerationChunk
@@ -144,6 +147,7 @@
tags: list[str] | None = None,
*,
metadata: Mapping[str, str] | None = None,
+ address: AgentAddress | None = None,
**kwargs: Any,
) -> None:
"""Initialize the LangChain tracer.
@@ -152,7 +156,8 @@
example_id: The example ID.
project_name: The project name.
- Defaults to the tracer project.
+ Resolved together with `address` from LangSmith config and the
+ environment when no destination is supplied.
client: The client.
Defaults to the global client.
@@ -162,13 +167,19 @@
metadata: Additional metadata to include if it isn't already in
the run.
Defaults to None.
+ address: The agent address to send traces to instead of a project.
+
+ !!! warning "Beta"
+ Agent addressing is in beta and must be enabled for the
+ LangSmith workspace.
**kwargs: Additional keyword arguments.
"""
super().__init__(**kwargs)
self.example_id = (
UUID(example_id) if isinstance(example_id, str) else example_id
)
- self.project_name = project_name or ls_utils.get_tracer_project()
+ self.project_name = project_name
+ self.address = ls_address.ensure_agent(address) if address is not None
else None
self.client = client or get_client()
self.tags = tags or []
self.latest_run: Run | None = None
@@ -209,6 +220,7 @@
return self.__class__(
example_id=self.example_id,
project_name=self.project_name,
+ address=self.address,
client=self.client,
tags=merged_tags,
metadata=merged_metadata,
@@ -217,9 +229,25 @@
_external_run_ids=self._external_run_ids,
)
+ def _get_run_kwargs(
+ self, parent_run_id: UUID | None, extra: dict[str, Any]
+ ) -> dict[str, Any]:
+ """Forward explicit or inherited destinations to RunTree."""
+ parent = self.run_map.get(str(parent_run_id)) if parent_run_id else
None
+ if parent is not None:
+ return {
+ "session_name": parent.session_name,
+ "address": parent.address,
+ "replicas": parent.replicas,
+ "extra": extra,
+ }
+ return {
+ "session_name": self.project_name,
+ "address": self.address,
+ "extra": extra,
+ }
+
def _start_trace(self, run: Run) -> None:
- if self.project_name:
- run.session_name = self.project_name
if self.tags is not None:
if run.tags:
run.tags = sorted(set(run.tags + self.tags))
@@ -267,7 +295,7 @@
parent_run_id=parent_run_id,
serialized=serialized,
inputs={"messages": [[dumpd(msg) for msg in batch] for batch in
messages]},
- extra=kwargs,
+ **self._get_run_kwargs(parent_run_id, kwargs),
events=[{"name": "start", "time": start_time}],
start_time=start_time,
run_type="llm",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langchain_core-1.6.10/langchain_core/version.py
new/langchain_core-1.6.12/langchain_core/version.py
--- old/langchain_core-1.6.10/langchain_core/version.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/langchain_core/version.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
"""Version information for `langchain-core`."""
-VERSION = "1.6.10"
+VERSION = "1.6.12"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langchain_core-1.6.10/pyproject.toml
new/langchain_core-1.6.12/pyproject.toml
--- old/langchain_core-1.6.10/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
+++ new/langchain_core-1.6.12/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
@@ -21,10 +21,10 @@
"Topic :: Software Development :: Libraries :: Python Modules",
]
-version = "1.6.10"
+version = "1.6.12"
requires-python = ">=3.10.0,<4.0.0"
dependencies = [
- "langsmith>=0.3.45,<1.0.0",
+ "langsmith>=0.14.9,<1.0.0",
"httpx>=0.23.0,<1.0.0",
"tenacity!=8.4.0,>=8.1.0,<10.0.0",
"jsonpatch>=1.33.0,<2.0.0",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/tests/unit_tests/language_models/test_compat_bridge.py
new/langchain_core-1.6.12/tests/unit_tests/language_models/test_compat_bridge.py
---
old/langchain_core-1.6.10/tests/unit_tests/language_models/test_compat_bridge.py
2020-02-02 01:00:00.000000000 +0100
+++
new/langchain_core-1.6.12/tests/unit_tests/language_models/test_compat_bridge.py
2020-02-02 01:00:00.000000000 +0100
@@ -1061,6 +1061,43 @@
assert finishes[2]["content"]["value"] == {"big": "C" * 1024}
+def test_chunks_to_events_text_start_preserves_metadata() -> None:
+ """Text/reasoning start keeps small metadata (e.g. `phase`) but no
payload."""
+ blocks: list[dict[str, Any]] = [
+ {"type": "reasoning", "reasoning": "hmm", "id": "rs_1", "index": 0},
+ {"type": "text", "text": "", "phase": "commentary", "index": 1},
+ {
+ "type": "text",
+ "text": "Hi",
+ "id": "msg_1",
+ "annotations": [{"type": "citation", "url": "u"}],
+ "index": 1,
+ },
+ ]
+ chunks = [
+ ChatGenerationChunk(message=AIMessageChunk(content=[block],
id="msg-1"))
+ for block in blocks
+ ]
+ events = list(chunks_to_events(iter(chunks)))
+ assert_valid_event_stream(events)
+
+ starts: list[Any] = [e for e in events if e["event"] ==
"content-block-start"]
+ assert starts[0]["content"] == {
+ "type": "reasoning",
+ "reasoning": "",
+ "id": "rs_1",
+ }
+ assert starts[1]["content"] == {
+ "type": "text",
+ "text": "",
+ "phase": "commentary",
+ }
+
+ finishes: list[Any] = [e for e in events if e["event"] ==
"content-block-finish"]
+ assert finishes[1]["content"]["phase"] == "commentary"
+ assert finishes[1]["content"]["text"] == "Hi"
+
+
def test_message_to_events_finalized_tool_call_start_strips_args() -> None:
"""Finalized `tool_call` keeps id/name on start but not parsed args."""
msg = AIMessage(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langchain_core-1.6.10/tests/unit_tests/tracers/test_address.py
new/langchain_core-1.6.12/tests/unit_tests/tracers/test_address.py
--- old/langchain_core-1.6.10/tests/unit_tests/tracers/test_address.py
1970-01-01 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/tests/unit_tests/tracers/test_address.py
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,515 @@
+"""Destination resolution for LangSmith tracing."""
+
+from collections.abc import Iterator
+from contextlib import AbstractContextManager, nullcontext
+from itertools import product
+from typing import Any
+from unittest.mock import MagicMock
+from uuid import uuid4
+
+import pytest
+from langsmith import Client, address, configure, tracing_context
+from langsmith.address import EnvAddressError
+from langsmith.run_trees import RunTree, WriteReplica
+from langsmith.utils import LangSmithUserError, get_env_var, get_tracer_project
+from typing_extensions import TypedDict
+
+from langchain_core.callbacks.manager import (
+ CallbackManager,
+ atrace_as_chain_group,
+ trace_as_chain_group,
+)
+from langchain_core.messages import HumanMessage
+from langchain_core.outputs import LLMResult
+from langchain_core.tracers.context import (
+ _get_trace_callbacks,
+ _get_tracer_kwargs,
+ tracing_v2_enabled,
+)
+from langchain_core.tracers.langchain import LangChainTracer
+
+ADDRESS = address.agent_address("code-agent", "production")
+ENV_ADDRESS = address.agent_address("env-agent", "staging")
+
+
[email protected](autouse=True)
+def clean_destination(monkeypatch: pytest.MonkeyPatch) -> Iterator[None]:
+ for name in (
+ "HOSTED_LANGSERVE_PROJECT_NAME",
+ "LANGSMITH_PROJECT",
+ "LANGCHAIN_PROJECT",
+ "LANGCHAIN_SESSION",
+ "LANGSMITH_AGENT_ID",
+ "LANGSMITH_AGENT_ENVIRONMENT",
+ ):
+ monkeypatch.delenv(name, raising=False)
+ get_env_var.cache_clear() # type: ignore[attr-defined]
+ get_tracer_project.cache_clear()
+ yield
+ get_env_var.cache_clear() # type: ignore[attr-defined]
+ get_tracer_project.cache_clear()
+
+
[email protected](
+ ("code_project", "code_address", "env_project", "env_address"),
+ list(product((False, True), repeat=4)),
+)
+def test_destination_matrix(
+ monkeypatch: pytest.MonkeyPatch,
+ *,
+ code_project: bool,
+ code_address: bool,
+ env_project: bool,
+ env_address: bool,
+) -> None:
+ if env_project:
+ monkeypatch.setenv("LANGSMITH_PROJECT", "env-project")
+ if env_address:
+ monkeypatch.setenv("LANGSMITH_AGENT_ID", "env-agent")
+ monkeypatch.setenv("LANGSMITH_AGENT_ENVIRONMENT", "staging")
+ get_env_var.cache_clear() # type: ignore[attr-defined]
+ get_tracer_project.cache_clear()
+ client = MagicMock(spec=Client)
+ kwargs: dict[str, Any] = {
+ "client": client,
+ "project_name": "code-project" if code_project else None,
+ "address": ADDRESS if code_address else None,
+ }
+ tracer = LangChainTracer(**kwargs)
+ run_id = uuid4()
+ if code_project and code_address:
+ with pytest.raises(LangSmithUserError, match="not both"):
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+ return
+ invalid_env = not (code_project or code_address) and env_project and
env_address
+ if invalid_env:
+ with pytest.raises(EnvAddressError):
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+ return
+ tracer.on_chain_start({}, {"input": "hello"}, run_id=run_id)
+ tracer.on_chain_end({}, run_id=run_id)
+ expected_address = (
+ ADDRESS
+ if code_address
+ else (ENV_ADDRESS if env_address and not code_project else None)
+ )
+ expected_project = (
+ None
+ if expected_address
+ else (
+ "code-project"
+ if code_project
+ else "env-project"
+ if env_project
+ else "default"
+ )
+ )
+ client.create_run.assert_called_once()
+ client.update_run.assert_called_once()
+ for payload in (
+ client.create_run.call_args.kwargs,
+ client.update_run.call_args.kwargs,
+ ):
+ assert payload.get("session_name") == expected_project
+ assert payload.get("address") == expected_address
+
+
[email protected]("run_type", ["llm", "chat", "tool", "retriever"])
+def test_callback_address_propagation(run_type: str) -> None:
+ client = MagicMock(spec=Client)
+ tracer = LangChainTracer(address=ADDRESS, client=client)
+ run_id = uuid4()
+ if run_type == "llm":
+ tracer.on_llm_start({}, ["hello"], run_id=run_id)
+ tracer.on_llm_end(LLMResult(generations=[]), run_id=run_id)
+ elif run_type == "chat":
+ tracer.on_chat_model_start({}, [[HumanMessage("hello")]],
run_id=run_id)
+ tracer.on_llm_end(LLMResult(generations=[]), run_id=run_id)
+ elif run_type == "tool":
+ tracer.on_tool_start({}, "hello", run_id=run_id)
+ tracer.on_tool_end("ok", run_id=run_id)
+ else:
+ tracer.on_retriever_start({}, "hello", run_id=run_id)
+ tracer.on_retriever_end([], run_id=run_id)
+ client.create_run.assert_called_once()
+ client.update_run.assert_called_once()
+ for payload in (
+ client.create_run.call_args.kwargs,
+ client.update_run.call_args.kwargs,
+ ):
+ assert payload.get("address") == ADDRESS
+ assert payload.get("session_name") is None
+
+
+class _Destination(TypedDict, total=False):
+ project_name: str
+ address: address.AgentAddress
+
+
[email protected](
+ "destination", [{"address": ADDRESS}, {"project_name": "configured"}]
+)
[email protected]("mode", ["context", "configure", "parent"])
+def test_ambient_destination(destination: _Destination, mode: str) -> None:
+ client = MagicMock(spec=Client)
+ scope: AbstractContextManager[None]
+ if mode == "context":
+ scope = tracing_context(
+ enabled=True,
+ client=client,
+ project_name=destination.get("project_name"),
+ address=destination.get("address"),
+ )
+ elif mode == "configure":
+ configure(
+ enabled=True,
+ client=client,
+ project_name=destination.get("project_name"),
+ address=destination.get("address"),
+ )
+ scope = nullcontext()
+ else:
+ parent = RunTree(name="parent", ls_client=client, **destination)
+ scope = tracing_context(enabled=True, client=client, parent=parent)
+
+ try:
+ with scope:
+ manager = CallbackManager.configure()
+ run = manager.on_chain_start({}, {})
+ run.on_chain_end({})
+ payload = client.create_run.call_args.kwargs
+ assert payload.get("address") == destination.get("address")
+ assert payload.get("session_name") == destination.get("project_name")
+ finally:
+ if mode == "configure":
+ configure(project_name=None, address=None, enabled=None,
client=None)
+
+
+def test_copy_and_context_address() -> None:
+ client = MagicMock(spec=Client)
+ with tracing_v2_enabled(address=ADDRESS, client=client) as tracer:
+ copied = tracer.copy_with_metadata_defaults(metadata={"foo": "bar"})
+ assert copied.address == ADDRESS
+ assert copied.project_name is None
+ manager = CallbackManager.configure()
+ run = manager.on_chain_start({}, {})
+ run.on_chain_end({})
+ assert client.create_run.call_args.kwargs["address"] == ADDRESS
+
+
+def test_plain_dictionary_address() -> None:
+ """A plain dictionary is normalized and forwarded to the SDK."""
+ destination = {"kind": "AGENT", "id": "code-agent", "environment":
"production"}
+ client = MagicMock(spec=Client)
+ tracer = LangChainTracer(address=destination, client=client) # type:
ignore[arg-type]
+ assert tracer.address == ADDRESS
+ assert destination["environment"] == "production"
+ run = CallbackManager(handlers=[tracer]).on_chain_start({}, {})
+ run.on_chain_end({})
+ assert client.create_run.call_args.kwargs["address"] == ADDRESS
+
+
[email protected](
+ "address",
+ [
+ "invalid",
+ "lrn:agents/code-agent/environments/production",
+ {"agent_id": "agent"},
+ {"kind": "AGENT", "id": "agent", "environment": "invalid"},
+ address.evaluator_address(),
+ address.experiment_address("0190c3d4-0000-7000-8000-0000000000b1"),
+ ],
+)
+def test_invalid_address(address: Any) -> None:
+ with pytest.raises(LangSmithUserError):
+ LangChainTracer(address=address, client=MagicMock(spec=Client))
+
+
[email protected]("name", ["LANGSMITH_AGENT_ID",
"LANGSMITH_AGENT_ENVIRONMENT"])
+def test_incomplete_environment(monkeypatch: pytest.MonkeyPatch, name: str) ->
None:
+ monkeypatch.setenv(name, "staging")
+ get_env_var.cache_clear() # type: ignore[attr-defined]
+ get_tracer_project.cache_clear()
+ client = MagicMock(spec=Client)
+ tracer = LangChainTracer(client=client)
+ run_id = uuid4()
+ with pytest.raises(EnvAddressError):
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+
+
+def test_parent_destination_overrides_tracer() -> None:
+ client = MagicMock(spec=Client)
+ tracer = LangChainTracer(project_name="other-project", client=client)
+ parent = RunTree(name="parent", address=ADDRESS, ls_client=client)
+ tracer.run_map[str(parent.id)] = parent
+ tracer.order_map[parent.id] = (parent.trace_id, parent.dotted_order)
+ run_id = uuid4()
+ tracer.on_chain_start({}, {}, run_id=run_id, parent_run_id=parent.id)
+ tracer.on_chain_end({}, run_id=run_id)
+ assert parent.child_runs[0].address == ADDRESS
+ assert parent.child_runs[0].session_name is None
+ assert client.create_run.call_args.kwargs["address"] == ADDRESS
+
+
+def test_context_conflicting_arguments() -> None:
+ client = MagicMock(spec=Client)
+ with (
+ tracing_v2_enabled(
+ project_name="project", address=ADDRESS, client=client
+ ) as tracer,
+ pytest.raises(LangSmithUserError, match="not both"),
+ ):
+ tracer.on_chain_start({}, {}, run_id=uuid4())
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+
+
+def test_trace_callbacks_resolve_environment_address(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv("LANGSMITH_TRACING", "true")
+ monkeypatch.setenv("LANGSMITH_AGENT_ID", "env-agent")
+ monkeypatch.setenv("LANGSMITH_AGENT_ENVIRONMENT", "staging")
+ get_env_var.cache_clear() # type: ignore[attr-defined]
+ get_tracer_project.cache_clear()
+ client = MagicMock(spec=Client)
+ with tracing_context(client=client):
+ callbacks = _get_trace_callbacks()
+ assert isinstance(callbacks, list)
+ tracer = callbacks[0]
+ assert isinstance(tracer, LangChainTracer)
+ run_id = uuid4()
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ tracer.on_chain_end({}, run_id=run_id)
+ assert client.create_run.call_args.kwargs["address"] == ENV_ADDRESS
+
+
[email protected](
+ "destination", [{"address": ADDRESS}, {"project_name": "parent"}]
+)
+def test_tracer_kwargs_parent_destination(destination: dict[str, str]) -> None:
+ client = MagicMock(spec=Client)
+ parent = RunTree(name="parent", ls_client=client, **destination)
+ with tracing_context(
+ parent=parent, project_name="ambient", tags=["tag"], metadata={"key":
"value"}
+ ):
+ kwargs = _get_tracer_kwargs()
+ assert kwargs["project_name"] == destination.get("project_name")
+ assert kwargs["address"] == destination.get("address")
+ assert kwargs["client"] is client
+ assert kwargs["tags"] == ["tag"]
+ assert kwargs["metadata"] == {"key": "value"}
+ explicit = _get_tracer_kwargs("explicit")
+ assert explicit["project_name"] == "explicit"
+ assert explicit["address"] is None
+
+
+def test_tracer_kwargs_leave_environment_to_sdk(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv("LANGSMITH_PROJECT", "env-project")
+ with tracing_context():
+ kwargs = _get_tracer_kwargs()
+ assert kwargs["project_name"] is None
+ assert kwargs["address"] is None
+
+
[email protected]("project_name", [None, "conflicting"])
+def test_trace_callbacks_explicit_address(project_name: str | None) -> None:
+ client = MagicMock(spec=Client)
+ with tracing_context(enabled=True, client=client, project_name="ambient"):
+ callbacks = _get_trace_callbacks(project_name, address=ADDRESS)
+ assert isinstance(callbacks, list)
+ tracer = callbacks[0]
+ assert isinstance(tracer, LangChainTracer)
+ if project_name is not None:
+ with pytest.raises(LangSmithUserError, match="not both"):
+ tracer.on_chain_start({}, {}, run_id=uuid4())
+ client.create_run.assert_not_called()
+ else:
+ run_id = uuid4()
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ tracer.on_chain_end({}, run_id=run_id)
+ assert client.create_run.call_args.kwargs["address"] == ADDRESS
+
+
+def test_chain_group_explicit_address() -> None:
+ client = MagicMock(spec=Client)
+ with (
+ tracing_context(enabled=True, client=client, project_name="ambient"),
+ trace_as_chain_group("group", address=ADDRESS) as manager,
+ ):
+ run = manager.on_chain_start({}, {})
+ run.on_chain_end({})
+ assert client.create_run.call_count == 2
+ for call in client.create_run.call_args_list:
+ assert call.kwargs["address"] == ADDRESS
+ assert call.kwargs.get("session_name") is None
+
+
+async def test_async_chain_group_explicit_address() -> None:
+ client = MagicMock(spec=Client)
+ with tracing_context(enabled=True, client=client, project_name="ambient"):
+ async with atrace_as_chain_group("group", address=ADDRESS) as manager:
+ run = await manager.on_chain_start({}, {})
+ await run.on_chain_end({})
+ assert client.create_run.call_count == 2
+ for call in client.create_run.call_args_list:
+ assert call.kwargs["address"] == ADDRESS
+ assert call.kwargs.get("session_name") is None
+
+
[email protected]("root_address", [False, True])
[email protected]("replica_mode", ["inherit", "project", "address"])
+def test_replica_destinations(*, root_address: bool, replica_mode: str) ->
None:
+ client = MagicMock(spec=Client)
+ replica_client = MagicMock(spec=Client)
+ replica: WriteReplica = {"client": replica_client, "primary": False}
+ replica_address = address.agent_address("replica-agent", "staging")
+ if replica_mode == "project":
+ replica["project_name"] = "replica-project"
+ elif replica_mode == "address":
+ replica["address"] = replica_address
+ tracer = LangChainTracer(
+ project_name=None if root_address else "root-project",
+ address=ADDRESS if root_address else None,
+ client=client,
+ )
+ parent_id, child_id = uuid4(), uuid4()
+ with tracing_context(replicas=[replica]):
+ tracer.on_chain_start({}, {}, run_id=parent_id)
+ tracer.on_chain_start({}, {}, run_id=child_id, parent_run_id=parent_id)
+ tracer.on_chain_end({}, run_id=child_id)
+ tracer.on_chain_end({}, run_id=parent_id)
+ expected_address = (
+ replica_address
+ if replica_mode == "address"
+ else ADDRESS
+ if replica_mode == "inherit" and root_address
+ else None
+ )
+ expected_project = (
+ "replica-project"
+ if replica_mode == "project"
+ else "root-project"
+ if replica_mode == "inherit" and not root_address
+ else None
+ )
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+ assert replica_client.create_run.call_count == 2
+ assert replica_client.update_run.call_count == 2
+ creates = [call.kwargs for call in
replica_client.create_run.call_args_list]
+ updates = [call.kwargs for call in
replica_client.update_run.call_args_list]
+ for payload in creates + updates:
+ assert payload.get("address") == expected_address
+ assert payload.get("session_name") == expected_project
+ assert {payload["run_id"] for payload in updates} == {
+ payload["id"] for payload in creates
+ }
+
+
+def test_replica_preserves_primary_ids() -> None:
+ client = MagicMock(spec=Client)
+ primary_client = MagicMock(spec=Client)
+ secondary_client = MagicMock(spec=Client)
+ secondary_address = address.agent_address("secondary-agent", "staging")
+ run_id = uuid4()
+ tracer = LangChainTracer(address=ADDRESS, client=client)
+ with tracing_context(
+ replicas=[
+ {"client": primary_client, "primary": True},
+ {"client": secondary_client, "address": secondary_address},
+ ]
+ ):
+ tracer.on_chain_start({}, {}, run_id=run_id)
+ tracer.on_chain_end({}, run_id=run_id)
+ assert primary_client.create_run.call_args.kwargs["id"] == run_id
+ assert primary_client.update_run.call_args.kwargs["run_id"] == run_id
+ assert primary_client.create_run.call_args.kwargs["address"] == ADDRESS
+ secondary_id = secondary_client.create_run.call_args.kwargs["id"]
+ assert secondary_id != run_id
+ assert secondary_client.update_run.call_args.kwargs["run_id"] ==
secondary_id
+ assert secondary_client.create_run.call_args.kwargs["address"] ==
secondary_address
+ client.create_run.assert_not_called()
+
+
+def test_replica_conflicting_destination() -> None:
+ client = MagicMock(spec=Client)
+ tracer = LangChainTracer(address=ADDRESS, client=client)
+ with (
+ tracing_context(replicas=[{"project_name": "conflict", "address":
ADDRESS}]),
+ pytest.raises(LangSmithUserError, match="not both"),
+ ):
+ tracer.on_chain_start({}, {}, run_id=uuid4())
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+
+
+def test_addressed_parent_replica_inherited_outside_context() -> None:
+ client = MagicMock(spec=Client)
+ replica_client = MagicMock(spec=Client)
+ parent = RunTree(
+ name="parent",
+ address=ADDRESS,
+ ls_client=client,
+ replicas=[{"client": replica_client, "project_name":
"replica-project"}],
+ )
+ tracer = LangChainTracer(client=client)
+ tracer.run_map[str(parent.id)] = parent
+ tracer.order_map[parent.id] = (parent.trace_id, parent.dotted_order)
+ run_id = uuid4()
+ tracer.on_chain_start({}, {}, run_id=run_id, parent_run_id=parent.id)
+ tracer.on_chain_end({}, run_id=run_id)
+ replica_client.create_run.assert_called_once()
+ replica_client.update_run.assert_called_once()
+ assert (
+ replica_client.create_run.call_args.kwargs["session_name"] ==
"replica-project"
+ )
+ assert replica_client.create_run.call_args.kwargs.get("address") is None
+ assert parent.child_runs[0].address == ADDRESS
+ client.create_run.assert_not_called()
+
+
[email protected]("use_address", [False, True])
+def test_chain_group_tracing_disabled(*, use_address: bool) -> None:
+ client = MagicMock(spec=Client)
+ with (
+ tracing_context(enabled=False, client=client),
+ trace_as_chain_group(
+ "group", address=ADDRESS if use_address else None
+ ) as manager,
+ ):
+ assert not any(
+ isinstance(handler, LangChainTracer) for handler in
manager.handlers
+ )
+ run = manager.on_chain_start({}, {})
+ run.on_chain_end({})
+ manager.on_chain_end({})
+ assert manager.ended
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
+
+
[email protected]("use_address", [False, True])
+async def test_async_chain_group_tracing_disabled(*, use_address: bool) ->
None:
+ client = MagicMock(spec=Client)
+ with tracing_context(enabled=False, client=client):
+ async with atrace_as_chain_group(
+ "group", address=ADDRESS if use_address else None
+ ) as manager:
+ assert not any(
+ isinstance(handler, LangChainTracer) for handler in
manager.handlers
+ )
+ run = await manager.on_chain_start({}, {})
+ await run.on_chain_end({})
+ await manager.on_chain_end({})
+ assert manager.ended
+ client.create_run.assert_not_called()
+ client.update_run.assert_not_called()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langchain_core-1.6.10/uv.lock
new/langchain_core-1.6.12/uv.lock
--- old/langchain_core-1.6.10/uv.lock 2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.6.12/uv.lock 2020-02-02 01:00:00.000000000 +0100
@@ -8,8 +8,10 @@
"python_full_version == '3.14.*' and platform_python_implementation !=
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation ==
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation !=
'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation ==
'PyPy' and sys_platform == 'emscripten'",
+ "python_full_version == '3.12.*' and platform_python_implementation !=
'PyPy' and sys_platform == 'emscripten'",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'
and sys_platform == 'emscripten')",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'
and sys_platform == 'emscripten')",
"python_full_version < '3.11' and platform_python_implementation ==
'PyPy'",
"python_full_version < '3.11' and platform_python_implementation !=
'PyPy'",
]
@@ -619,6 +621,19 @@
]
[[package]]
+name = "httpcore2"
+version = "2.13.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "h11", marker = "python_full_version != '3.12.*' or sys_platform
!= 'emscripten'" },
+ { name = "truststore", marker = "python_full_version != '3.12.*' or
sys_platform != 'emscripten'" },
+]
+sdist = { url =
"https://files.pythonhosted.org/packages/cb/f3/1db7aa2bc2524062192bb0e0323969492d1883152a232fe36eea65f4e35c/httpcore2-2.13.1.tar.gz",
hash =
"sha256:e0aa977abe17e69a3b820a24542a6fa88702676d83880b8d194dcd18408e5103", size
= 68071, upload-time = "2026-09-23T07:47:22.372Z" }
+wheels = [
+ { url =
"https://files.pythonhosted.org/packages/09/ba/a4568248771ce81957bfb7cc600264a40fbcda092391ee1c415c50be4bea/httpcore2-2.13.1-py3-none-any.whl",
hash =
"sha256:e1e05d4f25f7d7d496bfb96748f6f4b67657b03da069b3a68c36069f3db73d0a", size
= 83423, upload-time = "2026-09-23T07:47:19.365Z" },
+]
+
+[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
@@ -634,12 +649,38 @@
]
[[package]]
+name = "httpx2"
+version = "2.13.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio", marker = "sys_platform != 'emscripten'" },
+ { name = "httpcore2", marker = "sys_platform != 'emscripten'" },
+ { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and
sys_platform == 'emscripten'" },
+ { name = "idna" },
+ { name = "truststore", marker = "sys_platform != 'emscripten'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url =
"https://files.pythonhosted.org/packages/d5/44/474bef2a0e9d90f1715d32cb98b0738695ca17ba324095fb2497ed7fbd59/httpx2-2.13.1.tar.gz",
hash =
"sha256:e48744a19e3af5ee48313d0ce5fe941d5422fae5705ea922a4aabf94d7800dfa", size
= 100405, upload-time = "2026-09-23T07:47:23.052Z" }
+wheels = [
+ { url =
"https://files.pythonhosted.org/packages/d8/9c/6fe8931fd9f381042a9e4c7d5a7b4cbf7016b252bec0c99a49fce42c3326/httpx2-2.13.1-py3-none-any.whl",
hash =
"sha256:6dff50fabc270ee5fd25d845d0b078ed20564579744d6d962850975996d2f9a4", size
= 95597, upload-time = "2026-09-23T07:47:20.995Z" },
+]
+
+[[package]]
+name = "httpx2-jsfetch"
+version = "1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url =
"https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz",
hash =
"sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size
= 6872, upload-time = "2026-08-07T00:13:07.492Z" }
+wheels = [
+ { url =
"https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl",
hash =
"sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size
= 6382, upload-time = "2026-08-07T00:13:06.567Z" },
+]
+
+[[package]]
name = "idna"
-version = "3.15"
+version = "3.20"
source = { registry = "https://pypi.org/simple" }
-sdist = { url =
"https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz",
hash =
"sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size
= 199245, upload-time = "2026-05-12T22:45:57.011Z" }
+sdist = { url =
"https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz",
hash =
"sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size
= 216463, upload-time = "2026-09-17T14:11:04.752Z" }
wheels = [
- { url =
"https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl",
hash =
"sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size
= 72340, upload-time = "2026-05-12T22:45:55.733Z" },
+ { url =
"https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl",
hash =
"sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size
= 69583, upload-time = "2026-09-17T14:11:03.168Z" },
]
[[package]]
@@ -713,8 +754,10 @@
"python_full_version == '3.14.*' and platform_python_implementation !=
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation ==
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation !=
'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation ==
'PyPy' and sys_platform == 'emscripten'",
+ "python_full_version == '3.12.*' and platform_python_implementation !=
'PyPy' and sys_platform == 'emscripten'",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'
and sys_platform == 'emscripten')",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'
and sys_platform == 'emscripten')",
]
dependencies = [
{ name = "colorama", marker = "python_full_version >= '3.11' and
sys_platform == 'win32'" },
@@ -1087,7 +1130,7 @@
[[package]]
name = "langchain-core"
-version = "1.6.10"
+version = "1.6.12"
source = { editable = "." }
dependencies = [
{ name = "httpx" },
@@ -1141,7 +1184,7 @@
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "jsonpatch", specifier = ">=1.33.0,<2.0.0" },
{ name = "langchain-protocol", specifier = ">=0.0.17" },
- { name = "langsmith", specifier = ">=0.3.45,<1.0.0" },
+ { name = "langsmith", specifier = ">=0.14.9,<1.0.0" },
{ name = "packaging", specifier = ">=23.2.0" },
{ name = "pydantic", specifier = ">=2.7.4,<3.0.0" },
{ name = "pyyaml", specifier = ">=5.3.0,<7.0.0" },
@@ -1283,12 +1326,12 @@
[[package]]
name = "langsmith"
-version = "0.11.1"
+version = "0.14.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "distro" },
- { name = "httpx" },
+ { name = "httpx2" },
{ name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
{ name = "packaging" },
{ name = "pydantic" },
@@ -1301,9 +1344,9 @@
{ name = "xxhash" },
{ name = "zstandard" },
]
-sdist = { url =
"https://files.pythonhosted.org/packages/cc/57/7b6c11080c9e082ebf1456a2e2372fae8f23a85a5ae2869bdd5ab9a6507d/langsmith-0.11.1.tar.gz",
hash =
"sha256:47998977366acb3ba3093881fd465cbf11a5f8c2f4e87e40a17dda203f6dedf4", size
= 4814724, upload-time = "2026-08-19T15:47:44.116Z" }
+sdist = { url =
"https://files.pythonhosted.org/packages/81/78/53965f83edcaeb6a2e5eaf489951e1431752ba729eb6df24f4455b8470e9/langsmith-0.14.9.tar.gz",
hash =
"sha256:e54c41dcc06cbad971364fe5ecaaf976c11b418bdd0a125f816e2534c6caf2f2", size
= 5008113, upload-time = "2026-10-15T13:34:43.455Z" }
wheels = [
- { url =
"https://files.pythonhosted.org/packages/f1/85/0ad6df25588122760b2b40ec182eafc55532c9e66015c83e16675bd34a29/langsmith-0.11.1-py3-none-any.whl",
hash =
"sha256:cfc3437a9cf27440cd0095c24df945edbceb6df10b579bc8b3980b4ad367835f", size
= 744589, upload-time = "2026-08-19T15:47:42.043Z" },
+ { url =
"https://files.pythonhosted.org/packages/94/2d/eb8499785c05745e7b01822e3584bb8611b6684da811e776b3acab9ea024/langsmith-0.14.9-py3-none-any.whl",
hash =
"sha256:ae8a41f6b3712fe344c9163e16e06c6c1294623aaf5f565874da65f36482e7e4", size
= 862514, upload-time = "2026-10-15T13:34:41.319Z" },
]
[[package]]
@@ -1813,8 +1856,10 @@
"python_full_version == '3.14.*' and platform_python_implementation !=
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation ==
'PyPy'",
"python_full_version == '3.13.*' and platform_python_implementation !=
'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy'",
- "python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy'",
+ "python_full_version == '3.12.*' and platform_python_implementation ==
'PyPy' and sys_platform == 'emscripten'",
+ "python_full_version == '3.12.*' and platform_python_implementation !=
'PyPy' and sys_platform == 'emscripten'",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation == 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'
and sys_platform == 'emscripten')",
+ "(python_full_version >= '3.11' and python_full_version < '3.13' and
platform_python_implementation != 'PyPy' and sys_platform != 'emscripten') or
(python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'
and sys_platform == 'emscripten')",
]
sdist = { url =
"https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz",
hash =
"sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size
= 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
wheels = [
@@ -2024,7 +2069,7 @@
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "ptyprocess" },
+ { name = "ptyprocess", marker = "python_full_version != '3.12.*' or
sys_platform != 'emscripten'" },
]
sdist = { url =
"https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz",
hash =
"sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size
= 166450, upload-time = "2023-11-25T09:07:26.339Z" }
wheels = [
@@ -3018,6 +3063,15 @@
]
[[package]]
+name = "truststore"
+version = "0.10.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url =
"https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz",
hash =
"sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size
= 26169, upload-time = "2025-08-12T18:49:02.73Z" }
+wheels = [
+ { url =
"https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl",
hash =
"sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size
= 18660, upload-time = "2025-08-12T18:49:01.46Z" },
+]
+
+[[package]]
name = "types-pyyaml"
version = "6.0.12.20260408"
source = { registry = "https://pypi.org/simple" }