From 981631474983dbadc19cd28fcedd13a8cf35cb87 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:16:21 +0200 Subject: [PATCH] ai use message ids to point to data i thought it would give cleaner updates... --- .../quickshell/modules/sidebarLeft/AiChat.qml | 13 +++-- .config/quickshell/services/Ai.qml | 47 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index b8df45bb7..37fa876ed 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -18,7 +18,6 @@ Item { id: root property var panelWindow property var inputField: messageInputField - readonly property var messages: Ai.messages property string commandPrefix: "/" property string faviconDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/favicons`) @@ -31,7 +30,7 @@ Item { onFocusChanged: (focus) => { if (focus) { - messageInputField.forceActiveFocus() + root.inputField.forceActiveFocus() } } @@ -194,18 +193,22 @@ int main(int argc, char* argv[]) { } model: ScriptModel { - values: root.messages + values: Ai.messageIDs } delegate: AiMessage { + required property var modelData + required property int index messageIndex: index - messageData: modelData + messageData: { + Ai.messageByID[modelData] + } messageInputField: root.inputField faviconDownloadPath: root.faviconDownloadPath } } Item { // Placeholder when list is empty - opacity: root.messages.length === 0 ? 1 : 0 + opacity: Ai.messageIDs.length === 0 ? 1 : 0 visible: opacity > 0 anchors.fill: parent diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index 4f1ac52e9..949fa19cf 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -16,9 +16,20 @@ Singleton { property Component aiMessageComponent: AiMessageData {} property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? "" property var messages: [] + property var messageIDs: [] + property var messageByID: ({}) readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {} readonly property var apiKeysLoaded: KeyringStorage.loaded + function idForMessage(message) { + // Generate a unique ID using timestamp and random value + return Date.now().toString(36) + Math.random().toString(36).substr(2, 8); + } + + function safeModelName(modelName) { + return modelName.replace(/:/g, "_").replace(/\./g, "_") + } + // Model properties: // - name: Name of the model // - icon: Icon name of the model @@ -115,7 +126,8 @@ Singleton { const dataJson = JSON.parse(data); root.modelList = [...root.modelList, ...dataJson]; dataJson.forEach(model => { - root.models[model] = { + const safeModelName = root.safeModelName(model); + root.models[safeModelName] = { "name": guessModelName(model), "icon": guessModelLogo(model), "description": StringUtils.format(qsTr("Local Ollama model | {0}"), model), @@ -142,13 +154,17 @@ Singleton { "thinking": false, "done": true, }); - root.messages = [...root.messages, aiMessage]; + const id = idForMessage(aiMessage); + root.messageIDs = [...root.messageIDs, id]; + root.messageByID[id] = aiMessage; } function removeMessage(index) { - if (index < 0 || index >= messages.length) return; - root.messages.splice(index, 1); - root.messages = [...root.messages]; + if (index < 0 || index >= messageIDs.length) return; + const id = root.messageIDs[index]; + root.messageIDs.splice(index, 1); + root.messageIDs = [...root.messageIDs]; + delete root.messageByID[id]; } function addApiKeyAdvice(model) { @@ -160,6 +176,7 @@ Singleton { } function getModel() { + console.log("MODEL:", currentModelId); return models[currentModelId]; } @@ -213,7 +230,8 @@ Singleton { } function clearMessages() { - messages = []; + root.messageIDs = []; + root.messageByID = ({}); } Process { @@ -275,7 +293,8 @@ Singleton { /* Build endpoint, request data */ const endpoint = (apiFormat === "gemini") ? buildGeminiEndpoint(model) : buildOpenAIEndpoint(model); - const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, root.messages) : buildOpenAIRequestData(model, root.messages); + const messageArray = root.messageIDs.map(id => root.messageByID[id]); + const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, messageArray) : buildOpenAIRequestData(model, messageArray); let requestHeaders = { "Content-Type": "application/json", @@ -289,7 +308,9 @@ Singleton { "thinking": true, "done": false, }); - root.messages = [...root.messages, requester.message]; + const id = idForMessage(requester.message); + root.messageIDs = [...root.messageIDs, id]; + root.messageByID[id] = requester.message; /* Build header string for curl */ let headerString = Object.entries(requestHeaders) @@ -447,15 +468,7 @@ Singleton { function sendUserMessage(message) { if (message.length === 0) return; - - const userMessage = aiMessageComponent.createObject(root, { - "role": "user", - "content": message, - "thinking": false, - "done": true, - }); - root.messages = [...root.messages, userMessage]; - + root.addMessage(message, "user"); requester.makeRequest(); }