From b2721c965d27adc77783a65e1fcc761ae445e241 Mon Sep 17 00:00:00 2001 From: Webifi Date: Tue, 13 Jun 2023 19:14:37 -0500 Subject: [PATCH 1/3] Add gpt-3.5-turbo-0613 and gpt-3.5-turbo-16k --- src/lib/Models.svelte | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/Models.svelte b/src/lib/Models.svelte index 54ada48..4d36ea2 100644 --- a/src/lib/Models.svelte +++ b/src/lib/Models.svelte @@ -14,10 +14,20 @@ const modelDetails : Record = { completion: 0.00006, // $0.06 per 1000 tokens completion max: 8192 // 8k max token buffer }, + 'gpt-3.5-turbo-0613': { + prompt: 0.0000015, // $0.0015 per 1000 tokens prompt + completion: 0.0000015, // $0.0015 per 1000 tokens completion + max: 4096 // 4k max token buffer + }, 'gpt-3.5': { prompt: 0.000002, // $0.002 per 1000 tokens prompt completion: 0.000002, // $0.002 per 1000 tokens completion max: 4096 // 4k max token buffer + }, + 'gpt-3.5-turbo-16k': { + prompt: 0.000003, // $0.003 per 1000 tokens prompt + completion: 0.000004, // $0.004 per 1000 tokens completion + max: 16384 // 16k max token buffer } } @@ -35,7 +45,9 @@ export const supportedModels : Record = { 'gpt-4-32k': modelDetails['gpt-4-32k'], 'gpt-4-32k-0314': modelDetails['gpt-4-32k'], 'gpt-3.5-turbo': modelDetails['gpt-3.5'], - 'gpt-3.5-turbo-0301': 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-turbo-0613'] } const lookupList = { From d96b38e8eafc9d185c1f1c1f31e1530010a134bd Mon Sep 17 00:00:00 2001 From: Webifi Date: Tue, 13 Jun 2023 21:54:11 -0500 Subject: [PATCH 2/3] Constrain size of prompt textarea fix #172 --- src/app.scss | 6 ++++-- src/lib/Util.svelte | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app.scss b/src/app.scss index c7d2a98..da7f917 100644 --- a/src/app.scss +++ b/src/app.scss @@ -245,8 +245,6 @@ $modal-background-background-color-dark: rgba($dark, 0.86) !default; // remove t } } - - /* Loading chat messages */ .is-loading { opacity: 0.5; @@ -462,6 +460,10 @@ aside.menu.main-menu .menu-expanse { .control.send .button { width: 60px; } + textarea { + max-height: calc(100vh - (var(--chatContentPaddingBottom) + var(--runningTotalLineHeight) * var(--running-totals))) !important; + min-height: 38px !important; + } } @media only screen and (max-width: 768px) { diff --git a/src/lib/Util.svelte b/src/lib/Util.svelte index e2e4360..39831b8 100644 --- a/src/lib/Util.svelte +++ b/src/lib/Util.svelte @@ -21,6 +21,13 @@ const anyEl = el as any // Oh how I hate typescript. All the markup of Java with no real payoff.. if (!anyEl.__didAutoGrow) el.style.height = '38px' // don't use "auto" here. Firefox will over-size. el.style.height = el.scrollHeight + 'px' + setTimeout(() => { + if (el.scrollHeight > el.getBoundingClientRect().height + 5) { + el.style.overflowY = 'auto' + } else { + el.style.overflowY = '' + } + }, 0) anyEl.__didAutoGrow = true // don't resize this one again unless it's via an event } From 7424742ed2c85b9a5be95e56d28c810d0694a0d4 Mon Sep 17 00:00:00 2001 From: Webifi Date: Wed, 14 Jun 2023 00:34:24 -0500 Subject: [PATCH 3/3] Add DALL-E image generation --- package-lock.json | 7 ++ package.json | 1 + src/lib/ApiUtil.svelte | 2 + src/lib/ChatCompletionResponse.svelte | 26 ++++++- src/lib/ChatRequest.svelte | 108 +++++++++++++++++++++----- src/lib/EditMessage.svelte | 53 +++++++++++-- src/lib/Export.svelte | 13 +++- src/lib/ImageStore.svelte | 71 +++++++++++++++++ src/lib/Models.svelte | 21 ++++- src/lib/Settings.svelte | 19 +++++ src/lib/Storage.svelte | 36 +++++++-- src/lib/Types.svelte | 32 +++++++- 12 files changed, 347 insertions(+), 42 deletions(-) create mode 100644 src/lib/ImageStore.svelte diff --git a/package-lock.json b/package-lock.json index 2f715e4..e93028b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "bulma": "^0.9.4", "bulma-prefers-dark": "^0.1.0-beta.1", "copy-to-clipboard": "^3.3.3", + "dexie": "^4.0.1-alpha.20", "eslint-config-standard-with-typescript": "^35.0.0", "eslint-plugin-svelte3": "^4.0.0", "flourite": "^1.2.3", @@ -1601,6 +1602,12 @@ "node": ">=8" } }, + "node_modules/dexie": { + "version": "4.0.1-alpha.20", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.1-alpha.20.tgz", + "integrity": "sha512-q/nMsCQiTWTmnw11aseJLfAsGQ/9t05sjWltgw1/r2TbfnIkmfjdTt8ATSIwmtKXuSznEZ5czazvL5LO5rR+6w==", + "dev": true + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", diff --git a/package.json b/package.json index b08903e..2eba101 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "bulma": "^0.9.4", "bulma-prefers-dark": "^0.1.0-beta.1", "copy-to-clipboard": "^3.3.3", + "dexie": "^4.0.1-alpha.20", "eslint-config-standard-with-typescript": "^35.0.0", "eslint-plugin-svelte3": "^4.0.0", "flourite": "^1.2.3", diff --git a/src/lib/ApiUtil.svelte b/src/lib/ApiUtil.svelte index 7beff67..01b41ff 100644 --- a/src/lib/ApiUtil.svelte +++ b/src/lib/ApiUtil.svelte @@ -2,9 +2,11 @@ // 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 endpointCompletions = import.meta.env.VITE_ENDPOINT_COMPLETIONS || '/v1/chat/completions' + const endpointGenerations = import.meta.env.VITE_ENDPOINT_GENERATIONS || '/v1/images/generations' const endpointModels = import.meta.env.VITE_ENDPOINT_MODELS || '/v1/models' export const getApiBase = ():string => apiBase export const getEndpointCompletions = ():string => endpointCompletions + export const getEndpointGenerations = ():string => endpointGenerations export const getEndpointModels = ():string => endpointModels \ No newline at end of file diff --git a/src/lib/ChatCompletionResponse.svelte b/src/lib/ChatCompletionResponse.svelte index e2afb6d..986784a 100644 --- a/src/lib/ChatCompletionResponse.svelte +++ b/src/lib/ChatCompletionResponse.svelte @@ -1,7 +1,8 @@
+ {#if imageUrl} + + {/if} {:else}
+ {#if imageUrl} + + {/if}
{/if} {#if isSystem} @@ -261,7 +286,7 @@ { checkDelete() }} @@ -273,11 +298,11 @@ {/if} {/if} - {#if !message.summarized && !isError} + {#if !isImage && !message.summarized && !isError} { checkTruncate() }} @@ -289,11 +314,11 @@ {/if} {/if} - {#if !message.summarized && !isSystem && !isError} + {#if !isImage && !message.summarized && !isSystem && !isError} { setSuppress(!message.suppress) }} @@ -305,6 +330,18 @@ {/if} {/if} + {#if imageUrl} + { + downloadImage() + }} + > + + + {/if} diff --git a/src/lib/Export.svelte b/src/lib/Export.svelte index 7050fd2..e7fa582 100644 --- a/src/lib/Export.svelte +++ b/src/lib/Export.svelte @@ -1,8 +1,9 @@ \ No newline at end of file diff --git a/src/lib/Models.svelte b/src/lib/Models.svelte index 4d36ea2..a3953c8 100644 --- a/src/lib/Models.svelte +++ b/src/lib/Models.svelte @@ -31,6 +31,24 @@ const modelDetails : Record = { } } +const imageModels : Record = { + 'dall-e-1024x1024': { + prompt: 0.00, + completion: 0.020, // $0.020 per image + max: 1000 // 1000 char prompt, max + }, + 'dall-e-512x512': { + prompt: 0.00, + completion: 0.018, // $0.018 per image + max: 1000 // 1000 char prompt, max + }, + 'dall-e-256x256': { + prompt: 0.00, + completion: 0.016, // $0.016 per image + max: 1000 // 1000 char prompt, max + } +} + const unknownDetail = { prompt: 0, completion: 0, @@ -51,11 +69,12 @@ export const supportedModels : Record = { } const lookupList = { + ...imageModels, ...modelDetails, ...supportedModels } -export const supportedModelKeys = Object.keys(supportedModels) +export const supportedModelKeys = Object.keys({ ...supportedModels, ...imageModels }) const tpCache : Record = {} diff --git a/src/lib/Settings.svelte b/src/lib/Settings.svelte index b3dc4b9..d732417 100644 --- a/src/lib/Settings.svelte +++ b/src/lib/Settings.svelte @@ -86,6 +86,7 @@ const defaults:ChatSettings = { autoStartSession: false, trainingPrompts: [], hiddenPromptPrefix: '', + imageGenerationSize: '', // useResponseAlteration: false, // responseAlterations: [], isDirty: false @@ -97,6 +98,12 @@ const excludeFromProfile = { isDirty: true } +export const imageGenerationSizes = [ + '1024x1024', '512x512', '256x256' +] + +export const imageGenerationSizeTypes = ['', ...imageGenerationSizes] + const profileSetting: ChatSetting & SettingSelect = { key: 'profile', name: 'Profile', @@ -269,6 +276,18 @@ const summarySettings: ChatSetting[] = [ placeholder: 'Enter a prompt that will be used to summarize past prompts here.', type: 'textarea', hide: (chatId) => getChatSettings(chatId).continuousChat !== 'summary' + }, + { + key: 'imageGenerationSize', + name: 'Image Generation Size', + header: 'Image Generation', + headerClass: 'is-info', + title: 'Prompt an image with: show me an image of ...', + type: 'select', + options: [ + { value: '', text: 'OFF - Disable Image Generation' }, + ...imageGenerationSizes.map(s => { return { value: s, text: s } }) + ] } ] diff --git a/src/lib/Storage.svelte b/src/lib/Storage.svelte index 80eafe0..967eca0 100644 --- a/src/lib/Storage.svelte +++ b/src/lib/Storage.svelte @@ -6,7 +6,10 @@ import { v4 as uuidv4 } from 'uuid' import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte' import { errorNotice } from './Util.svelte' + import { deleteImage, setImage } from './ImageStore.svelte' + // TODO: move chatsStorage to indexedDB with localStorage as a fallback for private browsing. + // Enough long chats will overflow localStorage. export const chatsStorage = persisted('chats', [] as Chat[]) export const latestModelMap = persisted('latestModelMap', {} as Record) // What was returned when a model was requested export const globalStorage = persisted('global', {} as GlobalSettings) @@ -53,7 +56,7 @@ return chatId } - export const addChatFromJSON = (json: string): number => { + export const addChatFromJSON = async (json: string): Promise => { const chats = get(chatsStorage) // Find the max chatId @@ -73,6 +76,10 @@ chat.id = chatId + // Make sure images are moved to indexedDB store, + // else they would clobber local storage + await updateChatImages(chatId, chat) + // Add a new chat chats.push(chat) chatsStorage.set(chats) @@ -154,7 +161,10 @@ } export const clearChats = () => { - chatsStorage.set([]) + const chats = get(chatsStorage) + chats.forEach(c => deleteChat(c.id)) // make sure images are removed + // TODO: add a clear images option to make this faster + // chatsStorage.set([]) } export const saveChatStore = () => { const chats = get(chatsStorage) @@ -268,13 +278,16 @@ const chat = chats.find((chat) => chat.id === chatId) as Chat const index = chat.messages.findIndex((m) => m.uuid === uuid) const message = getMessage(chat, uuid) - if (message && message.summarized) throw new Error('Unable to delete summarized message') - if (message && message.summary) throw new Error('Unable to directly delete message summary') + if (message?.summarized) throw new Error('Unable to delete summarized message') + if (message?.summary) throw new Error('Unable to directly delete message summary') // const found = chat.messages.filter((m) => m.uuid === uuid) if (index < 0) { console.error(`Unable to find and delete message with ID: ${uuid}`) return } + if (message?.image) { + deleteImage(chatId, message.image.id) + } // console.warn(`Deleting message with ID: ${uuid}`, found, index) chat.messages.splice(index, 1) // remove item chatsStorage.set(chats) @@ -303,10 +316,21 @@ export const deleteChat = (chatId: number) => { const chats = get(chatsStorage) + const chat = getChat(chatId) + chat?.messages?.forEach(m => { + if (m.image) deleteImage(chatId, m.image.id) + }) chatsStorage.set(chats.filter((chat) => chat.id !== chatId)) } - export const copyChat = (chatId: number) => { + export const updateChatImages = async (chatId: number, chat: Chat) => { + for (let i = 0; i < chat.messages.length; i++) { + const m = chat.messages[i] + if (m.image) m.image = await setImage(chatId, m.image) + } + } + + export const copyChat = async (chatId: number) => { const chats = get(chatsStorage) const chat = chats.find((chat) => chat.id === chatId) as Chat const nameMap = chats.reduce((a, chat) => { a[chat.name] = chat; return a }, {}) @@ -323,6 +347,8 @@ // Set new name chatCopy.name = cname + await updateChatImages(chatId, chatCopy) + // Add a new chat chats.push(chatCopy) diff --git a/src/lib/Types.svelte b/src/lib/Types.svelte index 246eedf..f944556 100644 --- a/src/lib/Types.svelte +++ b/src/lib/Types.svelte @@ -1,8 +1,11 @@