Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test": "echo \"No test specified\" && exit 0",
"dev": "quasar dev -m pwa",
"build": "quasar build -m pwa"
"build": "quasar build -m pwa",
"dev:electron": "quasar dev -m electron"
},
"dependencies": {
"@quasar/extras": "^1.0.0",
Expand All @@ -22,7 +23,8 @@
"uuid": "^9.0.0",
"vue": "^3.0.0",
"vue-i18n": "^9.2.2",
"vue-router": "^4.0.0"
"vue-router": "^4.0.0",
"vue3-markdown-it": "^1.0.10"
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "^3.3.1",
Expand All @@ -32,6 +34,7 @@
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"autoprefixer": "^10.4.2",
"electron": "^24.1.0",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0",
Expand Down
6 changes: 4 additions & 2 deletions src/components/ChatItem.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template>
<!-- TODO: render in a nicer way -->
<div>{{ content }}</div>
<Markdown class="chat-item-content" :source="content"></Markdown>
</template>

<script lang="ts" setup>
import Markdown from 'vue3-markdown-it';

defineProps({
content: {
type: String,
required: true,
},
})
});
</script>
131 changes: 96 additions & 35 deletions src/pages/ChatPage.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
<template>
<q-page class="column items-center justify-evenly" padding>
<div class="container" ref="containerRef">
<q-chat-message v-for="{ id, role, content, stamp } in chatStore.history" :key="id" :name="roleToDisplayName[role]"
:avatar="roleToAvatarLink[role]" :sent="role === 'user'" :stamp="dayjs(stamp).fromNow()" size="8">
<q-chat-message
v-for="{ id, role, content, stamp } in chatStore.history"
:key="id"
:name="roleToDisplayName[role]"
:avatar="roleToAvatarLink[role]"
:sent="role === 'user'"
:stamp="dayjs(stamp).fromNow()"
size="8"
>
<ChatItem :content="content" />
</q-chat-message>

<!-- Waiting for AI -->
<q-chat-message v-if="thinking" :name="roleToDisplayName['assistant']" :avatar="roleToAvatarLink['assistant']">
<q-chat-message
v-if="thinking"
:name="roleToDisplayName['assistant']"
:avatar="roleToAvatarLink['assistant']"
>
<q-spinner-dots size="2rem" />
</q-chat-message>

<!-- Waiting for system -->
<q-chat-message v-if="executing" :name="roleToDisplayName['system']" :avatar="roleToAvatarLink['system']">
<q-chat-message
v-if="executing"
:name="roleToDisplayName['system']"
:avatar="roleToAvatarLink['system']"
>
<q-spinner-gears size="2rem" />
</q-chat-message>

<!-- Waiting for user's decision -->
<q-chat-message v-if="deciding" :name="roleToDisplayName['user']" :avatar="roleToAvatarLink['user']" sent size="4">
<q-chat-message
v-if="deciding"
:name="roleToDisplayName['user']"
:avatar="roleToAvatarLink['user']"
sent
size="4"
>
<div>
<q-spinner-rings size="2rem" />
<q-btn flat @click="moveOn()">move on!</q-btn>
Expand All @@ -26,13 +47,34 @@
</div>

<q-page-sticky position="bottom-right" :offset="[18, 18]">
<q-fab v-model="fabRight" vertical-actions-align="right" color="primary" icon="keyboard_arrow_up" direction="up">
<q-fab-action label-position="left" color="red" @click="resetChatHistory()" icon="restart_alt"
label="Reset chat history" />
<q-fab-action label-position="left" color="red" @click="resetAssistant()" icon="smart_toy"
label="Reset assistant" />
<q-fab-action label-position="left" color="red" @click="resetCredentials()" icon="key"
label="Reset credentials" />
<q-fab
v-model="fabRight"
vertical-actions-align="right"
color="primary"
icon="keyboard_arrow_up"
direction="up"
>
<q-fab-action
label-position="left"
color="red"
@click="resetChatHistory()"
icon="restart_alt"
label="Reset chat history"
/>
<q-fab-action
label-position="left"
color="red"
@click="resetAssistant()"
icon="smart_toy"
label="Reset assistant"
/>
<q-fab-action
label-position="left"
color="red"
@click="resetCredentials()"
icon="key"
label="Reset credentials"
/>
</q-fab>
</q-page-sticky>
</q-page>
Expand All @@ -43,35 +85,36 @@ import { onMounted, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import dayjs from 'dayjs';
import { useRouter } from 'vue-router';
import { useQuasar, scroll } from 'quasar'
import { useQuasar, scroll } from 'quasar';

import { useAssistantStore } from '../stores/assistant';
import { useChatStore } from '../stores/chat';
import ChatItem from '../components/ChatItem.vue';
import { useCredentialStore } from '../stores/credential';

const $q = useQuasar()
const $q = useQuasar();
const router = useRouter();
const assistantStore = useAssistantStore();
const chatStore = useChatStore();
const credentialStore = useCredentialStore();
const { lastHistoryItem, deciding, thinking, executing } = storeToRefs(chatStore);
const { lastHistoryItem, deciding, thinking, executing } =
storeToRefs(chatStore);

const roleToDisplayName = {
user: 'ME',
system: 'SYSTEM',
assistant: assistantStore.name,
}
};

