Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ GID='1000'
# AWS_BEDROCK_LLM_CONNECTION_METHOD=iam
# AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS=4096
# AWS_BEDROCK_LLM_SESSION_TOKEN= # Only required if CONNECTION_METHOD is 'sessionToken'
# or even use Short and Long Term API keys
# AWS_BEDROCK_LLM_CONNECTION_METHOD="apiKey"
# AWS_BEDROCK_LLM_API_KEY=

# LLM_PROVIDER='fireworksai'
# FIREWORKS_AI_LLM_API_KEY='my-fireworks-ai-key'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ export default function AwsBedrockLLMOptions({ settings }) {
settings?.AwsBedrockLLMConnectionMethod ?? "iam"
);

console.log("connectionMethod", connectionMethod);
return (
<div className="w-full flex flex-col">
{!settings?.credentialsOnly && (
{!settings?.credentialsOnly && connectionMethod !== "apiKey" && (
<div className="flex flex-col md:flex-row md:items-center gap-x-2 text-white mb-4 bg-blue-800/30 w-fit rounded-lg px-4 py-2">
<div className="gap-x-2 flex items-center">
<Info size={40} />
Expand All @@ -21,6 +20,7 @@ export default function AwsBedrockLLMOptions({ settings }) {
href="https://docs.anythingllm.com/setup/llm-configuration/cloud/aws-bedrock"
target="_blank"
className="underline flex gap-x-1 items-center"
rel="noreferrer"
>
Read more on how to use AWS Bedrock in AnythingLLM
<ArrowSquareOut size={14} />
Expand All @@ -38,7 +38,7 @@ export default function AwsBedrockLLMOptions({ settings }) {
/>
<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.
Expand All @@ -56,6 +56,7 @@ export default function AwsBedrockLLMOptions({ settings }) {
Session Token (Temporary Credentials)
</option>
<option value="iam_role">IAM Role (Implied Credentials)</option>
<option value="apiKey">Bedrock API Key</option>
</select>
</div>

Expand Down Expand Up @@ -117,6 +118,23 @@ export default function AwsBedrockLLMOptions({ settings }) {
/>
</div>
)}
{connectionMethod === "apiKey" && (
<div className="flex flex-col w-60">
<label className="text-theme-text-primary text-sm font-semibold block mb-3">
AWS Bedrock API Key
</label>
<input
type="password"
name="AwsBedrockLLMAPIKey"
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 API Key"
defaultValue={settings?.AwsBedrockLLMAPIKey ? "*".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 region
Expand Down
3 changes: 3 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ SIG_SALT='salt' # Please generate random string at least 32 chars long.
# AWS_BEDROCK_LLM_CONNECTION_METHOD=iam
# AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS=4096
# AWS_BEDROCK_LLM_SESSION_TOKEN= # Only required if CONNECTION_METHOD is 'sessionToken'
# or even use Short and Long Term API keys
# AWS_BEDROCK_LLM_CONNECTION_METHOD="apiKey"
# AWS_BEDROCK_LLM_API_KEY=

# LLM_PROVIDER='apipie'
# APIPIE_LLM_API_KEY='sk-123abc'
Expand Down
1 change: 1 addition & 0 deletions server/models/systemSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,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,
AwsBedrockLLMAPIKey: !!process.env.AWS_BEDROCK_LLM_API_KEY,
AwsBedrockLLMRegion: process.env.AWS_BEDROCK_LLM_REGION,
AwsBedrockLLMModel: process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE,
AwsBedrockLLMTokenLimit:
Expand Down
49 changes: 18 additions & 31 deletions server/utils/AiProviders/bedrock/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const {
BedrockRuntimeClient,
ConverseCommand,
ConverseStreamCommand,
} = require("@aws-sdk/client-bedrock-runtime");
Expand All @@ -15,9 +14,11 @@ const { v4: uuidv4 } = require("uuid");
const {
DEFAULT_MAX_OUTPUT_TOKENS,
DEFAULT_CONTEXT_WINDOW_TOKENS,
SUPPORTED_CONNECTION_METHODS,
getImageFormatFromMime,
base64ToUint8Array,
createBedrockCredentials,
createBedrockRuntimeClient,
getBedrockAuthMethod,
} = require("./utils");

class AWSBedrockLLM {
Expand All @@ -42,7 +43,7 @@ class AWSBedrockLLM {
*/
constructor(embedder = null, modelPreference = null) {
const requiredEnvVars = [
...(this.authMethod !== "iam_role"
...(!["iam_role", "apiKey"].includes(this.authMethod)
? [
// required for iam and sessionToken
"AWS_BEDROCK_LLM_ACCESS_KEY_ID",
Expand All @@ -55,6 +56,12 @@ class AWSBedrockLLM {
"AWS_BEDROCK_LLM_SESSION_TOKEN",
]
: []),
...(this.authMethod === "apiKey"
? [
// required for bedrock api key
"AWS_BEDROCK_LLM_API_KEY",
]
: []),
"AWS_BEDROCK_LLM_REGION",
"AWS_BEDROCK_LLM_MODEL_PREFERENCE",
];
Expand All @@ -75,10 +82,10 @@ class AWSBedrockLLM {
user: Math.floor(contextWindowLimit * 0.7),
};

this.bedrockClient = new BedrockRuntimeClient({
region: process.env.AWS_BEDROCK_LLM_REGION,
credentials: this.credentials,
});
this.bedrockClient = createBedrockRuntimeClient(
this.authMethod,
this.credentials
);

this.embedder = embedder ?? new NativeEmbedder();
this.defaultTemp = 0.7;
Expand All @@ -92,26 +99,7 @@ class AWSBedrockLLM {
* @returns {object} The credentials object.
*/
get credentials() {
switch (this.authMethod) {
case "iam": // explicit credentials
return {
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
};
case "sessionToken": // Session token is used for temporary credentials
return {
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
};
// IAM role is used for long-term credentials implied by system process
// is filled by the AWS SDK automatically if we pass in no credentials
// returning undefined will allow this to happen
case "iam_role":
return undefined;
default:
return undefined;
}
return createBedrockCredentials(this.authMethod);
}

/**
Expand All @@ -120,8 +108,7 @@ class AWSBedrockLLM {
* @returns {"iam" | "iam_role" | "sessionToken"} The authentication method.
*/
get authMethod() {
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
return SUPPORTED_CONNECTION_METHODS.includes(method) ? method : "iam";
return getBedrockAuthMethod();
}

/**
Expand Down Expand Up @@ -287,7 +274,7 @@ class AWSBedrockLLM {
#generateContent({ userPrompt = "", attachments = [] }) {
const content = [];
// Add text block if prompt is not empty
if (!!userPrompt?.trim()?.length) content.push({ text: userPrompt });
if (userPrompt?.trim()?.length) content.push({ text: userPrompt });

// Validate attachments and add valid attachments to content
const validAttachments = this.#validateAttachments(attachments);
Expand Down Expand Up @@ -384,7 +371,7 @@ class AWSBedrockLLM {
if (reasoningBlock) {
const reasoningText =
reasoningBlock.reasoningContent.reasoningText.text.trim();
if (!!reasoningText?.length)
if (reasoningText?.length)
textResponse = `<think>${reasoningText}</think>${textResponse}`;
}
return textResponse;
Expand Down
99 changes: 97 additions & 2 deletions server/utils/AiProviders/bedrock/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const { BedrockRuntimeClient } = require("@aws-sdk/client-bedrock-runtime");
const { fromStatic } = require("@aws-sdk/token-providers");
const { ChatBedrockConverse } = require("@langchain/aws");

/** @typedef {'jpeg' | 'png' | 'gif' | 'webp'} */
const SUPPORTED_BEDROCK_IMAGE_FORMATS = ["jpeg", "png", "gif", "webp"];

Expand All @@ -7,8 +11,95 @@ const DEFAULT_MAX_OUTPUT_TOKENS = 4096;
/** @type {number} */
const DEFAULT_CONTEXT_WINDOW_TOKENS = 8191;

/** @type {'iam' | 'iam_role' | 'sessionToken'} */
const SUPPORTED_CONNECTION_METHODS = ["iam", "iam_role", "sessionToken"];
/** @type {'iam' | 'iam_role' | 'sessionToken' | 'apiKey'} */
const SUPPORTED_CONNECTION_METHODS = [
"iam",
"iam_role",
"sessionToken",
"apiKey",
];

/**
* Gets the AWS Bedrock authentication method from the environment variables.
* @returns {"iam" | "iam_role" | "sessionToken" | "apiKey"} The authentication method.
*/
function getBedrockAuthMethod() {
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
return SUPPORTED_CONNECTION_METHODS.includes(method) ? method : "iam";
}

/**
* Creates the AWS Bedrock credentials object based on the authentication method.
* @param {"iam" | "iam_role" | "sessionToken" | "apiKey"} authMethod - The authentication method.
* @returns {object | undefined} The credentials object.
*/
function createBedrockCredentials(authMethod) {
switch (authMethod) {
case "iam": // explicit credentials
return {
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
};
case "sessionToken": // Session token is used for temporary credentials
return {
accessKeyId: process.env.AWS_BEDROCK_LLM_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_BEDROCK_LLM_ACCESS_KEY,
sessionToken: process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
};
// IAM role is used for long-term credentials implied by system process
// is filled by the AWS SDK automatically if we pass in no credentials
// returning undefined will allow this to happen
case "iam_role":
return undefined;
case "apiKey":
return fromStatic({
token: { token: process.env.AWS_BEDROCK_LLM_API_KEY },
});
default:
return undefined;
}
}

/**
* Creates the AWS Bedrock runtime client based on the authentication method.
* @param {"iam" | "iam_role" | "sessionToken" | "apiKey"} authMethod - The authentication method.
* @param {object | undefined} credentials - The credentials object.
* @returns {BedrockRuntimeClient} The runtime client.
*/
function createBedrockRuntimeClient(authMethod, credentials) {
const clientOpts = {
region: process.env.AWS_BEDROCK_LLM_REGION,
};
if (authMethod === "apiKey") {
clientOpts.token = credentials;
clientOpts.authSchemePreference = ["httpBearerAuth"];
} else {
clientOpts.credentials = credentials;
}
return new BedrockRuntimeClient(clientOpts);
}

/**
* Creates the AWS Bedrock chat client based on the authentication method.
* Used explicitly by the agent provider for the AWS Bedrock provider.
* @param {object} config - The configuration object.
* @param {"iam" | "iam_role" | "sessionToken" | "apiKey"} authMethod - The authentication method.
* @param {object | undefined} credentials - The credentials object.
* @param {string | null} model - The model to use.
* @returns {ChatBedrockConverse} The chat client.
*/
function createBedrockChatClient(config = {}, authMethod, credentials, model) {
authMethod ||= getBedrockAuthMethod();
credentials ||= createBedrockCredentials(authMethod);
model ||= process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE ?? null;
const client = createBedrockRuntimeClient(authMethod, credentials);
return new ChatBedrockConverse({
region: process.env.AWS_BEDROCK_LLM_REGION,
client,
model,
...config,
});
}

/**
* Parses a MIME type string (e.g., "image/jpeg") to extract and validate the image format
Expand Down Expand Up @@ -64,4 +155,8 @@ module.exports = {
DEFAULT_CONTEXT_WINDOW_TOKENS,
getImageFormatFromMime,
base64ToUint8Array,
getBedrockAuthMethod,
createBedrockCredentials,
createBedrockRuntimeClient,
createBedrockChatClient,
};
19 changes: 4 additions & 15 deletions server/utils/agents/aibitat/providers/ai-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
const { v4 } = require("uuid");
const { ChatOpenAI } = require("@langchain/openai");
const { ChatAnthropic } = require("@langchain/anthropic");
const { ChatBedrockConverse } = require("@langchain/aws");
const { ChatOllama } = require("@langchain/community/chat_models/ollama");
const { toValidNumber, safeJsonParse } = require("../../../http");
const { getLLMProviderClass } = require("../../../helpers");
Expand All @@ -22,6 +21,9 @@ const { parseFoundryBasePath } = require("../../../AiProviders/foundry");
const {
SystemPromptVariables,
} = require("../../../../models/systemPromptVariables");
const {
createBedrockChatClient,
} = require("../../../AiProviders/bedrock/utils");

const DEFAULT_WORKSPACE_PROMPT =
"You are a helpful ai assistant who can assist the user and use tools available to help answer the users prompts and questions.";
Expand Down Expand Up @@ -151,20 +153,7 @@ class Provider {
...config,
});
case "bedrock":
// Grab just the credentials from the bedrock provider
// using a closure to avoid circular dependency + to avoid instantiating the provider
const credentials = (() => {
const AWSBedrockProvider = require("./bedrock");
const bedrockProvider = new AWSBedrockProvider();
return bedrockProvider.credentials;
})();

return new ChatBedrockConverse({
model: process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE,
region: process.env.AWS_BEDROCK_LLM_REGION,
credentials: credentials,
...config,
});
return createBedrockChatClient(config);
case "fireworksai":
return new ChatOpenAI({
apiKey: process.env.FIREWORKS_AI_LLM_API_KEY,
Expand Down
Loading