Refactor settings again
This commit is contained in:
parent
6ce0355eec
commit
a4222c5347
|
@ -34,6 +34,7 @@
|
||||||
"svelte-local-storage-store": "^0.4.0",
|
"svelte-local-storage-store": "^0.4.0",
|
||||||
"svelte-markdown": "^0.2.3",
|
"svelte-markdown": "^0.2.3",
|
||||||
"svelte-spa-router": "^3.3.0",
|
"svelte-spa-router": "^3.3.0",
|
||||||
|
"svelte-use-click-outside": "^1.0.0",
|
||||||
"tslib": "^2.5.0",
|
"tslib": "^2.5.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
@ -4324,6 +4325,12 @@
|
||||||
"url": "https://github.com/sponsors/ItalyPaleAle"
|
"url": "https://github.com/sponsors/ItalyPaleAle"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte-use-click-outside": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-use-click-outside/-/svelte-use-click-outside-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-tOWeMPxeIoW9RshS0WbogRhdYdbxcJV0ebkzSh1lwR7Ihl0hSZMmB4YyCHHoXJK4xcbxCCFh0AnQ1vkzGZfLVQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"svelte-local-storage-store": "^0.4.0",
|
"svelte-local-storage-store": "^0.4.0",
|
||||||
"svelte-markdown": "^0.2.3",
|
"svelte-markdown": "^0.2.3",
|
||||||
"svelte-spa-router": "^3.3.0",
|
"svelte-spa-router": "^3.3.0",
|
||||||
|
"svelte-use-click-outside": "^1.0.0",
|
||||||
"tslib": "^2.5.0",
|
"tslib": "^2.5.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
|
|
@ -9,16 +9,17 @@
|
||||||
insertMessages,
|
insertMessages,
|
||||||
clearMessages,
|
clearMessages,
|
||||||
copyChat,
|
copyChat,
|
||||||
getChatSettingValue,
|
|
||||||
getChatSettingValueByKey,
|
|
||||||
setChatSettingValue,
|
|
||||||
getChatSettingValueNullDefault,
|
getChatSettingValueNullDefault,
|
||||||
setChatSettingValueByKey,
|
setChatSettingValueByKey,
|
||||||
saveCustomProfile,
|
saveCustomProfile,
|
||||||
deleteCustomProfile,
|
deleteCustomProfile,
|
||||||
setGlobalSettingValueByKey
|
setGlobalSettingValueByKey,
|
||||||
|
updateChatSettings,
|
||||||
|
resetChatSettings,
|
||||||
|
setChatSettingValue,
|
||||||
|
addChatFromJSON,
|
||||||
} from './Storage.svelte'
|
} from './Storage.svelte'
|
||||||
import { getChatSettingByKey, getChatSettingList } from './Settings.svelte'
|
import { getChatSettingObjectByKey, getChatSettingList, getRequestSettingList } from './Settings.svelte'
|
||||||
import {
|
import {
|
||||||
type Request,
|
type Request,
|
||||||
type Response,
|
type Response,
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
} from './Types.svelte'
|
} from './Types.svelte'
|
||||||
import Prompts from './Prompts.svelte'
|
import Prompts from './Prompts.svelte'
|
||||||
import Messages from './Messages.svelte'
|
import Messages from './Messages.svelte'
|
||||||
import { applyProfile, checkSessionActivity, getProfile, getProfileSelect, prepareSummaryPrompt } from './Profiles.svelte'
|
import { applyProfile, getProfile, getProfileSelect, prepareSummaryPrompt } from './Profiles.svelte'
|
||||||
|
|
||||||
import { afterUpdate, onMount } from 'svelte'
|
import { afterUpdate, onMount } from 'svelte'
|
||||||
import { replace } from 'svelte-spa-router'
|
import { replace } from 'svelte-spa-router'
|
||||||
|
@ -51,11 +52,14 @@
|
||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
faThumbtack,
|
faThumbtack,
|
||||||
faDownload,
|
faDownload,
|
||||||
faUpload
|
faUpload,
|
||||||
|
faEraser,
|
||||||
|
faRotateRight,
|
||||||
} from '@fortawesome/free-solid-svg-icons/index'
|
} from '@fortawesome/free-solid-svg-icons/index'
|
||||||
import { encode } from 'gpt-tokenizer'
|
import { encode } from 'gpt-tokenizer'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { exportProfileAsJSON } from './Export.svelte'
|
import { exportChatAsJSON, exportProfileAsJSON } from './Export.svelte'
|
||||||
|
import { clickOutside } from 'svelte-use-click-outside'
|
||||||
|
|
||||||
// This makes it possible to override the OpenAI API base URL in the .env file
|
// This makes it possible to override the OpenAI API base URL in the .env file
|
||||||
const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com'
|
const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com'
|
||||||
|
@ -70,26 +74,22 @@
|
||||||
let chatNameSettings: HTMLFormElement
|
let chatNameSettings: HTMLFormElement
|
||||||
let recognition: any = null
|
let recognition: any = null
|
||||||
let recording = false
|
let recording = false
|
||||||
|
let chatFileInput
|
||||||
let profileFileInput
|
let profileFileInput
|
||||||
let showSettingsModal = 0
|
let showSettingsModal = 0
|
||||||
let showProfileMenu = false
|
let showProfileMenu = false
|
||||||
|
let showChatMenu = false
|
||||||
|
|
||||||
const settingsList = getChatSettingList()
|
const settingsList = getChatSettingList()
|
||||||
const modelSetting = getChatSettingByKey('model') as ChatSetting & SettingSelect
|
const modelSetting = getChatSettingObjectByKey('model') as ChatSetting & SettingSelect
|
||||||
|
|
||||||
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
||||||
|
$: chatSettings = chat.settings
|
||||||
$: globalStore = $globalStorage
|
$: globalStore = $globalStorage
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Sanitize old save
|
// Make sure chat object is ready to go
|
||||||
if (!chat.settings) chat.settings = {} as ChatSettings
|
updateChatSettings(chatId)
|
||||||
// make sure old chat has UUID
|
|
||||||
if (chat && chat.messages && chat.messages[0] && !chat.messages[0].uuid) {
|
|
||||||
chat.messages.forEach((m) => {
|
|
||||||
m.uuid = uuidv4()
|
|
||||||
})
|
|
||||||
saveChatStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Focus the input on mount
|
// Focus the input on mount
|
||||||
focusInput()
|
focusInput()
|
||||||
|
@ -120,11 +120,12 @@
|
||||||
} else {
|
} else {
|
||||||
console.log('Speech recognition not supported')
|
console.log('Speech recognition not supported')
|
||||||
}
|
}
|
||||||
if (!chat.settings.profile) {
|
if (chatSettings.startSession) {
|
||||||
const profile = getProfile('') // get default profile
|
const profile = getProfile('') // get default profile
|
||||||
applyProfile(chatId, profile.profile as any)
|
applyProfile(chatId, profile.profile as any)
|
||||||
if (getChatSettingValueByKey(chatId, 'startSession')) {
|
if (chatSettings.startSession) {
|
||||||
setChatSettingValueByKey(chatId, 'startSession', false)
|
setChatSettingValueByKey(chatId, 'startSession', false)
|
||||||
|
// Auto start the session out of band
|
||||||
setTimeout(() => { submitForm(false, true) }, 0)
|
setTimeout(() => { submitForm(false, true) }, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,15 +161,15 @@
|
||||||
return a
|
return a
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
if (getChatSettingValueByKey(chatId, 'useSummarization') &&
|
if (chatSettings.useSummarization &&
|
||||||
!withSummary && !doingSummary &&
|
!withSummary && !doingSummary &&
|
||||||
(promptTokenCount > getChatSettingValueByKey(chatId, 'summaryThreshold'))) {
|
promptTokenCount > chatSettings.summaryThreshold) {
|
||||||
// Too many tokens -- well need to sumarize some past ones else we'll run out of space
|
// Too many tokens -- well need to sumarize some past ones else we'll run out of space
|
||||||
// Get a block of past prompts we'll summarize
|
// Get a block of past prompts we'll summarize
|
||||||
let pinTop = getChatSettingValueByKey(chatId, 'pinTop')
|
let pinTop = chatSettings.pinTop
|
||||||
const tp = getChatSettingValueByKey(chatId, 'trainingPrompts')
|
const tp = chatSettings.trainingPrompts
|
||||||
pinTop = Math.max(pinTop, tp || 0)
|
pinTop = Math.max(pinTop, tp ? 1 : 0)
|
||||||
let pinBottom = getChatSettingValueByKey(chatId, 'pinBottom')
|
let pinBottom = chatSettings.pinBottom
|
||||||
const systemPad = (filtered[0] || {} as Message).role === 'system' ? 1 : 0
|
const systemPad = (filtered[0] || {} as Message).role === 'system' ? 1 : 0
|
||||||
const mlen = filtered.length - systemPad // always keep system prompt
|
const mlen = filtered.length - systemPad // always keep system prompt
|
||||||
let diff = mlen - (pinTop + pinBottom)
|
let diff = mlen - (pinTop + pinBottom)
|
||||||
|
@ -258,13 +259,15 @@
|
||||||
messages: filtered.map(m => { return { role: m.role, content: m.content } }) as Message[],
|
messages: filtered.map(m => { return { role: m.role, content: m.content } }) as Message[],
|
||||||
|
|
||||||
// Provide the settings by mapping the settingsMap to key/value pairs
|
// Provide the settings by mapping the settingsMap to key/value pairs
|
||||||
...getChatSettingList().reduce((acc, setting) => {
|
...getRequestSettingList().reduce((acc, setting) => {
|
||||||
if (setting.noRequest) return acc // don't include non-request settings
|
|
||||||
let value = getChatSettingValueNullDefault(chatId, setting)
|
let value = getChatSettingValueNullDefault(chatId, setting)
|
||||||
if (value === null && setting.required) value = setting.default
|
|
||||||
if (doingSummary && setting.key === 'max_tokens') {
|
if (doingSummary && setting.key === 'max_tokens') {
|
||||||
// Override for summary
|
// Override for summary
|
||||||
value = getChatSettingValueByKey(chatId, 'summarySize')
|
// TODO: Auto adjust this above to make sure it doesn't go over avail token space
|
||||||
|
value = chatSettings.summarySize
|
||||||
|
}
|
||||||
|
if (typeof setting.apiTransform === 'function') {
|
||||||
|
value = setting.apiTransform(chatId, setting, value)
|
||||||
}
|
}
|
||||||
if (value !== null) acc[setting.key] = value
|
if (value !== null) acc[setting.key] = value
|
||||||
return acc
|
return acc
|
||||||
|
@ -344,6 +347,7 @@
|
||||||
if (updating) return
|
if (updating) return
|
||||||
|
|
||||||
if (!skipInput) {
|
if (!skipInput) {
|
||||||
|
setChatSettingValueByKey(chatId, 'sessionStarted', true)
|
||||||
if (input.value !== '') {
|
if (input.value !== '') {
|
||||||
// Compose the input message
|
// Compose the input message
|
||||||
const inputMessage: Message = { role: 'user', content: input.value, uuid: uuidv4() }
|
const inputMessage: Message = { role: 'user', content: input.value, uuid: uuidv4() }
|
||||||
|
@ -440,10 +444,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateProfileSelectOptions = () => {
|
const updateProfileSelectOptions = () => {
|
||||||
const profileSelect = getChatSettingByKey('profile') as ChatSetting & SettingSelect
|
const profileSelect = getChatSettingObjectByKey('profile') as ChatSetting & SettingSelect
|
||||||
const defaultProfile = getProfile('')
|
|
||||||
profileSelect.default = defaultProfile.profile as any
|
|
||||||
profileSelect.options = getProfileSelect()
|
profileSelect.options = getProfileSelect()
|
||||||
|
// const defaultProfile = globalStore.defaultProfile || profileSelect.options[0].value
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshSettings = async () => {
|
||||||
|
showSettingsModal && showSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSettings = async () => {
|
const showSettings = async () => {
|
||||||
|
@ -502,13 +509,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearSettings = () => {
|
const clearSettings = () => {
|
||||||
settingsList.forEach(s => {
|
resetChatSettings(chatId)
|
||||||
setChatSettingValue(chatId, s, null)
|
|
||||||
})
|
|
||||||
showSettingsModal++ // Make sure the dialog updates
|
showSettingsModal++ // Make sure the dialog updates
|
||||||
// const input = settings.querySelector(`#settings-${setting.key}`) as HTMLInputElement
|
|
||||||
// saveSetting(chatId, setting, null)
|
|
||||||
// input.value = ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const recordToggle = () => {
|
const recordToggle = () => {
|
||||||
|
@ -526,22 +528,34 @@
|
||||||
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
|
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
|
||||||
clearTimeout(debounce[setting.key])
|
clearTimeout(debounce[setting.key])
|
||||||
if (event.target === null) return
|
if (event.target === null) return
|
||||||
|
const val = chatSettings[setting.key]
|
||||||
const el = (event.target as HTMLInputElement)
|
const el = (event.target as HTMLInputElement)
|
||||||
const doSet = () => {
|
const doSet = () => {
|
||||||
|
try {
|
||||||
|
(typeof setting.beforeChange === 'function') && setting.beforeChange(chatId, setting, el.checked || el.value)
|
||||||
|
&& refreshSettings()
|
||||||
|
} catch (e) {
|
||||||
|
alert('Unable to change:\n' + e.message)
|
||||||
|
}
|
||||||
switch (setting.type) {
|
switch (setting.type) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
setChatSettingValue(chatId, setting, el.checked)
|
setChatSettingValue(chatId, setting, el.checked)
|
||||||
showSettingsModal && showSettingsModal++
|
refreshSettings()
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
setChatSettingValue(chatId, setting, el.value)
|
setChatSettingValue(chatId, setting, el.value)
|
||||||
}
|
}
|
||||||
(typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting)
|
try {
|
||||||
&& showSettingsModal && showSettingsModal++
|
(typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])
|
||||||
|
&& refreshSettings()
|
||||||
|
} catch (e) {
|
||||||
|
setChatSettingValue(chatId, setting, val)
|
||||||
|
alert('Unable to change:\n' + e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (setting.key === 'profile' && checkSessionActivity(chatId)
|
if (setting.key === 'profile' && chatSettings.sessionStarted
|
||||||
&& (getProfile(el.value).characterName !== getChatSettingValueByKey(chatId,'characterName'))) {
|
&& (getProfile(el.value).characterName !== chatSettings.characterName)) {
|
||||||
const val = getChatSettingValue(chatId, setting)
|
const val = chatSettings[setting.key]
|
||||||
if (window.confirm('Personality change will not correctly apply to existing chat session.\n Continue?')) {
|
if (window.confirm('Personality change will not correctly apply to existing chat session.\n Continue?')) {
|
||||||
doSet()
|
doSet()
|
||||||
} else {
|
} else {
|
||||||
|
@ -568,6 +582,7 @@
|
||||||
showProfileMenu = false
|
showProfileMenu = false
|
||||||
try {
|
try {
|
||||||
saveCustomProfile(chat.settings)
|
saveCustomProfile(chat.settings)
|
||||||
|
refreshSettings()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Error saving profile: \n' + e.message)
|
alert('Error saving profile: \n' + e.message)
|
||||||
}
|
}
|
||||||
|
@ -607,7 +622,7 @@
|
||||||
showProfileMenu = false
|
showProfileMenu = false
|
||||||
try {
|
try {
|
||||||
deleteCustomProfile(chatId, chat.settings.profile as any)
|
deleteCustomProfile(chatId, chat.settings.profile as any)
|
||||||
chat.settings.profile = globalStore.defaultProfile
|
chat.settings.profile = globalStore.defaultProfile || ''
|
||||||
saveChatStore()
|
saveChatStore()
|
||||||
setGlobalSettingValueByKey('lastProfile', chat.settings.profile)
|
setGlobalSettingValueByKey('lastProfile', chat.settings.profile)
|
||||||
applyProfile(chatId, chat.settings.profile as any)
|
applyProfile(chatId, chat.settings.profile as any)
|
||||||
|
@ -641,28 +656,78 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importChatFromFile = (e) => {
|
||||||
|
const image = e.target.files[0]
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsText(image)
|
||||||
|
reader.onload = e => {
|
||||||
|
const json = (e.target || {}).result as string
|
||||||
|
addChatFromJSON(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="level chat-header">
|
<nav class="level chat-header">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<p class="subtitle is-5">
|
<p class="subtitle is-5">
|
||||||
{chat.name || `Chat ${chat.id}`}
|
<span>{chat.name || `Chat ${chat.id}`}</span>
|
||||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Rename chat" on:click|preventDefault={showChatNameSettings}><Fa icon={faPenToSquare} /></a>
|
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Rename chat" on:click|preventDefault={showChatNameSettings}><Fa icon={faPenToSquare} /></a>
|
||||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Suggest a chat name" on:click|preventDefault={suggestName}><Fa icon={faLightbulb} /></a>
|
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Suggest a chat name" on:click|preventDefault={suggestName}><Fa icon={faLightbulb} /></a>
|
||||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Delete this chat" on:click|preventDefault={deleteChat}><Fa icon={faTrash} /></a>
|
<!-- <a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Copy this chat" on:click|preventDefault={() => { copyChat(chatId) }}><Fa icon={faClone} /></a> -->
|
||||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Copy this chat" on:click|preventDefault={() => { copyChat(chatId) }}><Fa icon={faClone} /></a>
|
<!-- <a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Delete this chat" on:click|preventDefault={deleteChat}><Fa icon={faTrash} /></a> -->
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
<p class="level-item">
|
<div class="level-item">
|
||||||
{#if chat.settings.autoStartSession && chat.settings.systemPrompt && chat.settings.useSystemPrompt}
|
|
||||||
|
<div class="dropdown is-right" class:is-active={showChatMenu} use:clickOutside={()=>{showChatMenu=false}}>
|
||||||
{/if}
|
<div class="dropdown-trigger">
|
||||||
<button class="button is-warning" on:click={() => { clearMessages(chatId); window.location.reload() }}><span class="greyscale mr-2"><Fa icon={faTrash} /></span> Clear messages</button>
|
<button class="button" aria-haspopup="true"
|
||||||
</p>
|
aria-controls="dropdown-menu3"
|
||||||
|
on:click|preventDefault|stopPropagation={() => { showChatMenu = !showChatMenu }}
|
||||||
|
>
|
||||||
|
<span><Fa icon={faEllipsisVertical}/></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { showChatMenu = false;showSettings() }}>
|
||||||
|
<span><Fa icon={faGear}/></span> Settings
|
||||||
|
</a>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { showChatMenu = false;copyChat(chatId) }}>
|
||||||
|
<span><Fa icon={faClone}/></span> Clone Chat
|
||||||
|
</a>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
<a href={'#'}
|
||||||
|
class="dropdown-item"
|
||||||
|
on:click|preventDefault={() => { showChatMenu = false; exportChatAsJSON(chatId) }}
|
||||||
|
>
|
||||||
|
<span><Fa icon={faDownload}/></span> Save Chat
|
||||||
|
</a>
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { showChatMenu = false; chatFileInput.click() }}>
|
||||||
|
<span><Fa icon={faUpload}/></span> Load Chat
|
||||||
|
</a>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={()=>{applyProfile(chatId, '', true);closeSettings()}}>
|
||||||
|
<span><Fa icon={faRotateRight}/></span> Restart Chat
|
||||||
|
</a>
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={()=>{showChatMenu = false;clearMessages(chatId)}}>
|
||||||
|
<span><Fa icon={faEraser}/></span> Clear Chat Messages
|
||||||
|
</a>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={()=>{showChatMenu = false;deleteChat()}}>
|
||||||
|
<span><Fa icon={faTrash}/></span> Delete Chat
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <button class="button is-warning" on:click={() => { clearMessages(chatId); window.location.reload() }}><span class="greyscale mr-2"><Fa icon={faTrash} /></span> Clear messages</button> -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
@ -720,6 +785,7 @@
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
closeSettings()
|
closeSettings()
|
||||||
closeChatNameSettings()
|
closeChatNameSettings()
|
||||||
|
showChatMenu = false
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -755,7 +821,6 @@
|
||||||
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { showProfileMenu = false; profileFileInput.click() }}>
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { showProfileMenu = false; profileFileInput.click() }}>
|
||||||
<span><Fa icon={faUpload}/></span> Import Profile
|
<span><Fa icon={faUpload}/></span> Import Profile
|
||||||
</a>
|
</a>
|
||||||
<input style="display:none" type="file" accept=".json" on:change={(e) => importProfileFromFile(e)} bind:this={profileFileInput} >
|
|
||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
<a href={'#'} class="dropdown-item" on:click|preventDefault={pinDefaultProfile}>
|
<a href={'#'} class="dropdown-item" on:click|preventDefault={pinDefaultProfile}>
|
||||||
<span><Fa icon={faThumbtack}/></span> Set as Default Profile
|
<span><Fa icon={faThumbtack}/></span> Set as Default Profile
|
||||||
|
@ -787,7 +852,7 @@
|
||||||
title="{setting.title}"
|
title="{setting.title}"
|
||||||
class="checkbox"
|
class="checkbox"
|
||||||
id="settings-{setting.key}"
|
id="settings-{setting.key}"
|
||||||
checked={getChatSettingValue(chatId, setting)}
|
checked={!!chatSettings[setting.key]}
|
||||||
on:click={e => queueSettingValueChange(e, setting)}
|
on:click={e => queueSettingValueChange(e, setting)}
|
||||||
>
|
>
|
||||||
{setting.name}
|
{setting.name}
|
||||||
|
@ -802,7 +867,7 @@
|
||||||
rows="1"
|
rows="1"
|
||||||
on:input={e => autoGrowInputOnEvent(e)}
|
on:input={e => autoGrowInputOnEvent(e)}
|
||||||
on:change={e => { queueSettingValueChange(e, setting); autoGrowInputOnEvent(e) }}
|
on:change={e => { queueSettingValueChange(e, setting); autoGrowInputOnEvent(e) }}
|
||||||
>{getChatSettingValue(chatId, setting)}</textarea>
|
>{chatSettings[setting.key]}</textarea>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
|
@ -818,18 +883,18 @@
|
||||||
type={setting.type}
|
type={setting.type}
|
||||||
title="{setting.title}"
|
title="{setting.title}"
|
||||||
id="settings-{setting.key}"
|
id="settings-{setting.key}"
|
||||||
value="{getChatSettingValue(chatId, setting)}"
|
value={chatSettings[setting.key]}
|
||||||
min={setting.min}
|
min={setting.min}
|
||||||
max={setting.max}
|
max={setting.max}
|
||||||
step={setting.step}
|
step={setting.step}
|
||||||
placeholder={String(setting.default)}
|
placeholder={String(setting.placeholder)}
|
||||||
on:change={e => queueSettingValueChange(e, setting)}
|
on:change={e => queueSettingValueChange(e, setting)}
|
||||||
/>
|
/>
|
||||||
{:else if setting.type === 'select'}
|
{:else if setting.type === 'select'}
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select id="settings-{setting.key}" title="{setting.title}" on:change={e => queueSettingValueChange(e, setting) } >
|
<select id="settings-{setting.key}" title="{setting.title}" on:change={e => queueSettingValueChange(e, setting) } >
|
||||||
{#each setting.options as option}
|
{#each setting.options as option}
|
||||||
<option value={option.value} selected={option.value === getChatSettingValue(chatId, setting)}>{option.text}</option>
|
<option value={option.value} selected={option.value === chatSettings[setting.key]}>{option.text}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -839,7 +904,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
title="{setting.title}"
|
title="{setting.title}"
|
||||||
class="input"
|
class="input"
|
||||||
value={getChatSettingValue(chatId, setting)}
|
value={chatSettings[setting.key]}
|
||||||
on:change={e => { queueSettingValueChange(e, setting) }}
|
on:change={e => { queueSettingValueChange(e, setting) }}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -859,6 +924,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<input style="display:none" type="file" accept=".json" on:change={(e) => importChatFromFile(e)} bind:this={chatFileInput} >
|
||||||
|
<input style="display:none" type="file" accept=".json" on:change={(e) => importProfileFromFile(e)} bind:this={profileFileInput} >
|
||||||
|
|
||||||
<!-- rename modal -->
|
<!-- rename modal -->
|
||||||
<form class="modal" bind:this={chatNameSettings} on:submit={saveChatNameSettings}>
|
<form class="modal" bind:this={chatNameSettings} on:submit={saveChatNameSettings}>
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Code from './Code.svelte'
|
import Code from './Code.svelte'
|
||||||
import { createEventDispatcher, onMount } from 'svelte'
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
import { deleteMessage, getChatSettingValueByKey } from './Storage.svelte'
|
import { deleteMessage, chatsStorage } from './Storage.svelte'
|
||||||
import { getPrice } from './Stats.svelte'
|
import { getPrice } from './Stats.svelte'
|
||||||
import SvelteMarkdown from 'svelte-markdown'
|
import SvelteMarkdown from 'svelte-markdown'
|
||||||
import type { Message, Model } from './Types.svelte'
|
import type { Message, Model, Chat } from './Types.svelte'
|
||||||
import Fa from 'svelte-fa/src/fa.svelte'
|
import Fa from 'svelte-fa/src/fa.svelte'
|
||||||
import { faTrash, faDiagramPredecessor, faDiagramNext } from '@fortawesome/free-solid-svg-icons/index'
|
import { faTrash, faDiagramPredecessor, faDiagramNext } from '@fortawesome/free-solid-svg-icons/index'
|
||||||
|
|
||||||
export let message:Message
|
export let message:Message
|
||||||
export let chatId:number
|
export let chatId:number
|
||||||
|
|
||||||
|
|
||||||
|
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
||||||
|
$: chatSettings = chat.settings
|
||||||
|
|
||||||
// Marked options
|
// Marked options
|
||||||
const markedownOptions = {
|
const markedownOptions = {
|
||||||
gfm: true, // Use GitHub Flavored Markdown
|
gfm: true, // Use GitHub Flavored Markdown
|
||||||
|
@ -26,7 +30,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
original = message.content
|
original = message.content
|
||||||
defaultModel = getChatSettingValueByKey(chatId, 'model')
|
defaultModel = chatSettings.model as any
|
||||||
noEdit = message.summarized
|
noEdit = message.summarized
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// Iterate messages
|
// Iterate messages
|
||||||
import type { Message } from './Types.svelte'
|
import type { Message, Chat } from './Types.svelte'
|
||||||
import { getChatSettingValueByKey } from './Storage.svelte'
|
import { chatsStorage } from './Storage.svelte'
|
||||||
import EditMessage from './EditMessage.svelte'
|
import EditMessage from './EditMessage.svelte'
|
||||||
|
|
||||||
export let messages : Message[]
|
export let messages : Message[]
|
||||||
export let chatId
|
export let chatId
|
||||||
|
|
||||||
|
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
||||||
|
$: chatSettings = chat.settings
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each messages as message, i}
|
{#each messages as message, i}
|
||||||
{#if !(i === 0 && message.role === 'system' && !getChatSettingValueByKey(chatId, 'useSystemPrompt'))}
|
{#if !(i === 0 && message.role === 'system' && !chatSettings.useSystemPrompt)}
|
||||||
<EditMessage bind:message={message} chatId={chatId} />
|
<EditMessage bind:message={message} chatId={chatId} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
import { getChatSettingByKey, getGlobalSettingByKey } from './Settings.svelte'
|
import copy from 'copy-to-clipboard';
|
||||||
|
import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
|
||||||
// Profile definitions
|
// Profile definitions
|
||||||
import { addMessage, clearMessages, getChatSettingValueByKey, getCustomProfiles, getMessages, setChatSettingValue, setChatSettingValueByKey, setGlobalSettingValueByKey } from './Storage.svelte'
|
import { addMessage, clearMessages, getChatSettings, getCustomProfiles, getGlobalSettings, getMessages, resetChatSettings, setChatSettingValue, setChatSettingValueByKey, setGlobalSettingValueByKey } from './Storage.svelte'
|
||||||
import type { Message, SelectOption, ChatSettings } from './Types.svelte'
|
import type { Message, SelectOption, ChatSettings } from './Types.svelte'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
const defaultProfile = 'default'
|
const defaultProfile = 'default'
|
||||||
|
|
||||||
|
const chatDefaults = getChatDefaults()
|
||||||
|
|
||||||
export const isStaticProfile = (key:string):boolean => {
|
export const isStaticProfile = (key:string):boolean => {
|
||||||
return !!profiles[key]
|
return !!profiles[key]
|
||||||
}
|
}
|
||||||
|
@ -34,64 +37,40 @@ export const getProfileSelect = ():SelectOption[] => {
|
||||||
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[getGlobalSettingByKey('defaultProfile') as any] ||
|
allProfiles[getGlobalSettings().defaultProfile||''] ||
|
||||||
profiles[defaultProfile] ||
|
profiles[defaultProfile] ||
|
||||||
profiles[Object.keys(profiles)[0]]
|
profiles[Object.keys(profiles)[0]]
|
||||||
return 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=>{
|
||||||
|
delete clone[k]
|
||||||
|
})
|
||||||
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prepareProfilePrompt = (chatId:number) => {
|
export const prepareProfilePrompt = (chatId:number) => {
|
||||||
const characterName = getChatSettingValueByKey(chatId, 'characterName')
|
const settings = getChatSettings(chatId)
|
||||||
const currentProfilePrompt = getChatSettingValueByKey(chatId, 'systemPrompt')
|
const characterName = settings.characterName
|
||||||
|
const currentProfilePrompt = settings.systemPrompt
|
||||||
return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName)
|
return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prepareSummaryPrompt = (chatId:number, promptsSize:number) => {
|
export const prepareSummaryPrompt = (chatId:number, promptsSize:number) => {
|
||||||
const characterName = getChatSettingValueByKey(chatId, 'characterName') || 'ChatGPT'
|
const settings = getChatSettings(chatId)
|
||||||
let maxTokens:number = getChatSettingValueByKey(chatId, 'summarySize')
|
const characterName = settings.characterName || 'ChatGPT'
|
||||||
|
let maxTokens:number = 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 = getChatSettingValueByKey(chatId, '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)) // ~.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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Apply currently selected profile
|
||||||
* Check if there has been activity/changes on the current session
|
export const applyProfile = (chatId:number, key?:string, resetChat:boolean = false) => {
|
||||||
* @param chatId
|
const settings = getChatSettings(chatId)
|
||||||
*/
|
const profile = getProfile(key || settings.profile)
|
||||||
export const checkSessionActivity = (chatId:number):boolean => {
|
resetChatSettings(chatId, resetChat) // Fully reset
|
||||||
const messages = getMessages(chatId)
|
if (!resetChat) return
|
||||||
if (messages.length === 0) return false
|
|
||||||
const useSystemPrompt = getChatSettingValueByKey(chatId, 'useSystemPrompt')
|
|
||||||
if (useSystemPrompt && messages[0].content !== getChatSettingValueByKey(chatId, 'systemPrompt')) return true
|
|
||||||
const trainingPrompts = getChatSettingValueByKey(chatId, 'trainingPrompts') || []
|
|
||||||
const messageStart = useSystemPrompt ? 1 : 0
|
|
||||||
let profileMessageLen = trainingPrompts.length
|
|
||||||
profileMessageLen += messageStart
|
|
||||||
if (messages.length - profileMessageLen > 1) return true
|
|
||||||
if (messages.length - profileMessageLen < 0) return false
|
|
||||||
for (let i = messageStart, l = messages.length; i < l; i++) {
|
|
||||||
const tpa = trainingPrompts[i]
|
|
||||||
const tpb = messages[i]
|
|
||||||
if (!tpa) return i + 1 !== l // allow one additional message
|
|
||||||
if (tpa.content !== tpb.content) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export const applyProfile = (chatId:number, key:string, resetChat:boolean = false) => {
|
|
||||||
const profile = getProfile(key)
|
|
||||||
Object.entries(profile).forEach(([k, v]) => {
|
|
||||||
const setting = getChatSettingByKey(k as any)
|
|
||||||
if (setting) setChatSettingValue(chatId, setting as any, v)
|
|
||||||
})
|
|
||||||
if (!resetChat && getMessages(chatId).length) {
|
|
||||||
// If there are any messages, just apply settings, don't apply chat messages
|
|
||||||
setChatSettingValueByKey(chatId, 'startSession', false)
|
|
||||||
setGlobalSettingValueByKey('lastProfile', key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Clear current messages
|
// Clear current messages
|
||||||
clearMessages(chatId)
|
clearMessages(chatId)
|
||||||
// Add the system prompt
|
// Add the system prompt
|
||||||
|
@ -109,7 +88,7 @@ export const applyProfile = (chatId:number, key:string, resetChat:boolean = fals
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Set to auto-start if we should
|
// Set to auto-start if we should
|
||||||
setChatSettingValueByKey(chatId, 'startSession', getChatSettingValueByKey(chatId, 'autoStartSession'))
|
setChatSettingValueByKey(chatId, 'startSession', settings.autoStartSession)
|
||||||
// Mark mark this as last used
|
// Mark mark this as last used
|
||||||
setGlobalSettingValueByKey('lastProfile', key)
|
setGlobalSettingValueByKey('lastProfile', key)
|
||||||
}
|
}
|
||||||
|
@ -157,6 +136,7 @@ Give no explanations.`
|
||||||
const profiles:Record<string, ChatSettings> = {
|
const profiles:Record<string, ChatSettings> = {
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
...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.',
|
||||||
|
@ -168,6 +148,7 @@ const profiles:Record<string, ChatSettings> = {
|
||||||
},
|
},
|
||||||
|
|
||||||
ChatGPT: {
|
ChatGPT: {
|
||||||
|
...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.',
|
||||||
|
@ -179,6 +160,7 @@ const profiles:Record<string, ChatSettings> = {
|
||||||
},
|
},
|
||||||
|
|
||||||
marvin: {
|
marvin: {
|
||||||
|
...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',
|
||||||
|
@ -188,8 +170,9 @@ const profiles:Record<string, ChatSettings> = {
|
||||||
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: [],
|
||||||
}
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set keys for static profiles
|
// Set keys for static profiles
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
import { applyProfile } from './Profiles.svelte'
|
import { applyProfile } from './Profiles.svelte'
|
||||||
import { getChatSettingValue, getChatSettingValueByKey } from './Storage.svelte'
|
import { getChatSettings } from './Storage.svelte';
|
||||||
|
import { encode } from 'gpt-tokenizer'
|
||||||
// Setting definitions
|
// Setting definitions
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -8,14 +9,19 @@ import {
|
||||||
type ChatSetting,
|
type ChatSetting,
|
||||||
type SettingSelect,
|
type SettingSelect,
|
||||||
type GlobalSetting,
|
type GlobalSetting,
|
||||||
type GlobalSettings
|
type GlobalSettings,
|
||||||
|
type Request
|
||||||
} from './Types.svelte'
|
} from './Types.svelte'
|
||||||
|
|
||||||
export const getChatSettingList = (): ChatSetting[] => {
|
export const getChatSettingList = (): ChatSetting[] => {
|
||||||
return chatSettingsList
|
return chatSettingsList
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getChatSettingByKey = (key: keyof ChatSettings): ChatSetting => {
|
export const getRequestSettingList = (): ChatSetting[] => {
|
||||||
|
return chatSettingsList.filter(s => s.key in gptDefaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getChatSettingObjectByKey = (key: keyof ChatSettings): ChatSetting => {
|
||||||
const result = chatSettingLookup[key]
|
const result = chatSettingLookup[key]
|
||||||
if (!result) console.error(`Chat Setting "${key}" not defined in Settings array.`)
|
if (!result) console.error(`Chat Setting "${key}" not defined in Settings array.`)
|
||||||
return result
|
return result
|
||||||
|
@ -25,24 +31,79 @@ export const getGlobalSettingList = (): GlobalSetting[] => {
|
||||||
return globalSettingsList
|
return globalSettingsList
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getGlobalSettingByKey = (key: keyof GlobalSettings): GlobalSetting => {
|
export const getGlobalSettingObjectByKey = (key: keyof GlobalSettings): GlobalSetting => {
|
||||||
return globalSettingLookup[key]
|
return globalSettingLookup[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getRequestDefaults = ():Request => {
|
||||||
|
return gptDefaults
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getChatDefaults = ():ChatSettings => {
|
||||||
|
return defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExcludeFromProfile = () => {
|
||||||
|
return excludeFromProfile
|
||||||
|
}
|
||||||
|
|
||||||
|
const gptDefaults: Request = Object.freeze({
|
||||||
|
model: 'gpt-3.5-turbo-0301',
|
||||||
|
messages: [],
|
||||||
|
temperature: 1,
|
||||||
|
top_p: 1,
|
||||||
|
n: 1,
|
||||||
|
stream: false,
|
||||||
|
stop: null,
|
||||||
|
max_tokens: 128,
|
||||||
|
presence_penalty: 0,
|
||||||
|
frequency_penalty: 0,
|
||||||
|
logit_bias: null,
|
||||||
|
user: undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Core set of defaults
|
||||||
|
const defaults:ChatSettings = Object.freeze({
|
||||||
|
...gptDefaults,
|
||||||
|
profile: '',
|
||||||
|
characterName: 'ChatGPT',
|
||||||
|
profileName: '',
|
||||||
|
profileDescription: '',
|
||||||
|
useSummarization: false,
|
||||||
|
summaryThreshold: 3000,
|
||||||
|
summarySize: 1000,
|
||||||
|
pinTop: 0,
|
||||||
|
pinBottom: 6,
|
||||||
|
summaryPrompt: '',
|
||||||
|
useSystemPrompt: false,
|
||||||
|
systemPrompt: '',
|
||||||
|
autoStartSession: false,
|
||||||
|
trainingPrompts: [],
|
||||||
|
// There are chat session state variables, and really don't belong here
|
||||||
|
// But it was easier to just put them here.
|
||||||
|
startSession: false, // Should the session start automatically
|
||||||
|
sessionStarted: false, // Has the session started (user made a first request)
|
||||||
|
})
|
||||||
|
|
||||||
|
const excludeFromProfile = {
|
||||||
|
messages: true,
|
||||||
|
startSession: true,
|
||||||
|
sessionStarted: true,
|
||||||
|
user: true,
|
||||||
|
}
|
||||||
|
|
||||||
const profileSetting: ChatSetting & SettingSelect = {
|
const profileSetting: ChatSetting & SettingSelect = {
|
||||||
key: 'profile',
|
key: 'profile',
|
||||||
name: 'Profile',
|
name: 'Profile',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'Choose how you want your assistant to act.',
|
title: 'Choose how you want your assistant to act.',
|
||||||
header: 'Profile / Presets',
|
header: 'Profile / Presets',
|
||||||
headerClass: 'is-info',
|
headerClass: 'is-info',
|
||||||
options: [], // Set by Profiles
|
options: [], // Set by Profiles
|
||||||
type: 'select',
|
type: 'select',
|
||||||
afterChange: (chatId, setting) => {
|
afterChange: (chatId, setting) => {
|
||||||
applyProfile(chatId, getChatSettingValue(chatId, setting))
|
applyProfile(chatId, '', !getChatSettings(chatId).sessionStarted)
|
||||||
return true // Signal we should refresh the setting modal
|
return true // Signal we should refresh the setting modal
|
||||||
},
|
},
|
||||||
noRequest: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings that will not be part of the API request
|
// Settings that will not be part of the API request
|
||||||
|
@ -51,75 +112,59 @@ const nonRequestSettings: ChatSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'profileName',
|
key: 'profileName',
|
||||||
name: 'Profile Name',
|
name: 'Profile Name',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'How this profile is displayed in the select list.',
|
title: 'How this profile is displayed in the select list.',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
noRequest: true // not part of request API
|
|
||||||
// hide: (chatId) => { return !getChatSettingValueByKey(chatId, 'useSystemPrompt') }
|
// hide: (chatId) => { return !getChatSettingValueByKey(chatId, 'useSystemPrompt') }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'profileDescription',
|
key: 'profileDescription',
|
||||||
name: 'Description',
|
name: 'Description',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'How this profile is displayed in the select list.',
|
title: 'How this profile is displayed in the select list.',
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
noRequest: true // not part of request API
|
|
||||||
// hide: (chatId) => { return !getChatSettingValueByKey(chatId, 'useSystemPrompt') }
|
// hide: (chatId) => { return !getChatSettingValueByKey(chatId, 'useSystemPrompt') }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'useSystemPrompt',
|
key: 'useSystemPrompt',
|
||||||
name: 'Use Profile/System Prompt',
|
name: 'Use Profile/System Prompt',
|
||||||
default: false,
|
|
||||||
title: 'Send a "System" prompt as the first prompt.',
|
title: 'Send a "System" prompt as the first prompt.',
|
||||||
header: 'System Prompt',
|
header: 'System Prompt',
|
||||||
headerClass: 'is-info',
|
headerClass: 'is-info',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
noRequest: true // not part of request API
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'characterName',
|
key: 'characterName',
|
||||||
name: 'Character Name',
|
name: 'Character Name',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'What the personality of this profile will be called.',
|
title: 'What the personality of this profile will be called.',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSystemPrompt,
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSystemPrompt')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'systemPrompt',
|
key: 'systemPrompt',
|
||||||
name: 'System Prompt',
|
name: 'System Prompt',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'First prompt to send.',
|
title: 'First prompt to send.',
|
||||||
placeholder: 'Enter the first prompt to send here.',
|
placeholder: 'Enter the first prompt to send here.',
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSystemPrompt
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSystemPrompt')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'trainingPrompts',
|
key: 'trainingPrompts',
|
||||||
name: 'Training Prompts',
|
name: 'Training Prompts',
|
||||||
title: 'Prompts used to train.',
|
title: 'Prompts used to train.',
|
||||||
default: null,
|
|
||||||
type: 'other',
|
type: 'other',
|
||||||
noRequest: true, // not part of request API
|
|
||||||
hide: (chatId) => true
|
hide: (chatId) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'autoStartSession',
|
key: 'autoStartSession',
|
||||||
name: 'Auto-Start Session',
|
name: 'Auto-Start Session',
|
||||||
default: false,
|
|
||||||
title: 'If possible, auto-start the chat session, sending a system prompt to get an initial response.',
|
title: 'If possible, auto-start the chat session, sending a system prompt to get an initial response.',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSystemPrompt
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSystemPrompt')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'startSession',
|
key: 'startSession',
|
||||||
name: 'Auto-Start Trigger',
|
name: 'Auto-Start Trigger',
|
||||||
default: false,
|
|
||||||
title: '',
|
title: '',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
noRequest: true, // not part of request API
|
|
||||||
hide: (chatId) => true
|
hide: (chatId) => true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,83 +172,70 @@ const nonRequestSettings: ChatSetting[] = [
|
||||||
name: 'Enable Auto Summarize',
|
name: 'Enable Auto Summarize',
|
||||||
header: 'Continuous Chat - Summarization',
|
header: 'Continuous Chat - Summarization',
|
||||||
headerClass: 'is-info',
|
headerClass: 'is-info',
|
||||||
default: false,
|
|
||||||
title: 'When out of token space, summarize past tokens and keep going.',
|
title: 'When out of token space, summarize past tokens and keep going.',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
noRequest: true // not part of request API
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'summaryThreshold',
|
key: 'summaryThreshold',
|
||||||
name: 'Summary Threshold',
|
name: 'Summary Threshold',
|
||||||
default: 3000,
|
|
||||||
title: 'When prompt history breaks this threshold, past prompts will be summarized to create space. 0 to disable.',
|
title: 'When prompt history breaks this threshold, past prompts will be summarized to create space. 0 to disable.',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 32000,
|
max: 32000,
|
||||||
step: 1,
|
step: 1,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSummarization!
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSummarization')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'summarySize',
|
key: 'summarySize',
|
||||||
name: 'Max Summary Size',
|
name: 'Max Summary Size',
|
||||||
default: 512,
|
|
||||||
title: 'Maximum number of tokens to use for summarization response.',
|
title: 'Maximum number of tokens to use for summarization response.',
|
||||||
min: 128,
|
min: 128,
|
||||||
max: 2048,
|
max: 2048,
|
||||||
step: 1,
|
step: 1,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSummarization!
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSummarization')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'pinTop',
|
key: 'pinTop',
|
||||||
name: 'Keep First Prompts During Summary',
|
name: 'Keep First Prompts During Summary',
|
||||||
default: 0,
|
|
||||||
title: 'When we run out of space and need to summarize prompts, the top number of prompts will not be removed after summarization.',
|
title: 'When we run out of space and need to summarize prompts, the top number of prompts will not be removed after summarization.',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 4,
|
max: 4,
|
||||||
step: 1,
|
step: 1,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSummarization!
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSummarization')
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'pinBottom',
|
key: 'pinBottom',
|
||||||
name: 'Exclude Bottom Prompts From Summary',
|
name: 'Exclude Bottom Prompts From Summary',
|
||||||
default: 6,
|
|
||||||
title: 'When we run out of space and need to summarize prompts, do not summarize the the last number prompts you set here.',
|
title: 'When we run out of space and need to summarize prompts, do not summarize the the last number prompts you set here.',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 20, // Will be auto adjusted down if needs more
|
max: 20, // Will be auto adjusted down if needs more
|
||||||
step: 1,
|
step: 1,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSummarization!
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSummarization')
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'summaryPrompt',
|
key: 'summaryPrompt',
|
||||||
name: 'Summary Generation Prompt',
|
name: 'Summary Generation Prompt',
|
||||||
default: '', // Set by Profiles
|
|
||||||
title: 'A prompt used to summarize past prompts.',
|
title: 'A prompt used to summarize past prompts.',
|
||||||
placeholder: 'Enter a prompt that will be used to summarize past prompts here.',
|
placeholder: 'Enter a prompt that will be used to summarize past prompts here.',
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
noRequest: true, // not part of request API
|
hide: (chatId) => !getChatSettings(chatId).useSummarization!
|
||||||
hide: (chatId) => !getChatSettingValueByKey(chatId, 'useSummarization')
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const modelSetting: ChatSetting & SettingSelect = {
|
const modelSetting: ChatSetting & SettingSelect = {
|
||||||
key: 'model',
|
key: 'model',
|
||||||
name: 'Model',
|
name: 'Model',
|
||||||
default: 'gpt-3.5-turbo-0301',
|
|
||||||
title: 'The model to use - GPT-3.5 is cheaper, but GPT-4 is more powerful.',
|
title: 'The model to use - GPT-3.5 is cheaper, but GPT-4 is more powerful.',
|
||||||
header: 'Below are the settings that OpenAI allows to be changed for the API calls. See the <a target="_blank" href="https://platform.openai.com/docs/api-reference/chat/create">OpenAI API docs</a> for more details.',
|
header: 'Below are the settings that OpenAI allows to be changed for the API calls. See the <a target="_blank" href="https://platform.openai.com/docs/api-reference/chat/create">OpenAI API docs</a> for more details.',
|
||||||
headerClass: 'is-warning',
|
headerClass: 'is-warning',
|
||||||
options: [],
|
options: [],
|
||||||
type: 'select',
|
type: 'select',
|
||||||
required: true
|
forceApi: true, // Need to make sure we send this
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatSettingsList: ChatSetting[] = [
|
const chatSettingsList: ChatSetting[] = [
|
||||||
|
@ -212,7 +244,6 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'temperature',
|
key: 'temperature',
|
||||||
name: 'Sampling Temperature',
|
name: 'Sampling Temperature',
|
||||||
default: 1,
|
|
||||||
title: 'What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n' +
|
title: 'What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
'We generally recommend altering this or top_p but not both.',
|
'We generally recommend altering this or top_p but not both.',
|
||||||
|
@ -224,7 +255,6 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'top_p',
|
key: 'top_p',
|
||||||
name: 'Nucleus Sampling',
|
name: 'Nucleus Sampling',
|
||||||
default: 1,
|
|
||||||
title: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n' +
|
title: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
'We generally recommend altering this or temperature but not both',
|
'We generally recommend altering this or temperature but not both',
|
||||||
|
@ -236,7 +266,6 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'n',
|
key: 'n',
|
||||||
name: 'Number of Messages',
|
name: 'Number of Messages',
|
||||||
default: 1,
|
|
||||||
title: 'CAREFUL WITH THIS ONE: How many chat completion choices to generate for each input message. This can eat tokens.',
|
title: 'CAREFUL WITH THIS ONE: How many chat completion choices to generate for each input message. This can eat tokens.',
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 10,
|
max: 10,
|
||||||
|
@ -249,17 +278,15 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
title: 'The maximum number of tokens to generate in the completion.\n' +
|
title: 'The maximum number of tokens to generate in the completion.\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
'The token count of your prompt plus max_tokens cannot exceed the model\'s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096).\n',
|
'The token count of your prompt plus max_tokens cannot exceed the model\'s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096).\n',
|
||||||
default: 128,
|
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 32768,
|
max: 32768,
|
||||||
step: 1024,
|
step: 1024,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true // Since default here is different than gpt default, will make sure we always send it
|
forceApi: true // Since default here is different than gpt default, will make sure we always send it
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'presence_penalty',
|
key: 'presence_penalty',
|
||||||
name: 'Presence Penalty',
|
name: 'Presence Penalty',
|
||||||
default: 0,
|
|
||||||
title: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.',
|
title: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.',
|
||||||
min: -2,
|
min: -2,
|
||||||
max: 2,
|
max: 2,
|
||||||
|
@ -269,12 +296,43 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'frequency_penalty',
|
key: 'frequency_penalty',
|
||||||
name: 'Frequency Penalty',
|
name: 'Frequency Penalty',
|
||||||
default: 0,
|
|
||||||
title: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.',
|
title: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.',
|
||||||
min: -2,
|
min: -2,
|
||||||
max: 2,
|
max: 2,
|
||||||
step: 0.2,
|
step: 0.2,
|
||||||
type: 'number'
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// logit bias editor not implemented yet
|
||||||
|
key: 'logit_bias',
|
||||||
|
name: 'Logit Bias',
|
||||||
|
title: 'Allows you to adjust bias of tokens used in completion.',
|
||||||
|
header: `Logit Bias. See <a target="_blank" href="https://help.openai.com/en/articles/5247780-using-logit-bias-to-define-token-probability">this article</a> for more details.`,
|
||||||
|
type: 'other',
|
||||||
|
hide: () => true,
|
||||||
|
// transform to JSON for request, first converting word->weight pairs to token(s)->weight.
|
||||||
|
// -- care should be taken to have each word key in the each record formatted in a way where they
|
||||||
|
// only take one token each else you'll end up with results you probably don't want.
|
||||||
|
// Generally, leading space plus common lower case word will more often result in a single token
|
||||||
|
// See: https://platform.openai.com/tokenizer
|
||||||
|
apiTransform: (chatId, setting, val:Record<string, number>) => {
|
||||||
|
console.log('logit_bias', val, getChatSettings(chatId).logit_bias)
|
||||||
|
if (!val) return null
|
||||||
|
const tokenized:Record<number, number> = Object.entries(val).reduce((a,[k,v])=>{
|
||||||
|
const tokens:number[] = encode(k)
|
||||||
|
tokens.forEach(t => {a[t] = v})
|
||||||
|
return a
|
||||||
|
}, {} as Record<number, number>)
|
||||||
|
return tokenized
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Enable?
|
||||||
|
{
|
||||||
|
key: 'user',
|
||||||
|
name: 'User?',
|
||||||
|
title: 'Name of user?',
|
||||||
|
type: 'text',
|
||||||
|
hide: () => true,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -289,13 +347,11 @@ const globalSettingsList:GlobalSetting[] = [
|
||||||
{
|
{
|
||||||
key: 'lastProfile',
|
key: 'lastProfile',
|
||||||
name: 'Last Profile',
|
name: 'Last Profile',
|
||||||
default: 'default',
|
|
||||||
type: 'text'
|
type: 'text'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'defaultProfile',
|
key: 'defaultProfile',
|
||||||
name: 'Default Profile',
|
name: 'Default Profile',
|
||||||
default: 'default',
|
|
||||||
type: 'text'
|
type: 'text'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
import { persisted } from 'svelte-local-storage-store'
|
import { persisted } from 'svelte-local-storage-store'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting } from './Types.svelte'
|
import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting } from './Types.svelte'
|
||||||
import { getChatSettingByKey, getGlobalSettingByKey } from './Settings.svelte'
|
import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { isStaticProfile } from './Profiles.svelte'
|
import { applyProfile, getProfile, isStaticProfile } 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)
|
||||||
export const apiKeyStorage = persisted('apiKey', '' as string)
|
export const apiKeyStorage = persisted('apiKey', '' as string)
|
||||||
|
|
||||||
|
const chatDefaults = getChatDefaults()
|
||||||
|
|
||||||
export const newChatID = (): number => {
|
export const newChatID = (): number => {
|
||||||
const chats = get(chatsStorage)
|
const chats = get(chatsStorage)
|
||||||
const chatId = chats.reduce((maxId, chat) => Math.max(maxId, chat.id), 0) + 1
|
const chatId = chats.reduce((maxId, chat) => Math.max(maxId, chat.id), 0) + 1
|
||||||
|
@ -30,6 +32,8 @@
|
||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
chatsStorage.set(chats)
|
chatsStorage.set(chats)
|
||||||
|
// Apply defaults and prepare it to start
|
||||||
|
applyProfile(chatId,'', true)
|
||||||
return chatId
|
return chatId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +63,41 @@
|
||||||
return chatId
|
return chatId
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getChat = (chatId: number):Chat => {
|
// Make sure all chat settings are set with current values or defaults
|
||||||
|
export const updateChatSettings = (chatId) => {
|
||||||
const chats = get(chatsStorage)
|
const chats = get(chatsStorage)
|
||||||
return chats.find((chat) => chat.id === chatId) as Chat
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
|
if (!chat.settings) {
|
||||||
|
chat.settings = {} as ChatSettings
|
||||||
|
}
|
||||||
|
Object.entries(getChatDefaults()).forEach(([k,v]) => {
|
||||||
|
const val = chat.settings[k]
|
||||||
|
chat.settings[k] = (val === undefined || val === null ? v : chat.settings[k]) as any
|
||||||
|
})
|
||||||
|
// make sure old chat messages have UUID
|
||||||
|
chat.messages.forEach((m) => {
|
||||||
|
m.uuid = m.uuid || uuidv4()
|
||||||
|
})
|
||||||
|
chatsStorage.set(chats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all setting to current profile defaults
|
||||||
|
export const resetChatSettings = (chatId, resetAll:boolean = false) => {
|
||||||
|
const chats = get(chatsStorage)
|
||||||
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
|
const profile = getProfile(chat.settings.profile)
|
||||||
|
const exclude = getExcludeFromProfile()
|
||||||
|
if (resetAll) {
|
||||||
|
// Reset to base defaults first, then apply profile
|
||||||
|
Object.entries(getChatDefaults()).forEach(([k,v]) => {
|
||||||
|
chat.settings[k] = v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Object.entries(profile).forEach(([k,v]) => {
|
||||||
|
if (exclude[k]) return
|
||||||
|
chat.settings[k] = v
|
||||||
|
})
|
||||||
|
chatsStorage.set(chats)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clearChats = () => {
|
export const clearChats = () => {
|
||||||
|
@ -72,6 +108,16 @@
|
||||||
chatsStorage.set(chats)
|
chatsStorage.set(chats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getChat = (chatId: number):Chat => {
|
||||||
|
const chats = get(chatsStorage)
|
||||||
|
return chats.find((chat) => chat.id === chatId) as Chat
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getChatSettings = (chatId: number):ChatSettings => {
|
||||||
|
const chats = get(chatsStorage)
|
||||||
|
return (chats.find((chat) => chat.id === chatId) as Chat).settings
|
||||||
|
}
|
||||||
|
|
||||||
export const addMessage = (chatId: number, message: Message) => {
|
export const addMessage = (chatId: number, message: Message) => {
|
||||||
const chats = get(chatsStorage)
|
const chats = get(chatsStorage)
|
||||||
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
|
@ -134,23 +180,20 @@
|
||||||
i++
|
i++
|
||||||
cname = chat.name + `-${i}`
|
cname = chat.name + `-${i}`
|
||||||
}
|
}
|
||||||
|
const chatCopy = JSON.parse(JSON.stringify(chat))
|
||||||
|
|
||||||
// Find the max chatId
|
// Set the ID
|
||||||
const newId = newChatID()
|
chatCopy.id = newChatID()
|
||||||
|
|
||||||
// Add a new chat
|
// Add a new chat
|
||||||
chats.push({
|
chats.push(chatCopy)
|
||||||
id: newId,
|
|
||||||
name: cname,
|
|
||||||
settings: JSON.parse(JSON.stringify(chat.settings)),
|
|
||||||
messages: JSON.parse(JSON.stringify(chat.messages))
|
|
||||||
})
|
|
||||||
// chatsStorage
|
// chatsStorage
|
||||||
chatsStorage.set(chats)
|
chatsStorage.set(chats)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cleanSettingValue = (chatId, setting:(GlobalSetting | ChatSetting), value: any) => {
|
export const cleanSettingValue = (type:string, value: any) => {
|
||||||
switch (setting.type) {
|
switch (type) {
|
||||||
case 'number':
|
case 'number':
|
||||||
value = parseFloat(value)
|
value = parseFloat(value)
|
||||||
if (isNaN(value)) { value = null }
|
if (isNaN(value)) { value = null }
|
||||||
|
@ -163,68 +206,52 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setGlobalSettingValueByKey = (key: keyof GlobalSettings, value) => {
|
|
||||||
return setGlobalSettingValue(getGlobalSettingByKey(key), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setGlobalSettingValue = (setting: GlobalSetting, value) => {
|
|
||||||
const store = get(globalStorage)
|
|
||||||
store[setting.key] = cleanSettingValue(0, setting, value)
|
|
||||||
globalStorage.set(store)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setChatSettingValueByKey = (chatId: number, key: keyof ChatSettings, value) => {
|
export const setChatSettingValueByKey = (chatId: number, key: keyof ChatSettings, value) => {
|
||||||
return setChatSettingValue(chatId, getChatSettingByKey(key), value)
|
const setting = getChatSettingObjectByKey(key)
|
||||||
|
if (setting) return setChatSettingValue(chatId, setting, value)
|
||||||
|
if (!(key in chatDefaults)) throw new Error('Invalid chat setting: ' + key)
|
||||||
|
const d = chatDefaults[key]
|
||||||
|
if (d === null || d === undefined) throw new Error('Unable to determine setting type for "'
|
||||||
|
+ key +' from default of "' + d + '"')
|
||||||
|
const chats = get(chatsStorage)
|
||||||
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
|
let settings = chat.settings as any
|
||||||
|
settings[key] = cleanSettingValue(typeof d, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setChatSettingValue = (chatId: number, setting: ChatSetting, value) => {
|
export const setChatSettingValue = (chatId: number, setting: ChatSetting, value) => {
|
||||||
const chats = get(chatsStorage)
|
const chats = get(chatsStorage)
|
||||||
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
let settings:ChatSettings = chat.settings
|
let settings = chat.settings as any
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
settings = {} as ChatSettings
|
settings = {} as ChatSettings
|
||||||
chat.settings = settings
|
chat.settings = settings
|
||||||
}
|
}
|
||||||
if (typeof setting.setFilter === 'function') value = setting.setFilter(chatId, setting, value)
|
settings[setting.key] = cleanSettingValue(setting.type, value)
|
||||||
settings[setting.key] = cleanSettingValue(chatId, setting, value)
|
|
||||||
chatsStorage.set(chats)
|
chatsStorage.set(chats)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getGlobalSettingValueNullDefault = (setting: GlobalSetting) => {
|
|
||||||
const store = get(globalStorage)
|
|
||||||
let value = store && store[setting.key] as any
|
|
||||||
value = (value === undefined) ? null : value
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getGlobalSettingValue = (setting: GlobalSetting) => {
|
|
||||||
let value = getGlobalSettingValueNullDefault(setting)
|
|
||||||
if (value === null) value = setting.default
|
|
||||||
return value as any
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getGlobalSettingValueByKey = (key: keyof GlobalSettings) => {
|
|
||||||
return getGlobalSettingValue(getGlobalSettingByKey(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getChatSettingValueNullDefault = (chatId: number, setting: ChatSetting):any => {
|
export const getChatSettingValueNullDefault = (chatId: number, setting: ChatSetting):any => {
|
||||||
const chats = get(chatsStorage)
|
const chats = get(chatsStorage)
|
||||||
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
const chat = chats.find((chat) => chat.id === chatId) as Chat
|
||||||
let value = chat.settings && chat.settings[setting.key]
|
let value = chat.settings && chat.settings[setting.key]
|
||||||
value = (value === undefined) ? null : value
|
value = (value === undefined) ? null : value
|
||||||
if (value === setting.default) value = null
|
if (!setting.forceApi && value === chatDefaults[setting.key]) value = null
|
||||||
if (typeof setting.getFilter === 'function') value = setting.getFilter(chatId, setting, value)
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getChatSettingValue = (chatId: number, setting: ChatSetting):any => {
|
export const setGlobalSettingValueByKey = (key: keyof GlobalSettings, value) => {
|
||||||
let value = getChatSettingValueNullDefault(chatId, setting)
|
return setGlobalSettingValue(getGlobalSettingObjectByKey(key), value)
|
||||||
if (value === null) value = setting.default
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getChatSettingValueByKey = (chatId: number, key: keyof ChatSettings):any => {
|
export const setGlobalSettingValue = (setting: GlobalSetting, value) => {
|
||||||
return getChatSettingValue(chatId, getChatSettingByKey(key)) as any
|
const store = get(globalStorage)
|
||||||
|
store[setting.key] = cleanSettingValue(setting.type, value)
|
||||||
|
globalStorage.set(store)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getGlobalSettings = ():GlobalSettings => {
|
||||||
|
return get(globalStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCustomProfiles = ():Record<string, ChatSettings> => {
|
export const getCustomProfiles = ():Record<string, ChatSettings> => {
|
||||||
|
@ -269,7 +296,11 @@
|
||||||
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.')
|
||||||
}
|
}
|
||||||
profiles[profile.profile as string] = 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=>{
|
||||||
|
delete clone[k]
|
||||||
|
})
|
||||||
|
profiles[profile.profile as string] = clone
|
||||||
globalStorage.set(store)
|
globalStorage.set(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,26 +41,27 @@
|
||||||
max_tokens?: number;
|
max_tokens?: number;
|
||||||
presence_penalty?: number;
|
presence_penalty?: number;
|
||||||
frequency_penalty?: number;
|
frequency_penalty?: number;
|
||||||
logit_bias?: Record<string, any>;
|
logit_bias?: Record<string, any> | null;
|
||||||
user?: string;
|
user?: string;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChatSettings = {
|
export type ChatSettings = {
|
||||||
profile?: string,
|
profile: string,
|
||||||
characterName?: string,
|
characterName: string,
|
||||||
profileName?: string,
|
profileName: string,
|
||||||
profileDescription?: string,
|
profileDescription: string,
|
||||||
useSummarization?: boolean;
|
useSummarization: boolean;
|
||||||
summaryThreshold?: number;
|
summaryThreshold: number;
|
||||||
summarySize?: number;
|
summarySize: number;
|
||||||
pinTop?: number,
|
pinTop: number;
|
||||||
pinBottom?: number,
|
pinBottom: number;
|
||||||
summaryPrompt?: string;
|
summaryPrompt: string;
|
||||||
useSystemPrompt?: boolean;
|
useSystemPrompt: boolean;
|
||||||
systemPrompt?: string;
|
systemPrompt: string;
|
||||||
autoStartSession?: boolean;
|
autoStartSession: boolean;
|
||||||
startSession?: false;
|
startSession: false;
|
||||||
|
sessionStarted: false;
|
||||||
trainingPrompts?: Message[];
|
trainingPrompts?: Message[];
|
||||||
} & Request;
|
} & Request;
|
||||||
|
|
||||||
|
@ -111,57 +112,50 @@
|
||||||
|
|
||||||
type SettingNumber = {
|
type SettingNumber = {
|
||||||
type: 'number';
|
type: 'number';
|
||||||
default: number;
|
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
step: number;
|
step: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingBoolean = {
|
|
||||||
type: 'boolean';
|
|
||||||
default: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectOption = {
|
export type SelectOption = {
|
||||||
value: string;
|
value: string;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SettingBoolean = {
|
||||||
|
type: 'boolean';
|
||||||
|
};
|
||||||
|
|
||||||
export type SettingSelect = {
|
export type SettingSelect = {
|
||||||
type: 'select';
|
type: 'select';
|
||||||
default: string;
|
|
||||||
options: SelectOption[];
|
options: SelectOption[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingText = {
|
export type SettingText = {
|
||||||
type: 'text';
|
type: 'text';
|
||||||
default: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingTextArea = {
|
export type SettingTextArea = {
|
||||||
type: 'textarea';
|
type: 'textarea';
|
||||||
lines?: number;
|
lines?: number;
|
||||||
default: string;
|
|
||||||
placeholder?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingOther = {
|
export type SettingOther = {
|
||||||
type: 'other';
|
type: 'other';
|
||||||
default: any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChatSetting = {
|
export type ChatSetting = {
|
||||||
key: keyof ChatSettings;
|
key: keyof ChatSettings;
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
required?: boolean; // force in request
|
forceApi?: boolean; // force in api requests, even if set to default
|
||||||
noRequest?: boolean; // exclude from request
|
|
||||||
hidden?: boolean; // Hide from setting menus
|
hidden?: boolean; // Hide from setting menus
|
||||||
header?: string;
|
header?: string;
|
||||||
headerClass?: string;
|
headerClass?: string;
|
||||||
|
placeholder?: string;
|
||||||
hide?: (number?) => boolean;
|
hide?: (number?) => boolean;
|
||||||
setFilter?: (number, ChatSetting?, any?) => any;
|
apiTransform?: (number, ChatSetting, any?) => any;
|
||||||
getFilter?: (number, ChatSetting?, any?) => any;
|
beforeChange?: (number, ChatSetting?, any?) => boolean;
|
||||||
afterChange?: (number, ChatSetting?, any?) => boolean;
|
afterChange?: (number, ChatSetting?, any?) => boolean;
|
||||||
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingTextArea | SettingOther);
|
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingTextArea | SettingOther);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue