GeezerVison is showing github.com — Python: Separate message texts with a newline in AgentResponse.text by Revinand · Pull Request #10093 · microsoft/agent-framework
Conversation
Motivation & Context
AgentResponse.text joins the texts of a response's messages with no separator. An agent that calls a tool returns the whole loop in one response, and a sentence the model writes before the tool call runs into the final answer: 'Let me check the weather.It is sunny in Paris.'. The same text comes back from str(response) and from an agent wrapped with as_tool(), which returns it to the calling model.
.NET's AgentResponse.Text puts a newline between the texts of non-empty messages (ConcatText), and Python's ChatResponse.text joins its messages with "\n".
Description & Review Guide
- What are the major changes?
AgentResponse.text joins the texts of non-empty messages with "\n" and skips messages without text, such as tool results, as ConcatText does. The docstring describes the separator. A new test covers a preamble with a tool call, a tool result and an answer. Two existing assertions pinned the joined output and now expect the newline: "HelloHello" becomes "Hello\nHello" in test_agent_run_response_text_property, and the two JSON documents in test_agent_response_value_parses_final_message_with_response_format are separated by a newline. Its .value assertions are unchanged.
- What is the impact of these changes? When a response has more than one message with text,
.text puts a newline between those texts, and str(response) and the result of as_tool() change with it. Responses with one message or none return the same text as before. .value is not affected: it parses only the last message since Python: parse structured response value from final message #6383.
- What do you want reviewers to focus on? Whether
ChatResponse.text should match. It joins every message with "\n" and strips the result, and an empty message in the middle, such as a tool result, leaves a blank line: 'Let me check.\n\nIt is sunny.'. I left it unchanged to keep this PR to .NET: Python: [Bug]: AgentResponse.text joins message texts without a separator #10067.
Related Issue
Fixes #10067
No other open PR addresses this issue.
AI Assistance
- No material AI assistance was used.
- This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.
AI assistance details: Claude Code helped with the issue analysis, the implementation, the tests and this description.
Contribution Checklist
- The code builds clean without any errors or warnings
- All unit tests pass, and I have added new tests where possible
- The PR follows the Contribution Guidelines
- This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
- This is not a breaking change. If it is a breaking change, add the
breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) - a workflow keeps the label and title prefix in sync automatically.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟢 Approval recommended
No active review issues are recorded for the change to separate non-empty response messages with newlines.
No open issues 🎉
What changed in this PR
Fixes #10067 by separating non-empty message texts in Python’s AgentResponse.text with newlines, preventing tool-call preambles from running into final answers. Structured .value parsing remains unchanged.
Changes:
- Join non-empty message texts with
"\n" and document the behavior.
- Add regression coverage for messages surrounding a tool call and update existing text assertions.
| File |
Description |
python/packages/core/tests/core/test_types.py |
Tests newline-separated output and omission of textless tool-result messages. |
python/packages/core/agent_framework/_types.py |
Updates AgentResponse.text joining logic and its docstring. |
Review effort: Balanced
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
@microsoft-github-policy-service agree
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks correct. Here's what I checked.
- The new join (
_types.py:3787). "\n".join(text for msg in self.messages if (text := msg.text)) skips messages whose .text is "", such as tool-result messages and function-call-only messages. So the tool loop in the new test gives "Let me check.\nIt is sunny." without a blank line. An empty messages list still gives "", so dropping the if self.messages else "" guard is safe.
- Where a new message starts. In the streaming path,
_process_update (_types.py:2590-2615) starts a new message only when the message_id, author_name or role changes. Streamed chunks of one assistant message stay in a single Message, so the newline only appears at real message boundaries, not between chunks.
- Callers.
as_tool() returns final_response.text (_agents.py:873), and so does __str__. So the sub-agent text that goes back to the calling model now has the separator, which is the point of #10067. The only other in-core consumer, _workflows/_agent_executor.py:271, upper-cases the text and isn't affected by the join.
.value. It still parses only the last message, and the updated test_agent_response_value_parses_final_message_with_response_format assertion keeps .value.response == "Hello".
git merge-tree against main is clean.
Notes for the maintainers:
- The three
text properties now differ slightly. Message.text joins the text contents within a message with " " (_types.py:2475). ChatResponse.text joins messages with "\n", doesn't skip empty ones, and .strip()s the result (:3476). AgentResponse.text now joins with "\n", skips empty messages, and doesn't strip. The description already raises the ChatResponse question. If the goal is one rule, sharing a helper between the two response types would keep them from drifting.
- This changes user-visible output. It changes
str(response) and as_tool() results for multi-message responses, so it probably warrants a "Changed" line in python/CHANGELOG.md at release time.
- Related draft. #10242 (draft, configurable response extraction for
as_tool()) touches the same as_tool() return value, so it's worth looking at the two together.
I didn't run the tests.
Reviewed with help from Claude Code.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This branch was successfully deployed
1 active
deployment
Reviewers
SergeyMenshykh
Awaiting requested review from SergeyMenshykh
SergeyMenshykh is a code owner
sophia-ramsey
Awaiting requested review from sophia-ramsey
sophia-ramsey is a code owner
+1 more reviewer
Reviewers whose approvals may not affect merge requirements
At least 1 approving review is required to merge this pull request.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Labels
python
Usage: [Issues, PRs], Target: Python
Milestone
No milestone
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.
|