mirror of
https://github.com/morgan9e/chatgpt-web
synced 2026-04-13 16:04:05 +09:00
FIX?
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -30,7 +30,6 @@
|
|||||||
"flourite": "^1.3.0",
|
"flourite": "^1.3.0",
|
||||||
"gpt-tokenizer": "^2.1.2",
|
"gpt-tokenizer": "^2.1.2",
|
||||||
"katex": "^0.16.10",
|
"katex": "^0.16.10",
|
||||||
"llama-tokenizer-js": "^1.2.2",
|
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"sass": "^1.77.6",
|
"sass": "^1.77.6",
|
||||||
"stacking-order": "^2.0.0",
|
"stacking-order": "^2.0.0",
|
||||||
@@ -3834,13 +3833,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/llama-tokenizer-js": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/llama-tokenizer-js/-/llama-tokenizer-js-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-Wmth393dc3odWU3IzARJ3r2oIfWgw9GdJ5Gm+hGhfECNO18UHLRqEFSf511jn4E9KcQGzuuKw4Wl08pHAemLAw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
"flourite": "^1.3.0",
|
"flourite": "^1.3.0",
|
||||||
"gpt-tokenizer": "^2.1.2",
|
"gpt-tokenizer": "^2.1.2",
|
||||||
"katex": "^0.16.10",
|
"katex": "^0.16.10",
|
||||||
"llama-tokenizer-js": "^1.2.2",
|
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"sass": "^1.77.6",
|
"sass": "^1.77.6",
|
||||||
"stacking-order": "^2.0.0",
|
"stacking-order": "^2.0.0",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import Home from './lib/Home.svelte'
|
import Home from './lib/Home.svelte'
|
||||||
import Chat from './lib/Chat.svelte'
|
import Chat from './lib/Chat.svelte'
|
||||||
import NewChat from './lib/NewChat.svelte'
|
import NewChat from './lib/NewChat.svelte'
|
||||||
import { chatsStorage, setGlobalSettingValueByKey } from './lib/Storage.svelte'
|
import { chatsStorage } from './lib/Storage.svelte'
|
||||||
import { Modals, closeModal } from 'svelte-modals'
|
import { Modals, closeModal } from 'svelte-modals'
|
||||||
import { dispatchModalEsc, checkModalEsc } from './lib/Util.svelte'
|
import { dispatchModalEsc, checkModalEsc } from './lib/Util.svelte'
|
||||||
import { set as setOpenAI } from './lib/providers/openai/util.svelte'
|
import { set as setOpenAI } from './lib/providers/openai/util.svelte'
|
||||||
@@ -19,10 +19,6 @@
|
|||||||
if (urlParams.has('key')) {
|
if (urlParams.has('key')) {
|
||||||
setOpenAI({ apiKey: urlParams.get('key') as string })
|
setOpenAI({ apiKey: urlParams.get('key') as string })
|
||||||
}
|
}
|
||||||
if (urlParams.has('petals')) {
|
|
||||||
console.log('enablePetals')
|
|
||||||
setGlobalSettingValueByKey('enablePetals', true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The definition of the routes with some conditions
|
// The definition of the routes with some conditions
|
||||||
const routes = {
|
const routes = {
|
||||||
|
|||||||
@@ -2,18 +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'
|
||||||
// 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 apiBaseStorage = persisted('apiBase', 'https://api.openai.com');
|
const apiBaseStorage = persisted('apiBase', 'https://api.openai.com')
|
||||||
|
|
||||||
const apiBase = get(apiBaseStorage) || 'https://api.openai.com';
|
const apiBase = get(apiBaseStorage) || 'https://api.openai.com'
|
||||||
const endpointCompletions = import.meta.env.VITE_ENDPOINT_COMPLETIONS || '/v1/chat/completions'
|
const endpointCompletions = import.meta.env.VITE_ENDPOINT_COMPLETIONS || '/v1/chat/completions'
|
||||||
const endpointGenerations = import.meta.env.VITE_ENDPOINT_GENERATIONS || '/v1/images/generations'
|
const endpointGenerations = import.meta.env.VITE_ENDPOINT_GENERATIONS || '/v1/images/generations'
|
||||||
const endpointModels = import.meta.env.VITE_ENDPOINT_MODELS || '/v1/models'
|
const endpointModels = import.meta.env.VITE_ENDPOINT_MODELS || '/v1/models'
|
||||||
const endpointEmbeddings = import.meta.env.VITE_ENDPOINT_EMBEDDINGS || '/v1/embeddings'
|
const endpointEmbeddings = import.meta.env.VITE_ENDPOINT_EMBEDDINGS || '/v1/embeddings'
|
||||||
const petalsBase = import.meta.env.VITE_PEDALS_WEBSOCKET || 'wss://chat.petals.dev'
|
|
||||||
const endpointPetals = import.meta.env.VITE_PEDALS_WEBSOCKET || '/api/v2/generate'
|
|
||||||
|
|
||||||
export const setApiBase = (e: Record<string>) => {
|
export const setApiBase = (e: string) => {
|
||||||
console.log(e);
|
console.log(e)
|
||||||
apiBaseStorage.set(e || '')
|
apiBaseStorage.set(e || '')
|
||||||
}
|
}
|
||||||
export const getApiBase = ():string => apiBase
|
export const getApiBase = ():string => apiBase
|
||||||
@@ -21,6 +19,4 @@
|
|||||||
export const getEndpointGenerations = ():string => endpointGenerations
|
export const getEndpointGenerations = ():string => endpointGenerations
|
||||||
export const getEndpointModels = ():string => endpointModels
|
export const getEndpointModels = ():string => endpointModels
|
||||||
export const getEndpointEmbeddings = ():string => endpointEmbeddings
|
export const getEndpointEmbeddings = ():string => endpointEmbeddings
|
||||||
export const getPetalsBase = ():string => petalsBase
|
|
||||||
export const getPetalsWebsocket = ():string => endpointPetals
|
|
||||||
</script>
|
</script>
|
||||||
@@ -51,15 +51,25 @@
|
|||||||
let recording = false
|
let recording = false
|
||||||
let lastSubmitRecorded = false
|
let lastSubmitRecorded = false
|
||||||
|
|
||||||
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
// Optimize chat lookup to avoid expensive find() on every chats update
|
||||||
$: chatSettings = chat?.settings
|
let chat: Chat
|
||||||
|
let chatSettings: ChatSettings
|
||||||
let showSettingsModal
|
let showSettingsModal
|
||||||
|
|
||||||
let scDelay
|
// Only update chat when chatId changes or when the specific chat is updated
|
||||||
|
$: {
|
||||||
|
const foundChat = $chatsStorage.find((c) => c.id === chatId)
|
||||||
|
if (foundChat && (!chat || chat.id !== foundChat.id || chat !== foundChat)) {
|
||||||
|
chat = foundChat
|
||||||
|
chatSettings = foundChat.settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let scDelay: any
|
||||||
const onStateChange = (...args:any) => {
|
const onStateChange = (...args:any) => {
|
||||||
if (!chat) return
|
if (!chat) return
|
||||||
clearTimeout(scDelay)
|
if (scDelay) clearTimeout(scDelay)
|
||||||
setTimeout(() => {
|
scDelay = setTimeout(() => {
|
||||||
if (chat.startSession) {
|
if (chat.startSession) {
|
||||||
restartProfile(chatId)
|
restartProfile(chatId)
|
||||||
if (chat.startSession) {
|
if (chat.startSession) {
|
||||||
@@ -101,6 +111,11 @@
|
|||||||
|
|
||||||
onDestroy(async () => {
|
onDestroy(async () => {
|
||||||
// clean up
|
// clean up
|
||||||
|
// Clear timer to prevent memory leaks
|
||||||
|
if (scDelay) {
|
||||||
|
clearTimeout(scDelay)
|
||||||
|
scDelay = null
|
||||||
|
}
|
||||||
// abort any pending requests.
|
// abort any pending requests.
|
||||||
chatRequest.controller.abort()
|
chatRequest.controller.abort()
|
||||||
ttsStop()
|
ttsStop()
|
||||||
@@ -286,10 +301,10 @@
|
|||||||
chatRequest.updatingMessage = ''
|
chatRequest.updatingMessage = ''
|
||||||
|
|
||||||
|
|
||||||
const userMessagesCount = chat.messages.filter(message => message.role === "user").length;
|
const userMessagesCount = chat.messages.filter(message => message.role === 'user').length
|
||||||
const assiMessagesCount = chat.messages.filter(message => message.role === "assistant").length;
|
const assiMessagesCount = chat.messages.filter(message => message.role === 'assistant').length
|
||||||
if (userMessagesCount == 3 && chat.name.startsWith("Chat ")) {
|
if (userMessagesCount == 3 && chat.name.startsWith('Chat ')) {
|
||||||
suggestName();
|
suggestName()
|
||||||
}
|
}
|
||||||
|
|
||||||
focusInput()
|
focusInput()
|
||||||
@@ -305,7 +320,7 @@
|
|||||||
const suggestMessages = $currentChatMessages.slice(0, 4)
|
const suggestMessages = $currentChatMessages.slice(0, 4)
|
||||||
suggestMessages.push(suggestMessage)
|
suggestMessages.push(suggestMessage)
|
||||||
|
|
||||||
const currentModel = chat.settings.model;
|
const currentModel = chat.settings.model
|
||||||
// chat.settings.model = "gpt-4o";
|
// chat.settings.model = "gpt-4o";
|
||||||
|
|
||||||
chatRequest.updating = true
|
chatRequest.updating = true
|
||||||
@@ -318,7 +333,7 @@
|
|||||||
maxTokens: 30
|
maxTokens: 30
|
||||||
})
|
})
|
||||||
|
|
||||||
chat.settings.model = currentModel;
|
chat.settings.model = currentModel
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await response.promiseToFinish()
|
await response.promiseToFinish()
|
||||||
|
|||||||
@@ -157,74 +157,72 @@
|
|||||||
reader.readAsText(image)
|
reader.readAsText(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
function dumpLocalStorage(){
|
function dumpLocalStorage () {
|
||||||
try {
|
try {
|
||||||
let storageObject = {};
|
const storageObject = {}
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i)
|
||||||
if (key) {
|
if (key) {
|
||||||
storageObject[key] = localStorage.getItem(key);
|
storageObject[key] = localStorage.getItem(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataStr = JSON.stringify(storageObject, null, 2);
|
const dataStr = JSON.stringify(storageObject, null, 2)
|
||||||
const blob = new Blob([dataStr], { type: "application/json" });
|
const blob = new Blob([dataStr], { type: 'application/json' })
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob)
|
||||||
const link = document.createElement("a");
|
const link = document.createElement('a')
|
||||||
link.href = url;
|
link.href = url
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_');
|
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_')
|
||||||
link.download = `ChatGPT-web-${dateTimeStr}.json`;
|
link.download = `ChatGPT-web-${dateTimeStr}.json`
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link)
|
||||||
link.click();
|
link.click()
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link)
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error dumping localStorage:', error);
|
console.error('Error dumping localStorage:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLocalStorage() {
|
function loadLocalStorage () {
|
||||||
var fileInput = document.createElement('input');
|
const fileInput = document.createElement('input')
|
||||||
fileInput.type = "file";
|
fileInput.type = 'file'
|
||||||
fileInput.addEventListener('change', function(e) {
|
fileInput.addEventListener('change', function (e) {
|
||||||
var file = e.target.files[0];
|
const file = e.target.files[0]
|
||||||
if (file) {
|
if (file) {
|
||||||
var reader = new FileReader();
|
const reader = new FileReader()
|
||||||
reader.onload = function(e) {
|
reader.onload = function (e) {
|
||||||
var data = JSON.parse(e.target.result);
|
const data = JSON.parse(e.target.result)
|
||||||
Object.keys(data).forEach(function(key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
localStorage.setItem(key, data[key]);
|
localStorage.setItem(key, data[key])
|
||||||
});
|
})
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
};
|
}
|
||||||
reader.readAsText(file);
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
document.body.appendChild(fileInput);
|
document.body.appendChild(fileInput)
|
||||||
fileInput.click();
|
fileInput.click()
|
||||||
fileInput.remove();
|
fileInput.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
function backupLocalStorage() {
|
function backupLocalStorage () {
|
||||||
try {
|
try {
|
||||||
let storageObject = {};
|
const storageObject = {}
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i)
|
||||||
if (key) {
|
if (key) {
|
||||||
storageObject[key] = localStorage.getItem(key);
|
storageObject[key] = localStorage.getItem(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const dataStr = JSON.stringify(storageObject, null, 2);
|
const dataStr = JSON.stringify(storageObject, null, 2)
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_');
|
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_')
|
||||||
localStorage.setItem(`prev-${dateTimeStr}`, dataStr);
|
localStorage.setItem(`prev-${dateTimeStr}`, dataStr)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error backing up localStorage:', error);
|
console.error('Error backing up localStorage:', error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ export class ChatRequest {
|
|||||||
if (value > maxAllowed || value < 1) value = null // if over max model, do not define max
|
if (value > maxAllowed || value < 1) value = null // if over max model, do not define max
|
||||||
if (value) value = Math.floor(value)
|
if (value) value = Math.floor(value)
|
||||||
if (modelDetail.reasoning == true) {
|
if (modelDetail.reasoning == true) {
|
||||||
key = 'max_completion_tokens';
|
key = 'max_completion_tokens'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (key === 'n') {
|
if (key === 'n') {
|
||||||
@@ -351,12 +351,21 @@ export class ChatRequest {
|
|||||||
* *************************************************************
|
* *************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let promptSize = countPromptTokens(top.concat(rw), model, chat) + countPadding
|
// Pre-calculate top tokens once to avoid repeated calculations
|
||||||
|
const topTokens = countPromptTokens(top, model, chat)
|
||||||
|
let rwTokens = countPromptTokens(rw, model, chat)
|
||||||
|
let promptSize = topTokens + rwTokens + countPadding
|
||||||
|
|
||||||
while (rw.length && rw.length > pinBottom && promptSize >= threshold) {
|
while (rw.length && rw.length > pinBottom && promptSize >= threshold) {
|
||||||
const rolled = rw.shift()
|
const rolled = rw.shift()
|
||||||
// Hide messages we're "rolling"
|
if (rolled) {
|
||||||
if (rolled) rolled.suppress = true
|
// Hide messages we're "rolling"
|
||||||
promptSize = countPromptTokens(top.concat(rw), model, chat) + countPadding
|
rolled.suppress = true
|
||||||
|
// Subtract only the rolled message tokens instead of recalculating all
|
||||||
|
const rolledTokens = countMessageTokens(rolled, model, chat)
|
||||||
|
rwTokens -= rolledTokens
|
||||||
|
promptSize = topTokens + rwTokens + countPadding
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Run a new request, now with the rolled messages hidden
|
// Run a new request, now with the rolled messages hidden
|
||||||
return await _this.sendRequest(get(currentChatMessages), {
|
return await _this.sendRequest(get(currentChatMessages), {
|
||||||
@@ -386,8 +395,11 @@ export class ChatRequest {
|
|||||||
// the last prompt is a user prompt as that seems to work better for summaries
|
// the last prompt is a user prompt as that seems to work better for summaries
|
||||||
while (rw.length > 2 && ((topSize + reductionPoolSize + promptSummarySize + maxSummaryTokens) >= maxTokens ||
|
while (rw.length > 2 && ((topSize + reductionPoolSize + promptSummarySize + maxSummaryTokens) >= maxTokens ||
|
||||||
(reductionPoolSize >= 100 && rw[rw.length - 1]?.role !== 'user'))) {
|
(reductionPoolSize >= 100 && rw[rw.length - 1]?.role !== 'user'))) {
|
||||||
bottom.unshift(rw.pop() as Message)
|
const removed = rw.pop() as Message
|
||||||
reductionPoolSize = countPromptTokens(rw, model, chat)
|
bottom.unshift(removed)
|
||||||
|
// Optimize: subtract removed message tokens instead of recalculating all
|
||||||
|
const removedTokens = countMessageTokens(removed, model, chat)
|
||||||
|
reductionPoolSize -= removedTokens
|
||||||
maxSummaryTokens = getSS()
|
maxSummaryTokens = getSS()
|
||||||
promptSummary = prepareSummaryPrompt(chatId, maxSummaryTokens)
|
promptSummary = prepareSummaryPrompt(chatId, maxSummaryTokens)
|
||||||
summaryRequest.content = promptSummary
|
summaryRequest.content = promptSummary
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
export const codeBlockStyle: 'indented' | undefined = undefined
|
export const codeBlockStyle: 'indented' | undefined = undefined
|
||||||
export let text: string
|
export let text: string
|
||||||
|
|
||||||
let renderedMath: string | undefined;
|
let renderedMath: string | undefined
|
||||||
|
|
||||||
// For copying code - reference: https://vyacheslavbasharov.com/blog/adding-click-to-copy-code-markdown-blog
|
// For copying code - reference: https://vyacheslavbasharov.com/blog/adding-click-to-copy-code-markdown-blog
|
||||||
const copyFunction = (event) => {
|
const copyFunction = (event) => {
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
import renderMathInElement from 'katex/contrib/auto-render'
|
import renderMathInElement from 'katex/contrib/auto-render'
|
||||||
|
|
||||||
let renderedMath: string | undefined
|
let renderedMath: string | undefined
|
||||||
if ( raw.startsWith('`\\(') || raw.startsWith('`\\[') || raw.startsWith('`$') || raw.startsWith('`$$') ) {
|
if (raw.startsWith('`\\(') || raw.startsWith('`\\[') || raw.startsWith('`$') || raw.startsWith('`$$')) {
|
||||||
let dummy = document.createElement("div")
|
const dummy = document.createElement('div')
|
||||||
dummy.textContent = raw.replace(/`/g, '')
|
dummy.textContent = raw.replace(/`/g, '')
|
||||||
renderMathInElement(dummy, {
|
renderMathInElement(dummy, {
|
||||||
delimiters: [
|
delimiters: [
|
||||||
{left: '\\(', right: '\\)', display: false},
|
{ left: '\\(', right: '\\)', display: false },
|
||||||
{left: '\\[', right: '\\]', display: true},
|
{ left: '\\[', right: '\\]', display: true },
|
||||||
{left: '$', right: '$', display: false},
|
{ left: '$', right: '$', display: false },
|
||||||
{left: '$$', right: '$$', display: true}
|
{ left: '$$', right: '$$', display: true }
|
||||||
],
|
],
|
||||||
throwOnError : false,
|
throwOnError: false,
|
||||||
output: "html"
|
output: 'html'
|
||||||
})
|
})
|
||||||
renderedMath = dummy.innerHTML;
|
renderedMath = dummy.innerHTML
|
||||||
dummy.remove();
|
dummy.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Code from './Code.svelte'
|
import Code from './Code.svelte'
|
||||||
import Codespan from './Codespan.svelte'
|
import Codespan from './Codespan.svelte'
|
||||||
import { afterUpdate, createEventDispatcher, onMount } from 'svelte'
|
import { afterUpdate, createEventDispatcher, onMount, onDestroy } from 'svelte'
|
||||||
import { deleteMessage, deleteSummaryMessage, truncateFromMessage, submitExitingPromptsNow, continueMessage, updateMessages } from './Storage.svelte'
|
import { deleteMessage, deleteSummaryMessage, truncateFromMessage, submitExitingPromptsNow, continueMessage, updateMessages } from './Storage.svelte'
|
||||||
import { getPrice } from './Stats.svelte'
|
import { getPrice } from './Stats.svelte'
|
||||||
import SvelteMarkdown from 'svelte-markdown'
|
import SvelteMarkdown from 'svelte-markdown'
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
import PromptConfirm from './PromptConfirm.svelte'
|
import PromptConfirm from './PromptConfirm.svelte'
|
||||||
import { getImage } from './ImageStore.svelte'
|
import { getImage } from './ImageStore.svelte'
|
||||||
import { getModelDetail } from './Models.svelte'
|
import { getModelDetail } from './Models.svelte'
|
||||||
import renderMathInElement from "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.mjs";
|
import renderMathInElement from 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.mjs'
|
||||||
|
|
||||||
export let message:Message
|
export let message:Message
|
||||||
export let chatId:number
|
export let chatId:number
|
||||||
@@ -177,22 +177,38 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const takeReason = (msg) => {
|
const takeReason = (msg) => {
|
||||||
if(isAssistant) {
|
if (isAssistant) {
|
||||||
const regex = /<think>([\s\S]*?)<\/think>/;
|
const regex = /<think>([\s\S]*?)<\/think>/
|
||||||
const match = msg.match(regex);
|
const match = msg.match(regex)
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
message.reason = match[1];
|
message.reason = match[1]
|
||||||
msg = msg.replace(regex, '');
|
msg = msg.replace(regex, '')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message.reason = "";
|
message.reason = ''
|
||||||
}
|
}
|
||||||
return msg;
|
return msg
|
||||||
};
|
}
|
||||||
|
|
||||||
let waitingForTruncateConfirm:any = 0
|
let waitingForTruncateConfirm:any = 0
|
||||||
|
|
||||||
|
// Clean up timers to prevent memory leaks
|
||||||
|
onDestroy(() => {
|
||||||
|
if (dbnc) {
|
||||||
|
clearTimeout(dbnc)
|
||||||
|
dbnc = null
|
||||||
|
}
|
||||||
|
if (waitingForDeleteConfirm) {
|
||||||
|
clearTimeout(waitingForDeleteConfirm)
|
||||||
|
waitingForDeleteConfirm = null
|
||||||
|
}
|
||||||
|
if (waitingForTruncateConfirm) {
|
||||||
|
clearTimeout(waitingForTruncateConfirm)
|
||||||
|
waitingForTruncateConfirm = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const checkTruncate = () => {
|
const checkTruncate = () => {
|
||||||
clearTimeout(waitingForDeleteConfirm); waitingForDeleteConfirm = 0
|
clearTimeout(waitingForDeleteConfirm); waitingForDeleteConfirm = 0
|
||||||
if (!waitingForTruncateConfirm) {
|
if (!waitingForTruncateConfirm) {
|
||||||
@@ -237,86 +253,86 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const replaceLatexDelimiters = (text: string): string => {
|
const replaceLatexDelimiters = (text: string): string => {
|
||||||
let result = '';
|
let result = ''
|
||||||
let i = 0;
|
let i = 0
|
||||||
|
|
||||||
while (i < text.length) {
|
while (i < text.length) {
|
||||||
// Check for display math: $$ ... $$
|
// Check for display math: $$ ... $$
|
||||||
if (text.startsWith('$$aaaaaaaa', i)) {
|
if (text.startsWith('$$aaaaaaaa', i)) {
|
||||||
const endPos = text.indexOf('$$', i + 2);
|
const endPos = text.indexOf('$$', i + 2)
|
||||||
if (endPos === -1) {
|
if (endPos === -1) {
|
||||||
console.error(`LaTeX: Delimiter mismatch (missing $$) at position ${i}`);
|
console.error(`LaTeX: Delimiter mismatch (missing $$) at position ${i}`)
|
||||||
result += text[i];
|
result += text[i]
|
||||||
i++;
|
i++
|
||||||
} else {
|
} else {
|
||||||
// Wrap in backticks for KaTeX
|
// Wrap in backticks for KaTeX
|
||||||
result += `\`\\[${text.slice(i + 2, endPos)}\\]\``;
|
result += `\`\\[${text.slice(i + 2, endPos)}\\]\``
|
||||||
i = endPos + 2;
|
i = endPos + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for inline math: $ ... $
|
||||||
|
else if (text.startsWith('$aaaaaaaaa', i)) {
|
||||||
|
const endPos = text.indexOf('$', i + 1)
|
||||||
|
if (endPos === -1) {
|
||||||
|
console.error(`LaTeX: Delimiter mismatch (missing $) at position ${i}`)
|
||||||
|
result += text[i]
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
result += `\`$${text.slice(i + 1, endPos)}$\``
|
||||||
|
i = endPos + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for inline math: \(...\)
|
||||||
|
else if (text.startsWith('\\(', i)) {
|
||||||
|
const endPos = text.indexOf('\\)', i + 2)
|
||||||
|
if (endPos === -1) {
|
||||||
|
console.error(`LaTeX: Delimiter mismatch (missing \\)) at position ${i}`)
|
||||||
|
result += text[i]
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
result += '`\\(' + text.slice(i + 2, endPos) + '\\)`'
|
||||||
|
i = endPos + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for display math: \[...\]
|
||||||
|
else if (text.startsWith('\\[', i)) {
|
||||||
|
const endPos = text.indexOf('\\]', i + 2)
|
||||||
|
if (endPos === -1) {
|
||||||
|
console.error(`LaTeX: Delimiter mismatch (missing \\]) at position ${i}`)
|
||||||
|
result += text[i]
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
result += `\`\\[${text.slice(i + 2, endPos)}\\]\``
|
||||||
|
i = endPos + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, just copy the current character (also handling backslash escapes)
|
||||||
|
else {
|
||||||
|
if (text.startsWith('\\(', i)) {
|
||||||
|
result += '\\('
|
||||||
|
i += 2
|
||||||
|
} else if (text.startsWith('\\)', i)) {
|
||||||
|
result += '\\)'
|
||||||
|
i += 2
|
||||||
|
} else if (text.startsWith('\\[', i)) {
|
||||||
|
result += '\\['
|
||||||
|
i += 2
|
||||||
|
} else if (text.startsWith('\\]', i)) {
|
||||||
|
result += '\\]'
|
||||||
|
i += 2
|
||||||
|
} else {
|
||||||
|
result += text[i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for inline math: $ ... $
|
return result
|
||||||
else if (text.startsWith('$aaaaaaaaa', i)) {
|
}
|
||||||
const endPos = text.indexOf('$', i + 1);
|
|
||||||
if (endPos === -1) {
|
|
||||||
console.error(`LaTeX: Delimiter mismatch (missing $) at position ${i}`);
|
|
||||||
result += text[i];
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
result += `\`$${text.slice(i + 1, endPos)}$\``;
|
|
||||||
i = endPos + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check for inline math: \(...\)
|
|
||||||
else if (text.startsWith('\\(', i)) {
|
|
||||||
const endPos = text.indexOf('\\)', i + 2);
|
|
||||||
if (endPos === -1) {
|
|
||||||
console.error(`LaTeX: Delimiter mismatch (missing \\)) at position ${i}`);
|
|
||||||
result += text[i];
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
result += '`\\(' + text.slice(i + 2, endPos) + '\\)`';
|
|
||||||
i = endPos + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check for display math: \[...\]
|
|
||||||
else if (text.startsWith('\\[', i)) {
|
|
||||||
const endPos = text.indexOf('\\]', i + 2);
|
|
||||||
if (endPos === -1) {
|
|
||||||
console.error(`LaTeX: Delimiter mismatch (missing \\]) at position ${i}`);
|
|
||||||
result += text[i];
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
result += `\`\\[${text.slice(i + 2, endPos)}\\]\``;
|
|
||||||
i = endPos + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise, just copy the current character (also handling backslash escapes)
|
|
||||||
else {
|
|
||||||
if (text.startsWith('\\(', i)) {
|
|
||||||
result += '\\(';
|
|
||||||
i += 2;
|
|
||||||
} else if (text.startsWith('\\)', i)) {
|
|
||||||
result += '\\)';
|
|
||||||
i += 2;
|
|
||||||
} else if (text.startsWith('\\[', i)) {
|
|
||||||
result += '\\[';
|
|
||||||
i += 2;
|
|
||||||
} else if (text.startsWith('\\]', i)) {
|
|
||||||
result += '\\]';
|
|
||||||
i += 2;
|
|
||||||
} else {
|
|
||||||
result += text[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const renderMathMsg = () => {
|
const renderMathMsg = () => {
|
||||||
displayMessage = replaceLatexDelimiters(message.content);
|
displayMessage = replaceLatexDelimiters(message.content)
|
||||||
};
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -349,7 +365,7 @@ const replaceLatexDelimiters = (text: string): string => {
|
|||||||
<div
|
<div
|
||||||
class="message-display"
|
class="message-display"
|
||||||
on:touchend={editOnDoubleTap}
|
on:touchend={editOnDoubleTap}
|
||||||
on:dblclick|preventDefault={() => {if(isUser){edit()}}}
|
on:dblclick|preventDefault={() => { if (isUser) { edit() } }}
|
||||||
>
|
>
|
||||||
{#if message.summary && !message.summary.length}
|
{#if message.summary && !message.summary.length}
|
||||||
<p><b>Summarizing...</b></p>
|
<p><b>Summarizing...</b></p>
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { apiKeyStorage, globalStorage, lastChatId, getChat, started, setGlobalSettingValueByKey, checkStateChange } from './Storage.svelte'
|
import { apiKeyStorage, lastChatId, getChat, started, 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 { afterUpdate, onMount } from 'svelte'
|
import { afterUpdate, onMount } from 'svelte'
|
||||||
import { getPetalsBase, getPetalsWebsocket, getApiBase, setApiBase } from './ApiUtil.svelte'
|
import { getApiBase, setApiBase } from './ApiUtil.svelte'
|
||||||
import { set as setOpenAI } from './providers/openai/util.svelte'
|
import { set as setOpenAI } from './providers/openai/util.svelte'
|
||||||
import { hasActiveModels } from './Models.svelte'
|
import { hasActiveModels } from './Models.svelte'
|
||||||
|
|
||||||
$: apiKey = $apiKeyStorage
|
$: apiKey = $apiKeyStorage
|
||||||
|
|
||||||
let showPetalsSettings = $globalStorage.enablePetals
|
|
||||||
let pedalsEndpoint = $globalStorage.pedalsEndpoint
|
|
||||||
let hasModels = hasActiveModels()
|
let hasModels = hasActiveModels()
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -28,17 +26,9 @@ onMount(() => {
|
|||||||
|
|
||||||
afterUpdate(() => {
|
afterUpdate(() => {
|
||||||
hasModels = hasActiveModels()
|
hasModels = hasActiveModels()
|
||||||
pedalsEndpoint = $globalStorage.pedalsEndpoint
|
|
||||||
$checkStateChange++
|
$checkStateChange++
|
||||||
})
|
})
|
||||||
|
|
||||||
const setPetalsEnabled = (event: Event) => {
|
|
||||||
const el = (event.target as HTMLInputElement)
|
|
||||||
setGlobalSettingValueByKey('enablePetals', !!el.checked)
|
|
||||||
showPetalsSettings = $globalStorage.enablePetals
|
|
||||||
hasModels = hasActiveModels()
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
@@ -53,9 +43,6 @@ const setPetalsEnabled = (event: Event) => {
|
|||||||
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>
|
||||||
<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>
|
|
||||||
<br>
|
<br>
|
||||||
<style>
|
<style>
|
||||||
.katex-version {display: none;}
|
.katex-version {display: none;}
|
||||||
@@ -104,7 +91,6 @@ const setPetalsEnabled = (event: Event) => {
|
|||||||
{#if !apiKey}
|
{#if !apiKey}
|
||||||
<p class:is-danger={!hasModels} class:is-warning={!apiKey}>
|
<p class:is-danger={!hasModels} class:is-warning={!apiKey}>
|
||||||
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.
|
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.
|
||||||
At least one API must be enabled to use ChatGPT-web.
|
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -117,10 +103,10 @@ const setPetalsEnabled = (event: Event) => {
|
|||||||
class="field has-addons has-addons-right"
|
class="field has-addons has-addons-right"
|
||||||
on:submit|preventDefault={(event) => {
|
on:submit|preventDefault={(event) => {
|
||||||
if (event.target && event.target[0].value) {
|
if (event.target && event.target[0].value) {
|
||||||
setApiBase(event.target[0].value);
|
setApiBase(event.target[0].value)
|
||||||
} else {
|
} else {
|
||||||
setApiBase("https://api.openai.com");
|
setApiBase('https://api.openai.com')
|
||||||
event.target[0].value = "https://api.openai.com";
|
event.target[0].value = 'https://api.openai.com'
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -139,70 +125,6 @@ const setPetalsEnabled = (event: Event) => {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="message" class:is-danger={!hasModels} class:is-warning={!showPetalsSettings} class:is-info={showPetalsSettings}>
|
|
||||||
<div class="message-body">
|
|
||||||
<label class="label" for="enablePetals">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="checkbox"
|
|
||||||
id="enablePetals"
|
|
||||||
checked={!!$globalStorage.enablePetals}
|
|
||||||
on:click={setPetalsEnabled}
|
|
||||||
>
|
|
||||||
Use Petals API and Models (Llama 2)
|
|
||||||
</label>
|
|
||||||
{#if showPetalsSettings}
|
|
||||||
<p>Set Petals API Endpoint:</p>
|
|
||||||
<form
|
|
||||||
class="field has-addons has-addons-right"
|
|
||||||
on:submit|preventDefault={(event) => {
|
|
||||||
if (event.target && event.target[0].value) {
|
|
||||||
const v = event.target[0].value.trim()
|
|
||||||
const v2 = v.replace(/^https:/i, 'wss:').replace(/(^wss:\/\/[^/]+)\/*$/i, '$1' + getPetalsWebsocket())
|
|
||||||
setGlobalSettingValueByKey('pedalsEndpoint', v2)
|
|
||||||
event.target[0].value = v2
|
|
||||||
} else {
|
|
||||||
setGlobalSettingValueByKey('pedalsEndpoint', '')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p class="control is-expanded">
|
|
||||||
<input
|
|
||||||
aria-label="PetalsAPI Endpoint"
|
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
placeholder={getPetalsBase() + getPetalsWebsocket()}
|
|
||||||
value={$globalStorage.pedalsEndpoint || ''}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
<p class="control">
|
|
||||||
<button class="button is-info" type="submit">Save</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{#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 class="mb-4">
|
|
||||||
You are encouraged to <a target="_blank" href="https://github.com/bigscience-workshop/petals#connect-your-gpu-and-increase-petals-capacity">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 1080 8GB, but the larger/faster the better.
|
|
||||||
</p>
|
|
||||||
<p class="mb-4">
|
|
||||||
If you're receiving errors while using Petals, <a target="_blank" href="https://health.petals.dev/">check swarm health</a> and consider <a target="_blank" href="https://github.com/bigscience-workshop/petals#connect-your-gpu-and-increase-petals-capacity">adding your GPU to the swarm</a> to help.
|
|
||||||
</p>
|
|
||||||
<p class="help is-warning">
|
|
||||||
Because Petals uses a public swarm, <b>do not send sensitive information</b> when using Petals.
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{#if apiKey}
|
{#if apiKey}
|
||||||
<article class="message is-info">
|
<article class="message is-info">
|
||||||
<div class="message-body">
|
<div class="message-body">
|
||||||
|
|||||||
@@ -10,10 +10,15 @@
|
|||||||
|
|
||||||
$: chatSettings = chat.settings
|
$: chatSettings = chat.settings
|
||||||
|
|
||||||
|
// Pre-compute filtered messages to avoid complex filtering in template
|
||||||
|
$: filteredMessages = messages.filter((message, i) => {
|
||||||
|
const isHiddenSummarized = (message.summarized) && $globalStorage.hideSummarized
|
||||||
|
const isHiddenSystemPrompt = i === 0 && message.role === 'system' && !chatSettings.useSystemPrompt
|
||||||
|
return !isHiddenSummarized && !isHiddenSystemPrompt
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each messages as message, i}
|
{#each filteredMessages as message}
|
||||||
{#if !((message.summarized) && $globalStorage.hideSummarized) && !(i === 0 && message.role === 'system' && !chatSettings.useSystemPrompt)}
|
|
||||||
{#key message.uuid}<EditMessage bind:message={message} chatId={chatId} chat={chat} />{/key}
|
{#key message.uuid}<EditMessage bind:message={message} chatId={chatId} chat={chat} />{/key}
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
import { apiKeyStorage, globalStorage } from './Storage.svelte'
|
import { apiKeyStorage } from './Storage.svelte'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
import type { ModelDetail, Model, SelectOption, Chat } from './Types.svelte'
|
import type { ModelDetail, Model, SelectOption, Chat } from './Types.svelte'
|
||||||
import { mergeProfileFields } from './Profiles.svelte'
|
import { mergeProfileFields } from './Profiles.svelte'
|
||||||
@@ -13,7 +13,7 @@ const unknownDetail = {
|
|||||||
} as ModelDetail
|
} as ModelDetail
|
||||||
|
|
||||||
export const supportedChatModels : Record<string, ModelDetail> = {
|
export const supportedChatModels : Record<string, ModelDetail> = {
|
||||||
...openAiModels,
|
...openAiModels
|
||||||
// ...petalsModels
|
// ...petalsModels
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +144,7 @@ export const countTokens = (model: Model, value: string): number => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const hasActiveModels = (): boolean => {
|
export const hasActiveModels = (): boolean => {
|
||||||
const globalSettings = get(globalStorage) || {}
|
return !!get(apiKeyStorage)
|
||||||
return !!get(apiKeyStorage) || !!globalSettings.enablePetals
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getChatModelOptions (): Promise<SelectOption[]> {
|
export async function getChatModelOptions (): Promise<SelectOption[]> {
|
||||||
@@ -155,12 +154,12 @@ export async function getChatModelOptions (): Promise<SelectOption[]> {
|
|||||||
const model = models[i]
|
const model = models[i]
|
||||||
const modelDetail = getModelDetail(model)
|
const modelDetail = getModelDetail(model)
|
||||||
await modelDetail.check(modelDetail)
|
await modelDetail.check(modelDetail)
|
||||||
if(modelDetail.enabled){
|
if (modelDetail.enabled) {
|
||||||
result.push({
|
result.push({
|
||||||
value: model,
|
value: model,
|
||||||
text: modelDetail.label || model,
|
text: modelDetail.label || model,
|
||||||
disabled: !modelDetail.enabled
|
disabled: !modelDetail.enabled
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
import { applyProfile } from './Profiles.svelte'
|
import { applyProfile } from './Profiles.svelte'
|
||||||
import { get } from 'svelte/store'
|
import { getChatSettings, getGlobalSettings, setGlobalSettingValueByKey } from './Storage.svelte'
|
||||||
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
|
||||||
|
|
||||||
@@ -21,10 +20,8 @@ import {
|
|||||||
import { getModelDetail, getTokens } from './Models.svelte'
|
import { getModelDetail, getTokens } from './Models.svelte'
|
||||||
|
|
||||||
const defaultModel:Model = 'gpt-4'
|
const defaultModel:Model = 'gpt-4'
|
||||||
const defaultModelPetals:Model = 'stabilityai/StableBeluga2'
|
|
||||||
|
|
||||||
export const getDefaultModel = (): Model => {
|
export const getDefaultModel = (): Model => {
|
||||||
if (!get(apiKeyStorage)) return defaultModelPetals
|
|
||||||
return defaultModel
|
return defaultModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ export const getExcludeFromProfile = () => {
|
|||||||
return excludeFromProfile
|
return excludeFromProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideModelSetting = (chatId, setting) => {
|
const hideModelSetting = (chatId: number, setting: ChatSetting) => {
|
||||||
return getModelDetail(getChatSettings(chatId).model).hideSetting(chatId, setting)
|
return getModelDetail(getChatSettings(chatId).model).hideSetting(chatId, setting)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,9 +131,7 @@ export const globalDefaults: GlobalSettings = {
|
|||||||
defaultProfile: 'default',
|
defaultProfile: 'default',
|
||||||
hideSummarized: false,
|
hideSummarized: false,
|
||||||
chatSort: 'created',
|
chatSort: 'created',
|
||||||
openAICompletionEndpoint: '',
|
openAICompletionEndpoint: ''
|
||||||
enablePetals: false,
|
|
||||||
pedalsEndpoint: ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const excludeFromProfile = {
|
const excludeFromProfile = {
|
||||||
@@ -711,16 +706,6 @@ const globalSettingsList:GlobalSetting[] = [
|
|||||||
key: 'openAICompletionEndpoint',
|
key: 'openAICompletionEndpoint',
|
||||||
name: 'OpenAI Completions Endpoint',
|
name: 'OpenAI Completions Endpoint',
|
||||||
type: 'text'
|
type: 'text'
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'enablePetals',
|
|
||||||
name: 'Enable Petals APIs',
|
|
||||||
type: 'boolean'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pedalsEndpoint',
|
|
||||||
name: 'Petals API Endpoint',
|
|
||||||
type: 'text'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,33 @@
|
|||||||
import { startNewChatWithWarning } from './Util.svelte'
|
import { startNewChatWithWarning } from './Util.svelte'
|
||||||
import { chatSortOptions } from './Settings.svelte'
|
import { chatSortOptions } from './Settings.svelte'
|
||||||
import { hasActiveModels } from './Models.svelte'
|
import { hasActiveModels } from './Models.svelte'
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
// Cache sorted chats to avoid expensive sorting on every update
|
||||||
|
let sortedChats: Chat[] = []
|
||||||
|
let lastSortOption: any = null
|
||||||
|
let lastChatsLength = 0
|
||||||
|
|
||||||
$: sortedChats = $chatsStorage.sort(getChatSortOption().sortFn)
|
|
||||||
$: 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()
|
let hasModels = hasActiveModels()
|
||||||
|
|
||||||
|
// Only re-sort when sort option changes or chats are added/removed
|
||||||
|
$: {
|
||||||
|
const currentSortOption = getChatSortOption()
|
||||||
|
const chatsChanged = $chatsStorage.length !== lastChatsLength
|
||||||
|
const sortChanged = !lastSortOption || lastSortOption.value !== currentSortOption.value
|
||||||
|
|
||||||
|
if (sortChanged || chatsChanged) {
|
||||||
|
sortedChats = [...$chatsStorage].sort(currentSortOption.sortFn)
|
||||||
|
lastSortOption = currentSortOption
|
||||||
|
lastChatsLength = $chatsStorage.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onStateChange = (...args:any) => {
|
const onStateChange = (...args:any) => {
|
||||||
sortOption = getChatSortOption()
|
sortOption = getChatSortOption()
|
||||||
sortedChats = $chatsStorage.sort(sortOption.sortFn)
|
|
||||||
hasModels = hasActiveModels()
|
hasModels = hasActiveModels()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,127 +44,124 @@
|
|||||||
|
|
||||||
let showSortMenu = false
|
let showSortMenu = false
|
||||||
|
|
||||||
async function uploadLocalStorage(uid = 19492){
|
async function uploadLocalStorage (uid = 19492) {
|
||||||
try {
|
try {
|
||||||
let storageObject = {};
|
const storageObject = {}
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i)
|
||||||
if (key) {
|
if (key) {
|
||||||
storageObject[key] = localStorage.getItem(key);
|
storageObject[key] = localStorage.getItem(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const response = await fetch(`https://api.morgan.kr/localstore/${uid}`, {
|
const response = await fetch(`https://api.morgan.kr/localstore/${uid}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({data: storageObject}),
|
body: JSON.stringify({ data: storageObject })
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok.');
|
throw new Error('Network response was not ok.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json()
|
||||||
console.log(data)
|
console.log(data)
|
||||||
console.log("Uploaded savedata.");
|
console.log('Uploaded savedata.')
|
||||||
alert("Uploaded savedata.");
|
alert('Uploaded savedata.')
|
||||||
return data.id;
|
return data.id
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error uploading localStorage:', error);
|
console.error('Error uploading localStorage:', error)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchLocalStorage(){
|
async function fetchLocalStorage () {
|
||||||
if (!confirm("This will override all local data. Proceed?")) {
|
if (!confirm('This will override all local data. Proceed?')) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// dumpLocalStorage();
|
// dumpLocalStorage();
|
||||||
await uploadLocalStorage(99999);
|
await uploadLocalStorage(99999)
|
||||||
const response = await fetch('https://api.morgan.kr/localstore/19492', {
|
const response = await fetch('https://api.morgan.kr/localstore/19492', {
|
||||||
method: 'GET',
|
method: 'GET'
|
||||||
});
|
})
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok.');
|
throw new Error('Network response was not ok.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const newData = await response.json();
|
const newData = await response.json()
|
||||||
localStorage.clear();
|
localStorage.clear()
|
||||||
|
|
||||||
Object.entries(newData).forEach(([key, value]) => {
|
Object.entries(newData).forEach(([key, value]) => {
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(key, value)
|
||||||
});
|
})
|
||||||
|
|
||||||
console.log('Fetched savedata');
|
|
||||||
alert('Fetched savedata');
|
|
||||||
|
|
||||||
|
console.log('Fetched savedata')
|
||||||
|
alert('Fetched savedata')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching localStorage:', error);
|
console.error('Error fetching localStorage:', error)
|
||||||
alert(error);
|
alert(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncLocalStorage(){
|
async function syncLocalStorage () {
|
||||||
console.log("Syncing...")
|
console.log('Syncing...')
|
||||||
uploadLocalStorage();
|
uploadLocalStorage()
|
||||||
localStorage.setItem('lastModified', new Date().toISOString());
|
localStorage.setItem('lastModified', new Date().toISOString())
|
||||||
}
|
}
|
||||||
|
|
||||||
function dumpLocalStorage(){
|
function dumpLocalStorage () {
|
||||||
try {
|
try {
|
||||||
let storageObject = {};
|
const storageObject = {}
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i)
|
||||||
if (key) {
|
if (key) {
|
||||||
storageObject[key] = localStorage.getItem(key);
|
storageObject[key] = localStorage.getItem(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataStr = JSON.stringify(storageObject, null, 2);
|
const dataStr = JSON.stringify(storageObject, null, 2)
|
||||||
const blob = new Blob([dataStr], { type: "application/json" });
|
const blob = new Blob([dataStr], { type: 'application/json' })
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob)
|
||||||
const link = document.createElement("a");
|
const link = document.createElement('a')
|
||||||
link.href = url;
|
link.href = url
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_');
|
const dateTimeStr = now.toISOString().replace(/:\d+\.\d+Z$/, '').replace(/-|:/g, '_')
|
||||||
link.download = `ChatGPT-web-${dateTimeStr}.json`;
|
link.download = `ChatGPT-web-${dateTimeStr}.json`
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link)
|
||||||
link.click();
|
link.click()
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error dumping localStorage:', error);
|
console.error('Error dumping localStorage:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLocalStorage() {
|
function loadLocalStorage () {
|
||||||
var fileInput = document.createElement('input');
|
const fileInput = document.createElement('input')
|
||||||
fileInput.type = "file";
|
fileInput.type = 'file'
|
||||||
fileInput.addEventListener('change', function(e) {
|
fileInput.addEventListener('change', function (e) {
|
||||||
var file = e.target.files[0];
|
const file = e.target.files[0]
|
||||||
if (file) {
|
if (file) {
|
||||||
var reader = new FileReader();
|
const reader = new FileReader()
|
||||||
reader.onload = function(e) {
|
reader.onload = function (e) {
|
||||||
var data = JSON.parse(e.target.result);
|
const data = JSON.parse(e.target.result)
|
||||||
Object.keys(data).forEach(function(key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
localStorage.setItem(key, data[key]);
|
localStorage.setItem(key, data[key])
|
||||||
});
|
})
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
};
|
}
|
||||||
reader.readAsText(file);
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
document.body.appendChild(fileInput);
|
document.body.appendChild(fileInput)
|
||||||
fileInput.click();
|
fileInput.click()
|
||||||
fileInput.remove();
|
fileInput.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// console.log('Downloading from server.');
|
// console.log('Downloading from server.');
|
||||||
// fetchLocalStorage();
|
// fetchLocalStorage();
|
||||||
});
|
})
|
||||||
|
|
||||||
// setInterval(syncLocalStorage, 10000);
|
// setInterval(syncLocalStorage, 10000);
|
||||||
</script>
|
</script>
|
||||||
@@ -198,12 +211,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-left is-up ml-2">
|
<div class="is-left is-up ml-2">
|
||||||
<button class="button" aria-haspopup="true" on:click|preventDefault|stopPropagation={() => { loadLocalStorage(); }}>
|
<button class="button" aria-haspopup="true" on:click|preventDefault|stopPropagation={() => { loadLocalStorage() }}>
|
||||||
<span class="icon"><Fa icon={faUpload}/></span>
|
<span class="icon"><Fa icon={faUpload}/></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-left is-up ml-2">
|
<div class="is-left is-up ml-2">
|
||||||
<button class="button" aria-haspopup="true" on:click|preventDefault|stopPropagation={() => { dumpLocalStorage(); }}>
|
<button class="button" aria-haspopup="true" on:click|preventDefault|stopPropagation={() => { dumpLocalStorage() }}>
|
||||||
<span class="icon"><Fa icon={faDownload}/></span>
|
<span class="icon"><Fa icon={faDownload}/></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -246,29 +246,43 @@
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
const signalChangeTimers: any = {}
|
const signalChangeTimers = new Map<number, any>()
|
||||||
const setChatLastUse = (chatId: number, time: number) => {
|
const setChatLastUse = (chatId: number, time: number) => {
|
||||||
clearTimeout(signalChangeTimers[chatId])
|
const existingTimer = signalChangeTimers.get(chatId)
|
||||||
signalChangeTimers[chatId] = setTimeout(() => {
|
if (existingTimer) {
|
||||||
|
clearTimeout(existingTimer)
|
||||||
|
}
|
||||||
|
const timer = setTimeout(() => {
|
||||||
getChat(chatId).lastUse = time
|
getChat(chatId).lastUse = time
|
||||||
saveChatStore()
|
saveChatStore()
|
||||||
|
signalChangeTimers.delete(chatId)
|
||||||
}, 500)
|
}, 500)
|
||||||
|
signalChangeTimers.set(chatId, timer)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setMessagesTimers: any = {}
|
const setMessagesTimers = new Map<number, any>()
|
||||||
export const setMessages = (chatId: number, messages: Message[]) => {
|
export const setMessages = (chatId: number, messages: Message[]) => {
|
||||||
if (get(currentChatId) === chatId) {
|
if (get(currentChatId) === chatId) {
|
||||||
// update current message cache right away
|
// update current message cache right away
|
||||||
currentChatMessages.set(messages)
|
currentChatMessages.set(messages)
|
||||||
clearTimeout(setMessagesTimers[chatId])
|
const existingTimer = setMessagesTimers.get(chatId)
|
||||||
|
if (existingTimer) {
|
||||||
|
clearTimeout(existingTimer)
|
||||||
|
}
|
||||||
// delay expensive all chats update for a bit
|
// delay expensive all chats update for a bit
|
||||||
setMessagesTimers[chatId] = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
getChat(chatId).messages = messages
|
getChat(chatId).messages = messages
|
||||||
saveChatStore()
|
saveChatStore()
|
||||||
setChatLastUse(chatId, Date.now())
|
setChatLastUse(chatId, Date.now())
|
||||||
|
setMessagesTimers.delete(chatId)
|
||||||
}, 200)
|
}, 200)
|
||||||
|
setMessagesTimers.set(chatId, timer)
|
||||||
} else {
|
} else {
|
||||||
clearTimeout(setMessagesTimers[chatId])
|
const existingTimer = setMessagesTimers.get(chatId)
|
||||||
|
if (existingTimer) {
|
||||||
|
clearTimeout(existingTimer)
|
||||||
|
setMessagesTimers.delete(chatId)
|
||||||
|
}
|
||||||
getChat(chatId).messages = messages
|
getChat(chatId).messages = messages
|
||||||
saveChatStore()
|
saveChatStore()
|
||||||
setChatLastUse(chatId, Date.now())
|
setChatLastUse(chatId, Date.now())
|
||||||
@@ -279,6 +293,24 @@
|
|||||||
setMessages(chatId, getMessages(chatId))
|
setMessages(chatId, getMessages(chatId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup function to clear all timers and prevent memory leaks
|
||||||
|
export const clearAllTimers = () => {
|
||||||
|
if (setChatTimer) {
|
||||||
|
clearTimeout(setChatTimer)
|
||||||
|
setChatTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
signalChangeTimers.forEach((timer) => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
})
|
||||||
|
signalChangeTimers.clear()
|
||||||
|
|
||||||
|
setMessagesTimers.forEach((timer) => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
})
|
||||||
|
setMessagesTimers.clear()
|
||||||
|
}
|
||||||
|
|
||||||
export const addError = (chatId: number, error: string) => {
|
export const addError = (chatId: number, error: string) => {
|
||||||
addMessage(chatId, { content: error } as Message)
|
addMessage(chatId, { content: error } as Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,8 +160,6 @@ export type GlobalSettings = {
|
|||||||
hideSummarized: boolean;
|
hideSummarized: boolean;
|
||||||
chatSort: ChatSortOptions;
|
chatSort: ChatSortOptions;
|
||||||
openAICompletionEndpoint: string;
|
openAICompletionEndpoint: string;
|
||||||
enablePetals: boolean;
|
|
||||||
pedalsEndpoint: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingNumber = {
|
type SettingNumber = {
|
||||||
|
|||||||
@@ -6,10 +6,25 @@
|
|||||||
import { replace } from 'svelte-spa-router'
|
import { replace } from 'svelte-spa-router'
|
||||||
// import PromptConfirm from './PromptConfirm.svelte'
|
// import PromptConfirm from './PromptConfirm.svelte'
|
||||||
import type { ChatSettings } from './Types.svelte'
|
import type { ChatSettings } from './Types.svelte'
|
||||||
|
// Cache for auto-size elements to avoid expensive DOM queries
|
||||||
|
let cachedAutoSizeElements: HTMLTextAreaElement[] = []
|
||||||
|
let lastElementCount = 0
|
||||||
|
|
||||||
export const sizeTextElements = (force?: boolean) => {
|
export const sizeTextElements = (force?: boolean) => {
|
||||||
const els = document.querySelectorAll('textarea.auto-size')
|
// Only re-query if force is true or element count changed
|
||||||
for (let i:number = 0, l = els.length; i < l; i++) {
|
const currentElements = document.querySelectorAll('textarea.auto-size')
|
||||||
autoGrowInput(els[i] as HTMLTextAreaElement, force)
|
if (force || currentElements.length !== lastElementCount) {
|
||||||
|
cachedAutoSizeElements = Array.from(currentElements) as HTMLTextAreaElement[]
|
||||||
|
lastElementCount = currentElements.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use cached elements for better performance
|
||||||
|
for (let i = 0, l = cachedAutoSizeElements.length; i < l; i++) {
|
||||||
|
const el = cachedAutoSizeElements[i]
|
||||||
|
// Check if element is still in DOM
|
||||||
|
if (document.contains(el)) {
|
||||||
|
autoGrowInput(el, force)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +1,82 @@
|
|||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
|
|
||||||
import { getApiBase, getEndpointCompletions, getEndpointGenerations } from "../../ApiUtil.svelte";
|
import { getApiBase, getEndpointCompletions, getEndpointGenerations } from '../../ApiUtil.svelte'
|
||||||
import { countTokens } from "../../Models.svelte";
|
import { countTokens } from '../../Models.svelte'
|
||||||
import { countMessageTokens } from "../../Stats.svelte";
|
import { countMessageTokens } from '../../Stats.svelte'
|
||||||
import { globalStorage } from "../../Storage.svelte";
|
import { globalStorage } from '../../Storage.svelte'
|
||||||
import type { Chat, Message, Model, ModelDetail } from "../../Types.svelte";
|
import type { Chat, Message, Model, ModelDetail } from '../../Types.svelte'
|
||||||
import { chatRequest, imageRequest } from "./request.svelte";
|
import { chatRequest, imageRequest } from './request.svelte'
|
||||||
import { checkModel } from "./util.svelte";
|
import { checkModel } from './util.svelte'
|
||||||
import { encode } from "gpt-tokenizer";
|
// Lazy-load tokenizer to improve initial load time
|
||||||
import { get } from "svelte/store";
|
let encode: any = null
|
||||||
import chatModelsJson from './models.json';
|
|
||||||
|
// Simple token approximation for faster initial loads
|
||||||
|
const approximateTokens = (text: string): number[] => {
|
||||||
|
// Rough approximation: 1 token ≈ 4 characters for most text
|
||||||
|
return new Array(Math.ceil(text.length / 4)).fill(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTokenizer = async () => {
|
||||||
|
if (!encode) {
|
||||||
|
const tokenizer = await import('gpt-tokenizer')
|
||||||
|
encode = tokenizer.encode
|
||||||
|
}
|
||||||
|
return encode
|
||||||
|
}
|
||||||
|
import { get } from 'svelte/store'
|
||||||
|
import chatModelsJson from './models.json'
|
||||||
|
|
||||||
const hiddenSettings = {
|
const hiddenSettings = {
|
||||||
startSequence: true,
|
startSequence: true,
|
||||||
stopSequence: true,
|
stopSequence: true,
|
||||||
aggressiveStop: true,
|
aggressiveStop: true,
|
||||||
delimiter: true,
|
delimiter: true,
|
||||||
userMessageStart: true,
|
userMessageStart: true,
|
||||||
userMessageEnd: true,
|
userMessageEnd: true,
|
||||||
assistantMessageStart: true,
|
assistantMessageStart: true,
|
||||||
assistantMessageEnd: true,
|
assistantMessageEnd: true,
|
||||||
systemMessageStart: true,
|
systemMessageStart: true,
|
||||||
systemMessageEnd: true,
|
systemMessageEnd: true,
|
||||||
repetitionPenalty: true,
|
repetitionPenalty: true,
|
||||||
holdSocket: true,
|
holdSocket: true
|
||||||
// leadPrompt: true
|
// leadPrompt: true
|
||||||
} as any;
|
} as any
|
||||||
|
|
||||||
const chatModelBase = {
|
const chatModelBase = {
|
||||||
type: "chat",
|
type: 'chat',
|
||||||
help: '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.',
|
help: '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.',
|
||||||
preFillMerge: (existingContent, newContent) => {
|
preFillMerge: (existingContent, newContent) => {
|
||||||
if (existingContent && !newContent.match(/^('(t|ll|ve|m|d|re)[^a-z]|\s|[.,;:(_-{}*^%$#@!?+=~`[\]])/i)) {
|
if (existingContent && !newContent.match(/^('(t|ll|ve|m|d|re)[^a-z]|\s|[.,;:(_-{}*^%$#@!?+=~`[\]])/i)) {
|
||||||
existingContent += " ";
|
existingContent += ' '
|
||||||
}
|
}
|
||||||
return existingContent;
|
return existingContent
|
||||||
},
|
},
|
||||||
request: chatRequest,
|
request: chatRequest,
|
||||||
check: checkModel,
|
check: checkModel,
|
||||||
getTokens: (value) => encode(value),
|
getTokens: (value) => {
|
||||||
getEndpoint: (model) => get(globalStorage).openAICompletionEndpoint || getApiBase() + getEndpointCompletions(),
|
// Use approximation for faster initial loads, actual tokenizer loads async
|
||||||
hideSetting: (chatId, setting) => !!hiddenSettings[setting.key],
|
if (!encode) {
|
||||||
countMessageTokens: (message: Message, model: Model, chat: Chat) => {
|
getTokenizer() // Start loading tokenizer for future use
|
||||||
return countTokens(model, "## " + message.role + " ##:\r\n\r\n" + message.content + "\r\n\r\n\r\n");
|
return approximateTokens(value)
|
||||||
},
|
}
|
||||||
countPromptTokens: (prompts: Message[], model: Model, chat: Chat): number => {
|
return encode(value)
|
||||||
return (
|
},
|
||||||
prompts.reduce((a, m) => {
|
getEndpoint: (model) => get(globalStorage).openAICompletionEndpoint || getApiBase() + getEndpointCompletions(),
|
||||||
a += countMessageTokens(m, model, chat);
|
hideSetting: (chatId, setting) => !!hiddenSettings[setting.key],
|
||||||
return a;
|
countMessageTokens: (message: Message, model: Model, chat: Chat) => {
|
||||||
}, 0) + 3
|
return countTokens(model, '## ' + message.role + ' ##:\r\n\r\n' + message.content + '\r\n\r\n\r\n')
|
||||||
);
|
},
|
||||||
},
|
countPromptTokens: (prompts: Message[], model: Model, chat: Chat): number => {
|
||||||
} as ModelDetail;
|
return (
|
||||||
|
prompts.reduce((a, m) => {
|
||||||
|
a += countMessageTokens(m, model, chat)
|
||||||
|
return a
|
||||||
|
}, 0) + 3
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} as ModelDetail
|
||||||
|
|
||||||
export const chatModels: Record<string, ModelDetail> = {};
|
export const chatModels: Record<string, ModelDetail> = {}
|
||||||
|
|
||||||
for (const [key, { prompt, completion, max, reasoning, alias }] of Object.entries(chatModelsJson)) {
|
for (const [key, { prompt, completion, max, reasoning, alias }] of Object.entries(chatModelsJson)) {
|
||||||
chatModels[key] = {
|
chatModels[key] = {
|
||||||
@@ -63,101 +85,101 @@
|
|||||||
completion: completion / 1_000_000,
|
completion: completion / 1_000_000,
|
||||||
max,
|
max,
|
||||||
...(reasoning ? { reasoning } : {}),
|
...(reasoning ? { reasoning } : {}),
|
||||||
...(alias ? { alias } : {}),
|
...(alias ? { alias } : {})
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageModelBase = {
|
const imageModelBase = {
|
||||||
type: "image",
|
type: 'image',
|
||||||
prompt: 0.0,
|
prompt: 0.0,
|
||||||
max: 1000, // 1000 char prompt, max
|
max: 1000, // 1000 char prompt, max
|
||||||
request: imageRequest,
|
request: imageRequest,
|
||||||
check: checkModel,
|
check: checkModel,
|
||||||
getTokens: (value) => [0],
|
getTokens: (value) => [0],
|
||||||
getEndpoint: (model) => getApiBase() + getEndpointGenerations(),
|
getEndpoint: (model) => getApiBase() + getEndpointGenerations(),
|
||||||
hideSetting: (chatId, setting) => false,
|
hideSetting: (chatId, setting) => false
|
||||||
} as ModelDetail;
|
} as ModelDetail
|
||||||
|
|
||||||
export const imageModels: Record<string, ModelDetail> = {
|
export const imageModels: Record<string, ModelDetail> = {
|
||||||
"dall-e-1024x1024": {
|
'dall-e-1024x1024': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
completion: 0.02, // $0.020 per image
|
completion: 0.02, // $0.020 per image
|
||||||
opt: {
|
opt: {
|
||||||
size: "1024x1024",
|
size: '1024x1024'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-512x512": {
|
'dall-e-512x512': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
completion: 0.018, // $0.018 per image
|
completion: 0.018, // $0.018 per image
|
||||||
opt: {
|
opt: {
|
||||||
size: "512x512",
|
size: '512x512'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-256x256": {
|
'dall-e-256x256': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.016, // $0.016 per image
|
completion: 0.016, // $0.016 per image
|
||||||
opt: {
|
opt: {
|
||||||
size: "256x256",
|
size: '256x256'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1024x1024": {
|
'dall-e-3-1024x1024': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.04, // $0.040 per image
|
completion: 0.04, // $0.040 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1024x1024",
|
size: '1024x1024'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1024x1792-Portrait": {
|
'dall-e-3-1024x1792-Portrait': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.08, // $0.080 per image
|
completion: 0.08, // $0.080 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1024x1792",
|
size: '1024x1792'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1792x1024-Landscape": {
|
'dall-e-3-1792x1024-Landscape': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.08, // $0.080 per image
|
completion: 0.08, // $0.080 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1792x1024",
|
size: '1792x1024'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1024x1024-HD": {
|
'dall-e-3-1024x1024-HD': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.08, // $0.080 per image
|
completion: 0.08, // $0.080 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1024x1024",
|
size: '1024x1024',
|
||||||
quality: "hd",
|
quality: 'hd'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1024x1792-Portrait-HD": {
|
'dall-e-3-1024x1792-Portrait-HD': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.12, // $0.080 per image
|
completion: 0.12, // $0.080 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1024x1792",
|
size: '1024x1792',
|
||||||
quality: "hd",
|
quality: 'hd'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"dall-e-3-1792x1024-Landscape-HD": {
|
'dall-e-3-1792x1024-Landscape-HD': {
|
||||||
...imageModelBase,
|
...imageModelBase,
|
||||||
type: "image",
|
type: 'image',
|
||||||
completion: 0.12, // $0.080 per image
|
completion: 0.12, // $0.080 per image
|
||||||
opt: {
|
opt: {
|
||||||
model: "dall-e-3",
|
model: 'dall-e-3',
|
||||||
size: "1792x1024",
|
size: '1792x1024',
|
||||||
quality: "hd",
|
quality: 'hd'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -33,8 +33,8 @@ export const chatRequest = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (modelDetail.stream === false) {
|
if (modelDetail.stream === false) {
|
||||||
opts.streaming = false;
|
opts.streaming = false
|
||||||
console.log("Disabled streaming on reasoning models.");
|
console.log('Disabled streaming on reasoning models.')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.streaming && !modelDetail.stream) {
|
if (opts.streaming && !modelDetail.stream) {
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import purgecss from '@fullhuman/postcss-purgecss'
|
|||||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
svelte(),
|
svelte(),
|
||||||
dsv(),
|
dsv()
|
||||||
// visualizer({
|
// visualizer({
|
||||||
// open: true,
|
// open: true,
|
||||||
// gzipSize: true,
|
// gzipSize: true,
|
||||||
// brotliSize: true,
|
// brotliSize: true,
|
||||||
// }),
|
// }),
|
||||||
]
|
]
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
|
|||||||
Reference in New Issue
Block a user