Skip to content

Commit

Permalink
AI Chatbot using AWS Bedrock (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakejscott authored Aug 27, 2024
1 parent 5563af9 commit 68e2c50
Show file tree
Hide file tree
Showing 21 changed files with 1,015 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Prerequisites:
* [ActivitySimple](src/ActivitySimple) - Simple workflow that runs simple activities.
* [ActivityWorker](src/ActivityWorker) - Use .NET activities from a workflow in another language.
* [AspNet](src/AspNet) - Demonstration of a generic host worker and an ASP.NET workflow starter.
* [Bedrock](src/Bedrock) - Orchestrate a chatbot with Amazon Bedrock.
* [ClientMtls](src/ClientMtls) - How to use client certificate authentication, e.g. for Temporal Cloud.
* [ContextPropagation](src/ContextPropagation) - Context propagation via interceptors.
* [CounterInterceptor](src/CounterInterceptor/) - Simple Workflow and Client Interceptors example.
Expand Down
31 changes: 31 additions & 0 deletions TemporalioSamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.CounterIn
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Patching", "src\Patching\TemporalioSamples.Patching.csproj", "{751E0AF8-62EE-4220-A571-D1C53DD0D45A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.SignalsQueries", "src\SignalsQueries\TemporalioSamples.SignalsQueries.csproj", "{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bedrock", "Bedrock", "{5339989C-3791-4D75-A9F1-42620C443D4A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Bedrock.Basic", "src\Bedrock\Basic\TemporalioSamples.Bedrock.Basic.csproj", "{CC3487CB-F795-4CA9-A4F6-28C145AE383B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Bedrock.SignalsAndQueries", "src\Bedrock\SignalsAndQueries\TemporalioSamples.Bedrock.SignalsAndQueries.csproj", "{4E69DE72-E972-4155-B4D1-ABFE0B054ECF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Bedrock.Entity", "src\Bedrock\Entity\TemporalioSamples.Bedrock.Entity.csproj", "{AE18875F-B7D2-4A3C-8784-15B76277D508}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -167,6 +177,22 @@ Global
{751E0AF8-62EE-4220-A571-D1C53DD0D45A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{751E0AF8-62EE-4220-A571-D1C53DD0D45A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{751E0AF8-62EE-4220-A571-D1C53DD0D45A}.Release|Any CPU.Build.0 = Release|Any CPU
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079}.Release|Any CPU.Build.0 = Release|Any CPU
{CC3487CB-F795-4CA9-A4F6-28C145AE383B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC3487CB-F795-4CA9-A4F6-28C145AE383B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC3487CB-F795-4CA9-A4F6-28C145AE383B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC3487CB-F795-4CA9-A4F6-28C145AE383B}.Release|Any CPU.Build.0 = Release|Any CPU
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF}.Release|Any CPU.Build.0 = Release|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -199,5 +225,10 @@ Global
{FAF5984A-5B1C-4686-B056-A4F2AC0E8EB7} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{F9C44936-8BF9-4919-BB66-8F1888E22AEB} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{751E0AF8-62EE-4220-A571-D1C53DD0D45A} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{5339989C-3791-4D75-A9F1-42620C443D4A} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{CC3487CB-F795-4CA9-A4F6-28C145AE383B} = {5339989C-3791-4D75-A9F1-42620C443D4A}
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF} = {5339989C-3791-4D75-A9F1-42620C443D4A}
{AE18875F-B7D2-4A3C-8784-15B76277D508} = {5339989C-3791-4D75-A9F1-42620C443D4A}
EndGlobalSection
EndGlobal
39 changes: 39 additions & 0 deletions src/Bedrock/Basic/BedrockActivities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
using Temporalio.Activities;

namespace TemporalioSamples.Bedrock.Basic;

public class BedrockActivities(IAmazonBedrockRuntime bedrock)
{
public record PromptArgs(string Prompt);

public record PromptResult(string Response);

[Activity]
public async Task<PromptResult> PromptBedrockAsync(PromptArgs args)
{
var body = JsonSerializer.Serialize(new
{
prompt = args.Prompt,
max_gen_len = 512,
temperature = 0.1,
top_p = 0.2,
});

var request = new InvokeModelRequest
{
ModelId = "meta.llama3-1-70b-instruct-v1:0",
Accept = "application/json",
ContentType = "application/json",
Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(body)),
};

var response = await bedrock.InvokeModelAsync(request);
var modelResponse = await JsonNode.ParseAsync(response.Body);
var responseText = modelResponse?["generation"]?.ToString() ?? string.Empty;
return new(responseText);
}
}
29 changes: 29 additions & 0 deletions src/Bedrock/Basic/BedrockWorkflow.workflow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Extensions.Logging;
using Temporalio.Workflows;

namespace TemporalioSamples.Bedrock.Basic;

[Workflow]
public class BedrockWorkflow
{
public record WorkflowArgs(string Prompt);

public record WorkflowResult(string Response);

[WorkflowRun]
public async Task<WorkflowResult> RunAsync(WorkflowArgs args)
{
Workflow.Logger.LogInformation("Prompt: {Prompt}", args.Prompt);

var promptResult = await Workflow.ExecuteActivityAsync(
(BedrockActivities activities) => activities.PromptBedrockAsync(new(args.Prompt)),
new()
{
StartToCloseTimeout = TimeSpan.FromSeconds(20),
});

Workflow.Logger.LogInformation("Response:\n{Response}", promptResult.Response);

return new(promptResult.Response);
}
}
69 changes: 69 additions & 0 deletions src/Bedrock/Basic/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Amazon.BedrockRuntime;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Temporalio.Client;
using Temporalio.Extensions.Hosting;
using TemporalioSamples.Bedrock.Basic;

async Task RunWorkerAsync()
{
var builder = Host.CreateApplicationBuilder(args);

builder.Logging.
SetMinimumLevel(LogLevel.Information).
AddSimpleConsole(options => options.SingleLine = true);

builder.Services.AddSingleton<IAmazonBedrockRuntime>(_ => new AmazonBedrockRuntimeClient());

builder.Services.
AddHostedTemporalWorker(clientTargetHost: "localhost:7233", clientNamespace: "default", taskQueue: "basic-bedrock-task-queue").
AddSingletonActivities<BedrockActivities>().
AddWorkflow<BedrockWorkflow>();

var app = builder.Build();
await app.RunAsync();
}

async Task SendMessageAsync()
{
var prompt = args.ElementAtOrDefault(1);
if (prompt is null)
{
Console.WriteLine("Usage: dotnet run send-message '<prompt>'");
Console.WriteLine("Example: dotnet run send-message 'What animals are marsupials?'");
return;
}

var client = await CreateClientAsync();
var workflowId = "basic-bedrock-workflow";

// Start the workflow
var result = await client.ExecuteWorkflowAsync(
(BedrockWorkflow workflow) =>
workflow.RunAsync(new(prompt)),
new WorkflowOptions(workflowId, "basic-bedrock-task-queue"));

Console.WriteLine($"Result: {result.Response}");
}

async Task<ITemporalClient> CreateClientAsync() =>
await TemporalClient.ConnectAsync(new("localhost:7233")
{
LoggerFactory = LoggerFactory.Create(builder =>
builder.
AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] ").
SetMinimumLevel(LogLevel.Information)),
});

switch (args.ElementAtOrDefault(0))
{
case "worker":
await RunWorkerAsync();
break;
case "send-message":
await SendMessageAsync();
break;
default:
throw new ArgumentException("Must pass 'worker' or 'send-message' as the single argument");
}
10 changes: 10 additions & 0 deletions src/Bedrock/Basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Basic Amazon Bedrock workflow

A basic Bedrock workflow. Starts a workflow with a prompt, generates a response and ends the workflow.

To run, first see `Bedrock` [README.md](../README.md) for prerequisites specific to this sample. Once set up, run the following from this directory:

1. Run the worker: `dotnet run worker`
2. In another terminal run the client with a prompt:

e.g. `dotnet run send-message 'What animals are marsupials?'`
11 changes: 11 additions & 0 deletions src/Bedrock/Basic/TemporalioSamples.Bedrock.Basic.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.BedrockRuntime" Version="3.7.401.11" />
</ItemGroup>

</Project>
39 changes: 39 additions & 0 deletions src/Bedrock/Entity/BedrockActivities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
using Temporalio.Activities;

namespace TemporalioSamples.Bedrock.Entity;

public class BedrockActivities(IAmazonBedrockRuntime bedrock)
{
public record PromptArgs(string Prompt);

public record PromptResult(string Response);

[Activity]
public async Task<PromptResult> PromptBedrockAsync(PromptArgs args)
{
var body = JsonSerializer.Serialize(new
{
prompt = args.Prompt,
max_gen_len = 512,
temperature = 0.1,
top_p = 0.2,
});

var request = new InvokeModelRequest
{
ModelId = "meta.llama3-1-70b-instruct-v1:0",
Accept = "application/json",
ContentType = "application/json",
Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(body)),
};

var response = await bedrock.InvokeModelAsync(request);
var modelResponse = await JsonNode.ParseAsync(response.Body);
var responseText = modelResponse?["generation"]?.ToString() ?? string.Empty;
return new(responseText);
}
}
Loading

0 comments on commit 68e2c50

Please sign in to comment.