Skip to content

feat(java): implement ParamSchema + ParamCoercion internals for Param (Phase 4.3)#1877

Merged
edburns merged 5 commits into
edburns/1810-java-tool-ergonomics-tool-as-lambdafrom
copilot/edburns1810-java-tool-ergonomics-tool-as-lambda-im
Jul 1, 2026
Merged

feat(java): implement ParamSchema + ParamCoercion internals for Param (Phase 4.3)#1877
edburns merged 5 commits into
edburns/1810-java-tool-ergonomics-tool-as-lambdafrom
copilot/edburns1810-java-tool-ergonomics-tool-as-lambda-im

Conversation

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Phase 4.3 of #1810. Adds the runtime schema/coercion layer that backs Param<T>-based lambda tool definitions, as the internal foundation consumed by the ToolDefinition.from* overloads added in Phase 4.2.

New classes (com.github.copilot.tool, package-private)

ParamSchema

  • buildSchema(toolName, mapper, params...) — produces the full Map<String,Object> JSON Schema; validates duplicate parameter names at construction time with a clear error identifying tool name and offending name
  • forType(Class<?>) — maps Java Class to JSON Schema type descriptor; runtime counterpart to the compile-time SchemaGenerator, covering the exact same type surface (primitives, String, UUID, temporal, enums, collections, arrays, maps, POJOs/records)

ParamCoercion

  • coerce(args, param, mapper) — resolves invocation arg → string default → empty-optional → missing-required-error; delegates complex conversion to ObjectMapper.convertValue matching existing policy
  • coerceDefault(param, mapper) — parses validated string defaults into the declared Java type
  • emptyOptionalOrNull(type) — returns empty OptionalInt/OptionalLong/OptionalDouble or null

Key constraints honored

  • Both classes are package-private — no new public types added to the exported module surface (resolution 3.8)
  • coerce() takes Map<String,Object> rather than ToolInvocation to avoid a toolrpc package dependency
  • Type mapping and coercion policy are identical to the existing inline private methods in ToolDefinition, maintaining behavioral parity with the baseline contract from Phase 2
// Schema generation
Map<String, Object> schema = ParamSchema.buildSchema(
    "greet", mapper,
    Param.of(String.class, "name", "User's name"),
    Param.of(Integer.class, "limit", "Max results", false, "10")
);
// → { type: object, properties: { name: {type:string,...}, limit: {type:integer,default:10,...} }, required: [name] }

// Coercion
String name = ParamCoercion.coerce(invocation.getArguments(), nameParam, mapper);
Integer limit = ParamCoercion.coerce(invocation.getArguments(), limitParam, mapper); // returns 10 if absent

Copilot AI and others added 2 commits July 1, 2026 18:13
…s-tool-as-lambda' into copilot/edburns1810-java-tool-ergonomics-tool-as-lambda-im

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Fixes #1841

Adds two package-private internal helper classes in com.github.copilot.tool:

- ParamSchema: runtime JSON Schema generation from Param<?> metadata.
  buildSchema() validates duplicate names; forType() mirrors the compile-time
  SchemaGenerator using java.lang.reflect.Class instead of javax.lang.model.

- ParamCoercion: runtime argument coercion using existing ObjectMapper policy.
  coerce() resolves args → default → empty-optional → required-error in order.
  coerceDefault() parses string defaults into declared Java types.
  emptyOptionalOrNull() returns empty Optional variants for optional primitives.

Both classes are package-private per resolution 3.8 (no public internal helpers).
coerce() takes Map<String,Object> to avoid a cyclic rpc dependency.

Updates Phase 4.3 checkbox in plan file.

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement schema and coercion internals for Param<T> feat(java): implement ParamSchema + ParamCoercion internals for Param (Phase 4.3) Jul 1, 2026
Copilot AI requested a review from edburns July 1, 2026 18:26
@edburns edburns changed the base branch from main to edburns/1810-java-tool-ergonomics-tool-as-lambda July 1, 2026 18:30
@github-actions

This comment has been minimized.

