fix: prevent newline accumulation in XML parameter serialization#11767
fix: prevent newline accumulation in XML parameter serialization#11767enyst merged 2 commits intoOpenHands:mainfrom
Conversation
|
@enyst is this also something that is in agent-sdk now? |
|
@aoi127 Thank you for the contribution! Do you have some prompt log where we can see the issue, maybe? Reason I ask is that I don't fully understand it, sorry, I think the tool prompts are recomputed every request... On the other hand, as Mamoodi said, we are launching OpenHands V1, which is deeply rebuilt and it's based on agent SDK. The LLM is in the agent-sdk, since it's part of the agentic loop. That means that in really short time we will actually remove llm.py here, and replace with using agent-sdk as a dependency. I'd suggest to make an issue on agent-sdk, maybe with a log if you can? |
|
@enyst Hello, I’ve uploaded a zip file containing logs from the Case 1
In
However, in Case 2
As shown, each reconstructed assistant message contains one additional The number of newline characters increases with each call to Root CauseAs noted in the earlier comment, the code bug creates a positive feedback loop that accumulates extra newlines. |
|
@enyst I'm not sure if the agent-sdk has this same issue. I'll test it and submit a PR once I have the chance. |
|
Thank you @aoi127 ! It would be great if you could check out agent-sdk! Maybe if you could take a look at the failing test, I think it's failing precisely because of newlines. It would be great if we could have a unit test for this exact behavior, too, so that we can reproduce it and make sure it doesn't happen again. |
When converting function calling format to XML format for non-native function calling LLMs, the converter was adding extra newlines around multiline parameter values. This caused newlines to accumulate across multiple LLM response cycles, leading to: 1. str_replace failures due to mismatched old_str with excessive newlines 2. Generated code containing redundant blank lines that grow over time For Example: - 1st LLM response: <parameter=old_str>\n...code...\n</parameter> - After context rebuild: <parameter=old_str>\n\n...code...\n\n</parameter> - 2nd LLM response learns this format: \n\n...code...\n\n - After rebuild again: \n\n\n...code...\n\n\n - Newlines keep accumulating with each request-response cycle Root cause: The convert_tool_call_to_string() function in fn_call_converter.py detected multiline content (is_multiline check) and added \n before and after parameter values. These extra newlines were preserved in context reconstruction, creating a positive feedback loop where LLM learned the inflated format and reproduced it in subsequent responses. Solution: Removed the is_multiline logic that added extra \n characters. Parameter values are now serialized as-is without additional formatting, preventing newline accumulation while maintaining the correct XML format from TOOL_EXAMPLES. Also update test expectations for multiline parameters in test_llm_fncall_converter.py to match the new behavior. Multiline parameter values should NOT have extra newlines before/after to prevent newline accumulation across multiple LLM response cycles. Impact: - Fixes str_replace_editor tool reliability - Prevents code quality degradation over long conversations - Maintains backward compatibility with existing prompts - All tests now pass with the corrected behavior
fb39e11 to
435d18b
Compare
|
Hi @enyst, I looked into the failing test. It appears that the faling test case itself was introducing the redundant newlines, which matches the behavior we saw in the logs. |
|
I think it's fine for now! Not sure for agent-sdk, we might do some more tests over there. Let's get this one in, first, though! |
|
@OpenHands Do a /codereview-roasted on this PR, including on the discussion in this PR. Read all comments here and look at the screenshots if you can, so you understand very well what is reported. You should think deeply and feel free to tell if you agree it's a real problem, too, and/or where it happens, or could it be a mistake. From now on, everytime you respond to the user, use markdown, with headings, bold, bulleted lists, so that we can read it easily. |
|
I'm on it! enyst can track my progress at all-hands.dev |
Summary of WorkWhat I did
Key Findings
Verdict
Recommendations (Follow-ups, not blockers)
No Code Changes Made
|








Summary of PR
Problem Description
When converting function calling format to XML format for non-native function calling LLMs, the converter was adding extra newlines around multiline parameter values. This caused newlines to accumulate across multiple LLM response cycles, leading to:
Reproduction Example
The newline accumulation follows this pattern:
<parameter=old_str>\n...code...\n</parameter><parameter=old_str>\n\n...code...\n\n</parameter>\n\n...code...\n\n<parameter=old_str>\n\n\n...code...\n\n\n</parameter>Root Cause
The
convert_tool_call_to_string()function inopenhands/llm/fn_call_converter.pydetected multiline content (viais_multilinecheck) and added\nbefore and after parameter values. These extra newlines were preserved in context reconstruction, creating a positive feedback loop where:Solution
Removed the
is_multilinelogic that added extra\ncharacters. Parameter values are now serialized as-is without additional formatting.Modified Files
openhands/llm/fn_call_converter.py(lines 423-434)is_multilinevariable and conditional newline insertionImpact
Positive Impacts
str_replace_editortool reliabilityTesting
Change Type
Checklist