Allow Petals and/or OpenAI

This commit is contained in:
Webifi 2023-07-24 15:26:17 -05:00
parent ca19bab19d
commit f6380e1cc2
11 changed files with 139 additions and 64 deletions

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { replace } from 'svelte-spa-router' import { replace } from 'svelte-spa-router'
import type { Chat } from './Types.svelte' import type { Chat } from './Types.svelte'
import { apiKeyStorage, deleteChat, pinMainMenu, saveChatStore } from './Storage.svelte' import { deleteChat, hasActiveModels, pinMainMenu, saveChatStore } from './Storage.svelte'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
import { faTrash, faCircleCheck, faPencil } from '@fortawesome/free-solid-svg-icons/index' import { faTrash, faCircleCheck, faPencil } from '@fortawesome/free-solid-svg-icons/index'
import { faMessage } from '@fortawesome/free-regular-svg-icons/index' import { faMessage } from '@fortawesome/free-regular-svg-icons/index'
@ -86,7 +86,7 @@
<a <a
href={`#/chat/${chat.id}`} href={`#/chat/${chat.id}`}
class="chat-menu-item" class="chat-menu-item"
class:is-waiting={waitingForConfirm} class:is-disabled={!$apiKeyStorage} class:is-active={activeChatId === chat.id} class:is-waiting={waitingForConfirm} class:is-disabled={!hasActiveModels()} class:is-active={activeChatId === chat.id}
on:click={() => { $pinMainMenu = false }} > on:click={() => { $pinMainMenu = false }} >
{#if waitingForConfirm} {#if waitingForConfirm}
<a class="is-pulled-right is-hidden px-1 py-0 has-text-weight-bold delete-button" href={'$'} on:click|preventDefault={() => delChat()}><Fa icon={faCircleCheck} /></a> <a class="is-pulled-right is-hidden px-1 py-0 has-text-weight-bold delete-button" href={'$'} on:click|preventDefault={() => delChat()}><Fa icon={faCircleCheck} /></a>

View File

@ -18,7 +18,7 @@
faEyeSlash faEyeSlash
} from '@fortawesome/free-solid-svg-icons/index' } from '@fortawesome/free-solid-svg-icons/index'
import { faSquareMinus, faSquarePlus as faSquarePlusOutline } from '@fortawesome/free-regular-svg-icons/index' import { faSquareMinus, faSquarePlus as faSquarePlusOutline } from '@fortawesome/free-regular-svg-icons/index'
import { apiKeyStorage, addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu, getChat, deleteChat, saveChatStore, saveCustomProfile } from './Storage.svelte' import { addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu, getChat, deleteChat, saveChatStore, saveCustomProfile, hasActiveModels } from './Storage.svelte'
import { exportAsMarkdown, exportChatAsJSON } from './Export.svelte' import { exportAsMarkdown, exportChatAsJSON } from './Export.svelte'
import { newNameForProfile, restartProfile } from './Profiles.svelte' import { newNameForProfile, restartProfile } from './Profiles.svelte'
import { replace } from 'svelte-spa-router' import { replace } from 'svelte-spa-router'
@ -173,7 +173,7 @@
<span class="menu-icon"><Fa icon={faGear}/></span> Chat Profile Settings <span class="menu-icon"><Fa icon={faGear}/></span> Chat Profile Settings
</a> </a>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
<a href={'#'} class:is-disabled={!$apiKeyStorage} on:click|preventDefault={() => { $apiKeyStorage && close(); $apiKeyStorage && startNewChatWithWarning(chatId) }} class="dropdown-item"> <a href={'#'} class:is-disabled={!hasActiveModels()} on:click|preventDefault={() => { hasActiveModels() && close(); hasActiveModels() && startNewChatWithWarning(chatId) }} class="dropdown-item">
<span class="menu-icon"><Fa icon={faSquarePlus}/></span> New Chat from Default <span class="menu-icon"><Fa icon={faSquarePlus}/></span> New Chat from Default
</a> </a>
<a href={'#'} class:is-disabled={!chatId} on:click|preventDefault={() => { chatId && close(); chatId && startNewChatFromChatId(chatId) }} class="dropdown-item"> <a href={'#'} class:is-disabled={!chatId} on:click|preventDefault={() => { chatId && close(); chatId && startNewChatFromChatId(chatId) }} class="dropdown-item">
@ -196,14 +196,14 @@
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { close(); exportChatAsJSON(chatId) }}> <a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { close(); exportChatAsJSON(chatId) }}>
<span class="menu-icon"><Fa icon={faDownload}/></span> Backup Chat JSON <span class="menu-icon"><Fa icon={faDownload}/></span> Backup Chat JSON
</a> </a>
<a href={'#'} class="dropdown-item" class:is-disabled={!$apiKeyStorage} on:click|preventDefault={() => { if (chatId) close(); chatFileInput.click() }}> <a href={'#'} class="dropdown-item" class:is-disabled={!hasActiveModels()} on:click|preventDefault={() => { if (chatId) close(); chatFileInput.click() }}>
<span class="menu-icon"><Fa icon={faUpload}/></span> Restore Chat JSON <span class="menu-icon"><Fa icon={faUpload}/></span> Restore Chat JSON
</a> </a>
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); exportAsMarkdown(chatId) }}> <a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); exportAsMarkdown(chatId) }}>
<span class="menu-icon"><Fa icon={faFileExport}/></span> Export Chat Markdown <span class="menu-icon"><Fa icon={faFileExport}/></span> Export Chat Markdown
</a> </a>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
<a href={'#'} class="dropdown-item" class:is-disabled={!$apiKeyStorage} on:click|preventDefault={() => { if (chatId) close(); profileFileInput.click() }}> <a href={'#'} class="dropdown-item" class:is-disabled={!hasActiveModels()} on:click|preventDefault={() => { if (chatId) close(); profileFileInput.click() }}>
<span class="menu-icon"><Fa icon={faUpload}/></span> Restore Profile JSON <span class="menu-icon"><Fa icon={faUpload}/></span> Restore Profile JSON
</a> </a>
<hr class="dropdown-divider"> <hr class="dropdown-divider">

