Mamadou DICKO 2023-11-15 12:42:19 +01:00 committed by GitHub
parent e71e79cf3e
commit 223af1d3f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 881 additions and 101 deletions

View File

@ -23,7 +23,7 @@ class ApiBrainDefinitionSchema(BaseModel, extra=Extra.forbid):
required: list[str] = [] required: list[str] = []
class ApiBrainDefinitionSecret(BaseModel): class ApiBrainDefinitionSecret(BaseModel, extra=Extra.forbid):
name: str name: str
type: str type: str

View File

@ -1,20 +1,51 @@
import { BrainRoleType } from "@/lib/components/BrainUsers/types"; import { BrainRoleType } from "@/lib/components/BrainUsers/types";
import { BrainStatus, BrainType } from "@/lib/types/brainConfig"; import { BrainStatus, BrainType, Model } from "@/lib/types/brainConfig";
export type ApiBrainDefinitionSchemaPropertyType = "string" | "number";
export type ApiBrainDefinitionSchemaProperty = {
type: ApiBrainDefinitionSchemaPropertyType;
description: string;
name: string;
};
export const allowedRequestMethods = ["GET", "POST", "PUT", "DELETE"];
export type AllowedRequestMethod = (typeof allowedRequestMethods)[number];
export type ApiBrainDefinitionSchema = {
properties: ApiBrainDefinitionSchemaProperty[];
required: string[];
};
export type SubscriptionUpdatableProperties = { export type SubscriptionUpdatableProperties = {
role: BrainRoleType | null; role: BrainRoleType | null;
}; };
export type ApiBrainDefinitionSecret = {
name: string;
type: ApiBrainDefinitionSchemaPropertyType;
};
export type ApiBrainDefinition = {
method: AllowedRequestMethod;
url: string;
search_params: ApiBrainDefinitionSchema;
params: ApiBrainDefinitionSchema;
secrets?: ApiBrainDefinitionSecret[];
};
export type CreateBrainInput = { export type CreateBrainInput = {
name: string; name: string;
description?: string; description?: string;
status?: BrainStatus; status?: BrainStatus;
model?: string; model?: Model;
temperature?: number; temperature?: number;
max_tokens?: number; max_tokens?: number;
openai_api_key?: string; openai_api_key?: string;
prompt_id?: string | null; prompt_id?: string | null;
brain_type?: BrainType; brain_type?: BrainType;
brain_definition?: ApiBrainDefinition;
brain_secrets_values?: Record<string, string>;
}; };
export type UpdateBrainInput = Partial<CreateBrainInput>; export type UpdateBrainInput = Partial<CreateBrainInput>;

View File

