diff --git a/docker/.env.example b/docker/.env.example
index 5fd93ab9e90..117a46133b2 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -105,6 +105,9 @@ GID='1000'
# AWS_BEDROCK_LLM_REGION=us-west-2
# AWS_BEDROCK_LLM_MODEL_PREFERENCE=meta.llama3-1-8b-instruct-v1:0
# AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT=8191
+# 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'
# LLM_PROVIDER='fireworksai'
# FIREWORKS_AI_LLM_API_KEY='my-fireworks-ai-key'
diff --git a/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx
index 779d487b929..ec4a3c6f747 100644
--- a/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx
+++ b/frontend/src/components/LLMSelection/AwsBedrockLLMOptions/index.jsx
@@ -175,7 +175,7 @@ export default function AwsBedrockLLMOptions({ settings }) {
type="number"
name="AwsBedrockLLMTokenLimit"
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="Content window limit (eg: 4096)"
+ placeholder="Content window limit (eg: 8192)"
min={1}
onScroll={(e) => e.target.blur()}
defaultValue={settings?.AwsBedrockLLMTokenLimit}
@@ -183,6 +183,22 @@ export default function AwsBedrockLLMOptions({ settings }) {
autoComplete="off"
/>
+
+
+ e.target.blur()}
+ defaultValue={settings?.AwsBedrockLLMMaxOutputTokens}
+ required={true}
+ autoComplete="off"
+ />
+
>
)}
diff --git a/server/.env.example b/server/.env.example
index 0c219e9324d..51f8c05d4ef 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -105,6 +105,16 @@ SIG_SALT='salt' # Please generate random string at least 32 chars long.
# COHERE_API_KEY=
# COHERE_MODEL_PREF='command-r'
+# LLM_PROVIDER='bedrock'
+# AWS_BEDROCK_LLM_ACCESS_KEY_ID=
+# AWS_BEDROCK_LLM_ACCESS_KEY=
+# AWS_BEDROCK_LLM_REGION=us-west-2
+# AWS_BEDROCK_LLM_MODEL_PREFERENCE=meta.llama3-1-8b-instruct-v1:0
+# AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT=8191
+# 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'
+
# LLM_PROVIDER='apipie'
# APIPIE_LLM_API_KEY='sk-123abc'
# APIPIE_LLM_MODEL_PREF='openrouter/llama-3.1-8b-instruct'
diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js
index 0ac29f19564..566632aaf83 100644
--- a/server/models/systemSettings.js
+++ b/server/models/systemSettings.js
@@ -540,7 +540,10 @@ const SystemSettings = {
AwsBedrockLLMSessionToken: !!process.env.AWS_BEDROCK_LLM_SESSION_TOKEN,
AwsBedrockLLMRegion: process.env.AWS_BEDROCK_LLM_REGION,
AwsBedrockLLMModel: process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE,
- AwsBedrockLLMTokenLimit: process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT,
+ AwsBedrockLLMTokenLimit:
+ process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT || 8192,
+ AwsBedrockLLMMaxOutputTokens:
+ process.env.AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS || 4096,
// Cohere API Keys
CohereApiKey: !!process.env.COHERE_API_KEY,
diff --git a/server/utils/AiProviders/bedrock/index.js b/server/utils/AiProviders/bedrock/index.js
index c5a9b8dbb68..0fd60c7f72a 100644
--- a/server/utils/AiProviders/bedrock/index.js
+++ b/server/utils/AiProviders/bedrock/index.js
@@ -12,14 +12,18 @@ const {
LLMPerformanceMonitor,
} = require("../../helpers/chat/LLMPerformanceMonitor");
const { v4: uuidv4 } = require("uuid");
+const {
+ DEFAULT_MAX_OUTPUT_TOKENS,
+ DEFAULT_CONTEXT_WINDOW_TOKENS,
+ SUPPORTED_CONNECTION_METHODS,
+ getImageFormatFromMime,
+ base64ToUint8Array,
+} = require("./utils");
class AWSBedrockLLM {
/**
- * These models do not support system prompts
- * It is not explicitly stated but it is observed that they do not use the system prompt
- * in their responses and will crash when a system prompt is provided.
- * We can add more models to this list as we discover them or new models are added.
- * We may want to extend this list or make a user-config if using custom bedrock models.
+ * List of Bedrock models observed to not support system prompts when using the Converse API.
+ * @type {string[]}
*/
noSystemPromptModels = [
"amazon.titan-text-express-v1",
@@ -27,32 +31,46 @@ class AWSBedrockLLM {
"cohere.command-text-v14",
"cohere.command-light-text-v14",
"us.deepseek.r1-v1:0",
+ // Add other models here if identified
];
+ /**
+ * Initializes the AWS Bedrock LLM connector.
+ * @param {object | null} [embedder=null] - An optional embedder instance. Defaults to NativeEmbedder.
+ * @param {string | null} [modelPreference=null] - Optional model ID override. Defaults to environment variable.
+ * @throws {Error} If required environment variables are missing or invalid.
+ */
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.");
-
- if (!process.env.AWS_BEDROCK_LLM_ACCESS_KEY)
- throw new Error("No AWS Bedrock LLM access key was set.");
+ const requiredEnvVars = [
+ "AWS_BEDROCK_LLM_ACCESS_KEY_ID",
+ "AWS_BEDROCK_LLM_ACCESS_KEY",
+ "AWS_BEDROCK_LLM_REGION",
+ "AWS_BEDROCK_LLM_MODEL_PREFERENCE",
+ ];
- if (!process.env.AWS_BEDROCK_LLM_REGION)
- throw new Error("No AWS Bedrock LLM region was set.");
+ // Validate required environment variables
+ for (const envVar of requiredEnvVars) {
+ if (!process.env[envVar])
+ throw new Error(`Required environment variable ${envVar} is not 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."
+ "AWS_BEDROCK_LLM_SESSION_TOKEN is not set for sessionToken authentication method."
);
+ }
this.model =
modelPreference || process.env.AWS_BEDROCK_LLM_MODEL_PREFERENCE;
+
+ const contextWindowLimit = this.promptWindowLimit();
this.limits = {
- history: this.promptWindowLimit() * 0.15,
- system: this.promptWindowLimit() * 0.15,
- user: this.promptWindowLimit() * 0.7,
+ history: Math.floor(contextWindowLimit * 0.15),
+ system: Math.floor(contextWindowLimit * 0.15),
+ user: Math.floor(contextWindowLimit * 0.7),
};
this.bedrockClient = new BedrockRuntimeClient({
@@ -69,156 +87,304 @@ class AWSBedrockLLM {
this.embedder = embedder ?? new NativeEmbedder();
this.defaultTemp = 0.7;
this.#log(
- `Loaded with model: ${this.model}. Will communicate with AWS Bedrock using ${this.authMethod} authentication.`
+ `Initialized with model: ${this.model}. Auth: ${this.authMethod}. Context Window: ${contextWindowLimit}.`
);
}
/**
- * 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"}
+ * Gets the configured AWS authentication method ('iam' or 'sessionToken').
+ * Defaults to 'iam' if the environment variable is invalid.
+ * @returns {"iam" | "sessionToken"} The authentication method.
*/
get authMethod() {
const method = process.env.AWS_BEDROCK_LLM_CONNECTION_METHOD || "iam";
- if (!["iam", "sessionToken"].includes(method)) return "iam";
- return method;
+ return SUPPORTED_CONNECTION_METHODS.includes(method) ? method : "iam";
}
+ /**
+ * Appends context texts to a string with standard formatting.
+ * @param {string[]} contextTexts - An array of context text snippets.
+ * @returns {string} Formatted context string or empty string if no context provided.
+ * @private
+ */
#appendContext(contextTexts = []) {
- if (!contextTexts || !contextTexts.length) return "";
+ if (!contextTexts?.length) return "";
return (
"\nContext:\n" +
contextTexts
- .map((text, i) => {
- return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
- })
+ .map((text, i) => `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`)
.join("")
);
}
+ /**
+ * Internal logging helper with provider prefix.
+ * @param {string} text - The log message.
+ * @param {...any} args - Additional arguments to log.
+ * @private
+ */
#log(text, ...args) {
console.log(`\x1b[32m[AWSBedrock]\x1b[0m ${text}`, ...args);
}
+ /**
+ * Internal logging helper with provider prefix for static methods.
+ * @private
+ */
+ static #slog(text, ...args) {
+ console.log(`\x1b[32m[AWSBedrock]\x1b[0m ${text}`, ...args);
+ }
+
+ /**
+ * Indicates if the provider supports streaming responses.
+ * @returns {boolean} True.
+ */
streamingEnabled() {
return "streamGetChatCompletion" in this;
}
- static promptWindowLimit(_modelName) {
- const limit = process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT || 8191;
- if (!limit || isNaN(Number(limit)))
- throw new Error("No valid token context limit was set.");
- return Number(limit);
+ /**
+ * @static
+ * Gets the total prompt window limit (total context window: input + output) from the environment variable.
+ * This value is used for calculating input limits, NOT for setting the max output tokens in API calls.
+ * @returns {number} The total context window token limit. Defaults to 8191.
+ */
+ static promptWindowLimit() {
+ const limit =
+ process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT ??
+ DEFAULT_CONTEXT_WINDOW_TOKENS;
+ const numericLimit = Number(limit);
+ if (isNaN(numericLimit) || numericLimit <= 0) {
+ this.#slog(
+ `[AWSBedrock ERROR] Invalid AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT found: "${limitSourceValue}". Must be a positive number - returning default ${DEFAULT_CONTEXT_WINDOW_TOKENS}.`
+ );
+ return DEFAULT_CONTEXT_WINDOW_TOKENS;
+ }
+ return numericLimit;
}
- // Ensure the user set a value for the token limit
- // and if undefined - assume 4096 window.
+ /**
+ * Gets the total prompt window limit (total context window) for the current model instance.
+ * @returns {number} The token limit.
+ */
promptWindowLimit() {
- const limit = process.env.AWS_BEDROCK_LLM_MODEL_TOKEN_LIMIT || 8191;
- if (!limit || isNaN(Number(limit)))
- throw new Error("No valid token context limit was set.");
- return Number(limit);
+ return AWSBedrockLLM.promptWindowLimit();
}
- async isValidChatCompletionModel(_ = "") {
+ /**
+ * Gets the maximum number of tokens the model should generate in its response.
+ * Reads from the AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS environment variable or uses a default.
+ * This is distinct from the total context window limit.
+ * @returns {number} The maximum output tokens limit for API calls.
+ */
+ getMaxOutputTokens() {
+ const outputLimitSource = process.env.AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS;
+ if (isNaN(Number(outputLimitSource))) {
+ this.#log(
+ `[AWSBedrock ERROR] Invalid AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS found: "${outputLimitSource}". Must be a positive number - returning default ${DEFAULT_MAX_OUTPUT_TOKENS}.`
+ );
+ return DEFAULT_MAX_OUTPUT_TOKENS;
+ }
+
+ const numericOutputLimit = Number(outputLimitSource);
+ if (numericOutputLimit <= 0) {
+ this.#log(
+ `[AWSBedrock ERROR] Invalid AWS_BEDROCK_LLM_MAX_OUTPUT_TOKENS found: "${outputLimitSource}". Must be a greater than 0 - returning default ${DEFAULT_MAX_OUTPUT_TOKENS}.`
+ );
+ return DEFAULT_MAX_OUTPUT_TOKENS;
+ }
+
+ return numericOutputLimit;
+ }
+
+ /** Stubbed method for compatibility with LLM interface. */
+ async isValidChatCompletionModel(_modelName = "") {
return true;
}
/**
- * Generates appropriate content array for a message + attachments.
- * TODO: Implement this - attachments are not supported yet for Bedrock.
- * @param {{userPrompt:string, attachments: import("../../helpers").Attachment[]}}
- * @returns {string|object[]}
+ * Validates attachments array and returns a new array with valid attachments.
+ * @param {Array<{contentString: string, mime: string}>} attachments - Array of attachments.
+ * @returns {Array<{image: {format: string, source: {bytes: Uint8Array}}>} Array of valid attachments.
+ * @private
+ */
+ #validateAttachments(attachments = []) {
+ if (!Array.isArray(attachments) || !attachments?.length) return [];
+ const validAttachments = [];
+ for (const attachment of attachments) {
+ if (
+ !attachment ||
+ typeof attachment.mime !== "string" ||
+ typeof attachment.contentString !== "string"
+ ) {
+ this.#log("Skipping invalid attachment object.", attachment);
+ continue;
+ }
+
+ // Strip data URI prefix (e.g., "data:image/png;base64,")
+ const base64Data = attachment.contentString.replace(
+ /^data:image\/\w+;base64,/,
+ ""
+ );
+
+ const format = getImageFormatFromMime(attachment.mime);
+ const attachmentInfo = {
+ valid: format !== null,
+ format,
+ imageBytes: base64ToUint8Array(base64Data),
+ };
+
+ if (!attachmentInfo.valid) {
+ this.#log(
+ `Skipping attachment with unsupported/invalid MIME type: ${attachment.mime}`
+ );
+ continue;
+ }
+
+ validAttachments.push({
+ image: {
+ format: format,
+ source: { bytes: attachmentInfo.imageBytes },
+ },
+ });
+ }
+
+ return validAttachments;
+ }
+
+ /**
+ * Generates the Bedrock Converse API content array for a message,
+ * processing text and formatting valid image attachments.
+ * @param {object} params
+ * @param {string} params.userPrompt - The text part of the message.
+ * @param {Array<{contentString: string, mime: string}>} params.attachments - Array of attachments for the message.
+ * @returns {Array