forked from open-webui/open-webui
chore: refac
This commit is contained in:
parent
f30d16a3fd
commit
cc49e0d10f
11 changed files with 239 additions and 397 deletions
|
@ -31,7 +31,7 @@ export const createNewChat = async (token: string, chat: object) => {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getChatlist = async (token: string = '') => {
|
export const getChatList = async (token: string = '') => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/`, {
|
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/`, {
|
||||||
|
|
|
@ -69,3 +69,68 @@ export const getOllamaModels = async (
|
||||||
|
|
||||||
return res?.models ?? [];
|
return res?.models ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const generateTitle = async (
|
||||||
|
base_url: string = OLLAMA_API_BASE_URL,
|
||||||
|
token: string = '',
|
||||||
|
model: string,
|
||||||
|
prompt: string
|
||||||
|
) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${base_url}/generate`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: model,
|
||||||
|
prompt: `Generate a brief 3-5 word title for this question, excluding the term 'title.' Then, please reply with only the title: ${prompt}`,
|
||||||
|
stream: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
if ('detail' in err) {
|
||||||
|
error = err.detail;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res?.response ?? 'New Chat';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateChatCompletion = async (
|
||||||
|
base_url: string = OLLAMA_API_BASE_URL,
|
||||||
|
token: string = '',
|
||||||
|
body: object
|
||||||
|
) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${base_url}/chat`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
}).catch((err) => {
|
||||||
|
error = err;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
|
@ -27,8 +27,6 @@ export const getOpenAIModels = async (
|
||||||
|
|
||||||
let models = Array.isArray(res) ? res : res?.data ?? null;
|
let models = Array.isArray(res) ? res : res?.data ?? null;
|
||||||
|
|
||||||
console.log(models);
|
|
||||||
|
|
||||||
return models
|
return models
|
||||||
.map((model) => ({ name: model.id, external: true }))
|
.map((model) => ({ name: model.id, external: true }))
|
||||||
.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true));
|
.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true));
|
||||||
|
|
|
@ -8,10 +8,11 @@
|
||||||
import auto_render from 'katex/dist/contrib/auto-render.mjs';
|
import auto_render from 'katex/dist/contrib/auto-render.mjs';
|
||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
|
|
||||||
import { config, db, modelfiles, settings, user } from '$lib/stores';
|
import { chats, config, db, modelfiles, settings, user } from '$lib/stores';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
|
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
import { getChatList, updateChatById } from '$lib/apis/chats';
|
||||||
|
|
||||||
export let chatId = '';
|
export let chatId = '';
|
||||||
export let sendPrompt: Function;
|
export let sendPrompt: Function;
|
||||||
|
@ -262,10 +263,12 @@
|
||||||
return message;
|
return message;
|
||||||
});
|
});
|
||||||
|
|
||||||
$db.updateChatById(chatId, {
|
await updateChatById(localStorage.token, chatId, {
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
|
|
||||||
const showPreviousMessage = async (message) => {
|
const showPreviousMessage = async (message) => {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { getChatById } from '$lib/apis/chats';
|
||||||
|
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import { chatId, db, modelfiles } from '$lib/stores';
|
import { chatId, db, modelfiles } from '$lib/stores';
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
|
@ -10,10 +8,10 @@
|
||||||
export let shareEnabled: boolean = false;
|
export let shareEnabled: boolean = false;
|
||||||
|
|
||||||
const shareChat = async () => {
|
const shareChat = async () => {
|
||||||
const chat = (await $db.getChatById($chatId)).chat;
|
const chat = (await getChatById(localStorage.token, $chatId)).chat;
|
||||||
console.log('share', chat);
|
console.log('share', chat);
|
||||||
toast.success('Redirecting you to OllamaHub');
|
|
||||||
|
|
||||||
|
toast.success('Redirecting you to OllamaHub');
|
||||||
const url = 'https://ollamahub.com';
|
const url = 'https://ollamahub.com';
|
||||||
// const url = 'http://localhost:5173';
|
// const url = 'http://localhost:5173';
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { user, db, chats, showSettings, chatId } from '$lib/stores';
|
import { user, db, chats, showSettings, chatId } from '$lib/stores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { deleteChatById, getChatList, updateChatById } from '$lib/apis/chats';
|
||||||
|
|
||||||
let show = false;
|
let show = false;
|
||||||
let navElement;
|
let navElement;
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
show = true;
|
show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await chats.set(await $db.getChats());
|
await chats.set(await getChatList(localStorage.token));
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadChat = async (id) => {
|
const loadChat = async (id) => {
|
||||||
|
@ -39,42 +40,46 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const editChatTitle = async (id, _title) => {
|
const editChatTitle = async (id, _title) => {
|
||||||
await $db.updateChatById(id, {
|
title = _title;
|
||||||
|
|
||||||
|
await updateChatById(localStorage.token, id, {
|
||||||
title: _title
|
title: _title
|
||||||
});
|
});
|
||||||
title = _title;
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteChat = async (id) => {
|
const deleteChat = async (id) => {
|
||||||
goto('/');
|
goto('/');
|
||||||
$db.deleteChatById(id);
|
|
||||||
|
await deleteChatById(localStorage.token, id);
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteChatHistory = async () => {
|
// const deleteChatHistory = async () => {
|
||||||
await $db.deleteAllChat();
|
// await $db.deleteAllChat();
|
||||||
};
|
// };
|
||||||
|
|
||||||
const importChats = async (chatHistory) => {
|
// const importChats = async (chatHistory) => {
|
||||||
await $db.importChats(chatHistory);
|
// await $db.importChats(chatHistory);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const exportChats = async () => {
|
// const exportChats = async () => {
|
||||||
let blob = new Blob([JSON.stringify(await $db.exportChats())], { type: 'application/json' });
|
// let blob = new Blob([JSON.stringify(await $db.exportChats())], { type: 'application/json' });
|
||||||
saveAs(blob, `chat-export-${Date.now()}.json`);
|
// saveAs(blob, `chat-export-${Date.now()}.json`);
|
||||||
};
|
// };
|
||||||
|
|
||||||
$: if (importFiles) {
|
// $: if (importFiles) {
|
||||||
console.log(importFiles);
|
// console.log(importFiles);
|
||||||
|
|
||||||
let reader = new FileReader();
|
// let reader = new FileReader();
|
||||||
reader.onload = (event) => {
|
// reader.onload = (event) => {
|
||||||
let chats = JSON.parse(event.target.result);
|
// let chats = JSON.parse(event.target.result);
|
||||||
console.log(chats);
|
// console.log(chats);
|
||||||
importChats(chats);
|
// importChats(chats);
|
||||||
};
|
// };
|
||||||
|
|
||||||
reader.readAsText(importFiles[0]);
|
// reader.readAsText(importFiles[0]);
|
||||||
}
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -13,6 +13,8 @@ export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`;
|
||||||
|
|
||||||
export const WEB_UI_VERSION = 'v1.0.0-alpha-static';
|
export const WEB_UI_VERSION = 'v1.0.0-alpha-static';
|
||||||
|
|
||||||
|
export const REQUIRED_OLLAMA_VERSION = '0.1.16';
|
||||||
|
|
||||||
// Source: https://kit.svelte.dev/docs/modules#$env-static-public
|
// Source: https://kit.svelte.dev/docs/modules#$env-static-public
|
||||||
// This feature, akin to $env/static/private, exclusively incorporates environment variables
|
// This feature, akin to $env/static/private, exclusively incorporates environment variables
|
||||||
// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
|
// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
|
||||||
|
|
|
@ -66,9 +66,9 @@ export const getGravatarURL = (email) => {
|
||||||
return `https://www.gravatar.com/avatar/${hash}`;
|
return `https://www.gravatar.com/avatar/${hash}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyToClipboard = (text) => {
|
export const copyToClipboard = (text) => {
|
||||||
if (!navigator.clipboard) {
|
if (!navigator.clipboard) {
|
||||||
var textArea = document.createElement('textarea');
|
const textArea = document.createElement('textarea');
|
||||||
textArea.value = text;
|
textArea.value = text;
|
||||||
|
|
||||||
// Avoid scrolling to bottom
|
// Avoid scrolling to bottom
|
||||||
|
@ -81,8 +81,8 @@ const copyToClipboard = (text) => {
|
||||||
textArea.select();
|
textArea.select();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var successful = document.execCommand('copy');
|
const successful = document.execCommand('copy');
|
||||||
var msg = successful ? 'successful' : 'unsuccessful';
|
const msg = successful ? 'successful' : 'unsuccessful';
|
||||||
console.log('Fallback: Copying text command was ' + msg);
|
console.log('Fallback: Copying text command was ' + msg);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fallback: Oops, unable to copy', err);
|
console.error('Fallback: Oops, unable to copy', err);
|
||||||
|
|
|
@ -1,37 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { openDB, deleteDB } from 'idb';
|
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
import {
|
import { info, user, showSettings, settings, models, modelfiles } from '$lib/stores';
|
||||||
config,
|
|
||||||
info,
|
import { OLLAMA_API_BASE_URL, REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants';
|
||||||
user,
|
import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama';
|
||||||
showSettings,
|
import { getOpenAIModels } from '$lib/apis/openai';
|
||||||
settings,
|
|
||||||
models,
|
|
||||||
db,
|
|
||||||
chats,
|
|
||||||
chatId,
|
|
||||||
modelfiles
|
|
||||||
} from '$lib/stores';
|
|
||||||
|
|
||||||
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
|
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
|
||||||
import Sidebar from '$lib/components/layout/Sidebar.svelte';
|
import Sidebar from '$lib/components/layout/Sidebar.svelte';
|
||||||
import toast from 'svelte-french-toast';
|
|
||||||
import { OLLAMA_API_BASE_URL, WEBUI_API_BASE_URL } from '$lib/constants';
|
|
||||||
import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama';
|
|
||||||
import { getOpenAIModels } from '$lib/apis/openai';
|
|
||||||
import {
|
|
||||||
createNewChat,
|
|
||||||
deleteChatById,
|
|
||||||
getChatById,
|
|
||||||
getChatlist,
|
|
||||||
updateChatById
|
|
||||||
} from '$lib/apis/chats';
|
|
||||||
|
|
||||||
let requiredOllamaVersion = '0.1.16';
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
|
|
||||||
const getModels = async () => {
|
const getModels = async () => {
|
||||||
|
@ -55,92 +36,19 @@
|
||||||
return models;
|
return models;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDB = async () => {
|
const setOllamaVersion = async (version: string = '') => {
|
||||||
const DB = await openDB('Chats', 1, {
|
if (version === '') {
|
||||||
upgrade(db) {
|
version = await getOllamaVersion(
|
||||||
const store = db.createObjectStore('chats', {
|
|
||||||
keyPath: 'id',
|
|
||||||
autoIncrement: true
|
|
||||||
});
|
|
||||||
store.createIndex('timestamp', 'timestamp');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
db: DB,
|
|
||||||
getChatById: async function (id) {
|
|
||||||
const chat = await getChatById(localStorage.token, id);
|
|
||||||
return chat;
|
|
||||||
},
|
|
||||||
getChats: async function () {
|
|
||||||
const chats = await getChatlist(localStorage.token);
|
|
||||||
return chats;
|
|
||||||
},
|
|
||||||
createNewChat: async function (_chat) {
|
|
||||||
const chat = await createNewChat(localStorage.token, { ..._chat, timestamp: Date.now() });
|
|
||||||
console.log(chat);
|
|
||||||
await chats.set(await this.getChats());
|
|
||||||
|
|
||||||
return chat;
|
|
||||||
},
|
|
||||||
|
|
||||||
addChat: async function (chat) {
|
|
||||||
await this.db.put('chats', {
|
|
||||||
...chat
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChatById: async function (id, updated) {
|
|
||||||
const chat = await updateChatById(localStorage.token, id, {
|
|
||||||
...updated,
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
await chats.set(await this.getChats());
|
|
||||||
return chat;
|
|
||||||
},
|
|
||||||
deleteChatById: async function (id) {
|
|
||||||
if ($chatId === id) {
|
|
||||||
goto('/');
|
|
||||||
await chatId.set(uuidv4());
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteChatById(localStorage.token, id);
|
|
||||||
await chats.set(await this.getChats());
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteAllChat: async function () {
|
|
||||||
const tx = this.db.transaction('chats', 'readwrite');
|
|
||||||
await Promise.all([tx.store.clear(), tx.done]);
|
|
||||||
await chats.set(await this.getChats());
|
|
||||||
},
|
|
||||||
exportChats: async function () {
|
|
||||||
let chats = await this.db.getAllFromIndex('chats', 'timestamp');
|
|
||||||
chats = chats.map((item, idx) => chats[chats.length - 1 - idx]);
|
|
||||||
return chats;
|
|
||||||
},
|
|
||||||
importChats: async function (_chats) {
|
|
||||||
for (const chat of _chats) {
|
|
||||||
console.log(chat);
|
|
||||||
await this.addChat(chat);
|
|
||||||
}
|
|
||||||
await chats.set(await this.getChats());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOllamaVersion = async () => {
|
|
||||||
const version = await getOllamaVersion(
|
|
||||||
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
||||||
localStorage.token
|
localStorage.token
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
toast.error(error);
|
|
||||||
return '0';
|
return '0';
|
||||||
});
|
});
|
||||||
|
}
|
||||||
await info.set({ ...$info, ollama: { version: version } });
|
await info.set({ ...$info, ollama: { version: version } });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
version.localeCompare(requiredOllamaVersion, undefined, {
|
version.localeCompare(REQUIRED_OLLAMA_VERSION, undefined, {
|
||||||
numeric: true,
|
numeric: true,
|
||||||
sensitivity: 'case',
|
sensitivity: 'case',
|
||||||
caseFirst: 'upper'
|
caseFirst: 'upper'
|
||||||
|
@ -151,19 +59,18 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if ($config && $user === undefined) {
|
if ($user === undefined) {
|
||||||
await goto('/auth');
|
await goto('/auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
|
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
|
||||||
await models.set(await getModels());
|
await models.set(await getModels());
|
||||||
|
|
||||||
await modelfiles.set(JSON.parse(localStorage.getItem('modelfiles') ?? '[]'));
|
await modelfiles.set(JSON.parse(localStorage.getItem('modelfiles') ?? '[]'));
|
||||||
|
|
||||||
modelfiles.subscribe(async () => {});
|
modelfiles.subscribe(async () => {
|
||||||
|
// should fetch models
|
||||||
|
});
|
||||||
|
|
||||||
let _db = await getDB();
|
|
||||||
await db.set(_db);
|
|
||||||
await setOllamaVersion();
|
await setOllamaVersion();
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
@ -214,7 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if ($info?.ollama?.version ?? '0').localeCompare( requiredOllamaVersion, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' } ) < 0}
|
{:else if ($info?.ollama?.version ?? '0').localeCompare( REQUIRED_OLLAMA_VERSION, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' } ) < 0}
|
||||||
<div class="absolute w-full h-full flex z-50">
|
<div class="absolute w-full h-full flex z-50">
|
||||||
<div
|
<div
|
||||||
class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center"
|
class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center"
|
||||||
|
@ -231,15 +138,15 @@
|
||||||
/>We've detected either a connection hiccup or observed that you're using an older
|
/>We've detected either a connection hiccup or observed that you're using an older
|
||||||
version. Ensure you're on the latest Ollama version
|
version. Ensure you're on the latest Ollama version
|
||||||
<br class=" hidden sm:flex" />(version
|
<br class=" hidden sm:flex" />(version
|
||||||
<span class=" dark:text-white font-medium">{requiredOllamaVersion} or higher</span>)
|
<span class=" dark:text-white font-medium">{REQUIRED_OLLAMA_VERSION} or higher</span
|
||||||
or check your connection.
|
>) or check your connection.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" mt-6 mx-auto relative group w-fit">
|
<div class=" mt-6 mx-auto relative group w-fit">
|
||||||
<button
|
<button
|
||||||
class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm"
|
class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm"
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
await setOllamaVersion(await getOllamaVersion());
|
await setOllamaVersion();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Check Again
|
Check Again
|
||||||
|
@ -248,7 +155,7 @@
|
||||||
<button
|
<button
|
||||||
class="text-xs text-center w-full mt-2 text-gray-400 underline"
|
class="text-xs text-center w-full mt-2 text-gray-400 underline"
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
await setOllamaVersion(requiredOllamaVersion);
|
await setOllamaVersion(REQUIRED_OLLAMA_VERSION);
|
||||||
}}>Close</button
|
}}>Close</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,23 +2,27 @@
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
import { onDestroy, onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
import { config, models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores';
|
import { models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores';
|
||||||
import { OLLAMA_API_BASE_URL } from '$lib/constants';
|
import { OLLAMA_API_BASE_URL } from '$lib/constants';
|
||||||
import { splitStream } from '$lib/utils';
|
|
||||||
|
import { generateChatCompletion, generateTitle } from '$lib/apis/ollama';
|
||||||
|
import { copyToClipboard, splitStream } from '$lib/utils';
|
||||||
|
|
||||||
import MessageInput from '$lib/components/chat/MessageInput.svelte';
|
import MessageInput from '$lib/components/chat/MessageInput.svelte';
|
||||||
import Messages from '$lib/components/chat/Messages.svelte';
|
import Messages from '$lib/components/chat/Messages.svelte';
|
||||||
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
|
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
|
||||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||||
|
import { createNewChat, getChatList, updateChatById } from '$lib/apis/chats';
|
||||||
|
|
||||||
let stopResponseFlag = false;
|
let stopResponseFlag = false;
|
||||||
let autoScroll = true;
|
let autoScroll = true;
|
||||||
|
|
||||||
let selectedModels = [''];
|
let selectedModels = [''];
|
||||||
|
|
||||||
let selectedModelfile = null;
|
let selectedModelfile = null;
|
||||||
$: selectedModelfile =
|
$: selectedModelfile =
|
||||||
selectedModels.length === 1 &&
|
selectedModels.length === 1 &&
|
||||||
|
@ -83,41 +87,6 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyToClipboard = (text) => {
|
|
||||||
if (!navigator.clipboard) {
|
|
||||||
var textArea = document.createElement('textarea');
|
|
||||||
textArea.value = text;
|
|
||||||
|
|
||||||
// Avoid scrolling to bottom
|
|
||||||
textArea.style.top = '0';
|
|
||||||
textArea.style.left = '0';
|
|
||||||
textArea.style.position = 'fixed';
|
|
||||||
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.focus();
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var successful = document.execCommand('copy');
|
|
||||||
var msg = successful ? 'successful' : 'unsuccessful';
|
|
||||||
console.log('Fallback: Copying text command was ' + msg);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Fallback: Oops, unable to copy', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigator.clipboard.writeText(text).then(
|
|
||||||
function () {
|
|
||||||
console.log('Async: Copying to clipboard was successful!');
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
console.error('Async: Could not copy text: ', err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// Ollama functions
|
// Ollama functions
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -135,11 +104,11 @@
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await chats.set(await $db.getChats());
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => {
|
const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => {
|
||||||
console.log('sendPromptOllama');
|
// Create response message
|
||||||
let responseMessageId = uuidv4();
|
let responseMessageId = uuidv4();
|
||||||
let responseMessage = {
|
let responseMessage = {
|
||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
|
@ -150,8 +119,11 @@
|
||||||
model: model
|
model: model
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add message to history and Set currentId to messageId
|
||||||
history.messages[responseMessageId] = responseMessage;
|
history.messages[responseMessageId] = responseMessage;
|
||||||
history.currentId = responseMessageId;
|
history.currentId = responseMessageId;
|
||||||
|
|
||||||
|
// Append messageId to childrenIds of parent message
|
||||||
if (parentId !== null) {
|
if (parentId !== null) {
|
||||||
history.messages[parentId].childrenIds = [
|
history.messages[parentId].childrenIds = [
|
||||||
...history.messages[parentId].childrenIds,
|
...history.messages[parentId].childrenIds,
|
||||||
|
@ -159,17 +131,16 @@
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait until history/message have been updated
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
|
// Scroll down
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
|
|
||||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
const res = await generateChatCompletion(
|
||||||
method: 'POST',
|
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
||||||
headers: {
|
localStorage.token,
|
||||||
'Content-Type': 'text/event-stream',
|
{
|
||||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
|
||||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: model,
|
model: model,
|
||||||
messages: [
|
messages: [
|
||||||
$settings.system
|
$settings.system
|
||||||
|
@ -191,20 +162,11 @@
|
||||||
})
|
})
|
||||||
})),
|
})),
|
||||||
options: {
|
options: {
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
...($settings.options ?? {})
|
||||||
},
|
},
|
||||||
format: $settings.requestFormat ?? undefined
|
format: $settings.requestFormat ?? undefined
|
||||||
})
|
}
|
||||||
}).catch((err) => {
|
);
|
||||||
console.log(err);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
const reader = res.body
|
const reader = res.body
|
||||||
|
@ -296,23 +258,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($chatId == _chatId) {
|
if ($chatId == _chatId) {
|
||||||
chat = await $db.updateChatById(_chatId, {
|
chat = await updateChatById(localStorage.token, _chatId, {
|
||||||
...chat.chat,
|
|
||||||
title: title === '' ? 'New Chat' : title,
|
|
||||||
models: selectedModels,
|
|
||||||
system: $settings.system ?? undefined,
|
|
||||||
options: {
|
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
|
||||||
},
|
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (res !== null) {
|
if (res !== null) {
|
||||||
|
@ -338,6 +288,7 @@
|
||||||
|
|
||||||
stopResponseFlag = false;
|
stopResponseFlag = false;
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
}
|
}
|
||||||
|
@ -483,23 +434,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($chatId == _chatId) {
|
if ($chatId == _chatId) {
|
||||||
chat = await $db.updateChatById(_chatId, {
|
chat = await updateChatById(localStorage.token, _chatId, {
|
||||||
...chat.chat,
|
|
||||||
title: title === '' ? 'New Chat' : title,
|
|
||||||
models: selectedModels,
|
|
||||||
system: $settings.system ?? undefined,
|
|
||||||
options: {
|
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
|
||||||
},
|
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (res !== null) {
|
if (res !== null) {
|
||||||
|
@ -549,10 +488,13 @@
|
||||||
if (selectedModels.includes('')) {
|
if (selectedModels.includes('')) {
|
||||||
toast.error('Model not selected');
|
toast.error('Model not selected');
|
||||||
} else if (messages.length != 0 && messages.at(-1).done != true) {
|
} else if (messages.length != 0 && messages.at(-1).done != true) {
|
||||||
|
// Response not done
|
||||||
console.log('wait');
|
console.log('wait');
|
||||||
} else {
|
} else {
|
||||||
|
// Reset chat message textarea height
|
||||||
document.getElementById('chat-textarea').style.height = '';
|
document.getElementById('chat-textarea').style.height = '';
|
||||||
|
|
||||||
|
// Create user message
|
||||||
let userMessageId = uuidv4();
|
let userMessageId = uuidv4();
|
||||||
let userMessage = {
|
let userMessage = {
|
||||||
id: userMessageId,
|
id: userMessageId,
|
||||||
|
@ -563,47 +505,42 @@
|
||||||
files: files.length > 0 ? files : undefined
|
files: files.length > 0 ? files : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add message to history and Set currentId to messageId
|
||||||
|
history.messages[userMessageId] = userMessage;
|
||||||
|
history.currentId = userMessageId;
|
||||||
|
|
||||||
|
// Append messageId to childrenIds of parent message
|
||||||
if (messages.length !== 0) {
|
if (messages.length !== 0) {
|
||||||
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
|
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
history.messages[userMessageId] = userMessage;
|
// Wait until history/message have been updated
|
||||||
history.currentId = userMessageId;
|
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
|
// Create new chat if only one message in messages
|
||||||
if (messages.length == 1) {
|
if (messages.length == 1) {
|
||||||
chat = await $db.createNewChat({
|
chat = await createNewChat(localStorage.token, {
|
||||||
id: $chatId,
|
id: $chatId,
|
||||||
title: 'New Chat',
|
title: 'New Chat',
|
||||||
models: selectedModels,
|
models: selectedModels,
|
||||||
system: $settings.system ?? undefined,
|
system: $settings.system ?? undefined,
|
||||||
options: {
|
options: {
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
...($settings.options ?? {})
|
||||||
},
|
},
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history,
|
||||||
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
console.log(chat);
|
|
||||||
|
|
||||||
await chatId.set(chat.id);
|
await chatId.set(chat.id);
|
||||||
await tick();
|
await tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset chat input textarea
|
||||||
prompt = '';
|
prompt = '';
|
||||||
files = [];
|
files = [];
|
||||||
|
|
||||||
setTimeout(() => {
|
// Send prompt
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
await sendPrompt(userPrompt, userMessageId);
|
await sendPrompt(userPrompt, userMessageId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -614,9 +551,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerateResponse = async () => {
|
const regenerateResponse = async () => {
|
||||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
console.log('regenerateResponse');
|
||||||
console.log('regenerateResponse', _chatId);
|
|
||||||
|
|
||||||
if (messages.length != 0 && messages.at(-1).done == true) {
|
if (messages.length != 0 && messages.at(-1).done == true) {
|
||||||
messages.splice(messages.length - 1, 1);
|
messages.splice(messages.length - 1, 1);
|
||||||
messages = messages;
|
messages = messages;
|
||||||
|
@ -624,40 +559,21 @@
|
||||||
let userMessage = messages.at(-1);
|
let userMessage = messages.at(-1);
|
||||||
let userPrompt = userMessage.content;
|
let userPrompt = userMessage.content;
|
||||||
|
|
||||||
await sendPrompt(userPrompt, userMessage.id, _chatId);
|
await sendPrompt(userPrompt, userMessage.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateChatTitle = async (_chatId, userPrompt) => {
|
const generateChatTitle = async (_chatId, userPrompt) => {
|
||||||
if ($settings.titleAutoGenerate ?? true) {
|
if ($settings.titleAutoGenerate ?? true) {
|
||||||
console.log('generateChatTitle');
|
const title = await generateTitle(
|
||||||
|
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
||||||
|
localStorage.token,
|
||||||
|
selectedModels[0],
|
||||||
|
userPrompt
|
||||||
|
);
|
||||||
|
|
||||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
if (title) {
|
||||||
method: 'POST',
|
await setChatTitle(_chatId, title);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/event-stream',
|
|
||||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: selectedModels[0],
|
|
||||||
prompt: `Generate a brief 3-5 word title for this question, excluding the term 'title.' Then, please reply with only the title: ${userPrompt}`,
|
|
||||||
stream: false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(async (res) => {
|
|
||||||
if (!res.ok) throw await res.json();
|
|
||||||
return res.json();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
if ('detail' in error) {
|
|
||||||
toast.error(error.detail);
|
|
||||||
}
|
|
||||||
console.log(error);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
await setChatTitle(_chatId, res.response === '' ? 'New Chat' : res.response);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await setChatTitle(_chatId, `${userPrompt}`);
|
await setChatTitle(_chatId, `${userPrompt}`);
|
||||||
|
@ -665,10 +581,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const setChatTitle = async (_chatId, _title) => {
|
const setChatTitle = async (_chatId, _title) => {
|
||||||
chat = await $db.updateChatById(_chatId, { ...chat.chat, title: _title });
|
|
||||||
if (_chatId === $chatId) {
|
if (_chatId === $chatId) {
|
||||||
title = _title;
|
title = _title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chat = await updateChatById(localStorage.token, _chatId, { title: _title });
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
|
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
|
||||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import { createNewChat, getChatById, getChatList } from '$lib/apis/chats';
|
||||||
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
let stopResponseFlag = false;
|
let stopResponseFlag = false;
|
||||||
|
@ -70,7 +71,7 @@
|
||||||
|
|
||||||
const loadChat = async () => {
|
const loadChat = async () => {
|
||||||
await chatId.set($page.params.id);
|
await chatId.set($page.params.id);
|
||||||
chat = await $db.getChatById($chatId);
|
chat = await getChatById(localStorage.token, $chatId);
|
||||||
|
|
||||||
const chatContent = chat.chat;
|
const chatContent = chat.chat;
|
||||||
|
|
||||||
|
@ -159,11 +160,11 @@
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await chats.set(await $db.getChats());
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => {
|
const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => {
|
||||||
console.log('sendPromptOllama');
|
// Create response message
|
||||||
let responseMessageId = uuidv4();
|
let responseMessageId = uuidv4();
|
||||||
let responseMessage = {
|
let responseMessage = {
|
||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
|
@ -174,8 +175,11 @@
|
||||||
model: model
|
model: model
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add message to history and Set currentId to messageId
|
||||||
history.messages[responseMessageId] = responseMessage;
|
history.messages[responseMessageId] = responseMessage;
|
||||||
history.currentId = responseMessageId;
|
history.currentId = responseMessageId;
|
||||||
|
|
||||||
|
// Append messageId to childrenIds of parent message
|
||||||
if (parentId !== null) {
|
if (parentId !== null) {
|
||||||
history.messages[parentId].childrenIds = [
|
history.messages[parentId].childrenIds = [
|
||||||
...history.messages[parentId].childrenIds,
|
...history.messages[parentId].childrenIds,
|
||||||
|
@ -183,17 +187,16 @@
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait until history/message have been updated
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
|
// Scroll down
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
|
|
||||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
const res = await generateChatCompletion(
|
||||||
method: 'POST',
|
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
||||||
headers: {
|
localStorage.token,
|
||||||
'Content-Type': 'text/event-stream',
|
{
|
||||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
|
||||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: model,
|
model: model,
|
||||||
messages: [
|
messages: [
|
||||||
$settings.system
|
$settings.system
|
||||||
|
@ -215,20 +218,11 @@
|
||||||
})
|
})
|
||||||
})),
|
})),
|
||||||
options: {
|
options: {
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
...($settings.options ?? {})
|
||||||
},
|
},
|
||||||
format: $settings.requestFormat ?? undefined
|
format: $settings.requestFormat ?? undefined
|
||||||
})
|
}
|
||||||
}).catch((err) => {
|
);
|
||||||
console.log(err);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
const reader = res.body
|
const reader = res.body
|
||||||
|
@ -320,23 +314,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($chatId == _chatId) {
|
if ($chatId == _chatId) {
|
||||||
chat = await $db.updateChatById(_chatId, {
|
chat = await updateChatById(localStorage.token, _chatId, {
|
||||||
...chat.chat,
|
|
||||||
title: title === '' ? 'New Chat' : title,
|
|
||||||
models: selectedModels,
|
|
||||||
system: $settings.system ?? undefined,
|
|
||||||
options: {
|
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
|
||||||
},
|
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (res !== null) {
|
if (res !== null) {
|
||||||
|
@ -362,6 +344,7 @@
|
||||||
|
|
||||||
stopResponseFlag = false;
|
stopResponseFlag = false;
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
}
|
}
|
||||||
|
@ -507,23 +490,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($chatId == _chatId) {
|
if ($chatId == _chatId) {
|
||||||
chat = await $db.updateChatById(_chatId, {
|
chat = await updateChatById(localStorage.token, _chatId, {
|
||||||
...chat.chat,
|
|
||||||
title: title === '' ? 'New Chat' : title,
|
|
||||||
models: selectedModels,
|
|
||||||
system: $settings.system ?? undefined,
|
|
||||||
options: {
|
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
|
||||||
},
|
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (res !== null) {
|
if (res !== null) {
|
||||||
|
@ -573,10 +544,13 @@
|
||||||
if (selectedModels.includes('')) {
|
if (selectedModels.includes('')) {
|
||||||
toast.error('Model not selected');
|
toast.error('Model not selected');
|
||||||
} else if (messages.length != 0 && messages.at(-1).done != true) {
|
} else if (messages.length != 0 && messages.at(-1).done != true) {
|
||||||
|
// Response not done
|
||||||
console.log('wait');
|
console.log('wait');
|
||||||
} else {
|
} else {
|
||||||
|
// Reset chat message textarea height
|
||||||
document.getElementById('chat-textarea').style.height = '';
|
document.getElementById('chat-textarea').style.height = '';
|
||||||
|
|
||||||
|
// Create user message
|
||||||
let userMessageId = uuidv4();
|
let userMessageId = uuidv4();
|
||||||
let userMessage = {
|
let userMessage = {
|
||||||
id: userMessageId,
|
id: userMessageId,
|
||||||
|
@ -587,47 +561,42 @@
|
||||||
files: files.length > 0 ? files : undefined
|
files: files.length > 0 ? files : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add message to history and Set currentId to messageId
|
||||||
|
history.messages[userMessageId] = userMessage;
|
||||||
|
history.currentId = userMessageId;
|
||||||
|
|
||||||
|
// Append messageId to childrenIds of parent message
|
||||||
if (messages.length !== 0) {
|
if (messages.length !== 0) {
|
||||||
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
|
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
history.messages[userMessageId] = userMessage;
|
// Wait until history/message have been updated
|
||||||
history.currentId = userMessageId;
|
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
|
// Create new chat if only one message in messages
|
||||||
if (messages.length == 1) {
|
if (messages.length == 1) {
|
||||||
chat = await $db.createNewChat({
|
chat = await createNewChat(localStorage.token, {
|
||||||
id: $chatId,
|
id: $chatId,
|
||||||
title: 'New Chat',
|
title: 'New Chat',
|
||||||
models: selectedModels,
|
models: selectedModels,
|
||||||
system: $settings.system ?? undefined,
|
system: $settings.system ?? undefined,
|
||||||
options: {
|
options: {
|
||||||
seed: $settings.seed ?? undefined,
|
|
||||||
temperature: $settings.temperature ?? undefined,
|
|
||||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
|
||||||
top_k: $settings.top_k ?? undefined,
|
|
||||||
top_p: $settings.top_p ?? undefined,
|
|
||||||
num_ctx: $settings.num_ctx ?? undefined,
|
|
||||||
...($settings.options ?? {})
|
...($settings.options ?? {})
|
||||||
},
|
},
|
||||||
messages: messages,
|
messages: messages,
|
||||||
history: history
|
history: history,
|
||||||
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
console.log(chat);
|
|
||||||
|
|
||||||
await chatId.set(chat.id);
|
await chatId.set(chat.id);
|
||||||
await tick();
|
await tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset chat input textarea
|
||||||
prompt = '';
|
prompt = '';
|
||||||
files = [];
|
files = [];
|
||||||
|
|
||||||
setTimeout(() => {
|
// Send prompt
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
await sendPrompt(userPrompt, userMessageId);
|
await sendPrompt(userPrompt, userMessageId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -638,9 +607,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerateResponse = async () => {
|
const regenerateResponse = async () => {
|
||||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
console.log('regenerateResponse');
|
||||||
console.log('regenerateResponse', _chatId);
|
|
||||||
|
|
||||||
if (messages.length != 0 && messages.at(-1).done == true) {
|
if (messages.length != 0 && messages.at(-1).done == true) {
|
||||||
messages.splice(messages.length - 1, 1);
|
messages.splice(messages.length - 1, 1);
|
||||||
messages = messages;
|
messages = messages;
|
||||||
|
@ -648,41 +615,21 @@
|
||||||
let userMessage = messages.at(-1);
|
let userMessage = messages.at(-1);
|
||||||
let userPrompt = userMessage.content;
|
let userPrompt = userMessage.content;
|
||||||
|
|
||||||
await sendPrompt(userPrompt, userMessage.id, _chatId);
|
await sendPrompt(userPrompt, userMessage.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateChatTitle = async (_chatId, userPrompt) => {
|
const generateChatTitle = async (_chatId, userPrompt) => {
|
||||||
if ($settings.titleAutoGenerate ?? true) {
|
if ($settings.titleAutoGenerate ?? true) {
|
||||||
console.log('generateChatTitle');
|
const title = await generateTitle(
|
||||||
|
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
|
||||||
|
localStorage.token,
|
||||||
|
selectedModels[0],
|
||||||
|
userPrompt
|
||||||
|
);
|
||||||
|
|
||||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
if (title) {
|
||||||
method: 'POST',
|
await setChatTitle(_chatId, title);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/event-stream',
|
|
||||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
|
||||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: selectedModels[0],
|
|
||||||
prompt: `Generate a brief 3-5 word title for this question, excluding the term 'title.' Then, please reply with only the title: ${userPrompt}`,
|
|
||||||
stream: false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(async (res) => {
|
|
||||||
if (!res.ok) throw await res.json();
|
|
||||||
return res.json();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
if ('detail' in error) {
|
|
||||||
toast.error(error.detail);
|
|
||||||
}
|
|
||||||
console.log(error);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
await setChatTitle(_chatId, res.response === '' ? 'New Chat' : res.response);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await setChatTitle(_chatId, `${userPrompt}`);
|
await setChatTitle(_chatId, `${userPrompt}`);
|
||||||
|
@ -690,13 +637,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const setChatTitle = async (_chatId, _title) => {
|
const setChatTitle = async (_chatId, _title) => {
|
||||||
chat = await $db.updateChatById(_chatId, {
|
|
||||||
...chat.chat,
|
|
||||||
title: _title
|
|
||||||
});
|
|
||||||
if (_chatId === $chatId) {
|
if (_chatId === $chatId) {
|
||||||
title = _title;
|
title = _title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chat = await updateChatById(localStorage.token, _chatId, { title: _title });
|
||||||
|
await chats.set(await getChatList(localStorage.token));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue