What v3 event stream was silently losing — LangChain 1.4.8

langchain-core 1.4.8 + 1.3.10 (June 18): v3 metering gaps, BaseTool schema caching, and gpt-5.x routing correction.

What v3 event stream was silently losing — LangChain 1.4.8
Share

LangChain shipped two coordinated point releases on the same afternoon — and the most consequential fixes are buried in mechanics commits, not a marketing post. Here is what actually changed when you run pip install -U.

What's in langchain-core 1.4.8 and langchain 1.3.10

On 18 June 2026, the LangChain team cut two coordinated maintenance releases within minutes of each other: langchain-core moved 1.4.7→1.4.8 (merged 19:23 UTC) and the meta-package langchain moved 1.3.9→1.3.10 (merged 19:40 UTC), with the top-level GitHub release published at 19:43 UTC . Both are patch cuts inside the post-1.0 stable line — no major or minor bump, and no documented breaking changes in either package. Both require Python >=3.10,<4.0 .

Quick Answer: langchain-core 1.4.8 and langchain 1.3.10 are same-day patch releases from 18 June 2026 with no breaking changes. The notable fixes: v3 stream-events now preserves cached-token usage details, BaseTool schemas are memoized (~33 ms down to ~0.33 ms per pass), and SummarizationMiddleware switched to XML to keep multimodal URLs.

The release PRs themselves were largely plumbing — version.py, pyproject.toml, snapshots, and lockfile refreshes — so the user-facing work came from earlier PRs swept into these tags . Three runtime-relevant changes land in core; two land in the meta-package.

PackageChangeWhy it matters
langchain-core 1.4.8v3 stream-events usage fixRestores input_token_details/output_token_details on cached runs
langchain-core 1.4.8BaseTool.tool_call_schema memoizationCuts per-step tool-schema overhead on agent loops
langchain-core 1.4.8coro_with_context on Python <3.11Context propagation now matches 3.11+ behavior
langchain 1.3.10SummarizationMiddleware → XML serializationPreserves image/audio/video URL references in summaries
langchain 1.3.10Dated OpenAI snapshot detectionRoutes gpt-5.2/gpt-5.4 snapshots to provider-native structured output

The sections that follow annotate each change — what it broke before, what it fixes now, and whether the upgrade is worth scheduling .

The v3 event stream gap: cached call metering was incomplete

What v3 event stream was silently losing — LangChain 1.4.8

The most consequential runtime fix in langchain-core 1.4.8 closes a silent accounting hole in v3 streaming. Before this release, stream_events(version="v3") and astream_events(version="v3") did not forward input_token_details and output_token_details into the assembled message or the on_llm_end event . The usage metadata still reported aggregate input and output token counts, but the per-category breakdown that distinguishes fresh tokens from cached ones never made it onto the wire. If you metered cost from v3 events, you were billing against incomplete data.

For prompt-cached calls the gap had a direct dollar impact. Cached tokens were counted inside the total input-token figure, but the cache_read and cache_creation fields that explain why those tokens are cheaper were absent . A cost-attribution layer consuming v3 events — LangSmith tracers being the obvious case — would therefore treat cache reads as full-price input and over-count input cost. On workloads built around large reusable system prompts, where cache hits are the norm rather than the exception, that overstatement is not a rounding error.

The fix routes the breakdown fields through the UsageInfo wire type so the detail survives serialization end to end, and it raises a new lower bound: langchain-core 1.4.8 now requires langchain-protocol>=0.0.17 . That dependency bump is the practical signal to watch — if your lockfile pins an older protocol package, the usage detail will not flow even after you upgrade core, so verify the transitive resolve rather than assuming the version bump alone is enough.

