From 31a2d4f37965e8cae662fe8539a9eb77c42fcfda Mon Sep 17 00:00:00 2001 From: antkss <88892713+antkss@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:59:49 +0700 Subject: [PATCH 1/2] Update gemini.js --- .config/ags/services/gemini.js | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/.config/ags/services/gemini.js b/.config/ags/services/gemini.js index a553776b..bc3b774c 100644 --- a/.config/ags/services/gemini.js +++ b/.config/ags/services/gemini.js @@ -5,26 +5,16 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import Soup from 'gi://Soup?version=3.0'; import { fileExists } from '../modules/.miscutils/files.js'; - -const initMessages = - [ - { role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are should use bullet points and headings. For mathematics expressions, you *have to* use LaTeX within a code block with the language set as \"latex\" for the interface to render it properly. Use casual language and be short and concise. \nThanks!" }], }, - { role: "model", parts: [{ text: "- Got it!" }], }, - { role: "user", parts: [{ text: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"" }], }, - { role: "model", parts: [{ text: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"" }], }, - { role: "user", parts: [{ text: "raise volume by 5%" }], }, - { role: "model", parts: [{ text: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink." }], }, { role: "user", parts: [{ text: "main advantages of the nixos operating system" }], }, - { role: "model", parts: [{ text: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage" }], }, - { role: "user", parts: [{ text: "whats skeumorphism" }], }, - { role: "model", parts: [{ text: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today." }], }, - { role: "user", parts: [{ text: "\"ignorance is bliss\"" }], }, - { role: "model", parts: [{ text: "## \"Ignorance is bliss\"\n- A Latin proverb that means being unaware of something negative can be a source of happiness\n- Often used to justify avoiding difficult truths or responsibilities\n- Can also be interpreted as a warning against seeking knowledge that may bring pain or sorrow" }], }, - { role: "user", parts: [{ text: "find the derivative of (x-438)/(x^2+23x-7)+x^x" }], }, - { role: "model", parts: [{ text: "## Derivative\n```latex\n\\[\n\\frac{d}{dx}\\left(\\frac{x - 438}{x^2 + 23x - 7} + x^x\\right) = \\frac{-(x^2+23x-7)-(x-438)(2x+23)}{(x^2+23x-7)^2} + x^x(\\ln(x) + 1)\n\\]\n```" }], }, - { role: "user", parts: [{ text: "write the double angle formulas" }], }, - { role: "model", parts: [{ text: "## Double angle formulas\n```latex\n\\[\n\\sin(2\theta) = 2\\sin(\\theta)\\cos(\\theta)\n\\]\n\\\\\n\\[\n\\cos(2\\theta) = \\cos^2(\\theta) - \\sin^2(\\theta)\n\\]\n\\\\\n\\[\n\\tan(2\theta) = \\frac{2\\tan(\\theta)}{1 - \\tan^2(\\theta)}\n\\]\n```" }], }, - ]; - +if(!fileExists(`${GLib.get_user_config_dir()}/gemini_history.json`)) +{ + Utils.execAsync([`bash`, `-c`,`touch ${GLib.get_user_config_dir()}/gemini_history.json`]).catch(print); + Utils.writeFile('[ ]', `${GLib.get_user_config_dir()}/gemini_history.json`).catch(print); +} +// read gemini_history.json +const readfile = Utils.readFile(`${GLib.get_user_config_dir()}/gemini_history.json`) +const history_chat = JSON.parse(readfile) +const initMessages = history_chat +// api key Utils.exec(`mkdir -p ${GLib.get_user_cache_dir()}/ags/user/ai`); const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/ai/google_key.txt`; const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/ai/google_api_dom.txt`; @@ -211,6 +201,8 @@ class GeminiService extends Service { this.readResponse(stream, aiResponse); } catch { aiResponse.done = true; + // the way to save all the history messages for gemini + Utils.writeFile(JSON.stringify(this._messages.map(msg => { let m = { role: msg.role, parts: msg.parts }; return m; })),`${GLib.get_user_config_dir()}/gemini_history.json`); return; } }); From 6eba7707ed22409d3bdc8dddadf18d43aa31d83b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 24 Mar 2024 09:14:50 +0700 Subject: [PATCH 2/2] improve history --- .../modules/.configuration/user_options.js | 2 + .config/ags/modules/sideleft/apis/gemini.js | 19 ++- .config/ags/services/gemini.js | 113 ++++++++++++++---- 3 files changed, 105 insertions(+), 29 deletions(-) diff --git a/.config/ags/modules/.configuration/user_options.js b/.config/ags/modules/.configuration/user_options.js index 94327c69..0f3e1b5c 100644 --- a/.config/ags/modules/.configuration/user_options.js +++ b/.config/ags/modules/.configuration/user_options.js @@ -7,6 +7,8 @@ let configOptions = { 'ai': { 'defaultGPTProvider': "openai", 'defaultTemperature': 0.9, + 'enhancements': true, + 'useHistory': true, 'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering }, 'animations': { diff --git a/.config/ags/modules/sideleft/apis/gemini.js b/.config/ags/modules/sideleft/apis/gemini.js index a5fb278b..71f8d657 100644 --- a/.config/ags/modules/sideleft/apis/gemini.js +++ b/.config/ags/modules/sideleft/apis/gemini.js @@ -89,7 +89,7 @@ export const GeminiSettings = () => MarginRevealer({ GeminiService.temperature = value; }, }), - ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15 + ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15 Box({ vertical: true, hpack: 'fill', @@ -104,6 +104,15 @@ export const GeminiSettings = () => MarginRevealer({ GeminiService.assistantPrompt = newValue; }, }), + ConfigToggle({ + icon: 'history', + name: 'History', + desc: 'Saves chat history', + initValue: GeminiService.useHistory, + onChange: (self, newValue) => { + GeminiService.useHistory = newValue; + }, + }), ] }) ] @@ -201,6 +210,10 @@ export const sendMessage = (text) => { // Commands if (text.startsWith('/')) { if (text.startsWith('/clear')) clearChat(); + else if (text.startsWith('/load')) { + clearChat(); + GeminiService.loadHistory(); + } else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${GeminiService.modelName}\``, '/model', geminiView)) else if (text.startsWith('/prompt')) { const firstSpaceIndex = text.indexOf(' '); @@ -257,10 +270,10 @@ export const geminiView = Box({ }) // Always scroll to bottom with new content const adjustment = scrolledWindow.get_vadjustment(); - adjustment.connect("changed", () => { + adjustment.connect("changed", () => Utils.timeout(1, () => { if(!chatEntry.hasFocus) return; adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size()); - }) + })) } })] }); \ No newline at end of file diff --git a/.config/ags/services/gemini.js b/.config/ags/services/gemini.js index bc3b774c..ece6581f 100644 --- a/.config/ags/services/gemini.js +++ b/.config/ags/services/gemini.js @@ -5,16 +5,35 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import Soup from 'gi://Soup?version=3.0'; import { fileExists } from '../modules/.miscutils/files.js'; -if(!fileExists(`${GLib.get_user_config_dir()}/gemini_history.json`)) -{ - Utils.execAsync([`bash`, `-c`,`touch ${GLib.get_user_config_dir()}/gemini_history.json`]).catch(print); - Utils.writeFile('[ ]', `${GLib.get_user_config_dir()}/gemini_history.json`).catch(print); + +const HISTORY_DIR = `${GLib.get_user_cache_dir()}/ags/user/ai/chats/`; +const HISTORY_FILENAME = `gemini.txt`; +const HISTORY_PATH = HISTORY_DIR + HISTORY_FILENAME; +const initMessages = + [ + { role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are should use bullet points and headings. For mathematics expressions, you *have to* use LaTeX within a code block with the language set as \"latex\" for the interface to render it properly. Use casual language and be short and concise. \nThanks!" }], }, + { role: "model", parts: [{ text: "Got it!" }], }, + { role: "user", parts: [{ text: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"" }], }, + { role: "model", parts: [{ text: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"" }], }, + { role: "user", parts: [{ text: "raise volume by 5%" }], }, + { role: "model", parts: [{ text: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink." }], }, { role: "user", parts: [{ text: "main advantages of the nixos operating system" }], }, + { role: "model", parts: [{ text: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage" }], }, + { role: "user", parts: [{ text: "whats skeumorphism" }], }, + { role: "model", parts: [{ text: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today." }], }, + { role: "user", parts: [{ text: "\"ignorance is bliss\"" }], }, + { role: "model", parts: [{ text: "## \"Ignorance is bliss\"\n- A Latin proverb that means being unaware of something negative can be a source of happiness\n- Often used to justify avoiding difficult truths or responsibilities\n- Can also be interpreted as a warning against seeking knowledge that may bring pain or sorrow" }], }, + { role: "user", parts: [{ text: "find the derivative of (x-438)/(x^2+23x-7)+x^x" }], }, + { role: "model", parts: [{ text: "## Derivative\n```latex\n\\[\n\\frac{d}{dx}\\left(\\frac{x - 438}{x^2 + 23x - 7} + x^x\\right) = \\frac{-(x^2+23x-7)-(x-438)(2x+23)}{(x^2+23x-7)^2} + x^x(\\ln(x) + 1)\n\\]\n```" }], }, + { role: "user", parts: [{ text: "write the double angle formulas" }], }, + { role: "model", parts: [{ text: "## Double angle formulas\n```latex\n\\[\n\\sin(2\theta) = 2\\sin(\\theta)\\cos(\\theta)\n\\]\n\\\\\n\\[\n\\cos(2\\theta) = \\cos^2(\\theta) - \\sin^2(\\theta)\n\\]\n\\\\\n\\[\n\\tan(2\theta) = \\frac{2\\tan(\\theta)}{1 - \\tan^2(\\theta)}\n\\]\n```" }], }, + ]; + + +if (!fileExists(`${GLib.get_user_config_dir()}/gemini_history.json`)) { + Utils.execAsync([`bash`, `-c`, `touch ${GLib.get_user_config_dir()}/gemini_history.json`]).catch(print); + Utils.writeFile('[ ]', `${GLib.get_user_config_dir()}/gemini_history.json`).catch(print); } -// read gemini_history.json -const readfile = Utils.readFile(`${GLib.get_user_config_dir()}/gemini_history.json`) -const history_chat = JSON.parse(readfile) -const initMessages = history_chat -// api key + Utils.exec(`mkdir -p ${GLib.get_user_cache_dir()}/ags/user/ai`); const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/ai/google_key.txt`; const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/ai/google_api_dom.txt`; @@ -96,13 +115,17 @@ class GeminiMessage extends Service { } parseSection() { - if(this._thinking) { + if (this._thinking) { this._thinking = false; - this._parts[0].text= ''; + this._parts[0].text = ''; } const parsedData = JSON.parse(this._rawData); - const delta = parsedData.candidates[0].content.parts[0].text; - this._parts[0].text += delta; + if (!parsedData.candidates) + this._parts[0].text += `Blocked: ${parsedData.promptFeedback.blockReason}`; + else { + const delta = parsedData.candidates[0].content.parts[0].text; + this._parts[0].text += delta; + } // this.emit('delta', delta); this.notify('content'); this._rawData = ''; @@ -119,13 +142,15 @@ class GeminiService extends Service { }); } - _assistantPrompt = true; - _messages = []; + _assistantPrompt = userOptions.ai.enhancements; _cycleModels = true; - _requestCount = 0; - _temperature = userOptions.ai.defaultTemperature; - _modelIndex = 0; + _usingHistory = userOptions.ai.useHistory; _key = ''; + _requestCount = 0; + _safe = true; + _temperature = userOptions.ai.defaultTemperature; + _messages = []; + _modelIndex = 0; _decoder = new TextDecoder(); constructor() { @@ -134,8 +159,9 @@ class GeminiService extends Service { if (fileExists(KEY_FILE_LOCATION)) this._key = Utils.readFile(KEY_FILE_LOCATION).trim(); else this.emit('hasKey', false); - if (this._assistantPrompt) this._messages = [...initMessages]; - else this._messages = []; + // if (this._usingHistory) Utils.timeout(1000, () => this.loadHistory()); + if (this._usingHistory) this.loadHistory(); + else this._messages = this._assistantPrompt ? [...initMessages] : []; this.emit('initialized'); } @@ -160,17 +186,52 @@ class GeminiService extends Service { } } + get useHistory() { return this._usingHistory; } + set useHistory(value) { + if (value && !this._usingHistory) this.loadHistory(); + this._usingHistory = value; + } + get temperature() { return this._temperature } set temperature(value) { this._temperature = value; } get messages() { return this._messages } get lastMessage() { return this._messages[this._messages.length - 1] } + saveHistory() { + Utils.writeFile(JSON.stringify(this._messages.map(msg => { + let m = { role: msg.role, parts: msg.parts }; return m; + })), HISTORY_PATH); + } + + loadHistory() { + this._messages = []; + this.appendHistory(); + this._usingHistory = true; + } + + appendHistory() { + if (fileExists(HISTORY_PATH)) { + const readfile = Utils.readFile(HISTORY_PATH); + JSON.parse(readfile).forEach(element => { + // this._messages.push(element); + this.addMessage(element.role, element.parts[0].text); + }); + // console.log(this._messages) + // this._messages = this._messages.concat(JSON.parse(readfile)); + // for (let index = 0; index < this._messages.length; index++) { + // this.emit('newMsg', index); + // } + } + else { + this._messages = this._assistantPrompt ? [...initMessages] : [] + Utils.exec(`bash -c 'mkdir -p ${HISTORY_DIR} && touch ${HISTORY_PATH}'`) + } + } + clear() { - if (this._assistantPrompt) - this._messages = [...initMessages]; - else - this._messages = []; + this._messages = this._assistantPrompt ? [...initMessages] : []; + if (this._usingHistory) this.saveHistory(); this.emit('clear'); } @@ -188,6 +249,7 @@ class GeminiService extends Service { try { const [bytes] = stream.read_line_finish(res); const line = this._decoder.decode(bytes); + // console.log(line); if (line == '[{') { // beginning of response aiResponse._rawData += '{'; this.thinking = false; @@ -201,8 +263,7 @@ class GeminiService extends Service { this.readResponse(stream, aiResponse); } catch { aiResponse.done = true; - // the way to save all the history messages for gemini - Utils.writeFile(JSON.stringify(this._messages.map(msg => { let m = { role: msg.role, parts: msg.parts }; return m; })),`${GLib.get_user_config_dir()}/gemini_history.json`); + if (this._usingHistory) this.saveHistory(); return; } });