Preserve large-integer precision when decoding FunctionResultContent.Result - #717
Preserve large-integer precision when decoding FunctionResultContent.Result#717PratikDhanave wants to merge 2 commits into
Conversation
5a0fcc0 to
24228ce
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes lossy decoding of FunctionResultContent.Result by switching UnmarshalJSON to decode the Result payload with a number-preserving JSON decoder (UseNumber), preventing large integers (> 2^53) from being silently corrupted and making internal marshal/unmarshal round-trips stable for tool results.
Changes:
- Add an unmarshal-only serialization struct using
json.RawMessageforResult. - Decode
Resultviajson.Decoder+UseNumber()so numeric leaves becomejson.Number. - Add a unit test ensuring large-int result round-trips preserve exact value and marshaled JSON bytes remain identical.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| message/content.go | Decode FunctionResultContent.Result using UseNumber() via a json.RawMessage intermediary to preserve large-integer precision. |
| message/content_test.go | Add a round-trip test that asserts large integer results and re-marshaled JSON remain unchanged. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| t.ContentHeader = tmp.ContentHeader | ||
| t.CallID = tmp.CallID | ||
| t.Result = tmp.Result | ||
| // Decode the result with a number-preserving decoder so numeric leaves become | ||
| // json.Number rather than float64. This keeps large integers exact (values | ||
| // above 2^53 would otherwise be corrupted) and makes Marshal/Unmarshal |
There was a problem hiding this comment.
Good catch — fixed in c7d6580: UnmarshalJSON now resets t.Result to nil when tmp.Result is empty and t.Error to nil when tmp.Error is empty, so stale values on a non-zero receiver no longer stick. Added TestFunctionResultContentUnmarshalResetsStaleFields to lock the behavior.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Decode the JSON result with a number-preserving decoder so numeric leaves become json.Number instead of float64. Unmarshaling into an any previously corrupted integers above 2^53 (9007199254740993 became 9007199254740992), so a Marshal followed by Unmarshal was not idempotent for large-integer tool results, biting checkpoint save/restore and provider re-serialization.
c7d6580 to
e14efb5
Compare
Parity Review: ✅ ApprovedThis PR fixes a JSON round-trip precision bug in Scope: Internal serialization behavior only. No exported types, fields, or method signatures changed. The Cross-repo parity assessment:
This change closes a Go-specific gap relative to both .NET and Python. It is a correctness fix, not a divergence. No parity issues found. Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
What
FunctionResultContent.UnmarshalJSONdecoded the JSONResultinto ananyviajson.Unmarshal. Go'sencoding/jsondecodes JSON numbers intoanyasfloat64, so integers above 2^53 were silently corrupted on the round trip (9007199254740993became9007199254740992). BecauseMarshalJSONemits the exactint64, aMarshalfollowed byUnmarshalwas not idempotent for large-integer tool results, which bites checkpoint save/restore and provider re-serialization.This change adds a
serializedFunctionResultContentForUnmarshalstruct whoseResultis ajson.RawMessage, and decodes it inUnmarshalJSONwith a number-preserving decoder (json.NewDecoder(...).UseNumber()). Numeric leaves becomejson.Number, so large integers stay exact.MarshalJSONis unchanged, mirroring the existingserialized...ForUnmarshalpattern already used in this file for tool-approval content.Why (parity)
This matches .NET's
JsonElement-based preservation of tool-result payloads, where numeric values are not eagerly narrowed to a lossy floating-point representation. It is distinct from #528, which fixedFunctionCallContent.Arguments(astringfield); the result payload is an arbitraryanyand needed a separate fix.How it is tested
Added
TestFunctionResultContentRoundtripPreservesResultin the canonicalmessage/content_test.go. It marshals aFunctionResultContent, unmarshals it, and re-marshals, asserting the two JSON byte slices are byte-identical and that a large integer (9007199254740993) survives exactly. Small-int and string-result cases guard against regressions. The test fails before the change (round-tripped value is9.007199254740992e+15) and passes after.go build ./...,go vet ./message/..., andgo test ./message/...all pass.