Add E2E integration test for ergonomic @CopilotTool + ToolDefinition.fromObject() API#1787
Conversation
96ede86 to
1aae0df
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adds a new ergonomic, annotation-driven tool-definition API to the Java SDK (@CopilotTool, @Param, and ToolDefinition.fromObject/fromClass) backed by a JSR-269 annotation processor that generates $$CopilotToolMeta companion classes. It also introduces new unit/compilation tests and a Java E2E integration test plus snapshot intended to prove wire-level equivalence with the existing low-level ToolDefinition.create() approach.
Changes:
- Add
com.github.copilot.toolpublic API: annotations, metadata-provider interface, schema generator, and annotation processor. - Add
ToolDefinition.fromObject/fromClassto load generated metadata and produceToolDefinitionlists with working handlers. - Add tests (compiler-based processor/schema tests, fromObject unit tests, and an E2E failsafe IT + snapshot).
Show a summary per file
| File | Description |
|---|---|
| test/snapshots/tools/ergonomic_tool_definition.yaml | Replay snapshot for the new ergonomic Java E2E tool-definition test. |
| java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java | Compilation-testing unit tests for SchemaGenerator type→schema mapping. |
| java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java | Compilation-based tests asserting generated code shape and diagnostics for CopilotToolProcessor. |
| java/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java | Unit tests validating annotation retention/targets/defaults and presence of @CopilotExperimental. |
| java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java | Unit tests for ToolDefinition.fromObject/fromClass behavior and handler invocation. |
| java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java | Hand-written fixture mimicking generated metadata for SimpleTools. |
| java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java | @CopilotTool fixture class for fromObject tests. |
| java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java | Hand-written fixture metadata for override tool behavior. |
| java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java | @CopilotTool fixture exercising overridesBuiltInTool. |
| java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java | Hand-written fixture metadata for return-type handling. |
| java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java | Fixture with multiple return shapes (String/void/CompletableFuture). |
| java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java | Hand-written fixture metadata exercising default-argument behavior. |
| java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java | Fixture with @Param(defaultValue=...) on a primitive parameter. |
| java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java | Hand-written fixture metadata for java.time arg coercion using mapper. |
| java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java | Fixture using LocalDateTime tool parameter. |
| java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java | Hand-written fixture metadata for primitive/enum coercion. |
| java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java | Fixture class for mixed arg coercion. |
| java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java | New Java failsafe IT that uses ToolDefinition.fromObject(...) against the replay proxy. |
| java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java | Hand-written $$CopilotToolMeta fixture for the E2E test. |
| java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java | Tool fixture class exercising annotation patterns for the E2E test. |
| java/src/main/resources/META-INF/services/javax.annotation.processing.Processor | Registers the new processor via SPI alongside CopilotExperimentalProcessor. |
| java/src/main/java/module-info.java | Exports new com.github.copilot.tool package and registers the processor in JPMS provides. |
| java/src/main/java/com/github/copilot/tool/SchemaGenerator.java | Compile-time JSON schema generator from TypeMirror / javax.lang.model types. |
| java/src/main/java/com/github/copilot/tool/Param.java | New @Param annotation (description/name/required/defaultValue). |
| java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java | JSR-269 processor generating $$CopilotToolMeta classes + schema + invocation lambdas. |
| java/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java | SPI-style interface implemented by generated meta classes to return ToolDefinitions. |
| java/src/main/java/com/github/copilot/tool/CopilotTool.java | New @CopilotTool annotation (name/override/skipPermission/defer). |
| java/src/main/java/com/github/copilot/rpc/ToolDefinition.java | Adds fromObject/fromClass runtime bridge for loading generated metadata. |
| java/src/main/java/com/github/copilot/rpc/ToolDefer.java | Adds NONE sentinel and adjusts JSON value handling to support omission semantics. |
| java/mvnw | Adds a Maven wrapper script under java/. |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/src/main/java/module-info.java | JPMS spike module for experimentation (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/src/main/java/com/github/dd3021192/MyTools$$CopilotToolMeta.java | JPMS spike meta-class example (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/src/main/java/com/github/dd3021192/MyTools.java | JPMS spike tool class (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/src/main/java/com/github/dd3021192/Main.java | JPMS spike main program (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/pom.xml | Spike Maven project (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd3021192/dependency-reduced-pom.xml | Generated Shade artifact (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/dd-3018003-ignorance-reduction-for-implementation-plan.md | Internal implementation-plan notes (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/add-tests-that-use-low_level_tool_definition.yaml.md | Internal prompt/instructions doc (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/20260618-prompts.md | Internal prompt transcript (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/20260617-prompts.md | Internal prompt transcript (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/20260616-prompts.md | Internal prompt transcript (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/20260615-prompts.md | Internal prompt transcript (staging content). |
| 1682-java-tool-ergonomics-prompts-remove-before-merge/1682-low-level-tool-definition.md | Internal plan doc (staging content). |
Copilot's findings
- Files reviewed: 44/46 changed files
- Comments generated: 7
…fromObject() API Create ErgonomicToolDefinitionIT that proves the ergonomic annotation-based API produces identical wire behavior to the low-level ToolDefinition.create() API, tested against the replay proxy. Files added: - test/snapshots/tools/ergonomic_tool_definition.yaml (identical to low_level_tool_definition.yaml since wire format is the same) - java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java Closes #1762
The single-record-parameter shortcut in CopilotToolProcessor generated invocation.getArgumentsAs() which uses an unconfigured ObjectMapper internally (no JavaTimeModule, no SDK settings). Switch to mapper.convertValue(args, RecordType.class) which uses the SDK-configured mapper passed to the definitions() method. Addresses review comment r3469523760.
CopilotToolProcessor.generateSchemaWithParamMetadata() now checks if a parameter type is Optional/OptionalInt/OptionalLong/OptionalDouble before adding it to the JSON Schema required list. This aligns with SchemaGenerator which already treats these types as optional. Addresses review comment r3469523801.
The class-level Javadoc incorrectly stated that the annotation processor generates $$CopilotToolMeta fixtures during test compilation. In reality, the module has <proc>none</proc> and these fixtures are hand-written classes under com.github.copilot.rpc.fixtures. Addresses review comment r3469523833.
The ErgonomicToolDefinitionIT snapshot only exercises set_current_phase and search_items. The grep tool (with overridesBuiltInTool=true) was never invoked, making it dead code that contradicted the PR description. Addresses review comment r3469523851.
3b89229 to
a423f7c
Compare
Cross-SDK Consistency Review ✅This PR is scoped entirely to the Java SDK — E2E test for the ergonomic Ergonomic tool-definition API — consistent across all SDKsThe
Optional-type fix — Java-specific, no cross-SDK action neededThe fix to mark
Minor observation (not a blocker)The PR description lists three annotation patterns under test — including
|
…fromObject() API (#1787) * Initial plan * Initial plan * Initial plan * Add E2E integration test for ergonomic @copilotTool + ToolDefinition.fromObject() API Create ErgonomicToolDefinitionIT that proves the ergonomic annotation-based API produces identical wire behavior to the low-level ToolDefinition.create() API, tested against the replay proxy. Files added: - test/snapshots/tools/ergonomic_tool_definition.yaml (identical to low_level_tool_definition.yaml since wire format is the same) - java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java Closes #1762 * spotless * fix: use passed ObjectMapper for record-parameter conversion The single-record-parameter shortcut in CopilotToolProcessor generated invocation.getArgumentsAs() which uses an unconfigured ObjectMapper internally (no JavaTimeModule, no SDK settings). Switch to mapper.convertValue(args, RecordType.class) which uses the SDK-configured mapper passed to the definitions() method. Addresses review comment r3469523760. * fix: exclude Optional types from required list in generated schema CopilotToolProcessor.generateSchemaWithParamMetadata() now checks if a parameter type is Optional/OptionalInt/OptionalLong/OptionalDouble before adding it to the JSON Schema required list. This aligns with SchemaGenerator which already treats these types as optional. Addresses review comment r3469523801. * fix: correct misleading Javadoc in ToolDefinitionFromObjectTest The class-level Javadoc incorrectly stated that the annotation processor generates $$CopilotToolMeta fixtures during test compilation. In reality, the module has <proc>none</proc> and these fixtures are hand-written classes under com.github.copilot.rpc.fixtures. Addresses review comment r3469523833. * fix: remove unused grep override tool from E2E test The ErgonomicToolDefinitionIT snapshot only exercises set_current_phase and search_items. The grep tool (with overridesBuiltInTool=true) was never invoked, making it dead code that contradicted the PR description. Addresses review comment r3469523851. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Ed Burns <edburns@microsoft.com>
…fromObject() API (#1787) * Initial plan * Initial plan * Initial plan * Add E2E integration test for ergonomic @copilotTool + ToolDefinition.fromObject() API Create ErgonomicToolDefinitionIT that proves the ergonomic annotation-based API produces identical wire behavior to the low-level ToolDefinition.create() API, tested against the replay proxy. Files added: - test/snapshots/tools/ergonomic_tool_definition.yaml (identical to low_level_tool_definition.yaml since wire format is the same) - java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java - java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java Closes #1762 * spotless * fix: use passed ObjectMapper for record-parameter conversion The single-record-parameter shortcut in CopilotToolProcessor generated invocation.getArgumentsAs() which uses an unconfigured ObjectMapper internally (no JavaTimeModule, no SDK settings). Switch to mapper.convertValue(args, RecordType.class) which uses the SDK-configured mapper passed to the definitions() method. Addresses review comment r3469523760. * fix: exclude Optional types from required list in generated schema CopilotToolProcessor.generateSchemaWithParamMetadata() now checks if a parameter type is Optional/OptionalInt/OptionalLong/OptionalDouble before adding it to the JSON Schema required list. This aligns with SchemaGenerator which already treats these types as optional. Addresses review comment r3469523801. * fix: correct misleading Javadoc in ToolDefinitionFromObjectTest The class-level Javadoc incorrectly stated that the annotation processor generates $$CopilotToolMeta fixtures during test compilation. In reality, the module has <proc>none</proc> and these fixtures are hand-written classes under com.github.copilot.rpc.fixtures. Addresses review comment r3469523833. * fix: remove unused grep override tool from E2E test The ErgonomicToolDefinitionIT snapshot only exercises set_current_phase and search_items. The grep tool (with overridesBuiltInTool=true) was never invoked, making it dead code that contradicted the PR description. Addresses review comment r3469523851. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Ed Burns <edburns@microsoft.com>
Proves the ergonomic
@CopilotToolannotation API produces byte-for-byte identical wire behavior to the low-levelToolDefinition.create()API against the replay proxy.Changes
test/snapshots/tools/ergonomic_tool_definition.yaml— Reuses the same snapshot aslow_level_tool_definition.yaml(wire format is identical by design)java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java— Tool fixture exercising all three annotation patterns:setCurrentPhase→set_current_phase)searchItems→search_items)name = "grep", overridesBuiltInTool = true)java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java— Hand-written processor companion (same pattern asrpc/fixtures/*$$CopilotToolMeta.java)java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java— Failsafe IT that callsToolDefinition.fromObject(tools), sends a prompt triggering all three tools, and asserts invocation args + state mutationWire equivalence
The meta class produces schemas with identical tool names, parameter names, types, and
overridesBuiltInToolflags asLowLevelToolDefinitionIT— allowing the same replay snapshot to serve both tests.