![image](https://private-user-images.githubusercontent.com/56931032/343861538-ccee2aac-200e-4979-9858-36ece03d9dad.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI0Nzg1NjEsIm5iZiI6MTcyMjQ3ODI2MSwicGF0aCI6Ii81NjkzMTAzMi8zNDM4NjE1MzgtY2NlZTJhYWMtMjAwZS00OTc5LTk4NTgtMzZlY2UwM2Q5ZGFkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAxVDAyMTEwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTlhYzNjYWU2NDZiYWI2ZWMwMGRhZmE1MWU5MTUyYzc0Mjg4N2ZhNDE5ZjViNjBjODlkNjlhN2Y5MzdhOThlYTMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.XhX6GLDnkkZrKwL-DUkQ53g_Ekcj-9jsRpSiLAeoVco)
![image](https://private-user-images.githubusercontent.com/56931032/343861600-b3d04342-6d00-4726-8380-0e54856f6303.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI0Nzg1NjEsIm5iZiI6MTcyMjQ3ODI2MSwicGF0aCI6Ii81NjkzMTAzMi8zNDM4NjE2MDAtYjNkMDQzNDItNmQwMC00NzI2LTgzODAtMGU1NDg1NmY2MzAzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAxVDAyMTEwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWFmMGYwYjcwNGVkOWRjNjcxNDc5ZTZiZjgyMzIzNDUwZjYyZGFkNGFmZTc4MzcyMTA4MmI1OTNmMmZlNDQzOTAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.4QYtjyb88tHC1wPWRncY7FCYdPCbbS6gNiKNSZN-HsA)
![image](https://private-user-images.githubusercontent.com/56931032/344028754-4ea1122a-5947-4f73-ba52-daafa050373b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI0Nzg1NjEsIm5iZiI6MTcyMjQ3ODI2MSwicGF0aCI6Ii81NjkzMTAzMi8zNDQwMjg3NTQtNGVhMTEyMmEtNTk0Ny00ZjczLWJhNTItZGFhZmEwNTAzNzNiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAxVDAyMTEwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTkxNWM1ZDdhZjI2ODVjODMxOWQxOWZkNzBmY2U0MmE5MzMxYmNkMmVmZTNjMGJjYzVlYTVmMzM2MWZiYjBjMzUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.vomSW8nkYE-uTsD8pIuCWFiIRef1r1cx_6MIekxemwc)
- new
![image](https://private-user-images.githubusercontent.com/56931032/344044837-825ecc32-0510-4471-bc48-2098b70eb45b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI0Nzg1NjEsIm5iZiI6MTcyMjQ3ODI2MSwicGF0aCI6Ii81NjkzMTAzMi8zNDQwNDQ4MzctODI1ZWNjMzItMDUxMC00NDcxLWJjNDgtMjA5OGI3MGViNDViLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAxVDAyMTEwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWFjY2YxMjE1MTI3MGY5YjUxZmZiNDAxMWM4ZTVlMzlkM2Y4ZWM2MWQ5YzJkZTEwZDdjMWRkZGM4NDcxZjllNDEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.QmhAW01OAGp2AGI97Q7gLcnRXo-cPto94LUmgJclyvQ)
![image](https://private-user-images.githubusercontent.com/56931032/344044932-94779900-5940-4c32-beed-d69ea6ed86d8.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjI0Nzg1NjEsIm5iZiI6MTcyMjQ3ODI2MSwicGF0aCI6Ii81NjkzMTAzMi8zNDQwNDQ5MzItOTQ3Nzk5MDAtNTk0MC00YzMyLWJlZWQtZDY5ZWE2ZWQ4NmQ4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODAxVDAyMTEwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTg3ZjRmMjM2NzliZDc1ZmRjY2MxYzU0YTJlYjkwZDIyYmNlMTYyNWE3MmMyYTRlYWU5NTNkYjY0NDBkYjIyNzgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.catSzXHKStgkQNx3FgpGuZN82w1ZFhJa41GZIOKksl0)
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
Sure, I'll show you how to replace the mock service with a real ChatClient
that streams responses. This example assumes you have the ChatClient
configured and ready to use.
Here's the updated Vaadin component that uses ChatClient
to stream responses:
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import org.springframework.beans.factory.annotation.Autowired;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.reactive.function.client.WebClient;
@Route("")
public class MainView extends VerticalLayout {
private final ChatClient chatClient;
@Autowired
public MainView(ChatClient chatClient) {
this.chatClient = chatClient;
Text loadingText = new Text("Loading...");
add(loadingText);
Button startButton = new Button("Start Loading", event -> startLoading(loadingText));
add(startButton);
}
private void startLoading(Text loadingText) {
Flux<String> dataStream = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();
dataStream.subscribeOn(Schedulers.boundedElastic())
.doOnNext(item -> getUI().ifPresent(ui -> ui.access(() -> {
loadingText.setText(item);
})))
.doOnComplete(() -> getUI().ifPresent(ui -> ui.access(() -> {
loadingText.setText("Completed!");
})))
.subscribe();
}
}
Ensure your ChatClient
is configured correctly. Here’s a simple configuration class for ChatClient
:
@Configuration
public class ChatClientConfig {
@Value("${chat.api.key}")
private String apiKey;
@Bean
public ChatClient chatClient() {
WebClient webClient = WebClient.builder()
.baseUrl("https://api.openai.com/v1")
.defaultHeader("Authorization", "Bearer " + apiKey)
.build();
return new OpenAiChatClient(webClient);
}
}
ChatClient Configuration:
ChatClient
using WebClient
. It uses the apiKey
from your properties file.MainView:
Text
component to display the loading status and a Button
to start the loading process.startLoading
method subscribes to the Flux
returned by chatClient.prompt()
, updating the Text
component with each emitted item.Reactive Data Streaming:
Flux<String>
is created by calling chatClient.prompt().user("Tell me a joke").stream().content()
.dataStream
is processed reactively, updating the UI with each new piece of data emitted by the Flux
.Ensure you have the necessary dependencies in your pom.xml
or build.gradle
:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
implementation 'com.vaadin:vaadin-spring-boot-starter'
implementation 'io.projectreactor:reactor-core'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework:spring-webflux'
This setup will allow your Vaadin application to consume streaming data from a ChatClient
and update the UI reactively.
Here is a simple template code to create a Vaadin application that consumes a reactive stream using Project Reactor's Flux
. This example includes a mock method returning a Flux<String>
and the Vaadin code to display the content with a delay of 1 second.
Flux<String>
import reactor.core.publisher.Flux;
import java.time.Duration;
public class DataService {
public Flux<String> getDataStream() {
return Flux.just("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
.delayElements(Duration.ofSeconds(1));
}
}
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import reactor.core.publisher.Flux;
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
DataService dataService = new DataService();
Div container = new Div();
container.setText("Loading data...");
add(container);
Flux<String> dataStream = dataService.getDataStream();
dataStream.subscribe(
item -> getUI().ifPresent(ui -> ui.access(() -> container.add(new Div(new Text(item))))),
error -> getUI().ifPresent(ui -> ui.access(() -> container.add(new Div(new Text("Error: " + error.getMessage()))))),
() -> getUI().ifPresent(ui -> ui.access(() -> container.add(new Div(new Text("All items loaded.")))))
);
}
}
Mock Method (DataService
):
getDataStream
returns a Flux<String>
that emits items every 1 second.Vaadin View (MainView
):
VerticalLayout
is created with a Div
to hold the loading data.DataService
is instantiated, and the Flux<String>
from getDataStream
is subscribed to.Div
with the item text is added to the container
Div
.container
.This template code should help you get started with a reactive Vaadin application displaying data with a delay. You can customize it further as per your requirements.
Here's a template code for a chat client using a ChatClient
bean from the Spring AI dependency. The focus here is on the ChatClient
code part within the Vaadin view.
ChatClient
Beanimport com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import org.springframework.beans.factory.annotation.Autowired;
import reactor.core.publisher.Flux;
@Route("")
public class ChatView extends VerticalLayout {
private final ChatClient chatClient;
@Autowired
public ChatView(ChatClient chatClient) {
this.chatClient = chatClient;
TextField questionField = new TextField("Enter your question");
Button sendButton = new Button("Send");
Div chatArea = new Div();
sendButton.addClickListener(event -> {
String question = questionField.getValue();
Flux<String> responseStream = chatClient.getResponse(question);
responseStream.subscribe(
response -> getUI().ifPresent(ui -> ui.access(() -> chatArea.add(new Div(new Text(response))))),
error -> getUI().ifPresent(ui -> ui.access(() -> chatArea.add(new Div(new Text("Error: " + error.getMessage()))))),
() -> getUI().ifPresent(ui -> ui.access(() -> chatArea.add(new Div(new Text("Conversation ended.")))))
);
});
add(questionField, sendButton, chatArea);
}
}
Autowired ChatClient:
ChatClient
bean is autowired into the ChatView
constructor.Vaadin Components:
TextField
for entering the question.Button
to send the question.Div
to display the chat messages.Button Click Listener:
ChatClient
's getResponse
method.Flux<String>
returned by getResponse
is subscribed to.chatArea
Div
as they arrive.This template demonstrates how to use a ChatClient
bean from the Spring AI dependency within a Vaadin view, making the chat functionality reactive.
OB empowers new developers with instant answers, personalized guidance, and 24/7 support, streamlining the onboarding process, boosting productivity, and freeing senior developers from repetitive tasks.