pragma Singleton pragma ComponentBehavior: Bound import "root:/modules/common" import Quickshell; import Quickshell.Io; import Qt.labs.platform import QtQuick; Singleton { id: root property Component aiMessageComponent: AiMessageData {} property var messages: [] property var modelList: ["ollama-llama-3.2", "gemini-2.0-flash"] property var models: { // TODO: Auto-detect installed ollama models "interface": { "name": "System", }, "ollama-llama-3.2": { "name": "Ollama - Llama 3.2", "icon": "ollama-symbolic", "description": "Local Ollama model - Llama 3.2", "endpoint": "http://localhost:11434/api/chat", "model": "llama3.2", }, "gemini-2.0-flash": { "name": "Gemini 2.0 Flash", "icon": "gemini-symbolic", "description": "Online Gemini 2.0 Flash", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", "model": "gemini-2.0-flash", "messageMapFunc": function (message) { return { "role": message.role, "parts": [{text: message.content}], } }, }, } property var currentModel: "ollama-llama-3.2" function addMessage(message, role) { if (message.length === 0) return; const aiMessage = aiMessageComponent.createObject(root, { "role": role, "content": message, "thinking": false, "done": true, }); root.messages = [...root.messages, aiMessage]; } function setModel(model) { if (!model) model = "" model = model.toLowerCase() if (modelList.indexOf(model) !== -1) { currentModel = model root.addMessage("Model set to " + models[model].name, "interface") } else { root.addMessage(qsTr("Invalid model. Supported: \n- ") + modelList.join("\n- "), "interface") } } function clearMessages() { messages = []; } Process { id: requester property var baseCommand: ["curl", "--no-buffer"] property var message function makeRequest() { const model = models[currentModel]; let endpoint = model.endpoint; // Build request data using OpenAI's format. If the model has a custom requestDataBuilder, use that instead. let data = model.requestDataBuilder ? model.requestDataBuilder(root.messages.filter(message => (message.role != "interface"))) : { "model": model.model, "messages": root.messages.filter(message => (message.role != "interface")).map(message => { return { // Remove unecessary properties "role": message.role, "content": message.content, } }), } let requestHeaders = { "Content-Type": "application/json", // "Authorization": model.endpoint.startsWith("http") ? "Bearer " + model.apiKey : "", } requester.message = root.aiMessageComponent.createObject(root, { "role": "assistant", "model": currentModel, "content": "", "thinking": true, "done": false, }); root.messages = [...root.messages, requester.message]; requester.command = baseCommand.concat([endpoint, "-d", JSON.stringify(data)]); console.log("Request command: ", requester.command.join(" ")); requester.running = true } stdout: SplitParser { onRead: data => { // console.log("Received data: ", data); if (data.length === 0) return; const dataJson = JSON.parse(data); if (requester.message.thinking) requester.message.thinking = false; requester.message.content += dataJson.message.content if (dataJson.done) requester.message.done = true; } } } 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]; requester.makeRequest(); } }