Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make pydantic model serialization consistent regardless of surrogates. #1405

Merged
merged 1 commit into from
Jan 10, 2025
Merged
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
2 changes: 1 addition & 1 deletion python/langsmith/_internal/_serde.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

Check notice on line 1 in python/langsmith/_internal/_serde.py

View workflow job for this annotation

GitHub Actions / benchmark

Benchmark results

........... create_5_000_run_trees: Mean +- std dev: 642 ms +- 57 ms ........... create_10_000_run_trees: Mean +- std dev: 1.30 sec +- 0.09 sec ........... create_20_000_run_trees: Mean +- std dev: 1.35 sec +- 0.10 sec ........... dumps_class_nested_py_branch_and_leaf_200x400: Mean +- std dev: 707 us +- 14 us ........... dumps_class_nested_py_leaf_50x100: Mean +- std dev: 25.5 ms +- 0.4 ms ........... dumps_class_nested_py_leaf_100x200: Mean +- std dev: 105 ms +- 2 ms ........... dumps_dataclass_nested_50x100: Mean +- std dev: 25.8 ms +- 0.6 ms ........... WARNING: the benchmark result may be unstable * the standard deviation (17.8 ms) is 24% of the mean (73.4 ms) Try to rerun the benchmark with more runs, values and/or loops. Run 'python -m pyperf system tune' command to reduce the system jitter. Use pyperf stats, pyperf dump and pyperf hist to analyze results. Use --quiet option to hide these warnings. dumps_pydantic_nested_50x100: Mean +- std dev: 73.4 ms +- 17.8 ms ........... dumps_pydanticv1_nested_50x100: Mean +- std dev: 198 ms +- 4 ms

Check notice on line 1 in python/langsmith/_internal/_serde.py

View workflow job for this annotation

GitHub Actions / benchmark

Comparison against main

+-----------------------------------------------+----------+------------------------+ | Benchmark | main | changes | +===============================================+==========+========================+ | dumps_pydanticv1_nested_50x100 | 220 ms | 198 ms: 1.11x faster | +-----------------------------------------------+----------+------------------------+ | create_5_000_run_trees | 668 ms | 642 ms: 1.04x faster | +-----------------------------------------------+----------+------------------------+ | create_10_000_run_trees | 1.29 sec | 1.30 sec: 1.00x slower | +-----------------------------------------------+----------+------------------------+ | dumps_class_nested_py_branch_and_leaf_200x400 | 705 us | 707 us: 1.00x slower | +-----------------------------------------------+----------+------------------------+ | dumps_class_nested_py_leaf_50x100 | 25.0 ms | 25.5 ms: 1.02x slower | +-----------------------------------------------+----------+------------------------+ | dumps_class_nested_py_leaf_100x200 | 103 ms | 105 ms: 1.02x slower | +-----------------------------------------------+----------+------------------------+ | dumps_dataclass_nested_50x100 | 25.1 ms | 25.8 ms: 1.03x slower | +-----------------------------------------------+----------+------------------------+ | create_20_000_run_trees | 1.31 sec | 1.35 sec: 1.03x slower | +-----------------------------------------------+----------+------------------------+ | dumps_pydantic_nested_50x100 | 69.9 ms | 73.4 ms: 1.05x slower | +-----------------------------------------------+----------+------------------------+ | Geometric mean | (ref) | 1.00x slower | +-----------------------------------------------+----------+------------------------+

import base64
import collections
Expand Down Expand Up @@ -146,7 +146,7 @@
logger.debug(f"Orjson serialization failed: {repr(e)}. Falling back to json.")
result = json.dumps(
obj,
default=_simple_default,
default=_serialize_json,
ensure_ascii=True,
).encode("utf-8")
try:
Expand Down
32 changes: 32 additions & 0 deletions python/tests/integration_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pydantic import BaseModel
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor

from langsmith._internal._serde import dumps_json
from langsmith.client import ID_TYPE, Client
from langsmith.evaluation import aevaluate, evaluate
from langsmith.schemas import (
Expand Down Expand Up @@ -1155,6 +1156,37 @@ def test_surrogates():
)


def test_fallback_json_serialization():
class Document(BaseModel):
content: str

raw_surrogates = [
("Hello\ud83d\ude00", "Hello😀"),
("Python\ud83d\udc0d", "Python🐍"),
("Surrogate\ud834\udd1e", "Surrogate𝄞"),
("Example\ud83c\udf89", "Example🎉"),
("String\ud83c\udfa7", "String🎧"),
("With\ud83c\udf08", "With🌈"),
("Surrogates\ud83d\ude0e", "Surrogates😎"),
("Embedded\ud83d\udcbb", "Embedded💻"),
("In\ud83c\udf0e", "In🌎"),
("The\ud83d\udcd6", "The📖"),
("Text\ud83d\udcac", "Text💬"),
("收花🙄·到", "收花🙄·到"),
]
pydantic_surrogates = [
(Document(content=item), expected) for item, expected in raw_surrogates
]

for item, expected in raw_surrogates:
output = dumps_json(item).decode("utf8")
assert f'"{expected}"' == output

for item, expected in pydantic_surrogates:
output = dumps_json(item).decode("utf8")
assert f'{{"content":"{expected}"}}' == output


def test_runs_stats():
langchain_client = Client()
# We always have stuff in the "default" project...
Expand Down
Loading