diff --git a/pom.xml b/pom.xml
index c0c8178..5c81464 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,6 +6,8 @@
6.10.0
3.0.3
+ 24
+ 24
diff --git a/src/main/java/de/derpandaa/aitestsuit/ChatGPTApiWrapper.java b/src/main/java/de/derpandaa/aitestsuit/ChatGPTApiWrapper.java
deleted file mode 100644
index e0fee73..0000000
--- a/src/main/java/de/derpandaa/aitestsuit/ChatGPTApiWrapper.java
+++ /dev/null
@@ -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 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 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;
- });
- }
-}
diff --git a/src/main/java/de/derpandaa/aitestsuit/AiWindow.java b/src/main/java/de/derpandaa/plinfa/AiWindow.java
similarity index 76%
rename from src/main/java/de/derpandaa/aitestsuit/AiWindow.java
rename to src/main/java/de/derpandaa/plinfa/AiWindow.java
index 20fc50c..807ac0c 100644
--- a/src/main/java/de/derpandaa/aitestsuit/AiWindow.java
+++ b/src/main/java/de/derpandaa/plinfa/AiWindow.java
@@ -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();
}
diff --git a/src/main/java/de/derpandaa/plinfa/ChatListWidget.java b/src/main/java/de/derpandaa/plinfa/ChatListWidget.java
new file mode 100644
index 0000000..e7aceff
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/ChatListWidget.java
@@ -0,0 +1,40 @@
+package de.derpandaa.plinfa;
+
+import java.io.File;
+import java.util.function.Consumer;
+
+import de.derpandaa.plinfa.dto.ChatDto;
+import de.derpandaa.plinfa.projects.GenericProject;
+import io.qt.core.Qt;
+import io.qt.widgets.QTreeWidget;
+import io.qt.widgets.QTreeWidgetItem;
+
+public class ChatListWidget extends QTreeWidget {
+
+ public ChatListWidget(Consumer clickedFunction) {
+ super();
+ setHeaderHidden(true);
+ GenericProject root = new GenericProject(new File(ChatDto.PROJECT_DIR));
+ loadProject(root, null);
+ itemClicked.connect((item, column) -> {
+ ChatDto chat = (ChatDto) item.data(0, Qt.ItemDataRole.UserRole);
+ if (chat != null) {
+ clickedFunction.accept(chat);
+ }
+ });
+ }
+
+ private void loadProject(GenericProject project, QTreeWidgetItem parent) {
+ for (GenericProject subProject : project.getSubProjects()) {
+ QTreeWidgetItem folderItem = parent == null ? new QTreeWidgetItem(this) : new QTreeWidgetItem(parent);
+ folderItem.setText(0, subProject.getName());
+ loadProject(subProject, folderItem);
+ }
+
+ for (ChatDto chat : project.getChats()) {
+ QTreeWidgetItem chatItem = parent == null ? new QTreeWidgetItem(this) : new QTreeWidgetItem(parent);
+ chatItem.setText(0, chat.getName());
+ chatItem.setData(0, Qt.ItemDataRole.UserRole, chat);
+ }
+ }
+}
diff --git a/src/main/java/de/derpandaa/aitestsuit/ChatWindow.java b/src/main/java/de/derpandaa/plinfa/ChatWindow.java
similarity index 58%
rename from src/main/java/de/derpandaa/aitestsuit/ChatWindow.java
rename to src/main/java/de/derpandaa/plinfa/ChatWindow.java
index 2d70f49..c2c1920 100644
--- a/src/main/java/de/derpandaa/aitestsuit/ChatWindow.java
+++ b/src/main/java/de/derpandaa/plinfa/ChatWindow.java
@@ -1,27 +1,24 @@
-package de.derpandaa.aitestsuit;
+package de.derpandaa.plinfa;
-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 de.derpandaa.plinfa.aiprovider.AiProviderManager;
+import de.derpandaa.plinfa.aiprovider.Model;
+import de.derpandaa.plinfa.dto.ChatDto;
+import de.derpandaa.plinfa.dto.MessageDto;
+import de.derpandaa.plinfa.dto.MessageDto.Role;
+import de.derpandaa.plinfa.dto.OpenAiDto;
import io.qt.core.QMimeData;
import io.qt.core.Qt.AlignmentFlag;
-import io.qt.core.Qt.ConnectionType;
+import io.qt.core.Qt.TextFormat;
import io.qt.gui.QColor;
import io.qt.gui.QKeySequence;
import io.qt.gui.QPalette;
+import io.qt.gui.QPalette.ColorRole;
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;
@@ -30,36 +27,43 @@ import io.qt.widgets.QTextEdit;
import io.qt.widgets.QVBoxLayout;
import io.qt.widgets.QWidget;
-public class ChatWindow extends QMainWindow {
+public class ChatWindow extends QWidget {
private QBoxLayout layout;
private QVBoxLayout chatLayout;
private QWidget chatWidget;
private QTextEdit textInput;
- private OpenAiDto openAiDto;
+ private ChatDto chatDto;
private QComboBox modelComboBox;
+ private QScrollArea scrollArea;
public ChatWindow() {
+ this(new ChatDto());
+ }
+
+ public ChatWindow(ChatDto chatDto) {
super();
- openAiDto = new OpenAiDto(Model.gpt5_2, new MessageDto(Role.system, "Antworte kurz und präzise"));
+ QPalette palette = getPalette();
+ palette.setColor(ColorRole.Window, PlinfaWindow.colorBackgroundAlt);
+ setPalette(palette);
+ this.chatDto = chatDto;
- QWidget widget = new QWidget();
- layout = new QVBoxLayout();
- layout.setContentsMargins(10, 10, 10, 10);
+ layout = new QVBoxLayout(this);
+ layout.setContentsMargins(10, 0, 10, 10);
- QPushButton button = new QPushButton("Import Chat");
- layout.addWidget(button);
+ QLabel chatNameLabel = new QLabel(chatDto.getName());
+ layout.addWidget(chatNameLabel);
modelComboBox = new QComboBox();
- for (OpenAiDto.Model m : OpenAiDto.Model.values()) {
+ for (Model m : AiProviderManager.getAiProviderManager().getActiveModels()) {
modelComboBox.addItem(m.toString(), m);
}
layout.addWidget(modelComboBox);
- QScrollArea scrollArea = new QScrollArea();
+ scrollArea = new QScrollArea();
scrollArea.setWidgetResizable(true);
- chatWidget = new QWidget();
+ chatWidget = new QWidget(this);
chatLayout = new QVBoxLayout(chatWidget);
chatLayout.setAlignment(AlignmentFlag.AlignTop);
chatLayout.setSpacing(5);
@@ -85,18 +89,21 @@ public class ChatWindow extends QMainWindow {
inputLayout.addWidget(sendButton);
layout.addLayout(inputLayout);
- QShortcut shortcut = new QShortcut(new QKeySequence("Ctrl+Return"), widget);
+ QShortcut shortcut = new QShortcut(new QKeySequence("Ctrl+Return"), this);
shortcut.activated.connect(() -> sendMessage());
- widget.setLayout(layout);
- setCentralWidget(widget);
resize(600, 500);
- show();
+ importChat(chatDto);
}
- private void importChat(OpenAiDto openAiDto) {
- for (MessageDto messageDto : openAiDto.getMessages()) {
-// chatLayout.addWidget(createMessage(messageDto.getContent(), true));
+ private void importChat(ChatDto chatDto) {
+ if (chatDto.getOpenAiDto().getMessages() == null) {
+ return;
+ }
+ for (MessageDto messageDto : chatDto.getOpenAiDto().getMessages()) {
+ if (messageDto.getRole() != Role.system) {
+ chatLayout.addWidget(createMessage(messageDto.getContent(), messageDto.getRole()));
+ }
}
}
@@ -105,13 +112,14 @@ public class ChatWindow extends QMainWindow {
frame.setFrameShape(QFrame.Shape.Box);
frame.setFrameShadow(QFrame.Shadow.Raised);
-// frame.setMaximumWidth(Integer.MAX_VALUE);
- frame.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred);
+ frame.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Maximum);
+ frame.setMaximumWidth((int) (chatWidget.getMaximumWidth() + 0.7));
QVBoxLayout frameLayout = new QVBoxLayout(frame);
frameLayout.setContentsMargins(10, 10, 10, 10);
QLabel textLabel = new QLabel(message);
+ textLabel.setTextFormat(TextFormat.MarkdownText);
textLabel.setWordWrap(true);
frameLayout.addWidget(textLabel);
@@ -152,12 +160,17 @@ public class ChatWindow extends QMainWindow {
if (message.isEmpty()) {
return;
}
- openAiDto.setModel((Model) modelComboBox.currentData());
+
+ OpenAiDto openAiDto = chatDto.getOpenAiDto();
+
+ openAiDto.setModel((de.derpandaa.plinfa.aiprovider.Model) modelComboBox.currentData());
MessageDto messageDto = new MessageDto(Role.user, message);
openAiDto.addMessage(messageDto);
- chatLayout.addWidget(createMessage(message, Role.user));
+ QWidget userWidget = createMessage(message, Role.user);
+ chatLayout.addWidget(userWidget);
+ scrollArea.ensureWidgetVisible(userWidget);
textInput.clear();
chatWidget.adjustSize();
@@ -166,41 +179,24 @@ public class ChatWindow extends QMainWindow {
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);
+// .runAsync(openAiDto, answer -> {
+// QMetaObject.invokeMethod(this, () -> {
+// chatLayout.removeWidget(loadingWidget);
+// loadingWidget.disposeLater();
+//
+// openAiDto.addMessage(new MessageDto(Role.assistant, answer));
+// QWidget assistantWidget = createMessage(answer, Role.assistant);
+// chatLayout.addWidget(assistantWidget);
+// scrollArea.ensureWidgetVisible(assistantWidget);
+// chatWidget.adjustSize();
+// chatDto.writeToFile();
+//
+// }, ConnectionType.QueuedConnection);
+// });
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
- ChatGPTApiWrapper.API_KEY = properties.getProperty("OPENAI_KEY");
}
}
\ No newline at end of file
diff --git a/src/main/java/de/derpandaa/plinfa/PlinfaMenu.java b/src/main/java/de/derpandaa/plinfa/PlinfaMenu.java
new file mode 100644
index 0000000..7269832
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/PlinfaMenu.java
@@ -0,0 +1,23 @@
+package de.derpandaa.plinfa;
+
+import java.util.function.Consumer;
+
+import io.qt.gui.QIcon;
+import io.qt.widgets.QToolBar;
+import io.qt.widgets.QToolButton;
+
+public class PlinfaMenu extends QToolBar {
+
+ public PlinfaMenu() {
+ setMaximumHeight(50);
+ }
+
+ public void addButton(QIcon icon, Consumer function, String toolTip) {
+ QToolButton button = new QToolButton();
+ button.setIcon(icon);
+ button.clicked.connect(bool -> function.accept(bool));
+ button.setToolTip(toolTip);
+ addWidget(button);
+ button.setDown(true);
+ }
+}
diff --git a/src/main/java/de/derpandaa/plinfa/PlinfaWindow.java b/src/main/java/de/derpandaa/plinfa/PlinfaWindow.java
new file mode 100644
index 0000000..9ec15aa
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/PlinfaWindow.java
@@ -0,0 +1,104 @@
+package de.derpandaa.plinfa;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import de.derpandaa.plinfa.aiprovider.OpenAiProvider;
+import de.derpandaa.plinfa.dto.ChatDto;
+import io.qt.gui.QColor;
+import io.qt.gui.QIcon;
+import io.qt.gui.QPalette;
+import io.qt.gui.QPalette.ColorRole;
+import io.qt.widgets.QApplication;
+import io.qt.widgets.QHBoxLayout;
+import io.qt.widgets.QMainWindow;
+import io.qt.widgets.QVBoxLayout;
+import io.qt.widgets.QWidget;
+
+public class PlinfaWindow extends QMainWindow {
+
+ public final static QColor colorBackground = new QColor(40, 42, 46);
+ public final static QColor colorBackgroundAlt = new QColor(55, 59, 65);
+ public final static QColor colorForeground = new QColor(208, 208, 208);
+
+ private ChatWindow chatWindow;
+ private QVBoxLayout vLayout;
+ private QHBoxLayout hLayout;
+ private PlinfaMenu menuBar;
+ private Map chatMap;
+
+ public PlinfaWindow() {
+ super();
+ QPalette palette = getPalette();
+ palette.setColor(ColorRole.Window, colorBackground);
+ palette.setColor(ColorRole.Base, colorBackgroundAlt);
+ palette.setColor(ColorRole.Button, colorBackgroundAlt);
+ palette.setColor(ColorRole.Text, colorForeground);
+ palette.setColor(ColorRole.ButtonText, colorForeground);
+ palette.setColor(ColorRole.PlaceholderText, colorForeground);
+ setPalette(palette);
+
+ QWidget mainWidget = new QWidget();
+ QWidget widget = new QWidget();
+ vLayout = new QVBoxLayout(mainWidget);
+ menuBar = new PlinfaMenu();
+ menuBar.addButton(new QIcon("/usr/share/pandaaPop/img/brightness.svg"), null, "Test");
+ vLayout.addWidget(menuBar);
+
+
+ hLayout = new QHBoxLayout(widget);
+ vLayout.addWidget(widget);
+
+ ChatListWidget chatListWidget = new ChatListWidget(chat -> loadChat(chat));
+ hLayout.addWidget(chatListWidget, 1);
+ chatWindow = new ChatWindow();
+ hLayout.addWidget(chatWindow, 3);
+
+ chatMap = new HashMap();
+
+ setCentralWidget(mainWidget);
+ show();
+ }
+
+ public void loadChat(ChatDto chatDto) {
+ hLayout.removeWidget(chatWindow);
+ chatWindow.hide();
+ ChatWindow cachedChatWindow = chatMap.get(chatDto);
+ if (cachedChatWindow == null) {
+ cachedChatWindow = new ChatWindow(chatDto);
+ chatMap.put(chatDto, cachedChatWindow);
+ }
+ chatWindow = cachedChatWindow;
+ hLayout.addWidget(chatWindow, 3);
+ chatWindow.show();
+ }
+
+ public static void main(String[] args) {
+ QApplication.initialize(args);
+ QApplication.setStyle("Fusion");
+ initProperties();
+ PlinfaWindow window = new PlinfaWindow();
+
+ 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);
+ }
+ OpenAiProvider.API_KEY = properties.getProperty("OPENAI_KEY");
+ ChatDto.PROJECT_DIR = properties.getProperty("PROJECT_DIR");
+ }
+}
diff --git a/src/main/java/de/derpandaa/plinfa/aiprovider/AbstractAiProvider.java b/src/main/java/de/derpandaa/plinfa/aiprovider/AbstractAiProvider.java
new file mode 100644
index 0000000..cc77bf2
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/aiprovider/AbstractAiProvider.java
@@ -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 de.derpandaa.plinfa.dto.AiRequest;
+import de.derpandaa.plinfa.dto.AiResponseDto;
+import de.derpandaa.plinfa.dto.OpenAiDto;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.ObjectMapper;
+
+public abstract class AbstractAiProvider {
+ public enum Provider {
+ OPEN_AI
+ }
+
+ 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 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 response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ return parseResponse(response.body());
+ } catch (IOException | InterruptedException e) {
+ // NOOP
+ }
+ return null;
+
+ }
+
+ public void runAsync(OpenAiDto dto, Consumer onResult) {
+ String jsonBody = mapper.writeValueAsString(dto);
+
+ HttpRequest request = buildRequest(jsonBody);
+
+ HttpClient client = HttpClient.newHttpClient();
+ client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body)
+ .thenAccept(body -> {
+ JsonNode root = mapper.readTree(body);
+ String content = root.path("choices").path(0).path("message").path("content").asString();
+ onResult.accept(content);
+ }).exceptionally(ex -> {
+ onResult.accept("");
+ return null;
+ });
+ }
+}
diff --git a/src/main/java/de/derpandaa/plinfa/aiprovider/AiProviderManager.java b/src/main/java/de/derpandaa/plinfa/aiprovider/AiProviderManager.java
new file mode 100644
index 0000000..e34b1a2
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/aiprovider/AiProviderManager.java
@@ -0,0 +1,34 @@
+package de.derpandaa.plinfa.aiprovider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AiProviderManager {
+ private final List providers;
+ private static AiProviderManager aiProviderManager;
+ private List models = new ArrayList();
+
+ public AiProviderManager() {
+ providers = new ArrayList();
+ if (OpenAiProvider.API_KEY != null) {
+ providers.add(new OpenAiProvider());
+ }
+ }
+
+ public static AiProviderManager getAiProviderManager() {
+ if (aiProviderManager == null) {
+ aiProviderManager = new AiProviderManager();
+ }
+ return aiProviderManager;
+ }
+
+ public List getActiveModels() {
+ if (models.isEmpty()) {
+ for (AbstractAiProvider aiProvider : providers) {
+ models.addAll(aiProvider.getModels());
+ }
+ }
+ return models;
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/plinfa/aiprovider/Model.java b/src/main/java/de/derpandaa/plinfa/aiprovider/Model.java
new file mode 100644
index 0000000..091f020
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/aiprovider/Model.java
@@ -0,0 +1,11 @@
+package de.derpandaa.plinfa.aiprovider;
+
+import de.derpandaa.plinfa.aiprovider.AbstractAiProvider.Provider;
+
+public record Model(String modelName, Provider provider) {
+ @Override
+ public String toString() {
+ return modelName;
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/plinfa/aiprovider/OpenAiProvider.java b/src/main/java/de/derpandaa/plinfa/aiprovider/OpenAiProvider.java
new file mode 100644
index 0000000..bae0abc
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/aiprovider/OpenAiProvider.java
@@ -0,0 +1,36 @@
+package de.derpandaa.plinfa.aiprovider;
+
+import java.net.URI;
+import java.net.http.HttpRequest;
+import java.util.List;
+
+import de.derpandaa.plinfa.dto.AiResponseDto;
+import de.derpandaa.plinfa.dto.OpenAiResponseDto;
+
+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 getModels() {
+
+ return List.of(new Model("gpt-5-nano", Provider.OPEN_AI));
+ }
+
+ @Override
+ public AiResponseDto parseResponse(String body) {
+ return new OpenAiResponseDto(body);
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/plinfa/dto/AiRequest.java b/src/main/java/de/derpandaa/plinfa/dto/AiRequest.java
new file mode 100644
index 0000000..4671d43
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/dto/AiRequest.java
@@ -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 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 getMessages() {
+ return messages;
+ }
+
+ public void addMessages(MessageDto message) {
+ messages.add(message);
+ }
+}
diff --git a/src/main/java/de/derpandaa/plinfa/dto/AiResponseDto.java b/src/main/java/de/derpandaa/plinfa/dto/AiResponseDto.java
new file mode 100644
index 0000000..13b6ad6
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/dto/AiResponseDto.java
@@ -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;
+ }
+}
diff --git a/src/main/java/de/derpandaa/plinfa/dto/AnthropicResponseDto.java b/src/main/java/de/derpandaa/plinfa/dto/AnthropicResponseDto.java
new file mode 100644
index 0000000..07afb6b
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/dto/AnthropicResponseDto.java
@@ -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();
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/plinfa/dto/ChatDto.java b/src/main/java/de/derpandaa/plinfa/dto/ChatDto.java
new file mode 100644
index 0000000..576d364
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/dto/ChatDto.java
@@ -0,0 +1,60 @@
+package de.derpandaa.plinfa.dto;
+
+import java.io.File;
+
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.ObjectMapper;
+
+public class ChatDto {
+
+ public static String PROJECT_DIR;
+
+ private String name;
+ private OpenAiDto openAiDto;
+
+ public ChatDto() {
+ this.openAiDto = new OpenAiDto();
+ }
+
+ public ChatDto(File f) {
+ loadFromFile(f);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public OpenAiDto getOpenAiDto() {
+ return openAiDto;
+ }
+
+ public void setOpenAiDto(OpenAiDto openAiDto) {
+ this.openAiDto = openAiDto;
+ }
+
+ private void loadFromFile(File f) {
+ ObjectMapper mapper = new ObjectMapper();
+
+ setName(f.getName().substring(0, f.getName().indexOf(".json")));
+ try {
+ openAiDto = mapper.readValue(f, OpenAiDto.class);
+ } catch (JacksonException e) {
+ e.printStackTrace();
+ System.out.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(), openAiDto);
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/aitestsuit/MessageDto.java b/src/main/java/de/derpandaa/plinfa/dto/MessageDto.java
similarity index 65%
rename from src/main/java/de/derpandaa/aitestsuit/MessageDto.java
rename to src/main/java/de/derpandaa/plinfa/dto/MessageDto.java
index 7ba996a..fab661a 100644
--- a/src/main/java/de/derpandaa/aitestsuit/MessageDto.java
+++ b/src/main/java/de/derpandaa/plinfa/dto/MessageDto.java
@@ -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;
+ }
+
}
diff --git a/src/main/java/de/derpandaa/aitestsuit/OpenAiDto.java b/src/main/java/de/derpandaa/plinfa/dto/OpenAiDto.java
similarity index 59%
rename from src/main/java/de/derpandaa/aitestsuit/OpenAiDto.java
rename to src/main/java/de/derpandaa/plinfa/dto/OpenAiDto.java
index 2f4f14a..3b4a979 100644
--- a/src/main/java/de/derpandaa/aitestsuit/OpenAiDto.java
+++ b/src/main/java/de/derpandaa/plinfa/dto/OpenAiDto.java
@@ -1,29 +1,17 @@
-package de.derpandaa.aitestsuit;
+package de.derpandaa.plinfa.dto;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import de.derpandaa.plinfa.aiprovider.Model;
+
+@JsonPropertyOrder({ "model", "messages" })
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;
@@ -31,6 +19,22 @@ public class OpenAiDto {
@JsonProperty("messages")
private List messages;
+ @JsonProperty("max_tokens")
+ private int maxTokens;
+
+ public OpenAiDto() {
+ this(8192);
+ }
+
+ public OpenAiDto(int maxTokens) {
+ this.maxTokens = maxTokens;
+ }
+
+ public OpenAiDto(Model model) {
+ this.model = model;
+ this.messages = new ArrayList();
+ }
+
public OpenAiDto(Model model, MessageDto... messages) {
this.model = model;
this.messages = new ArrayList();
diff --git a/src/main/java/de/derpandaa/plinfa/dto/OpenAiResponseDto.java b/src/main/java/de/derpandaa/plinfa/dto/OpenAiResponseDto.java
new file mode 100644
index 0000000..f6a2ad5
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/dto/OpenAiResponseDto.java
@@ -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();
+ }
+
+}
diff --git a/src/main/java/de/derpandaa/plinfa/projects/GenericProject.java b/src/main/java/de/derpandaa/plinfa/projects/GenericProject.java
new file mode 100644
index 0000000..22b1a8c
--- /dev/null
+++ b/src/main/java/de/derpandaa/plinfa/projects/GenericProject.java
@@ -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 subProjects;
+ private List 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 getSubProjects() {
+ return subProjects;
+ }
+
+ public List getChats() {
+ return chats;
+ }
+}