@edburns edburns marked this pull request as ready for review July 1, 2026 19:23
@edburns edburns requested a review from a team as a code owner July 1, 2026 19:23
Copilot AI review requested due to automatic review settings July 1, 2026 19:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds extracted, package-private runtime helpers in the Java SDK to (a) build JSON Schema from Param<?> descriptors and (b) coerce invocation argument maps into typed Java values, intended as internal foundations for the Param<T>-based lambda tool ergonomics work (Phase 4.3 of #1810).

Changes:

  • Added ParamSchema to build JSON Schema Map<String, Object> structures from Param<?>... and map Class<?> → schema fragments.
  • Added ParamCoercion to coerce Map<String,Object> invocation args into typed values and parse validated string defaults.
  • Updated the internal Phase 4 checklist document to mark Phase 4.3 complete.
Show a summary per file
File Description
java/src/main/java/com/github/copilot/tool/ParamSchema.java New internal schema builder and Class<?>→schema mapper for Param<?> descriptors.
java/src/main/java/com/github/copilot/tool/ParamCoercion.java New internal argument/default coercion helper for Param<T> invocation.
1810-java-tool-ergonomics-tool-as-lambda-remove-before-merge/1810-ignorance-reduction-for-implementation-plan.md Marks Phase 4.3 as completed in the implementation plan.

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 5
  • Review effort level: Low

Comment thread java/src/main/java/com/github/copilot/tool/ParamSchema.java
Comment thread java/src/main/java/com/github/copilot/tool/ParamSchema.java Outdated
Comment thread java/src/main/java/com/github/copilot/tool/ParamSchema.java Outdated
Comment thread java/src/main/java/com/github/copilot/tool/ParamCoercion.java
Comment thread java/src/main/java/com/github/copilot/tool/ParamSchema.java
…oercion

- Add null guard for varargs array in buildSchema()
- Clarify ParamSchema class Javadoc: simplified counterpart, not full parity
- Clarify forType() Javadoc: flat type mapping only, no generics resolution
- Clarify coerceDefault() Javadoc: ObjectMapper fallback is safety net only

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment thread java/src/main/java/com/github/copilot/tool/ParamSchema.java Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR adds two package-private Java classes (ParamSchema, ParamCoercion) as internal implementation details backing the Param<T> public API (introduced in Phase 4.1). No new public API surface is exposed.

Cross-SDK Alignment

The Java Param<T> ergonomic pattern introduced in this series is functionally equivalent to the idiomatic parameter-typing approaches in all other SDKs:

SDK Typed parameter mechanism Schema generation Arg coercion
Java Param<T> descriptors ParamSchema.buildSchema() (this PR) ParamCoercion.coerce() (this PR)
Python Pydantic BaseModel model_json_schema() model_validate()
Go Typed struct + struct tags google/jsonschema-go reflection json.Unmarshal into typed struct
Rust #[derive(JsonSchema)] struct schemars::schema_for!() serde_json::from_value()
.NET Delegate params / AIFunction Microsoft.Extensions.AI factory Parameter binding via MEAI

Assessment

No cross-SDK inconsistencies introduced. The ParamSchema and ParamCoercion internals are implementation details of a Java-idiomatic API equivalent to what all other SDKs already provide through their own ecosystem tools. The choice to use explicit Param<T> descriptors rather than reflection-on-records (like Go/Rust/Python) is appropriate for Java given its annotation processing and generic type erasure constraints.

Generated by SDK Consistency Review Agent for issue #1877 · sonnet46 851.4K ·

@edburns edburns merged commit be5e8cb into edburns/1810-java-tool-ergonomics-tool-as-lambda Jul 1, 2026
13 of 14 checks passed
@edburns edburns deleted the copilot/edburns1810-java-tool-ergonomics-tool-as-lambda-im branch July 1, 2026 19:54
edburns added a commit that referenced this pull request Jul 2, 2026
… (Phase 4.3) (#1877)

* Initial plan

* feat(java): implement schema + coercion internals for Param (Phase 4.3)

Fixes #1841

Adds two package-private internal helper classes in com.github.copilot.tool:

- ParamSchema: runtime JSON Schema generation from Param<?> metadata.
  buildSchema() validates duplicate names; forType() mirrors the compile-time
  SchemaGenerator using java.lang.reflect.Class instead of javax.lang.model.

- ParamCoercion: runtime argument coercion using existing ObjectMapper policy.
  coerce() resolves args → default → empty-optional → required-error in order.
  coerceDefault() parses string defaults into declared Java types.
  emptyOptionalOrNull() returns empty Optional variants for optional primitives.

Both classes are package-private per resolution 3.8 (no public internal helpers).
coerce() takes Map<String,Object> to avoid a cyclic rpc dependency.

Updates Phase 4.3 checkbox in plan file.

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* fix(java): address Copilot code review comments on ParamSchema/ParamCoercion

- Add null guard for varargs array in buildSchema()
- Clarify ParamSchema class Javadoc: simplified counterpart, not full parity
- Clarify forType() Javadoc: flat type mapping only, no generics resolution
- Clarify coerceDefault() Javadoc: ObjectMapper fallback is safety net only

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(java): correct ParamSchema Javadoc - arrays do produce items schema

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Co-authored-by: Ed Burns <edburns@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] Tool-as-lambda 4.3: Implement schema + coercion internals for Param<T>

3 participants