"v3 on_llm_end usage now matches v2 and preserves the cache_read and cache_creation details intact." — langchain-core 1.4.8 release notes, validated against a live claude-sonnet-4-6 cached prompt (source: PR #38255)

That validation matters because it pins the fix to a real provider response rather than a synthetic fixture. The PR author confirmed the v3 path now reproduces the v2 usage shape on a genuine Claude cached run, which is the closest thing to an end-to-end regression guarantee these maintenance cuts offer . One residual edge remains worth your own test: the exact handling of repeated nested token-detail keys across many streamed chunks, which the release does not exhaustively document.

Memoized schemas in langchain-core 1.4.8: 33ms down to 0.33ms

The second runtime change in langchain-core 1.4.8 is a tool-calling performance fix: BaseTool.tool_call_schema now memoizes the generated Pydantic subset model per tool instance and caches its default model_json_schema()/schema() output, instead of rebuilding that model on every property access . In agent loops that bind many tools and convert their schemas on each step, that rebuild was quietly dominating the per-step cost. The fix turns a repeated computation into a one-time one.

The before-state is the headline. According to the PR, the old path reconstructed the subset model on every access at roughly 1.5 ms per tool, so a single pass over a 23-tool set cost about 33 ms — and middleware that runs this conversion repeatedly per agent step stacked up hundreds of milliseconds of overhead per cycle . With memoization, the same 23-tool pass measures about 0.33 ms on the warm path — roughly a 100× reduction . None of this changes the schemas themselves; it only stops recomputing identical output.

PathPer-tool cost23-tool passRepeated loops
Before 1.4.8 (cold rebuild each access)~1.5 ms~33 mshundreds of ms of middleware overhead per step
1.4.8 (memoized, warm path)~0.014 ms~0.33 msnegligible after first build

The tradeoff is the part worth reading carefully before you upgrade and assume free wins. Because the generated class and the default schema dict are now reused across accesses, any code that mutates the returned object in place will see shared-state effects — the next caller gets your mutation. The PR explicitly treats in-place mutation of the returned schema as an unsupported pattern . If you have middleware or custom tool wrappers that edit the dict from tool_call_schema to inject fields, route around it: copy first, then mutate, or build a fresh schema rather than patching the shared one.

For most teams the practical takeaway is simple. If your agents bind a handful of tools, the savings are real but small in absolute terms; if you run wide tool sets through per-step middleware, this is where the CPU time was going, and 1.4.8 reclaims most of it. The benchmark numbers come from the PR itself rather than an independent profile, so treat them as the author's measured figures and confirm against your own tool count and call frequency .

How dated snapshot identifiers bypassed provider-native schema enforcement

What v3 event stream was silently losing — LangChain 1.4.8

The meta-package's second functional fix corrects a quieter bug in create_agent(..., response_format=<schema>): it was matching model names against a hardcoded allowlist, so bare identifiers like gpt-5.2 and gpt-5.4 were recognized as provider-native structured-output models while dated snapshots such as gpt-5.2-2025-12-01 and gpt-5.4-2026-03-05 were not . The string didn't match, so the dated pin fell through the detection logic and never reached the provider-native path.

The consequence was silent, which is what makes it worth flagging. When detection failed, create_agent downgraded from ProviderStrategy to ToolStrategy — it stopped asking OpenAI to enforce the JSON schema server-side and instead simulated structured output through a tool call . That swap changes three things at once: the trace shape (a tool-call round-trip instead of a native structured response), the token accounting (tool-calling overhead lands in your usage metrics), and, most importantly, the guarantee. ProviderStrategy means OpenAI validates the output against your schema before returning it; ToolStrategy leaves that enforcement to the model's best effort. No exception was raised — the agent kept working, just with weaker constraints than you configured.

If you had pinned a dated snapshot precisely because you wanted reproducible builds and strict schema enforcement, you were getting only the first half before this release. The behavior was a downgrade hiding behind a passing test suite.

"Dated snapshots like gpt-5.2-2025-12-01 and gpt-5.4-2026-03-05 should use provider-native structured output; -pro variants remain excluded by design." — rationale documented in the langchain 1.3.10 fix (source: PR #38222)

The fix expands detection to cover the dated suffix pattern, so a snapshot like gpt-5.2-2025-12-01 now routes through ProviderStrategy the same way the bare identifier always did . The -pro variants stay excluded deliberately — that's a capability boundary, not the same oversight. The practical takeaway: if you pin OpenAI model versions for production agents and rely on server-side schema validation, upgrading to langchain 1.3.10 restores the enforcement you thought you already had, and it's worth diffing a sample trace before and after to confirm the strategy actually flipped back.

SummarizationMiddleware switched to XML to preserve multimodal content references

The same upgrade that fixes structured-output routing also changes how SummarizationMiddleware hands conversation history to the summarizer: in langchain 1.3.10 it now serializes that history as XML instead of the default buffer-string format . The reason is multimodal preservation. The old buffer-string path could silently drop URL-backed content blocks — image, audio, and video references attached to a message were flattened away before the summarizer ever saw them, so any summary built from a multimodal thread lost those pointers entirely .

XML serialization keeps those URL references intact in the summarizer context without expanding raw binary metadata into the prompt budget. That distinction matters: the goal is not to stuff base64 payloads or full metadata into the summary call, but to retain the addressable URL so downstream steps still know an image or clip existed and where to find it. The change was verified with a test confirming that an image URL — https://example.com/shared-image.png — now survives end-to-end into the summarizer input .

"Default serialization could drop URL-backed image, audio, and video content blocks; switching the summary format to XML keeps those references available without spending the token budget on raw metadata," per the LangChain release notes (source: docs.langchain.com release policy).

This affects any chain or workflow that runs long-context summarization over conversations containing non-text parts:

  • Image-bearing threads — vision agents whose history references screenshots or diagrams now keep those URLs in the compressed summary.
  • Audio and video — transcription or media-analysis loops retain their source links rather than discarding them at the summarization boundary.
  • Mixed multimodal agents — any pipeline that summarizes to stay under a context window no longer trades away media pointers to do so.

If your agents are text-only, this is a no-op. But if you summarize multimodal histories and have noticed images or clips quietly vanishing from later turns, 1.3.10 is the fix — and it ships as part of the meta-package, so you inherit it on the langchain bump itself .

The cryptography 46→48 jump and other housekeeping in these releases

What v3 event stream was silently losing — LangChain 1.4.8

Beyond the headline behavior fixes, both releases carry a batch of dependency bumps and typing work that is easy to skim past but worth a moment if you pin transitive dependencies. The meta-package langchain==1.3.10 bumps cryptography from 46.0.7 to 48.0.1 — a two-major-version leap — alongside aiohttp 3.14.0→3.14.1 and pyjwt 2.12.0→2.13.0 . The cryptography jump is the one to validate. Most LangChain users never touch it directly, but anyone with a hard pin or a constraints file selecting cryptography 46.x will hit a resolver conflict, and shops that compile from source on locked-down builders should test the upgrade before rolling it out.

langchain-core==1.4.8 carries its own security-oriented bumps: jupyter-server 2.18.0→2.20.0, tornado 6.5.6→6.5.7, and bleach 6.3.0→6.4.0 . These are dev/test-surface dependencies in core, so they rarely affect a production install, but they keep the monorepo's tooling current.

The typing work matters more for maintainers than for callers. Core 1.4.8 resolves disallow_any_generics issues and enables a mypy warn_unreachable configuration, tightening what the type checker will accept going forward . One change has observable runtime reach: on Python <3.11, coro_with_context now routes task creation through the supplied context so context propagation matches 3.11+ behavior — a quiet correction that could alter async edge cases if you depend on the old behavior .

Finally, the deserialization allowlist tests now use explicit allowed_objects= strings such as "core", signalling a stricter deserialization posture being enforced library-wide rather than relying on implicit defaults . None of this is breaking, but together it nudges the codebase toward tighter typing and safer loads.

The resolver nuance: langchain 1.3.10 doesn't force langchain-core 1.4.8

Upgrading the meta-package does not guarantee you get the core fixes. langchain 1.3.10 declares langchain-core>=1.4.7,<2.0.0 — the lower bound stays at 1.4.7, not 1.4.8. A pip install or upgrade of langchain can therefore leave an already-satisfactory langchain-core==1.4.7 in place, because the resolver has no reason to bump a version that already meets the constraint unless your lockfile or constraints file selects 1.4.8.

That matters because the more consequential changes shipped in this batch are all core-side: the v3 streaming usage-detail fix, the per-tool schema memoization, and the pre-3.11 context-propagation cleanup. If you want any of those, the dependency graph won't pull them for you. Pin it explicitly:

langchain==1.3.10
langchain-core==1.4.8

Before you do, check the transitive floor that 1.4.8 introduces. langchain-core 1.4.8 requires langchain-protocol>=0.0.17 and langsmith>=0.3.45,<1.0.0, alongside the unchanged pydantic>=2.7.4,<3.0.0. The langchain-protocol bump is the one tied to the v3 metering fix, since the corrected token breakdowns now route through that package's UsageInfo wire type. Confirm these don't collide with existing pins — especially if you constrain langsmith for a specific tracer integration.

The concrete takeaway: treat 1.3.10 and 1.4.8 as two separate decisions, not one. The releases carry no documented breaking changes, so the upgrade itself is low-risk, but the benefit you actually want lives in core. Pin langchain-core==1.4.8 deliberately, run your dependency resolver in a clean environment to surface any langchain-protocol or langsmith conflicts, and verify the resolved versions in your lockfile before deploying — otherwise you may ship 1.3.10 and still be metering cached calls on the old 1.4.7 path.

Frequently asked questions

Does pip install 'langchain==1.3.10' automatically install langchain-core 1.4.8?

Not guaranteed. langchain 1.3.10 declares langchain-core>=1.4.7,<2.0.0 , so a resolver that already has 1.4.7 installed and satisfactory can legitimately keep it. If you want the core-side changes — the v3 usage-detail fix and tool-schema memoization — run pip install 'langchain-core==1.4.8' explicitly, or pin it in your lockfile, and verify the resolved version before deploying.

Who is actually affected by the v3 on_llm_end usage-detail omission?

Anyone calling astream_events(version="v3") (or stream_events) together with a tracer — LangSmith or a custom one — that reads input_token_details or output_token_details. Before 1.4.8 those breakdowns were dropped from the assembled message and the on_llm_end event . The impact is sharpest for Claude prompt-cached calls, where cache_read/cache_creation fields were silently absent while cached tokens still counted toward total input — inflating reported cost. The author validated the fix against a live claude-sonnet-4-6 cached prompt, confirming v3 output matched v2 .

Is the BaseTool schema memoization safe if my code modifies the returned schema object?

No. In langchain-core 1.4.8, tool_call_schema memoizes the generated Pydantic subset model per tool instance and caches its default schema dict . The returned object is now shared across accesses for that tool, so in-place mutation — for example tool.tool_call_schema['properties']['x'] = ... — leaks into every subsequent read. Read-only inspection is safe; mutation is treated as unsupported. Audit any code that writes to the returned schema and copy it first if you need to edit.

Are there any breaking changes in langchain-core 1.4.8 or langchain 1.3.10?

None are documented; both are maintenance/patch cuts within the post-1.0 stable line, published 18 June 2026 . Three practical caveats remain. On Python <3.11, coro_with_context now routes task creation through the supplied context, so async context propagation matches 3.11+ and may differ from prior behavior . The transitive cryptography 46→48 bump is worth testing where it is pinned. And SummarizationMiddleware now emits XML rather than buffer-string format, a different serialization shape if anything parses that output .

Why does langchain-core 1.4.8 now require langchain-protocol>=0.0.17?

Because the v3 streaming-metering fix routes cache_read/cache_creation breakdowns through the UsageInfo wire type, and 0.0.17 is the first release of langchain-protocol to expose those fields on the wire . Earlier versions lacked the schema, so the lower bound was lifted to guarantee the detail fields survive serialization. langchain-core 1.4.8 also requires langsmith>=0.3.45,<1.0.0 and pydantic>=2.7.4,<3.0.0 , which is why a clean-environment resolve is the safest way to surface conflicts.