Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@

<properties>
<java.version>17</java.version>
<vaadin.version>24.4.0.beta1</vaadin.version>
<langchain.version>0.30.0</langchain.version>
<vaadin.version>24.8.6</vaadin.version>
<langchain.version>1.3.0-beta9</langchain.version>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<version>3.5.4</version>
</parent>

<repositories>
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/com/vaadin/demo/AIConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.Tokenizer;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
Expand Down Expand Up @@ -64,7 +61,7 @@ ApplicationRunner docImporter(EmbeddingStore<TextSegment> embeddingStore) {
return;
}
log.info("Importing documents from {}", docsLocation);
var docs = FileSystemDocumentLoader.loadDocuments(docsLocation);
var docs = FileSystemDocumentLoader.loadDocumentsRecursively(docsLocation);
EmbeddingStoreIngestor.ingest(docs, embeddingStore);
log.info("Imported {} documents", docs.size());
};
Expand Down
91 changes: 54 additions & 37 deletions src/main/java/com/vaadin/demo/views/ChatView.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,86 @@
import com.vaadin.demo.AiAssistant;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.messages.MessageInput;
import com.vaadin.flow.component.messages.MessageList;
import com.vaadin.flow.component.messages.MessageListItem;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.vaadin.firitin.components.messagelist.MarkdownMessage;

import java.util.UUID;

@PageTitle("Chat")
@Route(value = "", layout = MainLayout.class)
public class ChatView extends VerticalLayout {

private final AiAssistant aiAssistant;

private final MessageList messageList = new MessageList();
private final MessageInput messageInput = new MessageInput();
private final Scroller scroller = new Scroller(messageList);

private String chatId = UUID.randomUUID().toString();
private MessageInput messageInput = new MessageInput();

public ChatView(AiAssistant aiAssistant) {
var newChatButton = new Button("New Chat");
var messageList = new VerticalLayout();
focusMessageInput();

setPadding(false);
setSpacing(false);
messageList.setSpacing(true);
messageList.addClassNames(LumoUtility.Padding.Horizontal.SMALL, LumoUtility.Margin.Horizontal.AUTO,
LumoUtility.MaxWidth.SCREEN_MEDIUM);
this.aiAssistant = aiAssistant;

var newChatButton = new Button("New Chat");
newChatButton.addClassName("new-chat-button");
newChatButton.addClickListener(e -> {
chatId = UUID.randomUUID().toString();
messageList.removeAll();
focusMessageInput();
messageList.getItems().clear();
messageInput.focus();
});
add(newChatButton);

messageInput.setWidthFull();
messageInput.addClassNames(LumoUtility.Padding.Horizontal.LARGE, LumoUtility.Padding.Vertical.MEDIUM,
LumoUtility.Margin.Horizontal.AUTO, LumoUtility.MaxWidth.SCREEN_MEDIUM);
messageInput.addSubmitListener(e -> {
var questionText = e.getValue();
var question = new MarkdownMessage(questionText, "You");
question.addClassName("you");
var answer = new MarkdownMessage("Assistant");
answer.getElement().executeJs("this.scrollIntoView()");

messageList.add(question);
messageList.add(answer);
messageList.addClassNames(LumoUtility.MaxWidth.SCREEN_MEDIUM);
messageList.setMarkdown(true);

aiAssistant.chat(chatId, questionText)
.onNext(answer::appendMarkdownAsync)
.onError(err -> System.err.println("ooops" + e))
.start();
});

add(newChatButton);
var scroller = new Scroller(messageList);
scroller.addClassNames(LumoUtility.AlignContent.END, LumoUtility.MaxWidth.SCREEN_MEDIUM);
scroller.setWidthFull();
scroller.addClassName(LumoUtility.AlignContent.END);
addAndExpand(scroller);

messageInput.addClassNames(LumoUtility.MaxWidth.SCREEN_MEDIUM);
messageInput.setWidthFull();
messageInput.addSubmitListener(this::onSubmit);
messageInput.focus();
add(messageInput);
}

private void focusMessageInput() {
messageInput.getElement().executeJs("requestAnimationFrame(() => this.querySelector('vaadin-text-area').focus() )");
setAlignItems(Alignment.CENTER);
setPadding(false);
setSpacing(false);
}

private void onSubmit(MessageInput.SubmitEvent submitEvent) {

var uiOptional = submitEvent.getSource().getUI();

var questionText = submitEvent.getValue();
var question = new MessageListItem(questionText, "You");
question.setUserColorIndex(1);
question.addClassNames("you");

var answer = new MessageListItem("...", "Assistant");
answer.setUserColorIndex(0);

messageList.addItem(question);
messageList.addItem(answer);

aiAssistant.chat(chatId, questionText)
.onPartialResponse(answerToken ->
uiOptional.ifPresent(ui ->
ui.access(() -> {
if (answer.getText().equals("..."))
answer.setText("");
answer.appendText(answerToken);
scroller.scrollToBottom();
})
)
)
.onError(err -> System.err.println("ooops" + err.toString()))
.start();

}
}
3 changes: 2 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ ai.docs.location=/Users/mhellber/Desktop/docs
# OpenAI API
# Better quality, requires sending data to OpenAI
langchain4j.open-ai.streaming-chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.streaming-chat-model.model-name=gpt-4o
langchain4j.open-ai.streaming-chat-model.model-name=gpt-5
langchain4j.open-ai.streaming-chat-model.temperature=1

# Local OpenAI compatible API (ollama)
# Not as performant, but your data does not leave your computer
Expand Down