@ -1,9 +1,9 @@
import { FormProvider, useForm } from "react-hook-form"; import { FormProvider, useForm } from "react-hook-form";
import { defaultBrainConfig } from "@/lib/config/defaultBrainConfig"; import { addBrainDefaultValues } from "@/lib/config/defaultBrainConfig";
import { BrainConfig } from "@/lib/types/brainConfig";
import { AddBrainConfig } from "./components/AddBrainConfig/AddBrainConfig"; import { AddBrainConfig } from "./components/AddBrainConfig/AddBrainConfig";
import { CreateBrainProps } from "./components/AddBrainConfig/types";
type AddBrainModalProps = { type AddBrainModalProps = {
triggerClassName?: string; triggerClassName?: string;
@ -12,8 +12,17 @@ type AddBrainModalProps = {
export const AddBrainModal = ({ export const AddBrainModal = ({
triggerClassName, triggerClassName,
}: AddBrainModalProps): JSX.Element => { }: AddBrainModalProps): JSX.Element => {
const methods = useForm<BrainConfig>({ const defaultValues: CreateBrainProps = {
defaultValues: defaultBrainConfig, ...addBrainDefaultValues,
prompt: {
title: "",
content: "",
},
setDefault: true,
};
const methods = useForm<CreateBrainProps>({
defaultValues,
}); });
return ( return (

View File

@ -11,6 +11,7 @@ import { Modal } from "@/lib/components/ui/Modal";
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens"; import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ApiRequestDefinition } from "./components/ApiRequestDefinition";
import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirmationModal"; import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirmationModal";
import { useAddBrainConfig } from "./hooks/useAddBrainConfig"; import { useAddBrainConfig } from "./hooks/useAddBrainConfig";
import { useAddBrainConfigLabels } from "./hooks/useAddBrainConfigLabels"; import { useAddBrainConfigLabels } from "./hooks/useAddBrainConfigLabels";
@ -105,22 +106,25 @@ export const AddBrainConfig = ({
/> />
</fieldset> </fieldset>
{brainApiIsOn && ( {brainApiIsOn && (
<>
<fieldset className="w-full flex flex-col"> <fieldset className="w-full flex flex-col">
<Radio <Radio
items={knowledgeSourceOptions} items={knowledgeSourceOptions}
label={t("knowledge_source_label", { ns: "brain" })} label={t("knowledge_source_label", { ns: "brain" })}
value={brainType} value={brainType}
className="flex-1 justify-between w-[50%]" className="flex-1 justify-between w-[50%]"
{...register("brainType")} {...register("brain_type")}
/> />
</fieldset> </fieldset>
<ApiRequestDefinition />
</>
)} )}
<Field <Field
label={t("openAiKeyLabel", { ns: "config" })} label={t("openAiKeyLabel", { ns: "config" })}
placeholder={t("openAiKeyPlaceholder", { ns: "config" })} placeholder={t("openAiKeyPlaceholder", { ns: "config" })}
autoComplete="off" autoComplete="off"
className="flex-1" className="flex-1"
{...register("openAiKey")} {...register("openai_api_key")}
/> />
<fieldset className="w-full flex flex-col"> <fieldset className="w-full flex flex-col">
@ -163,7 +167,7 @@ export const AddBrainConfig = ({
min="10" min="10"
max={defineMaxTokens(model)} max={defineMaxTokens(model)}
value={maxTokens} value={maxTokens}
{...register("maxTokens")} {...register("max_tokens")}
/> />
</fieldset> </fieldset>
<Divider text={t("customPromptSection", { ns: "config" })} /> <Divider text={t("customPromptSection", { ns: "config" })} />

View File

@ -1,53 +1,95 @@
import { Content, Root } from "@radix-ui/react-tabs"; import { Content, List, Root } from "@radix-ui/react-tabs";
import { Fragment } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { BrainType } from "@/lib/types/brainConfig"; import { allowedRequestMethods, CreateBrainInput } from "@/lib/api/brain/types";
import { useApiDefinitionTabs } from "./hooks/useApiDefinitionTabs"; import { BrainDefinitionTabTrigger } from "./components/BrainDefinitionTabTrigger";
import { HeadersDefinition } from "./tabs/HeadersDefinition"; import { ParamsDefinition } from "./components/ParamsDefinition/ParamsDefinition";
import { ParamsDefinition } from "./tabs/ParamsDefinition"; import { SecretsDefinition } from "./components/SecretsDefinition/SecretsDefinition";
import { SearchParamsDefinition } from "./tabs/SearchParamsDefinition"; import { useApiRequestDefinition } from "./hooks/useApiRequestDefinition";
export const ApiRequestDefinition = (brainType: {
brainType: BrainType;
}): JSX.Element => {
const { selectedTab, setSelectedTab } = useApiDefinitionTabs();
console.log(brainType);
setSelectedTab("searchParams"); export const ApiRequestDefinition = (): JSX.Element => {
const { selectedTab, setSelectedTab } = useApiRequestDefinition();
const { t } = useTranslation(["external_api_definition"]);
const { watch, register } = useFormContext<CreateBrainInput>();
const brainType = watch("brain_type");
if (brainType !== "api") {
return <Fragment />;
}
const allowedMethodsOptions = allowedRequestMethods.map((method) => ({
label: method,
value: method,
}));
return ( return (
<Root <>
className="flex flex-col w-full h-full overflow-scroll bg-white dark:bg-black p-4 md:p-10 max-w-5xl" <div className="flex gap-2 w-full">
value={selectedTab}
>
<div className="flex gap-2 p-4">
<select <select
className="block w-32 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" className="block w-32 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
defaultValue="GET" defaultValue="GET"
// {...register("api.method")} {...register("brain_definition.method")}
> >
<option value="GET">GET</option> {allowedMethodsOptions.map(({ label, value }) => (
<option value="POST">POST</option> <option key={value} value={value}>
<option value="PUT">PUT</option> {label}
<option value="DELETE">DELETE</option> </option>
))}
</select> </select>
<input <input
className="flex-1 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" className="flex-1 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
placeholder="https://api.example.com/resource" placeholder="https://api.example.com/resource"
// {...register("api.url", { required: true })} {...register("brain_definition.url", { required: true })}
/> />
</div> </div>
<Root
className="flex flex-col w-full h-full overflow-scroll bg-white dark:bg-black py-4 md:py-10 max-w-5xl"
value={selectedTab}
>
<List className="flex flex-col md:flex-row justify-between space-y-2 md:space-y-0">
<BrainDefinitionTabTrigger
value="params"
label={t("params")}
selected={selectedTab === "params"}
onChange={setSelectedTab}
/>
<BrainDefinitionTabTrigger
value="searchParams"
label={t("searchParams")}
selected={selectedTab === "searchParams"}
onChange={setSelectedTab}
/>
<BrainDefinitionTabTrigger
value="secrets"
label={t("secrets")}
selected={selectedTab === "secrets"}
onChange={setSelectedTab}
/>
</List>
<div className="flex-1 md:pt-0 pb-0"> <div className="flex-1 md:pt-0 pb-0">
<Content value="people"> <Content value="params">
<SearchParamsDefinition /> <ParamsDefinition
description={t("paramsTabDescription")}
name="brain_definition.params"
/>
</Content> </Content>
<Content value="settings"> <Content value="searchParams">
<HeadersDefinition /> <ParamsDefinition
description={t("searchParamsTabDescription")}
name="brain_definition.search_params"
/>
</Content> </Content>
<Content value="knowledge"> <Content value="secrets">
<ParamsDefinition /> <SecretsDefinition />
</Content> </Content>
</div> </div>
</Root> </Root>
</>
); );
}; };

View File

@ -0,0 +1,29 @@
import { Trigger } from "@radix-ui/react-tabs";
import { ApiTab } from "../types";
type BrainDefinitionTabTriggerProps = {
label: string;
value: ApiTab;
selected: boolean;
onChange: (value: ApiTab) => unknown;
};
export const BrainDefinitionTabTrigger = ({
label,
value,
selected,
onChange,
}: BrainDefinitionTabTriggerProps): JSX.Element => {
return (
<Trigger
className={`flex-1 pb-2 border-gray-500 text-md align-center mb-0 ${
selected ? "font-medium border-b-2" : ""
}`}
value={value}
onClick={() => onChange(value)}
>
{label}
</Trigger>
);
};

View File

@ -0,0 +1,72 @@
import { useFieldArray } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { MdAdd } from "react-icons/md";
import { TiInfoOutline } from "react-icons/ti";
import Button from "@/lib/components/ui/Button";
import { ParamDefinitionRow } from "./components/ParamDefinitionRow";
import { defaultParamDefinitionRow } from "./config";
import { useParamsDefinition } from "./hooks/useParamsDefinition";
import { tabDescriptionStyle } from "../../styles";
type ParamsDefinitionProps = {
name: string;
description: string;
};
const paramsNameStyle = "flex flex-1 justify-center";
export const ParamsDefinition = ({
name,
description,
}: ParamsDefinitionProps): JSX.Element => {
const { t } = useTranslation(["brain"]);
const { control, register } = useParamsDefinition({
name,
});
const { fields, append, remove } = useFieldArray({
control,
name,
});
return (
<div>
<div className={tabDescriptionStyle}>
<TiInfoOutline size={30} />
<p className="ml-5">{description}</p>
</div>
<div className="flex flex-1 font-medium">
<div className="flex flex-1">
<div className={paramsNameStyle}>{t("api_brain.name")}</div>
<div className={paramsNameStyle}>{t("api_brain.description")}</div>
<div className={paramsNameStyle}>{t("api_brain.type")}</div>
<div className={paramsNameStyle}>{t("api_brain.required")}</div>
</div>
</div>
{fields.map((field, index) => (
<ParamDefinitionRow
name={name}
key={field.id}
index={index}
remove={remove}
register={register}
/>
))}
<div className="flex justify-end mt-3">
<Button
type="button"
onClick={() => {
append(defaultParamDefinitionRow);
}}
className="p-2"
variant={"secondary"}
>
<MdAdd size={20} /> {t("api_brain.addRow")}
</Button>
</div>
</div>
);
};

View File

@ -0,0 +1,79 @@
import { UseFormRegister } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { MdCancel } from "react-icons/md";
import { paramsNameStyle } from "../../styles";
import { ParameterDefinition } from "../types";
type ParamControl = {
[name: string]: ParameterDefinition[];
};
type ParamDefinitionRowProps = {
register: UseFormRegister<ParamControl>;
index: number;
remove: (index: number) => void;
name: string;
};
export const ParamDefinitionRow = ({
index,
remove,
register,
name,
}: ParamDefinitionRowProps): JSX.Element => {
const { t } = useTranslation(["brain"]);
return (
<div className="flex flex-1 justify-between items-center py-4 border-b border-gray-300 relative gap-2">
<div className={paramsNameStyle}>
<input
type="text"
className="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block shadow-sm sm:text-sm border-gray-300 dark:bg-gray-800 dark:text-gray-100 rounded-md w-full outline-none"
placeholder={t("api_brain.name")}
{...register(
`${name}.properties[${index}].name` as `${typeof name}.${number}.name`
)}
/>
</div>
<div className="flex-1">
<input
type="text"
id={`description-${index}`}
className="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 dark:bg-gray-800 dark:text-gray-100 rounded-md outline-none"
placeholder={t("api_brain.description")}
{...register(
`${name}.properties[${index}].description` as `${typeof name}.${number}.description`
)}
/>
</div>
<div className="flex-1">
<select
id={`type-${index}`}
className="mt-1 block w-full py-2 px-3 border border-gray-300 dark:bg-gray-800 dark:text-gray-100 bg-white dark:border-gray-800 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
{...register(
`${name}.properties[${index}].type` as `${typeof name}.${number}.type`
)}
>
<option value="string">string</option>
<option value="number">number</option>
</select>
</div>
<div className="flex-1 justify-center flex">
<input
type="checkbox"
className="form-checkbox h-5 w-5 text-indigo-600 rounded focus:ring-2 focus:ring-indigo-400 outline-none"
{...register(`${name}.required[${index}]`)}
/>
</div>
<button
type="button"
onClick={() => remove(index)}
className="absolute right-0 text-red-500 bg-transparent border-none cursor-pointer"
>
<MdCancel />
</button>
</div>
);
};

View File

@ -0,0 +1,8 @@
import { ParameterDefinition } from "./types";
export const defaultParamDefinitionRow: ParameterDefinition = {
name: "",
type: "string",
required: false,
description: "",
};

View File

@ -0,0 +1,55 @@
import { useEffect } from "react";
import { useForm, useFormContext, useWatch } from "react-hook-form";
import { ApiBrainDefinitionSchema } from "@/lib/api/brain/types";
import { defaultParamDefinitionRow } from "../config";
import { ParameterDefinition } from "../types";
import { mapApiBrainDefinitionSchemaToParameterDefinition } from "../utils/mapApiBrainDefinitionSchemaToParameterDefinition";
import { mapParameterDefinitionToApiBrainDefinitionSchema } from "../utils/mapParameterDefinitionToApiBrainDefinitionSchema";
type UseParamsDefinitionProps = {
name: string;
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useParamsDefinition = ({ name }: UseParamsDefinitionProps) => {
const { watch, register } = useFormContext();
const existingParams = watch(name) as ApiBrainDefinitionSchema;
const existingProperties =
mapApiBrainDefinitionSchemaToParameterDefinition(existingParams);
const { control } = useForm<{
[name: string]: ParameterDefinition[];
}>({
defaultValues: {
[name]:
existingProperties.length > 0
? existingProperties
: [defaultParamDefinitionRow],
},
});
const params = useWatch({
control,
name,
});
const { setValue } = useFormContext();
useEffect(() => {
const paramsWithValues = params.filter(
(param) => param.name !== "" && param.description !== ""
);
setValue(
name,
mapParameterDefinitionToApiBrainDefinitionSchema(paramsWithValues)
);
}, [params, name, setValue]);
return {
control,
register,
};
};

View File

@ -0,0 +1,8 @@
import { ApiBrainDefinitionSchemaPropertyType } from "@/lib/api/brain/types";
export type ParameterDefinition = {
name: string;
type: ApiBrainDefinitionSchemaPropertyType;
required: boolean;
description: string;
};

View File

@ -0,0 +1,23 @@
import { ApiBrainDefinitionSchema } from "@/lib/api/brain/types";
import { ParameterDefinition } from "../types";
export const mapApiBrainDefinitionSchemaToParameterDefinition = (
schema?: ApiBrainDefinitionSchema
): ParameterDefinition[] => {
if (schema === undefined) {
return [];
}
const { properties, required } = schema;
return properties.map((property) => {
const { name, type, description } = property;
return {
name,
type: type === "string" ? "string" : "number",
required: required.includes(name),
description,
};
});
};

View File

@ -0,0 +1,34 @@
import {
ApiBrainDefinitionSchema,
ApiBrainDefinitionSchemaProperty,
} from "@/lib/api/brain/types";
import { ParameterDefinition } from "../types";
export const mapParameterDefinitionToApiBrainDefinitionSchema = (
params: ParameterDefinition[]
): ApiBrainDefinitionSchema => {
const properties: ApiBrainDefinitionSchemaProperty[] = [];
const required: string[] = [];
params.forEach((param) => {
const { name, type, required: isRequired, description } = param;
const property: ApiBrainDefinitionSchemaProperty = {
name,
type: type === "string" ? "string" : "number",
description,
};
properties.push(property);
if (isRequired) {
required.push(name);
}
});
return {
properties,
required,
};
};

View File

@ -0,0 +1,66 @@
import { useFieldArray } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { MdAdd } from "react-icons/md";
import { TiInfoOutline } from "react-icons/ti";
import Button from "@/lib/components/ui/Button";
import { SecretDefinitionRow } from "./components/SecretDefinitionRow";
import {
brainSecretsSchemaDefinitionKeyInForm,
defaultSecretDefinitionRow,
} from "./config";
import { useSecretsDefinition } from "./hooks/useSecretsDefinition";
import { tabDescriptionStyle } from "../../styles";
const paramsNameStyle = "flex flex-1 justify-center";
export const SecretsDefinition = (): JSX.Element => {
const { t } = useTranslation(["brain", "external_api_definition"]);
const { control, register } = useSecretsDefinition();
const { fields, append, remove } = useFieldArray({
control,
name: brainSecretsSchemaDefinitionKeyInForm,
});
return (
<div>
<div className={tabDescriptionStyle}>
<TiInfoOutline size={30} />
<p className="ml-5">
{t("secretsTabDescription", { ns: "external_api_definition" })}
</p>
</div>
<div className="flex flex-1 font-medium">
<div className="flex flex-1">
<div className={paramsNameStyle}>{t("api_brain.name")}</div>
<div className={paramsNameStyle}>{t("api_brain.description")}</div>
<div className={paramsNameStyle}>{t("api_brain.type")}</div>
<div className={paramsNameStyle}>{t("api_brain.value")}</div>
</div>
</div>
{fields.map((field, index) => (
<SecretDefinitionRow
key={field.id}
index={index}
remove={remove}
register={register}
/>
))}
<div className="flex justify-end mt-3">
<Button
type="button"
onClick={() => {
append(defaultSecretDefinitionRow);
}}
className="p-2"
variant={"secondary"}
>
<MdAdd size={20} /> {t("api_brain.addRow")}
</Button>
</div>
</div>
);
};

View File

@ -0,0 +1,81 @@
import { UseFormRegister } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { MdCancel } from "react-icons/md";
import { paramsNameStyle } from "../../styles";
import { brainSecretsSchemaDefinitionKeyInForm } from "../config";
import { SecretDefinition } from "../types";
type SecretControl = {
[name: string]: SecretDefinition[];
};
type SecretDefinitionRowProps = {
register: UseFormRegister<SecretControl>;
index: number;
remove: (index: number) => void;
};
export const SecretDefinitionRow = ({
index,
remove,
register,
}: SecretDefinitionRowProps): JSX.Element => {
const { t } = useTranslation(["brain"]);
return (
<div className="flex flex-1 justify-between items-center py-4 border-b border-gray-300 relative gap-2">
<div className={paramsNameStyle}>
<input
type="text"
className="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block shadow-sm sm:text-sm border-gray-300 dark:bg-gray-800 dark:text-gray-100 rounded-md w-full outline-none"
placeholder={t("api_brain.name")}
{...register(
`${brainSecretsSchemaDefinitionKeyInForm}[${index}].name`
)}
/>
</div>
<div className="flex-1">
<input
type="text"
id={`description-${index}`}
className="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 dark:bg-gray-800 dark:text-gray-100 rounded-md outline-none"
placeholder={t("api_brain.description")}
{...register(
`${brainSecretsSchemaDefinitionKeyInForm}[${index}].description`
)}
/>
</div>
<div className="flex-1">
<select
id={`type-${index}`}
className="mt-1 block w-full py-2 px-3 border border-gray-300 dark:bg-gray-800 dark:text-gray-100 bg-white dark:border-gray-800 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
{...register(
`${brainSecretsSchemaDefinitionKeyInForm}[${index}].type`
)}
>
<option value="string">string</option>
<option value="number">number</option>
</select>
</div>
<div className={paramsNameStyle}>
<input
type="text"
className="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 dark:bg-gray-800 dark:text-gray-100 rounded-md outline-none"
placeholder={t("api_brain.value")}
{...register(
`${brainSecretsSchemaDefinitionKeyInForm}[${index}].value`
)}
/>
</div>
<button
type="button"
onClick={() => remove(index)}
className="absolute right-0 text-red-500 bg-transparent border-none cursor-pointer"
>
<MdCancel />
</button>
</div>
);
};

View File

@ -0,0 +1,11 @@
import { SecretDefinition } from "./types";
export const defaultSecretDefinitionRow: SecretDefinition = {
name: "",
type: "string",
value: "",
description: "",
};
export const brainSecretsSchemaDefinitionKeyInForm = "brain_definition.secrets";
export const brainSecretsValueKeyInForm = "brain_secrets_values";

View File

@ -0,0 +1,67 @@
import { useEffect } from "react";
import { useForm, useFormContext, useWatch } from "react-hook-form";
import {
brainSecretsSchemaDefinitionKeyInForm,
brainSecretsValueKeyInForm,
defaultSecretDefinitionRow,
} from "../config";
import { SecretDefinition, SecretRelatedFields } from "../types";
import { mapSecretDefinitionToApiBrainSecretsDefinitionsAndValue } from "../utils/mapSecretDefinitionToApiBrainSecretDefinition";
import { mapSecretsDefinitionsAndValuesToSecretDefinition } from "../utils/mapSecretsDefinitionsAndValuesToSecretDefinition";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useSecretsDefinition = () => {
const { getValues: getContextValues } = useFormContext<{
[brainSecretsSchemaDefinitionKeyInForm]: SecretDefinition[];
[brainSecretsValueKeyInForm]: Record<string, string>;
}>();
const { setValue } = useFormContext();
const { register, control } = useForm<SecretRelatedFields>({
defaultValues: {
[brainSecretsSchemaDefinitionKeyInForm]: [defaultSecretDefinitionRow],
},
});
useEffect(() => {
const existingSecretsDefinitionsSchemas = getContextValues(
brainSecretsSchemaDefinitionKeyInForm
);
const existingSecretsValues = getContextValues(brainSecretsValueKeyInForm);
const secretDefinition = mapSecretsDefinitionsAndValuesToSecretDefinition(
existingSecretsDefinitionsSchemas,
existingSecretsValues
);
if (secretDefinition.length === 0) {
return;
}
setValue(brainSecretsSchemaDefinitionKeyInForm, secretDefinition);
}, [getContextValues, setValue]);
const secretsDefinitionSchemas = useWatch({
control,
name: brainSecretsSchemaDefinitionKeyInForm,
}) as SecretDefinition[] | undefined;
useEffect(() => {
if (secretsDefinitionSchemas === undefined) {
return;
}
const paramsWithValues = secretsDefinitionSchemas.filter(
(param) => param.name !== "" && param.description !== ""
);
const { secrets, brain_secrets_values } =
mapSecretDefinitionToApiBrainSecretsDefinitionsAndValue(paramsWithValues);
setValue(brainSecretsSchemaDefinitionKeyInForm, secrets);
setValue(brainSecretsValueKeyInForm, brain_secrets_values);
}, [secretsDefinitionSchemas, setValue]);
return {
control,
register,
};
};

View File

@ -0,0 +1,12 @@
import { ApiBrainDefinitionSchemaPropertyType } from "@/lib/api/brain/types";
export type SecretDefinition = {
name: string;
type: ApiBrainDefinitionSchemaPropertyType;
description: string;
value: string;
};
export type SecretRelatedFields = {
[name: string]: SecretDefinition[];
};

View File

@ -0,0 +1,31 @@
import { ApiBrainDefinitionSecret } from "@/lib/api/brain/types";
import { SecretDefinition } from "../types";
export type ApiBrainSecretsDefinitionsAndValues = {
secrets: ApiBrainDefinitionSecret[];
brain_secrets_values: Record<string, string>;
};
export const mapSecretDefinitionToApiBrainSecretsDefinitionsAndValue = (
secretDefinitions: SecretDefinition[]
): ApiBrainSecretsDefinitionsAndValues => {
const secrets: ApiBrainDefinitionSecret[] = secretDefinitions.map(
(secretDefinition) => {
const { name, type } = secretDefinition;
return { name, type };
}
);
const brain_secrets_values: Record<string, string> = secretDefinitions.reduce(
(acc, secretDefinition) => {
const { name, value } = secretDefinition;
return { ...acc, [name]: value };
},
{}
);
return { secrets, brain_secrets_values };
};

View File

@ -0,0 +1,29 @@
import { ApiBrainDefinition } from "@/lib/api/brain/types";
import { defaultSecretDefinitionRow } from "../config";
import { SecretDefinition } from "../types";
export const mapSecretsDefinitionsAndValuesToSecretDefinition = (
apiBrainDefinition?: ApiBrainDefinition,
brainSecretsValue?: Record<string, string>
): SecretDefinition[] => {
if (
apiBrainDefinition === undefined ||
brainSecretsValue === undefined ||
apiBrainDefinition.secrets === undefined ||
apiBrainDefinition.secrets.length === 0
) {
return [defaultSecretDefinitionRow];
}
const secrets = apiBrainDefinition.secrets;
const secretDefinition: SecretDefinition[] = secrets.map((secret) => {
const { name, type } = secret;
const value = brainSecretsValue[name] ?? "";
return { name, type, description: "", value };
});
return secretDefinition;
};

View File

@ -0,0 +1 @@
export const paramsNameStyle = "flex flex-1 justify-center";

View File

@ -3,8 +3,8 @@ import { useState } from "react";
import { ApiTab } from "../types"; import { ApiTab } from "../types";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useApiDefinitionTabs = () => { export const useApiRequestDefinition = () => {
const [selectedTab, setSelectedTab] = useState<ApiTab>("searchParams"); const [selectedTab, setSelectedTab] = useState<ApiTab>("params");
return { return {
selectedTab, selectedTab,

View File

@ -0,0 +1 @@
export const tabDescriptionStyle = "flex items-center text-gray-500 my-3";

View File

@ -1,3 +0,0 @@
export const HeadersDefinition = (): JSX.Element => {
return <></>;
};

View File

@ -1,3 +0,0 @@
export const ParamsDefinition = (): JSX.Element => {
return <></>;
};

View File

@ -1,3 +0,0 @@
export const SearchParamsDefinition = (): JSX.Element => {
return <></>;
};

View File

@ -1,3 +1,3 @@
export const apiTabs = ["searchParams", "headers", "params"] as const; export const apiTabs = ["params", "searchParams", "secrets"] as const;
export type ApiTab = (typeof apiTabs)[number]; export type ApiTab = (typeof apiTabs)[number];

View File

@ -10,12 +10,12 @@ import { useBrainApi } from "@/lib/api/brain/useBrainApi";
import { usePromptApi } from "@/lib/api/prompt/usePromptApi"; import { usePromptApi } from "@/lib/api/prompt/usePromptApi";
import { USER_DATA_KEY } from "@/lib/api/user/config"; import { USER_DATA_KEY } from "@/lib/api/user/config";
import { useUserApi } from "@/lib/api/user/useUserApi"; import { useUserApi } from "@/lib/api/user/useUserApi";
import { defaultBrainConfig } from "@/lib/config/defaultBrainConfig";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens"; import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
import { getAccessibleModels } from "@/lib/helpers/getAccessibleModels"; import { getAccessibleModels } from "@/lib/helpers/getAccessibleModels";
import { useToast } from "@/lib/hooks"; import { useToast } from "@/lib/hooks";
import { BrainConfig } from "@/lib/types/brainConfig";
import { CreateBrainProps } from "../types";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useAddBrainConfig = () => { export const useAddBrainConfig = () => {
@ -38,17 +38,6 @@ export const useAddBrainConfig = () => {
queryFn: getUser, queryFn: getUser,
}); });
const defaultValues = {
...defaultBrainConfig,
name: "",
description: "",
setDefault: false,
prompt: {
title: "",
content: "",
},
};
const { const {
register, register,
getValues, getValues,
@ -56,14 +45,14 @@ export const useAddBrainConfig = () => {
watch, watch,
setValue, setValue,
formState: { dirtyFields }, formState: { dirtyFields },
} = useFormContext<BrainConfig>(); } = useFormContext<CreateBrainProps>();
const openAiKey = watch("openAiKey"); const openAiKey = watch("openai_api_key");
const model = watch("model"); const model = watch("model");
const temperature = watch("temperature"); const temperature = watch("temperature");
const maxTokens = watch("maxTokens"); const maxTokens = watch("max_tokens");
const status = watch("status"); const status = watch("status");
const brainType = watch("brainType"); const brainType = watch("brain_type");
const accessibleModels = getAccessibleModels({ const accessibleModels = getAccessibleModels({
openAiKey, openAiKey,
@ -77,7 +66,9 @@ export const useAddBrainConfig = () => {
}, [dirtyFields.status, status]); }, [dirtyFields.status, status]);
useEffect(() => { useEffect(() => {
setValue("maxTokens", Math.min(maxTokens, defineMaxTokens(model))); if (maxTokens !== undefined && model !== undefined) {
setValue("max_tokens", Math.min(maxTokens, defineMaxTokens(model)));
}
}, [maxTokens, model, setValue]); }, [maxTokens, model, setValue]);
const getCreatingBrainPromptId = async (): Promise<string | undefined> => { const getCreatingBrainPromptId = async (): Promise<string | undefined> => {
@ -91,7 +82,13 @@ export const useAddBrainConfig = () => {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
const { name, description, setDefault } = getValues(); const {
name,
description,
setDefault,
brain_definition,
brain_secrets_values,
} = getValues();
if (name.trim() === "" || isPending) { if (name.trim() === "" || isPending) {
publish({ publish({
@ -117,6 +114,8 @@ export const useAddBrainConfig = () => {
prompt_id, prompt_id,
status, status,
brain_type: brainType, brain_type: brainType,
brain_definition,
brain_secrets_values,
}); });
if (createdBrainId === undefined) { if (createdBrainId === undefined) {
@ -135,7 +134,7 @@ export const useAddBrainConfig = () => {
} }
setIsShareModalOpen(false); setIsShareModalOpen(false);
reset(defaultValues); reset();
publish({ publish({
variant: "success", variant: "success",
text: t("brainCreated", { ns: "brain" }), text: t("brainCreated", { ns: "brain" }),

View File

@ -0,0 +1,7 @@
import { CreateBrainInput } from "@/lib/api/brain/types";
import { CreatePromptProps } from "@/lib/api/prompt/prompt";
export type CreateBrainProps = CreateBrainInput & {
prompt: CreatePromptProps;
setDefault: boolean;
};

View File

@ -1,5 +1,18 @@
import { CreateBrainInput } from "../api/brain/types";
import { BrainConfig } from "../types/brainConfig"; import { BrainConfig } from "../types/brainConfig";
export const addBrainDefaultValues: CreateBrainInput = {
model: "gpt-3.5-turbo",
temperature: 0,
max_tokens: 500,
openai_api_key: undefined,
prompt_id: undefined,
status: "private",
name: "",
description: "",
brain_type: "doc",
};
export const defaultBrainConfig: BrainConfig = { export const defaultBrainConfig: BrainConfig = {
model: "gpt-3.5-turbo", model: "gpt-3.5-turbo",
temperature: 0, temperature: 0,

View File

@ -51,7 +51,10 @@ export const useBrainProvider = () => {
try { try {
setCurrentBrainId(createdBrain.id); setCurrentBrainId(createdBrain.id);
void track("BRAIN_CREATED"); void track("BRAIN_CREATED", {
brainType: brain.brain_type,
});
void fetchAllBrains(); void fetchAllBrains();
return createdBrain.id; return createdBrain.id;

View File

@ -1,6 +1,8 @@
import { Model, PaidModels } from "../types/brainConfig"; import { Model, PaidModels } from "../types/brainConfig";
export const defineMaxTokens = (model: Model | PaidModels): number => { export const defineMaxTokens = (
model: Model | PaidModels | undefined
): number => {
//At the moment is evaluating only models from OpenAI //At the moment is evaluating only models from OpenAI
switch (model) { switch (model) {
case "gpt-3.5-turbo": case "gpt-3.5-turbo":

View File

@ -6,6 +6,8 @@ export const brainTypes = ["doc", "api"] as const;
export type BrainType = (typeof brainTypes)[number]; export type BrainType = (typeof brainTypes)[number];
export type Model = (typeof freeModels)[number];
export type BrainConfig = { export type BrainConfig = {
model: Model; model: Model;
temperature: number; temperature: number;
@ -57,5 +59,3 @@ export const freeModels = [
export const paidModels = [...openAiPaidModels] as const; export const paidModels = [...openAiPaidModels] as const;
export type PaidModels = (typeof paidModels)[number]; export type PaidModels = (typeof paidModels)[number];
export type Model = (typeof freeModels)[number];

View File

@ -49,5 +49,13 @@
"myBrains": "My Brains", "myBrains": "My Brains",
"knowledge_source_doc": "Documents", "knowledge_source_doc": "Documents",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "Knowledge source" "knowledge_source_label": "Knowledge source",
"api_brain":{
"name": "Name",
"description": "Description",
"type": "Type",
"required": "Required",
"addRow": "Add row",
"value": "Value"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "Secrets",
"paramsTabDescription": "These values are sent as the request body.",
"searchParamsTabDescription": "These values are sent as the query string.",
"secretsTabDescription": "These values are sent as header keys"
} }

View File

@ -49,5 +49,13 @@
"myBrains": "Mis cerebros", "myBrains": "Mis cerebros",
"knowledge_source_doc": "Documentos", "knowledge_source_doc": "Documentos",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "Fuente de conocimiento" "knowledge_source_label": "Fuente de conocimiento",
"api_brain":{
"name": "Nombre",
"description": "Descripción",
"type": "Tipo",
"required": "Requerido",
"addRow": "Agregar fila",
"value": "Valor"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "Secretos",
"paramsTabDescription": "Estos valores se envían como cuerpo de la solicitud.",
"searchParamsTabDescription": "Estos valores se envían como cadena de consulta.",
"secretsTabDescription": "Estos valores se envían como claves de encabezado."
} }

View File

@ -49,5 +49,13 @@
"myBrains": "Mes cerveaux", "myBrains": "Mes cerveaux",
"knowledge_source_doc": "Documents", "knowledge_source_doc": "Documents",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "Source de connaissance" "knowledge_source_label": "Source de connaissance",
"api_brain":{
"name": "Nom",
"description": "Description",
"type": "Type",
"required": "Requis",
"addRow": "Ajouter une ligne",
"value": "Valeur"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "Secrets",
"paramsTabDescription": "Ces valeurs sont envoyées sous forme de corps de requête.",
"searchParamsTabDescription": "Ces valeurs sont envoyées sous forme de chaîne de requête.",
"secretsTabDescription": "Ces valeurs sont envoyées sous forme de clés d'en-tête."
} }

View File

@ -49,5 +49,13 @@
"myBrains": "Meus cérebros", "myBrains": "Meus cérebros",
"knowledge_source_doc": "Documentos", "knowledge_source_doc": "Documentos",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "Fonte de conhecimento" "knowledge_source_label": "Fonte de conhecimento",
"api_brain":{
"name": "Name",
"description": "Description",
"type": "Type",
"required": "Required",
"addRow": "Add row",
"value": "Value"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "Secrets",
"paramsTabDescription": "Estes valores são enviados como corpo da solicitação.",
"searchParamsTabDescription": "Estes valores são enviados como string de consulta.",
"secretsTabDescription": "Estes valores são enviados como chaves de cabeçalho"
} }

View File

@ -49,5 +49,13 @@
"myBrains": "Мои мозги", "myBrains": "Мои мозги",
"knowledge_source_doc": "Документы", "knowledge_source_doc": "Документы",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "Источник знаний" "knowledge_source_label": "Источник знаний",
"api_brain":{
"name": "Название",
"description": "Описание",
"type": "Тип",
"required": "Обязательно",
"addRow": "Добавить строку",
"value": "Значение"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "секреты",
"paramsTabDescription": "Эти значения отправляются в теле запроса.",
"searchParamsTabDescription": "Эти значения отправляются в виде строки запроса.",
"secretsTabDescription": "Эти значения отправляются в виде ключей заголовка."
} }

View File

@ -49,5 +49,13 @@
"myBrains": "我的大脑", "myBrains": "我的大脑",
"knowledge_source_doc": "文档", "knowledge_source_doc": "文档",
"knowledge_source_api": "API", "knowledge_source_api": "API",
"knowledge_source_label": "知识来源" "knowledge_source_label": "知识来源",
"api_brain":{
"name": "名称",
"description": "描述",
"type": "类型",
"required": "必填",
"addRow": "添加行",
"value": "值"
}
} }

View File

@ -6,5 +6,9 @@
"params": "Params", "params": "Params",
"key": "Key", "key": "Key",
"type": "Type (string or number)", "type": "Type (string or number)",
"description": "Description" "description": "Description",
"secrets": "秘密",
"paramsTabDescription": "这些值作为请求体发送。",
"searchParamsTabDescription": "这些值作为查询字符串发送。",
"secretsTabDescription": "这些值作为头部键发送。"
} }