mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-11 20:14:24 +01:00
* feat: add support for inferenceProfiles mapping * fix: remove friendly name since api requires actual model id for validation alongside inference profile * docs: more generic description in docs * chore: address comments * chore: update peer dependency versions in package.json - Bump @aws-sdk/client-bedrock-runtime from ^3.941.0 to ^3.970.0 - Update @librechat/agents from ^3.0.78 to ^3.0.79 * fix: update @librechat/agents dependency to version 3.0.80 * test: add unit tests for inference profile configuration in initializeBedrock function - Introduced tests to validate the applicationInferenceProfile setting based on model configuration. - Ensured correct handling of environment variables and fallback scenarios for inference profile ARNs. - Added cases for empty inferenceProfiles and absence of bedrock config to confirm expected behavior. * fix: update bedrock endpoint schema reference in config - Changed the bedrock endpoint reference from baseEndpointSchema to bedrockEndpointSchema for improved clarity and accuracy in configuration. * test: add unit tests for Bedrock endpoint configuration - Introduced tests to validate the configuration of Bedrock endpoints with models and inference profiles. - Added scenarios for both complete and minimal configurations to ensure expected behavior. - Enhanced coverage for the handling of inference profiles without a models array. --------- Co-authored-by: Danny Avila <danny@librechat.ai>
179 lines
5.9 KiB
TypeScript
179 lines
5.9 KiB
TypeScript
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
|
import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
|
|
import {
|
|
AuthType,
|
|
EModelEndpoint,
|
|
extractEnvVariable,
|
|
bedrockInputParser,
|
|
bedrockOutputParser,
|
|
removeNullishValues,
|
|
} from 'librechat-data-provider';
|
|
import type {
|
|
BaseInitializeParams,
|
|
InitializeResultBase,
|
|
BedrockCredentials,
|
|
GuardrailConfiguration,
|
|
InferenceProfileConfig,
|
|
} from '~/types';
|
|
import { checkUserKeyExpiry } from '~/utils';
|
|
|
|
/**
|
|
* Initializes Bedrock endpoint configuration.
|
|
*
|
|
* This module handles configuration for AWS Bedrock endpoints, including support for
|
|
* HTTP/HTTPS proxies and reverse proxies.
|
|
*
|
|
* Proxy Support:
|
|
* - When the PROXY environment variable is set, creates a custom BedrockRuntimeClient
|
|
* with an HttpsProxyAgent to route all Bedrock API calls through the specified proxy
|
|
* - The custom client is fully configured with credentials, region, and endpoint,
|
|
* and is passed directly to ChatBedrockConverse via the 'client' parameter
|
|
*
|
|
* Reverse Proxy Support:
|
|
* - When BEDROCK_REVERSE_PROXY is set, routes Bedrock API calls through a custom endpoint
|
|
* - Works with or without the PROXY setting
|
|
*
|
|
* Without Proxy:
|
|
* - Credentials and endpoint configuration are passed separately to ChatBedrockConverse,
|
|
* which creates its own BedrockRuntimeClient internally
|
|
*
|
|
* @param params - Configuration parameters
|
|
* @returns Promise resolving to Bedrock configuration options
|
|
* @throws Error if credentials are not provided when required
|
|
*/
|
|
export async function initializeBedrock({
|
|
req,
|
|
endpoint,
|
|
model_parameters,
|
|
db,
|
|
}: BaseInitializeParams): Promise<InitializeResultBase> {
|
|
void endpoint;
|
|
const appConfig = req.config;
|
|
const bedrockConfig = appConfig?.endpoints?.[EModelEndpoint.bedrock] as
|
|
| ({
|
|
guardrailConfig?: GuardrailConfiguration;
|
|
inferenceProfiles?: InferenceProfileConfig;
|
|
} & Record<string, unknown>)
|
|
| undefined;
|
|
|
|
const {
|
|
BEDROCK_AWS_SECRET_ACCESS_KEY,
|
|
BEDROCK_AWS_ACCESS_KEY_ID,
|
|
BEDROCK_AWS_SESSION_TOKEN,
|
|
BEDROCK_REVERSE_PROXY,
|
|
BEDROCK_AWS_DEFAULT_REGION,
|
|
PROXY,
|
|
} = process.env;
|
|
|
|
const { key: expiresAt } = req.body;
|
|
const isUserProvided = BEDROCK_AWS_SECRET_ACCESS_KEY === AuthType.USER_PROVIDED;
|
|
|
|
let credentials: BedrockCredentials | undefined = isUserProvided
|
|
? await db
|
|
.getUserKey({ userId: req.user?.id ?? '', name: EModelEndpoint.bedrock })
|
|
.then((key) => JSON.parse(key) as BedrockCredentials)
|
|
: {
|
|
accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID,
|
|
secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY,
|
|
...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }),
|
|
};
|
|
|
|
if (!credentials) {
|
|
throw new Error('Bedrock credentials not provided. Please provide them again.');
|
|
}
|
|
|
|
if (
|
|
!isUserProvided &&
|
|
(credentials.accessKeyId === undefined || credentials.accessKeyId === '') &&
|
|
(credentials.secretAccessKey === undefined || credentials.secretAccessKey === '')
|
|
) {
|
|
credentials = undefined;
|
|
}
|
|
|
|
if (expiresAt && isUserProvided) {
|
|
checkUserKeyExpiry(expiresAt, EModelEndpoint.bedrock);
|
|
}
|
|
|
|
const requestOptions: Record<string, unknown> = {
|
|
model: model_parameters?.model as string | undefined,
|
|
region: BEDROCK_AWS_DEFAULT_REGION,
|
|
};
|
|
|
|
const configOptions: Record<string, unknown> = {};
|
|
|
|
const llmConfig = bedrockOutputParser(
|
|
bedrockInputParser.parse(
|
|
removeNullishValues({
|
|
...requestOptions,
|
|
...(model_parameters ?? {}),
|
|
}),
|
|
),
|
|
) as InitializeResultBase['llmConfig'] & {
|
|
model?: string;
|
|
region?: string;
|
|
client?: BedrockRuntimeClient;
|
|
credentials?: BedrockCredentials;
|
|
endpointHost?: string;
|
|
guardrailConfig?: GuardrailConfiguration;
|
|
applicationInferenceProfile?: string;
|
|
};
|
|
|
|
if (bedrockConfig?.guardrailConfig) {
|
|
llmConfig.guardrailConfig = bedrockConfig.guardrailConfig;
|
|
}
|
|
|
|
const model = model_parameters?.model as string | undefined;
|
|
if (model && bedrockConfig?.inferenceProfiles?.[model]) {
|
|
const applicationInferenceProfile = extractEnvVariable(bedrockConfig.inferenceProfiles[model]);
|
|
llmConfig.applicationInferenceProfile = applicationInferenceProfile;
|
|
}
|
|
|
|
/** Only include credentials if they're complete (accessKeyId and secretAccessKey are both set) */
|
|
const hasCompleteCredentials =
|
|
credentials &&
|
|
typeof credentials.accessKeyId === 'string' &&
|
|
credentials.accessKeyId !== '' &&
|
|
typeof credentials.secretAccessKey === 'string' &&
|
|
credentials.secretAccessKey !== '';
|
|
|
|
if (PROXY) {
|
|
const proxyAgent = new HttpsProxyAgent(PROXY);
|
|
|
|
// Create a custom BedrockRuntimeClient with proxy-enabled request handler.
|
|
// ChatBedrockConverse will use this pre-configured client directly instead of
|
|
// creating its own. Credentials are only set if explicitly provided; otherwise
|
|
// the AWS SDK's default credential provider chain is used (instance profiles,
|
|
// AWS profiles, environment variables, etc.)
|
|
const customClient = new BedrockRuntimeClient({
|
|
region: (llmConfig.region as string) ?? BEDROCK_AWS_DEFAULT_REGION,
|
|
...(hasCompleteCredentials && {
|
|
credentials: credentials as { accessKeyId: string; secretAccessKey: string },
|
|
}),
|
|
requestHandler: new NodeHttpHandler({
|
|
httpAgent: proxyAgent,
|
|
httpsAgent: proxyAgent,
|
|
}),
|
|
...(BEDROCK_REVERSE_PROXY && {
|
|
endpoint: `https://${BEDROCK_REVERSE_PROXY}`,
|
|
}),
|
|
});
|
|
|
|
llmConfig.client = customClient;
|
|
} else {
|
|
// When not using a proxy, let ChatBedrockConverse create its own client
|
|
// by providing credentials and endpoint separately
|
|
if (credentials) {
|
|
llmConfig.credentials = credentials;
|
|
}
|
|
|
|
if (BEDROCK_REVERSE_PROXY) {
|
|
llmConfig.endpointHost = BEDROCK_REVERSE_PROXY;
|
|
}
|
|
}
|
|
|
|
return {
|
|
llmConfig,
|
|
configOptions,
|
|
};
|
|
}
|