Skip to content
Draft
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
4 changes: 3 additions & 1 deletion api/server/services/Config/EndpointService.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ module.exports = {
EModelEndpoint.azureAssistants,
),
[EModelEndpoint.bedrock]: generateConfig(
process.env.BEDROCK_AWS_SECRET_ACCESS_KEY ?? process.env.BEDROCK_AWS_DEFAULT_REGION,
process.env.BEDROCK_AWS_SECRET_ACCESS_KEY ??
process.env.BEDROCK_AWS_BEARER_TOKEN ??
process.env.BEDROCK_AWS_DEFAULT_REGION,
),
/* key will be part of separate config */
[EModelEndpoint.agents]: generateConfig('true', undefined, EModelEndpoint.agents),
Expand Down
17 changes: 17 additions & 0 deletions api/server/services/Config/getEndpointsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ async function getEndpointsConfig(req) {
};
}

// Add individual credential flags for Bedrock
if (mergedConfig[EModelEndpoint.bedrock]) {
const userProvideAccessKeyId = process.env.BEDROCK_AWS_ACCESS_KEY_ID === 'user_provided';
const userProvideSecretAccessKey =
process.env.BEDROCK_AWS_SECRET_ACCESS_KEY === 'user_provided';
const userProvideSessionToken = process.env.BEDROCK_AWS_SESSION_TOKEN === 'user_provided';
const userProvideBearerToken = process.env.BEDROCK_AWS_BEARER_TOKEN === 'user_provided';

mergedConfig[EModelEndpoint.bedrock] = {
...mergedConfig[EModelEndpoint.bedrock],
userProvideAccessKeyId,
userProvideSecretAccessKey,
userProvideSessionToken,
userProvideBearerToken,
};
}

const endpointsConfig = orderEndpointsConfig(mergedConfig);

await cache.set(CacheKeys.ENDPOINT_CONFIG, endpointsConfig);
Expand Down
35 changes: 26 additions & 9 deletions api/server/services/Endpoints/bedrock/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,51 @@ const {
bedrockOutputParser,
removeNullishValues,
} = require('librechat-data-provider');
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
const { getUserKeyValues, checkUserKeyExpiry } = require('~/server/services/UserService');

