core: use run tree post/patch (#31500)

Use run post/patch
This commit is contained in:
lc-arjun 2025-06-05 14:05:57 -07:00 committed by GitHub
parent 73655b0ca8
commit 35ae5eab4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1619 additions and 1642 deletions

View File

@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import warnings
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, Optional, Union from typing import TYPE_CHECKING, Any, Optional, Union
@ -12,7 +11,6 @@ from uuid import UUID
from langsmith import Client from langsmith import Client
from langsmith import run_trees as rt from langsmith import run_trees as rt
from langsmith import utils as ls_utils from langsmith import utils as ls_utils
from pydantic import PydanticDeprecationWarning
from tenacity import ( from tenacity import (
Retrying, Retrying,
retry_if_exception_type, retry_if_exception_type,
@ -67,21 +65,6 @@ def _get_executor() -> ThreadPoolExecutor:
return _EXECUTOR return _EXECUTOR
def _run_to_dict(run: Run, *, exclude_inputs: bool = False) -> dict:
# TODO: Update once langsmith moves to Pydantic V2 and we can swap run.dict for
# run.model_dump
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=PydanticDeprecationWarning)
res = {
**run.dict(exclude={"child_runs", "inputs", "outputs"}),
"outputs": run.outputs,
}
if not exclude_inputs:
res["inputs"] = run.inputs
return res
class LangChainTracer(BaseTracer): class LangChainTracer(BaseTracer):
"""Implementation of the SharedTracer that POSTS to the LangChain endpoint.""" """Implementation of the SharedTracer that POSTS to the LangChain endpoint."""
@ -218,18 +201,11 @@ class LangChainTracer(BaseTracer):
def _persist_run_single(self, run: Run) -> None: def _persist_run_single(self, run: Run) -> None:
"""Persist a run.""" """Persist a run."""
try: try:
run_dict = _run_to_dict(run) run.extra["runtime"] = get_runtime_environment()
run_dict["tags"] = self._get_tags(run) run.tags = self._get_tags(run)
extra = run_dict.get("extra", {}) if run.ls_client is not self.client:
extra["runtime"] = get_runtime_environment() run.ls_client = self.client
run_dict["extra"] = extra run.post()
inputs_ = run_dict.get("inputs")
if inputs_ and (len(inputs_) > 1 or bool(next(iter(inputs_.values())))):
inputs_is_truthy = True
else:
inputs_is_truthy = False
run.extra["inputs_is_truthy"] = inputs_is_truthy
self.client.create_run(**run_dict, project_name=self.project_name)
except Exception as e: except Exception as e:
# Errors are swallowed by the thread executor so we need to log them here # Errors are swallowed by the thread executor so we need to log them here
log_error_once("post", e) log_error_once("post", e)
@ -238,10 +214,7 @@ class LangChainTracer(BaseTracer):
def _update_run_single(self, run: Run) -> None: def _update_run_single(self, run: Run) -> None:
"""Update a run.""" """Update a run."""
try: try:
exclude_inputs = run.extra.get("inputs_is_truthy", False) run.patch(exclude_inputs=run.extra.get("inputs_is_truthy", False))
run_dict = _run_to_dict(run, exclude_inputs=exclude_inputs)
run_dict["tags"] = self._get_tags(run)
self.client.update_run(run.id, **run_dict)
except Exception as e: except Exception as e:
# Errors are swallowed by the thread executor so we need to log them here # Errors are swallowed by the thread executor so we need to log them here
log_error_once("patch", e) log_error_once("patch", e)

View File

@ -1,3 +1,3 @@
"""langchain-core version information and utilities.""" """langchain-core version information and utilities."""
VERSION = "0.3.63" VERSION = "0.3.64"

View File

@ -7,7 +7,7 @@ authors = []
license = {text = "MIT"} license = {text = "MIT"}
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = [
"langsmith<0.4,>=0.1.126", "langsmith<0.4,>=0.3.45",
"tenacity!=8.4.0,<10.0.0,>=8.1.0", "tenacity!=8.4.0,<10.0.0,>=8.1.0",
"jsonpatch<2.0,>=1.33", "jsonpatch<2.0,>=1.33",
"PyYAML>=5.3", "PyYAML>=5.3",
@ -16,7 +16,7 @@ dependencies = [
"pydantic>=2.7.4", "pydantic>=2.7.4",
] ]
name = "langchain-core" name = "langchain-core"
version = "0.3.63" version = "0.3.64"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
readme = "README.md" readme = "README.md"

View File

@ -53,6 +53,8 @@ def test_lazy_load() -> None:
} }
expected.append( expected.append(
Document(example.inputs["first"]["second"].upper(), metadata=metadata) Document(example.inputs["first"]["second"].upper(), metadata=metadata)
if example.inputs
else None
) )
actual = list(loader.lazy_load()) actual = list(loader.lazy_load())
assert expected == actual assert expected == actual

View File

@ -130,7 +130,7 @@ def test_config_traceable_handoff() -> None:
parent_run_id = None parent_run_id = None
for name in ordered_names: for name in ordered_names:
id_ = name_to_body[name]["id"] id_ = name_to_body[name]["id"]
parent_run_id_ = name_to_body[name]["parent_run_id"] parent_run_id_ = name_to_body[name].get("parent_run_id")
if parent_run_id_ is not None: if parent_run_id_ is not None:
assert parent_run_id == parent_run_id_ assert parent_run_id == parent_run_id_
assert name in name_to_body assert name in name_to_body
@ -199,7 +199,7 @@ async def test_config_traceable_async_handoff() -> None:
parent_run_id = None parent_run_id = None
for name in ordered_names: for name in ordered_names:
id_ = name_to_body[name]["id"] id_ = name_to_body[name]["id"]
parent_run_id_ = name_to_body[name]["parent_run_id"] parent_run_id_ = name_to_body[name].get("parent_run_id")
if parent_run_id_ is not None: if parent_run_id_ is not None:
assert parent_run_id == parent_run_id_ assert parent_run_id == parent_run_id_
assert name in name_to_body assert name in name_to_body

View File

@ -139,7 +139,7 @@ class LangChainProjectNameTest(unittest.TestCase):
projects = [] projects = []
def mock_create_run(**kwargs: Any) -> Any: def mock_create_run(**kwargs: Any) -> Any:
projects.append(kwargs.get("project_name")) # noqa: B023 projects.append(kwargs.get("session_name")) # noqa: B023
return unittest.mock.MagicMock() return unittest.mock.MagicMock()
client.create_run = mock_create_run client.create_run = mock_create_run

File diff suppressed because it is too large Load Diff