Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: knowledge tab add button #1277

Merged
merged 9 commits into from
Sep 28, 2023
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import { useEffect, useState } from "react";
import { AiOutlineLoading3Quarters } from "react-icons/ai";

import { KnowledgeToFeedInput } from "@/lib/components/KnowledgeToFeedInput";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";

import { useFeedBrain } from "./hooks/useFeedBrain";
import { useKnowledge } from "./hooks/useKnowledge";

export const AddKnowledge = (): JSX.Element => {
const [shouldDisplayModal, setShouldDisplayModal] = useState(false);
const { currentBrain } = useBrainContext();
const { invalidateKnowledgeDataKey } = useKnowledge({
brainId: currentBrain?.id,
});

const { feedBrain, hasPendingRequests, setHasPendingRequests } = useFeedBrain(
{
dispatchHasPendingRequests: () => setHasPendingRequests(true),
closeFeedInput: () => setShouldDisplayModal(false),
}
);

useEffect(() => {
if (!hasPendingRequests) {
invalidateKnowledgeDataKey();
}
}, [hasPendingRequests, invalidateKnowledgeDataKey]);

return (
<>
<button
className="flex flex-1 items-center justify-center rounded-xl bg-white dark:bg-black border border-black/10 dark:border-white/25 p-2 md:p-6"
onClick={() => setShouldDisplayModal(true)}
>
<div className="flex flex-lin items-center">
<span className="text-2xl md:text-3xl">+</span>
</div>
</button>
{hasPendingRequests && (
<div className="flex mt-1 flex-col md:flex-row shadow-md dark:shadow-primary/25 hover:shadow-xl transition-shadow rounded-xl bg-white dark:bg-black border border-black/10 dark:border-white/25 p-2 md:p-6 pl-6">
<AiOutlineLoading3Quarters className="animate-spin text-2xl md:text-3xl self-center" />
</div>
)}
{shouldDisplayModal && (
<KnowledgeToFeedInput feedBrain={() => void feedBrain()} />
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"use client";

import { AiOutlineLoading3Quarters } from "react-icons/ai";
import { MdDelete } from "react-icons/md";

import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { Knowledge } from "@/lib/types/Knowledge";

import { useKnowledgeItem } from "./useKnowledgeItem";

export const DeleteKnowledge = ({
knowledge,
}: {
Expand All @@ -18,18 +18,18 @@ export const DeleteKnowledge = ({

const canDeleteFile = currentBrain?.role === "Owner";

console.log("isDeleting", isDeleting);

return (
<>
{canDeleteFile && (
<button
className="text-red-600 hover:text-red-900"
onClick={() => void onDeleteKnowledge(knowledge)}
>
<MdDelete size="20" />
</button>
)}
</>
if (!canDeleteFile) {
return <></>;
}

return isDeleting ? (
<AiOutlineLoading3Quarters />
) : (
<button
className="text-red-600 hover:text-red-900"
onClick={() => void onDeleteKnowledge(knowledge)}
>
<MdDelete size="20" />
</button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ import { useTranslation } from "react-i18next";

import { useKnowledgeApi } from "@/lib/api/knowledge/useKnowledgeApi";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useKnowledgeContext } from "@/lib/context/KnowledgeProvider/hooks/useKnowledgeContext";
import { useToast } from "@/lib/hooks";
import { Knowledge } from "@/lib/types/Knowledge";
import { useEventTracking } from "@/services/analytics/june/useEventTracking";

import { useKnowledge } from "../hooks/useKnowledge";

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useKnowledgeItem = () => {
const { deleteKnowledge } = useKnowledgeApi();
const [isDeleting, setIsDeleting] = useState(false);
const { publish } = useToast();
const { track } = useEventTracking();
const { currentBrain } = useBrainContext();
const { setAllKnowledge } = useKnowledgeContext();

const { invalidateKnowledgeDataKey } = useKnowledge({
brainId: currentBrain?.id,
});

const { t } = useTranslation(["translation", "explore"]);

const onDeleteKnowledge = async (knowledge: Knowledge) => {
setIsDeleting(true);
void track("DELETE_DOCUMENT");
const knowledge_name = 'fileName' in knowledge ? knowledge.fileName : knowledge.url;
const knowledge_name =
"fileName" in knowledge ? knowledge.fileName : knowledge.url;
try {
if (currentBrain?.id === undefined) {
throw new Error(t("noBrain", { ns: "explore" }));
Expand All @@ -31,9 +37,9 @@ export const useKnowledgeItem = () => {
brainId: currentBrain.id,
knowledgeId: knowledge.id,
});
setAllKnowledge((knowledges) =>
knowledges.filter((k) => k.id !== knowledge.id)
); // Optimistic update

invalidateKnowledgeDataKey();

publish({
variant: "success",
text: t("deleted", {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";

import {
FeedItemCrawlType,
FeedItemUploadType,
} from "@/app/chat/[chatId]/components/ActionsBar/types";
import { useChatApi } from "@/lib/api/chat/useChatApi";
import { useKnowledgeToFeedInput } from "@/lib/components/KnowledgeToFeedInput/hooks/useKnowledgeToFeedInput.ts";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
import { useToast } from "@/lib/hooks";

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useFeedBrain = ({
dispatchHasPendingRequests,
closeFeedInput,
}: {
dispatchHasPendingRequests?: () => void;
closeFeedInput?: () => void;
}) => {
const { publish } = useToast();
const { t } = useTranslation(["upload"]);

const { currentBrainId } = useBrainContext();
const { setKnowledgeToFeed, knowledgeToFeed } = useKnowledgeToFeedContext();
const [hasPendingRequests, setHasPendingRequests] = useState(false);

const { createChat, deleteChat } = useChatApi();

const { crawlWebsiteHandler, uploadFileHandler } = useKnowledgeToFeedInput();

const files: File[] = (
knowledgeToFeed.filter((c) => c.source === "upload") as FeedItemUploadType[]
).map((c) => c.file);

const urls: string[] = (
knowledgeToFeed.filter((c) => c.source === "crawl") as FeedItemCrawlType[]
).map((c) => c.url);

const feedBrain = async (): Promise<void> => {
if (currentBrainId === null) {
publish({
variant: "danger",
text: t("selectBrainFirst"),
});

return;
}

if (knowledgeToFeed.length === 0) {
publish({
variant: "danger",
text: t("addFiles"),
});

return;
}

//TODO: Modify backend archi to avoid creating a chat for each feed action
const currentChatId = (await createChat("New Chat")).chat_id;

try {
dispatchHasPendingRequests?.();
closeFeedInput?.();
setHasPendingRequests(true);
const uploadPromises = files.map((file) =>
uploadFileHandler(file, currentBrainId, currentChatId)
);
const crawlPromises = urls.map((url) =>
crawlWebsiteHandler(url, currentBrainId, currentChatId)
);

await Promise.all([...uploadPromises, ...crawlPromises]);

setKnowledgeToFeed([]);
} catch (e) {
publish({
variant: "danger",
text: JSON.stringify(e),
});
} finally {
setHasPendingRequests(false);
await deleteChat(currentChatId);
}
};

return {
feedBrain,
hasPendingRequests,
setHasPendingRequests,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { UUID } from "crypto";

import { getKnowledgeDataKey } from "@/lib/api/knowledge/config";
import { useKnowledgeApi } from "@/lib/api/knowledge/useKnowledgeApi";

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useKnowledge = ({ brainId }: { brainId?: UUID }) => {
const queryClient = useQueryClient();

const { getAllKnowledge } = useKnowledgeApi();
const { data: allKnowledge, isLoading: isPending } = useQuery({
queryKey: [getKnowledgeDataKey(brainId!)],
queryFn: () => getAllKnowledge({ brainId: brainId! }),
enabled: brainId !== undefined,
});

if (brainId === undefined) {
return {
invalidateKnowledgeDataKey: () => void {},
isPending: false,
allKnowledge: [],
};
}

const knowledge_data_key = getKnowledgeDataKey(brainId);

const invalidateKnowledgeDataKey = () => {
void queryClient.invalidateQueries({ queryKey: [knowledge_data_key] });
};

return {
invalidateKnowledgeDataKey,
isPending,
allKnowledge: allKnowledge ?? [],
};
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ import { AnimatePresence, motion } from "framer-motion";
import { useTranslation } from "react-i18next";

import Spinner from "@/lib/components/ui/Spinner";
import { KnowledgeProvider } from "@/lib/context";
import { KnowledgeToFeedProvider } from "@/lib/context";

import { AddKnowledge } from "./AddKnowledge";
import { KnowledgeTable } from "./KnowledgeTable";
import { useKnowledgeTab } from "./hooks/useKnowledgeTab";
import { useKnowledge } from "./hooks/useKnowledge";

type KnowledgeTabProps = {
brainId: UUID;
};
export const KnowledgeTab = ({ brainId }: KnowledgeTabProps): JSX.Element => {
const { t } = useTranslation(["translation", "explore"]);
const { isPending, allKnowledge } = useKnowledgeTab({
const { isPending, allKnowledge } = useKnowledge({
brainId,
});

return (
<KnowledgeProvider>
<KnowledgeToFeedProvider>
<main>
<section className="w-full outline-none pt-10 flex flex-col gap-5 items-center justify-center p-6">
<div className="flex flex-col items-center justify-center">
Expand All @@ -28,6 +29,7 @@ export const KnowledgeTab = ({ brainId }: KnowledgeTabProps): JSX.Element => {
</h1>
<h2 className="opacity-50">{t("subtitle", { ns: "explore" })}</h2>
</div>
<AddKnowledge />
{isPending ? (
<Spinner />
) : (
Expand All @@ -48,6 +50,6 @@ export const KnowledgeTab = ({ brainId }: KnowledgeTabProps): JSX.Element => {
)}
</section>
</main>
</KnowledgeProvider>
</KnowledgeToFeedProvider>
);
};
3 changes: 1 addition & 2 deletions frontend/app/brains-management/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";
import { ReactNode } from "react";

import { KnowledgeProvider } from "@/lib/context";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { redirectToLogin } from "@/lib/router/redirectToLogin";

Expand All @@ -20,7 +19,7 @@ const Layout = ({ children }: LayoutProps): JSX.Element => {
return (
<div className="relative h-full w-full flex justify-stretch items-stretch">
<BrainsList />
<KnowledgeProvider>{children}</KnowledgeProvider>
{children}
</div>
);
};
Expand Down
Loading