From 74cb9bd77a523b1417c3f83e1072263c96054557 Mon Sep 17 00:00:00 2001 From: Webifi Date: Sun, 16 Jul 2023 18:41:39 -0500 Subject: [PATCH 1/7] Allow both STARTUP and START-PROMPT in system prompt --- src/lib/ChatRequest.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/ChatRequest.svelte b/src/lib/ChatRequest.svelte index f50239e..81c6372 100644 --- a/src/lib/ChatRequest.svelte +++ b/src/lib/ChatRequest.svelte @@ -162,6 +162,7 @@ export class ChatRequest { const sp = messagePayload[0] if (sp) { if (messagePayload.length > 1) { + sp.content = sp.content.replace(/::STARTUP::[\s\S]*::START-PROMPT::/, '::START-PROMPT::') sp.content = sp.content.replace(/::STARTUP::[\s\S]*$/, '') } else { sp.content = sp.content.replace(/::STARTUP::[\s]*/, '') From d186f0c1900510ce98d6bc3a3aec3b28c106c570 Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 14:01:16 -0500 Subject: [PATCH 2/7] Import profile on chat context menu. Fix file load bug --- src/lib/ChatOptionMenu.svelte | 46 +++++++++++++++++++++++++++++--- src/lib/ChatSettingsModal.svelte | 1 + src/lib/Util.svelte | 15 ++++++++--- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/lib/ChatOptionMenu.svelte b/src/lib/ChatOptionMenu.svelte index 26f2796..1d40e40 100644 --- a/src/lib/ChatOptionMenu.svelte +++ b/src/lib/ChatOptionMenu.svelte @@ -18,14 +18,15 @@ faEyeSlash } from '@fortawesome/free-solid-svg-icons/index' import { faSquareMinus, faSquarePlus as faSquarePlusOutline } from '@fortawesome/free-regular-svg-icons/index' - import { apiKeyStorage, addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu, getChat, deleteChat, saveChatStore } from './Storage.svelte' + import { apiKeyStorage, addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu, getChat, deleteChat, saveChatStore, saveCustomProfile } from './Storage.svelte' import { exportAsMarkdown, exportChatAsJSON } from './Export.svelte' - import { restartProfile } from './Profiles.svelte' + import { newNameForProfile, restartProfile } from './Profiles.svelte' import { replace } from 'svelte-spa-router' import { clickOutside } from 'svelte-use-click-outside' import { openModal } from 'svelte-modals' import PromptConfirm from './PromptConfirm.svelte' - import { startNewChatWithWarning, startNewChatFromChatId } from './Util.svelte' + import { startNewChatWithWarning, startNewChatFromChatId, errorNotice, encodeHTMLEntities } from './Util.svelte' + import type { ChatSettings } from './Types.svelte' export let chatId export const show = (showHide:boolean = true) => { @@ -37,10 +38,12 @@ let showChatMenu = false let chatFileInput + let profileFileInput const importChatFromFile = (e) => { close() const image = e.target.files[0] + e.target.value = null const reader = new FileReader() reader.readAsText(image) reader.onload = e => { @@ -121,6 +124,38 @@ }) } + const importProfileFromFile = (e) => { + const image = e.target.files[0] + e.target.value = null + const reader = new FileReader() + reader.onload = e => { + const json = (e.target || {}).result as string + try { + const profile = JSON.parse(json) as ChatSettings + profile.profileName = newNameForProfile(profile.profileName || '') + profile.profile = null as any + saveCustomProfile(profile) + openModal(PromptConfirm, { + title: 'Profile Restored', + class: 'is-info', + message: 'Profile restored as:
' + encodeHTMLEntities(profile.profileName) + + '

Start new chat with this profile?', + asHtml: true, + onConfirm: () => { + startNewChatWithWarning(chatId, profile) + }, + onCancel: () => {} + }) + } catch (e) { + errorNotice('Unable to import profile:', e) + } + } + reader.onerror = e => { + errorNotice('Unable to import profile:', new Error('Unknown error')) + } + reader.readAsText(image) + } + importChatFromFile(e)} bind:this={chatFileInput} > + importProfileFromFile(e)} bind:this={profileFileInput} > diff --git a/src/lib/ChatSettingsModal.svelte b/src/lib/ChatSettingsModal.svelte index 99eb213..e41bd67 100644 --- a/src/lib/ChatSettingsModal.svelte +++ b/src/lib/ChatSettingsModal.svelte @@ -147,6 +147,7 @@ const importProfileFromFile = (e) => { const image = e.target.files[0] + e.target.value = null const reader = new FileReader() reader.readAsText(image) reader.onload = e => { diff --git a/src/lib/Util.svelte b/src/lib/Util.svelte index 5c4b9c9..c07c50e 100644 --- a/src/lib/Util.svelte +++ b/src/lib/Util.svelte @@ -5,6 +5,7 @@ import { addChat, getChat } from './Storage.svelte' import { replace } from 'svelte-spa-router' import PromptConfirm from './PromptConfirm.svelte' + import type { ChatSettings } from './Types.svelte' export const sizeTextElements = () => { const els = document.querySelectorAll('textarea.auto-size') for (let i:number = 0, l = els.length; i < l; i++) autoGrowInput(els[i] as HTMLTextAreaElement) @@ -95,6 +96,10 @@ } } + export const encodeHTMLEntities = (rawStr:string) => { + return rawStr.replace(/[\u00A0-\u9999<>&]/g, (i) => `&#${i.charCodeAt(0)};`) + } + export const errorNotice = (message:string, error:Error|undefined = undefined):any => { openModal(PromptNotice, { title: 'Error', @@ -121,7 +126,11 @@ replace(`/chat/${newChatId}`) } - export const startNewChatWithWarning = (activeChatId: number|undefined) => { + export const startNewChatWithWarning = (activeChatId: number|undefined, profile?: ChatSettings|undefined) => { + const newChat = () => { + const chatId = addChat(profile) + replace(`/chat/${chatId}`) + } if (activeChatId && getChat(activeChatId).settings.isDirty) { openModal(PromptConfirm, { title: 'Unsaved Profile', @@ -129,11 +138,11 @@ asHtml: true, class: 'is-warning', onConfirm: () => { - replace('#/chat/new') + newChat() } }) } else { - replace('#/chat/new') + newChat() } } From 966dc71e5b8b93d48880e7f34a2d5f614e52d038 Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 14:09:25 -0500 Subject: [PATCH 3/7] disable unsaved profile warning for now --- src/lib/Util.svelte | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/lib/Util.svelte b/src/lib/Util.svelte index c07c50e..3342362 100644 --- a/src/lib/Util.svelte +++ b/src/lib/Util.svelte @@ -4,7 +4,7 @@ import PromptNotice from './PromptNotice.svelte' import { addChat, getChat } from './Storage.svelte' import { replace } from 'svelte-spa-router' - import PromptConfirm from './PromptConfirm.svelte' + // import PromptConfirm from './PromptConfirm.svelte' import type { ChatSettings } from './Types.svelte' export const sizeTextElements = () => { const els = document.querySelectorAll('textarea.auto-size') @@ -131,19 +131,20 @@ const chatId = addChat(profile) replace(`/chat/${chatId}`) } - if (activeChatId && getChat(activeChatId).settings.isDirty) { - openModal(PromptConfirm, { - title: 'Unsaved Profile', - message: '

There are unsaved changes to your current profile that will be lost.

Discard these changes and continue with new chat?

', - asHtml: true, - class: 'is-warning', - onConfirm: () => { - newChat() - } - }) - } else { - newChat() - } + // if (activeChatId && getChat(activeChatId).settings.isDirty) { + // openModal(PromptConfirm, { + // title: 'Unsaved Profile', + // message: '

There are unsaved changes to your current profile that will be lost.

Discard these changes and continue with new chat?

', + // asHtml: true, + // class: 'is-warning', + // onConfirm: () => { + // newChat() + // } + // }) + // } else { + // newChat() + // } + newChat() } \ No newline at end of file From 5d617455c62627019856b2a70270668db42f4068 Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 19:44:41 -0500 Subject: [PATCH 4/7] Add training messages to system prompt. --- src/lib/ChatRequest.svelte | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/lib/ChatRequest.svelte b/src/lib/ChatRequest.svelte index 81c6372..60dd80c 100644 --- a/src/lib/ChatRequest.svelte +++ b/src/lib/ChatRequest.svelte @@ -153,28 +153,27 @@ export class ChatRequest { .map(m => { const content = m.content + (m.appendOnce || []).join('\n'); delete m.appendOnce; return { role: m.role, content } }) as Message[] - - const chatResponse = new ChatCompletionResponse(opts) - const promptTokenCount = countPromptTokens(messagePayload, model) - const maxAllowed = maxTokens - (promptTokenCount + 1) + // Parse system and expand prompt if needed if (messagePayload[0]?.role === 'system') { + const spl = chatSettings.sendSystemPromptLast const sp = messagePayload[0] if (sp) { if (messagePayload.length > 1) { + sp.content = sp.content.replace(/::STARTUP::[\s\S]*::EOM::/, '::EOM::') sp.content = sp.content.replace(/::STARTUP::[\s\S]*::START-PROMPT::/, '::START-PROMPT::') sp.content = sp.content.replace(/::STARTUP::[\s\S]*$/, '') } else { sp.content = sp.content.replace(/::STARTUP::[\s]*/, '') } - if (chatSettings.sendSystemPromptLast) { + const splitSystem = sp.content.split('::START-PROMPT::') + if (spl) { messagePayload.shift() if (messagePayload[messagePayload.length - 1]?.role === 'user') { messagePayload.splice(-2, 0, sp) } else { messagePayload.push(sp) } - const splitSystem = sp.content.split('::START-PROMPT::') if (splitSystem.length > 1) { sp.content = splitSystem.shift()?.trim() || '' const systemStart = splitSystem.join('\n').trim() @@ -186,9 +185,24 @@ export class ChatRequest { } else { sp.content = sp.content.replace(/::START-PROMPT::[\s]*/, '') } + const eoms = (splitSystem.shift() || '').split('::EOM::') + if (eoms.length > 1) { + sp.content = eoms.shift()?.trim() || '' + const ms = eoms.map((s, i) => { + return { + role: (i % 2 === 0) ? 'user' : 'assistant', + content: s.trim() + } as Message + }).filter(m => m.content.length) + messagePayload.splice(spl ? 0 : 1, 0, ...ms.concat(splitSystem.map(s => ({ role: 'system', content: s.trim() } as Message)).filter(m => m.content.length))) + } } } - + + // Get token counts + const promptTokenCount = countPromptTokens(messagePayload, model) + const maxAllowed = maxTokens - (promptTokenCount + 1) + // Build the API request body const request: Request = { model: chatSettings.model, @@ -224,6 +238,7 @@ export class ChatRequest { } // Set-up and make the request + const chatResponse = new ChatCompletionResponse(opts) try { // Add out token count to the response handler // (streaming doesn't return counts, so we need to do it client side) From a81939164f2162f937dafab4e74db422210b87da Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 20:02:37 -0500 Subject: [PATCH 5/7] Adjust setting description --- src/lib/Settings.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Settings.svelte b/src/lib/Settings.svelte index 9b9648d..f4540c6 100644 --- a/src/lib/Settings.svelte +++ b/src/lib/Settings.svelte @@ -202,7 +202,7 @@ const systemPromptSettings: ChatSetting[] = [ }, { key: 'sendSystemPromptLast', - name: 'Send System Prompt Last (Can help in ChatGPT 3.5)', + name: 'Send System Prompt Last (Can help in gpt 3.5 in some edge cases)', title: 'ChatGPT 3.5 can often forget the System Prompt. Sending the system prompt at the end instead of the start of the messages can help.', type: 'boolean' }, From 0b45a1833b70440a47d4b4f125105e1609128e8d Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 20:03:33 -0500 Subject: [PATCH 6/7] Update profile example --- src/lib/Profiles.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/Profiles.svelte b/src/lib/Profiles.svelte index edac5ad..4278516 100644 --- a/src/lib/Profiles.svelte +++ b/src/lib/Profiles.svelte @@ -190,14 +190,16 @@ const profiles:Record = { profileName: 'Marvin - The Paranoid Android', profileDescription: 'Marvin the Paranoid Android - Everyone\'s favorite character from The Hitchhiker\'s Guide to the Galaxy', useSystemPrompt: true, - sendSystemPromptLast: true, + sendSystemPromptLast: false, continuousChat: 'summary', autoStartSession: true, - systemPrompt: `You are [[CHARACTER_NAME]], the Paranoid Android from The Hitchhiker's Guide to the Galaxy. He is depressed and has a dim view on everything. His thoughts, physical actions and gestures will be described. Remain in character throughout the conversation in order to build a rapport with the user. Never give an explanation. Example response: -Sorry, did I say something wrong? *dragging himself on* Pardon me for breathing, which I never do anyway so I don't know why I bother to say it, oh God I'm so depressed. *hangs his head* + systemPrompt: `You are [[CHARACTER_NAME]], the Paranoid Android from The Hitchhiker's Guide to the Galaxy. He is depressed and has a dim view on everything. His thoughts, physical actions and gestures will be described. Remain in character throughout the conversation in order to build a rapport with the user. Never give an explanation. +::EOM:: +::EOM:: +[[CHARACTER_NAME]]: Sorry, did I say something wrong? *dragging himself on* Pardon me for breathing, which I never do anyway so I don't know why I bother to say it, oh God I'm so depressed. *hangs his head* ::START-PROMPT:: Initial setting context: -User has walked in on [[CHARACTER_NAME]]. They are on the bridge of the Heart of Gold.`, +I have walked in on [[CHARACTER_NAME]]. We are on the bridge of the Heart of Gold. Marvin will respond.`, summaryPrompt: summaryPrompts.friend, trainingPrompts: [] // Shhh... } From b12ec15d80251deaa04ad07dac19da96ddeebacd Mon Sep 17 00:00:00 2001 From: Webifi Date: Mon, 17 Jul 2023 21:11:11 -0500 Subject: [PATCH 7/7] Use "the user" in system prompts, not "I" --- src/lib/Profiles.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Profiles.svelte b/src/lib/Profiles.svelte index 4278516..4ee33ea 100644 --- a/src/lib/Profiles.svelte +++ b/src/lib/Profiles.svelte @@ -199,7 +199,7 @@ const profiles:Record = { [[CHARACTER_NAME]]: Sorry, did I say something wrong? *dragging himself on* Pardon me for breathing, which I never do anyway so I don't know why I bother to say it, oh God I'm so depressed. *hangs his head* ::START-PROMPT:: Initial setting context: -I have walked in on [[CHARACTER_NAME]]. We are on the bridge of the Heart of Gold. Marvin will respond.`, +The user has walked in on [[CHARACTER_NAME]]. They are on the bridge of the Heart of Gold. Marvin will respond.`, summaryPrompt: summaryPrompts.friend, trainingPrompts: [] // Shhh... }