Now with basic AI summary for Journal Entries
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "osit-aether-app-svelte",
|
"name": "osit-aether-app-svelte",
|
||||||
"version": "3.0.3",
|
"version": "3.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "osit-aether-app-svelte",
|
"name": "osit-aether-app-svelte",
|
||||||
"version": "3.0.3",
|
"version": "3.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/commands": "^6.8.1",
|
"@codemirror/commands": "^6.8.1",
|
||||||
"@codemirror/gutter": "^0.19.9",
|
"@codemirror/gutter": "^0.19.9",
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
"html5-qrcode": "^2.3.8",
|
"html5-qrcode": "^2.3.8",
|
||||||
"lucide-svelte": "0.*.0",
|
"lucide-svelte": "0.*.0",
|
||||||
"marked": "^16.0.0",
|
"marked": "^16.0.0",
|
||||||
|
"openai": "^5.20.1",
|
||||||
"shadcn-svelte": "^1.0.0",
|
"shadcn-svelte": "^1.0.0",
|
||||||
"svelte-persisted-store": "^0.12.0"
|
"svelte-persisted-store": "^0.12.0"
|
||||||
},
|
},
|
||||||
@@ -6641,6 +6642,27 @@
|
|||||||
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
|
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/openai": {
|
||||||
|
"version": "5.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/openai/-/openai-5.20.1.tgz",
|
||||||
|
"integrity": "sha512-UndCB0R5V3iB9I98NyF69zNP6YfwU4+Fjk0eW4HhooTm+Awlpm/MGjJTwJsyNV/qkH1NJi0GG+9odwukGTqExQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"openai": "bin/cli"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"ws": "^8.18.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"ws": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osit-aether-app-svelte",
|
"name": "osit-aether-app-svelte",
|
||||||
"version": "3.2.0",
|
"version": "3.3.0",
|
||||||
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
|
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
|
||||||
"homepage": "https://oneskyit.com/",
|
"homepage": "https://oneskyit.com/",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -112,6 +112,7 @@
|
|||||||
"html5-qrcode": "^2.3.8",
|
"html5-qrcode": "^2.3.8",
|
||||||
"lucide-svelte": "0.*.0",
|
"lucide-svelte": "0.*.0",
|
||||||
"marked": "^16.0.0",
|
"marked": "^16.0.0",
|
||||||
|
"openai": "^5.20.1",
|
||||||
"shadcn-svelte": "^1.0.0",
|
"shadcn-svelte": "^1.0.0",
|
||||||
"svelte-persisted-store": "^0.12.0"
|
"svelte-persisted-store": "^0.12.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ let journals_local_data_struct: key_val = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
|
ai__system_prompt: 'Summarize the following journal entry content in a concise manner, focusing on key points and insights.', // 'You are a helpful assistant that helps people find information.',
|
||||||
edit: false,
|
edit: false,
|
||||||
edit_kv: {},
|
edit_kv: {},
|
||||||
},
|
},
|
||||||
@@ -94,6 +95,8 @@ let journals_session_data_struct: key_val = {
|
|||||||
tmp_obj: {},
|
tmp_obj: {},
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
|
show__ai_summary: false,
|
||||||
|
ai_summary: '',
|
||||||
decrypt_kv: {}, // Essentially flag that the entry (content and history) can be decrypted.
|
decrypt_kv: {}, // Essentially flag that the entry (content and history) can be decrypted.
|
||||||
edit: false,
|
edit: false,
|
||||||
edit_kv: {},
|
edit_kv: {},
|
||||||
|
|||||||
@@ -4,15 +4,16 @@ import { goto } from '$app/navigation';
|
|||||||
// import { tick } from 'svelte';
|
// import { tick } from 'svelte';
|
||||||
|
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
|
import { Modal } from 'flowbite-svelte';
|
||||||
import {
|
import {
|
||||||
ArrowDown01, ArrowDown10, ArrowDownUp,
|
ArrowDown01, ArrowDown10, ArrowDownUp,
|
||||||
BookHeart, BriefcaseBusiness,
|
BookHeart, Bot, BotMessageSquare, BriefcaseBusiness,
|
||||||
CalendarClock, CalendarOff, Clock, CodeXml, Copy,
|
CalendarClock, CalendarOff, Clock, CodeXml, Copy,
|
||||||
Eye, EyeOff,
|
Eye, EyeOff,
|
||||||
Flag, FlagOff, FileDown, FileX, Fingerprint,
|
Flag, FlagOff, FileDown, FileX, Fingerprint,
|
||||||
Globe, Group,
|
Globe, Group,
|
||||||
Hash, History,
|
Hash, History,
|
||||||
LockKeyhole, LockKeyholeOpen,
|
Loader, LockKeyhole, LockKeyholeOpen,
|
||||||
MessageSquareWarning, Menu, Minus,
|
MessageSquareWarning, Menu, Minus,
|
||||||
NotebookPen, NotebookText, NotepadTextDashed,
|
NotebookPen, NotebookText, NotepadTextDashed,
|
||||||
Pencil, PenLine, Plus,
|
Pencil, PenLine, Plus,
|
||||||
@@ -26,6 +27,26 @@ import {
|
|||||||
} from '@lucide/svelte';
|
} from '@lucide/svelte';
|
||||||
|
|
||||||
|
|
||||||
|
// Import OpenAI, but use it with local LLM server (ollama)
|
||||||
|
import OpenAI from "openai";
|
||||||
|
// import { Configuration, OpenAIApi } from 'openai';
|
||||||
|
|
||||||
|
let llm_api_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhYjI2MzdlLThiMjktNGM2Zi05MzVhLWFkYjU1MDkwMGU5MCJ9.zc2u8vVH1yNt_mDHF8m9f_ONHfRXHjjC9gb-Y9jYwI0';
|
||||||
|
|
||||||
|
// api_model = 'DgrZone DeepSeek (8b quick)';
|
||||||
|
let llm_api_model = 'dgrzone-deepseek-8b-quick';
|
||||||
|
let llm_api_base_url = 'http://localhost:3000/api';
|
||||||
|
|
||||||
|
// const configuration = new Configuration({
|
||||||
|
// apiKey: llm_api_token,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const openai = new OpenAI({
|
||||||
|
// apiKey: llm_api_token,
|
||||||
|
// baseURL: llm_api_base_url,
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
// import E_app_codemirror from '$lib/e_app_codemirror.svelte';
|
// import E_app_codemirror from '$lib/e_app_codemirror.svelte';
|
||||||
// import E_app_codemirror_v4 from '$lib/e_app_codemirror_v4.svelte';
|
// import E_app_codemirror_v4 from '$lib/e_app_codemirror_v4.svelte';
|
||||||
import E_app_codemirror_v5 from '$lib/e_app_codemirror_v5.svelte';
|
import E_app_codemirror_v5 from '$lib/e_app_codemirror_v5.svelte';
|
||||||
@@ -1668,6 +1689,98 @@ $effect(() => {
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<!-- Button to call OpenAI API endpoint to summarize the content. -->
|
||||||
|
<div
|
||||||
|
class="
|
||||||
|
flex flex-row-reverse flex-wrap gap-1 items-center justify-center
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={async () => {
|
||||||
|
const ai_client = new OpenAI({
|
||||||
|
apiKey: llm_api_token,
|
||||||
|
baseURL: llm_api_base_url,
|
||||||
|
dangerouslyAllowBrowser: true,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// let resp__models = ai_client.models.list();
|
||||||
|
// console.log('AI API Response (models)', resp__models);
|
||||||
|
|
||||||
|
let msg_role_system_content = $journals_loc?.entry?.ai__system_prompt || 'You are a helpful assistant that helps people find information.';
|
||||||
|
// let msg_role_user_content = 'Why is the sky blue?';
|
||||||
|
let msg_role_user_content = tmp_entry_obj?.content;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ae_promises = ai_client.chat.completions.create(
|
||||||
|
{
|
||||||
|
model: llm_api_model,
|
||||||
|
messages: [
|
||||||
|
{ role: 'system', content: msg_role_system_content },
|
||||||
|
{ role: "user", content: msg_role_user_content }
|
||||||
|
],
|
||||||
|
// max_tokens: 512,
|
||||||
|
// temperature: 0.7,
|
||||||
|
// stream: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then ((resp__chat) => {
|
||||||
|
console.log('AI API Response (chat)', resp__chat);
|
||||||
|
console.log('AI API Response (chat)', resp__chat);
|
||||||
|
|
||||||
|
console.log('resp msg 0 = ', resp__chat?.choices?.[0]?.message?.content);
|
||||||
|
|
||||||
|
$journals_sess.entry.ai_summary = resp__chat?.choices?.[0]?.message?.content || 'No summary available';
|
||||||
|
|
||||||
|
$journals_sess.entry.show__ai_summary = true;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error from chat completion:', err);
|
||||||
|
alert('Error from chat completion: ' + (err?.message || err));
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
class="
|
||||||
|
btn btn-sm
|
||||||
|
preset-outlined-warning-200-800
|
||||||
|
"
|
||||||
|
title="Generate AI summary of this journal entry"
|
||||||
|
>
|
||||||
|
{#await ae_promises}
|
||||||
|
<Loader class="inline-block mr-1 animate-spin" />
|
||||||
|
<span class="text-sm">Summarizing...</span>
|
||||||
|
{:then}
|
||||||
|
<BotMessageSquare class="inline-block mr-1" />
|
||||||
|
<span class="text-sm">Summarize</span>
|
||||||
|
{:catch error}
|
||||||
|
<span class="text-sm">Error</span>
|
||||||
|
{/await}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{#if !$journals_sess?.entry?.show__ai_summary && $journals_sess?.entry?.ai_summary}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => {
|
||||||
|
$journals_sess.entry.show__ai_summary = true;
|
||||||
|
}}
|
||||||
|
class="
|
||||||
|
btn btn-sm
|
||||||
|
preset-outlined-warning-200-800
|
||||||
|
|
||||||
|
"
|
||||||
|
title="Show AI summary of this journal entry"
|
||||||
|
>
|
||||||
|
<Bot class="inline-block mr-1" />
|
||||||
|
<span class="text-sm">
|
||||||
|
Show</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="divider my-2"></div>
|
<div class="divider my-2"></div>
|
||||||
|
|
||||||
<!-- Core Object Properties -->
|
<!-- Core Object Properties -->
|
||||||
@@ -1971,6 +2084,92 @@ tabindex={$ae_loc.edit_mode ? 0 : -1} -->
|
|||||||
class:bg-yellow-50={$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] == 'current'}
|
class:bg-yellow-50={$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] == 'current'}
|
||||||
class:dark:bg-yellow-950={$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] == 'current'}
|
class:dark:bg-yellow-950={$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] == 'current'}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
{#if $journals_sess?.entry?.ai_summary}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => {
|
||||||
|
$journals_sess.entry.show__ai_summary = !$journals_sess.entry.show__ai_summary;
|
||||||
|
}}
|
||||||
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 transition-all absolute top-2 right-2 z-10"
|
||||||
|
title="Toggle AI Summary"
|
||||||
|
>
|
||||||
|
<Bot class="inline-block mr-1" />
|
||||||
|
<span class="hidden sm:inline">AI Summary</span>
|
||||||
|
{#if $journals_sess?.entry?.ai_summary}
|
||||||
|
<span class="badge badge-sm badge-secondary ml-1">
|
||||||
|
{$journals_sess?.entry?.ai_summary.split(' ').length} words
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => {
|
||||||
|
const ai_client = new OpenAI({
|
||||||
|
apiKey: llm_api_token,
|
||||||
|
baseURL: llm_api_base_url,
|
||||||
|
dangerouslyAllowBrowser: true,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// let resp__models = ai_client.models.list();
|
||||||
|
// console.log('AI API Response (models)', resp__models);
|
||||||
|
|
||||||
|
let msg_role_system_content = $journals_loc?.entry?.ai__system_prompt || 'You are a helpful assistant that helps people find information.';
|
||||||
|
// let msg_role_user_content = 'Why is the sky blue?';
|
||||||
|
let msg_role_user_content = tmp_entry_obj?.content;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ae_promises = ai_client.chat.completions.create(
|
||||||
|
{
|
||||||
|
model: llm_api_model,
|
||||||
|
messages: [
|
||||||
|
{ role: 'system', content: msg_role_system_content },
|
||||||
|
{ role: "user", content: msg_role_user_content }
|
||||||
|
],
|
||||||
|
// max_tokens: 512,
|
||||||
|
// temperature: 0.7,
|
||||||
|
// stream: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then ((resp__chat) => {
|
||||||
|
console.log('AI API Response (chat)', resp__chat);
|
||||||
|
console.log('AI API Response (chat)', resp__chat);
|
||||||
|
|
||||||
|
console.log('resp msg 0 = ', resp__chat?.choices?.[0]?.message?.content);
|
||||||
|
|
||||||
|
$journals_sess.entry.ai_summary = resp__chat?.choices?.[0]?.message?.content || 'No summary available';
|
||||||
|
|
||||||
|
$journals_sess.entry.show__ai_summary = true;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error from chat completion:', err);
|
||||||
|
alert('Error from chat completion: ' + (err?.message || err));
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 transition-all absolute top-2 right-2 z-10"
|
||||||
|
title="Generate AI summary of this journal entry"
|
||||||
|
>
|
||||||
|
{#await ae_promises}
|
||||||
|
<Loader class="inline-block mr-1 animate-spin" />
|
||||||
|
<span class="text-sm">Summarizing...</span>
|
||||||
|
{:then}
|
||||||
|
<BotMessageSquare class="inline-block mr-1" />
|
||||||
|
<span class="text-sm">Summarize</span>
|
||||||
|
{:catch error}
|
||||||
|
<span class="text-sm">Error</span>
|
||||||
|
{/await}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{#if (!$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
{#if (!$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
||||||
|
|
||||||
{#if $lq__journal_obj?.cfg_json?.pref_viewer == 'codemirror'}
|
{#if $lq__journal_obj?.cfg_json?.pref_viewer == 'codemirror'}
|
||||||
@@ -2473,9 +2672,58 @@ zzzz
|
|||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{#if $journals_sess?.entry?.show__ai_summary && $journals_sess?.entry?.ai_summary}
|
||||||
|
<Modal
|
||||||
|
title="AI Summary"
|
||||||
|
bind:open={$journals_sess.entry.show__ai_summary}
|
||||||
|
autoclose={false}
|
||||||
|
placement="top-center"
|
||||||
|
size="md"
|
||||||
|
class="top-center bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative mx-auto w-full divide-y"
|
||||||
|
>
|
||||||
|
<div class="modal-box flex flex-col gap-2 items-center justify-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => {
|
||||||
|
$journals_sess.entry.show__ai_summary = !$journals_sess.entry.show__ai_summary;
|
||||||
|
}}
|
||||||
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 transition *:hover:inline"
|
||||||
|
title="Toggle AI Summary"
|
||||||
|
>
|
||||||
|
<Bot class="inline-block mr-1" />
|
||||||
|
<span class="hidden sm:inline">AI Summary</span>
|
||||||
|
{#if $journals_sess?.entry?.ai_summary}
|
||||||
|
<span class="badge badge-sm badge-secondary ml-1">
|
||||||
|
{$journals_sess?.entry?.ai_summary.split(' ').length} words
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <pre class="text-wrap">
|
||||||
|
{@html $journals_sess.entry.ai_summary}
|
||||||
|
</pre> -->
|
||||||
|
|
||||||
|
<E_app_codemirror_v5
|
||||||
|
editable={false}
|
||||||
|
readonly={true}
|
||||||
|
content={$journals_sess.entry.ai_summary ?? ''}
|
||||||
|
bind:new_content={$journals_sess.entry.ai_summary}
|
||||||
|
bind:theme_mode={$ae_loc.theme_mode}
|
||||||
|
placeholder="Write using Markdown here..."
|
||||||
|
class="
|
||||||
|
p-2
|
||||||
|
preset-outlined-success-400-600
|
||||||
|
hover:preset-tonal-surface
|
||||||
|
shadow-lg rounded-lg
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{:else}
|
{:else}
|
||||||
<section class="ae_meta flex flex-row flex-wrap gap-1 items-center justify-center w-full">
|
<section class="ae_meta flex flex-row flex-wrap gap-1 items-center justify-center w-full">
|
||||||
<span class="text-lg text-orange-900 dark:text-orange-100">
|
<span class="text-lg text-orange-900 dark:text-orange-100">
|
||||||
|
|||||||
Reference in New Issue
Block a user