mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-23 21:22:35 +03:00
feat: add Agent creation frontend (#1836)
Issue: https://github.com/StanGirard/quivr/issues/1832 Demo: https://github.com/StanGirard/quivr/assets/63923024/e9c0e9b8-04b1-4f12-a8ef-b27f0f489e48 <img width="1503" alt="Screenshot 2023-12-06 at 15 23 12" src="https://github.com/StanGirard/quivr/assets/63923024/11e6960b-c1c3-4abf-aeed-c226f034246f"> <img width="1503" alt="Screenshot 2023-12-06 at 15 23 23" src="https://github.com/StanGirard/quivr/assets/63923024/09681a9d-ce2e-42bb-8151-1986e27f7312">
This commit is contained in:
parent
90c4a44525
commit
d9f6ce4e68
@ -49,6 +49,7 @@ export type CreateBrainInput = {
|
||||
brain_type?: BrainType;
|
||||
brain_definition?: Omit<ApiBrainDefinition, "brain_id">;
|
||||
brain_secrets_values?: Record<string, string>;
|
||||
connected_brains_ids?: UUID[];
|
||||
};
|
||||
|
||||
export type UpdateBrainInput = Partial<CreateBrainInput>;
|
||||
|
@ -6,6 +6,7 @@ import { ApiRequestDefinition } from "@/lib/components/ApiRequestDefinition";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { BrainType } from "@/lib/types/brainConfig";
|
||||
|
||||
import { CompositeBrainConnections } from "./components/CompositeBrainConnections/CompositeBrainConnections";
|
||||
import { KnowledgeToFeedInput } from "./components/KnowledgeToFeedInput";
|
||||
import { useBrainCreationHandler } from "./hooks/useBrainCreationHandler";
|
||||
import { useBrainKnowledgeStep } from "./hooks/useBrainKnowledgeStep";
|
||||
@ -30,7 +31,7 @@ export const BrainKnowledgeStep = ({
|
||||
const brainTypeToKnowledgeComponent: Record<BrainType, JSX.Element> = {
|
||||
doc: <KnowledgeToFeedInput />,
|
||||
api: <ApiRequestDefinition />,
|
||||
chatflow: <div>Coming soon</div>,
|
||||
composite: <CompositeBrainConnections />,
|
||||
};
|
||||
|
||||
if (currentStep !== "KNOWLEDGE" || brainType === undefined) {
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { Checkbox } from "@/lib/components/ui/Checkbox";
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
|
||||
import { useConnectableBrain } from "./hooks/useConnectableBrain";
|
||||
|
||||
type ConnectableBrainProps = {
|
||||
brain: MinimalBrainForUser;
|
||||
};
|
||||
|
||||
export const ConnectableBrain = ({
|
||||
brain,
|
||||
}: ConnectableBrainProps): JSX.Element => {
|
||||
const { onCheckedChange } = useConnectableBrain();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<Checkbox
|
||||
className="text-white"
|
||||
onCheckedChange={(checked) =>
|
||||
onCheckedChange({
|
||||
brainId: brain.id,
|
||||
checked,
|
||||
})
|
||||
}
|
||||
id={`connected_brains_ids-${brain.id}`}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`connected_brains_ids-${brain.id}`}
|
||||
className="text-md font-medium leading-none cursor-pointer"
|
||||
>
|
||||
{brain.name}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
import { CheckedState } from "@radix-ui/react-checkbox";
|
||||
import { UUID } from "crypto";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useConnectableBrain = () => {
|
||||
const { setValue, getValues } = useFormContext<CreateBrainProps>();
|
||||
|
||||
const onCheckedChange = ({
|
||||
checked,
|
||||
brainId,
|
||||
}: {
|
||||
checked: CheckedState;
|
||||
brainId: UUID;
|
||||
}) => {
|
||||
if (checked === "indeterminate") {
|
||||
return;
|
||||
}
|
||||
const connected_brains_ids = getValues("connected_brains_ids") ?? [];
|
||||
if (checked) {
|
||||
setValue("connected_brains_ids", [...connected_brains_ids, brainId]);
|
||||
} else {
|
||||
setValue(
|
||||
"connected_brains_ids",
|
||||
connected_brains_ids.filter((id) => id !== brainId)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
onCheckedChange,
|
||||
};
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { ConnectableBrain } from "./Components/ConnectableBrain/ConnectableBrain";
|
||||
|
||||
export const CompositeBrainConnections = (): JSX.Element => {
|
||||
const { allBrains } = useBrainContext();
|
||||
const sortedBrains = allBrains.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const { t } = useTranslation("brain");
|
||||
|
||||
return (
|
||||
<div className="px-10">
|
||||
<p className="text-center mb-8 italic text-sm w-full">
|
||||
{t("composite_brain_composition_invitation")}
|
||||
</p>
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
{sortedBrains.map((brain) => (
|
||||
<ConnectableBrain key={brain.id} brain={brain} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -49,6 +49,7 @@ export const useBrainCreationApi = ({
|
||||
brain_secrets_values,
|
||||
status,
|
||||
brain_type,
|
||||
connected_brains_ids,
|
||||
} = getValues();
|
||||
|
||||
const createdBrainId = await createBrainApi({
|
||||
@ -59,6 +60,8 @@ export const useBrainCreationApi = ({
|
||||
brain_definition: brain_type === "api" ? brain_definition : undefined,
|
||||
brain_secrets_values:
|
||||
brain_type === "api" ? brain_secrets_values : undefined,
|
||||
connected_brains_ids:
|
||||
brain_type === "composite" ? connected_brains_ids : undefined,
|
||||
});
|
||||
|
||||
if (createdBrainId === undefined) {
|
||||
@ -92,6 +95,12 @@ export const useBrainCreationApi = ({
|
||||
text: t("brainCreated", { ns: "brain" }),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: t("errorCreatingBrain", { ns: "brain" }),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -7,13 +7,15 @@ export const useBrainKnowledgeStep = () => {
|
||||
const { watch } = useFormContext<CreateBrainProps>();
|
||||
const brainType = watch("brain_type");
|
||||
const url = watch("brain_definition.url");
|
||||
const compositeBrainConnections = watch("connected_brains_ids") ?? [];
|
||||
const isApiBrain = brainType === "api";
|
||||
const isChatflowBrain = brainType === "chatflow";
|
||||
const isCompositeBrain = brainType === "composite";
|
||||
|
||||
const isApiBrainDefinitionsFilled = url !== "";
|
||||
|
||||
const isSubmitButtonDisabled =
|
||||
isChatflowBrain || (isApiBrain && !isApiBrainDefinitionsFilled);
|
||||
(isCompositeBrain && compositeBrainConnections.length === 0) ||
|
||||
(isApiBrain && !isApiBrainDefinitionsFilled);
|
||||
|
||||
return {
|
||||
brainType,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { BrainType } from "@/lib/types/brainConfig";
|
||||
@ -5,6 +6,7 @@ import { BrainType } from "@/lib/types/brainConfig";
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useKnowledgeSourceLabel = () => {
|
||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||
const isCompositeBrainActivated = useFeatureIsOn("agent-brain");
|
||||
|
||||
const knowledgeSourceOptions: {
|
||||
label: string;
|
||||
@ -18,12 +20,15 @@ export const useKnowledgeSourceLabel = () => {
|
||||
label: t("knowledge_source_api", { ns: "brain" }),
|
||||
value: "api",
|
||||
},
|
||||
{
|
||||
label: t("knowledge_source_chatflow", { ns: "brain" }),
|
||||
value: "chatflow",
|
||||
},
|
||||
];
|
||||
|
||||
if (isCompositeBrainActivated) {
|
||||
knowledgeSourceOptions.push({
|
||||
label: t("knowledge_source_composite_brain", { ns: "brain" }),
|
||||
value: "composite",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
knowledgeSourceOptions,
|
||||
};
|
||||
|
26
frontend/lib/components/ui/Checkbox.tsx
Normal file
26
frontend/lib/components/ui/Checkbox.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||
import * as React from "react";
|
||||
import { LuCheck } from "react-icons/lu";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<LuCheck className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
@ -23,6 +23,7 @@ export const addBrainDefaultValues: CreateBrainInput = {
|
||||
},
|
||||
secrets: [],
|
||||
},
|
||||
connected_brains_ids: [],
|
||||
};
|
||||
|
||||
export const defaultModel: Model = "gpt-3.5-turbo";
|
||||
|
@ -6,7 +6,7 @@ export const brainStatuses = ["private", "public"] as const;
|
||||
|
||||
export type BrainStatus = (typeof brainStatuses)[number];
|
||||
|
||||
export const brainTypes = ["doc", "api", "chatflow"] as const;
|
||||
export const brainTypes = ["doc", "api", "composite"] as const;
|
||||
|
||||
export type BrainType = (typeof brainTypes)[number];
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
"@growthbook/growthbook-react": "^0.17.0",
|
||||
"@june-so/analytics-next": "^2.0.0",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.3",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-radio-group": "^1.1.3",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"shareBrainUsers": "Users with access",
|
||||
"shareBrainLink": "Click to copy link to share your brain",
|
||||
"copiedToClipboard": "Copied to clipboard",
|
||||
"composite_brain_composition_invitation": "Connect your new brain to other existing brains from your library by selecting them.",
|
||||
"inviteUsers": "Add new users",
|
||||
"usersInvited": "Users successfully invited",
|
||||
"errorSendingInvitation": "An error occurred while sending invitations",
|
||||
@ -50,7 +51,7 @@
|
||||
"knowledge_source_doc": "Documents",
|
||||
"knowledge_source_api": "App (Through API)",
|
||||
"knowledge_source_label": "Knowledge source",
|
||||
"knowledge_source_chatflow":"Agent",
|
||||
"knowledge_source_composite_brain":"Agent",
|
||||
"api_brain": {
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"brainNamePlaceholder": "Ejemplo: Anotaciones de historia",
|
||||
"brainUndefined": "Cerebro no definido",
|
||||
"copiedToClipboard": "Copiado al portapeles",
|
||||
"composite_brain_composition_invitation": "Conecta tu nuevo cerebro a otros cerebros existentes de tu biblioteca seleccionándolos.",
|
||||
"defaultBrain": "Cerebro por defecto",
|
||||
"errorCreatingBrain": "Error al crear cerebro",
|
||||
"errorFetchingBrainUsers": "Error al obtener usuarios del cerebro",
|
||||
@ -49,7 +50,7 @@
|
||||
"myBrains": "Mis cerebros",
|
||||
"knowledge_source_doc": "Documentos",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow": "Agente",
|
||||
"knowledge_source_composite_brain": "Agente",
|
||||
"knowledge_source_label": "Fuente de conocimiento",
|
||||
"api_brain":{
|
||||
"name": "Nombre",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"brainNamePlaceholder": "Ex. Notes d'histoire",
|
||||
"brainUndefined": "Cerveau non défini",
|
||||
"copiedToClipboard": "Copié dans le presse-papier",
|
||||
"composite_brain_composition_invitation":"Connectez votre nouveau cerveau à d'autres cerveaux existants de votre librairie en les sélectionnant.",
|
||||
"defaultBrain": "Cerveau par défaut",
|
||||
"errorCreatingBrain": "Une erreur s'est produite lors de la création d'un cerveau",
|
||||
"errorFetchingBrainUsers": "Une erreur s'est produite lors de la récupération des utilisateurs du cerveau",
|
||||
@ -49,7 +50,7 @@
|
||||
"myBrains": "Mes cerveaux",
|
||||
"knowledge_source_doc": "Documents",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow": "Agent",
|
||||
"knowledge_source_composite_brain": "Agent",
|
||||
"knowledge_source_label": "Source de connaissance",
|
||||
"api_brain": {
|
||||
"name": "Nom",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"shareBrainUsers": "Usuários com acesso",
|
||||
"shareBrainLink": "Clique para copiar o link de compartilhamento",
|
||||
"copiedToClipboard": "Copiado para a área de transferência",
|
||||
"composite_brain_composition_invitation": "Conecte seu novo cérebro a outros cérebros existentes em sua biblioteca selecionando-os.",
|
||||
"inviteUsers": "Adicionar novos usuários",
|
||||
"usersInvited": "Usuários convidados com sucesso",
|
||||
"errorSendingInvitation": "Um erro ocorreu ao enviar o(s) convite(s)",
|
||||
@ -49,7 +50,7 @@
|
||||
"myBrains": "Meus cérebros",
|
||||
"knowledge_source_doc": "Documentos",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow": "Agente",
|
||||
"knowledge_source_composite_brain": "Agente",
|
||||
"knowledge_source_label": "Fonte de conhecimento",
|
||||
"api_brain":{
|
||||
"name": "Name",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"shareBrainUsers": "Пользователи с доступом",
|
||||
"shareBrainLink": "Нажмите, чтобы скопировать ссылку для совместного использования мозга",
|
||||
"copiedToClipboard": "Скопировано в буфер обмена",
|
||||
"composite_brain_composition_invitation": "Подключите свой новый мозг к другим существующим мозгам из вашей библиотеки, выбрав их.",
|
||||
"inviteUsers": "Добавить новых пользователей",
|
||||
"usersInvited": "Пользователи успешно приглашены",
|
||||
"errorSendingInvitation": "Ошибка при отправке приглашений",
|
||||
@ -49,7 +50,7 @@
|
||||
"myBrains": "Мои мозги",
|
||||
"knowledge_source_doc": "Документы",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow": "Агент",
|
||||
"knowledge_source_composite_brain": "Агент",
|
||||
"knowledge_source_label": "Источник знаний",
|
||||
"api_brain":{
|
||||
"name": "Название",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"shareBrainUsers": "具有访问权限的用户",
|
||||
"shareBrainLink": "点击复制链接来分享大脑",
|
||||
"copiedToClipboard": "已复制到剪贴板",
|
||||
"composite_brain_composition_invitation": "通过选择将您的新大脑连接到现有的其他大脑来进行组合。",
|
||||
"inviteUsers": "添加新用户",
|
||||
"usersInvited": "已成功邀请用户",
|
||||
"errorSendingInvitation": "发送邀请时发生错误",
|
||||
@ -49,7 +50,7 @@
|
||||
"myBrains": "我的大脑",
|
||||
"knowledge_source_doc": "文档",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow": "代理",
|
||||
"knowledge_source_composite_brain": "代理",
|
||||
"knowledge_source_label": "知识来源",
|
||||
"api_brain":{
|
||||
"name": "名称",
|
||||
|
@ -816,6 +816,21 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
|
||||
"@radix-ui/react-checkbox@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
|
||||
integrity sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-presence" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
"@radix-ui/react-use-previous" "1.0.1"
|
||||
"@radix-ui/react-use-size" "1.0.1"
|
||||
|
||||
"@radix-ui/react-collapsible@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user