Skip to content

feat!: switch form filler value options to items-based resolution (#9564) (CP: 25.2)#9627

Merged
sissbruecker merged 1 commit into
25.2from
cherry-pick-9564-to-25.2-1782713524751
Jun 29, 2026
Merged

feat!: switch form filler value options to items-based resolution (#9564) (CP: 25.2)#9627
sissbruecker merged 1 commit into
25.2from
cherry-pick-9564-to-25.2-1782713524751

Conversation

@vaadin-bot

Copy link
Copy Markdown
Collaborator

This PR cherry-picks changes from the original PR #9564 to branch 25.2.

Original PR description

Description

In form filler DX test sessions, the return type of value options APIs was a pain point. Even after updating examples and clarifying it in the documentation, it still was not intuitive: the developer had to think in labels (strings) when the rest of their codebase thought in domain objects, and had to supply a toValue converter that re-encoded the label-to-value mapping the field's ItemLabelGenerator already expressed.

This PR replaces that shape with an items-based resolution path:

  • Registrations now carry the domain objects, not pre-rendered labels. Aligned with how the rest of the codebase models selection options.
  • Labels for the LLM are derived through one ItemLabelGenerator. By default, the controller reflects the field's own setItemLabelGenerator(...) so common cases need no extra wiring; an explicit one can be supplied on ValueOptions for custom components or cases where the LLM should see a different label than the UI.
  • When the LLM picks a label, the controller walks the registration's items, applies the same generator per item, and writes the first matching item to the field. The bijection lives in one place — the label generator — and the toValue converter is no longer needed and has been removed.

Changed in ValueOptions:

  • options(Collection<String>)options(Collection<V>)
  • options(BiFunction<String, Integer, List<String>>)options(BiFunction<String, Integer, List<V>>)

New in ValueOptions:

  • itemLabelGenerator(ItemLabelGenerator<V>) — optional; falls back to the field's own labeler, then to String.valueOf(item).

Changed in FormAIController:

  • fieldValueOptions(ValueOptions<String>)fieldValueOptions(ValueOptions<V>)
  • fieldValueOptions(ValueOptions<V>, Function<String, V> toValue) removed.

Other behavior:

  • Items with duplicate labels resolve to the first in registration order; a fixed-options registration logs a warning at registration so the developer notices.
  • A query-mode fill_form that arrives before any query_field_options call is rejected with a reason naming query_field_options so the LLM can recover on the next turn.

[!WARNING]
The two-argument fieldValueOptions(ValueOptions, Function<String, V>) overload and the String-only generics on the single-argument variant are gone.

Based on DX test findings.

No related issue.

Type of change

  • Bugfix
  • Feature

Checklist

  • I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview
  • I have added a description following the guideline.
  • The issue is created in the corresponding repository and I have referenced it.
  • I have added tests to ensure my change is effective and works as intended.
  • New and existing tests are passing locally with my change.
  • I have performed self-review and corrected misspellings.
  • Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created.

)

## Description

In form filler DX test sessions, the return type of value options APIs
was a pain point. Even after updating examples and clarifying it in the
documentation, it still was not intuitive: the developer had to think in
labels (strings) when the rest of their codebase thought in domain
objects, and had to supply a `toValue` converter that re-encoded the
label-to-value mapping the field's `ItemLabelGenerator` already
expressed.

This PR replaces that shape with an items-based resolution path:

- Registrations now carry the domain objects, not pre-rendered labels.
Aligned with how the rest of the codebase models selection options.
- Labels for the LLM are derived through one `ItemLabelGenerator`. By
default, the controller reflects the field's own
`setItemLabelGenerator(...)` so common cases need no extra wiring; an
explicit one can be supplied on `ValueOptions` for custom components or
cases where the LLM should see a different label than the UI.
- When the LLM picks a label, the controller walks the registration's
items, applies the same generator per item, and writes the first
matching item to the field. The bijection lives in one place — the label
generator — and the `toValue` converter is no longer needed and has been
removed.

Changed in `ValueOptions`:
- `options(Collection<String>)` → `options(Collection<V>)`
- `options(BiFunction<String, Integer, List<String>>)` →
`options(BiFunction<String, Integer, List<V>>)`

New in `ValueOptions`:
- `itemLabelGenerator(ItemLabelGenerator<V>)` — optional; falls back to
the field's own labeler, then to `String.valueOf(item)`.

Changed in `FormAIController`:
- `fieldValueOptions(ValueOptions<String>)` →
`fieldValueOptions(ValueOptions<V>)`
- `fieldValueOptions(ValueOptions<V>, Function<String, V> toValue)`
removed.

Other behavior:
- Items with duplicate labels resolve to the first in registration
order; a fixed-options registration logs a warning at registration so
the developer notices.
- A query-mode `fill_form` that arrives before any `query_field_options`
call is rejected with a reason naming `query_field_options` so the LLM
can recover on the next turn.

> [!WARNING]
> The two-argument `fieldValueOptions(ValueOptions, Function<String,
V>)` overload and the `String`-only generics on the single-argument
variant are gone.

Based on DX test findings.

No related issue.

## Type of change

- [ ] Bugfix
- [x] Feature

## Checklist

- [x] I have read the contribution guide:
https://vaadin.com/docs/latest/contributing/overview
- [x] I have added a description following the guideline.
- [ ] The issue is created in the corresponding repository and I have
referenced it.
- [x] I have added tests to ensure my change is effective and works as
intended.
- [x] New and existing tests are passing locally with my change.
- [x] I have performed self-review and corrected misspellings.
- [ ] Enhancement / new feature was discussed in a corresponding GitHub
issue and Acceptance Criteria were created.
@sonarqubecloud

Copy link
Copy Markdown

@sissbruecker sissbruecker merged commit 641dc3a into 25.2 Jun 29, 2026
16 checks passed
@sissbruecker sissbruecker deleted the cherry-pick-9564-to-25.2-1782713524751 branch June 29, 2026 06:39
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.

3 participants