View File

@ -5,7 +5,7 @@
import type { Chat, ChatCompletionOpts, ChatSettings, Message, Model, Request, RequestImageGeneration } from './Types.svelte' import type { Chat, ChatCompletionOpts, ChatSettings, Message, Model, Request, RequestImageGeneration } from './Types.svelte'
import { deleteMessage, getChatSettingValueNullDefault, insertMessages, getApiKey, addError, currentChatMessages, getMessages, updateMessages, deleteSummaryMessage } from './Storage.svelte' import { deleteMessage, getChatSettingValueNullDefault, insertMessages, getApiKey, addError, currentChatMessages, getMessages, updateMessages, deleteSummaryMessage } from './Storage.svelte'
import { scrollToBottom, scrollToMessage } from './Util.svelte' import { scrollToBottom, scrollToMessage } from './Util.svelte'
import { getRequestSettingList, defaultModel } from './Settings.svelte' import { getDefaultModel, getRequestSettingList } from './Settings.svelte'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { get } from 'svelte/store' import { get } from 'svelte/store'
import { getEndpoint, getModelDetail } from './Models.svelte' import { getEndpoint, getModelDetail } from './Models.svelte'
@ -26,6 +26,7 @@ export class ChatRequest {
setChat (chat: Chat) { setChat (chat: Chat) {
this.chat = chat this.chat = chat
this.chat.settings.model = this.getModel()
} }
getChat (): Chat { getChat (): Chat {
@ -283,7 +284,7 @@ export class ChatRequest {
} }
getModel (): Model { getModel (): Model {
return this.chat.settings.model || defaultModel return this.chat.settings.model || getDefaultModel()
} }
private buildHiddenPromptPrefixMessages (messages: Message[], insert:boolean = false): Message[] { private buildHiddenPromptPrefixMessages (messages: Message[], insert:boolean = false): Message[] {

View File

@ -211,7 +211,7 @@
{#key rkey} {#key rkey}
<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 class:is-default={option.value === chatDefaults[setting.key]} value={option.value} selected={option.value === chatSettings[setting.key]}>{option.text}</option> <option class:is-default={option.value === chatDefaults[setting.key]} value={option.value} selected={option.value === chatSettings[setting.key]} disabled={option.disabled}>{option.text}</option>
{/each} {/each}
</select> </select>
{/key} {/key}

View File

@ -1,19 +1,22 @@
<script lang="ts"> <script lang="ts">
import { apiKeyStorage, globalStorage, lastChatId, getChat, started, setGlobalSettingValueByKey } from './Storage.svelte' import { apiKeyStorage, globalStorage, lastChatId, getChat, started, setGlobalSettingValueByKey, hasActiveModels, checkStateChange } from './Storage.svelte'
import Footer from './Footer.svelte' import Footer from './Footer.svelte'
import { replace } from 'svelte-spa-router' import { replace } from 'svelte-spa-router'
import { onMount } from 'svelte' import { afterUpdate, onMount } from 'svelte'
import { getPetals } from './ApiUtil.svelte' import { getPetals } from './ApiUtil.svelte'
import { clearModelOptionCache } from './Models.svelte'
$: apiKey = $apiKeyStorage $: apiKey = $apiKeyStorage
let showPetalsSettings = $globalStorage.enablePetals let showPetalsSettings = $globalStorage.enablePetals
let pedalsEndpoint = $globalStorage.pedalsEndpoint
let hasModels = hasActiveModels()
onMount(() => { onMount(() => {
if (!$started) { if (!$started) {
$started = true $started = true
// console.log('started', apiKey, $lastChatId, getChat($lastChatId)) // console.log('started', apiKey, $lastChatId, getChat($lastChatId))
if (apiKey && getChat($lastChatId)) { if (hasActiveModels() && getChat($lastChatId)) {
const chatId = $lastChatId const chatId = $lastChatId
$lastChatId = 0 $lastChatId = 0
replace(`/chat/${chatId}`) replace(`/chat/${chatId}`)
@ -22,6 +25,13 @@ onMount(() => {
$lastChatId = 0 $lastChatId = 0
}) })
afterUpdate(() => {
clearModelOptionCache()
hasModels = hasActiveModels()
pedalsEndpoint = $globalStorage.pedalsEndpoint
$checkStateChange++
})
const setPetalsEnabled = (event: Event) => { const setPetalsEnabled = (event: Event) => {
const el = (event.target as HTMLInputElement) const el = (event.target as HTMLInputElement)
setGlobalSettingValueByKey('enablePetals', !!el.checked) setGlobalSettingValueByKey('enablePetals', !!el.checked)
@ -33,16 +43,21 @@ const setPetalsEnabled = (event: Event) => {
<section class="section"> <section class="section">
<article class="message"> <article class="message">
<div class="message-body"> <div class="message-body">
<strong><a href="https://github.com/Niek/chatgpt-web">ChatGPT-web</a></strong> <p class="mb-4">
<strong><a href="https://github.com/Niek/chatgpt-web" target="_blank">ChatGPT-web</a></strong>
is a simple one-page web interface to the OpenAI ChatGPT API. To use it, you need to register for is a simple one-page web interface to the OpenAI ChatGPT API. To use it, you need to register for
<a href="https://platform.openai.com/account/api-keys" target="_blank" rel="noreferrer">an OpenAI API key</a> <a href="https://platform.openai.com/account/api-keys" target="_blank" rel="noreferrer">an OpenAI API key</a>
first. OpenAI bills per token (usage-based), which means it is a lot cheaper than first. OpenAI bills per token (usage-based), which means it is a lot cheaper than
<a href="https://openai.com/blog/chatgpt-plus" target="_blank" rel="noreferrer">ChatGPT Plus</a>, unless you use <a href="https://openai.com/blog/chatgpt-plus" target="_blank" rel="noreferrer">ChatGPT Plus</a>, unless you use
more than 10 million tokens per month. All messages are stored in your browser's local storage, so everything is more than 10 million tokens per month. All messages are stored in your browser's local storage, so everything is
<strong>private</strong>. You can also close the browser tab and come back later to continue the conversation. <strong>private</strong>. You can also close the browser tab and come back later to continue the conversation.
</p>
<p>
As an alternative to OpenAI, you can also use Petals swarm as a free API option for open chat models like Llama 2.
</p>
</div> </div>
</article> </article>
<article class="message" class:is-danger={!apiKey} class:is-warning={apiKey}> <article class="message" class:is-danger={!hasModels} class:is-warning={!apiKey} class:is-info={apiKey}>
<div class="message-body"> <div class="message-body">
Set your OpenAI API key below: Set your OpenAI API key below:
@ -62,7 +77,8 @@ const setPetalsEnabled = (event: Event) => {
type="password" type="password"
autocomplete="off" autocomplete="off"
class="input" class="input"
class:is-danger={!apiKey} class:is-danger={!hasModels}
class:is-warning={!apiKey} class:is-info={apiKey}
value={apiKey} value={apiKey}
/> />
</p> </p>
@ -74,16 +90,16 @@ const setPetalsEnabled = (event: Event) => {
</form> </form>
{#if !apiKey} {#if !apiKey}
<p class="help is-danger"> <p class:is-danger={!hasModels} class:is-warning={!apiKey}>
Please enter your <a href="https://platform.openai.com/account/api-keys">OpenAI API key</a> above to use ChatGPT-web. Please enter your <a target="_blank" href="https://platform.openai.com/account/api-keys">OpenAI API key</a> above to use Open AI's ChatGPT API.
It is required to use ChatGPT-web. At least one API must be enabled to use ChatGPT-web.
</p> </p>
{/if} {/if}
</div> </div>
</article> </article>
<article class="message" class:is-info={true}> <article class="message" class:is-danger={!hasModels} class:is-warning={!showPetalsSettings} class:is-info={showPetalsSettings}>
<div class="message-body"> <div class="message-body">
<label class="label" for="enablePetals"> <label class="label" for="enablePetals">
<input <input
@ -122,21 +138,20 @@ const setPetalsEnabled = (event: Event) => {
</form> </form>
<p>
Only use <u>{getPetals()}</u> for testing. You must set up your own Petals server for actual use. {#if !pedalsEndpoint}
<p class="help is-warning">
Please only use the default public API for testing. It's best to <a target="_blank" href="https://github.com/petals-infra/chat.petals.dev">configure a private endpoint</a> and enter it above for connection to the Petals swarm.
</p>
{/if}
<p class="my-4">
<a target="_blank" href="https://petals.dev/">Petals</a> lets you run large language models at home by connecting to a public swarm, BitTorrent-style, without hefty GPU requirements.
</p> </p>
<p> <p class="mb-4">
<b>Do not send sensitive information when using Petals.</b> You are encouraged to <a target="_blank" href="https://github.com/bigscience-workshop/petals/wiki/FAQ:-Frequently-asked-questions#running-a-server">set up a Petals server to share your GPU resources</a> with the public swarm. Minimum requirements to contribute Llama 2 completions are a GTX&nbsp;1080&nbsp;8GB, but the larger/faster the better.
</p> </p>
<p> <p class="help is-warning">
For more information on Petals, see Because Petals uses a public swarm, <b>do not send sensitive information</b> when using Petals.
<a href="https://github.com/petals-infra/chat.petals.dev">https://github.com/petals-infra/chat.petals.dev</a>
</p>
{/if}
{#if !apiKey}
<p class="help is-danger">
Please enter your <a href="https://platform.openai.com/account/api-keys">OpenAI API key</a> above to use ChatGPT-web.
It is required to use ChatGPT-web.
</p> </p>
{/if} {/if}
</div> </div>

View File

@ -1,7 +1,7 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import { getApiBase, getEndpointCompletions, getEndpointGenerations, getEndpointModels, getPetals } from './ApiUtil.svelte' import { getApiBase, getEndpointCompletions, getEndpointGenerations, getEndpointModels, getPetals } from './ApiUtil.svelte'
import { apiKeyStorage, globalStorage } from './Storage.svelte' import { apiKeyStorage, globalStorage } from './Storage.svelte'
import { get } from 'svelte/store' import { get, writable } from 'svelte/store'
import type { ModelDetail, Model, ResponseModels, SelectOption, Chat } from './Types.svelte' import type { ModelDetail, Model, ResponseModels, SelectOption, Chat } from './Types.svelte'
import { encode } from 'gpt-tokenizer' import { encode } from 'gpt-tokenizer'
import llamaTokenizer from 'llama-tokenizer-js' import llamaTokenizer from 'llama-tokenizer-js'
@ -9,6 +9,12 @@ import llamaTokenizer from 'llama-tokenizer-js'
import { getChatSettingObjectByKey } from './Settings.svelte' import { getChatSettingObjectByKey } from './Settings.svelte'
import { valueOf } from './Util.svelte' import { valueOf } from './Util.svelte'
/**
* TODO: All of this + what's scattered about need to be refactored to interfaces and classes
* to make it all more modular
*/
const modelOptionCache = writable([] as SelectOption[])
// Reference: https://openai.com/pricing#language-models // Reference: https://openai.com/pricing#language-models
// Eventually we'll add API hosts and endpoints to this // Eventually we'll add API hosts and endpoints to this
const modelDetails : Record<string, ModelDetail> = { const modelDetails : Record<string, ModelDetail> = {
@ -46,6 +52,17 @@ const modelDetails : Record<string, ModelDetail> = {
prompt: 0.000000, // $0.000 per 1000 tokens prompt prompt: 0.000000, // $0.000 per 1000 tokens prompt
completion: 0.000000, // $0.000 per 1000 tokens completion completion: 0.000000, // $0.000 per 1000 tokens completion
max: 4096 // 4k max token buffer max: 4096 // 4k max token buffer
},
'timdettmers/guanaco-65b': {
type: 'Petals',
label: 'Petals - guanaco-65b',
stop: ['</s>'],
userStart: '[user]',
assistantStart: '[[[CHARACTER_NAME]]]',
systemStart: '',
prompt: 0.000000, // $0.000 per 1000 tokens prompt
completion: 0.000000, // $0.000 per 1000 tokens completion
max: 2048 // 2k max token buffer
} }
} }
@ -80,17 +97,18 @@ const unknownDetail = {
// See: https://platform.openai.com/docs/models/model-endpoint-compatibility // See: https://platform.openai.com/docs/models/model-endpoint-compatibility
// Eventually we'll add UI for managing this // Eventually we'll add UI for managing this
export const supportedModels : Record<string, ModelDetail> = { export const supportedModels : Record<string, ModelDetail> = {
'gpt-3.5-turbo': modelDetails['gpt-3.5'],
'gpt-3.5-turbo-0301': modelDetails['gpt-3.5'],
'gpt-3.5-turbo-0613': modelDetails['gpt-3.5'],
'gpt-3.5-turbo-16k': modelDetails['gpt-3.5-turbo-16k'],
'gpt-4': modelDetails['gpt-4'], 'gpt-4': modelDetails['gpt-4'],
'gpt-4-0314': modelDetails['gpt-4'], 'gpt-4-0314': modelDetails['gpt-4'],
'gpt-4-0613': modelDetails['gpt-4'], 'gpt-4-0613': modelDetails['gpt-4'],
'gpt-4-32k': modelDetails['gpt-4-32k'], 'gpt-4-32k': modelDetails['gpt-4-32k'],
'gpt-4-32k-0314': modelDetails['gpt-4-32k'], 'gpt-4-32k-0314': modelDetails['gpt-4-32k'],
'gpt-4-32k-0613': modelDetails['gpt-4-32k'], 'gpt-4-32k-0613': modelDetails['gpt-4-32k'],
'gpt-3.5-turbo': modelDetails['gpt-3.5'],
'gpt-3.5-turbo-16k': modelDetails['gpt-3.5-turbo-16k'],
'gpt-3.5-turbo-0301': modelDetails['gpt-3.5'],
'gpt-3.5-turbo-0613': modelDetails['gpt-3.5'],
'meta-llama/Llama-2-70b-chat-hf': modelDetails['meta-llama/Llama-2-70b-chat-hf'] 'meta-llama/Llama-2-70b-chat-hf': modelDetails['meta-llama/Llama-2-70b-chat-hf']
// 'timdettmers/guanaco-65b': modelDetails['timdettmers/guanaco-65b']
} }
const lookupList = { const lookupList = {
@ -192,43 +210,67 @@ export const countTokens = (model: Model, value: string): number => {
return getTokens(model, value).length return getTokens(model, value).length
} }
export const clearModelOptionCache = () => {
modelOptionCache.set([])
}
export async function getModelOptions (): Promise<SelectOption[]> { export async function getModelOptions (): Promise<SelectOption[]> {
const gSettings = get(globalStorage) const gSettings = get(globalStorage)
const openAiKey = get(apiKeyStorage) const openAiKey = get(apiKeyStorage)
const cachedOptions = get(modelOptionCache)
if (cachedOptions && cachedOptions.length) return cachedOptions
// Load available models from OpenAI // Load available models from OpenAI
let openAiModels let openAiModels
try { let allowCache = true
openAiModels = (await ( if (openAiKey) {
await fetch(getApiBase() + getEndpointModels(), { try {
method: 'GET', openAiModels = (await (
headers: { await fetch(getApiBase() + getEndpointModels(), {
Authorization: `Bearer ${openAiKey}`, method: 'GET',
'Content-Type': 'application/json' headers: {
} Authorization: `Bearer ${openAiKey}`,
}) 'Content-Type': 'application/json'
).json()) as ResponseModels }
} catch (e) { })
).json()) as ResponseModels
} catch (e) {
allowCache = false
openAiModels = { data: [] }
}
} else {
openAiModels = { data: [] } openAiModels = { data: [] }
} }
const filteredModels = supportedModelKeys.filter((model) => { // const filteredModels = Object.keys(supportedModels).filter((model) => {
switch (getModelDetail(model).type) { // switch (getModelDetail(model).type) {
// case 'Petals':
// return gSettings.enablePetals
// case 'OpenAIChat':
// default:
// return openAiModels.data && openAiModels.data.find((m) => m.id === model)
// }
// })
const modelOptions:SelectOption[] = Object.keys(supportedModels).reduce((a, m) => {
let disabled
switch (getModelDetail(m).type) {
case 'Petals': case 'Petals':
return gSettings.enablePetals disabled = !gSettings.enablePetals
break
case 'OpenAIChat': case 'OpenAIChat':
default: default:
return openAiModels.data.find((m) => m.id === model) disabled = !(openAiModels.data && openAiModels.data.find((m) => m.id === m))
} }
})
const modelOptions:SelectOption[] = filteredModels.reduce((a, m) => {
const o:SelectOption = { const o:SelectOption = {
value: m, value: m,
text: m text: m,
disabled
} }
a.push(o) a.push(o)
return a return a
}, [] as SelectOption[]) }, [] as SelectOption[])
if (allowCache) modelOptionCache.set(modelOptions)
return modelOptions return modelOptions
} }

View File

@ -1,5 +1,5 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte' import { getChatDefaults, getDefaultModel, getExcludeFromProfile } from './Settings.svelte'
import { get, writable } from 'svelte/store' import { get, writable } from 'svelte/store'
// Profile definitions // Profile definitions
import { addMessage, clearMessages, deleteMessage, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, getMessages, newName, resetChatSettings, saveChatStore, setGlobalSettingValueByKey, setMessages, updateProfile } from './Storage.svelte' import { addMessage, clearMessages, deleteMessage, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, getMessages, newName, resetChatSettings, saveChatStore, setGlobalSettingValueByKey, setMessages, updateProfile } from './Storage.svelte'
@ -22,7 +22,9 @@ export const getProfiles = (forceUpdate:boolean = false):Record<string, ChatSett
} }
const result = Object.entries(profiles const result = Object.entries(profiles
).reduce((a, [k, v]) => { ).reduce((a, [k, v]) => {
v = JSON.parse(JSON.stringify(v))
a[k] = v a[k] = v
v.model = v.model || getDefaultModel()
return a return a
}, {} as Record<string, ChatSettings>) }, {} as Record<string, ChatSettings>)
Object.entries(getCustomProfiles()).forEach(([k, v]) => { Object.entries(getCustomProfiles()).forEach(([k, v]) => {

View File

@ -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 { getChatSettings, getGlobalSettings, setGlobalSettingValueByKey } from './Storage.svelte' import { get } from 'svelte/store'
import { apiKeyStorage, getChatSettings, getGlobalSettings, setGlobalSettingValueByKey } from './Storage.svelte'
import { faArrowDown91, faArrowDownAZ, faCheck, faThumbTack } from '@fortawesome/free-solid-svg-icons/index' import { faArrowDown91, faArrowDownAZ, faCheck, faThumbTack } from '@fortawesome/free-solid-svg-icons/index'
// Setting definitions // Setting definitions
@ -19,7 +20,13 @@ import {
} from './Types.svelte' } from './Types.svelte'
import { getModelDetail, getTokens } from './Models.svelte' import { getModelDetail, getTokens } from './Models.svelte'
export const defaultModel:Model = 'gpt-3.5-turbo' const defaultModel:Model = 'gpt-3.5-turbo'
const defaultModelPetals:Model = 'meta-llama/Llama-2-70b-chat-hf'
export const getDefaultModel = (): Model => {
if (!get(apiKeyStorage)) return defaultModelPetals
return defaultModel
}
export const getChatSettingList = (): ChatSetting[] => { export const getChatSettingList = (): ChatSetting[] => {
return chatSettingsList return chatSettingsList
@ -64,7 +71,7 @@ const isNotPetals = (chatId) => {
} }
const gptDefaults = { const gptDefaults = {
model: defaultModel, model: '',
messages: [], messages: [],
temperature: 1, temperature: 1,
top_p: 1, top_p: 1,

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { params } from 'svelte-spa-router' import { params } from 'svelte-spa-router'
import ChatMenuItem from './ChatMenuItem.svelte' import ChatMenuItem from './ChatMenuItem.svelte'
import { apiKeyStorage, chatsStorage, pinMainMenu, checkStateChange, getChatSortOption, setChatSortOption } from './Storage.svelte' import { apiKeyStorage, chatsStorage, pinMainMenu, checkStateChange, getChatSortOption, setChatSortOption, hasActiveModels } from './Storage.svelte'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
import { faSquarePlus, faKey } from '@fortawesome/free-solid-svg-icons/index' import { faSquarePlus, faKey } from '@fortawesome/free-solid-svg-icons/index'
import ChatOptionMenu from './ChatOptionMenu.svelte' import ChatOptionMenu from './ChatOptionMenu.svelte'
@ -14,10 +14,12 @@
$: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined $: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined
let sortOption = getChatSortOption() let sortOption = getChatSortOption()
let hasModels = hasActiveModels()
const onStateChange = (...args:any) => { const onStateChange = (...args:any) => {
sortOption = getChatSortOption() sortOption = getChatSortOption()
sortedChats = $chatsStorage.sort(sortOption.sortFn) sortedChats = $chatsStorage.sort(sortOption.sortFn)
hasModels = hasActiveModels()
} }
$: onStateChange($checkStateChange) $: onStateChange($checkStateChange)
@ -72,14 +74,14 @@
</div> </div>
</div> </div>
<div class="level-right"> <div class="level-right">
{#if !$apiKeyStorage} {#if !hasModels}
<div class="level-item"> <div class="level-item">
<a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage} <a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage}
><span class="greyscale mr-1"><Fa icon={faKey} /></span> API key</a ><span class="greyscale mr-1"><Fa icon={faKey} /></span> API key</a
></div> ></div>
{:else} {:else}
<div class="level-item"> <div class="level-item">
<button on:click={() => { $pinMainMenu = false; startNewChatWithWarning(activeChatId) }} class="panel-block button" title="Start new chat with default profile" class:is-disabled={!$apiKeyStorage} <button on:click={() => { $pinMainMenu = false; startNewChatWithWarning(activeChatId) }} class="panel-block button" title="Start new chat with default profile" class:is-disabled={!hasModels}
><span class="greyscale mr-1"><Fa icon={faSquarePlus} /></span> New chat</button> ><span class="greyscale mr-1"><Fa icon={faSquarePlus} /></span> New chat</button>
</div> </div>
{/if} {/if}

View File

@ -30,6 +30,11 @@
return get(apiKeyStorage) return get(apiKeyStorage)
} }
export const hasActiveModels = (): boolean => {
const globalSettings = get(globalStorage) || {}
return !!get(apiKeyStorage) || !!globalSettings.enablePetals
}
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

View File

@ -199,6 +199,7 @@ export type GlobalSettings = {
export type SelectOption = { export type SelectOption = {
value: string|number; value: string|number;
text: string; text: string;
disabled?: boolean;
}; };
export type ChatSortOption = SelectOption & { export type ChatSortOption = SelectOption & {