Skip to content

Commit

Permalink
Merge pull request #49 from your-papa/dev
Browse files Browse the repository at this point in the history
0.4.0
  • Loading branch information
nicobrauchtgit authored Mar 14, 2024
2 parents 739971e + b994f83 commit 48696f8
Show file tree
Hide file tree
Showing 33 changed files with 793 additions and 357 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/logs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Check for console.logs

on:
pull_request:
branches:
- main

jobs:
check-logs:
runs-on: ubuntu-latest
steps:
- name: get the sources
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Run check script
run: node .github/workflows/create-check.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Interact with your privacy focused assistant, leveraging Ollama or OpenAI, making your second brain even smarter.",
"author": "Leo310, nicobrauchtgit",
"authorUrl": "https://github.com/nicobrauchtgit",
"version": "0.3.2",
"version": "0.4.2",
"minAppVersion": "1.5.0",
"isDesktopOnly": true
}
35 changes: 21 additions & 14 deletions src/SmartSecondBrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isOllamaRunning, getOllamaModels } from './controller/Ollama';
import { isAPIKeyValid } from './controller/OpenAI';
import Log, { LogLvl } from './logging';
import { data, papaState, errorState, papaIndexingProgress, chatHistory, serializeChatHistory, runState, runContent } from './store';
import { _ } from 'svelte-i18n';