const roleToAvatarLink = {
user: 'https://cdn.quasar.dev/img/avatar1.jpg',
system: 'https://cdn.quasar.dev/img/avatar2.jpg',
assistant: 'https://cdn.quasar.dev/img/avatar3.jpg',
}
};

// Return to homepage if assistant is not completed
if (!assistantStore.completed) {
router.push('/')
router.push('/');
} else if (!chatStore.hasHistory) {
chatStore.addBasicPrompt(assistantStore.prompt);
}
Expand All @@ -81,10 +124,12 @@ const moveOn = async () => {

try {
await chatStore.exec();
await chatStore.chat([{
role: 'user',
content: 'Generate next command json',
}])
await chatStore.chat([
{
role: 'user',
content: 'Generate next command json',
},
]);
} catch (e) {
if (e instanceof Error) {
$q.notify({
Expand All @@ -99,13 +144,16 @@ const moveOn = async () => {
message: `Unknown error: ${e}`,
});
}
}
};

onMounted(async () => {
scrollToBottom();

try {
if (lastHistoryItem.value?.role && ['user', 'system'].includes(lastHistoryItem.value?.role)) {
if (
lastHistoryItem.value?.role &&
['user', 'system'].includes(lastHistoryItem.value?.role)
) {
await chatStore.chat();
}
} catch (e) {
Expand All @@ -122,7 +170,7 @@ onMounted(async () => {
message: `Unknown error: ${e}`,
});
}
})
});

const containerRef = ref<HTMLDivElement | null>(null);
const scrollToBottom = () => {
Expand All @@ -132,33 +180,37 @@ const scrollToBottom = () => {
const duration = 1000;
scroll.animVerticalScrollTo(target, offset, duration);
}
}

watch(chatStore, () => {
scrollToBottom();
}, {
deep: true
});
};

watch(
chatStore,
() => {
scrollToBottom();
},
{
deep: true,
}
);

const fabRight = ref(false);
const resetChatHistory = () => {
fabRight.value = false;
chatStore.$reset();
location.reload();
}
};
const resetAssistant = () => {
fabRight.value = false;
chatStore.$reset();
assistantStore.$reset();
location.reload();
}
};
const resetCredentials = () => {
fabRight.value = false;
chatStore.$reset();
assistantStore.$reset();
credentialStore.$reset();
location.reload();
}
};
</script>

<style scoped lang="scss">
Expand All @@ -168,5 +220,14 @@ const resetCredentials = () => {
}

width: 80%;

:deep() .chat-item-content pre {
overflow-x: auto;
background-color: rgb(23, 15, 62);
color: white;
border-radius: 0.2rem;
padding: 0.5rem;
margin: 0.5rem 0.5rem;
}
}
</style>
7 changes: 4 additions & 3 deletions src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ PERFORMANCE EVALUATION:
You should only respond in JSON format as described below

RESPONSE FORMAT:
\`\`\`json
{
"command": {
"name": "command name",
Expand All @@ -44,9 +45,9 @@ RESPONSE FORMAT:
"speak": "thoughts summary to say to user"
}
}

\`\`\`
Ensure the response can be parsed by Javascript JSON.parse() function.
`
`;

export const getWebsiteSummary = (content: string, question?: string) => {
return `"""
Expand All @@ -55,4 +56,4 @@ ${content}

Using the above text, please answer the following question: "${question}" -- if the question cannot be answered using the text, please summarize the text.
`;
}
};
2 changes: 2 additions & 0 deletions src/shims-vue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>;
export default component;
}

declare module 'vue3-markdown-it';
Loading