Skip to content

Refactor options for strict immutability and centralized default values#6218

Closed
sdeleuze wants to merge 15 commits into
spring-projects:mainfrom
sdeleuze:option-refactoring
Closed

Refactor options for strict immutability and centralized default values#6218
sdeleuze wants to merge 15 commits into
spring-projects:mainfrom
sdeleuze:option-refactoring

Conversation

@sdeleuze
Copy link
Copy Markdown
Contributor

@sdeleuze sdeleuze commented May 30, 2026

Summary

This PR refactors the various Options classes (such as ChatOptions, EmbeddingOptions, etc.) across the framework to enforce strict immutability, improve null-safety representation, and centralize where default values are defined.
Previously, default values were scattered across Model implementations and Auto-configuration *Properties classes, and options collections were sometimes mutable or defaulted to empty collections (which caused issues when merging). This PR streamlines the options architecture to be safer and more predictable.

Key Changes

  • Strict Immutability: Enforced strict immutability for all collections within options (e.g., toolCallbacks, stopSequences, customHeaders) by using unmodifiable copies.
  • Nullable Collections: Replaced empty collections with nullable collections (@Nullable) to accurately represent the absence of a value, which fixes and improves the behavior of options merging.
  • Centralized Defaults: Moved default values (like default model names, temperatures, etc.) from the model implementations and *Properties configuration classes directly into the options constructors.
  • Removed Redundant Properties: Removed default configuration properties from *Properties classes since they are now consistently handled at the options level.
  • API Updates:
    • Renamed ChatModel#getDefaultOptions() to ChatModel#getOptions() (the former is now deprecated).
    • Removed ChatOptions#copy() since options are now strictly immutable (users should use mutate().build() instead).
  • Cleanup: Improved the consistency of ternary operators in options, cleaned up @NullMarked annotations, and fixed integration tests to align with the new options behavior.
  • Documentation: Updated upgrade-notes.adoc for 2.0.0-RC1 to detail the breaking changes and migration paths for these options updates.

Breaking Changes & Migration

  • ChatOptions#copy() and *Options#fromOptions have been removed. Use mutate().build() to create a modified copy of an options instance.
  • Modifying collections returned by options getters will now throw an UnsupportedOperationException.
  • ChatModel#getDefaultOptions() is deprecated in favor of ChatModel#getOptions().
  • Default values are no longer duplicated in *Properties classes, which may affect code that relied on reading those default properties directly.

@tzolov @ilayaperumalg I will take care of merging it, please just provide your review.
@ThomasVitale @nicolaskrier Feel free to provide your feedback as well.
I recommend reviewing commit by commit, each has a meaningful description.

sdeleuze added 11 commits May 29, 2026 13:09
- Use protected ctor
- Make fields final
- Remove Jackson annotations not needed anymore
- Stop using options classes directly as `@NestedConfigurationProperty`
  in Spring Boot `@ConfigurationProperties`.
- Introduce dedicated nested options classes for property binding.
- Update auto-configurations and tests to use the new properties
  structure.

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Not needed anymore since options are now immutable

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Not needed since should be consistent with options level ones.

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Make collections (lists, sets, maps) in options classes strictly immutable
by wrapping them in List.copyOf(), Set.copyOf(), Map.copyOf(), and
Collectors.toUnmodifiableMap() instead of instantiating mutable collections.

Update combineWith() logic in options builders to properly merge
collections when overridden, rather than blindly overwriting the base
collections with the override ones. Also optimize builder clone()
methods to take advantage of this immutability.

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Refactor model options to use null values for undefined
collections (like tool callbacks, names, and headers) instead
of defaulting to empty collections.

This ensures that explicitly empty collections are not ignored
during option merging, properly distinguishing between an unset
option and an intentionally empty one.

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
@nicolaskrier
Copy link
Copy Markdown
Contributor

nicolaskrier commented May 31, 2026

@sdeleuze: I like this refactor! I have added a few comments that could be applied to other AI vendors as well.

Comment thread spring-ai-docs/src/main/antora/modules/ROOT/pages/upgrade-notes.adoc Outdated
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Removed because options are now strictly immutable.

See spring-projects#6218
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
@sdeleuze
Copy link
Copy Markdown
Contributor Author

sdeleuze commented Jun 1, 2026

I applied the suggested changes.

sdeleuze added 4 commits June 1, 2026 11:59
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Removed because options are now strictly immutable.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
See spring-projects#6218

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
See spring-projects#6218

Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
@sdeleuze sdeleuze force-pushed the option-refactoring branch from 658d2ce to 9789d54 Compare June 1, 2026 10:00
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
- Use protected ctor
- Make fields final
- Remove Jackson annotations not needed anymore
- Stop using options classes directly as `@NestedConfigurationProperty`
  in Spring Boot `@ConfigurationProperties`.
- Introduce dedicated nested options classes for property binding.
- Update auto-configurations and tests to use the new properties
  structure.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Not needed anymore since options are now immutable

Seee spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Not needed since should be consistent with options level ones.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Make collections (lists, sets, maps) in options classes strictly immutable
by wrapping them in List.copyOf(), Set.copyOf(), Map.copyOf(), and
Collectors.toUnmodifiableMap() instead of instantiating mutable collections.

Update combineWith() logic in options builders to properly merge
collections when overridden, rather than blindly overwriting the base
collections with the override ones. Also optimize builder clone()
methods to take advantage of this immutability.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Refactor model options to use null values for undefined
collections (like tool callbacks, names, and headers) instead
of defaulting to empty collections.

This ensures that explicitly empty collections are not ignored
during option merging, properly distinguishing between an unset
option and an intentionally empty one.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
Removed because options are now strictly immutable.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 1, 2026
See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
@sdeleuze sdeleuze closed this in 2128c67 Jun 1, 2026
nicolaskrier added a commit to nicolaskrier/spring-ai that referenced this pull request Jun 1, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor
- Rename with methods from N to n

See spring-projects#6218
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
nicolaskrier added a commit to nicolaskrier/spring-ai that referenced this pull request Jun 2, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor
- Rename with methods from N to n

See spring-projects#6218
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
nicolaskrier added a commit to nicolaskrier/spring-ai that referenced this pull request Jun 2, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor
- Rename with methods from N to n

See spring-projects#6218
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
sdeleuze pushed a commit to sdeleuze/spring-ai that referenced this pull request Jun 4, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor

See spring-projects#6218
Closes spring-projects#6248
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze pushed a commit to sdeleuze/spring-ai that referenced this pull request Jun 4, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor

See spring-projects#6218
Closes spring-projects#6248
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
sdeleuze pushed a commit to sdeleuze/spring-ai that referenced this pull request Jun 4, 2026
…rties

- Remove unnecessary null cheks
- Make some with methods nullable for option builder
- Handle default values in options constructor

See spring-projects#6218
Closes spring-projects#6248
Signed-off-by: Nicolas Krier <7557886+nicolaskrier@users.noreply.github.com>
sdeleuze added a commit to sdeleuze/spring-ai that referenced this pull request Jun 5, 2026
They should now be managed and exposed at options level.

See spring-projects#6218
Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants