Done some basic things
This commit is contained in:
29
pom.xml
29
pom.xml
@@ -1,13 +1,16 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.derpandaa</groupId>
|
||||
<artifactId>plinfa</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<properties>
|
||||
<qtjambi.version>6.10.0</qtjambi.version>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.derpandaa</groupId>
|
||||
<artifactId>plinfa</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<properties>
|
||||
<qtjambi.version>6.11.0</qtjambi.version>
|
||||
<jackson.version>3.0.3</jackson.version>
|
||||
<maven.compiler.release>25</maven.compiler.release>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.qtjambi</groupId>
|
||||
<artifactId>qtjambi</artifactId>
|
||||
@@ -18,6 +21,16 @@
|
||||
<artifactId>qtjambi-native-linux-x64</artifactId>
|
||||
<version>${qtjambi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.formdev</groupId>
|
||||
<artifactId>flatlaf</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.formdev</groupId>
|
||||
<artifactId>flatlaf-extras</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tools.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package de.derpandaa.aitestsuit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import de.derpandaa.aitestsuit.MessageDto.Role;
|
||||
import de.derpandaa.aitestsuit.OpenAiDto.Model;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
/*
|
||||
* I dont know how to name it
|
||||
*/
|
||||
public class ChatGPTApiWrapper {
|
||||
|
||||
public static String API_KEY;
|
||||
|
||||
public static String run(String systemPrompt, String userPrompt) {
|
||||
OpenAiDto dto = new OpenAiDto(Model.gpt5nano, new MessageDto(Role.system, systemPrompt),
|
||||
new MessageDto(Role.user, userPrompt));
|
||||
return run(dto);
|
||||
}
|
||||
|
||||
public static String run(OpenAiDto dto) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String jsonBody = mapper.writeValueAsString(dto);
|
||||
|
||||
System.out.println(jsonBody);
|
||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://api.openai.com/v1/chat/completions"))
|
||||
.header("Content-Type", "application/json").header("Authorization", "Bearer " + API_KEY)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody)).build();
|
||||
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
try {
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
JsonNode root = mapper.readTree(response.body());
|
||||
return root.path("choices").path(0).path("message").path("content").asString();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
// NOOP
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void runAsync(OpenAiDto dto, Consumer<String> onResult) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String jsonBody = mapper.writeValueAsString(dto);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://api.openai.com/v1/chat/completions"))
|
||||
.header("Content-Type", "application/json").header("Authorization", "Bearer " + API_KEY)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody)).build();
|
||||
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body)
|
||||
.thenAccept(body -> {
|
||||
try {
|
||||
JsonNode root = mapper.readTree(body);
|
||||
String content = root.path("choices").path(0).path("message").path("content").asString();
|
||||
onResult.accept(content);
|
||||
} catch (Exception e) {
|
||||
onResult.accept("");
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
onResult.accept("");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package de.derpandaa.aitestsuit;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import de.derpandaa.aitestsuit.MessageDto.Role;
|
||||
import de.derpandaa.aitestsuit.OpenAiDto.Model;
|
||||
import io.qt.core.QMetaObject;
|
||||
import io.qt.core.QMimeData;
|
||||
import io.qt.core.Qt.AlignmentFlag;
|
||||
import io.qt.core.Qt.ConnectionType;
|
||||
import io.qt.gui.QColor;
|
||||
import io.qt.gui.QKeySequence;
|
||||
import io.qt.gui.QPalette;
|
||||
import io.qt.gui.QShortcut;
|
||||
import io.qt.widgets.QApplication;
|
||||
import io.qt.widgets.QBoxLayout;
|
||||
import io.qt.widgets.QComboBox;
|
||||
import io.qt.widgets.QFrame;
|
||||
import io.qt.widgets.QHBoxLayout;
|
||||
import io.qt.widgets.QLabel;
|
||||
import io.qt.widgets.QMainWindow;
|
||||
import io.qt.widgets.QPushButton;
|
||||
import io.qt.widgets.QScrollArea;
|
||||
import io.qt.widgets.QSizePolicy;
|
||||
import io.qt.widgets.QSizePolicy.Policy;
|
||||
import io.qt.widgets.QTextEdit;
|
||||
import io.qt.widgets.QVBoxLayout;
|
||||
import io.qt.widgets.QWidget;
|
||||
|
||||
public class ChatWindow extends QMainWindow {
|
||||
|
||||
private QBoxLayout layout;
|
||||
private QVBoxLayout chatLayout;
|
||||
private QWidget chatWidget;
|
||||
private QTextEdit textInput;
|
||||
private OpenAiDto openAiDto;
|
||||
private QComboBox modelComboBox;
|
||||
|
||||
public ChatWindow() {
|
||||
super();
|
||||
openAiDto = new OpenAiDto(Model.gpt5_2, new MessageDto(Role.system, "Antworte kurz und präzise"));
|
||||
|
||||
QWidget widget = new QWidget();
|
||||
layout = new QVBoxLayout();
|
||||
layout.setContentsMargins(10, 10, 10, 10);
|
||||
|
||||
QPushButton button = new QPushButton("Import Chat");
|
||||
layout.addWidget(button);
|
||||
|
||||
modelComboBox = new QComboBox();
|
||||
for (OpenAiDto.Model m : OpenAiDto.Model.values()) {
|
||||
modelComboBox.addItem(m.toString(), m);
|
||||
}
|
||||
layout.addWidget(modelComboBox);
|
||||
|
||||
QScrollArea scrollArea = new QScrollArea();
|
||||
scrollArea.setWidgetResizable(true);
|
||||
|
||||
chatWidget = new QWidget();
|
||||
chatLayout = new QVBoxLayout(chatWidget);
|
||||
chatLayout.setAlignment(AlignmentFlag.AlignTop);
|
||||
chatLayout.setSpacing(5);
|
||||
|
||||
scrollArea.setWidget(chatWidget);
|
||||
layout.addWidget(scrollArea);
|
||||
|
||||
QHBoxLayout inputLayout = new QHBoxLayout();
|
||||
textInput = new QTextEdit() {
|
||||
@Override
|
||||
protected void insertFromMimeData(QMimeData source) {
|
||||
insertPlainText(source.text());
|
||||
}
|
||||
};
|
||||
textInput.setMaximumHeight(100);
|
||||
textInput.setPlaceholderText("Nachricht eingeben...");
|
||||
|
||||
QPushButton sendButton = new QPushButton("Senden");
|
||||
sendButton.clicked.connect(() -> sendMessage());
|
||||
sendButton.setSizePolicy(new QSizePolicy(Policy.Minimum, Policy.Minimum));
|
||||
|
||||
inputLayout.addWidget(textInput);
|
||||
inputLayout.addWidget(sendButton);
|
||||
layout.addLayout(inputLayout);
|
||||
|
||||
QShortcut shortcut = new QShortcut(new QKeySequence("Ctrl+Return"), widget);
|
||||
shortcut.activated.connect(() -> sendMessage());
|
||||
|
||||
widget.setLayout(layout);
|
||||
setCentralWidget(widget);
|
||||
resize(600, 500);
|
||||
show();
|
||||
}
|
||||
|
||||
private void importChat(OpenAiDto openAiDto) {
|
||||
for (MessageDto messageDto : openAiDto.getMessages()) {
|
||||
// chatLayout.addWidget(createMessage(messageDto.getContent(), true));
|
||||
}
|
||||
}
|
||||
|
||||
private QWidget createMessage(String message, Role role) {
|
||||
QFrame frame = new QFrame();
|
||||
frame.setFrameShape(QFrame.Shape.Box);
|
||||
frame.setFrameShadow(QFrame.Shadow.Raised);
|
||||
|
||||
// frame.setMaximumWidth(Integer.MAX_VALUE);
|
||||
frame.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred);
|
||||
|
||||
QVBoxLayout frameLayout = new QVBoxLayout(frame);
|
||||
|
||||
frameLayout.setContentsMargins(10, 10, 10, 10);
|
||||
QLabel textLabel = new QLabel(message);
|
||||
textLabel.setWordWrap(true);
|
||||
frameLayout.addWidget(textLabel);
|
||||
|
||||
QWidget container = new QWidget();
|
||||
QHBoxLayout boxLayout = new QHBoxLayout(container);
|
||||
boxLayout.setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QPalette palette = frame.palette();
|
||||
QColor messageColor;
|
||||
|
||||
switch (role) {
|
||||
case user:
|
||||
messageColor = new QColor(209, 114, 246);
|
||||
boxLayout.addStretch(30);
|
||||
boxLayout.addWidget(frame, 70);
|
||||
break;
|
||||
case assistant:
|
||||
messageColor = new QColor(106, 91, 117);
|
||||
boxLayout.addWidget(frame, 70);
|
||||
boxLayout.addStretch(30);
|
||||
break;
|
||||
default:
|
||||
messageColor = new QColor(100, 100, 100);
|
||||
boxLayout.addStretch(15);
|
||||
boxLayout.addWidget(frame, 70);
|
||||
boxLayout.addStretch(15);
|
||||
break;
|
||||
}
|
||||
palette.setColor(QPalette.ColorRole.Window, messageColor);
|
||||
frame.setAutoFillBackground(true);
|
||||
frame.setPalette(palette);
|
||||
return container;
|
||||
}
|
||||
|
||||
private void sendMessage() {
|
||||
String message = textInput.getPlainText().trim();
|
||||
|
||||
if (message.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
openAiDto.setModel((Model) modelComboBox.currentData());
|
||||
|
||||
MessageDto messageDto = new MessageDto(Role.user, message);
|
||||
openAiDto.addMessage(messageDto);
|
||||
|
||||
chatLayout.addWidget(createMessage(message, Role.user));
|
||||
|
||||
textInput.clear();
|
||||
chatWidget.adjustSize();
|
||||
|
||||
QWidget loadingWidget = createMessage("Loading...", Role.assistant);
|
||||
chatLayout.addWidget(loadingWidget);
|
||||
chatWidget.adjustSize();
|
||||
|
||||
ChatGPTApiWrapper.runAsync(openAiDto, answer -> {
|
||||
QMetaObject.invokeMethod(this, () -> {
|
||||
chatLayout.removeWidget(loadingWidget);
|
||||
loadingWidget.disposeLater();
|
||||
|
||||
openAiDto.addMessage(new MessageDto(Role.assistant, answer));
|
||||
chatLayout.addWidget(createMessage(answer, Role.assistant));
|
||||
chatWidget.adjustSize();
|
||||
}, ConnectionType.QueuedConnection);
|
||||
// update();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
QApplication.initialize(args);
|
||||
QApplication.setStyle("Fusion");
|
||||
initProperties();
|
||||
ChatWindow window = new ChatWindow();
|
||||
|
||||
QApplication.exec();
|
||||
QApplication.shutdown();
|
||||
}
|
||||
|
||||
private static void initProperties() {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties.load(new FileInputStream("ai.properties"));
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("[ERROR] No Properties File");
|
||||
System.exit(1);
|
||||
} catch (IOException e) {
|
||||
System.out.println("[ERROR] Cannot Access File");
|
||||
System.exit(1);
|
||||
}
|
||||
ChatGPTApiWrapper.API_KEY = properties.getProperty("OPENAI_KEY");
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package de.derpandaa.aitestsuit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class OpenAiDto {
|
||||
|
||||
public enum Model {
|
||||
gpt5nano("gpt-5-nano"),
|
||||
gpt5mini("gpt-5-mini"),
|
||||
gpt5_2("gpt-5.2");
|
||||
|
||||
final String value;
|
||||
|
||||
Model(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("model")
|
||||
private Model model;
|
||||
|
||||
@JsonProperty("messages")
|
||||
private List<MessageDto> messages;
|
||||
|
||||
public OpenAiDto(Model model, MessageDto... messages) {
|
||||
this.model = model;
|
||||
this.messages = new ArrayList<MessageDto>();
|
||||
this.messages.addAll(Arrays.asList(messages));
|
||||
}
|
||||
|
||||
public void addMessage(MessageDto messageDto) {
|
||||
messages.add(messageDto);
|
||||
}
|
||||
|
||||
public List<MessageDto> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void setModel(Model model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.derpandaa.aitestsuit;
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import io.qt.widgets.QApplication;
|
||||
import io.qt.widgets.QBoxLayout;
|
||||
@@ -9,12 +9,9 @@ import io.qt.widgets.QTextEdit;
|
||||
import io.qt.widgets.QVBoxLayout;
|
||||
import io.qt.widgets.QWidget;
|
||||
|
||||
public class AiWindow {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
QApplication.initialize(args);
|
||||
QMainWindow window = new QMainWindow();
|
||||
public class AiWindow extends QMainWindow {
|
||||
public AiWindow() {
|
||||
super();
|
||||
QWidget widget = new QWidget();
|
||||
QBoxLayout layout = new QVBoxLayout();
|
||||
|
||||
@@ -39,16 +36,23 @@ public class AiWindow {
|
||||
|
||||
QPushButton buttonRun = new QPushButton("Run");
|
||||
buttonRun.clicked.connect(() -> {
|
||||
String result = ChatGPTApiWrapper.run(editSystemPrompt.getPlainText(), editUserPrompt.getPlainText());
|
||||
editAiAnser.setText(result);
|
||||
// String result = AiProviderManager.getAiProviderManager().run(editSystemPrompt.getPlainText(),
|
||||
// editUserPrompt.getPlainText());
|
||||
// editAiAnser.setText(result);
|
||||
});
|
||||
|
||||
layout.addWidget(buttonRun);
|
||||
|
||||
widget.setLayout(layout);
|
||||
|
||||
window.setCentralWidget(widget);
|
||||
window.show();
|
||||
setCentralWidget(widget);
|
||||
show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
QApplication.initialize(args);
|
||||
AiWindow window = new AiWindow();
|
||||
|
||||
QApplication.exec();
|
||||
QApplication.shutdown();
|
||||
}
|
||||
67
src/main/java/de/derpandaa/plinfa/ChatListWidget.java
Normal file
67
src/main/java/de/derpandaa/plinfa/ChatListWidget.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.io.File;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
||||
import de.derpandaa.plinfa.dto.ChatDto;
|
||||
import de.derpandaa.plinfa.projects.GenericProject;
|
||||
|
||||
public class ChatListWidget extends JTree {
|
||||
|
||||
private static final long serialVersionUID = 559712098635547536L;
|
||||
private final Consumer<ChatDto> clickHandler;
|
||||
|
||||
public ChatListWidget(Consumer<ChatDto> clickedFunction) {
|
||||
super();
|
||||
this.clickHandler = clickedFunction;
|
||||
setRootVisible(true);
|
||||
GenericProject rootProject = new GenericProject(new File(ChatDto.PROJECT_DIR));
|
||||
DefaultMutableTreeNode rootNode = buildTree(rootProject);
|
||||
setModel(new DefaultTreeModel(rootNode));
|
||||
addTreeSelectionListener(e -> {
|
||||
if (getLastSelectedPathComponent() == null)
|
||||
return;
|
||||
|
||||
Object node = ((DefaultMutableTreeNode) getLastSelectedPathComponent()).getUserObject();
|
||||
|
||||
if (node instanceof ChatDto chat) {
|
||||
clickHandler.accept(chat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DefaultMutableTreeNode buildTree(GenericProject project) {
|
||||
DefaultMutableTreeNode node = new DefaultMutableTreeNode(project.getName());
|
||||
for (GenericProject sub : project.getSubProjects()) {
|
||||
node.add(buildTree(sub));
|
||||
}
|
||||
for (ChatDto chat : project.getChats()) {
|
||||
DefaultMutableTreeNode chatNode = new DefaultMutableTreeNode(chat);
|
||||
node.add(chatNode);
|
||||
System.out.println(chat.getName());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public void addChat(ChatDto chat) {
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
|
||||
DefaultMutableTreeNode chatNode = new DefaultMutableTreeNode(chat);
|
||||
root.add(chatNode);
|
||||
((DefaultTreeModel) getModel()).reload(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processFocusEvent(FocusEvent e) {
|
||||
if (e.FOCUS_LOST == e.getID()) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
super.processFocusEvent(e);
|
||||
}
|
||||
|
||||
}
|
||||
191
src/main/java/de/derpandaa/plinfa/ChatWindow.java
Normal file
191
src/main/java/de/derpandaa/plinfa/ChatWindow.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import de.derpandaa.plinfa.aiprovider.AiProviderManager;
|
||||
import de.derpandaa.plinfa.aiprovider.Model;
|
||||
import de.derpandaa.plinfa.dto.AiRequest;
|
||||
import de.derpandaa.plinfa.dto.ChatDto;
|
||||
import de.derpandaa.plinfa.dto.MessageDto;
|
||||
import de.derpandaa.plinfa.dto.MessageDto.Role;
|
||||
|
||||
public class ChatWindow extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = -1698148372733277109L;
|
||||
private final JPanel messageContainer;
|
||||
private final JScrollPane scrollPane;
|
||||
private final JTextArea input;
|
||||
private JComboBox<Model> modelComboBox;
|
||||
private ChatDto chatDto;
|
||||
|
||||
private final Runnable onFirstSave;
|
||||
|
||||
public ChatWindow(Runnable onFirstSave) {
|
||||
this.onFirstSave = onFirstSave;
|
||||
setLayout(new BorderLayout());
|
||||
modelComboBox = new JComboBox<>();
|
||||
|
||||
for (Model m : AiProviderManager.getAiProviderManager().getActiveModels()) {
|
||||
modelComboBox.addItem(m);
|
||||
}
|
||||
|
||||
add(modelComboBox, BorderLayout.NORTH);
|
||||
|
||||
// ===== Message Area =====
|
||||
messageContainer = new JPanel();
|
||||
messageContainer.setLayout(new BoxLayout(messageContainer, BoxLayout.Y_AXIS));
|
||||
messageContainer.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
messageContainer.setOpaque(false);
|
||||
|
||||
JPanel northWrapper = new JPanel(new BorderLayout());
|
||||
northWrapper.add(messageContainer, BorderLayout.NORTH);
|
||||
scrollPane = new JScrollPane(northWrapper);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(PlinfaWindow.BG_ALT, 2));
|
||||
scrollPane.getVerticalScrollBar().setUnitIncrement(12);
|
||||
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// ===== Input Area =====
|
||||
JPanel inputPanel = new JPanel(new BorderLayout());
|
||||
input = new JTextArea(3, 20);
|
||||
input.setLineWrap(true);
|
||||
input.setWrapStyleWord(true);
|
||||
|
||||
JButton send = new JButton("Send");
|
||||
|
||||
send.addActionListener(e -> sendMessage());
|
||||
|
||||
inputPanel.add(new JScrollPane(input), BorderLayout.CENTER);
|
||||
inputPanel.add(send, BorderLayout.EAST);
|
||||
|
||||
add(inputPanel, BorderLayout.SOUTH);
|
||||
|
||||
getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK),
|
||||
"save");
|
||||
|
||||
getActionMap().put("save", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
saveChat();
|
||||
}
|
||||
});
|
||||
|
||||
getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK),
|
||||
"send");
|
||||
|
||||
getActionMap().put("send", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
chatDto = new ChatDto();
|
||||
}
|
||||
|
||||
public MessageBubble addUserMessage(String text) {
|
||||
return addMessage(new MessageDto(Role.user, text));
|
||||
}
|
||||
|
||||
public MessageBubble addAssistantMessage(String text) {
|
||||
return addMessage(new MessageDto(Role.assistant, text));
|
||||
}
|
||||
|
||||
private MessageBubble addMessage(MessageDto messageDto) {
|
||||
MessageBubble bubble = new MessageBubble(messageDto);
|
||||
|
||||
messageContainer.add(bubble);
|
||||
messageContainer.revalidate();
|
||||
messageContainer.repaint();
|
||||
|
||||
scrollToBottom();
|
||||
return bubble;
|
||||
}
|
||||
|
||||
private void scrollToBottom() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
messageContainer.revalidate();
|
||||
messageContainer.repaint();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JScrollBar bar = scrollPane.getVerticalScrollBar();
|
||||
bar.setValue(bar.getMaximum());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage() {
|
||||
|
||||
String text = input.getText().trim();
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
addUserMessage(text);
|
||||
input.setText("");
|
||||
|
||||
AiRequest request = new AiRequest((Model) modelComboBox.getSelectedItem());
|
||||
request.addMessages(new MessageDto(Role.user, text));
|
||||
|
||||
request.setSystemPrompt("Du bist ein einfacher Assistent der kurz antwortet");
|
||||
|
||||
MessageBubble bubble = addAssistantMessage("Loading...");
|
||||
|
||||
AiProviderManager.getAiProviderManager().runAsync(request, answer -> {
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
addAssistantMessage(answer);
|
||||
chatDto.addMessage(new MessageDto(Role.user, text));
|
||||
chatDto.addMessage(new MessageDto(Role.assistant, answer));
|
||||
messageContainer.remove(bubble);
|
||||
messageContainer.revalidate();
|
||||
messageContainer.repaint();
|
||||
chatDto.writeToFile();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void loadChat(ChatDto chatDto) {
|
||||
this.chatDto = chatDto == null ? new ChatDto() : chatDto;
|
||||
|
||||
chatDto.getMessages().forEach(m -> {
|
||||
if (m.getRole() == Role.user) {
|
||||
addUserMessage(m.getContent());
|
||||
} else if (m.getRole() == Role.assistant) {
|
||||
addAssistantMessage(m.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveChat() {
|
||||
if (chatDto.getName() == null) {
|
||||
String name = JOptionPane.showInputDialog(this, "Chat Name:", "Speichern", JOptionPane.PLAIN_MESSAGE);
|
||||
if (name == null || name.isBlank())
|
||||
return;
|
||||
chatDto.setName(name);
|
||||
chatDto.writeToFile();
|
||||
onFirstSave.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void focusInput() {
|
||||
input.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
59
src/main/java/de/derpandaa/plinfa/MessageBubble.java
Normal file
59
src/main/java/de/derpandaa/plinfa/MessageBubble.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import de.derpandaa.plinfa.dto.MessageDto;
|
||||
|
||||
public class MessageBubble extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = -6278277156331505629L;
|
||||
|
||||
public MessageBubble(MessageDto messageDto) {
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(6, 10, 6, 10));
|
||||
|
||||
JTextArea label = new JTextArea(messageDto.getContent());
|
||||
label.setLineWrap(true);
|
||||
label.setWrapStyleWord(true);
|
||||
label.setEditable(false);
|
||||
label.setOpaque(false);
|
||||
|
||||
JPanel bubble = new JPanel(new BorderLayout());
|
||||
bubble.setBorder(new EmptyBorder(10, 12, 10, 12));
|
||||
bubble.add(label, BorderLayout.CENTER);
|
||||
|
||||
Color bg;
|
||||
FlowLayout wrapperLayout;
|
||||
|
||||
switch (messageDto.getRole()) {
|
||||
case user -> {
|
||||
bg = new Color(120, 80, 200);
|
||||
wrapperLayout = new FlowLayout(FlowLayout.RIGHT);
|
||||
}
|
||||
case assistant -> {
|
||||
bg = new Color(60, 60, 60);
|
||||
wrapperLayout = new FlowLayout(FlowLayout.LEFT);
|
||||
}
|
||||
default -> {
|
||||
bg = new Color(90, 90, 90);
|
||||
wrapperLayout = new FlowLayout(FlowLayout.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
bubble.setBackground(bg);
|
||||
bubble.setOpaque(true);
|
||||
|
||||
JPanel wrapper = new JPanel(wrapperLayout);
|
||||
wrapper.setOpaque(false);
|
||||
wrapper.add(bubble);
|
||||
|
||||
setOpaque(false);
|
||||
add(wrapper, BorderLayout.CENTER);
|
||||
}
|
||||
}
|
||||
47
src/main/java/de/derpandaa/plinfa/PlinfaMenu.java
Normal file
47
src/main/java/de/derpandaa/plinfa/PlinfaMenu.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
|
||||
public class PlinfaMenu extends JToolBar {
|
||||
|
||||
public PlinfaMenu() {
|
||||
super();
|
||||
|
||||
setFloatable(false);
|
||||
setPreferredSize(new Dimension(0, 50));
|
||||
}
|
||||
|
||||
public void addToggleButton(Icon icon, Consumer<Boolean> function, String toolTip) {
|
||||
|
||||
JToggleButton button = new JToggleButton();
|
||||
|
||||
button.setIcon(icon);
|
||||
button.setToolTipText(toolTip);
|
||||
button.setSelected(true);
|
||||
|
||||
button.addActionListener(e -> {
|
||||
if (function != null) {
|
||||
function.accept(button.isSelected());
|
||||
}
|
||||
});
|
||||
|
||||
add(button);
|
||||
}
|
||||
|
||||
public void addButton(Icon icon, Runnable function, String toolTip) {
|
||||
JButton button = new JButton();
|
||||
button.setIcon(icon);
|
||||
button.setToolTipText(toolTip);
|
||||
button.addActionListener(e -> {
|
||||
if (function != null)
|
||||
function.run();
|
||||
});
|
||||
add(button);
|
||||
}
|
||||
}
|
||||
141
src/main/java/de/derpandaa/plinfa/PlinfaWindow.java
Normal file
141
src/main/java/de/derpandaa/plinfa/PlinfaWindow.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package de.derpandaa.plinfa;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.formdev.flatlaf.extras.FlatSVGIcon;
|
||||
import com.formdev.flatlaf.extras.FlatSVGIcon.ColorFilter;
|
||||
|
||||
import de.derpandaa.plinfa.aiprovider.OpenAiProvider;
|
||||
import de.derpandaa.plinfa.dto.ChatDto;
|
||||
|
||||
public class PlinfaWindow extends JFrame {
|
||||
|
||||
private static final long serialVersionUID = 1340010605617450301L;
|
||||
public static final Color BG = new Color(40, 42, 46);
|
||||
public static final Color BG_ALT = new Color(55, 59, 65);
|
||||
public static final Color FG = new Color(208, 208, 208);
|
||||
public static final Color ACCENT = new Color(0x6A5B75);
|
||||
|
||||
private final Map<ChatDto, ChatWindow> chatCache = new HashMap<>();
|
||||
|
||||
private ChatWindow currentChatWindow;
|
||||
|
||||
private JPanel centerContainer;
|
||||
private ChatListWidget chatListWidget;
|
||||
|
||||
public PlinfaWindow() {
|
||||
super("Plinfa");
|
||||
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(1200, 800);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
applyTheme();
|
||||
initUI();
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
setLayout(new BorderLayout());
|
||||
PlinfaMenu menu = new PlinfaMenu();
|
||||
FlatSVGIcon addIcon = new FlatSVGIcon("img/add-icon.svg");
|
||||
addIcon.setColorFilter(new ColorFilter((color) -> PlinfaWindow.ACCENT));
|
||||
menu.addButton(addIcon, () -> {
|
||||
loadChat(new ChatDto());
|
||||
}, "Neuer Chat");
|
||||
add(menu, BorderLayout.NORTH);
|
||||
|
||||
|
||||
|
||||
chatListWidget = new ChatListWidget(this::loadChat);
|
||||
JScrollPane leftScroll = new JScrollPane(chatListWidget);
|
||||
leftScroll.setFocusable(false);
|
||||
leftScroll.setPreferredSize(new Dimension(260, 0));
|
||||
|
||||
centerContainer = new JPanel(new BorderLayout());
|
||||
|
||||
loadChat(new ChatDto());
|
||||
centerContainer.add(currentChatWindow, BorderLayout.CENTER);
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll, centerContainer);
|
||||
|
||||
splitPane.setDividerLocation(260);
|
||||
splitPane.setResizeWeight(0.0);
|
||||
splitPane.setBorder(new javax.swing.border.EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
add(splitPane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private void applyTheme() {
|
||||
UIManager.put("Panel.background", BG);
|
||||
UIManager.put("OptionPane.background", BG);
|
||||
UIManager.put("Label.foreground", FG);
|
||||
UIManager.put("TextField.background", BG_ALT);
|
||||
|
||||
}
|
||||
|
||||
public void loadChat(ChatDto chatDto) {
|
||||
centerContainer.removeAll();
|
||||
|
||||
ChatWindow window = chatCache.get(chatDto);
|
||||
|
||||
if (window == null) {
|
||||
window = new ChatWindow(() -> {
|
||||
chatListWidget.addChat(chatDto);
|
||||
});
|
||||
window.loadChat(chatDto);
|
||||
chatCache.put(chatDto, window);
|
||||
}
|
||||
|
||||
currentChatWindow = window;
|
||||
centerContainer.add(currentChatWindow, BorderLayout.CENTER);
|
||||
centerContainer.revalidate();
|
||||
centerContainer.repaint();
|
||||
currentChatWindow.focusInput();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
initProperties();
|
||||
try {
|
||||
FlatDarkLaf.setup();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
UIManager.put("Component.focusColor", new Color(40, 42, 46));
|
||||
SwingUtilities.invokeLater(PlinfaWindow::new);
|
||||
}
|
||||
|
||||
private static void initProperties() {
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(new FileInputStream("ai.properties"));
|
||||
} catch (IOException e) {
|
||||
System.out.println("[ERROR] Cannot load config");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
OpenAiProvider.API_KEY = props.getProperty("OPENAI_KEY");
|
||||
ChatDto.PROJECT_DIR = props.getProperty("PROJECT_DIR");
|
||||
}
|
||||
|
||||
public static void noOutline(JComponent c) {
|
||||
c.putClientProperty("focusWidth", 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package de.derpandaa.plinfa.aiprovider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import de.derpandaa.plinfa.dto.AiRequest;
|
||||
import de.derpandaa.plinfa.dto.AiResponseDto;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
public abstract class AbstractAiProvider {
|
||||
|
||||
protected static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public static String OpenAiKey;
|
||||
|
||||
|
||||
// GPT5_NANO("gpt-5-nano", Provider.OPENAI), GPT5_MINI("gpt-5-mini", Provider.OPENAI),
|
||||
// GPT5_2("gpt-5.2", Provider.OPENAI), CLAUDE_OPUS("claude-opus-4-6", Provider.ANTHROPIC),
|
||||
// CLAUDE_SONNET("claude-sonnet-4-6", Provider.ANTHROPIC), CLAUDE_HAIKU("claude-haiku-4-5", Provider.ANTHROPIC);
|
||||
|
||||
public abstract List<Model> getModels();
|
||||
|
||||
public abstract HttpRequest buildRequest(String jsonBody);
|
||||
|
||||
public abstract AiResponseDto parseResponse(String body);
|
||||
|
||||
public AiResponseDto run(AiRequest request) {
|
||||
String jsonBody = mapper.writeValueAsString(request);
|
||||
|
||||
System.out.println(jsonBody);
|
||||
HttpRequest httpRequest = buildRequest(jsonBody);
|
||||
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
try {
|
||||
HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
|
||||
return parseResponse(response.body());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
// NOOP
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public void runAsync(AiRequest request, Consumer<String> onResult) {
|
||||
try {
|
||||
String jsonBody = buildRequestBody(request);
|
||||
HttpRequest httpRequest = buildRequest(jsonBody);
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
|
||||
client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body)
|
||||
.thenAccept(body -> {
|
||||
AiResponseDto dto = parseResponse(body);
|
||||
onResult.accept(dto != null ? dto.getResponse() : "");
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(null, e.getMessage());
|
||||
onResult.accept("");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract String buildRequestBody(AiRequest request) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package de.derpandaa.plinfa.aiprovider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import de.derpandaa.plinfa.dto.AiRequest;
|
||||
|
||||
public class AiProviderManager {
|
||||
private final List<AbstractAiProvider> providers;
|
||||
private static AiProviderManager aiProviderManager;
|
||||
private List<Model> models = new ArrayList<Model>();
|
||||
|
||||
public AiProviderManager() {
|
||||
providers = new ArrayList<AbstractAiProvider>();
|
||||
if (OpenAiProvider.API_KEY != null) {
|
||||
providers.add(new OpenAiProvider());
|
||||
}
|
||||
}
|
||||
|
||||
public static AiProviderManager getAiProviderManager() {
|
||||
if (aiProviderManager == null) {
|
||||
aiProviderManager = new AiProviderManager();
|
||||
}
|
||||
return aiProviderManager;
|
||||
}
|
||||
|
||||
public List<Model> getActiveModels() {
|
||||
if (models.isEmpty()) {
|
||||
for (AbstractAiProvider aiProvider : providers) {
|
||||
models.addAll(aiProvider.getModels());
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
|
||||
public AbstractAiProvider getProviderForModel(Model model) {
|
||||
for (AbstractAiProvider aiProvider : providers) {
|
||||
if (model.clazz().equals(aiProvider.getClass())) {
|
||||
return aiProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void runAsync(AiRequest aiRequest, Consumer<String> onResult) {
|
||||
getProviderForModel(aiRequest.getModel()).runAsync(aiRequest, onResult);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/main/java/de/derpandaa/plinfa/aiprovider/Model.java
Normal file
16
src/main/java/de/derpandaa/plinfa/aiprovider/Model.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package de.derpandaa.plinfa.aiprovider;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
public record Model(String modelName, Class<? extends AbstractAiProvider> clazz) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return modelName;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String modelName() {
|
||||
return modelName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.derpandaa.plinfa.aiprovider;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.util.List;
|
||||
|
||||
import de.derpandaa.plinfa.dto.AiRequest;
|
||||
import de.derpandaa.plinfa.dto.AiResponseDto;
|
||||
import de.derpandaa.plinfa.dto.MessageDto;
|
||||
import de.derpandaa.plinfa.dto.OpenAiResponseDto;
|
||||
import tools.jackson.databind.node.ArrayNode;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
public class OpenAiProvider extends AbstractAiProvider {
|
||||
public static String API_KEY;
|
||||
private static int maxTokens;
|
||||
private static OpenAiProvider openAiProvider;
|
||||
|
||||
public OpenAiProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest buildRequest(String jsonBody) {
|
||||
return HttpRequest.newBuilder().uri(URI.create("https://api.openai.com/v1/chat/completions"))
|
||||
.header("Content-Type", "application/json").header("Authorization", "Bearer " + API_KEY)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Model> getModels() {
|
||||
|
||||
return List.of(new Model("gpt-5-nano", this.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiResponseDto parseResponse(String body) {
|
||||
return new OpenAiResponseDto(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildRequestBody(AiRequest request) throws Exception {
|
||||
|
||||
ObjectNode root = mapper.createObjectNode();
|
||||
|
||||
root.put("model", request.getModel().modelName());
|
||||
root.put("max_completion_tokens", request.getMaxTokens());
|
||||
|
||||
if (request.getSystemPrompt() != null) {
|
||||
ArrayNode messages = root.putArray("messages");
|
||||
|
||||
ObjectNode system = mapper.createObjectNode();
|
||||
system.put("role", "system");
|
||||
system.put("content", request.getSystemPrompt());
|
||||
messages.add(system);
|
||||
|
||||
for (MessageDto m : request.getMessages()) {
|
||||
ObjectNode msg = mapper.createObjectNode();
|
||||
msg.put("role", m.getRole().name());
|
||||
msg.put("content", m.getContent());
|
||||
messages.add(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return mapper.writeValueAsString(root);
|
||||
}
|
||||
|
||||
}
|
||||
51
src/main/java/de/derpandaa/plinfa/dto/AiRequest.java
Normal file
51
src/main/java/de/derpandaa/plinfa/dto/AiRequest.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.derpandaa.plinfa.aiprovider.Model;
|
||||
|
||||
public class AiRequest {
|
||||
private Model model;
|
||||
private int maxTokens;
|
||||
private String systemPrompt;
|
||||
private List<MessageDto> messages;
|
||||
|
||||
public AiRequest(Model model) {
|
||||
this.model = model;
|
||||
this.maxTokens = 4096;
|
||||
this.messages = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Model getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(Model model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public int getMaxTokens() {
|
||||
return maxTokens;
|
||||
}
|
||||
|
||||
public void setMaxTokens(int maxTokens) {
|
||||
this.maxTokens = maxTokens;
|
||||
}
|
||||
|
||||
public String getSystemPrompt() {
|
||||
return systemPrompt;
|
||||
}
|
||||
|
||||
public void setSystemPrompt(String systemPrompt) {
|
||||
this.systemPrompt = systemPrompt;
|
||||
}
|
||||
|
||||
public List<MessageDto> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void addMessages(MessageDto message) {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
23
src/main/java/de/derpandaa/plinfa/dto/AiResponseDto.java
Normal file
23
src/main/java/de/derpandaa/plinfa/dto/AiResponseDto.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
public abstract class AiResponseDto {
|
||||
protected String response;
|
||||
protected int tokensUsed;
|
||||
protected ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public AiResponseDto(String jsonBody) {
|
||||
parseBody(jsonBody);
|
||||
}
|
||||
|
||||
protected abstract void parseBody(String jsonBody);
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public int getTokensUsed() {
|
||||
return tokensUsed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
public class AnthropicResponseDto extends AiResponseDto {
|
||||
public AnthropicResponseDto(String jsonBody) {
|
||||
super(jsonBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseBody(String jsonBody) {
|
||||
JsonNode root = mapper.readTree(jsonBody);
|
||||
response = root.path("content").path(0).path("text").asString();
|
||||
tokensUsed = root.path("usage").path("input_tokens").asInt() + root.path("usage").path("output_tokens").asInt();
|
||||
}
|
||||
|
||||
}
|
||||
68
src/main/java/de/derpandaa/plinfa/dto/ChatDto.java
Normal file
68
src/main/java/de/derpandaa/plinfa/dto/ChatDto.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
public class ChatDto {
|
||||
|
||||
public static String PROJECT_DIR;
|
||||
|
||||
private String name;
|
||||
List<MessageDto> messages;
|
||||
|
||||
public ChatDto() {
|
||||
messages = new ArrayList<MessageDto>();
|
||||
}
|
||||
|
||||
public ChatDto(File f) {
|
||||
loadFromFile(f);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<MessageDto> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void addMessage(MessageDto messageDto) {
|
||||
messages.add(messageDto);
|
||||
}
|
||||
|
||||
private void loadFromFile(File f) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
setName(f.getName().substring(0, f.getName().indexOf(".json")));
|
||||
try {
|
||||
messages = mapper.readValue(f, new TypeReference<List<MessageDto>>() {
|
||||
});
|
||||
} catch (JacksonException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Deserialisierung fehlgeschlagen");
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToFile() {
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
File f = new File(String.format("%s/%s.json", PROJECT_DIR, name));
|
||||
mapper.writeValue(f.getAbsoluteFile(), messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package de.derpandaa.aitestsuit;
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
@JsonPropertyOrder({ "role", "content" })
|
||||
public class MessageDto {
|
||||
|
||||
public enum Role {
|
||||
@@ -13,6 +15,9 @@ public class MessageDto {
|
||||
@JsonProperty("content")
|
||||
private String content;
|
||||
|
||||
public MessageDto() {
|
||||
}
|
||||
|
||||
public MessageDto(Role role, String content) {
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
@@ -22,4 +27,8 @@ public class MessageDto {
|
||||
return content;
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
}
|
||||
17
src/main/java/de/derpandaa/plinfa/dto/OpenAiResponseDto.java
Normal file
17
src/main/java/de/derpandaa/plinfa/dto/OpenAiResponseDto.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package de.derpandaa.plinfa.dto;
|
||||
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
public class OpenAiResponseDto extends AiResponseDto {
|
||||
public OpenAiResponseDto(String jsonBody) {
|
||||
super(jsonBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseBody(String jsonBody) {
|
||||
JsonNode root = mapper.readTree(jsonBody);
|
||||
response = root.path("choices").path(0).path("message").path("content").asString();
|
||||
tokensUsed = root.path("usage").path("total_tokens").asInt();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.derpandaa.plinfa.projects;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.derpandaa.plinfa.dto.ChatDto;
|
||||
|
||||
public class GenericProject {
|
||||
private String name;
|
||||
private List<GenericProject> subProjects;
|
||||
private List<ChatDto> chats;
|
||||
|
||||
public GenericProject(File dir) {
|
||||
this.name = dir.getName();
|
||||
this.subProjects = new ArrayList<>();
|
||||
this.chats = new ArrayList<>();
|
||||
load(dir);
|
||||
}
|
||||
|
||||
private void load(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null)
|
||||
return;
|
||||
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
subProjects.add(new GenericProject(f));
|
||||
} else if (f.getName().endsWith(".json")) {
|
||||
chats.add(new ChatDto(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<GenericProject> getSubProjects() {
|
||||
return subProjects;
|
||||
}
|
||||
|
||||
public List<ChatDto> getChats() {
|
||||
return chats;
|
||||
}
|
||||
}
|
||||
3
src/main/resources/img/add-icon.svg
Normal file
3
src/main/resources/img/add-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="none" stroke="#6A5B75" stroke-linecap="round" stroke-width="1.5" d="M15 12h-3m0 0H9m3 0V9m0 3v3m10-3c0 4.714 0 7.071-1.465 8.535C19.072 22 16.714 22 12 22s-7.071 0-8.536-1.465C2 19.072 2 16.714 2 12s0-7.071 1.464-8.536C4.93 2 7.286 2 12 2s7.071 0 8.535 1.464c.974.974 1.3 2.343 1.41 4.536" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
Reference in New Issue
Block a user