Introduction
In this article, you'll learn how to create a .NET console application that streams conversations using the Amazon Bedrock Converse API. The application utilizes the Anthropic Claude 3 Sonnet model as an example, demonstrating how to generate responses through the ConverseStream operation.
Prerequisites
- Create an AWS account and log in. Ensure the IAM user you use has sufficient permissions to make necessary AWS service calls and manage AWS resources.
- Download and install the AWS Command Line Interface (CLI).
- Configure the AWS CLI.
- Download and install Visual Studio or Visual Studio Code.
- Download and install .NET 8.0 SDK
- Access to Amazon Bedrock foundation model
Tools
Visual Studio 2022
Steps Involved
Perform the following steps to create a .NET console application in Visual Studio 2022 to send input text, inference parameters, and additional model-specific parameters.
- Open Visual Studio 2022.
- Click File -> New -> Project.
- Select the Console App template. Click Next.
- Enter the project name and click Next.
- Select the .NET 8.0 framework. Click Create.
- Add the following NuGet packages.
AWSSDK.BedrockRuntime
- Open Program. cs and replace the code with the following.
using Amazon;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
using Amazon.Runtime.Documents;
namespace AmazonBedrockConverseStreamApp
{
internal class Program
{
/// <summary>
/// Main entry point of the console application.
/// </summary>
/// <param name="args">Command-line arguments.</param>
static async Task Main(string[] args)
{
// Fetch the model ID from an environment variable or use a default value.
var modelId = Environment.GetEnvironmentVariable("MODEL_ID") ??
"anthropic.claude-3-sonnet-20240229-v1:0";
// Fetch the system prompt text from an environment variable or use a default value.
var systemPromptText = Environment.GetEnvironmentVariable("SYSTEM_PROMPT_TEXT") ??
"You are an app that creates playlists for a radio station that plays rock and pop music. Only return song names and the artist.";
// Fetch the AWS region from an environment variable or use a default value.
var awsRegion = Environment.GetEnvironmentVariable("AWS_REGION") ?? "us-east-1";
// Store the initial message text in a variable for easy modification and reuse.
var initialMessageText = "Create a list of 3 pop songs.";
// Configure the Amazon Bedrock Runtime client with the specified AWS region.
var config = new AmazonBedrockRuntimeConfig
{
RegionEndpoint = RegionEndpoint.GetBySystemName(awsRegion)
};
// Create the system prompt as a list of SystemContentBlock objects.
// This will guide the model on how to structure its responses.
var systemPrompts = new List<SystemContentBlock>
{
new SystemContentBlock { Text = systemPromptText }
};
// Define the initial message sent to the model by the user.
var initialMessage = new Message
{
Role = "user", // Indicates the role of the sender (in this case, the user).
Content = new List<ContentBlock>
{
new ContentBlock { Text = initialMessageText }
}
};
// Create the Bedrock Runtime client using the provided configuration.
using var bedrockClient = new AmazonBedrockRuntimeClient(config);
try
{
// Stream the conversation with the specified model, messages, and system prompts.
await StreamConversationAsync(bedrockClient, modelId, new List<Message> { initialMessage }, systemPrompts);
}
catch (AmazonBedrockRuntimeException ex)
{
// Handle AWS-specific runtime exceptions and log them to the console.
Console.WriteLine($"AWS Bedrock Runtime error: {ex.Message}");
}
catch (Exception ex)
{
// Handle any unexpected exceptions and log them to the console.
Console.WriteLine($"Unexpected error: {ex.Message}");
}
}
/// <summary>
/// Streams a conversation with the specified model, handling and displaying each event.
/// </summary>
/// <param name="bedrockClient">The Bedrock Runtime client used to communicate with the service.</param>
/// <param name="modelId">The ID of the model to use.</param>
/// <param name="messages">The list of messages to send to the model.</param>
/// <param name="systemPrompts">The list of system prompts to guide the model's responses.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async Task StreamConversationAsync(
IAmazonBedrockRuntime bedrockClient,
string modelId,
List<Message> messages,
List<SystemContentBlock> systemPrompts)
{
// Configure the inference settings for the model, such as temperature, which controls randomness.
var inferenceConfig = new InferenceConfiguration
{
Temperature = 0.5f // A lower value makes the output more deterministic, while a higher value increases variability.
};
// Define additional fields specific to the model, such as the number of top predictions to consider.
var additionalModelFields = new Document(new Dictionary<string, Document>
{
{ "top_k", new Document(200) } // 'top_k' controls the number of highest-probability predictions considered during sampling.
});
// Prepare the request to be sent to the Bedrock service.
var request = new ConverseStreamRequest
{
ModelId = modelId,
Messages = messages,
System = systemPrompts,
InferenceConfig = inferenceConfig,
AdditionalModelRequestFields = additionalModelFields
};
// Send the request to the Bedrock service and start receiving a stream of events.
var response = await bedrockClient.ConverseStreamAsync(request);
// Access the stream of events from the response.
var stream = response.Stream;
if (stream != null)
{
// Iterate over each event in the stream and handle it accordingly.
foreach (var eventItem in stream)
{
switch (eventItem)
{
case MessageStartEvent messageStart:
// Log the start of a message, including the role of the sender.
Console.WriteLine($"\nRole: {messageStart.Role}");
break;
case ContentBlockDeltaEvent contentBlockDelta:
// Append the content text to the output as it streams in.
if (contentBlockDelta.Delta?.Text != null)
{
Console.Write(contentBlockDelta.Delta.Text);
}
break;
case MessageStopEvent messageStop:
// Log the reason why the message streaming stopped.
Console.WriteLine($"\nStop reason: {messageStop.StopReason}");
break;
case ConverseStreamMetadataEvent metadata:
// Log token usage and latency information.
if (metadata.Usage != null)
{
Console.WriteLine("\nToken usage");
Console.WriteLine($"Input tokens: {metadata.Usage.InputTokens}");
Console.WriteLine($"Output tokens: {metadata.Usage.OutputTokens}");
Console.WriteLine($"Total tokens: {metadata.Usage.TotalTokens}");
}
if (metadata.Metrics != null)
{
Console.WriteLine($"Latency: {metadata.Metrics.LatencyMs} milliseconds");
}
break;
default:
// Handle unknown or unexpected event types.
Console.WriteLine("Unknown event type received.");
break;
}
}
}
// Log that the conversation has completed.
Console.WriteLine($"\nFinished streaming messages with model {modelId}.");
}
}
}
- Press F5 to run the application. You should see the below streaming conversation output.
Summary
This article describes how to create a .NET console application that streams conversations using the Amazon Bedrock Converse API.