mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-24 05:55:13 +03:00
feat: add brain creation steps system (#1814)
Issue: https://github.com/StanGirard/quivr/issues/1769 - Turn brain creation into steps - Add first step content - Add feature flag Demo: https://github.com/StanGirard/quivr/assets/63923024/1ce33a3a-86ea-4051-ba02-f95b7478411e
This commit is contained in:
parent
5f23bcfcb8
commit
77410f04b8
@ -2,7 +2,7 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { AddBrainModal } from "@/lib/components/AddBrainModal/AddBrainModal";
|
||||
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
||||
import { Sidebar } from "@/lib/components/Sidebar/Sidebar";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
|
||||
|
@ -2,8 +2,8 @@ import { FormProvider, useForm } from "react-hook-form";
|
||||
|
||||
import { addBrainDefaultValues } from "@/lib/config/defaultBrainConfig";
|
||||
|
||||
import { AddBrainConfig } from "./components/AddBrainConfig/AddBrainConfig";
|
||||
import { CreateBrainProps } from "./components/AddBrainConfig/types";
|
||||
import { AddBrainSteps } from "./components/AddBrainSteps/AddBrainSteps";
|
||||
import { CreateBrainProps } from "./types";
|
||||
|
||||
type AddBrainModalProps = {
|
||||
triggerClassName?: string;
|
||||
@ -14,11 +14,8 @@ export const AddBrainModal = ({
|
||||
}: AddBrainModalProps): JSX.Element => {
|
||||
const defaultValues: CreateBrainProps = {
|
||||
...addBrainDefaultValues,
|
||||
prompt: {
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
setDefault: true,
|
||||
brainCreationStep: "BRAIN_TYPE",
|
||||
};
|
||||
|
||||
const methods = useForm<CreateBrainProps>({
|
||||
@ -27,7 +24,7 @@ export const AddBrainModal = ({
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<AddBrainConfig triggerClassName={triggerClassName} />
|
||||
<AddBrainSteps triggerClassName={triggerClassName} />
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,20 @@
|
||||
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
||||
|
||||
import { AddBrainModal as AddBrainModalNew } from "./AddBrainModal";
|
||||
import { AddBrainModal as AddBrainModalOld } from "../AddBrainModalOld";
|
||||
|
||||
type AddBrainModalSelectorProps = {
|
||||
triggerClassName?: string;
|
||||
};
|
||||
|
||||
export const AddBrainModalSelector = ({
|
||||
triggerClassName,
|
||||
}: AddBrainModalSelectorProps): JSX.Element => {
|
||||
const isNewBrainCreationOn = useFeatureIsOn("new-brain-creation");
|
||||
|
||||
if (isNewBrainCreationOn) {
|
||||
return <AddBrainModalNew triggerClassName={triggerClassName} />;
|
||||
}
|
||||
|
||||
return <AddBrainModalOld triggerClassName={triggerClassName} />;
|
||||
};
|
@ -0,0 +1,58 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdAdd } from "react-icons/md";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Modal } from "@/lib/components/ui/Modal";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/BrainTypeSelectionStep";
|
||||
import { Stepper } from "./components/Stepper/Stepper";
|
||||
import { useAddBrainConfig } from "./hooks/useAddBrainConfig";
|
||||
|
||||
type AddBrainConfigProps = {
|
||||
triggerClassName?: string;
|
||||
};
|
||||
|
||||
export const AddBrainSteps = ({
|
||||
triggerClassName,
|
||||
}: AddBrainConfigProps): JSX.Element => {
|
||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||
|
||||
const { isBrainCreationModalOpened, setIsBrainCreationModalOpened } =
|
||||
useAddBrainConfig();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
Trigger={
|
||||
<Button
|
||||
onClick={() => void 0}
|
||||
variant={"tertiary"}
|
||||
className={cn("border-0", triggerClassName)}
|
||||
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={isBrainCreationModalOpened}
|
||||
setOpen={setIsBrainCreationModalOpened}
|
||||
CloseTrigger={<div />}
|
||||
>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
className="my-10 flex flex-col items-center gap-2 justify-center"
|
||||
>
|
||||
<Stepper />
|
||||
<BrainTypeSelectionStep
|
||||
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
||||
/>
|
||||
</form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
import { Fragment } from "react";
|
||||
import { FaArrowRight } from "react-icons/fa";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Radio } from "@/lib/components/ui/Radio";
|
||||
|
||||
import { useBrainTypeSelectionStep } from "./hooks/useBrainTypeSelectionStep";
|
||||
import { useKnowledgeSourceLabel } from "./hooks/useKnowledgeSourceLabel";
|
||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||
|
||||
type BrainTypeSelectionStepProps = {
|
||||
onCancelBrainCreation: () => void;
|
||||
};
|
||||
|
||||
export const BrainTypeSelectionStep = ({
|
||||
onCancelBrainCreation,
|
||||
}: BrainTypeSelectionStepProps): JSX.Element => {
|
||||
const { knowledgeSourceOptions } = useKnowledgeSourceLabel();
|
||||
const { register } = useBrainTypeSelectionStep();
|
||||
const { goToNextStep, currentStep } = useBrainCreationSteps();
|
||||
|
||||
if (currentStep !== "BRAIN_TYPE") {
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Radio
|
||||
items={knowledgeSourceOptions}
|
||||
className="flex-1 justify-between"
|
||||
{...register("brain_type")}
|
||||
/>
|
||||
<div className="flex flex-row flex-1 justify-center w-full gap-48 mt-10">
|
||||
<Button
|
||||
type="button"
|
||||
variant="tertiary"
|
||||
onClick={onCancelBrainCreation}
|
||||
>
|
||||
Annuler
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="bg-primary text-white py-2 border-none"
|
||||
type="button"
|
||||
data-testid="create-brain-submit-button"
|
||||
onClick={goToNextStep}
|
||||
>
|
||||
Suivant
|
||||
<FaArrowRight className="text-xl" size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
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 useBrainTypeSelectionStep = () => {
|
||||
const { register } = useFormContext<CreateBrainProps>();
|
||||
|
||||
return {
|
||||
register,
|
||||
};
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
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 knowledgeSourceOptions: {
|
||||
label: string;
|
||||
value: BrainType;
|
||||
}[] = [
|
||||
{
|
||||
label: t("knowledge_source_doc", { ns: "brain" }),
|
||||
value: "doc",
|
||||
},
|
||||
{
|
||||
label: t("knowledge_source_api", { ns: "brain" }),
|
||||
value: "api",
|
||||
},
|
||||
{
|
||||
label: t("knowledge_source_chatflow", { ns: "brain" }),
|
||||
value: "chatflow",
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
knowledgeSourceOptions,
|
||||
};
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
import { Fragment } from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||
|
||||
export const Stepper = (): JSX.Element => {
|
||||
const { currentStep, steps } = useBrainCreationSteps();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row justify-between w-full px-12 mb-12">
|
||||
{steps.map((step, index) => (
|
||||
<Fragment key={step.value}>
|
||||
<div
|
||||
key={step.label}
|
||||
className="flex flex-row justify-center items-center flex-1"
|
||||
>
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<div
|
||||
className={cn(
|
||||
"h-[40px] w-[40px] border-solid rounded-full flex flex-row items-center justify-center mb-2 border-primary border-2",
|
||||
step.value === currentStep ? "bg-primary text-white" : ""
|
||||
)}
|
||||
>
|
||||
{index + 1}
|
||||
</div>
|
||||
<span key={step.label} className="text-xs text-center">
|
||||
{step.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{index < steps.length - 1 && ( // Add horizontal line for all but the last step
|
||||
<hr
|
||||
className={cn(
|
||||
"flex-grow border-t-2 border-primary m-4",
|
||||
step.value === currentStep
|
||||
? "border-primary"
|
||||
: "border-gray-300"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
import { useState } from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useAddBrainConfig = () => {
|
||||
const [isBrainCreationModalOpened, setIsBrainCreationModalOpened] =
|
||||
useState(false);
|
||||
|
||||
return {
|
||||
isBrainCreationModalOpened,
|
||||
setIsBrainCreationModalOpened,
|
||||
};
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
||||
|
||||
import { StepperStep } from "../types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useBrainCreationSteps = () => {
|
||||
const { t } = useTranslation("brain");
|
||||
|
||||
const steps: StepperStep[] = [
|
||||
{
|
||||
label: t("brain_type"),
|
||||
value: "BRAIN_TYPE",
|
||||
},
|
||||
{
|
||||
label: t("brain_params"),
|
||||
value: "BRAIN_PARAMS",
|
||||
},
|
||||
{
|
||||
label: t("brain_data"),
|
||||
value: "KNOWLEDGE",
|
||||
},
|
||||
];
|
||||
const { watch, setValue } = useFormContext<CreateBrainProps>();
|
||||
const currentStep = watch("brainCreationStep");
|
||||
|
||||
const goToNextStep = () => {
|
||||
const currentStepIndex = steps.findIndex(
|
||||
(step) => step.value === currentStep
|
||||
);
|
||||
if (currentStepIndex === -1 || currentStepIndex === steps.length - 1) {
|
||||
return;
|
||||
}
|
||||
const nextStep = steps[currentStepIndex + 1];
|
||||
|
||||
return setValue("brainCreationStep", nextStep.value);
|
||||
};
|
||||
|
||||
const goToPreviousStep = () => {
|
||||
const currentStepIndex = steps.findIndex(
|
||||
(step) => step.value === currentStep
|
||||
);
|
||||
if (currentStepIndex === -1 || currentStepIndex === 0) {
|
||||
return;
|
||||
}
|
||||
const previousStep = steps[currentStepIndex - 1];
|
||||
|
||||
return setValue("brainCreationStep", previousStep.value);
|
||||
};
|
||||
|
||||
return {
|
||||
currentStep,
|
||||
steps,
|
||||
goToNextStep,
|
||||
goToPreviousStep,
|
||||
};
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./AddBrainSteps";
|
@ -0,0 +1,6 @@
|
||||
import { BrainCreationStep } from "../../types";
|
||||
|
||||
export type StepperStep = {
|
||||
label: string;
|
||||
value: BrainCreationStep;
|
||||
};
|
@ -1 +1 @@
|
||||
export * from "./AddBrainConfig";
|
||||
export * from "./AddBrainSteps";
|
||||
|
@ -1 +1 @@
|
||||
export * from "./AddBrainModal";
|
||||
export { AddBrainModalSelector as AddBrainModal } from "./AddBrainModalSelector";
|
||||
|
10
frontend/lib/components/AddBrainModal/types.ts
Normal file
10
frontend/lib/components/AddBrainModal/types.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { CreateBrainInput } from "@/lib/api/brain/types";
|
||||
|
||||
const brainCreationSteps = ["BRAIN_TYPE", "BRAIN_PARAMS", "KNOWLEDGE"] as const;
|
||||
|
||||
export type BrainCreationStep = (typeof brainCreationSteps)[number];
|
||||
|
||||
export type CreateBrainProps = CreateBrainInput & {
|
||||
setDefault: boolean;
|
||||
brainCreationStep: BrainCreationStep;
|
||||
};
|
33
frontend/lib/components/AddBrainModalOld/AddBrainModal.tsx
Normal file
33
frontend/lib/components/AddBrainModalOld/AddBrainModal.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
|
||||
import { addBrainDefaultValues } from "@/lib/config/defaultBrainConfig";
|
||||
|
||||
import { AddBrainConfig } from "./components/AddBrainConfig/AddBrainConfig";
|
||||
import { CreateBrainProps } from "./components/AddBrainConfig/types";
|
||||
|
||||
type AddBrainModalProps = {
|
||||
triggerClassName?: string;
|
||||
};
|
||||
|
||||
export const AddBrainModal = ({
|
||||
triggerClassName,
|
||||
}: AddBrainModalProps): JSX.Element => {
|
||||
const defaultValues: CreateBrainProps = {
|
||||
...addBrainDefaultValues,
|
||||
prompt: {
|
||||
title: "",
|
||||
content: "",
|
||||
},
|
||||
setDefault: true,
|
||||
};
|
||||
|
||||
const methods = useForm<CreateBrainProps>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<AddBrainConfig triggerClassName={triggerClassName} />
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
@ -111,7 +111,6 @@ export const AddBrainConfig = ({
|
||||
<ApiRequestDefinition />
|
||||
</>
|
||||
|
||||
|
||||
<fieldset className="w-full flex flex-col">
|
||||
<label className="flex-1 text-sm" htmlFor="model">
|
||||
{t("modelLabel", { ns: "config" })}
|
||||
@ -129,7 +128,6 @@ export const AddBrainConfig = ({
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset className="w-full flex mt-4">
|
||||
<label className="flex-1" htmlFor="tokens">
|
||||
{t("maxTokens", { ns: "config" })}: {maxTokens}
|
@ -0,0 +1 @@
|
||||
export * from "./AddBrainConfig";
|
1
frontend/lib/components/AddBrainModalOld/index.ts
Normal file
1
frontend/lib/components/AddBrainModalOld/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./AddBrainModal";
|
@ -1,6 +1,6 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { CreateBrainProps } from "../../../../AddBrainModal/components/AddBrainConfig/types";
|
||||
import { CreateBrainProps } from "../../../../AddBrainModalOld/components/AddBrainConfig/types";
|
||||
import { defaultParamDefinitionRow } from "../config";
|
||||
import { mapApiBrainDefinitionSchemaToParameterDefinition } from "../utils/mapApiBrainDefinitionSchemaToParameterDefinition";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { CreateBrainProps } from "../../../../AddBrainModal/components/AddBrainConfig/types";
|
||||
import { CreateBrainProps } from "../../../../AddBrainModalOld/components/AddBrainConfig/types";
|
||||
import {
|
||||
brainSecretsValueKeyInForm,
|
||||
defaultSecretDefinitionRow,
|
||||
|
@ -6,7 +6,7 @@ export const brainStatuses = ["private", "public"] as const;
|
||||
|
||||
export type BrainStatus = (typeof brainStatuses)[number];
|
||||
|
||||
export const brainTypes = ["doc", "api"] as const;
|
||||
export const brainTypes = ["doc", "api", "chatflow"] as const;
|
||||
|
||||
export type BrainType = (typeof brainTypes)[number];
|
||||
|
||||
@ -37,7 +37,11 @@ export type BrainConfig = {
|
||||
brain_definition?: ApiBrainDefinition;
|
||||
};
|
||||
|
||||
export const openAiFreeModels = ["gpt-3.5-turbo","gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k"] as const;
|
||||
export const openAiFreeModels = [
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-1106",
|
||||
"gpt-3.5-turbo-16k",
|
||||
] as const;
|
||||
|
||||
export const openAiPaidModels = [...openAiFreeModels, "gpt-4"] as const;
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
"knowledge_source_doc": "Documents",
|
||||
"knowledge_source_api": "App (Through API)",
|
||||
"knowledge_source_label": "Knowledge source",
|
||||
"knowledge_source_chatflow":"Brain network",
|
||||
"api_brain": {
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button":"Update secrets",
|
||||
"secrets_updated":"Secrets updated",
|
||||
"secrets_update_error":"Error while updating secrets",
|
||||
"manage_brain": "Manage brain"
|
||||
"manage_brain": "Manage brain",
|
||||
"brain_type":"Type of brain",
|
||||
"brain_params":"Brain params",
|
||||
"brain_data":"Brain data"
|
||||
}
|
@ -49,6 +49,7 @@
|
||||
"myBrains": "Mis cerebros",
|
||||
"knowledge_source_doc": "Documentos",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow":"Red de cerebro",
|
||||
"knowledge_source_label": "Fuente de conocimiento",
|
||||
"api_brain":{
|
||||
"name": "Nombre",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button": "Actualizar secretos",
|
||||
"secrets_updated": "Secretos actualizados",
|
||||
"secrets_update_error": "Error al actualizar secretos",
|
||||
"manage_brain": "Gestionar cerebro"
|
||||
"manage_brain": "Gestionar cerebro",
|
||||
"brain_type": "Tipo de cerebro",
|
||||
"brain_params": "Parámetros del cerebro",
|
||||
"brain_data": "Datos del cerebro"
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
"myBrains": "Mes cerveaux",
|
||||
"knowledge_source_doc": "Documents",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow":"Réseau cérébral",
|
||||
"knowledge_source_label": "Source de connaissance",
|
||||
"api_brain": {
|
||||
"name": "Nom",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button": "Mettre à jour les secrets",
|
||||
"secrets_updated": "Secrets mis à jour",
|
||||
"secrets_update_error": "Erreur lors de la mise à jour des secrets",
|
||||
"manage_brain": "Gérer le cerveau"
|
||||
"manage_brain": "Gérer le cerveau",
|
||||
"brain_type": "Type de cerveau",
|
||||
"brain_params": "Paramètres du cerveau",
|
||||
"brain_data": "Données du cerveau"
|
||||
}
|
@ -49,6 +49,7 @@
|
||||
"myBrains": "Meus cérebros",
|
||||
"knowledge_source_doc": "Documentos",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow":"Rede de cérebro",
|
||||
"knowledge_source_label": "Fonte de conhecimento",
|
||||
"api_brain":{
|
||||
"name": "Name",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button": "Atualizar segredos",
|
||||
"secrets_updated": "Segredos atualizados",
|
||||
"secrets_update_error": "Erro ao atualizar segredos",
|
||||
"manage_brain": "Gerenciar cérebro"
|
||||
"manage_brain": "Gerenciar cérebro",
|
||||
"brain_type": "Tipo de cérebro",
|
||||
"brain_params": "Parâmetros do cérebro",
|
||||
"brain_data": "Dados do cérebro"
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
"myBrains": "Мои мозги",
|
||||
"knowledge_source_doc": "Документы",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow":"Сеть мозга",
|
||||
"knowledge_source_label": "Источник знаний",
|
||||
"api_brain":{
|
||||
"name": "Название",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button": "Обновить секреты",
|
||||
"secrets_updated": "Секреты обновлены",
|
||||
"secrets_update_error": "Ошибка при обновлении секретов",
|
||||
"manage_brain": "Управление мозгом"
|
||||
"manage_brain": "Управление мозгом",
|
||||
"brain_type": "Тип мозга",
|
||||
"brain_params": "Параметры мозга",
|
||||
"brain_data": "Данные мозга"
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
"myBrains": "我的大脑",
|
||||
"knowledge_source_doc": "文档",
|
||||
"knowledge_source_api": "API",
|
||||
"knowledge_source_chatflow":"脑网络",
|
||||
"knowledge_source_label": "知识来源",
|
||||
"api_brain":{
|
||||
"name": "名称",
|
||||
@ -62,5 +63,8 @@
|
||||
"update_secrets_button": "更新秘密",
|
||||
"secrets_updated": "秘密已更新",
|
||||
"secrets_update_error": "更新秘密时出错",
|
||||
"manage_brain": "管理大脑"
|
||||
"manage_brain": "管理大脑",
|
||||
"brain_type": "脑类型",
|
||||
"brain_params": "脑参数",
|
||||
"brain_data": "脑数据"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user