Modify for 1.1

This commit is contained in:
Minecon724 2024-09-07 14:58:56 +02:00
parent 876b72ef6a
commit f5f20dd0e9
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
8 changed files with 258 additions and 79 deletions

View file

@ -36,7 +36,7 @@
<dependency> <dependency>
<groupId>eu.m724</groupId> <groupId>eu.m724</groupId>
<artifactId>chatapi</artifactId> <artifactId>chatapi</artifactId>
<version>1.0</version> <version>1.1.1</version>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -1,14 +0,0 @@
package eu.m724.chaqt;
import eu.m724.chatapi.chat.Chat;
public class ChatWindowViewModel {
private ChatWindowWidget widget;
private Chat chat;
public ChatWindowViewModel(ChatWindowWidget widget, Chat chat) {
this.widget = widget;
this.chat = chat;
}
}

View file

@ -1,62 +0,0 @@
package eu.m724.chaqt;
import eu.m724.chatapi.chat.Chat;
import io.qt.core.Qt;
import io.qt.widgets.*;
public class ChatWindowWidget extends QWidget {
private ChatWindowViewModel viewModel;
private QVBoxLayout layout;
private QTextEdit textEdit;
public ChatWindowWidget(Chat chat) {
this.viewModel = new ChatWindowViewModel(this, chat);
//
this.layout = new QVBoxLayout();
layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
QWidget widget = new QWidget();
widget.setLayout(layout);
QScrollArea scrollArea = new QScrollArea();
scrollArea.setWidgetResizable(true);
scrollArea.setWidget(widget);
//
this.textEdit = new QTextEdit();
textEdit.setPlaceholderText("Write a message...");
textEdit.textChanged.connect(this, "handleComposerType()");
QPushButton sendButton = new QPushButton("Send");
QHBoxLayout composerLayout = new QHBoxLayout();
composerLayout.addWidget(textEdit);
composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
QWidget composerWidget = new QWidget();
composerWidget.setLayout(composerLayout);
handleComposerType(); // TODO make it resizable and fix it
//
QVBoxLayout mainLayout = new QVBoxLayout();
mainLayout.addWidget(scrollArea, 1);
mainLayout.addWidget(composerWidget);
setLayout(mainLayout);
for (int i=0; i<10; i++) {
QPushButton pushButton = new QPushButton("hello");
layout.addWidget(pushButton);
}
}
public void handleComposerType() {
int height = (int) textEdit.document().size().height();
textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
}
}

View file

@ -1,8 +1,8 @@
package eu.m724.chaqt; package eu.m724.chaqt;
import eu.m724.chaqt.chatwindow.ChatWindowWidget;
import eu.m724.chatapi.chat.Chat; import eu.m724.chatapi.chat.Chat;
import io.qt.widgets.QApplication; import io.qt.widgets.QApplication;
import io.qt.widgets.QMessageBox;
public class Main { public class Main {
@ -11,9 +11,10 @@ public class Main {
Chat chat = new Chat(); Chat chat = new Chat();
ChatWindowWidget widget = new ChatWindowWidget(chat); ChatWindowWidget widget = new ChatWindowWidget();
widget.show(); widget.show();
QApplication.exec(); QApplication.exec();
QApplication.shutdown();
} }
} }

View file

@ -0,0 +1,22 @@
package eu.m724.chaqt;
import eu.m724.chatapi.chat.Chat;
import eu.m724.chatapi.chat.ChatMessage;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
public class ObservableChat extends Chat {
private Set<Consumer<ChatMessage>> watchers = new HashSet<>();
@Override
public void addMessage(ChatMessage message) {
super.addMessage(message);
watchers.forEach(c -> c.accept(message));
}
public void watch(Consumer<ChatMessage> callable) {
this.watchers.add(callable);
}
}

View file

@ -0,0 +1,30 @@
package eu.m724.chaqt;
import io.qt.core.QMargins;
import io.qt.gui.QPalette;
import io.qt.widgets.QLabel;
import io.qt.widgets.QVBoxLayout;
import io.qt.widgets.QWidget;
// TODO I think we could put Qtextdocument here
public class QMessage extends QWidget {
private final QLabel label;
public QMessage(boolean assistant, String text) {
this.label = new QLabel(text);
label.setWordWrap(true);
QVBoxLayout layout = new QVBoxLayout();
layout.addWidget(label);
if (assistant) {
layout.setContentsMargins(5,0,0, 0);
}
setLayout(layout);
}
public void addText(String text) {
label.setText(label.text() + text);
}
}

View file

