Class AiChat<T extends AiChatMessage>
- Type Parameters:
T- the application's message type
- All Implemented Interfaces:
com.vaadin.flow.component.AttachNotifier,com.vaadin.flow.component.DetachNotifier,com.vaadin.flow.component.HasElement,com.vaadin.flow.component.HasSize,com.vaadin.flow.component.HasStyle,com.vaadin.flow.component.HasTheme,com.vaadin.flow.component.shared.HasThemeVariant<AiChatVariant>,HasActions,HasSubParts,Serializable,org.springframework.beans.factory.Aware,org.springframework.beans.factory.InitializingBean,org.springframework.context.ApplicationContextAware
- Direct Known Subclasses:
BuilderAiChat
AiMessageList
and an AiMessageInput, owns the generation lifecycle over an
LLMProvider, and binds messages as typed data (T).
This is a facade: the wrapped components are implementation details and are not exposed. Configure the chat through this class.
Responses stream asynchronously via UI.access(...): enable server
push (or polling) so tokens reach the browser while the request is in
flight; without it, updates arrive only with the next client round trip.
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprotected static classA live view of the current Vaadin session's HTTP session attributes, resolved on every read rather than copied — seerunWithGenerationContext(com.vaadin.flow.server.VaadinRequest, java.lang.Runnable)for why that matters.protected static classprotected static classAThemeListview over several element theme lists: reads reflect the first target (the chat's own root, which receives every write, so it always holds the full set), while writes fan out to all targets. -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected @Nullable AiChatActionsSupportprotected @Nullable AiMessageListItemprotected @Nullable StringBuilderprotected org.springframework.context.ApplicationContextprotected @Nullable reactor.core.Disposableprotected AiChatI18nprotected @Nullable AiChatMessageFactory<SimpleAiChatMessage> protected com.vaadin.flow.component.html.Divprotected com.vaadin.flow.component.html.Divprotected FileStorageLocatorprotected AiChatI18nprotected AiChatItems<T> protected @Nullable com.vaadin.flow.shared.Registrationprotected @Nullable Tprotected @Nullable List<com.vaadin.flow.component.ai.common.AIAttachment> protected @Nullable Stringprotected com.vaadin.flow.component.html.Divprotected @Nullable com.vaadin.flow.component.ai.provider.LLMProviderprotected @Nullable AiChatMessageFactory<T> protected AiMessageInputprotected AiMessageListprotected booleanprotected GenerationStateprotected @Nullable String -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidAdd an action to the component at the specified index.voidaddAssistantAction(AiMessageAction<?> action) Appends an action to the per-role action set for assistant messages, keeping the ones already there — including any declared in a view descriptor.com.vaadin.flow.shared.RegistrationaddAttachmentClickListener(com.vaadin.flow.component.ComponentEventListener<AttachmentClickEvent<T>> listener) Adds a listener notified when the user clicks a message attachment.com.vaadin.flow.shared.RegistrationaddAttachmentRejectedListener(com.vaadin.flow.component.ComponentEventListener<AttachmentRejectedEvent<T>> listener) Adds a listener notified when an attached file is rejected (too many, too large, or wrong type).protected com.vaadin.flow.shared.Registrationcom.vaadin.flow.shared.RegistrationaddGenerationFailedListener(com.vaadin.flow.component.ComponentEventListener<GenerationFailedEvent<T>> listener) Adds a listener for generation failures.com.vaadin.flow.shared.RegistrationaddGenerationStateChangeListener(com.vaadin.flow.component.ComponentEventListener<GenerationStateChangeEvent<T>> listener) Adds a listener for generation state transitions.com.vaadin.flow.shared.RegistrationaddMessageChangeListener(com.vaadin.flow.component.ComponentEventListener<MessageChangeEvent<T>> listener) Adds a listener for message add/remove changes (the persistence hook).voidaddUserAction(AiMessageAction<?> action) Appends an action to the per-role action set for user messages, keeping the ones already there — including any declared in a view descriptor.voidprotected voidprotected voidbindItems(AiChatItems<T> items) Makes the given source the bound one, re-subscribing and re-rendering.protected com.vaadin.flow.component.ai.provider.LLMProvider.LLMRequestbuildRequest(String userMessage, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) Builds anLLMProvider.LLMRequestfor the given user message and attachments.protected voidCancels the in-flight stream and resets to idle without announcing anything.protected voidprotected voidprotected voidFails fast when messages cannot be created for the bound entity type: the default factory producesSimpleAiChatMessage, so an entity-backed source needs an application-supplied factory.protected TcommitMessage(AiMessageRole role, String content, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) protected AiChatActionsSupportprotected AiMessageInputCreates the inner message input.protected AiMessageListCreates the inner message list.protected static @Nullable com.vaadin.flow.server.WrappedSessionThe current Vaadin session's underlying HTTP session, ornullwhen there is none to read — no current session (so nothing installed a view of it), or one already invalidated.protected StringNames a message in a failure message.protected voidfailGeneration(StringBuilder buffer, Throwable error) Ends the current generation inGenerationState.ERROR: clears the stream state, drops an empty transient placeholder, and firesGenerationFailedEventwith whatever text had arrived.protected intfindPromptIndex(List<T> data, int index) Finds the message that carries the prompt to re-run: the nearest user message at or before the given index.protected Optional<com.vaadin.flow.component.Component> findSlotSubPart(com.vaadin.flow.component.Component slotComponent, String name) Resolves an id against a single slot component: the component itself when its own id matches, otherwise a descendant of it.protected voidprotected voidfireMessageChange(T message, MessageChangeEvent.ChangeType changeType) @Nullable ActionReturns an action with passed id.protected AiChatActionsSupportList<AiMessageAction<?>> protected AiChatMessageFactory<SimpleAiChatMessage> Returns the factory used when the application supplied none.@Nullable com.vaadin.flow.component.Component@Nullable com.vaadin.flow.component.ComponentgetI18n()Gets the localization currently in effect — resolved from the message bundle when the chat was created, and replaced by everysetI18n(AiChatI18n)since.@Nullable com.vaadin.flow.component.Component@Nullable com.vaadin.flow.component.Component@Nullable com.vaadin.flow.component.Component@Nullable com.vaadin.flow.component.ComponentgetItems()@Nullable com.vaadin.flow.component.ai.provider.LLMProviderintlongint@Nullable AiChatMessageFactory<T> protected AiMessageInputgetState()@Nullable ObjectgetSubPart(String name) Exposes the slot components and the message actions to the framework's id-based component walk.@Nullable Stringcom.vaadin.flow.dom.ThemeListReturns a theme list projecting every change onto the inner message list and message input as well as this chat's own root element.@Nullable com.vaadin.flow.component.ComponentGets the escalating thinking stages.The thinking-status feed of the inner message list: the ordered account of what the assistant is doing, rendered while generating.List<AiMessageAction<?>> protected StringRegisters — once perFileRef— a session download handler that streams the file lazily from storage, and returns its URL.protected voidWires the inner input/list and the message source.protected com.vaadin.flow.component.html.Divprotected voidinitI18n()booleanbooleanbooleanprotected voidonAttach(com.vaadin.flow.component.AttachEvent attachEvent) protected voidonComplete(StringBuilder buffer) protected voidonDetach(com.vaadin.flow.component.DetachEvent detachEvent) protected voidonError(StringBuilder buffer, Throwable error) protected voidHandles a change in the bound message source: everyAiChatItems.ItemsChangeTyperebuilds the rendered list, and aREFRESHadditionally re-checks the order.protected voidonToken(StringBuilder buffer, String token) voidSends a prompt programmatically, as if typed and submitted.protected List<com.vaadin.flow.component.ai.common.AIAttachment> promptAttachments(T message) Reads a stored prompt's attachments back into the form the provider takes.protected voidrebuild()voidRe-runs the last user prompt and streams a fresh answer.voidregenerate(AiMessageListItem item) Resolves the rendered message to the domain message it was rendered from and delegates toregenerateFrom(AiChatMessage).protected voidregenerateAt(int index) Rewinds the conversation to the user prompt at or before the given index in the message source and streams a fresh answer.voidregenerateFrom(T message) Re-runs the user prompt the given message belongs to and streams a fresh answer, dropping the message and everything after it (each firingREMOVED).voidremoveAction(Action action) Removes the action from the component.protected voidprotected voidremoveMessage(T message) protected AiChatMessageFactory<T> protected StringresolveAnnouncement(Function<AiChatI18n, @Nullable String> getter, String fallback) Resolves an aria-live announcement: the user-set value, else the bundle-localized default, else the hardcoded fallback.protected voidrunWithGenerationContext(@Nullable com.vaadin.flow.server.VaadinRequest request, Runnable body) Runs a generation callback with the two pieces of request-bound context an application listener needs: the HTTP session's attributes and theVaadinRequestcaptured when the generation started.protected voidCommits the user message and streams an answer for it.voidsetAcceptedFileExtensions(String... extensions) Restricts attachments to the given file extensions.voidsetAcceptedMimeTypes(String... mimeTypes) Restricts attachments to the given MIME types.voidsetAnnounceMessages(boolean announceMessages) Announces new messages through the message list's ARIA live region.voidsetApplicationContext(org.springframework.context.ApplicationContext applicationContext) voidsetAssistantActions(List<? extends AiMessageAction<?>> actions) Sets the per-role action set for assistant messages.voidsetAttachmentsEnabled(boolean attachmentsEnabled) Sets whether the attach affordance (paperclip, drag-and-drop, chips) is shown.voidsetEmptyStateFooter(@Nullable com.vaadin.flow.component.Component component) Sets the component shown below the centered input in the empty state.voidsetEmptyStateHeader(@Nullable com.vaadin.flow.component.Component component) Sets the component shown above the centered input in the empty state.voidsetEnterAction(EnterAction enterAction) Sets the Enter-key behavior.voidsetI18n(AiChatI18n i18n) Applies the localization, handing each nested holder to the component it belongs to.voidsetInputFooterComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's footer slot — the band below the composer's input box.voidsetInputHeaderComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's header slot — the band above the composer's input box.voidsetInputPrefixComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's prefix slot.voidsetInputSuffixComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's suffix slot.voidsetItems(AiChatItems<T> items) Binds the chat to the given message source.voidsetItems(CollectionContainer<T> container) Binds the chat to a Jmix data container, which supplies the history and receives the generated messages, so the chat takes part in the view's ordinary load and save cycle.voidsetItems(Collection<T> items) Sets the messages from aCollection, wrapping them in aListAiChatItemsthat copies them, so generation works even for an immutable collection.voidsetLlmProvider(@Nullable com.vaadin.flow.component.ai.provider.LLMProvider llmProvider) Sets the LLM provider used to generate responses.voidsetMaxFiles(int maxFiles) Sets the maximum number of files that can be attached to one message.voidsetMaxFileSize(long maxFileSize) Sets the maximum size of a single attachment.voidsetMaxRows(int maxRows) Sets the maximum visible textarea rows before it scrolls.voidsetMessageFactory(@Nullable AiChatMessageFactory<T> messageFactory) Sets the factory creating theTinstances for the user echo and the assistant response.voidsetRemoveDelegate(Consumer<T> removeDelegate) Sets the delegate that deletes a message the component is removing from the bound source, before it leaves that source.voidsetSaveDelegate(Function<T, T> saveDelegate) Sets the delegate that persists a message the component has just created, before it enters the bound source.protected voidsetState(GenerationState newState) voidsetSystemPrompt(@Nullable String systemPrompt) Sets the system prompt injected into every request.voidsetThinkingIndicator(@Nullable com.vaadin.flow.component.Component indicator) Sets a custom thinking indicator shown while the assistant is generating, ornullfor the built-in default.voidsetThinkingStages(List<ThinkingStage> stages) Sets the escalating thinking stages.voidsetUploadDropZoneEnabled(boolean uploadDropZoneEnabled) Sets whether files may be attached by dropping them on the composer.voidsetUserActions(List<? extends AiMessageAction<?>> actions) Sets the per-role action set for user messages.voidstop()Cancels the in-flight generation.protected voidSubscribes to the provider and routes its signals onto the UI thread.protected voidtargetMessageAction(AiMessageAction<?> action) Hands the chat to a message action being assigned to a role, if it declares itself aTargetAction— the contract by which a per-message action reaches the message source.protected AiMessageListItem.AttachmentMaps a stored file reference to a display attachment.protected AiMessageListItemtoListItem(T message) Maps a data message to a rendered list item, rejecting anullrole as a contract violation.protected MessageRoletoRenderRole(AiMessageRole role) Projects the contract's role onto the message list's render discriminator.protected voidprotected voidWarns — once per binding — when the messages are not in ascending chronological order.Methods inherited from class com.vaadin.flow.component.Composite
getChildren, getContent, getElementMethods inherited from class com.vaadin.flow.component.Component
addListener, bindVisible, findAncestor, fireEvent, from, get, getEventBus, getId, getListeners, getLocale, getParent, getTestId, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getUI, hasListener, isAttached, isTemplateMapped, isVisible, onEnabledStateChanged, removeFromParent, scrollIntoView, scrollIntoView, set, setElement, setId, setTestId, setVisible, whenAttachedMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.vaadin.flow.component.AttachNotifier
addAttachListenerMethods inherited from interface com.vaadin.flow.component.DetachNotifier
addDetachListenerMethods inherited from interface io.jmix.flowui.kit.component.HasActions
addAction, removeAction, removeAllActionsMethods inherited from interface com.vaadin.flow.component.HasElement
getElementMethods inherited from interface com.vaadin.flow.component.HasSize
bindHeight, bindWidth, getHeight, getHeightUnit, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getWidth, getWidthUnit, setHeight, setHeight, setHeightFull, setMaxHeight, setMaxHeight, setMaxWidth, setMaxWidth, setMinHeight, setMinHeight, setMinWidth, setMinWidth, setSizeFull, setSizeUndefined, setWidth, setWidth, setWidthFullMethods inherited from interface com.vaadin.flow.component.HasStyle
addClassName, addClassNames, bindClassName, bindClassNames, getClassName, getClassNames, getStyle, hasClassName, removeClassName, removeClassNames, setClassName, setClassNameMethods inherited from interface com.vaadin.flow.component.HasTheme
addThemeName, addThemeNames, bindThemeName, bindThemeNames, getThemeName, hasThemeName, removeThemeName, removeThemeNames, setThemeName, setThemeNameMethods inherited from interface com.vaadin.flow.component.shared.HasThemeVariant
addThemeVariants, bindThemeVariant, bindThemeVariants, removeThemeVariants, setThemeVariant, setThemeVariants, setThemeVariants
-
Field Details
-
messageList
-
messageInput
-
emptyStateHeader
protected com.vaadin.flow.component.html.Div emptyStateHeader -
liveRegion
protected com.vaadin.flow.component.html.Div liveRegion -
applicationContext
protected org.springframework.context.ApplicationContext applicationContext -
fileStorageLocator
-
llmProvider
protected transient @Nullable com.vaadin.flow.component.ai.provider.LLMProvider llmProvider -
systemPrompt
-
items
-
itemsChangeRegistration
protected @Nullable com.vaadin.flow.shared.Registration itemsChangeRegistration -
sourceOrderWarned
protected boolean sourceOrderWarned -
messageFactory
-
defaultMessageFactory
-
saveDelegate
-
removeDelegate
-
i18n
-
defaultI18n
-
actionsSupport
-
attachmentResources
-
attachmentsEnabledListeners
-
state
-
currentStream
protected transient @Nullable reactor.core.Disposable currentStream -
activeBuffer
-
activeAssistantItem
-
lastUserMessage
-
lastUserAttachments
-
lastAssistantMessage
-
-
Constructor Details
-
AiChat
public AiChat()Creates an empty chat. Initialized by Spring viaafterPropertiesSet(); a provider must be set viasetLlmProvider(LLMProvider)before the first send.
-
-
Method Details
-
currentHttpSession
protected static @Nullable com.vaadin.flow.server.WrappedSession currentHttpSession()The current Vaadin session's underlying HTTP session, ornullwhen there is none to read — no current session (so nothing installed a view of it), or one already invalidated.- Returns:
- the HTTP session, or
nullif none is available
-
initComponent
protected void initComponent()Wires the inner input/list and the message source. Runs fromafterPropertiesSet(), after the application context is injected — not from the constructor. -
initContent
protected com.vaadin.flow.component.html.Div initContent()- Overrides:
initContentin classcom.vaadin.flow.component.Composite<com.vaadin.flow.component.html.Div>
-
createMessageList
Creates the inner message list. Override to supply a customized instance.- Returns:
- a new message list
-
createMessageInput
Creates the inner message input. Override to supply a customized instance.- Returns:
- a new message input
-
setApplicationContext
public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws org.springframework.beans.BeansException - Specified by:
setApplicationContextin interfaceorg.springframework.context.ApplicationContextAware- Throws:
org.springframework.beans.BeansException
-
afterPropertiesSet
public void afterPropertiesSet()- Specified by:
afterPropertiesSetin interfaceorg.springframework.beans.factory.InitializingBean
-
initI18n
protected void initI18n() -
checkLlmProviderConfigured
protected void checkLlmProviderConfigured() -
checkMessageFactoryConfigured
protected void checkMessageFactoryConfigured()Fails fast when messages cannot be created for the bound entity type: the default factory producesSimpleAiChatMessage, so an entity-backed source needs an application-supplied factory. -
setLlmProvider
public void setLlmProvider(@Nullable com.vaadin.flow.component.ai.provider.LLMProvider llmProvider) Sets the LLM provider used to generate responses.- Parameters:
llmProvider- the provider, ornullto unset
-
getLlmProvider
public @Nullable com.vaadin.flow.component.ai.provider.LLMProvider getLlmProvider()- Returns:
- the LLM provider, or
nullif none is set
-
setSystemPrompt
Sets the system prompt injected into every request. Whitespace is stripped; anullor blank value defers to the provider's own default.- Parameters:
systemPrompt- the system prompt, ornull
-
getSystemPrompt
- Returns:
- the system prompt, or
null
-
setMessageFactory
Sets the factory creating theTinstances for the user echo and the assistant response.- Parameters:
messageFactory- the factory, ornullto use the default one
-
getMessageFactory
- Returns:
- the message factory, or
nullif none is set
-
getSaveDelegate
- Returns:
- the delegate that persists a committed message, or
nullif none is set
-
setSaveDelegate
Sets the delegate that persists a message the component has just created, before it enters the bound source.The delegate returns the instance the component keeps — a persistence layer routinely answers with a different object than it was handed (a tracked or merged copy); returning the argument unchanged is valid for a layer that saves in place.
Throwing abandons the commit, nothing having entered the source yet: on a user message the exception propagates out of the send, on an assistant message the generation ends in
GenerationState#ERRORwith aGenerationFailedEvent.Unset, the component persists nothing. The
autoSavedescriptor attribute installs aDataContext-backed default.- Parameters:
saveDelegate- the delegate to set, notnull
-
getRemoveDelegate
- Returns:
- the delegate that deletes a removed message, or
nullif none is set
-
setRemoveDelegate
Sets the delegate that deletes a message the component is removing from the bound source, before it leaves that source.Called on the rewind paths —
regenerate()andregenerateFrom(T), once per message, newest first. A delegate that throws stops the truncation where it failed; the deletions already made cannot be undone. Unset, the component deletes nothing.- Parameters:
removeDelegate- the delegate to set, notnull
-
setMaxRows
public void setMaxRows(int maxRows) Sets the maximum visible textarea rows before it scrolls.- Parameters:
maxRows- the maximum rows
-
getMaxRows
public int getMaxRows()- Returns:
- the maximum visible textarea rows
-
setEnterAction
Sets the Enter-key behavior.- Parameters:
enterAction- the behavior
-
getEnterAction
- Returns:
- the Enter-key behavior
-
getMessageInputInternal
-
addAttachmentsEnabledChangeListenerInternal
-
isAttachmentsEnabled
public boolean isAttachmentsEnabled()- Returns:
trueif the attach affordance is shown
-
setAttachmentsEnabled
public void setAttachmentsEnabled(boolean attachmentsEnabled) Sets whether the attach affordance (paperclip, drag-and-drop, chips) is shown. Off by default.- Parameters:
attachmentsEnabled-trueto enable attachments
-
getMaxFiles
public int getMaxFiles()- Returns:
- the maximum number of files that can be attached to one message
-
setMaxFiles
public void setMaxFiles(int maxFiles) Sets the maximum number of files that can be attached to one message.- Parameters:
maxFiles- the maximum file count
-
getMaxFileSize
public long getMaxFileSize()- Returns:
- the maximum size of a single attachment, in bytes
-
setMaxFileSize
public void setMaxFileSize(long maxFileSize) Sets the maximum size of a single attachment.- Parameters:
maxFileSize- the maximum size, in bytes
-
getAcceptedMimeTypes
- Returns:
- an unmodifiable view of the accepted MIME types, empty when any type is accepted
-
setAcceptedMimeTypes
Restricts attachments to the given MIME types. Passing none accepts any type.- Parameters:
mimeTypes- the accepted MIME types, e.g."image/*"
-
getAcceptedFileExtensions
- Returns:
- an unmodifiable view of the accepted file extensions, empty when any is accepted
-
setAcceptedFileExtensions
Restricts attachments to the given file extensions. Passing none accepts any extension.- Parameters:
extensions- the accepted extensions, e.g.".png"
-
isUploadDropZoneEnabled
public boolean isUploadDropZoneEnabled()- Returns:
trueif files may be attached by dropping them on the composer
-
setUploadDropZoneEnabled
public void setUploadDropZoneEnabled(boolean uploadDropZoneEnabled) Sets whether files may be attached by dropping them on the composer. Enabled by default; the paperclip stays available either way.- Parameters:
uploadDropZoneEnabled-trueto accept dropped files
-
getThemeNames
public com.vaadin.flow.dom.ThemeList getThemeNames()Returns a theme list projecting every change onto the inner message list and message input as well as this chat's own root element. EveryHasThemeandHasThemeVariantmutator funnels through here, so a variant set either way reaches both inner components.- Specified by:
getThemeNamesin interfacecom.vaadin.flow.component.HasTheme
-
setInputPrefixComponent
public void setInputPrefixComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's prefix slot.- Parameters:
component- the component, ornullto clear
-
getInputPrefixComponent
public @Nullable com.vaadin.flow.component.Component getInputPrefixComponent()- Returns:
- the input's prefix component, or
null
-
setInputSuffixComponent
public void setInputSuffixComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's suffix slot.- Parameters:
component- the component, ornullto clear
-
getInputSuffixComponent
public @Nullable com.vaadin.flow.component.Component getInputSuffixComponent()- Returns:
- the input's suffix component, or
null
-
setInputHeaderComponent
public void setInputHeaderComponent(@Nullable com.vaadin.flow.component.Component component) Sets a component into the input's header slot — the band above the composer's input box. Hidden by default while the chat is empty, since the empty state shows the chat's own bands;AiChatVariant.INPUT_HEADER_COMPONENT_ALWAYS_VISIBLEkeeps it shown.- Parameters:
component- the component, ornullto clear
-
getInputHeaderComponent
public @Nullable com.vaadin.flow.component.Component getInputHeaderComponent()- Returns:
- the input's header component, or
null
-
setAnnounceMessages
public void setAnnounceMessages(boolean announceMessages) Announces new messages through the message list's ARIA live region.falseby default. Independent of the chat's own live region, which announces the generation lifecycle and is always on.- Parameters:
announceMessages-trueto announce new messages
-
isAnnounceMessages
public boolean isAnnounceMessages()- Returns:
trueif new messages are announced to assistive technologies
-
setThinkingIndicator
public void setThinkingIndicator(@Nullable com.vaadin.flow.component.Component indicator) Sets a custom thinking indicator shown while the assistant is generating, ornullfor the built-in default.- Parameters:
indicator- the indicator component, ornullfor the default
-
getThinkingIndicator
public @Nullable com.vaadin.flow.component.Component getThinkingIndicator()- Returns:
- the custom thinking indicator, or
nullwhen the built-in default is used
-
setThinkingStages
Sets the escalating thinking stages.- Parameters:
stages- the stages
-
getThinkingStages
Gets the escalating thinking stages.- Returns:
- an unmodifiable view of the stages
-
getThinkingStatusPublisher
The thinking-status feed of the inner message list: the ordered account of what the assistant is doing, rendered while generating. Ephemeral — cleared at the start of every generation and again when it finishes, fails or is stopped.- Returns:
- the feed, never
null
-
setUserActions
Sets the per-role action set for user messages.- Parameters:
actions- the actions
-
getUserActions
- Returns:
- an unmodifiable view of the per-role action set for user messages
-
addUserAction
Appends an action to the per-role action set for user messages, keeping the ones already there — including any declared in a view descriptor.- Parameters:
action- the action to append- See Also:
-
setAssistantActions
Sets the per-role action set for assistant messages.- Parameters:
actions- the actions
-
getAssistantActions
- Returns:
- an unmodifiable view of the per-role action set for assistant messages
-
addAssistantAction
Appends an action to the per-role action set for assistant messages, keeping the ones already there — including any declared in a view descriptor.- Parameters:
action- the action to append- See Also:
-
targetMessageAction
Hands the chat to a message action being assigned to a role, if it declares itself aTargetAction— the contract by which a per-message action reaches the message source. An action targeting anything else must not be declared aTargetAction.Assigning is the only wiring point: the role API has no remove operation, so there is no detach to clear the target on.
- Parameters:
action- the action to wire
-
setI18n
Applies the localization, handing each nested holder to the component it belongs to. Replace semantics: the given holder becomes the effective one, so a field leftnullfalls back to the client's hardcoded English rather than to the message bundle. Anullnested holder leaves that component's localization alone.To re-word one label, start from
getI18n()rather than from a freshnew AiChatI18n().- Parameters:
i18n- the localization bean, notnull
-
getI18n
Gets the localization currently in effect — resolved from the message bundle when the chat was created, and replaced by everysetI18n(AiChatI18n)since.- Returns:
- the i18n bean
-
getState
- Returns:
- the current generation state
-
prompt
Sends a prompt programmatically, as if typed and submitted. Silently ignored while a generation is in progress and for blank input.- Parameters:
userMessage- the prompt text- Throws:
IllegalStateException- if no LLM provider is configured or the component is not attached to a UI
-
regenerate
public void regenerate()Re-runs the last user prompt and streams a fresh answer. A committed assistant message is removed first (firingREMOVED); an errored or stopped one leaves the data untouched, only dropping the transient partial answer.- Throws:
IllegalStateException- if no LLM provider is configured or the component is not attached to a UI
-
regenerateFrom
Re-runs the user prompt the given message belongs to and streams a fresh answer, dropping the message and everything after it (each firingREMOVED).The prompt is the nearest
AiMessageRole.USERmessage at or before the given one, so an assistant answer rewinds to the prompt it answers.Silently ignored while a generation is in progress, for a message that is not in this chat's source, and when no user prompt precedes it.
- Parameters:
message- the message to regenerate from, notnull- Throws:
IllegalStateException- if no LLM provider is configured or the component is not attached to a UI
-
regenerate
Resolves the rendered message to the domain message it was rendered from and delegates toregenerateFrom(AiChatMessage).Silently ignored for an item that is not rendered by this chat. An item with no source message — the transient bubble a stopped or failed generation leaves behind — rewinds to the conversation's last prompt, behaving like
regenerate().- Parameters:
item- the rendered message to regenerate from, notnull- Throws:
IllegalStateException- if no LLM provider is configured or the component is not attached to a UI
-
regenerateAt
protected void regenerateAt(int index) Rewinds the conversation to the user prompt at or before the given index in the message source and streams a fresh answer.- Parameters:
index- the index in the message source to regenerate from
-
findPromptIndex
Finds the message that carries the prompt to re-run: the nearest user message at or before the given index.- Parameters:
data- the messages in source orderindex- the index to search back from; clamped to the last message, defensively — every caller now passes an in-range index- Returns:
- the prompt's index, or
-1if none precedes it
-
promptAttachments
Reads a stored prompt's attachments back into the form the provider takes. Only aFileRefsurvives in the data, so the bytes are re-read from the storage the reference names.- Parameters:
message- the prompt to re-send- Returns:
- its attachments, empty if it has none
-
removeMessage
-
stop
public void stop()Cancels the in-flight generation. No-op unless a generation is in progress. Partially streamed text stays visible until the next rebuild and is not committed to the data; a stop before the first token leaves nothing behind. -
cancelGeneration
protected void cancelGeneration()Cancels the in-flight stream and resets to idle without announcing anything. Shared bystop(), detach cleanup, and message source replacement. -
onAttach
protected void onAttach(com.vaadin.flow.component.AttachEvent attachEvent) - Overrides:
onAttachin classcom.vaadin.flow.component.Component
-
onDetach
protected void onDetach(com.vaadin.flow.component.DetachEvent detachEvent) - Overrides:
onDetachin classcom.vaadin.flow.component.Component
-
send
protected void send(String userMessage, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) Commits the user message and streams an answer for it. A message needs text or at least one attachment: a blank prompt carrying attachments is sent (an attachment alone is an ordinary chat message), a blank one carrying none is silently ignored, as is any prompt arriving while a generation is in progress.The text is committed and sent to the provider unchanged, blank or empty — the component substitutes no wording of its own.
- Parameters:
userMessage- the prompt text, notnull; may be empty or blank whenattachmentsis not emptyattachments- the files attached to the message, notnull
-
checkAttached
protected void checkAttached() -
stream
protected void stream(String userMessage, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) Subscribes to the provider and routes its signals onto the UI thread. An override that re-implements the subscription must wrap the terminal callbacks inrunWithGenerationContext(com.vaadin.flow.server.VaadinRequest, java.lang.Runnable), or application listeners lose the request-bound context there; overridingonComplete(java.lang.StringBuilder)oronError(java.lang.StringBuilder, java.lang.Throwable)alone is safe, since the wrapping sits outside them.- Parameters:
userMessage- the prompt textattachments- the prompt's attachments
-
runWithGenerationContext
protected void runWithGenerationContext(@Nullable com.vaadin.flow.server.VaadinRequest request, Runnable body) Runs a generation callback with the two pieces of request-bound context an application listener needs: the HTTP session's attributes and theVaadinRequestcaptured when the generation started. The terminal callbacks run on a provider thread, where otherwise{session_*}query parameters resolve tonullbehind a warning and a view access check throws. Mirrors what Jmix does for the same hop inInMemoryUploadHandlerandBackgroundWorkerImpl; authentication needs none.This installs two thread-locals, not Spring's session scope: a session-scoped bean such as
SessionDatastill fails here withScopeNotActiveException.Nothing is installed over a context the thread already carries. Session attributes are a live view rather than a snapshot, so a re-entrant
session.access(...)on another user's session reads its own attributes rather than this generation's.- Parameters:
request- the request captured when the generation started, ornullbody- the callback to run
-
onToken
-
onComplete
-
onError
-
failGeneration
Ends the current generation inGenerationState.ERROR: clears the stream state, drops an empty transient placeholder, and firesGenerationFailedEventwith whatever text had arrived. Called for a failing stream and for a failure to persist an otherwise complete answer, which is the same outcome for the reader — the answer is on screen and is not stored.- Parameters:
buffer- the accumulated answer texterror- the failure
-
finishGeneration
protected void finishGeneration() -
commitMessage
protected T commitMessage(AiMessageRole role, String content, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) -
removeLastAssistant
protected void removeLastAssistant() -
requireFactory
-
getDefaultMessageFactory
Returns the factory used when the application supplied none. Resolved lazily and only from the fallback path, so a chat given its own factory never asks the context for this bean. Override to supply a different fallback; to replace it application-wide, declare a@PrimaryDefaultAiChatMessageFactorysubclass instead.- Returns:
- the fallback message factory
-
setState
-
resolveAnnouncement
Resolves an aria-live announcement: the user-set value, else the bundle-localized default, else the hardcoded fallback.- Parameters:
getter- the accessor of the label to resolvefallback- the hardcoded fallback- Returns:
- the announcement text
-
announce
-
setItems
Binds the chat to the given message source. Replacing the source cancels an in-flight generation and clears theregenerate()target, so a restored conversation does not inherit the previous one's last prompt.- Parameters:
items- the message source, notnull
-
setItems
Sets the messages from aCollection, wrapping them in aListAiChatItemsthat copies them, so generation works even for an immutable collection.- Parameters:
items- the messages to display, notnull- See Also:
-
setItems
Binds the chat to a Jmix data container, which supplies the history and receives the generated messages, so the chat takes part in the view's ordinary load and save cycle. Messages render in container order, so the loader must sort them chronologically ascending (seewarnIfSourceOutOfOrder()).The container's entity type must implement
AiChatMessage, and aAiChatMessageFactorymust be supplied — the component does not create entities itself.- Parameters:
container- the container to bind to, notnull- See Also:
-
getItems
- Returns:
- the bound message source; never
null— an unbound chat holds an empty in-memory source
-
bindItems
Makes the given source the bound one, re-subscribing and re-rendering. The single place that both listens and renders, so the two never diverge.- Parameters:
items- the message source to bind, notnull
-
onItemsChanged
Handles a change in the bound message source: everyAiChatItems.ItemsChangeTyperebuilds the rendered list, and aREFRESHadditionally re-checks the order. AREFRESHis deliberately not read as a conversation swap — a container fires it on every in-place reload, and cancelling generation there would break the common commit-then-reload pattern. Swaps are recognized only insetItems(AiChatItems).- Parameters:
event- the change event
-
warnIfSourceOutOfOrder
protected void warnIfSourceOutOfOrder()Warns — once per binding — when the messages are not in ascending chronological order. The component renders in source order and never re-sorts, so an unsorted loader is a silent defect: the history reads the wrong way round, andregenerateAt(int)rewinds by position, truncating the wrong messages.Nothing is re-ordered and nothing throws:
AiChatMessage.getTime()is too weak a sort key to carry the component — nullable by contract, and unable to order a turn's own two messages stamped in one commit. So anullis skipped rather than read as a reset, and equal times pass unjudged.An
ADDis not checked, the chat's own appends being chronological by construction. Override as a no-op to silence the warning for a source whose order is deliberately not chronological. -
rebuild
protected void rebuild() -
toListItem
Maps a data message to a rendered list item, rejecting anullrole as a contract violation.Carries the source message onto the item, which is what lets a per-message action and an attachment click resolve back to the domain message. The streaming placeholder is built outside this method and deliberately carries none.
- Parameters:
message- the data message- Returns:
- the rendered item
-
toRenderRole
Projects the contract's role onto the message list's render discriminator.- Parameters:
role- the contract role- Returns:
- the render role
- Throws:
IllegalArgumentException- if the role has no rendering
-
describe
Names a message in a failure message. Prefers the entity id, since a message entity'stoString()rarely identifies the row.- Parameters:
message- the message to describe- Returns:
- a short description for a failure message
-
toDisplayAttachment
Maps a stored file reference to a display attachment. Images get a session download-handler URL for an inline thumbnail; other types get anullURL and render as a file chip (download runs through the click event).- Parameters:
ref- the stored file reference- Returns:
- the display attachment
-
imageUrl
Registers — once perFileRef— a session download handler that streams the file lazily from storage, and returns its URL. MirrorsUiComponentUtils.createResource'sFileRefbranch, but also registers the handler to obtain a URL string.- Parameters:
ref- the stored file reference- Returns:
- the download URL
-
updateEmptyState
protected void updateEmptyState() -
setEmptyStateHeader
public void setEmptyStateHeader(@Nullable com.vaadin.flow.component.Component component) Sets the component shown above the centered input in the empty state.- Parameters:
component- the header component, ornullto clear
-
getEmptyStateHeader
public @Nullable com.vaadin.flow.component.Component getEmptyStateHeader()- Returns:
- the empty-state header component, or
nullif none
-
getSubPart
Exposes the slot components and the message actions to the framework's id-based component walk. A facade's slot children are reachable neither as own components nor throughHasPrefix/HasSuffix, so without this an id declared in a slot could not be injected with@ViewComponent.Only the slots nobody else can answer for are resolved here — the input's
prefix/suffixand the chat's own empty-state bands; the composed components answer for their own children.- Specified by:
getSubPartin interfaceHasSubParts- Parameters:
name- the component id to find- Returns:
- the matching action, slot component or descendant, or
nullif none matches
-
findSlotSubPart
protected Optional<com.vaadin.flow.component.Component> findSlotSubPart(com.vaadin.flow.component.Component slotComponent, String name) Resolves an id against a single slot component: the component itself when its own id matches, otherwise a descendant of it.- Parameters:
slotComponent- the slot component to searchname- the component id to find- Returns:
- the matching component, or an empty
Optional
-
addMessageChangeListener
public com.vaadin.flow.shared.Registration addMessageChangeListener(com.vaadin.flow.component.ComponentEventListener<MessageChangeEvent<T>> listener) Adds a listener for message add/remove changes (the persistence hook).- Parameters:
listener- the listener- Returns:
- a registration for removing the listener
-
addGenerationStateChangeListener
public com.vaadin.flow.shared.Registration addGenerationStateChangeListener(com.vaadin.flow.component.ComponentEventListener<GenerationStateChangeEvent<T>> listener) Adds a listener for generation state transitions.- Parameters:
listener- the listener- Returns:
- a registration for removing the listener
-
addGenerationFailedListener
public com.vaadin.flow.shared.Registration addGenerationFailedListener(com.vaadin.flow.component.ComponentEventListener<GenerationFailedEvent<T>> listener) Adds a listener for generation failures.- Parameters:
listener- the listener- Returns:
- a registration for removing the listener
-
addAttachmentClickListener
public com.vaadin.flow.shared.Registration addAttachmentClickListener(com.vaadin.flow.component.ComponentEventListener<AttachmentClickEvent<T>> listener) Adds a listener notified when the user clicks a message attachment.- Parameters:
listener- the listener- Returns:
- a registration for removing the listener
-
addAttachmentRejectedListener
public com.vaadin.flow.shared.Registration addAttachmentRejectedListener(com.vaadin.flow.component.ComponentEventListener<AttachmentRejectedEvent<T>> listener) Adds a listener notified when an attached file is rejected (too many, too large, or wrong type).- Parameters:
listener- the listener- Returns:
- a registration for removing the listener
-
addAction
Description copied from interface:HasActionsAdd an action to the component at the specified index.- Specified by:
addActionin interfaceHasActions- Parameters:
action- action to addindex- index at which the specified action is to be added
-
removeAction
Description copied from interface:HasActionsRemoves the action from the component.- Specified by:
removeActionin interfaceHasActions- Parameters:
action- action to remove
-
getActions
- Specified by:
getActionsin interfaceHasActions- Returns:
- unmodifiable collection of actions
-
getAction
Description copied from interface:HasActionsReturns an action with passed id.- Specified by:
getActionin interfaceHasActions- Parameters:
id- id of the action to find- Returns:
- an action by its id, or
nullif not found
-
getActionsSupport
-
createActionsSupport
-
fireMessageChange
-
buildRequest
protected com.vaadin.flow.component.ai.provider.LLMProvider.LLMRequest buildRequest(String userMessage, List<com.vaadin.flow.component.ai.common.AIAttachment> attachments) Builds anLLMProvider.LLMRequestfor the given user message and attachments. Tools are empty in this iteration. Subclasses may override to customize the request (e.g. future tools).- Parameters:
userMessage- the user's prompt textattachments- the files attached to the message (may be empty)- Returns:
- the request
-