Repository navigation
fix(memory): encode bytes in OpenAI converter reasoning blocks - #718
Merged
jariy17 merged 1 commit intoOct 27, 2026
Merged
Conversation
OpenAIConverseConverter stored reasoningContent blocks as is, so a redactedContent value (bytes) made json.dumps raise and broke the agent call. Encode bytes the same way SessionMessage.to_dict does when saving, and decode them on restore, matching AgentCoreMemoryConverter. Fixes aws#680
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
abhiramshaji-cyber
requested a deployment
to
manual-approval
October 27, 2026 16:42 — with
GitHub Actions
Waiting
jariy17
enabled auto-merge (squash)
October 27, 2026 16:55
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #718 +/- ##
=======================================
Coverage ? 89.68%
=======================================
Files ? 125
Lines ? 10885
Branches ? 1694
=======================================
Hits ? 9762
Misses ? 735
Partials ? 388
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
jariy17
approved these changes
Oct 27, 2026
Contributor
Author
|
Thanks for the review @jariy17! The integration test jobs are waiting on the |
This branch is waiting to be deployed
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.
Fixes #680
Agents using the OpenAI memory format crashed on any reply that carried hidden reasoning, and now that reply is saved and restored like any other.
What this code is for
OpenAIConverseConverterinconverters/openai.pyturns each Strands message into an OpenAI shaped JSON string beforeAgentCoreMemorySessionManagersaves it to AgentCore Memory, and turns stored events back into Strands messages when a session is restored. Reasoning blocks have no OpenAI field, so they are kept as they are under_strands_reasoning_content.Before this PR
A developer runs a Strands agent with
AgentCoreMemorySessionManager(converter=OpenAIConverseConverter)on a model that returns reasoning asreasoningContent.redactedContent.{"reasoningContent": {"redactedContent": b"..."}}, where the value is rawbytes.append_messagecallscreate_message, which callsOpenAIConverseConverter.message_to_payload._bedrock_to_openaicopies the block into_strands_reasoning_contentunchanged,bytesincluded.json.dumps(openai_msg)raisesTypeError: Object of type bytes is not JSON serializable.EventLoopException, on every turn that has redacted reasoning.After this PR
Same developer, same model, same turn.
_bedrock_to_openaipasses each reasoning block throughencode_bytes_values, sobytesbecomes{"__bytes_encoded__": true, "data": "<base64>"}.json.dumpssucceeds and the event is saved._openai_to_bedrockpasses each stored block throughdecode_bytes_values, so the model gets back the originalbytes.The diagram
flowchart LR classDef box fill:#eef1ff,stroke:#4c5bd4,color:#111827 classDef bad fill:#ffd6d6,stroke:#cc0000,color:#111827,stroke-width:3px classDef good fill:#c8f0c8,stroke:#008800,color:#111827,stroke-width:3px subgraph BEFORE["BEFORE: every redacted reasoning turn fails"] direction TB B1["model returns redactedContent bytes"] --> B2["message_to_payload"] B2 --> B3["_bedrock_to_openai keeps raw block"] B3 --> B4{"json.dumps"} B4 -->|"bytes in block"| B5["TypeError, agent call fails"] B4 -->|"text only reasoning"| B6["saved"] end subgraph AFTER["AFTER: saved and restored as bytes"] direction TB A1["model returns redactedContent bytes"] --> A2["message_to_payload"] A2 --> A3{"THE CHANGE: encode_bytes_values on block"} A3 --> A4{"json.dumps"} A4 -->|"bytes now base64"| A5["saved, decode_bytes_values on restore"] A4 -->|"text only reasoning"| A6["saved, unchanged"] end B1 ~~~ A1 class B1,B2,B3,B6 box class A1,A2,A6 box class B4,B5 bad class A3,A5 good linkStyle 3 stroke:#cc0000,stroke-width:5px linkStyle 8 stroke:#008800,stroke-width:5px style BEFORE fill:#fff5f5,stroke:#cc0000,color:#111827 style AFTER fill:#f3fff3,stroke:#008800,color:#111827The change
In
converters/openai.py,reasoning_blocks.append(item)becomesreasoning_blocks.append(encode_bytes_values(item))andreasoning_items.append(rc)becomesreasoning_items.append(decode_bytes_values(rc)). Both helpers come fromstrands.types.session, the same pairSessionMessage.to_dictandSessionMessage.from_dictuse, which is howAgentCoreMemoryConverteralready handles this. A new test,test_redacted_reasoning_bytes_round_trip_in_openai_converter, saves and restores a redacted block and checks thebytescome back equal.What changes for a user
bytes, so the model receives it back on the next turn.