const getOptions = async ({ req, overrideModel, endpointOption }) => {
const {
BEDROCK_AWS_SECRET_ACCESS_KEY,
BEDROCK_AWS_ACCESS_KEY_ID,
BEDROCK_AWS_SESSION_TOKEN,
BEDROCK_AWS_BEARER_TOKEN,
BEDROCK_REVERSE_PROXY,
BEDROCK_AWS_DEFAULT_REGION,
PROXY,
} = process.env;
const expiresAt = req.body.key;
const isUserProvided = BEDROCK_AWS_SECRET_ACCESS_KEY === AuthType.USER_PROVIDED;
const isUserProvided =
BEDROCK_AWS_SECRET_ACCESS_KEY === AuthType.USER_PROVIDED ||
BEDROCK_AWS_BEARER_TOKEN === AuthType.USER_PROVIDED;

let userValues = null;
if (isUserProvided) {
if (expiresAt) {
checkUserKeyExpiry(expiresAt, EModelEndpoint.bedrock);
}
userValues = await getUserKeyValues({ userId: req.user.id, name: EModelEndpoint.bedrock });
}

let credentials = isUserProvided
? await getUserKey({ userId: req.user.id, name: EModelEndpoint.bedrock })
: {
accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID,
secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY,
...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }),
};
let credentials;
if (isUserProvided) {
credentials = JSON.parse(userValues.apiKey);
} else if (BEDROCK_AWS_BEARER_TOKEN) {
credentials = { bearerToken: BEDROCK_AWS_BEARER_TOKEN };
} else {
credentials = {
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.bearerToken &&
(credentials.accessKeyId === undefined || credentials.accessKeyId === '') &&
(credentials.secretAccessKey === undefined || credentials.secretAccessKey === '')
) {
Expand Down
20 changes: 20 additions & 0 deletions client/src/components/Chat/Menus/Endpoints/DialogManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ const DialogManager = ({
endpointType={getEndpointField(endpointsConfig, keyDialogEndpoint, 'type')}
onOpenChange={onOpenChange}
userProvideURL={getEndpointField(endpointsConfig, keyDialogEndpoint, 'userProvideURL')}
userProvideAccessKeyId={getEndpointField(
endpointsConfig,
keyDialogEndpoint,
'userProvideAccessKeyId',
)}
userProvideSecretAccessKey={getEndpointField(
endpointsConfig,
keyDialogEndpoint,
'userProvideSecretAccessKey',
)}
userProvideSessionToken={getEndpointField(
endpointsConfig,
keyDialogEndpoint,
'userProvideSessionToken',
)}
userProvideBearerToken={getEndpointField(
endpointsConfig,
keyDialogEndpoint,
'userProvideBearerToken',
)}
/>
)}
</>
Expand Down
111 changes: 111 additions & 0 deletions client/src/components/Input/SetKeyDialog/BedrockConfig.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React from 'react';
import { EModelEndpoint } from 'librechat-data-provider';
import { useFormContext, Controller } from 'react-hook-form';
import { useLocalize } from '~/hooks';
import InputWithLabel from './InputWithLabel';

const BedrockConfig = ({
userProvideAccessKeyId,
userProvideSecretAccessKey,
userProvideSessionToken,
userProvideBearerToken,
}: {
endpoint: EModelEndpoint | string;
userProvideURL?: boolean | null;
userProvideAccessKeyId?: boolean;
userProvideSecretAccessKey?: boolean;
userProvideSessionToken?: boolean;
userProvideBearerToken?: boolean;
}) => {
const { control } = useFormContext();
const localize = useLocalize();

const renderFields = () => {
const fields: React.ReactNode[] = [];

if (userProvideAccessKeyId) {
fields.push(
<Controller
key="bedrockAccessKeyId"
name="bedrockAccessKeyId"
control={control}
render={({ field }) => (
<InputWithLabel
id="bedrockAccessKeyId"
{...field}
label={localize('com_endpoint_config_bedrock_access_key_id')}
labelClassName="mb-1"
inputClassName="mb-2"
/>
)}
/>,
);
}

if (userProvideSecretAccessKey) {
if (fields.length > 0) fields.push(<div key="spacer1" className="mt-3" />);
fields.push(
<Controller
key="bedrockSecretAccessKey"
name="bedrockSecretAccessKey"
control={control}
render={({ field }) => (
<InputWithLabel
id="bedrockSecretAccessKey"
{...field}
label={localize('com_endpoint_config_bedrock_secret_access_key')}
labelClassName="mb-1"
inputClassName="mb-2"
/>
)}
/>,
);
}

if (userProvideSessionToken) {
if (fields.length > 0) fields.push(<div key="spacer2" className="mt-3" />);
fields.push(
<Controller
key="bedrockSessionToken"
name="bedrockSessionToken"
control={control}
render={({ field }) => (
<InputWithLabel
id="bedrockSessionToken"
{...field}
label={localize('com_endpoint_config_bedrock_session_token')}
labelClassName="mb-1"
inputClassName="mb-2"
/>
)}
/>,
);
}

if (userProvideBearerToken) {
if (fields.length > 0) fields.push(<div key="spacer3" className="mt-3" />);
fields.push(
<Controller
key="bedrockBearerToken"
name="bedrockBearerToken"
control={control}
render={({ field }) => (
<InputWithLabel
id="bedrockBearerToken"
{...field}
label={localize('com_endpoint_config_bedrock_bearer_token')}
labelClassName="mb-1"
inputClassName="mb-2"
/>
)}
/>,
);
}

return <>{fields}</>;
};

return <form className="flex-wrap">{renderFields()}</form>;
};

export default BedrockConfig;
Loading