Repository navigation
fix(core): report the tool return value on ToolResultEndEvent #3331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,19 @@ public class ToolResultEndEvent extends AgentEvent { | |
| private final String toolCallName; | ||
| private final ToolResultState state; | ||
|
|
||
| /** | ||
| * The tool method's return value as text, or {@code null} when the producer reports nothing. | ||
| * | ||
| * <p>{@code ""} is a reported value, not an absent one: it means the tool returned content with | ||
| * no text in it (an image, or a blank result), and a consumer must not fall back to its delta | ||
| * buffer for that case. | ||
| * | ||
| * <p>This deliberately does not ride on {@link AgentEvent#getMetadata()}, which passes the tool | ||
| * result's own metadata through unchanged — {@code ReActAgentNewLoopE2ETest} asserts that map | ||
| * exactly, so a framework key in it would be a contract change for unrelated consumers. | ||
| */ | ||
| private final String finalResultText; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Info] Compatibility nit on the wire format: |
||
|
|
||
| @JsonCreator | ||
| public ToolResultEndEvent( | ||
| @JsonProperty("id") String id, | ||
|
|
@@ -35,12 +48,14 @@ public ToolResultEndEvent( | |
| @JsonProperty("toolCallId") String toolCallId, | ||
| @JsonProperty("toolCallName") String toolCallName, | ||
| @JsonProperty("state") ToolResultState state, | ||
| @JsonProperty("metadata") Map<String, Object> metadata) { | ||
| @JsonProperty("metadata") Map<String, Object> metadata, | ||
| @JsonProperty("finalResultText") String finalResultText) { | ||
| super(id, createdAt); | ||
| this.replyId = replyId; | ||
| this.toolCallId = toolCallId; | ||
| this.toolCallName = toolCallName; | ||
| this.state = state; | ||
| this.finalResultText = finalResultText; | ||
| this.withMetadata(metadata); | ||
| } | ||
|
|
||
|
|
@@ -54,15 +69,29 @@ public ToolResultEndEvent( | |
| String toolCallId, | ||
| String toolCallName, | ||
| ToolResultState state) { | ||
| this(id, createdAt, replyId, toolCallId, toolCallName, state, null); | ||
| this(id, createdAt, replyId, toolCallId, toolCallName, state, null, null); | ||
| } | ||
|
|
||
| public ToolResultEndEvent( | ||
| String replyId, String toolCallId, String toolCallName, ToolResultState state) { | ||
| this(replyId, toolCallId, toolCallName, state, null); | ||
| } | ||
|
|
||
| /** | ||
| * As {@link #ToolResultEndEvent(String, String, String, ToolResultState)} additionally reporting | ||
| * the tool method's return value. | ||
| */ | ||
| public ToolResultEndEvent( | ||
| String replyId, | ||
| String toolCallId, | ||
| String toolCallName, | ||
| ToolResultState state, | ||
| String finalResultText) { | ||
| this.replyId = replyId; | ||
| this.toolCallId = toolCallId; | ||
| this.toolCallName = toolCallName; | ||
| this.state = state; | ||
| this.finalResultText = finalResultText; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -85,4 +114,14 @@ public String getToolCallName() { | |
| public ToolResultState getState() { | ||
| return state; | ||
| } | ||
|
|
||
| /** | ||
| * The tool method's return value as text. | ||
| * | ||
| * @return the return value (possibly empty), or {@code null} when the producer reports nothing, | ||
| * which leaves consumers falling back to the delta stream | ||
| */ | ||
| public String getFinalResultText() { | ||
| return finalResultText; | ||
| } | ||
| } | ||
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.
[Warning] The new field is honoured only by the AG-UI adapter. Other first-party consumers of the same event stream still derive the persisted tool result purely from the delta buffer (e.g.
agentscope-service/service-dataplane/.../managed/SessionEventMapper.java, which buildsAGENT_TOOL_RESULToutput/text/contentfromToolResultBuffer.outputText()), so managed sessions keep recording progress text as the result and now disagree with what AG-UI clients show for the same call. Suggest resolvinggetFinalResultText()(falling back to the buffer) in that path too, so every consumer ofToolResultEndEventagrees.