export default class SmartSecondBrain {
private papa: Papa;
Expand All @@ -20,23 +21,29 @@ export default class SmartSecondBrain {

async init() {
const d = get(data);
if (get(papaState) === 'running') return new Notice('Smart Second Brain is still running.', 4000);
const t = get(_);
if (get(papaState) === 'running') return new Notice(t('notice.still_running'), 4000);
else if (get(papaState) === 'indexing' || get(papaState) === 'loading') {
return new Notice('Please wait for the indexing to finish', 4000);
return new Notice(t('notice.still_indexing'), 4000);
} else if (d.isIncognitoMode && !(await isOllamaRunning())) {
papaState.set('error');
errorState.set('ollama-not-running');
return new Notice('Please make sure Ollama is running before initializing Smart Second Brain.', 4000);
return new Notice(t('notice.ollama_not_running'), 4000);
} else if (d.isIncognitoMode) {
const models = await getOllamaModels();
if (!models.includes(d.ollamaGenModel.model)) {
papaState.set('error');
errorState.set('ollama-model-not-installed');
return new Notice('Ollama model not installed. Please install the model before initializing Smart Second Brain.', 4000);
errorState.set('ollama-gen-model-not-installed');
return new Notice(t('notice.ollama_gen_model'), 4000);
}
if (!models.includes(d.ollamaEmbedModel.model)) {
papaState.set('error');
errorState.set('ollama-embed-model-not-installed');
return new Notice(t('notice.ollama_embed_model'), 4000);
}
} else if (!d.isIncognitoMode && !(await isAPIKeyValid(d.openAIGenModel.openAIApiKey))) {
papaState.set('error');
return new Notice('Please make sure OpenAI API Key is valid before initializing Smart Second Brain.', 4000);
return new Notice(t('notice.openai_key'), 4000);
}
if (get(papaState) !== 'indexing-pause') {
papaState.set('loading');
Expand All @@ -58,7 +65,7 @@ export default class SmartSecondBrain {
} catch (e) {
Log.error(e);
papaState.set('error');
return new Notice('Failed to initialize Smart Second Brain (Error: ' + e + '). Please retry.', 4000);
return new Notice(t('notice.failed', { values: { error: e } }), 4000);
}
// check if vector store data exists
if (await this.app.vault.adapter.exists(this.getVectorStorePath())) {
Expand All @@ -75,7 +82,6 @@ export default class SmartSecondBrain {
})
);
papaState.set('indexing');
// const embedNotice = new Notice('Indexing notes into your smart second brain...', 0);
let needsSave = false;
try {
for await (const result of this.papa.embedDocuments(docs)) {
Expand All @@ -93,23 +99,24 @@ export default class SmartSecondBrain {
Log.error(e);
papaState.set('error');
// TODO add error state
new Notice('Failed to index notes into your smart second brain. Please retry.', 4000);
new Notice(t('notice.failed_indexing'), 4000);
}
this.needsToSaveVectorStoreData = needsSave;
this.saveVectorStoreData();
if (get(papaIndexingProgress) === 100) {
new Notice('Smart Second Brain initialized.', 2000);
new Notice(t('notice.done'), 2000);
papaIndexingProgress.set(0);
papaState.set('idle');
}
}

canRunPapa() {
if (get(papaState) === 'running') return new Notice('Please wait for the current query to finish', 4000) && false;
const t = get(_);
if (get(papaState) === 'running') return new Notice(t('notice.still_running'), 4000) && false;
else if (get(papaState) === 'indexing' || get(papaState) === 'indexing-pause' || get(papaState) === 'loading')
return new Notice('Please wait for the indexing to finish', 4000) && false;
else if (get(papaState) === 'error') return new Notice('Please wait for the error to resolve', 4000) && false;
else if (get(papaState) !== 'idle') return new Notice('Please initialize your Smart Second Brain first', 4000) && false;
return new Notice(t('notice.still_indexing'), 4000) && false;
else if (get(papaState) === 'error') return new Notice(t('notice.error'), 4000) && false;
else if (get(papaState) !== 'idle') return new Notice(t('notice.not_initialized'), 4000) && false;
return true;
}

Expand Down
29 changes: 12 additions & 17 deletions src/components/Chat/Chat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { afterUpdate } from 'svelte';
import DotAnimation from '../base/DotAnimation.svelte';
import MessageContainer from './MessageContainer.svelte';
import { t } from 'svelte-i18n';
import {
papaState,
chatHistory,
Expand Down Expand Up @@ -45,7 +46,7 @@
});
$: if ($runState === 'retrieving' && $runContent == '0') {
new Notice('No notes retrieved. Maybe lower the similarity threshold.');
new Notice($t('notice.no_notes_retrieved'));
}
let editElem: HTMLSpanElement;
Expand Down Expand Up @@ -90,11 +91,11 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="flex {$data.isChatComfy ? 'justify-end' : ''} gap-1 opacity-0 group-hover:opacity-100">
{#if $isEditing && editMessageId === message.id}
<span aria-label="Copy Text" class={iconStyle} on:click|preventDefault={cancelEditing} use:icon={'x-circle'} />
<span aria-label={$t('chat.copy')} class={iconStyle} on:click|preventDefault={cancelEditing} use:icon={'x-circle'} />
{:else}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span
aria-label="Edit query and regenerate the answer"
aria-label={$t('chat.edit')}
class={iconStyle}
on:click|preventDefault={() => wrapperEditMessage(message, textarea)}
use:icon={'pencil-line'}
Expand All @@ -105,23 +106,17 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<span
on:mouseover={onMouseOver}
use:renderMarkdown={message.content}
style="background: transparent;"
on:click={onClick}
bind:this={initialAssistantMessageSpan}
/>
<span on:mouseover={onMouseOver} use:renderMarkdown={message.content} on:click={onClick} bind:this={initialAssistantMessageSpan} />
<div class="flex gap-1 opacity-0 group-hover:opacity-100">
{#if !$isEditingAssistantMessage}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span aria-label="Copy Text" class={iconStyle} on:click={() => toClipboard(message.content)} use:icon={'copy'} />
<span aria-label={$t('chat.copy')} class={iconStyle} on:click={() => toClipboard(message.content)} use:icon={'copy'} />
{#if $chatHistory.indexOf(message) !== 0}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span
aria-label="Deletes all following Messages and regenerates the answer to the current query"
aria-label={$t('chat.regenerate')}
class={iconStyle}
on:click|preventDefault={() => redoGeneration(message)}
use:icon={'refresh-cw'}
Expand All @@ -131,7 +126,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span
aria-label="Change the initial assistant message"
aria-label={$t('chat.change_assistant_prompt')}
class={iconStyle}
on:click|preventDefault={() => editInitialAssistantMessage(message.content, textarea)}
use:icon={'pencil-line'}
Expand All @@ -141,15 +136,15 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span
aria-label="Cancel editing"
aria-label={$t('chat.cancel_edit')}
class={iconStyle}
on:click|preventDefault={() => cancelEditingInitialAssistantMessage(initialAssistantMessageSpan)}
use:icon={'x-circle'}
/>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span
aria-label="Reset to default"
aria-label={$t('chat.reset_assistant_prompt')}
class={iconStyle}
on:click={() => resetInitialAssistantMessage(initialAssistantMessageSpan)}
use:icon={'rotate-ccw'}
Expand All @@ -164,9 +159,9 @@
{#if $runState === 'startup'}
<DotAnimation />
{:else if $runState === 'retrieving'}
<p>Retrieving<DotAnimation /></p>
<p>{$t('chat.retrieving')}<DotAnimation /></p>
{:else if $runState === 'reducing'}
<p>Reducing {$runContent} Notes<DotAnimation /></p>
<p>{$t('chat.reducing', { values: { num: $runContent } })}<DotAnimation /></p>
{:else if $runState === 'generating' && $runContent}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
Expand Down
46 changes: 18 additions & 28 deletions src/components/Chat/Input.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { Notice, setIcon } from 'obsidian';
import type { KeyboardEventHandler } from 'svelte/elements';
import { nanoid } from 'nanoid';
import { t } from 'svelte-i18n';
import {
plugin,
data,
Expand All @@ -17,6 +16,7 @@
} from '../../store';
import ProgressCircle from '../base/ProgressCircle.svelte';
import { addMessage } from '../../controller/Messages';
import Logo from '../base/Logo.svelte';
export let textarea: HTMLTextAreaElement;
Expand Down Expand Up @@ -48,14 +48,9 @@
addMessage('Assistant', $runContent);
}
}
function injectContext(event: KeyboardEvent): KeyboardEventHandler<HTMLInputElement> {
if (event.key !== '[') return;
new Notice('Injecting context...');
}
function handleRAGToggle() {
$data.isUsingRag = !$data.isUsingRag;
$plugin.saveSettings();
new Notice($data.isUsingRag ? 'Now chatting with your Second Brain' : 'Now chatting with the LLM');
}
function handelEnter(event: KeyboardEvent) {
Expand All @@ -74,42 +69,38 @@
if (textarea.scrollHeight == 42) textarea.style.height = '2rem';
else textarea.style.height = textarea.scrollHeight + 'px';
}
const iconStyle = 'text-[--text-normal] hover:text-[--text-accent-hover]';
</script>

<!-- save delete and rag settings slightly above input field -->
<div class="relative">
<div
class="absolute -top-[54px] left-1/2 flex -translate-x-1/2 items-center gap-3 rounded-t-2xl border border-solid border-x-[--background-modifier-border] border-b-transparent border-t-[--background-modifier-border] {$isChatInSidebar
class="absolute -top-[62px] left-1/2 flex -translate-x-1/2 items-center gap-3 rounded-t-2xl border border-solid border-x-[--background-modifier-border] border-b-transparent border-t-[--background-modifier-border] {$isChatInSidebar
? 'bg-[--background-secondary]'
: 'bg-[--background-primary]'} p-2"
: 'bg-[--background-primary]'} pt-2 px-2"
>
{#if $chatHistory.length > 1}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
aria-label="Save the Chat to a file"
class="text-[--text-normal] hover:text-[--text-accent-hover]"
use:icon={'save'}
on:click={() => $plugin.saveChat()}
hidden={$papaState === 'running'}
/>
<div aria-label={$t('chat.save')} class={iconStyle} use:icon={'save'} on:click={() => $plugin.saveChat()} hidden={$papaState === 'running'} />
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
aria-label="Toggle between chatting with your Notes or the LLM"
aria-label={$data.isUsingRag ? $t('chat.toggle_llm') : $t('chat.toggle_papa')}
on:click={handleRAGToggle}
use:icon={'brain-circuit'}
class={`h-[--icon-xl] w-[--icon-xl] *:!h-[--icon-xl] *:!w-[--icon-xl] hover:text-[--text-accent-hover] ${
class={`h-[48px] w-[48px] *:!h-[48px] *:!w-[48px] hover:text-[--text-accent-hover] ${
$data.isUsingRag ? 'text-[--color-accent]' : 'text-[--text-normal]'
}`}
/>
>
<Logo />
</div>
{#if $chatHistory.length > 1}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
aria-label="Delete Chat History"
class="text-[--text-normal] hover:text-[--text-accent-hover]"
aria-label={$t('chat.delete')}
class={iconStyle}
on:click|preventDefault={chatHistory.reset}
use:icon={'trash-2'}
hidden={$papaState === 'running'}
Expand All @@ -122,35 +113,34 @@
bind:this={textarea}
id="chat-view-user-input-element"
class="h-8 max-h-40 flex-1 resize-none"
placeholder={'Chat with your second Brain...'}
placeholder={$t('chat.input_placeholder')}
bind:value={$chatInput}
on:keydown={handelEnter}
on:keyup={injectContext}
/>
{#if $papaState === 'running'}
<button
aria-label="Stop your Smart Second Brain"
aria-label={$t('chat.stop')}
on:click={() => ($runState = 'stopped')}
class="h-8 rounded-r-md px-4 py-2 transition duration-300 ease-in-out hover:bg-[--text-accent-hover]"
use:icon={'stop-circle'}
/>
{:else if $papaState === 'idle'}
<button
aria-label="Run your Smart Second Brain"
aria-label={$t('chat.send')}
on:click={runPapaFromInput}
class="h-8 rounded-r-md px-4 py-2 transition duration-300 ease-in-out hover:bg-[--text-accent-hover]"
use:icon={'send-horizontal'}
/>
{:else if $papaState === 'error'}
<button
aria-label="Retry initializing"
aria-label={$t('chat.retry_error')}
on:click={() => $plugin.s2b.init()}
class="h-8 rounded-l-md px-4 py-2 transition duration-300 ease-in-out hover:bg-[--text-accent-hover]"
use:icon={'refresh-cw'}
/>
{:else if $papaState === 'settings-change'}
<button
aria-label="Reinitialize, Settings changed"
aria-label={$t('chat.reintialize')}
on:click={() => $plugin.s2b.init()}
class="h-8 rounded-l-md px-4 py-2 transition duration-300 ease-in-out hover:bg-[--text-accent-hover]"
use:icon={'refresh-cw'}
Expand Down
10 changes: 6 additions & 4 deletions src/components/Chat/MessageContainer.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
<script lang="ts">
import { data, isChatInSidebar } from '../../store';
import { t } from 'svelte-i18n';
export let role: 'User' | 'Assistant';
</script>

{#if role === 'User'}
{#if $data.isChatComfy}
<div class="my-4 mr-4 flex justify-end">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="group max-w-[80%] rounded-t-lg rounded-bl-lg px-4" style="background-color: var(--text-selection);">
<div class="group max-w-[80%] [&_p]:mb-2 rounded-t-lg rounded-bl-lg pb-1 px-4" style="background-color: var(--text-selection);">
<slot />
</div>
</div>
{:else}
<div class="group border-x-0 border-b border-t-0 border-solid border-[--background-modifier-border] p-2 pr-4">
<div class="text-[--text-accent] mt-2 font-bold">User</div>
<div class="text-[--text-accent] mt-2 font-bold">{$t('chat.user')}</div>
<slot />
</div>
{/if}
{:else if $data.isChatComfy}
<div
class="group my-4 ml-4 w-fit max-w-[80%] rounded-t-lg rounded-br-lg p-1 pl-4 pr-4 {$isChatInSidebar
class="group ml-4 w-fit max-w-[80%] [&_p]:mb-2 rounded-t-lg rounded-br-lg pb-1 px-4 mt-4 pt-[1px] {$isChatInSidebar
? 'bg-[--background-secondary]'
: 'bg-[--background-primary]'}"
>
Expand All @@ -31,7 +33,7 @@
? 'bg-[--background-secondary]'
: 'bg-[--background-primary]'}"
>
<div class="text-[--text-accent] mt-2 font-bold">Smart2Brain</div>
<div class="text-[--text-accent] mt-2 font-bold">{$t('chat.assistant')}</div>
<slot />
</div>
{/if}
Loading

0 comments on commit 48696f8

Please sign in to comment.