@ -0,0 +1,69 @@
package eu.m724.chaqt.chatwindow;
import eu.m724.chatapi.chat.Chat;
import eu.m724.chatapi.chat.ChatEvent;
import eu.m724.chatapi.chat.ChatMessage;
import eu.m724.chatapi.example.ExampleSource;
import eu.m724.chatapi.source.ChatResponse;
import eu.m724.chatapi.source.ChatSource;
import io.qt.core.QObject.Signal1;
import io.qt.core.QThread;
public class ChatWindowViewModel {
private final ChatWindowWidget widget;
private final Signal1<ChatMessage> chatMessageSignal;
private final Signal1<ChatEvent> chatEventSignal;
private Chat chat = new Chat();
private ChatSource source = new ExampleSource(); // TODO
public ChatWindowViewModel(ChatWindowWidget widget, Signal1<ChatEvent> chatEventSignal, Signal1<ChatMessage> chatMessageSignal) {
this.widget = widget;
this.chatEventSignal = chatEventSignal;
this.chatMessageSignal = chatMessageSignal;
}
void send(String text) {
ChatMessage promptMessage = new ChatMessage(false, text);
chatMessageSignal.emit(promptMessage);
chat.addMessage(new ChatMessage(false, text));
ChatResponse response = source.ask(chat);
ChatMessage message = response.message();
chatMessageSignal.emit(message);
message.addEventConsumer(chatEventSignal::emit, true);
/*QThread thread = new QThread() {
@Override
protected void run() {
ChatEvent token;
do {
try {
token = response.eventQueue().take();
} catch (InterruptedException e) {
throw new RuntimeException(e); // TODO
}
chatEventSignal.emit(token);
if (token.finishReason() != null) {
chatMessageSignal.emit(response.message().join()); // TODO ???
}
/*if (!"error".equals(token.finishReason())) {
if (token.text() != null) {
chatEventSignal.emit(token.text());
}
} else {
System.out.print("Error: " + token.error().toString());
}/
} while (token.finishReason() == null);
// QApplication.invokeLater(() -> resultField.setText(result));
}
};
thread.start();*/
//
}
}

View file

@ -0,0 +1,133 @@
package eu.m724.chaqt.chatwindow;
import eu.m724.chaqt.QMessage;
import eu.m724.chatapi.chat.ChatEvent;
import eu.m724.chatapi.chat.ChatMessage;
import io.qt.core.Qt;
import io.qt.widgets.*;
public class ChatWindowWidget extends QWidget {
private ChatWindowViewModel viewModel;
private QVBoxLayout layout;
private QTextEdit textEdit;
// TODO are both really needed
private final Signal1<ChatMessage> chatMessageSignal = new Signal1<>();
private final Signal1<ChatEvent> chatEventSignal = new Signal1<>();
private QMessage latestMessage;
public ChatWindowWidget() {
this.viewModel = new ChatWindowViewModel(this, chatEventSignal, chatMessageSignal);
//
chatEventSignal.connect(this::handleChatEvent);
chatMessageSignal.connect(this::handleChatMessage);
//
this.layout = new QVBoxLayout();
layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
QWidget widget = new QWidget();
widget.setLayout(layout);
QScrollArea scrollArea = new QScrollArea();
scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff);
scrollArea.setWidgetResizable(true);
scrollArea.setWidget(widget);
//
this.textEdit = new QTextEdit();
textEdit.setPlaceholderText("Write a message...");
textEdit.textChanged.connect(this, "handleComposerType()");
QPushButton sendButton = new QPushButton("Send");
sendButton.clicked.connect(this::onSend);
QHBoxLayout composerLayout = new QHBoxLayout();
composerLayout.addWidget(textEdit);
composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
QWidget composerWidget = new QWidget();
composerWidget.setLayout(composerLayout);
handleComposerType(); // TODO make it resizable and fix it
//
QVBoxLayout mainLayout = new QVBoxLayout();
mainLayout.addWidget(scrollArea, 1);
mainLayout.addWidget(composerWidget);
setLayout(mainLayout);
/*for (int i=0; i<10; i++) {
QPushButton pushButton = new QPushButton("hello");
layout.addWidget(pushButton);
}*/
textEdit.setFocus();
}
public void handleComposerType() {
int height = (int) textEdit.document().size().height();
textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
}
public void onSend() {
String prompt = textEdit.getPlainText();
if (!prompt.isBlank()) {
textEdit.setText("");
viewModel.send(prompt);
textEdit.setEnabled(false);
textEdit.setPlaceholderText("Asking...");
}
}
private void handleChatMessage(ChatMessage message) {
System.out.printf("Widget received a message. Is response: %b\n", message.response());
QMessage qMessage = new QMessage(false, message.content());
layout.addWidget(qMessage);
// if the message is by assistant, disable composer.
// I don't know if this is good, but it works
if (message.response()) {
textEdit.setEnabled(false);
textEdit.setPlaceholderText("Thinking...");
}
}
private void handleChatEvent(ChatEvent chatEvent) {
String token = chatEvent.text();
// usually when it's the first token
if (latestMessage == null) {
latestMessage = new QMessage(true, "");
layout.addWidget(latestMessage);
textEdit.setPlaceholderText("Responding...");
}
if (token != null) {
latestMessage.addText(token);
}
// when finished
if (chatEvent.finishReason() != null) {
// to make sure we won't be streaming to the same message
latestMessage = null;
// enable composer
textEdit.setEnabled(true);
textEdit.setPlaceholderText("Write a message...");
textEdit.setFocus();
}
System.out.printf("Event: %s %s\n", chatEvent.finishReason(), chatEvent.text());
} // TODO that's messy so do something about that
}