mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-03-13 05:32:24 +00:00
Merge a456ed7603
into 4bdd921c75
This commit is contained in:
commit
f93267974c
8 changed files with 227 additions and 139 deletions
frontend/src
server
models
utils
AiProviders/bedrock
agents
helpers
|
@ -3,10 +3,21 @@ import { AWS_REGIONS } from "./regions";
|
|||
import { useState } from "react";
|
||||
|
||||
export default function AwsBedrockLLMOptions({ settings }) {
|
||||
const [useSessionToken, setUseSessionToken] = useState(
|
||||
settings?.AwsBedrockLLMConnectionMethod === "sessionToken"
|
||||
const [authMethod, setAuthMethod] = useState(
|
||||
settings?.AwsBedrockLLMConnectionMethod || "iam"
|
||||
);
|
||||
|
||||
const renderAuthInputs = () => {
|
||||
switch (authMethod) {
|
||||
case "profile":
|
||||
return <ProfileInputs settings={settings} />;
|
||||
case "sessionToken":
|
||||
return <SessionTokenInputs settings={settings} />;
|
||||
default:
|
||||
return <IAMUserInputs settings={settings} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col">
|
||||
{!settings?.credentialsOnly && (
|
||||
|
@ -19,6 +30,7 @@ export default function AwsBedrockLLMOptions({ settings }) {
|
|||
<a
|
||||
href="https://docs.anythingllm.com/setup/llm-configuration/cloud/aws-bedrock"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="underline flex gap-x-1 items-center"
|
||||
>
|
||||
Read more on how to use AWS Bedrock in AnythingLLM
|
||||
|
@ -33,101 +45,39 @@ export default function AwsBedrockLLMOptions({ settings }) {
|
|||
<input
|
||||
type="hidden"
|
||||
name="AwsBedrockLLMConnectionMethod"
|
||||
value={useSessionToken ? "sessionToken" : "iam"}
|
||||
value={authMethod}
|
||||
/>
|
||||
<div className="flex flex-col w-full">
|
||||
<label className="text-theme-text-primary text-sm font-semibold block mb-3">
|
||||
Use session token
|
||||
Authentication Method
|
||||
</label>
|
||||
<p className="text-theme-text-secondary text-sm">
|
||||
Select the method to authenticate with AWS Bedrock.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-start gap-x-4 bg-theme-settings-input-bg p-2.5 rounded-lg w-fit">
|
||||
<span
|
||||
className={`text-sm ${
|
||||
!useSessionToken
|
||||
? "text-theme-text-primary"
|
||||
: "text-theme-text-secondary"
|
||||
}`}
|
||||
>
|
||||
IAM
|
||||
</span>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="sr-only peer"
|
||||
checked={useSessionToken}
|
||||
onChange={(e) => setUseSessionToken(e.target.checked)}
|
||||
/>
|
||||
<div className="w-11 h-6 bg-[#4b5563] light:bg-[#e5e7eb] peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-[#d1d5db] light:after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-button"></div>
|
||||
</label>
|
||||
<span
|
||||
className={`text-sm ${
|
||||
useSessionToken
|
||||
? "text-theme-text-primary"
|
||||
: "text-theme-text-secondary"
|
||||
}`}
|
||||
>
|
||||
Session Token
|
||||
</span>
|
||||
{["iam", "sessionToken", "profile"].map((method) => (
|
||||
<span
|
||||
key={method}
|
||||
onClick={() => setAuthMethod(method)}
|
||||
className={`text-sm cursor-pointer ${
|
||||
authMethod === method
|
||||
? "text-theme-text-primary"
|
||||
: "text-theme-text-secondary"
|
||||
}`}
|
||||
>
|
||||
{method === "iam"
|
||||
? "IAM User"
|
||||
: method === "sessionToken"
|
||||
? "Session Token"
|
||||
: "Profile"}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex items-center gap-[36px] my-1.5">
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS Bedrock IAM Access ID
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMAccessKeyId"
|
||||
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock IAM User Access ID"
|
||||
defaultValue={
|
||||
settings?.AwsBedrockLLMAccessKeyId ? "*".repeat(20) : ""
|
||||
}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS Bedrock IAM Access Key
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMAccessKey"
|
||||
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock IAM User Access Key"
|
||||
defaultValue={
|
||||
settings?.AwsBedrockLLMAccessKey ? "*".repeat(20) : ""
|
||||
}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
{useSessionToken && (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-theme-text-primary text-sm font-semibold block mb-3">
|
||||
AWS Bedrock Session Token
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMSessionToken"
|
||||
className="border-none bg-theme-settings-input-bg text-theme-text-primary placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock Session Token"
|
||||
defaultValue={
|
||||
settings?.AwsBedrockLLMSessionToken ? "*".repeat(20) : ""
|
||||
}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{renderAuthInputs()}
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS region
|
||||
|
@ -189,3 +139,87 @@ export default function AwsBedrockLLMOptions({ settings }) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IAMUserInputs({ settings }) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS Bedrock IAM Access ID
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMAccessKeyId"
|
||||
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock IAM User Access ID"
|
||||
defaultValue={
|
||||
settings?.AwsBedrockLLMAccessKeyId ? "*".repeat(20) : ""
|
||||
}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS Bedrock IAM Access Key
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMAccessKey"
|
||||
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock IAM User Access Key"
|
||||
defaultValue={settings?.AwsBedrockLLMAccessKey ? "*".repeat(20) : ""}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SessionTokenInputs({ settings }) {
|
||||
return (
|
||||
<>
|
||||
<IAMUserInputs settings={settings} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-theme-text-primary text-sm font-semibold block mb-3">
|
||||
AWS Bedrock Session Token
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="AwsBedrockLLMSessionToken"
|
||||
className="border-none bg-theme-settings-input-bg text-theme-text-primary placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Bedrock Session Token"
|
||||
defaultValue={
|
||||
settings?.AwsBedrockLLMSessionToken ? "*".repeat(20) : ""
|
||||
}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ProfileInputs({ settings }) {
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
AWS Credential Profile
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="AwsBedrockLLMProfileName"
|
||||
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||
placeholder="AWS Credential Profile name"
|
||||
defaultValue={settings?.AwsBedrockLLMProfileName || ""}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -252,12 +252,7 @@ export const AVAILABLE_LLM_PROVIDERS = [
|
|||
logo: AWSBedrockLogo,
|
||||
options: (settings) => <AWSBedrockLLMOptions settings={settings} />,
|
||||
description: "Run powerful foundation models privately with AWS Bedrock.",
|
||||
requiredConfig: [
|
||||
"AwsBedrockLLMAccessKeyId",
|
||||
"AwsBedrockLLMAccessKey",
|
||||
"AwsBedrockLLMRegion",
|
||||
"AwsBedrockLLMModel",
|
||||
],
|
||||
requiredConfig: ["AwsBedrockLLMRegion", "AwsBedrockLLMModel"],
|
||||
},
|
||||
{
|
||||
name: "APIpie",
|
||||
|
|
|
@ -534,6 +534,7 @@ const SystemSettings = {
|
|||
AwsBedrockLLMAccessKeyId: !!process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
AwsBedrockLLMAccessKey: !!process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
AwsBedrockLLMSessionToken: !!process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
|
||||
AwsBedrockLLMProfileName: process.env.AWS_BEDROCK_LLM_PROFILE_NAME,
|
||||
AwsBedrockLLMRegion: process.env.AWS_BEDROCK_LLM_REGION,
|
||||
AwsBedrockLLMModel: process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE,
|
||||
AwsBedrockLLMTokenLimit: process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT,
|
||||
|
|
|
@ -26,22 +26,40 @@ class AWSBedrockLLM {
|
|||
];
|
||||
|
||||
constructor(embedder = null, modelPreference = null) {
|
||||
if (!process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID)
|
||||
throw new Error("No AWS Bedrock LLM profile id was set.");
|
||||
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
|
||||
|
||||
if (!process.env.AWS_BEDROCK_LLM_ACCESS_KEY)
|
||||
throw new Error("No AWS Bedrock LLM access key was set.");
|
||||
|
||||
if (!process.env.AWS_BEDROCK_LLM_REGION)
|
||||
if (!process.env.AWS_BEDROCK_LLM_REGION) {
|
||||
throw new Error("No AWS Bedrock LLM region was set.");
|
||||
}
|
||||
|
||||
if (
|
||||
process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD === "sessionToken" &&
|
||||
!process.env.AWS_BEDROCK_LLM_SESSION_TOKEN
|
||||
)
|
||||
throw new Error(
|
||||
"No AWS Bedrock LLM session token was set while using session token as the authentication method."
|
||||
);
|
||||
if (method === "profile") {
|
||||
if (!process.env.AWS_BEDROCK_LLM_PROFILE_NAME) {
|
||||
throw new Error(
|
||||
"No AWS Bedrock LLM profile name was set for profile authentication method."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID) {
|
||||
throw new Error(
|
||||
"No AWS Bedrock LLM access key ID was set for iam authentication method."
|
||||
);
|
||||
}
|
||||
if (!process.env.AWS_BEDROCK_LLM_ACCESS_KEY) {
|
||||
throw new Error(
|
||||
"No AWS Bedrock LLM access key was set for iam authentication method."
|
||||
);
|
||||
}
|
||||
|
||||
// Additional validation for sessionToken method
|
||||
if (
|
||||
method === "sessionToken" &&
|
||||
!process.env.AWS_BEDROCK_LLM_SESSION_TOKEN
|
||||
) {
|
||||
throw new Error(
|
||||
"No AWS Bedrock LLM session token was set while using session token as the authentication method."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.model =
|
||||
modelPreference || process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE;
|
||||
|
@ -60,12 +78,12 @@ class AWSBedrockLLM {
|
|||
|
||||
/**
|
||||
* Get the authentication method for the AWS Bedrock LLM.
|
||||
* There are only two valid values for this setting - anything else will default to "iam".
|
||||
* @returns {"iam"|"sessionToken"}
|
||||
* There are only three valid values for this setting - anything else will default to "iam".
|
||||
* @returns {"iam"|"sessionToken"|"profile"}
|
||||
*/
|
||||
get authMethod() {
|
||||
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
|
||||
if (!["iam", "sessionToken"].includes(method)) return "iam";
|
||||
if (!["iam", "sessionToken", "profile"].includes(method)) return "iam";
|
||||
return method;
|
||||
}
|
||||
|
||||
|
@ -74,13 +92,19 @@ class AWSBedrockLLM {
|
|||
return new ChatBedrockConverse({
|
||||
model: this.model,
|
||||
region: process.env.AWS_BEDROCK_LLM_REGION,
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
...(this.authMethod === "sessionToken"
|
||||
? { sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN }
|
||||
: {}),
|
||||
},
|
||||
...(this.authMethod === "profile"
|
||||
? {
|
||||
profile: process.env.AWS_BEDROCK_LLM_PROFILE_NAME,
|
||||
}
|
||||
: {
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
...(this.authMethod === "sessionToken" && {
|
||||
sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
temperature,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -116,16 +116,28 @@ class Provider {
|
|||
),
|
||||
...config,
|
||||
});
|
||||
case "bedrock":
|
||||
case "bedrock": {
|
||||
const awsAuthMethod =
|
||||
process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
|
||||
return new ChatBedrockConverse({
|
||||
model: process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE,
|
||||
region: process.env.AWS_BEDROCK_LLM_REGION,
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
},
|
||||
...(awsAuthMethod === "profile"
|
||||
? {
|
||||
profile: process.env.AWS_BEDROCK_LLM_PROFILE_NAME,
|
||||
}
|
||||
: {
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
...(awsAuthMethod === "sessionToken" && {
|
||||
sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
...config,
|
||||
});
|
||||
}
|
||||
case "fireworksai":
|
||||
return new ChatOpenAI({
|
||||
apiKey: process.env.FIREWORKS_AI_LLM_API_KEY,
|
||||
|
|
|
@ -19,15 +19,19 @@ class AWSBedrockProvider extends InheritMultiple([Provider, UnTooled]) {
|
|||
const model = process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE ?? null;
|
||||
const client = new ChatBedrockConverse({
|
||||
region: process.env.AWS_BEDROCK_LLM_REGION,
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
// If we're using a session token, we need to pass it in as a credential
|
||||
// otherwise we must omit it so it does not conflict if using IAM auth
|
||||
...(this.authMethod === "sessionToken"
|
||||
? { sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN }
|
||||
: {}),
|
||||
},
|
||||
...(this.authMethod === "profile"
|
||||
? {
|
||||
profile: process.env.AWS_BEDROCK_LLM_PROFILE_NAME,
|
||||
}
|
||||
: {
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
|
||||
...(this.authMethod === "sessionToken" && {
|
||||
sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
model,
|
||||
});
|
||||
|
||||
|
@ -38,12 +42,12 @@ class AWSBedrockProvider extends InheritMultiple([Provider, UnTooled]) {
|
|||
|
||||
/**
|
||||
* Get the authentication method for the AWS Bedrock LLM.
|
||||
* There are only two valid values for this setting - anything else will default to "iam".
|
||||
* @returns {"iam"|"sessionToken"}
|
||||
* There are only three valid values for this setting - anything else will default to "iam".
|
||||
* @returns {"iam"|"sessionToken"|"profile"}
|
||||
*/
|
||||
get authMethod() {
|
||||
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
|
||||
if (!["iam", "sessionToken"].includes(method)) return "iam";
|
||||
if (!["iam", "sessionToken", "profile"].includes(method)) return "iam";
|
||||
return method;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,14 +140,26 @@ class AgentHandler {
|
|||
);
|
||||
break;
|
||||
case "bedrock":
|
||||
if (
|
||||
!process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID ||
|
||||
!process.env.AWS_BEDROCK_LLM_ACCESS_KEY ||
|
||||
!process.env.AWS_BEDROCK_LLM_REGION
|
||||
)
|
||||
throw new Error(
|
||||
"AWS Bedrock Access Keys and region must be provided to use agents."
|
||||
);
|
||||
if (process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD === "profile") {
|
||||
if (
|
||||
!process.env.AWS_BEDROCK_LLM_PROFILE_NAME ||
|
||||
!process.env.AWS_BEDROCK_LLM_REGION
|
||||
) {
|
||||
throw new Error(
|
||||
"AWS Bedrock requires a profile name and region to use agents for the profile authentication method."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID ||
|
||||
!process.env.AWS_BEDROCK_LLM_ACCESS_KEY ||
|
||||
!process.env.AWS_BEDROCK_LLM_REGION
|
||||
) {
|
||||
throw new Error(
|
||||
"AWS Bedrock requires valid access keys and a region to use agents for the iam authentication method."
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "fireworksai":
|
||||
if (!process.env.FIREWORKS_AI_LLM_API_KEY)
|
||||
|
|
|
@ -209,7 +209,9 @@ const KEY_MAPPING = {
|
|||
envKey: "AWS_BEDROCK_LLM_CONNECTION_METHOD",
|
||||
checks: [
|
||||
(input) =>
|
||||
["iam", "sessionToken"].includes(input) ? null : "Invalid value",
|
||||
["iam", "sessionToken", "profile"].includes(input)
|
||||
? null
|
||||
: "Invalid value",
|
||||
],
|
||||
},
|
||||
AwsBedrockLLMAccessKeyId: {
|
||||
|
@ -224,6 +226,10 @@ const KEY_MAPPING = {
|
|||
envKey: "AWS_BEDROCK_LLM_SESSION_TOKEN",
|
||||
checks: [],
|
||||
},
|
||||
AwsBedrockLLMProfileName: {
|
||||
envKey: "AWS_BEDROCK_LLM_PROFILE_NAME",
|
||||
checks: [isNotEmpty],
|
||||
},
|
||||
AwsBedrockLLMRegion: {
|
||||
envKey: "AWS_BEDROCK_LLM_REGION",
|
||||
checks: [isNotEmpty],
|
||||
|
|
Loading…
Add table
Reference in a new issue