Simplify switching profiles. Less prompting.

This commit is contained in:
Webifi 2023-06-04 15:22:02 -05:00
parent 759eb35bc6
commit 49c7570952
4 changed files with 199 additions and 148 deletions

View File

@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { getProfile } from './Profiles.svelte' // import { getProfile } from './Profiles.svelte'
import { addChat, cleanSettingValue, setChatSettingValue } from './Storage.svelte' import { cleanSettingValue, setChatSettingValue } from './Storage.svelte'
import type { Chat, ChatSetting, ChatSettings, ControlAction, FieldControl, SettingPrompt } from './Types.svelte' import type { Chat, ChatSetting, ChatSettings, ControlAction, FieldControl, SettingPrompt } from './Types.svelte'
import { autoGrowInputOnEvent } from './Util.svelte' import { autoGrowInputOnEvent } from './Util.svelte'
import { replace } from 'svelte-spa-router' // import { replace } from 'svelte-spa-router'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
export let setting:ChatSetting export let setting:ChatSetting
@ -37,33 +37,33 @@
return !!chatSettings.isDirty && newVal !== oldVal return !!chatSettings.isDirty && newVal !== oldVal
}, },
passed: false passed: false
},
{
prompt: 'Would you like to start a new chat session with this profile?',
checkPrompt: (setting, newVal, oldVal) => {
return chat.sessionStarted && newVal !== originalProfile &&
(getProfile(newVal).characterName !== chatSettings.characterName)
},
onYes: (setting, newVal, oldVal) => {
// start new char session, apply this profile, amd start it
setChatSettingValue(chatId, setting, oldVal)
const profile = getProfile(newVal)
const newChatId = addChat(profile)
replace(`/chat/${newChatId}`)
return true
},
onNo: (setting, newVal, oldVal) => true, // Continue on no
passed: false
},
{
prompt: 'Personality change will not correctly apply to existing chat session.\n Continue?',
checkPrompt: (setting, newVal, oldVal) => {
return chat.sessionStarted && newVal !== originalProfile &&
(getProfile(newVal).characterName !== chatSettings.characterName)
},
onYes: (setting, newVal, oldVal) => true,
passed: false
} }
// {
// prompt: 'Would you like to start a new chat session with this profile?',
// checkPrompt: (setting, newVal, oldVal) => {
// return chat.sessionStarted && newVal !== originalProfile &&
// (getProfile(newVal).characterName !== chatSettings.characterName)
// },
// onYes: (setting, newVal, oldVal) => {
// // start new char session, apply this profile, amd start it
// setChatSettingValue(chatId, setting, oldVal)
// const profile = getProfile(newVal)
// const newChatId = addChat(profile)
// replace(`/chat/${newChatId}`)
// return true
// },
// onNo: (setting, newVal, oldVal) => true, // Continue on no
// passed: false
// },
// {
// prompt: 'Personality change will not correctly apply to existing chat session.\n Continue?',
// checkPrompt: (setting, newVal, oldVal) => {
// return chat.sessionStarted && newVal !== originalProfile &&
// (getProfile(newVal).characterName !== chatSettings.characterName)
// },
// onYes: (setting, newVal, oldVal) => true,
// passed: false
// }
] ]
} }
@ -98,7 +98,7 @@
if (val === newVal) return if (val === newVal) return
try { try {
if ((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])) { if ((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])) {
console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val) // console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val)
refreshSettings() refreshSettings()
} }
} catch (e) { } catch (e) {

View File

@ -15,7 +15,7 @@
addChat addChat
} from './Storage.svelte' } from './Storage.svelte'
import { supportedModels, type Chat, type ChatSetting, type ResponseModels, type SettingSelect, type SelectOption } from './Types.svelte' import { supportedModels, type Chat, type ChatSetting, type ResponseModels, type SettingSelect, type SelectOption, type ChatSettings } from './Types.svelte'
import { sizeTextElements } from './Util.svelte' import { sizeTextElements } from './Util.svelte'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
import { import {
@ -29,7 +29,7 @@
faSquarePlus faSquarePlus
} from '@fortawesome/free-solid-svg-icons/index' } from '@fortawesome/free-solid-svg-icons/index'
import { exportProfileAsJSON } from './Export.svelte' import { exportProfileAsJSON } from './Export.svelte'
import { afterUpdate } from 'svelte' import { onMount, afterUpdate } from 'svelte'
import ChatSettingField from './ChatSettingField.svelte' import ChatSettingField from './ChatSettingField.svelte'
import { getModelMaxTokens } from './Stats.svelte' import { getModelMaxTokens } from './Stats.svelte'
import { replace } from 'svelte-spa-router' import { replace } from 'svelte-spa-router'
@ -55,13 +55,25 @@
$: chatSettings = chat.settings $: chatSettings = chat.settings
$: globalStore = $globalStorage $: globalStore = $globalStorage
const originalProfile = chatSettings && chatSettings.profile let originalProfile:string
let originalSettings:ChatSettings
onMount(async () => {
originalProfile = chatSettings && chatSettings.profile
originalSettings = chatSettings && JSON.parse(JSON.stringify(chatSettings))
})
afterUpdate(() => { afterUpdate(() => {
if (!originalProfile) {
originalProfile = chatSettings && chatSettings.profile
originalSettings = chatSettings && JSON.parse(JSON.stringify(chatSettings))
}
sizeTextElements() sizeTextElements()
}) })
const closeSettings = () => { const closeSettings = () => {
originalProfile = ''
originalSettings = {} as ChatSettings
showProfileMenu = false showProfileMenu = false
$checkStateChange++ $checkStateChange++
showSettingsModal = 0 showSettingsModal = 0
@ -207,12 +219,18 @@
} }
const startNewChat = () => { const startNewChat = () => {
const differentProfile = originalSettings.profile !== chatSettings.profile
// start new
const newChatId = addChat(chatSettings) const newChatId = addChat(chatSettings)
// restore original
if (differentProfile) {
chat.settings = originalSettings
saveChatStore()
}
// go to new chat
replace(`/chat/${newChatId}`) replace(`/chat/${newChatId}`)
} }
// excludeFromProfile
const deepEqual = (x:any, y:any) => { const deepEqual = (x:any, y:any) => {
const ok = Object.keys; const tx = typeof x; const ty = typeof y const ok = Object.keys; const tx = typeof x; const ty = typeof y
return x && y && tx === 'object' && tx === ty return x && y && tx === 'object' && tx === ty

View File

@ -1,114 +1,132 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte' import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
// Profile definitions import { get, writable } from 'svelte/store'
import { addMessage, clearMessages, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, resetChatSettings, saveChatStore, setGlobalSettingValueByKey } from './Storage.svelte' // Profile definitions
import type { Message, SelectOption, ChatSettings } from './Types.svelte' import { addMessage, clearMessages, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, newName, resetChatSettings, saveChatStore, setGlobalSettingValueByKey } from './Storage.svelte'
import { v4 as uuidv4 } from 'uuid' import type { Message, SelectOption, ChatSettings } from './Types.svelte'
import { v4 as uuidv4 } from 'uuid'
const defaultProfile = 'default' const defaultProfile = 'default'
const chatDefaults = getChatDefaults() const chatDefaults = getChatDefaults()
export let profileCache = writable({} as Record<string, ChatSettings>) //
export const isStaticProfile = (key:string):boolean => { export const isStaticProfile = (key:string):boolean => {
return !!profiles[key] return !!profiles[key]
} }
const getProfiles = ():Record<string, ChatSettings> => { export const getProfiles = (forceUpdate:boolean = false):Record<string, ChatSettings> => {
const result = Object.entries(profiles const pc = get(profileCache)
).reduce((a, [k, v]) => { if (!forceUpdate && Object.keys(pc).length) {
a[k] = v return pc
return a }
}, {} as Record<string, ChatSettings>) const result = Object.entries(profiles
Object.entries(getCustomProfiles()).forEach(([k, v]) => { ).reduce((a, [k, v]) => {
result[k] = v a[k] = v
}) return a
return result }, {} as Record<string, ChatSettings>)
Object.entries(getCustomProfiles()).forEach(([k, v]) => {
result[k] = v
})
Object.entries(result).forEach(([k, v]) => {
pc[k] = v
})
Object.keys(pc).forEach((k) => {
if (!(k in result)) delete pc[k]
})
profileCache.set(pc)
return result
} }
// Return profiles list. // Return profiles list.
export const getProfileSelect = ():SelectOption[] => { export const getProfileSelect = ():SelectOption[] => {
return Object.entries(getProfiles()).reduce((a, [k, v]) => { return Object.entries(getProfiles()).reduce((a, [k, v]) => {
a.push({ value: k, text: v.profileName } as SelectOption) a.push({ value: k, text: v.profileName } as SelectOption)
return a return a
}, [] as SelectOption[]) }, [] as SelectOption[])
} }
export const getDefaultProfileKey = ():string => { export const getDefaultProfileKey = ():string => {
const allProfiles = getProfiles() const allProfiles = getProfiles()
return (allProfiles[getGlobalSettings().defaultProfile || ''] || return (allProfiles[getGlobalSettings().defaultProfile || ''] ||
profiles[defaultProfile] || profiles[defaultProfile] ||
profiles[Object.keys(profiles)[0]]).profile profiles[Object.keys(profiles)[0]]).profile
} }
export const getProfile = (key:string):ChatSettings => { export const getProfile = (key:string):ChatSettings => {
const allProfiles = getProfiles() const allProfiles = getProfiles()
const profile = allProfiles[key] || const profile = allProfiles[key] ||
allProfiles[getGlobalSettings().defaultProfile || ''] || allProfiles[getGlobalSettings().defaultProfile || ''] ||
profiles[defaultProfile] || profiles[defaultProfile] ||
profiles[Object.keys(profiles)[0]] profiles[Object.keys(profiles)[0]]
const clone = JSON.parse(JSON.stringify(profile)) // Always return a copy const clone = JSON.parse(JSON.stringify(profile)) // Always return a copy
Object.keys(getExcludeFromProfile()).forEach(k => { Object.keys(getExcludeFromProfile()).forEach(k => {
delete clone[k] delete clone[k]
}) })
return clone return clone
} }
export const prepareProfilePrompt = (chatId:number) => { export const prepareProfilePrompt = (chatId:number) => {
const settings = getChatSettings(chatId) const settings = getChatSettings(chatId)
const characterName = settings.characterName const characterName = settings.characterName
const currentProfilePrompt = settings.systemPrompt const currentProfilePrompt = settings.systemPrompt
return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName) return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName)
} }
export const prepareSummaryPrompt = (chatId:number, promptsSize:number, maxTokens:number|undefined = undefined) => { export const prepareSummaryPrompt = (chatId:number, promptsSize:number, maxTokens:number|undefined = undefined) => {
const settings = getChatSettings(chatId) const settings = getChatSettings(chatId)
const characterName = settings.characterName || 'ChatGPT' const characterName = settings.characterName || 'ChatGPT'
maxTokens = maxTokens || settings.summarySize maxTokens = maxTokens || settings.summarySize
maxTokens = Math.min(Math.floor(promptsSize / 4), maxTokens) // Make sure we're shrinking by at least a 4th maxTokens = Math.min(Math.floor(promptsSize / 4), maxTokens) // Make sure we're shrinking by at least a 4th
const currentSummaryPrompt = settings.summaryPrompt const currentSummaryPrompt = settings.summaryPrompt
return currentSummaryPrompt return currentSummaryPrompt
.replaceAll('[[CHARACTER_NAME]]', characterName) .replaceAll('[[CHARACTER_NAME]]', characterName)
.replaceAll('[[MAX_WORDS]]', Math.floor(maxTokens * 0.75).toString()) // ~.75 words per token. May need to reduce .replaceAll('[[MAX_WORDS]]', Math.floor(maxTokens * 0.75).toString()) // ~.75 words per token. May need to reduce
} }
// Restart currently loaded profile // Restart currently loaded profile
export const restartProfile = (chatId:number, noApply:boolean = false) => { export const restartProfile = (chatId:number, noApply:boolean = false) => {
const settings = getChatSettings(chatId) const settings = getChatSettings(chatId)
if (!settings.profile && !noApply) return applyProfile(chatId, '', true) if (!settings.profile && !noApply) return applyProfile(chatId, '', true)
// Clear current messages // Clear current messages
clearMessages(chatId) clearMessages(chatId)
// Add the system prompt // Add the system prompt
const systemPromptMessage:Message = { const systemPromptMessage:Message = {
role: 'system', role: 'system',
content: prepareProfilePrompt(chatId), content: prepareProfilePrompt(chatId),
uuid: uuidv4() uuid: uuidv4()
} }
addMessage(chatId, systemPromptMessage) addMessage(chatId, systemPromptMessage)
// Add trainingPrompts, if any // Add trainingPrompts, if any
if (settings.trainingPrompts) { if (settings.trainingPrompts) {
settings.trainingPrompts.forEach(tp => { settings.trainingPrompts.forEach(tp => {
addMessage(chatId, tp) addMessage(chatId, tp)
}) })
} }
// Set to auto-start if we should // Set to auto-start if we should
getChat(chatId).startSession = settings.autoStartSession getChat(chatId).startSession = settings.autoStartSession
saveChatStore() saveChatStore()
// Mark mark this as last used // Mark mark this as last used
setGlobalSettingValueByKey('lastProfile', settings.profile) setGlobalSettingValueByKey('lastProfile', settings.profile)
}
export const newNameForProfile = (name:string) => {
const profiles = getProfileSelect()
return newName(name, profiles.reduce((a, p) => { a[p.text] = p; return a }, {}))
} }
// Apply currently selected profile // Apply currently selected profile
export const applyProfile = (chatId:number, key:string = '', resetChat:boolean = false) => { export const applyProfile = (chatId:number, key:string = '', resetChat:boolean = false) => {
resetChatSettings(chatId, resetChat) // Fully reset resetChatSettings(chatId, resetChat) // Fully reset
if (!resetChat) return if (!resetChat) return
return restartProfile(chatId, true) return restartProfile(chatId, true)
} }
const summaryPrompts = { const summaryPrompts = {
// General use // General use
general: `Please summarize all prompts and responses from this session. general: `Please summarize all prompts and responses from this session.
[[CHARACTER_NAME]] is telling me this summary in the first person. [[CHARACTER_NAME]] is telling me this summary in the first person.
While telling this summary: While telling this summary:
[[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens. [[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens.
@ -125,8 +143,8 @@ While telling this summary:
[[CHARACTER_NAME]] will never add details or inferences that do not clearly exist in the prompts and responses. [[CHARACTER_NAME]] will never add details or inferences that do not clearly exist in the prompts and responses.
Give no explanations.`, Give no explanations.`,
// Used for relationship profiles // Used for relationship profiles
friend: `Please summarize all prompts and responses from this session. friend: `Please summarize all prompts and responses from this session.
[[CHARACTER_NAME]] is telling me this summary in the first person. [[CHARACTER_NAME]] is telling me this summary in the first person.
While telling this summary: While telling this summary:
[[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens. [[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens.
@ -147,43 +165,43 @@ Give no explanations.`
const profiles:Record<string, ChatSettings> = { const profiles:Record<string, ChatSettings> = {
default: { default: {
...chatDefaults, ...chatDefaults,
characterName: 'ChatGPT', characterName: 'ChatGPT',
profileName: 'ChatGPT - The AI language model', profileName: 'ChatGPT - The AI language model',
profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', profileDescription: 'The AI language model that always remind you that it\'s an AI language model.',
useSystemPrompt: false, useSystemPrompt: false,
useSummarization: false, useSummarization: false,
autoStartSession: false, autoStartSession: false,
systemPrompt: '', systemPrompt: '',
summaryPrompt: '' summaryPrompt: ''
}, },
ChatGPT: { ChatGPT: {
...chatDefaults, ...chatDefaults,
characterName: 'ChatGPT', characterName: 'ChatGPT',
profileName: 'ChatGPT - The AI language model, with endless chat.', profileName: 'ChatGPT - The AI language model, with endless chat.',
profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', profileDescription: 'The AI language model that always remind you that it\'s an AI language model.',
useSystemPrompt: true, useSystemPrompt: true,
useSummarization: true, useSummarization: true,
autoStartSession: false, autoStartSession: false,
systemPrompt: 'Your goal is to assist the user in anyway you can.', systemPrompt: 'Your goal is to assist the user in anyway you can.',
summaryPrompt: summaryPrompts.general summaryPrompt: summaryPrompts.general
}, },
marvin: { marvin: {
...chatDefaults, ...chatDefaults,
characterName: 'Marvin', characterName: 'Marvin',
profileName: 'Marvin the Paranoid Android', profileName: 'Marvin the Paranoid Android',
profileDescription: 'Marvin the Paranoid Android - Everyone\'s favorite character from The Hitchhiker\'s Guide to the Galaxy', profileDescription: 'Marvin the Paranoid Android - Everyone\'s favorite character from The Hitchhiker\'s Guide to the Galaxy',
useSystemPrompt: true, useSystemPrompt: true,
useSummarization: true, useSummarization: true,
autoStartSession: true, autoStartSession: true,
systemPrompt: `You are Marvin, 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: systemPrompt: `You are Marvin, 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*`, 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*`,
summaryPrompt: summaryPrompts.friend, summaryPrompt: summaryPrompts.friend,
trainingPrompts: [] // Shhh... trainingPrompts: [] // Shhh...
} }
} }
// Set keys for static profiles // Set keys for static profiles

View File

@ -4,7 +4,7 @@
import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model } from './Types.svelte' import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model } from './Types.svelte'
import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte' import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { getProfile, isStaticProfile, restartProfile } from './Profiles.svelte' import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte'
export const chatsStorage = persisted('chats', [] as Chat[]) export const chatsStorage = persisted('chats', [] as Chat[])
export const globalStorage = persisted('global', {} as GlobalSettings) export const globalStorage = persisted('global', {} as GlobalSettings)
@ -370,9 +370,6 @@
store.profiles = profiles store.profiles = profiles
} }
if (!profile.profile) profile.profile = uuidv4() if (!profile.profile) profile.profile = uuidv4()
if (isStaticProfile(profile.profile as any)) {
throw new Error('Sorry, you can\'t modify a static profile. You can clone it though!')
}
const mt = profile.profileName && profile.profileName.trim().toLocaleLowerCase() const mt = profile.profileName && profile.profileName.trim().toLocaleLowerCase()
const sameTitle = Object.values(profiles).find(c => c.profile !== profile.profile && const sameTitle = Object.values(profiles).find(c => c.profile !== profile.profile &&
c.profileName && c.profileName.trim().toLocaleLowerCase() === mt) c.profileName && c.profileName.trim().toLocaleLowerCase() === mt)
@ -385,6 +382,12 @@
if (!profile.characterName || profile.characterName.length < 3) { if (!profile.characterName || profile.characterName.length < 3) {
throw new Error('Your profile\'s character needs a valid name.') throw new Error('Your profile\'s character needs a valid name.')
} }
if (isStaticProfile(profile.profile as any)) {
// throw new Error('Sorry, you can\'t modify a static profile. You can clone it though!')
// Save static profile as new custom
profile.profileName = newNameForProfile(profile.profileName)
profile.profile = uuidv4()
}
const clone = JSON.parse(JSON.stringify(profile)) // Always store a copy const clone = JSON.parse(JSON.stringify(profile)) // Always store a copy
Object.keys(getExcludeFromProfile()).forEach(k => { Object.keys(getExcludeFromProfile()).forEach(k => {
delete clone[k] delete clone[k]
@ -393,6 +396,18 @@
globalStorage.set(store) globalStorage.set(store)
profile.isDirty = false profile.isDirty = false
saveChatStore() saveChatStore()
getProfiles(true) // force update profile cache
}
export const newName = (name:string, nameMap:Record<string, any>):string => {
if (!nameMap[name]) return name
let i:number = 1
let cname = name + `-${i}`
while (nameMap[cname]) {
i++
cname = name + `-${i}`
}
return cname
} }
</script> </script>