AWS bedrock documentation change
Summary
Added JavaScript code example demonstrating tool use scenario with Amazon Bedrock's Converse API and weather tool integration
Security assessment
The changes add a functional example of using external weather API integration but contain no explicit security vulnerability fixes or security documentation. While the code makes external API calls, there's no evidence of addressing specific security vulnerabilities like input validation or SSRF protection in the example.
Diff
diff --git a/bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_Scenario_ToolUse_section.md b/bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_Scenario_ToolUse_section.md index ce0ebc708..ba560884f 100644 --- a//bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_Scenario_ToolUse_section.md +++ b//bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_Scenario_ToolUse_section.md @@ -1055,0 +1056,329 @@ The Converse API action with a tool configuration. +JavaScript + + +**SDK for JavaScript (v3)** + + +###### Note + +There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/bedrock-runtime/scenarios/converse_tool_scenario#code-examples). + +The primary execution of the scenario flow. This scenario orchestrates the conversation between the user, the Amazon Bedrock Converse API, and a weather tool. + + + /* Before running this JavaScript code example, set up your development environment, including your credentials. + This demo illustrates a tool use scenario using Amazon Bedrock's Converse API and a weather tool. + The script interacts with a foundation model on Amazon Bedrock to provide weather information based on user + input. It uses the Open-Meteo API (https://open-meteo.com) to retrieve current weather data for a given location.*/ + + import { + Scenario, + ScenarioAction, + ScenarioInput, + ScenarioOutput, + } from "@aws-doc-sdk-examples/lib/scenario/index.js"; + import { + BedrockRuntimeClient, + ConverseCommand, + } from "@aws-sdk/client-bedrock-runtime"; + + import { parseArgs } from "node:util"; + import { fileURLToPath } from "node:url"; + import { dirname } from "node:path"; + const __filename = fileURLToPath(import.meta.url); + import data from "./questions.json" with { type: "json" }; + import toolConfig from "./tool_config.json" with { type: "json" }; + + const systemPrompt = [ + { + text: + "You are a weather assistant that provides current weather data for user-specified locations using only\n" + + "the Weather_Tool, which expects latitude and longitude. Infer the coordinates from the location yourself.\n" + + "If the user provides coordinates, infer the approximate location and refer to it in your response.\n" + + "To use the tool, you strictly apply the provided tool specification.\n" + + "If the user specifies a state, country, or region, infer the locations of cities within that state.\n" + + "\n" + + "- Explain your step-by-step process, and give brief updates before each step.\n" + + "- Only use the Weather_Tool for data. Never guess or make up information. \n" + + "- Repeat the tool use for subsequent requests if necessary.\n" + + "- If the tool errors, apologize, explain weather is unavailable, and suggest other options.\n" + + "- Report temperatures in °C (°F) and wind in km/h (mph). Keep weather reports concise. Sparingly use\n" + + " emojis where appropriate.\n" + + "- Only respond to weather queries. Remind off-topic users of your purpose. \n" + + "- Never claim to search online, access external data, or use tools besides Weather_Tool.\n" + + "- Complete the entire process until you have all required data before sending the complete response.", + }, + ]; + const tools_config = toolConfig; + + /// Starts the conversation with the user and handles the interaction with Bedrock. + async function askQuestion(userMessage) { + // The maximum number of recursive calls allowed in the tool use function. + // This helps prevent infinite loops and potential performance issues. + const max_recursions = 5; + const messages = [ + { + role: "user", + content: [{ text: userMessage }], + }, + ]; + try { + const response = await SendConversationtoBedrock(messages); + await ProcessModelResponseAsync(response, messages, max_recursions); + } catch (error) { + console.log("error ", error); + } + } + + // Sends the conversation, the system prompt, and the tool spec to Amazon Bedrock, and returns the response. + // param "messages" - The conversation history including the next message to send. + // return - The response from Amazon Bedrock. + async function SendConversationtoBedrock(messages) { + const bedRockRuntimeClient = new BedrockRuntimeClient({ + region: "us-east-1", + }); + try { + const modelId = "amazon.nova-lite-v1:0"; + const response = await bedRockRuntimeClient.send( + new ConverseCommand({ + modelId: modelId, + messages: messages, + system: systemPrompt, + toolConfig: tools_config, + }), + ); + return response; + } catch (caught) { + if (caught.name === "ModelNotReady") { + console.log( + "`${caught.name}` - Model not ready, please wait and try again.", + ); + throw caught; + } + if (caught.name === "BedrockRuntimeException") { + console.log( + '`${caught.name}` - "Error occurred while sending Converse request.', + ); + throw caught; + } + } + } + + // Processes the response received via Amazon Bedrock and performs the necessary actions based on the stop reason. + // param "response" - The model's response returned via Amazon Bedrock. + // param "messages" - The conversation history. + // param "max_recursions" - The maximum number of recursive calls allowed. + async function ProcessModelResponseAsync(response, messages, max_recursions) { + if (max_recursions <= 0) { + await HandleToolUseAsync(response, messages); + } + if (response.stopReason === "tool_use") { + await HandleToolUseAsync(response, messages, max_recursions - 1); + } + if (response.stopReason === "end_turn") { + const messageToPrint = response.output.message.content[0].text; + console.log(messageToPrint.replace(/<[^>]+>/g, "")); + } + } + // Handles the tool use case by invoking the specified tool and sending the tool's response back to Bedrock. + // The tool response is appended to the conversation, and the conversation is sent back to Amazon Bedrock for further processing. + // param "response" - the model's response containing the tool use request. + // param "messages" - the conversation history. + // param "max_recursions" - The maximum number of recursive calls allowed. + async function HandleToolUseAsync(response, messages, max_recursions) { + const toolResultFinal = []; + try { + const output_message = response.output.message; + messages.push(output_message); + const toolRequests = output_message.content; + const toolMessage = toolRequests[0].text; + console.log(toolMessage.replace(/<[^>]+>/g, "")); + for (const toolRequest of toolRequests) { + if (Object.hasOwn(toolRequest, "toolUse")) { + const toolUse = toolRequest.toolUse; + const latitude = toolUse.input.latitude; + const longitude = toolUse.input.longitude; + const toolUseID = toolUse.toolUseId; + console.log( + `Requesting tool ${toolUse.name}, Tool use id ${toolUseID}`, + ); + if (toolUse.name === "Weather_Tool") { + try { + const current_weather = await callWeatherTool( + longitude, + latitude, + ).then((current_weather) => current_weather); + const currentWeather = current_weather; + const toolResult = { + toolResult: { + toolUseId: toolUseID, + content: [{ json: currentWeather }], + }, + }; + toolResultFinal.push(toolResult); + } catch (err) { + console.log("An error occurred. ", err); + } + } + } + } + + const toolResultMessage = { + role: "user", + content: toolResultFinal, + }; + messages.push(toolResultMessage); + // Send the conversation to Amazon Bedrock + await ProcessModelResponseAsync( + await SendConversationtoBedrock(messages), + messages, + ); + } catch (error) { + console.log("An error occurred. ", error); + } + } + // Call the Weathertool. + // param = longitude of location + // param = latitude of location + async function callWeatherTool(longitude, latitude) { + // Open-Meteo API endpoint + const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true`; + + // Fetch the weather data. + return fetch(apiUrl) + .then((response) => { + return response.json().then((current_weather) => {