Reputation: 757
I am writing a web app in vanilla javascript that uses AWS Bedrock. If I use the Bedrock CLI, all is well - I have a credentials file in .aws
in my home directory and the necessary IAM Permissions to access the model. Similarly if I hard code the AWS credentials in the call to the API in my javascript code:
const client = new BedrockRuntimeClient({
region: "eu-west-2",
credentials: {
secretAccessKey: "My secret key",
accessKeyId: "My access key"
}
});
this works too.
However, if I remove the hard coded credentials from the code, as of course I should, where should I put the credentials file so that my web app can access it? It won't find it in my home directory, because the server knows nothing about my home directory. The advice on the web is very confusing, not helped by the change from version 2 to version 3 of the AWS-SDK, thus making suggestions that work for version 2 no longer applicable (I am using version 3).
To complicate matters more, I am developing the app on MacOS, but intending to deploy it on an EC2 instance and would like any solution to be portable between these two. For both, the server is Apache2.4.
Upvotes: 0
Views: 107
Reputation: 757
I eventually found how to do this, with a lot of help from AlanKrantas's example : use AWS Cognito to generate a temporary identity. Here is some example code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Simple Web Page</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
line-height: 1.5;
}
.container {
max-width: 600px;
margin: auto;
}
textarea {
width: 100%;
height: 100px;
margin-bottom: 10px;
}
button {
display: block;
margin-bottom: 10px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #0056b3;
}
.output {
padding: 10px;
border: 1px solid #ccc;
background-color: #f9f9f9;
min-height: 50px;
}
</style>
<script type="module" src="app.mjs"></script>
</head>
<body>
<div class="container">
<h1>Simple Llambda 3 Chat</h1>
<textarea id="inputText" placeholder="Enter some text..."></textarea>
<button onclick="submitText()">Submit</button>
<div class="output" id="outputText"></div>
</div>
<script>
async function submitText() {
const input = document.getElementById('inputText').value
const output = document.getElementById('outputText')
output.textContent = input ? await chat(input) : 'Please enter some text.'
}
</script>
</body>
</html>
and
/*
* Invoke AWS Bedrock LLM model in guest mode using AWS SDK for JavaScript
*
* This is a simplified version of the code example in the blog post:
* https://github.com/alankrantas/aws-sdk-js-bedrock-llm-example/blob/main/README.md
* See the blog post for details about how to set up the AWS environment.
*/
// bedrock resource
const region = "AWS REGION"
const cognitoIdentityPoolId = `{REGION}:00000000-0000-0000-0000-000000000000`
const bedrockRoleArn =
"arn:aws:iam::000000000000:role/service-role/{AWSRoleForBedrockName}"
// select model
const modelId = "AWS MODEL ID"
// ================================================================================
import {
CognitoIdentityClient,
GetIdCommand,
GetOpenIdTokenCommand,
} from "@aws-sdk/client-cognito-identity"
import {
STSClient,
AssumeRoleWithWebIdentityCommand,
} from "@aws-sdk/client-sts"
import {
BedrockRuntimeClient,
ConverseCommand,
} from "@aws-sdk/client-bedrock-runtime"
export async function chat(userMessage) {
try {
const config = { region: region }
// get guest identity id
const cognitoClient = new CognitoIdentityClient(config)
const idResponse = await cognitoClient.send(
new GetIdCommand({ IdentityPoolId: cognitoIdentityPoolId })
)
const id = idResponse.IdentityId
// get access token
const tokenResponse = await cognitoClient.send(
new GetOpenIdTokenCommand({ IdentityId: id })
)
const token = tokenResponse.Token
// get credential with session policy
const stsClient = new STSClient(config)
const credentialsResponse = await stsClient.send(
new AssumeRoleWithWebIdentityCommand({
RoleArn: bedrockRoleArn,
WebIdentityToken: token,
RoleSessionName: "bedrock_session",
})
)
const credentials = credentialsResponse.Credentials
// get bedrock client using the credential
const bedrockClient = new BedrockRuntimeClient({
region: region,
credentials: {
accessKeyId: credentials.AccessKeyId,
secretAccessKey: credentials.SecretAccessKey,
expiration: credentials.Expiration,
sessionToken: credentials.SessionToken,
},
})
// set the system prompt and the user's query
const systemPrompt = [{ text: "answer concisely in no more than 200 words. Use Markdown formatting for your response" }]
const conversation = [
{
role: "user",
content: [{ text: userMessage }],
},
]
// Create a command with the model ID, the message, and a basic configuration.
const command = new ConverseCommand({
modelId,
messages: conversation,
system: systemPrompt,
inferenceConfig: { maxTokens: 512, temperature: 0.5, topP: 0.9 },
})
// Send the command to the model and wait for the response
const response = await bedrockClient.send(command)
// Extract and print the response text.
const responseText = response.output.message.content[0].text
console.log(response)
console.log(responseText)
return responseText
} catch (err) {
console.log(`ERROR: Can't invoke '${modelId}'. Reason: ${err}`)
}
}
window.chat = chat
It is a shame that AWS doesn't provide an example such as this in its documentation. It would have saved me a lot of time!
Upvotes: 0