diff --git a/package.json b/package.json index 7760284..72c75eb 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,11 @@ "version": "0.0.1", "description": "A UI for decentralized AI", "productName": "Libertai UI", - "author": "david.amelekh@protonmail.com", + "author": "LibertAI Team", + "contributors": [ + "David Amelekh ", + "Reza Rahemtola " + ], "private": true, "license": "MIT", "engines": { diff --git a/src/components/MessageInput.vue b/src/components/MessageInput.vue index db2edac..fdf40fe 100644 --- a/src/components/MessageInput.vue +++ b/src/components/MessageInput.vue @@ -63,7 +63,7 @@ diff --git a/src/pages/NewChat.vue b/src/pages/NewChat.vue index 6c7e9ef..50ac422 100644 --- a/src/pages/NewChat.vue +++ b/src/pages/NewChat.vue @@ -43,6 +43,7 @@
-import { defaultChatTopic } from 'src/utils/chat'; // Import State +import { defaultChatTopic } from 'src/utils/chat'; import { useModelsStore } from 'stores/models'; import { useChatsStore } from 'stores/chats'; import { usePersonasStore } from 'stores/personas'; import { useSettingsStore } from 'stores/settings'; import { ref, watch } from 'vue'; -import { useRoute, useRouter } from 'vue-router'; // Import components +import { useRoute, useRouter } from 'vue-router'; import MessageInput from 'src/components/MessageInput.vue'; import PersonaDropdown from 'components/select/PersonaSelector.vue'; import { getPersonaAvatarUrl } from 'src/utils/personas'; @@ -71,14 +72,16 @@ import { UIModel } from 'src/utils/models'; import { UIPersona } from 'src/types/personas'; import { SendMessageParams } from 'src/types/chats'; import KnowledgeBasesSelector from 'components/select/KnowledgeBasesSelector.vue'; +import { useKnowledgeStore } from 'stores/knowledge'; const router = useRouter(); -// Stored State +// Store state const modelsStore = useModelsStore(); const chatsStore = useChatsStore(); const personasStore = usePersonasStore(); const settingsStore = useSettingsStore(); +const knowledgeStore = useKnowledgeStore(); const route = useRoute(); @@ -88,14 +91,17 @@ const selectedPersona = ref(personasStore.personas[0]); const username = ref(settingsStore.username); const selectedKnowledgeBases = ref([]); +const knowledgeAttachmentFile = ref(undefined); + watch( () => route.query.persona as string | undefined, (personaId: string | undefined) => { - if (personaId) { - const persona = personasStore.personas.find((p) => p.id === personaId); - if (persona) { - selectedPersona.value = persona; - } + if (!personaId) { + return; + } + const persona = personasStore.personas.find((p) => p.id === personaId); + if (persona) { + selectedPersona.value = persona; } }, { @@ -103,6 +109,18 @@ watch( }, ); +watch( + () => route.query.knowledgeDocumentAttachment as string | undefined, + async (knowledgeDocumentAttachmentInfo: string | undefined) => { + if (!knowledgeDocumentAttachmentInfo) { + return; + } + const [kbIdentifierId, documentId] = knowledgeDocumentAttachmentInfo.split(','); + knowledgeAttachmentFile.value = await knowledgeStore.getDocumentFile(kbIdentifierId, documentId); + }, + { immediate: true }, +); + async function sendMessage({ content, attachments }: SendMessageParams) { // Extract the values out of our relevant refs const title = defaultChatTopic; diff --git a/src/stores/knowledge.ts b/src/stores/knowledge.ts index 54439b6..0fcacf9 100644 --- a/src/stores/knowledge.ts +++ b/src/stores/knowledge.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import { KnowledgeBase, KnowledgeBaseIdentifier, KnowledgeDocument } from 'src/types/knowledge'; import { useAccountStore } from 'stores/account'; +import { decryptFile } from 'src/utils/encryption'; type KnowledgeStoreState = { knowledgeBases: KnowledgeBase[]; @@ -17,7 +18,7 @@ export const useKnowledgeStore = defineStore('knowledge', { isLoaded: false, }), getters: { - getDocumentsFrom: (state) => { + getDocumentsFromBases: (state) => { return (ids: string[]): KnowledgeDocument[] => { return state.knowledgeBases .filter((kb) => ids.includes(kb.id)) @@ -25,6 +26,33 @@ export const useKnowledgeStore = defineStore('knowledge', { .flat(); }; }, + getDocumentFile: (state) => { + return async (kbIdentifierId: string, documentId: string): Promise => { + const { alephStorage } = useAccountStore(); + if (alephStorage === null) { + return; + } + + const document = state.knowledgeBases + .map((kb) => kb.documents) + .flat() + .find((document) => document.id === documentId); + if (!document) { + return; + } + const kbIdentifier = state.knowledgeBaseIdentifiers.find((kbi) => kbi.id === kbIdentifierId); + if (!kbIdentifier) { + return; + } + + const encryptionKey = Buffer.from(kbIdentifier.encryption.key); + const encryptionIv = Buffer.from(kbIdentifier.encryption.iv); + + const downloadedFile = await alephStorage.downloadFile(document.store.ipfs_hash); + const decryptedContent = decryptFile(downloadedFile, encryptionKey, encryptionIv); + return new File([decryptedContent], document.name, { type: document.type }); + }; + }, }, actions: { async load() { diff --git a/src/utils/knowledge/attachments.ts b/src/utils/knowledge/attachments.ts index 6040cf8..97a0c4b 100644 --- a/src/utils/knowledge/attachments.ts +++ b/src/utils/knowledge/attachments.ts @@ -2,11 +2,13 @@ import { v4 as uuidv4 } from 'uuid'; import { MessageAttachment } from 'src/types/chats'; import { extractFileContent } from 'src/utils/knowledge/parsing'; +export const MAX_ATTACHMENT_SIZE = 4 * 1024; // 4 KiB + export const processAttachment = async (file: File): Promise => { const title = file.name; const fileInfo = await extractFileContent(file); - if (fileInfo.content.length > 4 * 1024) { + if (fileInfo.content.length > MAX_ATTACHMENT_SIZE) { // File is too big to be inlined, rejecting it. // Later we'll use a knowledge db to fix this. throw new Error('File is too big, please use a file of 4 KB of content or less.'); diff --git a/src/utils/knowledge/parsing.ts b/src/utils/knowledge/parsing.ts index fc7279e..12008fa 100644 --- a/src/utils/knowledge/parsing.ts +++ b/src/utils/knowledge/parsing.ts @@ -2,7 +2,7 @@ import mime from 'mime'; import * as pdfjs from 'pdfjs-dist'; import { TextItem } from 'pdfjs-dist/types/src/display/api'; -export const supportedInputFiles = ['.txt', '.md', '.pdf'].join(','); +export const supportedInputFiles = ['.txt', '.md', '.pdf', '.py'].join(','); const extractTextFromPdfFile = async (file: File): Promise => { const pdfUrl = URL.createObjectURL(file);