Repository navigation
fix(langsmith): json.dumps with default=str so non-serializable metadata does not crash batch flush - #42424
Conversation
…ata does not crash batch flush Serialize the runs/batch payload with json.dumps(default=str, allow_nan=False) and send it as content= with an explicit Content-Type, so datetime, Decimal and similar metadata values no longer raise TypeError and drop the batch. Forward content= on the AsyncHTTPHandler retry path so a retried batch re-sends the identical body Replaces #39133, which was cut from the retired staging branch and conflicts with main Co-authored-by: Damien Smrt <dsmrt@users.noreply.github.com> Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
bugbot run |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…client-injecting handler Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3f038e0. Configure here.
TLDR
Problem this solves:
datetime,Decimaland similar Python valuesjson=cannot encode them, so the whole batch flush raisesTypeErrorHow it solves it:
json.dumps(default=str, allow_nan=False)and send it ascontent=allow_nan=Falsestill rejects NaN and Infinity instead of emitting invalid JSONAsyncHTTPHandlerretry path now forwardscontent=, so a retried batch re-sends the same bodyReplaces #39133 (same five-file change, re-based on
main; the original branch was cut from the retired staging branch and conflicts withmain). The change was written by the original contributor and is credited with aCo-authored-bytrailerUser Flow
Before: a developer with Langsmith logging enabled gets no runs when request metadata contains non-serializable values
{"created_at": datetime(...), "spend": Decimal("0.0042")}Langsmith Layer Error ... TypeError: Object of type datetime is not JSON serializableAfter: the same request lands in Langsmith with the values stored as strings
created_at: "2026-01-02 03:04:05+00:00"andspend: "0.0042"Relevant issues
Supersedes #39133
Affected release
Linear ticket
Resolves LIT-8310
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Tests: unit regressions in
tests/test_litellm/integrations/test_langsmith_init.py,tests/test_litellm/llms/custom_httpx/test_http_handler.py(retry forwardscontent=, red when thecontent=forwarding is removed at the post, patch and delete retry sites) andtests/logging_callback_tests/test_langsmith_unit_test.py, plus a live SDK e2etests/e2e/logging/test_langsmith_batch_serialization_e2e.pyagainst the real OpenAI and LangSmith EU APIs (fails at the poll deadline on13b37487, passes at this tip). The bug is only reachable through the SDK path since the proxy JSON-decodes request metadata, which is why the e2e driveslitellm.acompletionin-processRig: two live litellm proxies per leg (aiohttp + pure-httpx twins), each with a forwarding recorder in front of the real LangSmith EU API (
https://eu.api.smith.langchain.com). Head3f038e0a2f246c82b2e612c9fe1ea73f4cd201bb(PR tip) on :4120/:4122, base13b374873d7b75f400fc1702c759d68195620f62(origin/main) on :4121/:4123. Realgpt-4o-mini/claude-haiku-4-5calls. Commits after4dedd083touch tests and the CI e2e lane selector only;litellm/is identical across every head shaBefore (13b3748)
acompletionwith langsmith callback, metadata holdingdatetimeandDecimal:Recorder observed: no
/api/v1/runs/batchrequest at all (batch died in json.dumps).GET https://eu.api.smith.langchain.com/api/v1/runs/e658efeb-f8ce-4762-9f7b-be12bc066e25->404D — retry cell,
drop_firston recorder thencurl :4123 /v1/chat/completions:POST :4121 /v1/chat/completions->chatcmpl-EQnHZwwZXYcjt2y2gLFJGRdzKlo45, run66442cf5-4d2a-4642-a17a-658b2695cbbc, GET 200POST :4121 /v1/messages->msg_011CfHvMoKxyB6Au3HH4XyZq, run22460677-d871-4d2f-a2d1-1820f3aa1226, GET 200POST :4121 /v1/responses->resp_yMuoGk2jpxnK8ZGsdoxJxEaJDgyD..., runa4369bba-31f6-4fa3-83f6-c5e46db89fc1, GET 200After (3f038e0, PR tip)
Recorder observed
POST /api/v1/runs/batch->202,Content-Type: application/json, batch bodyextra.requester_metadata={"cell": "N2", "created_at": "2026-01-02 03:04:05+00:00", "spend": "0.0042"}(non-native values serialized as theirstr()form).GET https://eu.api.smith.langchain.com/api/v1/runs/d7a7769c-1a4e-453f-aae5-8fa53a510ca4->200N3, same command with
float("nan")in metadata:ValueError: Out of range float values are not JSON compliant: nan, batch dropped, nothing on the wire (allow_nan=False, same as base)D, retry cell,
drop_firston recorder thencurl :4122 /v1/chat/completions:POST :4120 /v1/chat/completions->chatcmpl-EQpQFBST4glkSJrPyamyvuSlcKSm1, runb565c3e8-b1cb-4806-bd39-8d9259886997, GET 200POST :4120 /v1/messages->msg_011CfJ6p4sFZ9avXEGGU9PBA, rundada6664-9f12-4527-958e-5004c01fcb78, GET 200POST :4120 /v1/responses->resp_9ThGPQ6CAIkSpb0LEUDy_IBDp8ACpcfUJ_B, run45d1b3cc-f145-45c1-bbb4-e01c54ce7279, GET 200pytest tests/e2e/logging/test_langsmith_batch_serialization_e2e.pyagainst real OpenAI and LangSmith EU: 1 passed, run893cadbf-1114-4dd8-8a7d-542c942787feread back with thestr()forms of the datetime and Decimal (the same file copied onto13b37487fails at the poll deadline,TypeError: Object of type datetime is not JSON serializablein the log)The full live audit (happy-path matrix on chat, messages and responses, streaming and non-streaming, OpenAI and Anthropic SDKs and curl, sad paths, key and team level LangSmith settings, idempotency and chaos cells) is in the comment below
Screen recording (before on
13b37487, after on5a877c55; the only later commit3f038e0achanges.github/e2e-stack/select_tests.py, nothing underlitellm/ortests/) is posted in the requesting Slack thread, screenshots in the PR comment: base N2chatcmpl-EQoDbXRryr7KkEpqYJ20t4Fb3n9eVraised the TypeError, run11b42a8b-9607-4a71-bf48-7768da4e025eGET 404; tip N2chatcmpl-EQoET2g6714tt3LfOg3hW9gpOVxVGbatch 202, run4503fe51-3e37-4fc3-a1cf-b580846c4274GET 200; retry Dchatcmpl-EQoGmKUG6tiXsfuF7ZihRhVd9VWJu, attempt 2 identical body sha25662729496e6f91c5e9f0559cd7f3dd134ae97132b9753ab214b8c1f2fa82a01a1, runf78a9633-e0fd-48d7-827a-1d4352df71aaGET 200Type
🐛 Bug Fix
Caveats (if any)
Low
str; the logged payload type differs from the original Python typehttpx.ReadError, which the handler does not retry (same onmain)Final Attestation
Link to Devin session: https://app.devin.ai/sessions/63688ea4e4e941ef82072628a246d466
Open in Devin Desktop: https://app.devin.ai/desktop/session/63688ea4e4e941ef82072628a246d466?variant=devin
Requested by: @yucheng-berri