diff --git a/src/lib/components/notes/NoteEditor.svelte b/src/lib/components/notes/NoteEditor.svelte index fa878b2d0..a14682d0a 100644 --- a/src/lib/components/notes/NoteEditor.svelte +++ b/src/lib/components/notes/NoteEditor.svelte @@ -12,11 +12,11 @@ import { marked } from 'marked'; import { toast } from 'svelte-sonner'; - import { config, settings, showSidebar } from '$lib/stores'; + import { config, models, settings, showSidebar } from '$lib/stores'; import { goto } from '$app/navigation'; - import { compressImage, copyToClipboard } from '$lib/utils'; - import { WEBUI_API_BASE_URL } from '$lib/constants'; + import { compressImage, copyToClipboard, splitStream } from '$lib/utils'; + import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; import { uploadFile } from '$lib/apis/files'; import dayjs from '$lib/dayjs'; @@ -65,6 +65,10 @@ import Bars3BottomLeft from '../icons/Bars3BottomLeft.svelte'; import ArrowUturnLeft from '../icons/ArrowUturnLeft.svelte'; import ArrowUturnRight from '../icons/ArrowUturnRight.svelte'; + import Sidebar from '../common/Sidebar.svelte'; + import ArrowRight from '../icons/ArrowRight.svelte'; + import Cog6 from '../icons/Cog6.svelte'; + import { chatCompletion } from '$lib/apis/openai'; export let id: null | string = null; @@ -88,14 +92,19 @@ let files = []; let versionIdx = null; + let selectedModelId = null; + let recording = false; let displayMediaRecord = false; + let showSettings = false; let showDeleteConfirm = false; - let dragged = false; + let dragged = false; let loading = false; + let enhancing = false; + let streaming = false; const init = async () => { loading = true; @@ -165,29 +174,22 @@ return false; } - async function aiEnhanceContent(content) { - // fake delay - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const md = content.md + '_ai'; - const html = marked.parse(md); - - return { - json: null, - html: html, - md: md - }; - } - async function enhanceNoteHandler() { - insertNoteVersion(note); + if (selectedModelId === '') { + toast.error($i18n.t('Please select a model.')); + return; + } + + const model = $models.find((model) => model.id === selectedModelId); + if (!model) { + selectedModelId = ''; + return; + } enhancing = true; - const aiResult = await aiEnhanceContent(note.data.content); - note.data.content.json = aiResult.json; - note.data.content.html = aiResult.html; - note.data.content.md = aiResult.md; + insertNoteVersion(note); + await enhanceCompletionHandler(model); enhancing = false; versionIdx = null; @@ -456,6 +458,96 @@ } }; + const enhanceCompletionHandler = async (model) => { + let enhancedContent = { + json: null, + html: '', + md: '' + }; + + const systemPrompt = `Enhance existing notes using additional context provided from audio transcription or uploaded file content. Your task is to make the notes more useful and comprehensive by incorporating relevant information from the provided context. + +Input will be provided within and XML tags, providing a structure for the existing notes and context respectively. + +# Output Format + +Provide the enhanced notes in markdown format. Use markdown syntax for headings, lists, and emphasis to improve clarity and presentation. Ensure that all integrated content from the context is accurately reflected. Return only the markdown formatted note. +`; + + const [res, controller] = await chatCompletion( + localStorage.token, + { + model: model.id, + stream: true, + messages: [ + { + role: 'system', + content: systemPrompt + }, + { + role: 'user', + content: + `${note.data.content.md}` + + (files && files.length > 0 + ? `\n${files.map((file) => `${file.name}: ${file?.file?.data?.content ?? 'Could not extract content'}\n`).join('')}` + : '') + } + ] + }, + `${WEBUI_BASE_URL}/api` + ); + + await tick(); + + streaming = true; + + if (res && res.ok) { + const reader = res.body + .pipeThrough(new TextDecoderStream()) + .pipeThrough(splitStream('\n')) + .getReader(); + + while (true) { + const { value, done } = await reader.read(); + if (done) { + break; + } + + try { + let lines = value.split('\n'); + + for (const line of lines) { + if (line !== '') { + console.log(line); + if (line === 'data: [DONE]') { + console.log(line); + } else { + let data = JSON.parse(line.replace(/^data: /, '')); + console.log(data); + + if (data.choices && data.choices.length > 0) { + const choice = data.choices[0]; + if (choice.delta && choice.delta.content) { + enhancedContent.md += choice.delta.content; + enhancedContent.html = marked.parse(enhancedContent.md); + + note.data.content.md = enhancedContent.md; + note.data.content.html = enhancedContent.html; + note.data.content.json = null; + } + } + } + } + } + } catch (error) { + console.log(error); + } + } + } + + streaming = false; + }; + const onDragOver = (e) => { e.preventDefault(); @@ -489,6 +581,14 @@ onMount(async () => { await tick(); + if ($settings?.models) { + selectedModelId = $settings?.models[0]; + } else if ($config?.default_models) { + selectedModelId = $config?.default_models.split(',')[0]; + } else { + selectedModelId = ''; + } + const dropzoneElement = document.getElementById('note-editor'); dropzoneElement?.addEventListener('dragover', onDragOver); @@ -524,6 +624,42 @@
+ +
+
+
Settings
+ +
+ +
+
+ +
+
+
Model
+ +
+ +
+
+
+
+
+ {#if loading}
@@ -542,7 +678,7 @@ required /> -
+
{#if note.data?.versions?.length > 0}
@@ -588,13 +724,17 @@ showDeleteConfirm = true; }} > - + + +
@@ -618,7 +758,9 @@
{#if enhancing}
{/if} @@ -674,7 +816,7 @@