spring-projects / spring-projects/spring-ai

`ToolInputSchemaAugmenter` defaults `required` to false, unlike `JsonSchemaGenerator`

Open
#6,944 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

ToolInputSchemaAugmenter and JsonSchemaGenerator produce different required sets for the same record, and I can't tell from the code which one is intended.

What happens
record Bare(String plain, @ToolParam(description = "annotated") String annotated) {}

JsonSchemaGenerator.generateForType(Bare.class):

"required" : [ "annotated", "plain" ]

ToolInputSchemaAugmenter.augmentToolInputSchema("{}", toAugmentedArgumentTypes(Bare.class)):

"required" : [ "annotated" ]

plain is required on the main path and optional through the augmenter, so a model talking to an augmented tool may leave it out.

Where it comes from

ToolInputSchemaAugmenter:66-69:

return new AugmentedArgumentType(c.getName(), c.getGenericType(),
        toolParam != null ? toolParam.description() : "no description",
        toolParam != null ? toolParam.required() : false);

JsonSchemaGenerator:87 sets the opposite default, and the javadoc says it is the framework's rule:

/**
 * ... all properties in the JSON Schema are considered required by default. This can
 * be overridden by setting the {@link ToolParam#required()}, ...
 */
private static final boolean PROPERTY_REQUIRED_BY_DEFAULT = true;

ToolParam.required() is itself default true, so the two branches disagree in a way that reads oddly: writing @ToolParam(description = "…") makes a field required, and leaving it unannotated makes it optional.

The description default has the same shape. Unannotated fields get the literal string "no description", which ends up in the schema as the property's description, whereas JsonSchemaGenerator just omits description for plain.

Why I'm asking rather than sending a patch

ToolInputSchemaAugmenterTest asserts the current behaviour explicitly:

for (AugmentedArgumentType argType : argumentTypes) {
    assertEquals("no description", argType.description());
    assertFalse(argType.required());
}

So either the augmenter is deliberately more permissive than the main path and the tests are right, or the defaults were an oversight and the tests were written around them. Changing it means rewriting those assertions and changing the schema existing AugmentedToolCallback users get, which didn't feel like my call.

Happy to send a PR for whichever you prefer — aligning both defaults with PROPERTY_REQUIRED_BY_DEFAULT and dropping the "no description" placeholder, or leaving the behaviour and documenting that the augmenter defaults differ.

Measured on main (bf122ac).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ToolInputSchemaAugmenter:66-69 and JsonSchemaGenerator:87, then read ToolInputSchemaAugmenterTest and the ToolParam defaults. Compare the required and description behavior for annotated and unannotated record components. Done means the intended default is decided with maintainers and the implementation, tests, and documentation consistently reflect it.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.