Skip to content

feat(tool): add copy builder to ToolCallParam#1524

Open
hexly666 wants to merge 2 commits into
agentscope-ai:mainfrom
hexly666:feat/tool-call-param-copy-builder
Open

feat(tool): add copy builder to ToolCallParam#1524
hexly666 wants to merge 2 commits into
agentscope-ai:mainfrom
hexly666:feat/tool-call-param-copy-builder

Conversation

@hexly666
Copy link
Copy Markdown

Add builder(ToolCallParam) factory method to create a builder initialized with values from an existing instance. Mutable fields such as the input map are deep-copied to ensure independence.

Closes #1517

AgentScope-Java Version

[The version of AgentScope-Java you are working on, e.g. 1.0.12, check your pom.xml dependency version or run mvn dependency:tree | grep agentscope-parent:pom(only mac/linux)]

Description

[Please describe the background, purpose, changes made, and how to test this PR]

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with mvn spotless:apply
  • All tests are passing (mvn test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

Add builder(ToolCallParam) factory method to create a builder
initialized with values from an existing instance. Mutable fields
such as the input map are deep-copied to ensure independence.

Closes agentscope-ai#1517
@hexly666 hexly666 requested a review from a team May 28, 2026 19:07
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@AgentScopeJavaBot AgentScopeJavaBot added enhancement New feature or request area/core/tool Tool, skill, RAG abstractions labels May 29, 2026
Copy link
Copy Markdown
Collaborator

@AgentScopeJavaBot AgentScopeJavaBot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

The PR adds a clean copy-builder factory ToolCallParam.builder(ToolCallParam source) with thorough unit coverage (7 new tests across copy semantics, null handling, and field replacement). Logic is correct and the implementation is consistent with the existing builder pattern. Two documentation/clarity issues stand out: the Javadoc claim that mutable fields are "deep-copied" overstates what new HashMap<>(source.input) actually does (it is a shallow copy of entries — nested mutable values are still shared), and the test testDeepCopyInputMap does not actually assert deep-copy semantics. A minor nit: the @throws NullPointerException contract relies on implicit dereference; using Objects.requireNonNull(source, "source") would yield a clearer, stable error message. Also note the copy-builder's defensive new HashMap<> is redundant given ToolCallParam's constructor already copies the map at build time, but this is purely a small efficiency concern, not a bug.

* .build();
* }</pre>
*
* <p>Note: Mutable fields (such as the input map) are deep-copied to ensure
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] The Javadoc states 'Mutable fields (such as the input map) are deep-copied', but new HashMap<>(source.input) performs a shallow copy: only the outer map structure is duplicated, while values inside the map (which are typed as Object and may be mutable, e.g. nested List/Map) remain shared with the source. If a caller mutates a nested value on either instance, the change is visible to both. Please either reword to 'shallow-copied' (entries are independent, but nested values are shared) or, if true deep-copy semantics are intended, document the requirement that input values be effectively immutable.

* @throws NullPointerException if source is null
*/
public static Builder builder(ToolCallParam source) {
return new Builder(source);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] The Javadoc declares @throws NullPointerException if source is null, but the NPE is currently raised only implicitly by source.toolUseBlock dereference inside the private Builder(source) constructor. Consider an explicit Objects.requireNonNull(source, "source") here (or at the start of the private constructor) so the failure message is stable and self-describing, and the contract is enforced at the public entry point.


private Builder(ToolCallParam source) {
this.toolUseBlock = source.toolUseBlock;
this.input = source.input.isEmpty() ? null : new HashMap<>(source.input);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] This new HashMap<>(source.input) is redundant. The ToolCallParam constructor already performs new HashMap<>(builder.input) at build time (line 54), so when the user calls builder(source).build() without overriding input, the map gets copied twice. You can safely assign this.input = source.input.isEmpty() ? null : source.input; and rely on the existing defensive copy in the constructor. Pure micro-optimization — not blocking.


@Test
@DisplayName("Should deep copy input map")
void testDeepCopyInputMap() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] testDeepCopyInputMap does not actually verify deep-copy behavior. It builds modified from original with a wholly new Map via .input(modifiedInput), which only exercises field replacement (already covered by testModifyCopiedFields). To truly assert deep-copy semantics you would need a nested mutable value, e.g. input.put("list", new ArrayList<>(List.of("a"))), then mutate ((List<?>) copy.getInput().get("list")) and confirm the original list is unchanged. With the current shallow-copy implementation, that assertion would actually fail — which is itself useful information (see the related Javadoc finding).

- Fix Javadoc: describe shallow copy accurately (entries independent,
  nested values shared) instead of deep-copied
- Add explicit Objects.requireNonNull(source) with descriptive message
- Remove redundant new HashMap<>() in copy Builder constructor since
  ToolCallParam constructor already performs defensive copy
- Rewrite testShallowCopyInputMap to verify actual shallow copy
  behavior: nested mutable values are shared by reference, top-level
  entries remain independent
@hexly666 hexly666 force-pushed the feat/tool-call-param-copy-builder branch from 4d84c11 to a8f42a4 Compare May 29, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core/tool Tool, skill, RAG abstractions enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: ToolCallParam builder support itself

2 participants