Skip to content

Commit

Permalink
feat: add public brain creation (#1218)
Browse files Browse the repository at this point in the history
* feat: add brain status input

* feat: update translations

* feat: add confirmation modal for brain status changing to public
  • Loading branch information
mamadoudicko authored Sep 20, 2023
1 parent 9d73531 commit 37935c5
Show file tree
Hide file tree
Showing 17 changed files with 418 additions and 187 deletions.
3 changes: 2 additions & 1 deletion frontend/lib/api/brain/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
import { BrainStatus } from "@/lib/types/brainConfig";

export type SubscriptionUpdatableProperties = {
role: BrainRoleType | null;
Expand All @@ -7,7 +8,7 @@ export type SubscriptionUpdatableProperties = {
export type CreateBrainInput = {
name: string;
description?: string;
status?: string;
status?: BrainStatus;
model?: string;
temperature?: number;
max_tokens?: number;
Expand Down
285 changes: 154 additions & 131 deletions frontend/lib/components/AddBrainModal/AddBrainModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable */

import { useTranslation } from "react-i18next";
import { MdAdd } from "react-icons/md";

Expand All @@ -9,9 +10,11 @@ import Field from "@/lib/components/ui/Field";
import { Modal } from "@/lib/components/ui/Modal";
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";

import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirmationModal";
import { useAddBrainModal } from "./hooks/useAddBrainModal";
import { Divider } from "../ui/Divider";
import { Radio } from "../ui/Radio";
import { TextArea } from "../ui/TextArea";
import { useAddBrainModal } from "./hooks/useAddBrainModal";

export const AddBrainModal = (): JSX.Element => {
const { t } = useTranslation(["translation", "brain", "config"]);
Expand All @@ -20,150 +23,170 @@ export const AddBrainModal = (): JSX.Element => {
isShareModalOpen,
setIsShareModalOpen,
register,
openAiKey,
temperature,
maxTokens,
model,
isPending,
pickPublicPrompt,
accessibleModels,
brainStatusOptions,
status,
isPublicAccessConfirmationModalOpened,
onCancelPublicAccess,
onConfirmPublicAccess,
} = useAddBrainModal();

return (
<Modal
Trigger={
<Button
onClick={() => void 0}
variant={"tertiary"}
className="border-0"
data-testid="add-brain-button"
>
{t("newBrain", { ns: "brain" })}
<MdAdd className="text-xl" />
</Button>
}
title={t("newBrainTitle", { ns: "brain" })}
desc={t("newBrainSubtitle", { ns: "brain" })}
isOpen={isShareModalOpen}
setOpen={setIsShareModalOpen}
CloseTrigger={<div />}
>
<form
onSubmit={(e) => {
e.preventDefault();
void handleSubmit();
}}
className="my-10 flex flex-col items-center gap-2"
>
<Field
label={t("brainName", { ns: "brain" })}
autoFocus
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
autoComplete="off"
className="flex-1"
required
data-testid="brain-name"
{...register("name")}
/>

<TextArea
label={t("brainDescription", { ns: "brain" })}
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
autoComplete="off"
className="flex-1 m-3"
{...register("description")}
/>

<Field
label={t("openAiKeyLabel", { ns: "config" })}
placeholder={t("openAiKeyPlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("openAiKey")}
/>

<fieldset className="w-full flex flex-col">
<label className="flex-1 text-sm" htmlFor="model">
{t("modelLabel", { ns: "config" })}
</label>
<select
id="model"
{...register("model")}
className="px-5 py-2 dark:bg-gray-700 bg-gray-200 rounded-md"
<>
<Modal
Trigger={
<Button
onClick={() => void 0}
variant={"tertiary"}
className="border-0"
data-testid="add-brain-button"
>
{accessibleModels.map((availableModel) => (
<option value={availableModel} key={availableModel}>
{availableModel}
</option>
))}
</select>
</fieldset>
{t("newBrain", { ns: "brain" })}
<MdAdd className="text-xl" />
</Button>
}
title={t("newBrainTitle", { ns: "brain" })}
desc={t("newBrainSubtitle", { ns: "brain" })}
isOpen={isShareModalOpen}
setOpen={setIsShareModalOpen}
CloseTrigger={<div />}
>
<form
onSubmit={(e) => {
e.preventDefault();
void handleSubmit();
}}
className="my-10 flex flex-col items-center gap-2"
>
<Field
label={t("brainName", { ns: "brain" })}
autoFocus
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
autoComplete="off"
className="flex-1"
required
data-testid="brain-name"
{...register("name")}
/>

<fieldset className="w-full flex mt-4">
<label className="flex-1" htmlFor="temp">
{t("temperature", { ns: "config" })}: {temperature}
</label>
<input
id="temp"
type="range"
min="0"
max="1"
step="0.01"
value={temperature}
{...register("temperature")}
<TextArea
label={t("brainDescription", { ns: "brain" })}
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
autoComplete="off"
className="flex-1 m-3"
{...register("description")}
/>
</fieldset>
<fieldset className="w-full flex mt-4">
<label className="flex-1" htmlFor="tokens">
{t("maxTokens", { ns: "config" })}: {maxTokens}
</label>
<input
type="range"
min="10"
max={defineMaxTokens(model)}
value={maxTokens}
{...register("maxTokens")}
<fieldset className="w-full flex flex-col">
<Radio
items={brainStatusOptions}
label={t("brain_status_label", { ns: "brain" })}
value={status}
className="flex-1 justify-between w-[50%]"
{...register("status")}
/>
</fieldset>
<Field
label={t("openAiKeyLabel", { ns: "config" })}
placeholder={t("openAiKeyPlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("openAiKey")}
/>
</fieldset>
<Divider text={t("customPromptSection", { ns: "config" })} />
<PublicPrompts onSelect={pickPublicPrompt} />
<Field
label={t("promptName", { ns: "config" })}
placeholder={t("promptNamePlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("prompt.title")}
/>
<TextArea
label={t("promptContent", { ns: "config" })}
placeholder={t("promptContentPlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("prompt.content")}
/>
<div className="flex flex-row justify-start w-full mt-4">
<label className="flex items-center">
<span className="mr-2 text-gray-700">
{t("setDefaultBrain", { ns: "brain" })}
</span>

<fieldset className="w-full flex flex-col">
<label className="flex-1 text-sm" htmlFor="model">
{t("modelLabel", { ns: "config" })}
</label>
<select
id="model"
{...register("model")}
className="px-5 py-2 dark:bg-gray-700 bg-gray-200 rounded-md"
>
{accessibleModels.map((availableModel) => (
<option value={availableModel} key={availableModel}>
{availableModel}
</option>
))}
</select>
</fieldset>

<fieldset className="w-full flex mt-4">
<label className="flex-1" htmlFor="temp">
{t("temperature", { ns: "config" })}: {temperature}
</label>
<input
id="temp"
type="range"
min="0"
max="1"
step="0.01"
value={temperature}
{...register("temperature")}
/>
</fieldset>
<fieldset className="w-full flex mt-4">
<label className="flex-1" htmlFor="tokens">
{t("maxTokens", { ns: "config" })}: {maxTokens}
</label>
<input
type="checkbox"
{...register("setDefault")}
className="form-checkbox h-5 w-5 text-indigo-600 rounded focus:ring-2 focus:ring-indigo-400"
type="range"
min="10"
max={defineMaxTokens(model)}
value={maxTokens}
{...register("maxTokens")}
/>
</label>
</div>
</fieldset>
<Divider text={t("customPromptSection", { ns: "config" })} />
<PublicPrompts onSelect={pickPublicPrompt} />
<Field
label={t("promptName", { ns: "config" })}
placeholder={t("promptNamePlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("prompt.title")}
/>
<TextArea
label={t("promptContent", { ns: "config" })}
placeholder={t("promptContentPlaceholder", { ns: "config" })}
autoComplete="off"
className="flex-1"
{...register("prompt.content")}
/>
<div className="flex flex-row justify-start w-full mt-4">
<label className="flex items-center">
<span className="mr-2 text-gray-700">
{t("setDefaultBrain", { ns: "brain" })}
</span>
<input
type="checkbox"
{...register("setDefault")}
className="form-checkbox h-5 w-5 text-indigo-600 rounded focus:ring-2 focus:ring-indigo-400"
/>
</label>
</div>

<Button
isLoading={isPending}
className="mt-12 self-end"
type="submit"
data-testid="create-brain-submit-button"
>
{t("createButton")}
<MdAdd className="text-xl" />
</Button>
</form>
</Modal>
<Button
isLoading={isPending}
className="mt-12 self-end"
type="submit"
data-testid="create-brain-submit-button"
>
{t("createButton")}
<MdAdd className="text-xl" />
</Button>
</form>
</Modal>
<PublicAccessConfirmationModal
opened={isPublicAccessConfirmationModalOpened}
onClose={onCancelPublicAccess}
onCancel={onCancelPublicAccess}
onConfirm={onConfirmPublicAccess}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useTranslation } from "react-i18next";

import Button from "../../ui/Button";
import { Modal } from "../../ui/Modal";

type PublicAccessConfirmationModalProps = {
opened: boolean;
onClose: () => void;
onConfirm: () => void;
onCancel: () => void;
};
export const PublicAccessConfirmationModal = ({
opened,
onClose,
onConfirm,
onCancel,
}: PublicAccessConfirmationModalProps): JSX.Element => {
const { t } = useTranslation(["brain"]);

return (
<Modal isOpen={opened} setOpen={onClose} CloseTrigger={<div />}>
<div
dangerouslySetInnerHTML={{
__html: t("set_brain_status_to_public_modal_title"),
}}
/>
<div
dangerouslySetInnerHTML={{
__html: t("set_brain_status_to_public_modal_description"),
}}
/>
<div className="flex flex-row justify-between pt-10 px-10">
<Button type="button" onClick={onConfirm} variant="secondary">
{t("confirm_set_brain_status_to_public")}
</Button>
<Button type="button" onClick={onCancel}>
{t("cancel_set_brain_status_to_public")}
</Button>
</div>
</Modal>
);
};
Loading

0 comments on commit 37935c5

Please sign in to comment.