fix(history): return only the fields the UI renders - #39
Merged
Merged
Conversation
`toDict()` is LangChain's serialization, not our wire contract, and casting it to `MessageResponse` shipped every field it carries: `content_blocks` (a byte-identical copy of `content`), token counts and provider metadata no component reads. A 24-message thread went out at 34.7 KB. Project explicitly instead — 37% smaller on that thread, with the OpenAPI response narrowed from an open object to the real shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GET /api/agent/history/:threadIdreturnedmsg.toDict()cast toMessageResponse— LangChain's own serialization, not our wire contract. The cast asserted a shape rather than producing one, so every field LangChain carries rode along unnoticed:content_blocks(a byte-identical duplicate ofcontent, verified on all 65 AI messages in the sample),usage_metadata,response_metadata,invalid_tool_calls,ai.name(always"model") andtool.metadata(the same@langchain/coreversion on every row). None have a reader anywhere insrc/.A 24-message thread went out at 34.7 KB.
projectHistorynow builds the response explicitly. Also drops an AIcontentarray when it only restatestool_calls— the same call was serialized three times.34.7 KB → 21.8 KB (−37%), measured live on the same thread.
Verification
Diffed the live before/after payload field by field: every UI-read field byte-identical, order preserved, all 10 tool results and the chart artifact intact. The only differences were three
tool_calls: []empty arrays now absent — both consumers are guarded (Array.isArray(...) && length > 0,|| []), so behavior is unchanged.Kept deliberately: the human
contentarray passes through untouched, sinceHumanMessageextracts file attachments from inside it for checkpoint-loaded messages; andtool.status, which costs ~0.3% and is where an approval outcome would surface.historyProjection.test.tspins the contract (DB-free). 274 tests pass,tsc --noEmitclean.🤖 Generated with Claude Code