├── doc └── img │ ├── ChatPlayground.png │ └── ImagePlayground.png ├── src ├── DotnetFMPlayground.App │ ├── wwwroot │ │ ├── scroll.js │ │ ├── favicon.ico │ │ ├── css │ │ │ ├── open-iconic │ │ │ │ ├── font │ │ │ │ │ ├── fonts │ │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ │ └── open-iconic.woff │ │ │ │ │ └── css │ │ │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── FONT-LICENSE │ │ │ └── app.css │ │ └── index.html │ ├── Resources │ │ ├── Fonts │ │ │ └── OpenSans-Regular.ttf │ │ ├── AppIcon │ │ │ ├── appicon.svg │ │ │ └── appiconfg.svg │ │ ├── Raw │ │ │ └── AboutAssets.txt │ │ ├── Splash │ │ │ └── splash.svg │ │ └── Images │ │ │ └── dotnet_bot.svg │ ├── Properties │ │ └── launchSettings.json │ ├── Components │ │ ├── InferenceParametersBase.razor │ │ ├── TitanTextInferenceParameters.razor │ │ ├── Jurassic2TextInferenceParameters.razor │ │ ├── LlamaTextInferenceParameters.razor │ │ ├── MistralTextInferenceParameters.razor │ │ ├── ClaudeInferenceParameters.razor │ │ ├── CommandTextInferenceParameters.razor │ │ └── ModelConfigurator.razor │ ├── MainPage.xaml.cs │ ├── App.xaml.cs │ ├── Platforms │ │ ├── Android │ │ │ ├── Resources │ │ │ │ └── values │ │ │ │ │ └── colors.xml │ │ │ ├── AndroidManifest.xml │ │ │ ├── MainApplication.cs │ │ │ └── MainActivity.cs │ │ ├── iOS │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── MacCatalyst │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── Windows │ │ │ ├── App.xaml │ │ │ ├── app.manifest │ │ │ ├── App.xaml.cs │ │ │ └── Package.appxmanifest │ │ └── Tizen │ │ │ ├── Main.cs │ │ │ └── tizen-manifest.xml │ ├── _Imports.razor │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── NavMenu.razor.css │ │ ├── MainLayout.razor.css │ │ └── NavMenu.razor │ ├── Main.razor │ ├── MainPage.xaml │ ├── App.xaml │ ├── Pages │ │ ├── ListFoundationModels.razor │ │ ├── ImagePlayground.razor │ │ ├── EmbeddingPlayground.razor │ │ ├── TextPlayground.razor │ │ ├── ChatPlayground.razor │ │ └── VoiceChatPlayground.razor │ ├── MauiProgram.cs │ └── DotnetFMPlayground.App.csproj ├── DotnetFMPlayground.Core │ ├── Models │ │ ├── Prompt.cs │ │ ├── PromptItemType.cs │ │ ├── InferenceParameters │ │ │ └── BaseInferenceParameters.cs │ │ ├── ModelResponse │ │ │ ├── IFoundationModelResponse.cs │ │ │ ├── AnthropicClaudeResponse.cs │ │ │ ├── AmazonTitanTextStreamingResponse.cs │ │ │ ├── AmazonTitanTextResponse.cs │ │ │ ├── StableDiffusionXLResponse.cs │ │ │ ├── CohereCommandResponse.cs │ │ │ └── AI21LabsJurassic2Response.cs │ │ ├── PromptItem.cs │ │ └── ModelIds.cs │ ├── DotnetFMPlayground.Core.csproj │ ├── Builders │ │ ├── Commands │ │ │ ├── InvokeModelRequestBuildCommand.cs │ │ │ ├── CohereCommandRequestBuildCommand.cs │ │ │ ├── AI21LabsJurassic2RequestBuildCommand.cs │ │ │ ├── StabilityAIStableDiffusionRequestBuildCommand.cs │ │ │ ├── AmazonTitanRequestBuildCommand.cs │ │ │ └── AnthropicClaudeRequestBuildCommand.cs │ │ ├── FoundationModelResponseBuilder.cs │ │ └── InvokeModelRequestBuilder.cs │ └── AmazonBedrockRuntimeClientExtension.cs └── DotnetFMPlayground.sln ├── CODE_OF_CONDUCT.md ├── LICENSE ├── .gitattributes ├── CONTRIBUTING.md ├── README.md └── .gitignore /doc/img/ChatPlayground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/doc/img/ChatPlayground.png -------------------------------------------------------------------------------- /doc/img/ImagePlayground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/doc/img/ImagePlayground.png -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/scroll.js: -------------------------------------------------------------------------------- 1 | function scrollToElement(id) { 2 | document.getElementById(id).scrollIntoView() 3 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/Fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/Resources/Fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Windows Machine": { 4 | "commandName": "MsixPackage", 5 | "nativeDebugging": false 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/build-on-aws/dotnet-fm-playground/HEAD/src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/InferenceParametersBase.razor: -------------------------------------------------------------------------------- 1 | @inherits MudComponentBase 2 | 3 | 4 | @code { 5 | public IDictionary InferenceParameters { get; } = new Dictionary(); 6 | } 7 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetFMPlayground.App 2 | { 3 | public partial class MainPage : ContentPage 4 | { 5 | public MainPage() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/AppIcon/appicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DotnetFMPlayground.App 2 | { 3 | public partial class App : Application 4 | { 5 | public App() 6 | { 7 | InitializeComponent(); 8 | 9 | MainPage = new MainPage(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #512BD4 4 | #2B0B98 5 | #2B0B98 6 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace DotnetFMPlayground.App 4 | { 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } 10 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/Prompt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DotnetFMPlayground.Core.Models 9 | { 10 | public class Prompt : List { } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/MacCatalyst/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace DotnetFMPlayground.App 4 | { 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/PromptItemType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotnetFMPlayground.Core.Models 8 | { 9 | public enum PromptItemType 10 | { 11 | User = 0, 12 | FMAnswer = 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/InferenceParameters/BaseInferenceParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotnetFMPlayground.Core.Models.InferenceParameters 8 | { 9 | public class BaseInferenceParameters : Dictionary 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Windows/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.AspNetCore.Components.Web.Virtualization 6 | @using Microsoft.JSInterop 7 | @using DotnetFMPlayground.App 8 | @using DotnetFMPlayground.App.Shared 9 | @using DotnetFMPlayground.Core 10 | @using MudBlazor -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/IFoundationModelResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotnetFMPlayground.Core.Models.ModelResponse 8 | { 9 | public interface IFoundationModelResponse 10 | { 11 | public string? GetResponse(); 12 | 13 | public string? GetStopReason(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 4 | 5 | 6 | 7 |
8 | 11 | 12 |
13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Tizen/Main.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Maui; 2 | using Microsoft.Maui.Hosting; 3 | using System; 4 | 5 | namespace DotnetFMPlayground.App 6 | { 7 | internal class Program : MauiApplication 8 | { 9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 10 | 11 | static void Main(string[] args) 12 | { 13 | var app = new Program(); 14 | app.Run(args); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Main.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Sorry, there's nothing at this address.

9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Android/MainApplication.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Runtime; 3 | 4 | namespace DotnetFMPlayground.App 5 | { 6 | [Application] 7 | public class MainApplication : MauiApplication 8 | { 9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership) 10 | : base(handle, ownership) 11 | { 12 | } 13 | 14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | 5 | namespace DotnetFMPlayground.App 6 | { 7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] 8 | public class MainActivity : MauiAppCompatActivity 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/iOS/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace DotnetFMPlayground.App 5 | { 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/DotnetFMPlayground.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 12 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/MacCatalyst/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace DotnetFMPlayground.App 5 | { 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/PromptItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotnetFMPlayground.Core.Models 8 | { 9 | public class PromptItem 10 | { 11 | public PromptItemType Type { get; } 12 | 13 | public string Prompt { get; set; } 14 | 15 | public PromptItem(PromptItemType type, string prompt) 16 | { 17 | Type = type; 18 | Prompt = prompt; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/InvokeModelRequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using DotnetFMPlayground.Core.Models; 3 | using DotnetFMPlayground.Core.Models.InferenceParameters; 4 | 5 | namespace DotnetFMPlayground.Core.Builders.Commands 6 | { 7 | internal abstract class InvokeModelRequestBuildCommand 8 | { 9 | internal abstract InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters); 10 | 11 | internal abstract InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/AnthropicClaudeResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DotnetFMPlayground.Core.Models.ModelResponse 4 | { 5 | public class AnthropicClaudeResponse : IFoundationModelResponse 6 | { 7 | [JsonPropertyName("completion")] public string? Completion { get; init; } 8 | 9 | [JsonPropertyName("stop_reason")] public string? StopReason { get; init; } 10 | 11 | [JsonPropertyName("stop")] public string? Stop { get; init; } 12 | 13 | public string? GetResponse() 14 | { 15 | return Completion; 16 | } 17 | 18 | public string? GetStopReason() 19 | { 20 | return StopReason; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/Raw/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories). Deployment of the asset to your application 3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. 4 | 5 | 6 | 7 | These files will be deployed with you package and will be accessible using Essentials: 8 | 9 | async Task LoadMauiAsset() 10 | { 11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); 12 | using var reader = new StreamReader(stream); 13 | 14 | var contents = reader.ReadToEnd(); 15 | } 16 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Windows/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | true/PM 12 | PerMonitorV2, PerMonitor 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Tizen/tizen-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | maui-appicon-placeholder 7 | 8 | 9 | 10 | 11 | http://tizen.org/privilege/internet 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/AmazonTitanTextStreamingResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotnetFMPlayground.Core.Models.ModelResponse 8 | { 9 | public class AmazonTitanTextStreamingResponse : IFoundationModelResponse 10 | { 11 | public string? OutputText { get; set; } 12 | public int TotalOutputTextTokenCount { get; set; } 13 | public int Index { get; set; } 14 | public string? CompletionReason { get; set; } 15 | public int InputTextTokenCount { get; set; } 16 | 17 | public string? GetResponse() 18 | { 19 | return OutputText; 20 | } 21 | 22 | public string? GetStopReason() 23 | { 24 | return CompletionReason; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Windows/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | 3 | // To learn more about WinUI, the WinUI project structure, 4 | // and more about our project templates, see: http://aka.ms/winui-project-info. 5 | 6 | namespace DotnetFMPlayground.App.WinUI 7 | { 8 | /// 9 | /// Provides application-specific behavior to supplement the default Application class. 10 | /// 11 | public partial class App : MauiWinUIApplication 12 | { 13 | /// 14 | /// Initializes the singleton application object. This is the first line of authored code 15 | /// executed, and as such is the logical equivalent of main() or WinMain(). 16 | /// 17 | public App() 18 | { 19 | this.InitializeComponent(); 20 | } 21 | 22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/MacCatalyst/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 10 | UIRequiredDeviceCapabilities 11 | 12 | arm64 13 | 14 | UISupportedInterfaceOrientations 15 | 16 | UIInterfaceOrientationPortrait 17 | UIInterfaceOrientationLandscapeLeft 18 | UIInterfaceOrientationLandscapeRight 19 | 20 | UISupportedInterfaceOrientations~ipad 21 | 22 | UIInterfaceOrientationPortrait 23 | UIInterfaceOrientationPortraitUpsideDown 24 | UIInterfaceOrientationLandscapeLeft 25 | UIInterfaceOrientationLandscapeRight 26 | 27 | XSAppIconAssets 28 | Assets.xcassets/appicon.appiconset 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | #512bdf 10 | White 11 | 12 | 16 | 17 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSRequiresIPhoneOS 6 | 7 | UIDeviceFamily 8 | 9 | 1 10 | 2 11 | 12 | UIRequiredDeviceCapabilities 13 | 14 | arm64 15 | 16 | UISupportedInterfaceOrientations 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationLandscapeLeft 20 | UIInterfaceOrientationLandscapeRight 21 | 22 | UISupportedInterfaceOrientations~ipad 23 | 24 | UIInterfaceOrientationPortrait 25 | UIInterfaceOrientationPortraitUpsideDown 26 | UIInterfaceOrientationLandscapeLeft 27 | UIInterfaceOrientationLandscapeRight 28 | 29 | XSAppIconAssets 30 | Assets.xcassets/appicon.appiconset 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DotnetFMPlayground.App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
Loading...
20 | 21 |
22 | An unhandled error has occurred. 23 | Reload 24 | 🗙 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/AmazonTitanTextResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace DotnetFMPlayground.Core.Models.ModelResponse 9 | { 10 | public class AmazonTitanTextResponse : IFoundationModelResponse 11 | { 12 | public class AmazonTitanTextOutput 13 | { 14 | [JsonPropertyName("tokenCount")] 15 | public int TokenCount { get; set; } 16 | 17 | [JsonPropertyName("outputText")] 18 | public string? OutputText { get; set; } 19 | 20 | [JsonPropertyName("completionReason")] 21 | public string? CompletionReason { get; set; } 22 | } 23 | 24 | [JsonPropertyName("inputTextTokenCount")] 25 | public int InputTextTokenCount { get; set; } 26 | 27 | [JsonPropertyName("results")] 28 | public IEnumerable? Results { get; set; } 29 | 30 | public string? GetResponse() 31 | { 32 | return Results?.FirstOrDefault()?.OutputText; 33 | } 34 | 35 | public string? GetStopReason() 36 | { 37 | return Results?.FirstOrDefault()?.CompletionReason; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/StableDiffusionXLResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace DotnetFMPlayground.Core.Models.ModelResponse 9 | { 10 | public class StableDiffusionXLResponse : IFoundationModelResponse 11 | { 12 | public class StableDiffusionXLGeneratedImage 13 | { 14 | [JsonPropertyName("base64")] 15 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 16 | public string? Base64 { get; set; } 17 | 18 | [JsonPropertyName("finishReason")] 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 20 | public string? FinishReason { get; set; } 21 | 22 | [JsonPropertyName("seed")] 23 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 24 | public long? Seed { get; set; } 25 | } 26 | 27 | [JsonPropertyName("artifacts")] 28 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 29 | public IEnumerable? Artifacts { get; set; } 30 | 31 | public string? GetResponse() 32 | { 33 | return Artifacts?.FirstOrDefault()?.Base64; 34 | } 35 | 36 | public string? GetStopReason() 37 | { 38 | return Artifacts?.FirstOrDefault()?.FinishReason; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/TitanTextInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | @code { 10 | private float? _temperature { get; set; } = null; 11 | private float? _topP { get; set; } = null; 12 | private int? _maxTokenCount { get; set; } = null; 13 | 14 | private void OnTemperatureChanged(float? value) 15 | { 16 | if(value is not null) 17 | { 18 | _temperature = value; 19 | InferenceParameters["temperature"] = value; 20 | } 21 | } 22 | 23 | private void OnTopPChanged(float? value) 24 | { 25 | if (value is not null) 26 | { 27 | _topP = value; 28 | InferenceParameters["topP"] = value; 29 | } 30 | } 31 | 32 | private void OnMaxTokenCountChanged(int? value) 33 | { 34 | if (value is not null) 35 | { 36 | _maxTokenCount = value; 37 | InferenceParameters["maxTokenCount"] = value; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/Jurassic2TextInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | @code { 10 | private float? _temperature { get; set; } = null; 11 | private float? _topP { get; set; } = null; 12 | private int? _maxTokenCount { get; set; } = null; 13 | 14 | private void OnTemperatureChanged(float? value) 15 | { 16 | if(value is not null) 17 | { 18 | _temperature = value; 19 | InferenceParameters["temperature"] = value; 20 | } 21 | } 22 | 23 | private void OnTopPChanged(float? value) 24 | { 25 | if (value is not null) 26 | { 27 | _topP = value; 28 | InferenceParameters["topP"] = value; 29 | } 30 | } 31 | 32 | private void OnMaxTokenCountChanged(int? value) 33 | { 34 | if (value is not null) 35 | { 36 | _maxTokenCount = value; 37 | InferenceParameters["maxTokenCount"] = value; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/LlamaTextInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | @code { 10 | private float? _temperature { get; set; } = null; 11 | private float? _topP { get; set; } = null; 12 | private int? _maxTokenCount { get; set; } = null; 13 | 14 | private void OnTemperatureChanged(float? value) 15 | { 16 | if(value is not null) 17 | { 18 | _temperature = value; 19 | InferenceParameters["temperature"] = value; 20 | } 21 | } 22 | 23 | private void OnTopPChanged(float? value) 24 | { 25 | if (value is not null) 26 | { 27 | _topP = value; 28 | InferenceParameters["topP"] = value; 29 | } 30 | } 31 | 32 | private void OnMaxTokenCountChanged(int? value) 33 | { 34 | if (value is not null) 35 | { 36 | _maxTokenCount = value; 37 | InferenceParameters["maxTokenCount"] = value; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/CohereCommandRequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using DotnetFMPlayground.Core.Models; 3 | using DotnetFMPlayground.Core.Models.InferenceParameters; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json; 9 | using System.Threading.Tasks; 10 | 11 | namespace DotnetFMPlayground.Core.Builders.Commands 12 | { 13 | internal class CohereCommandRequestBuildCommand : InvokeModelRequestBuildCommand 14 | { 15 | internal override InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 16 | { 17 | InvokeModelRequest request = new() 18 | { 19 | ModelId = modelId, 20 | ContentType = "application/json" 21 | }; 22 | 23 | request.Accept = "application/json"; 24 | request.Body = new MemoryStream( 25 | Encoding.UTF8.GetBytes( 26 | JsonSerializer.Serialize(new 27 | { 28 | prompt = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString(), 29 | }) 30 | ) 31 | ); 32 | 33 | return request; 34 | } 35 | 36 | internal override InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/AI21LabsJurassic2RequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using DotnetFMPlayground.Core.Models; 3 | using DotnetFMPlayground.Core.Models.InferenceParameters; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json; 9 | using System.Threading.Tasks; 10 | 11 | namespace DotnetFMPlayground.Core.Builders.Commands 12 | { 13 | internal class AI21LabsJurassic2RequestBuildCommand : InvokeModelRequestBuildCommand 14 | { 15 | internal override InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 16 | { 17 | InvokeModelRequest request = new() 18 | { 19 | ModelId = modelId, 20 | ContentType = "application/json" 21 | }; 22 | 23 | request.Accept = "application/json"; 24 | request.Body = new MemoryStream( 25 | Encoding.UTF8.GetBytes( 26 | JsonSerializer.Serialize(new 27 | { 28 | prompt = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString(), 29 | }) 30 | ) 31 | ); 32 | 33 | return request; 34 | } 35 | 36 | internal override InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/StabilityAIStableDiffusionRequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using Amazon.Runtime.Internal; 3 | using DotnetFMPlayground.Core.Models; 4 | using DotnetFMPlayground.Core.Models.InferenceParameters; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net.Mime; 9 | using System.Text; 10 | using System.Text.Json; 11 | using System.Threading.Tasks; 12 | 13 | namespace DotnetFMPlayground.Core.Builders.Commands 14 | { 15 | internal class StabilityAIStableDiffusionRequestBuildCommand : InvokeModelRequestBuildCommand 16 | { 17 | internal override InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 18 | { 19 | InvokeModelRequest request = new() 20 | { 21 | ModelId = modelId, 22 | ContentType = "application/json" 23 | }; 24 | 25 | request.Accept = "application/json"; 26 | request.Body = new MemoryStream( 27 | Encoding.UTF8.GetBytes( 28 | JsonSerializer.Serialize(new 29 | { 30 | text_prompts = prompt.Select(x => new { text = x.Prompt }).ToArray() 31 | }) 32 | ) 33 | ); 34 | 35 | return request; 36 | } 37 | 38 | internal override InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 39 | { 40 | throw new NotSupportedException($"ModelId {modelId} doesn't support streaming"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row, article { 72 | padding-left: 2rem !important; 73 | padding-right: 1.5rem !important; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/Splash/splash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/AppIcon/appiconfg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/ListFoundationModels.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @page "/foundation-models" 3 | @using Amazon.Bedrock.Model; 4 | @using System.Text; 5 | @using Amazon.Bedrock; 6 | @using DotnetFMPlayground.Core.Models; 7 | @inject AmazonBedrockClient BedrockClient 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @code { 26 | private IEnumerable foundationModels; 27 | 28 | protected override async Task OnInitializedAsync() 29 | { 30 | foundationModels = (await BedrockClient.ListFoundationModelsAsync(new ListFoundationModelsRequest())).ModelSummaries; 31 | await base.OnInitializedAsync(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/MauiProgram.cs: -------------------------------------------------------------------------------- 1 | using Amazon; 2 | using Amazon.Bedrock; 3 | using Amazon.BedrockRuntime; 4 | using Amazon.BedrockAgent; 5 | using Amazon.BedrockAgentRuntime; 6 | using Microsoft.Extensions.Logging; 7 | using MudBlazor.Services; 8 | 9 | namespace DotnetFMPlayground.App 10 | { 11 | public static class MauiProgram 12 | { 13 | public static MauiApp CreateMauiApp() 14 | { 15 | var builder = MauiApp.CreateBuilder(); 16 | builder 17 | .UseMauiApp() 18 | .ConfigureFonts(fonts => 19 | { 20 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); 21 | }); 22 | 23 | builder.Services.AddMauiBlazorWebView(); 24 | builder.Services.AddMudServices(); 25 | builder.Services.AddSpeechRecognitionServices(); 26 | builder.Services.AddSpeechSynthesisServices(); 27 | 28 | #if DEBUG 29 | builder.Services.AddBlazorWebViewDeveloperTools(); 30 | builder.Logging.AddDebug(); 31 | #endif 32 | builder.Services.AddSingleton( 33 | new AmazonBedrockRuntimeClient(new AmazonBedrockRuntimeConfig() 34 | { 35 | RegionEndpoint = RegionEndpoint.USEast1 36 | })); 37 | builder.Services.AddSingleton( 38 | new AmazonBedrockClient(new AmazonBedrockConfig() 39 | { 40 | RegionEndpoint = RegionEndpoint.USEast1 41 | })); 42 | 43 | builder.Services.AddSingleton( 44 | new AmazonBedrockAgentRuntimeClient() 45 | ); 46 | 47 | builder.Services.AddSingleton(); 48 | 49 | return builder.Build(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetFMPlayground.Core", "DotnetFMPlayground.Core\DotnetFMPlayground.Core.csproj", "{6EF3D9F5-7233-4FE4-B228-DC8A9FB079F5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetFMPlayground.App", "DotnetFMPlayground.App\DotnetFMPlayground.App.csproj", "{B75EEBD2-9A47-4845-970B-77BC648F25F8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6EF3D9F5-7233-4FE4-B228-DC8A9FB079F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {6EF3D9F5-7233-4FE4-B228-DC8A9FB079F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {6EF3D9F5-7233-4FE4-B228-DC8A9FB079F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {6EF3D9F5-7233-4FE4-B228-DC8A9FB079F5}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 23 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {B75EEBD2-9A47-4845-970B-77BC648F25F8}.Release|Any CPU.Deploy.0 = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | GlobalSection(ExtensibilityGlobals) = postSolution 31 | SolutionGuid = {EA67ABA6-B275-4959-8DA6-23D42939F292} 32 | EndGlobalSection 33 | EndGlobal 34 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/AmazonBedrockRuntimeClientExtension.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime; 2 | using DotnetFMPlayground.Core.Builders; 3 | using DotnetFMPlayground.Core.Models; 4 | using DotnetFMPlayground.Core.Models.InferenceParameters; 5 | using DotnetFMPlayground.Core.Models.ModelResponse; 6 | 7 | namespace DotnetFMPlayground.Core 8 | { 9 | public static class AmazonBedrockRuntimeClientExtension 10 | { 11 | public static async Task InvokeModelAsync(this AmazonBedrockRuntimeClient client, string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters, CancellationToken cancellationToken = default) 12 | { 13 | var invokeModelResponse = await client.InvokeModelAsync(InvokeModelRequestBuilder.Build(modelId, prompt, inferenceParameters), cancellationToken); 14 | return await FoundationModelResponseBuilder.Build(modelId, invokeModelResponse.Body); 15 | } 16 | 17 | public static async Task InvokeModelWithResponseStreamAsync(this AmazonBedrockRuntimeClient client, string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters, Func chunkReceived, Func exceptionReceived, CancellationToken cancellationToken = default) 18 | { 19 | var response = await client.InvokeModelWithResponseStreamAsync(InvokeModelRequestBuilder.BuildWithResponseStream(modelId, prompt, inferenceParameters), cancellationToken); 20 | 21 | response.Body.ChunkReceived += async (sender, e) => 22 | { 23 | var chunk = await FoundationModelResponseBuilder.Build(modelId, e.EventStreamEvent.Bytes, true); 24 | await chunkReceived(chunk?.GetResponse()); 25 | }; 26 | 27 | response.Body.ExceptionReceived += async (sender, e) => 28 | { 29 | await exceptionReceived(e.EventStreamException.Message); 30 | }; 31 | 32 | response.Body.StartProcessing(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Platforms/Windows/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | $placeholder$ 15 | User Name 16 | $placeholder$.png 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/MistralTextInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | @code { 11 | private float? _temperature { get; set; } = null; 12 | private float? _topP { get; set; } = null; 13 | private int? _topK { get; set; } = null; 14 | private int? _maxTokensToSample { get; set; } = null; 15 | 16 | private void OnTemperatureChanged(float? value) 17 | { 18 | if(value is not null) 19 | { 20 | _temperature = value; 21 | InferenceParameters["temperature"] = value; 22 | } 23 | } 24 | 25 | private void OnTopPChanged(float? value) 26 | { 27 | if (value is not null) 28 | { 29 | _topP = value; 30 | InferenceParameters["top_p"] = value; 31 | } 32 | } 33 | 34 | private void OnTopKChanged(int? value) 35 | { 36 | if (value is not null) 37 | { 38 | _topK = value; 39 | InferenceParameters["top_k"] = value; 40 | } 41 | } 42 | 43 | private void OnTopMaxTokensToSampleChanged(int? value) 44 | { 45 | if (value is not null) 46 | { 47 | _maxTokensToSample = value; 48 | InferenceParameters["max_tokens_to_sample"] = value; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/ClaudeInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | @code { 11 | private float? _temperature { get; set; } = null; 12 | private float? _topP { get; set; } = null; 13 | private int? _topK { get; set; } = null; 14 | private int? _maxTokensToSample { get; set; } = null; 15 | 16 | private void OnTemperatureChanged(float? value) 17 | { 18 | if(value is not null) 19 | { 20 | _temperature = value; 21 | InferenceParameters["temperature"] = value; 22 | } 23 | } 24 | 25 | private void OnTopPChanged(float? value) 26 | { 27 | if (value is not null) 28 | { 29 | _topP = value; 30 | InferenceParameters["top_p"] = value; 31 | } 32 | } 33 | 34 | private void OnTopKChanged(int? value) 35 | { 36 | if (value is not null) 37 | { 38 | _topK = value; 39 | InferenceParameters["top_k"] = value; 40 | } 41 | } 42 | 43 | private void OnTopMaxTokensToSampleChanged(int? value) 44 | { 45 | if (value is not null) 46 | { 47 | _maxTokensToSample = value; 48 | InferenceParameters["max_tokens_to_sample"] = value; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/CommandTextInferenceParameters.razor: -------------------------------------------------------------------------------- 1 | @inherits InferenceParametersBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | @code { 11 | private float? _temperature { get; set; } = null; 12 | private float? _topP { get; set; } = null; 13 | private int? _topK { get; set; } = null; 14 | private int? _maxTokensToSample { get; set; } = null; 15 | 16 | private void OnTemperatureChanged(float? value) 17 | { 18 | if (value is not null) 19 | { 20 | _temperature = value; 21 | InferenceParameters["temperature"] = value; 22 | } 23 | } 24 | 25 | private void OnTopPChanged(float? value) 26 | { 27 | if (value is not null) 28 | { 29 | _topP = value; 30 | InferenceParameters["top_p"] = value; 31 | } 32 | } 33 | 34 | private void OnTopKChanged(int? value) 35 | { 36 | if (value is not null) 37 | { 38 | _topK = value; 39 | InferenceParameters["top_k"] = value; 40 | } 41 | } 42 | 43 | private void OnTopMaxTokensToSampleChanged(int? value) 44 | { 45 | if (value is not null) 46 | { 47 | _maxTokensToSample = value; 48 | InferenceParameters["max_tokens_to_sample"] = value; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/CohereCommandResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Nodes; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | 9 | namespace DotnetFMPlayground.Core.Models.ModelResponse 10 | { 11 | public class CohereCommandResponse : IFoundationModelResponse 12 | { 13 | [JsonPropertyName("generations")] public IEnumerable? Generations { get; set; } 14 | 15 | [JsonPropertyName("id")] public string? Id { get; init; } 16 | 17 | [JsonPropertyName("prompt")] public string? Prompt { get; init; } 18 | 19 | public class Generation 20 | { 21 | [JsonPropertyName("finish_reason")] public FinishReason? FinishReason { get; init; } 22 | 23 | [JsonPropertyName("id")] public string? Id { get; init; } 24 | 25 | [JsonPropertyName("text")] public string? Text { get; init; } 26 | 27 | [JsonPropertyName("prompt")] public string? Prompt { get; init; } 28 | 29 | [JsonPropertyName("likelihood")] public float? Likelihood { get; init; } 30 | 31 | [JsonPropertyName("token_likelihoods")] public IEnumerable? TokenLikelihoods { get; init; } 32 | 33 | [JsonPropertyName("is_finished")] public bool? IsFinished { get; init; } 34 | 35 | [JsonPropertyName("index")] public int? Index { get; init; } 36 | } 37 | 38 | [JsonConverter(typeof(JsonStringEnumConverter))] 39 | public enum FinishReason 40 | { 41 | COMPLETE, 42 | MAX_TOKENS, 43 | ERROR, 44 | ERROR_TOXIC 45 | } 46 | 47 | public string? GetResponse() 48 | { 49 | return Generations?.FirstOrDefault()?.Text; 50 | } 51 | 52 | public string? GetStopReason() 53 | { 54 | return Generations?.FirstOrDefault()?.FinishReason.ToString(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/AmazonTitanRequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using DotnetFMPlayground.Core.Models; 3 | using DotnetFMPlayground.Core.Models.InferenceParameters; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json; 9 | using System.Threading.Tasks; 10 | 11 | namespace DotnetFMPlayground.Core.Builders.Commands 12 | { 13 | internal class AmazonTitanRequestBuildCommand : InvokeModelRequestBuildCommand 14 | { 15 | internal override InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 16 | { 17 | InvokeModelRequest request = new() 18 | { 19 | ModelId = modelId, 20 | ContentType = "application/json" 21 | }; 22 | 23 | request.Accept = "application/json"; 24 | request.Body = new MemoryStream( 25 | Encoding.UTF8.GetBytes( 26 | JsonSerializer.Serialize(new 27 | { 28 | inputText = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString() 29 | }) 30 | ) 31 | ); 32 | 33 | return request; 34 | } 35 | 36 | internal override InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 37 | { 38 | if(modelId == "amazon.titan-embed-text-v1") 39 | { 40 | throw new NotSupportedException($"ModelId {modelId} doesn't support streaming"); 41 | } 42 | 43 | InvokeModelWithResponseStreamRequest request = new() 44 | { 45 | ModelId = modelId, 46 | ContentType = "application/json" 47 | }; 48 | 49 | request.Accept = "application/json"; 50 | request.Body = new MemoryStream( 51 | Encoding.UTF8.GetBytes( 52 | JsonSerializer.Serialize(new 53 | { 54 | inputText = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString() 55 | }) 56 | ) 57 | ); 58 | 59 | return request; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 48 |
49 | 50 | @code { 51 | private bool collapseNavMenu = true; 52 | 53 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 54 | 55 | private void ToggleNavMenu() 56 | { 57 | collapseNavMenu = !collapseNavMenu; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelResponse/AI21LabsJurassic2Response.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DotnetFMPlayground.Core.Models.ModelResponse 4 | { 5 | public class AI21LabsJurassic2Response : IFoundationModelResponse 6 | { 7 | [JsonPropertyName("id")] public int? Id { get; init; } 8 | 9 | [JsonPropertyName("prompt")] public J2Text? Prompt { get; init; } 10 | 11 | [JsonPropertyName("completions")] 12 | public IEnumerable? Completions { get; init; } 13 | 14 | public class J2Text 15 | { 16 | [JsonPropertyName("text")] public string? Text { get; init; } 17 | 18 | [JsonPropertyName("tokens")] public IEnumerable? Tokens { get; init; } 19 | } 20 | 21 | public class J2Token 22 | { 23 | [JsonPropertyName("generatedToken")] public J2GeneratedToken? GeneratedToken { get; init; } 24 | 25 | [JsonPropertyName("topTokens")] public string? TopTokens { get; init; } 26 | 27 | [JsonPropertyName("textRange")] public J2TextRange? TextRange { get; init; } 28 | } 29 | 30 | public class J2GeneratedToken 31 | { 32 | [JsonPropertyName("token")] public string? Token { get; init; } 33 | 34 | [JsonPropertyName("logprob")] public double? LogProb { get; init; } 35 | 36 | [JsonPropertyName("raw_logprob")] public double? RawLogProb { get; init; } 37 | } 38 | 39 | public class J2TextRange 40 | { 41 | [JsonPropertyName("start")] public int? Start { get; init; } 42 | 43 | [JsonPropertyName("end")] public int? End { get; init; } 44 | } 45 | 46 | public class J2Completion 47 | { 48 | [JsonPropertyName("data")] public J2Text? Data { get; init; } 49 | 50 | [JsonPropertyName("finishReason")] public J2FinishReason? FinishReason { get; init; } 51 | } 52 | 53 | public class J2FinishReason 54 | { 55 | [JsonPropertyName("reason")] public string? Reason { get; init; } 56 | 57 | [JsonPropertyName("length")] public int? Length { get; init; } 58 | } 59 | 60 | 61 | public string? GetResponse() 62 | { 63 | return Completions?.FirstOrDefault()?.Data?.Text; 64 | } 65 | 66 | public string? GetStopReason() 67 | { 68 | return Completions?.FirstOrDefault()?.FinishReason?.Reason; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Models/ModelIds.cs: -------------------------------------------------------------------------------- 1 | using DotnetFMPlayground.Core.Builders.Commands; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DotnetFMPlayground.Core.Models 9 | { 10 | public static class ModelIds 11 | { 12 | private static readonly IList Ids = 13 | [ 14 | "anthropic.claude-instant-v1", 15 | "anthropic.claude-v1", 16 | "anthropic.claude-v2", 17 | "stability.stable-diffusion-xl-v0", 18 | "amazon.titan-text-lite-v1", 19 | "amazon.titan-text-express-v1", 20 | "amazon.titan-image-generator-v1", 21 | "cohere.command-text-v14", 22 | "ai21.j2-mid-v1", 23 | "ai21.j2-ultra-v1", 24 | "anthropic.claude-v2:1", 25 | "cohere.command-light-text-v14" 26 | ]; 27 | 28 | private static readonly IList StreamingSupported = 29 | [ 30 | "anthropic.claude-instant-v1", 31 | "anthropic.claude-v1", 32 | "anthropic.claude-v2", 33 | "amazon.titan-text-lite-v1", 34 | "amazon.titan-text-express-v1", 35 | "anthropic.claude-v2:1" 36 | ]; 37 | 38 | public static string ANTHROPIC_CLAUDE_INSTANT_V1 => Ids[0]; 39 | 40 | public static string ANTHROPIC_CLAUDE_V1 => Ids[1]; 41 | 42 | public static string ANTHROPIC_CLAUDE_V2 => Ids[2]; 43 | 44 | public static string ANTHROPIC_CLAUDE_V2_1 => Ids[10]; 45 | 46 | public static string STABILITY_AI_STABLE_DIFFUSION_XL_V0 => Ids[3]; 47 | 48 | public static string AMAZON_TITAN_TEXT_LITE_G1_V1 => Ids[4]; 49 | 50 | public static string AMAZON_TITAN_TEXT_EXPRESS_G1_V1 => Ids[5]; 51 | 52 | public static string AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 => Ids[6]; 53 | 54 | public static string COHERE_COMMAND_TEXT_V14 => Ids[7]; 55 | 56 | public static string COHERE_COMMAND_TEXT_LIGHT_V14 => Ids[11]; 57 | 58 | public static string AI21_LABS_JURASSIC_V2_MID_V1 => Ids[8]; 59 | 60 | public static string AI21_LABS_JURASSIC_V2_ULTRA_V1 => Ids[9]; 61 | 62 | 63 | public static bool IsSupported(string modelId) 64 | { 65 | return Ids.Contains(modelId); 66 | } 67 | 68 | public static bool IsStreamingSupported(string modelId) 69 | { 70 | return StreamingSupported.Contains(modelId); 71 | } 72 | 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/FoundationModelResponseBuilder.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Util.Internal.PlatformServices; 2 | using DotnetFMPlayground.Core.Models; 3 | using DotnetFMPlayground.Core.Models.ModelResponse; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json; 9 | using System.Threading.Tasks; 10 | 11 | namespace DotnetFMPlayground.Core.Builders 12 | { 13 | internal static class FoundationModelResponseBuilder 14 | { 15 | internal static async Task Build(string modelId, MemoryStream stream, bool streaming = false) 16 | { 17 | IFoundationModelResponse? response; 18 | switch (modelId) 19 | { 20 | case var _ when modelId == ModelIds.ANTHROPIC_CLAUDE_INSTANT_V1: 21 | case var _ when modelId == ModelIds.ANTHROPIC_CLAUDE_V1: 22 | case var _ when modelId == ModelIds.ANTHROPIC_CLAUDE_V2: 23 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 24 | break; 25 | case var _ when modelId == ModelIds.STABILITY_AI_STABLE_DIFFUSION_XL_V0: 26 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 27 | break; 28 | case var _ when modelId == ModelIds.AMAZON_TITAN_TEXT_LITE_G1_V1: 29 | case var _ when modelId == ModelIds.AMAZON_TITAN_TEXT_EXPRESS_G1_V1: 30 | if (streaming) 31 | { 32 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 33 | } 34 | else 35 | { 36 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 37 | } 38 | break; 39 | case var _ when modelId == ModelIds.COHERE_COMMAND_TEXT_V14: 40 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 41 | break; 42 | case var _ when modelId == ModelIds.AI21_LABS_JURASSIC_V2_MID_V1: 43 | case var _ when modelId == ModelIds.AI21_LABS_JURASSIC_V2_ULTRA_V1: 44 | response = await JsonSerializer.DeserializeAsync(stream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); 45 | break; 46 | default: 47 | throw new NotSupportedException($"ModelId {modelId} not supported"); 48 | }; 49 | return response; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/ImagePlayground.razor: -------------------------------------------------------------------------------- 1 | @page "/image-playground" 2 | @using Amazon.Bedrock.Model; 3 | @using System.Text; 4 | @using Amazon.Bedrock; 5 | @using Amazon.BedrockRuntime; 6 | @using DotnetFMPlayground.App.Components 7 | @using Rockhead.Extensions; 8 | @using Rockhead.Extensions.Amazon; 9 | @using Rockhead.Extensions.StabilityAI; 10 | @using DotnetFMPlayground.Core.Models; 11 | @inject AmazonBedrockRuntimeClient BedrockRuntimeClient 12 | @inject AmazonBedrockClient BedrockClient 13 | @inject IJSRuntime JS 14 | 15 | Image Playground 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Submit 25 | 26 | 27 | 28 | @if (imageSrc != String.Empty) 29 | { 30 | 31 | 32 | 33 | } 34 | 35 | 36 | @code { 37 | public class UserPrompt 38 | { 39 | public string Prompt { get; set; } 40 | } 41 | 42 | private UserPrompt userPrompt = new UserPrompt(); 43 | 44 | private ModelConfigurator _modelConfigurator; 45 | 46 | private Model _model; 47 | 48 | private void OnSelectedModelChanged(Model model) 49 | { 50 | _model = model; 51 | imageSrc = string.Empty; 52 | } 53 | 54 | private string imageSrc = ""; 55 | 56 | private async Task OnSubmit(EditContext context) 57 | { 58 | 59 | Prompt prompt = 60 | [ 61 | new PromptItem(PromptItemType.User, userPrompt.Prompt) 62 | ]; 63 | 64 | if(_model is Model.TitanImageGeneratorV1) 65 | { 66 | var response = await BedrockRuntimeClient.InvokeTitanImageGeneratorG1ForTextToImageAsync(new TitanImageTextToImageParams() 67 | { 68 | Text = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString() 69 | }); 70 | imageSrc = $"data:image/jpeg;base64,{(object)response?.Images?.FirstOrDefault()}"; 71 | } 72 | else if(_model is Model.StableDiffusionXl) 73 | { 74 | var response = await BedrockRuntimeClient.InvokeStableDiffusionXlForTextToImageAsync((Model.StableDiffusionXl)_model, new StableDiffusionTextToImageParams() 75 | { 76 | TextPrompts = new StableDiffusionTextToImageParams.TextPrompt[] { new StableDiffusionTextToImageParams.TextPrompt() { Text = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString() } } 77 | }); 78 | imageSrc = $"data:image/jpeg;base64,{(object)response?.Artifacts?.FirstOrDefault()?.Base64}"; 79 | } 80 | StateHasChanged(); 81 | await JS.InvokeVoidAsync("scrollToElement", "ImageId"); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/EmbeddingPlayground.razor: -------------------------------------------------------------------------------- 1 | @page "/embedding-playground" 2 | @using System.Text; 3 | @using Amazon.BedrockRuntime; 4 | @using Amazon.Bedrock; 5 | @using Amazon.Bedrock.Model; 6 | @using Rockhead.Extensions; 7 | @using System.Text.Json; 8 | @using DotnetFMPlayground.Core.Models; 9 | @using Rockhead.Extensions.Amazon; 10 | @using Rockhead.Extensions.Anthropic; 11 | @using Rockhead.Extensions.AI21Labs; 12 | @using Rockhead.Extensions.Cohere; 13 | @using Rockhead.Extensions.Meta; 14 | @using Rockhead.Extensions.MistralAI; 15 | @using DotnetFMPlayground.App.Components; 16 | @inject AmazonBedrockRuntimeClient BedrockRuntimeClient; 17 | @inject AmazonBedrockClient BedrockClient; 18 | @inject IJSRuntime JS; 19 | 20 | Embedding Playground 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Submit 30 | 31 | 32 | 33 | 34 | 35 | @outputText 36 | 37 | 38 | 39 | @code { 40 | 41 | public class UserPrompt 42 | { 43 | public string Prompt { get; set; } 44 | } 45 | 46 | private ModelConfigurator _modelConfigurator; 47 | 48 | private Model _model; 49 | 50 | private UserPrompt userPrompt = new UserPrompt(); 51 | 52 | private string outputText; 53 | 54 | private void OnSelectedModelChanged(Model model) 55 | { 56 | _model = model; 57 | outputText = string.Empty; 58 | } 59 | 60 | private async Task OnSubmit(EditContext context) 61 | { 62 | outputText = string.Empty; 63 | Prompt prompt = new(); 64 | prompt.Add(new PromptItem(PromptItemType.User, userPrompt.Prompt)); 65 | 66 | try 67 | { 68 | outputText = await InvokeModelAsync(prompt); 69 | StateHasChanged(); 70 | await JS.InvokeVoidAsync("scrollToElement", "ResponseField"); 71 | } 72 | catch (Exception e) 73 | { 74 | outputText = e.Message; 75 | StateHasChanged(); 76 | } 77 | } 78 | 79 | private async Task InvokeModelAsync(Prompt prompt) 80 | { 81 | IFoundationModelResponse? response = null; 82 | string textPrompt = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString(); 83 | if(_model is Model.TitanEmbedTextV1) 84 | { 85 | response = await BedrockRuntimeClient.InvokeTitanEmbeddingsG1TextAsync(textPrompt); 86 | } 87 | else if(_model is Model.Embed) 88 | { 89 | response = await BedrockRuntimeClient.InvokeEmbedV3Async((Model.Embed)_model, new []{ textPrompt }); 90 | } 91 | return response?.GetResponse(); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | h1:focus { 8 | outline: none; 9 | } 10 | 11 | a, .btn-link { 12 | color: #0071c1; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .content { 22 | padding-top: 1.1rem; 23 | } 24 | 25 | .valid.modified:not([type=checkbox]) { 26 | outline: 1px solid #26b050; 27 | } 28 | 29 | .invalid { 30 | outline: 1px solid red; 31 | } 32 | 33 | .validation-message { 34 | color: red; 35 | } 36 | 37 | #blazor-error-ui { 38 | background: lightyellow; 39 | bottom: 0; 40 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 41 | display: none; 42 | left: 0; 43 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 44 | position: fixed; 45 | width: 100%; 46 | z-index: 1000; 47 | } 48 | 49 | #blazor-error-ui .dismiss { 50 | cursor: pointer; 51 | position: absolute; 52 | right: 0.75rem; 53 | top: 0.5rem; 54 | } 55 | 56 | .blazor-error-boundary { 57 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 58 | padding: 1rem 1rem 1rem 3.7rem; 59 | color: white; 60 | } 61 | 62 | .blazor-error-boundary::after { 63 | content: "An error has occurred." 64 | } 65 | 66 | .status-bar-safe-area { 67 | display: none; 68 | } 69 | 70 | @supports (-webkit-touch-callout: none) { 71 | .status-bar-safe-area { 72 | display: flex; 73 | position: sticky; 74 | top: 0; 75 | height: env(safe-area-inset-top); 76 | background-color: #f7f7f7; 77 | width: 100%; 78 | z-index: 1; 79 | } 80 | 81 | .flex-column, .navbar-brand { 82 | padding-left: env(safe-area-inset-left); 83 | } 84 | } 85 | 86 | .white-space-pre-line { 87 | white-space: pre-line; 88 | } -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/Commands/AnthropicClaudeRequestBuildCommand.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using Amazon.Runtime.Internal; 3 | using DotnetFMPlayground.Core.Models; 4 | using DotnetFMPlayground.Core.Models.InferenceParameters; 5 | using System.Text; 6 | using System.Text.Json; 7 | 8 | namespace DotnetFMPlayground.Core.Builders.Commands 9 | { 10 | internal class AnthropicClaudeRequestBuildCommand : InvokeModelRequestBuildCommand 11 | { 12 | internal override InvokeModelRequest Execute(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 13 | { 14 | InvokeModelRequest request = new() 15 | { 16 | ModelId = modelId, 17 | ContentType = "application/json" 18 | }; 19 | 20 | StringBuilder promtValueBuilder = new(); 21 | PromptItemType? lastPromptType = null; 22 | foreach (var item in prompt) 23 | { 24 | lastPromptType = item.Type; 25 | string label = item.Type == PromptItemType.User ? "Human" : "Assistant"; 26 | promtValueBuilder.Append(label); 27 | promtValueBuilder.Append(": "); 28 | promtValueBuilder.Append(item.Prompt); 29 | promtValueBuilder.AppendLine(); 30 | } 31 | if (lastPromptType != PromptItemType.FMAnswer) 32 | { 33 | if (!promtValueBuilder.ToString().EndsWith('.')) 34 | promtValueBuilder.Append('.'); 35 | promtValueBuilder.AppendLine(); 36 | promtValueBuilder.AppendLine("Assistant: "); 37 | } 38 | 39 | request.Accept = "application/json"; 40 | request.Body = new MemoryStream( 41 | Encoding.UTF8.GetBytes( 42 | JsonSerializer.Serialize(new 43 | { 44 | prompt = promtValueBuilder.ToString(), 45 | max_tokens_to_sample = inferenceParameters["max_tokens_to_sample"] 46 | }) 47 | ) 48 | ); 49 | 50 | return request; 51 | } 52 | 53 | internal override InvokeModelWithResponseStreamRequest ExecuteWithResponseStream(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 54 | { 55 | InvokeModelWithResponseStreamRequest request = new() 56 | { 57 | ModelId = modelId, 58 | ContentType = "application/json" 59 | }; 60 | 61 | StringBuilder promtValueBuilder = new(); 62 | PromptItemType? lastPromptType = null; 63 | foreach (var item in prompt) 64 | { 65 | lastPromptType = item.Type; 66 | string label = item.Type == PromptItemType.User ? "Human" : "Assistant"; 67 | promtValueBuilder.Append(label); 68 | promtValueBuilder.Append(": "); 69 | promtValueBuilder.Append(item.Prompt); 70 | promtValueBuilder.AppendLine(); 71 | } 72 | if (lastPromptType != PromptItemType.FMAnswer) 73 | { 74 | if (!promtValueBuilder.ToString().EndsWith('.')) 75 | promtValueBuilder.Append('.'); 76 | promtValueBuilder.AppendLine(); 77 | promtValueBuilder.AppendLine("Assistant: "); 78 | } 79 | 80 | request.Accept = "application/json"; 81 | request.Body = new MemoryStream( 82 | Encoding.UTF8.GetBytes( 83 | JsonSerializer.Serialize(new 84 | { 85 | prompt = promtValueBuilder.ToString(), 86 | max_tokens_to_sample = inferenceParameters["max_tokens_to_sample"] 87 | }) 88 | ) 89 | ); 90 | 91 | return request; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET FM Playground 2 | 3 | .NET FM Playground is a .NET MAUI Blazor sample application showcasing 4 | how to leverage Amazon Bedrock from C# code. As any sample application, it is not production-ready. 5 | It is provided for the sole purpose of illustrating of .NET and C# developers can leverage Amazon Bedrock 6 | to build generative AI enabled applications. 7 | 8 | Amazon Bedrock is a fully managed 9 | service that offers a choice of high-performing foundation models (FMs) 10 | from leading AI companies like AI21 Labs, Anthropic, Cohere, Meta, Stability AI, Mistral AI 11 | and Amazon via a single API. 12 | 13 | It also offers Knowledge Bases and Agents for Amazon Bedrock to speed up the implementation of Retrieval Augmented Generation pattern and execute complex tasks. 14 | 15 | In order to let you test and interact with the different foundation models and advanced features, the .NET FM Playground offers you five playgrounds: 16 | * A text playground 17 | * A chat playground 18 | * A voice chat playground 19 | * An embedding playground 20 | * An image playground 21 | * An agent playground 22 | 23 | In addition, it also lists and displays the foundation models you have access to and their characteristics. 24 | 25 | ![Screenshot of the chat playground](/doc/img/ChatPlayground.png "Screenshot of the chat playground") 26 | 27 | ![Screenshot of the image playground](/doc/img/ImagePlayground.png "Screenshot of the image playground") 28 | 29 | ## Dev Environment Prerequisite 30 | 31 | This sample has been developed using: 32 | * .NET 8.0 33 | * .NET MAUI 8.0 34 | 35 | You need to install .NET 8.0 SDK. 36 | 37 | For .NET MAUI 8.0: 38 | * if you use Visual Studio 2022, install the .NET Multi-platform App UI development 39 | workload from the Visual Studio Installer 40 | * otherwise, you can install maui-windows and maui-maccatalyst workload running the 41 | following commands 42 | ``` 43 | dotnet workload install maui-windows 44 | dotnet workload install maui-maccatalyst 45 | ``` 46 | 47 | This sample has been developed using Visual Studio 2022 17.7.3 on Windows 11. While 48 | it should work on MacOS and in other IDEs supporting .NET MAUI development, we can't 49 | guarantee. 50 | 51 | ## AWS Account prerequisite 52 | 53 | ### Identity & Access Management 54 | 55 | For now, the .NET FM Playground requires you to configure your default AWS profile with 56 | credentials giving you access to Amazon Bedrock. 57 | 58 | To learn more about granting programmatic access and setting up permissions, read the following 59 | documentations: 60 | * https://docs.aws.amazon.com/bedrock/latest/userguide/setting-up.html 61 | * https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html 62 | 63 | ### Model Access 64 | 65 | An AWS Account does not have access to models by default. An Admin user with IAM access permissions 66 | can add access to specific models using the model access page. 67 | 68 | To learn more about managing model access, read the following documentation: 69 | * https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html 70 | 71 | ### Model Access 72 | 73 | To use the agent playground, you first need to create an agent in your AWS account. 74 | 75 | To learn more about creating and managing agents for Amazon Bedrock, read the following documentation: 76 | * https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html 77 | 78 | ## Repository Structure 79 | 80 | The repository has the following structure 81 | * **src:** contains the solution 82 | * **DonetFMPlayground.Core:** contains a library adding method extensions to the 83 | AmazonBedrockRuntimeClient object from the AWS SDK for .NET 84 | * **DotnetFMPlayground.App:** contains the .NET MAUI Blazor application 85 | * **doc:** contains documentation 86 | * **img:** contains screenshots and images 87 | 88 | 89 | ## Security 90 | 91 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 92 | 93 | ## License 94 | 95 | This library is licensed under the MIT-0 License. See the LICENSE file. 96 | 97 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](https://github.com/iconic/open-iconic) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](https://github.com/iconic/open-iconic). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](https://github.com/iconic/open-iconic) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](https://github.com/iconic/open-iconic) and [Reference](https://github.com/iconic/open-iconic) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.Core/Builders/InvokeModelRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | using Amazon.BedrockRuntime.Model; 2 | using DotnetFMPlayground.Core.Builders.Commands; 3 | using DotnetFMPlayground.Core.Models; 4 | using DotnetFMPlayground.Core.Models.InferenceParameters; 5 | using System.Net.Http.Json; 6 | using System.Text; 7 | using System.Text.Json; 8 | 9 | namespace DotnetFMPlayground.Core.Builders 10 | { 11 | internal class InvokeModelRequestBuilder 12 | { 13 | private static readonly Dictionary invokeModelRequestBuilderRegistry = new(); 14 | 15 | static InvokeModelRequestBuilder() 16 | { 17 | 18 | AnthropicClaudeRequestBuildCommand claudeRequestBuildCommand = new(); 19 | invokeModelRequestBuilderRegistry.Add("anthropic.claude-instant-v1", claudeRequestBuildCommand); 20 | invokeModelRequestBuilderRegistry.Add("anthropic.claude-v1", claudeRequestBuildCommand); 21 | invokeModelRequestBuilderRegistry.Add("anthropic.claude-v2", claudeRequestBuildCommand); 22 | 23 | StabilityAIStableDiffusionRequestBuildCommand stableDiffusionRequestBuildCommand = new(); 24 | invokeModelRequestBuilderRegistry.Add("stability.stable-diffusion-xl-v0", stableDiffusionRequestBuildCommand); 25 | 26 | AmazonTitanRequestBuildCommand amazonTitanRequestBuildCommand = new(); 27 | invokeModelRequestBuilderRegistry.Add("amazon.titan-text-lite-v1", amazonTitanRequestBuildCommand); 28 | invokeModelRequestBuilderRegistry.Add("amazon.titan-text-express-v1", amazonTitanRequestBuildCommand); 29 | invokeModelRequestBuilderRegistry.Add("amazon.titan-text-agile-v1", amazonTitanRequestBuildCommand); 30 | invokeModelRequestBuilderRegistry.Add("amazon.titan-embed-text-v1", amazonTitanRequestBuildCommand); 31 | 32 | CohereCommandRequestBuildCommand cohereCommandRequestBuildCommand = new(); 33 | invokeModelRequestBuilderRegistry.Add("cohere.command-text-v14", cohereCommandRequestBuildCommand); 34 | 35 | AI21LabsJurassic2RequestBuildCommand aI21LabsJurassic2RequestBuildCommand = new(); 36 | invokeModelRequestBuilderRegistry.Add("ai21.j2-mid-v1", aI21LabsJurassic2RequestBuildCommand); 37 | invokeModelRequestBuilderRegistry.Add("ai21.j2-ultra-v1", aI21LabsJurassic2RequestBuildCommand); 38 | } 39 | 40 | internal static InvokeModelRequest Build( 41 | string modelId, 42 | Prompt prompt, 43 | BaseInferenceParameters inferenceParameters) 44 | { 45 | InvokeModelRequest request; 46 | 47 | if (invokeModelRequestBuilderRegistry.ContainsKey(modelId)) 48 | { 49 | request = invokeModelRequestBuilderRegistry[modelId].Execute(modelId, prompt, inferenceParameters); 50 | } 51 | else 52 | { 53 | throw new NotSupportedException($"ModelId {modelId} not supported"); 54 | } 55 | return request; 56 | } 57 | 58 | internal static InvokeModelWithResponseStreamRequest BuildWithResponseStream( 59 | string modelId, 60 | Prompt prompt, 61 | BaseInferenceParameters inferenceParameters) 62 | { 63 | InvokeModelWithResponseStreamRequest request; 64 | 65 | if (invokeModelRequestBuilderRegistry.ContainsKey(modelId)) 66 | { 67 | request = invokeModelRequestBuilderRegistry[modelId].ExecuteWithResponseStream(modelId, prompt, inferenceParameters); 68 | } 69 | else 70 | { 71 | throw new NotSupportedException($"ModelId {modelId} not supported"); 72 | } 73 | return request; 74 | } 75 | 76 | 77 | protected virtual InvokeModelRequest Create(string modelId, Prompt prompt, BaseInferenceParameters inferenceParameters) 78 | { 79 | throw new NotSupportedException("Create Method from InvokeModelRequestBuilder class should not be called directly. Only inherited methods should be called"); 80 | } 81 | } 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/DotnetFMPlayground.App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-maccatalyst 5 | $(TargetFrameworks);net8.0-windows10.0.19041.0 6 | Exe 7 | DotnetFMPlayground.App 8 | true 9 | true 10 | enable 11 | false 12 | 13 | 14 | DotnetFMPlayground.App 15 | 16 | 17 | com.companyname.DotnetFMPlayground.app 18 | F8422E3F-70A1-4A6E-80C0-5D4C781B988B 19 | 20 | 21 | 1.0 22 | 1 23 | 24 | 14.0 25 | 10.0.17763.0 26 | 10.0.17763.0 27 | False 28 | True 29 | False 30 | True 31 | 0 32 | 33 | 34 | 35 | com.amazon.dotnetfmplayground.app 36 | .NET FM Playground 37 | 38 | 39 | 40 | com.amazon.dotnetfmplayground.app 41 | .NET FM Playground 42 | 43 | 44 | 45 | com.amazon.dotnetfmplayground.app 46 | .NET FM Playground 47 | 48 | 49 | 50 | com.amazon.dotnetfmplayground.app 51 | .NET FM Playground 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/TextPlayground.razor: -------------------------------------------------------------------------------- 1 | @page "/text-playground" 2 | @using System.Text; 3 | @using Amazon.BedrockRuntime; 4 | @using Amazon.Bedrock; 5 | @using Amazon.Bedrock.Model; 6 | @using Rockhead.Extensions; 7 | @using System.Text.Json; 8 | @using DotnetFMPlayground.Core.Models; 9 | @using Rockhead.Extensions.Amazon; 10 | @using Rockhead.Extensions.Anthropic; 11 | @using Rockhead.Extensions.AI21Labs; 12 | @using Rockhead.Extensions.Cohere; 13 | @using Rockhead.Extensions.Meta; 14 | @using Rockhead.Extensions.MistralAI; 15 | @using DotnetFMPlayground.App.Components; 16 | @inject AmazonBedrockRuntimeClient BedrockRuntimeClient; 17 | @inject AmazonBedrockClient BedrockClient; 18 | @inject IJSRuntime JS; 19 | 20 | Text Playground 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Submit 30 | 31 | 32 | 33 | 34 | 35 | @outputText 36 | 37 | 38 | 39 | @code { 40 | 41 | public class UserPrompt 42 | { 43 | public string Prompt { get; set; } 44 | } 45 | 46 | private ModelConfigurator _modelConfigurator; 47 | 48 | private Model _model; 49 | 50 | private UserPrompt userPrompt = new UserPrompt(); 51 | 52 | private string outputText; 53 | 54 | private bool _streaming = false; 55 | 56 | private bool _messagesAPI = false; 57 | 58 | private void OnSelectedModelChanged(Model model) 59 | { 60 | _model = model; 61 | outputText = string.Empty; 62 | } 63 | 64 | private void OnStreamingChanged(bool value) 65 | { 66 | _streaming = value; 67 | } 68 | 69 | private void OnMessagesAPIChanged(bool value) 70 | { 71 | _messagesAPI = value; 72 | } 73 | 74 | private void OnAddPromptFormat(string value) 75 | { 76 | userPrompt.Prompt = value; 77 | } 78 | 79 | private async Task OnSubmit(EditContext context) 80 | { 81 | outputText = string.Empty; 82 | Prompt prompt = new(); 83 | prompt.Add(new PromptItem(PromptItemType.User, userPrompt.Prompt)); 84 | 85 | try 86 | { 87 | if(_model.StreamingSupported && _streaming) 88 | { 89 | await foreach(var chunk in InvokeModelWithStreamingAsync(prompt)) 90 | { 91 | outputText += chunk; 92 | StateHasChanged(); 93 | await JS.InvokeVoidAsync("scrollToElement", "ResponseField"); 94 | } 95 | } 96 | else 97 | { 98 | outputText = await InvokeModelAsync(prompt); 99 | StateHasChanged(); 100 | await JS.InvokeVoidAsync("scrollToElement", "ResponseField"); 101 | } 102 | } 103 | catch (Exception e) 104 | { 105 | outputText = e.Message; 106 | StateHasChanged(); 107 | } 108 | } 109 | 110 | private async Task InvokeModelAsync(Prompt prompt) 111 | { 112 | IFoundationModelResponse? response = null; 113 | string textPrompt = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString(); 114 | if (_model is Model.TitanText) 115 | { 116 | response = await BedrockRuntimeClient.InvokeTitanTextG1Async((Model.TitanText)_model, textPrompt, _modelConfigurator?.GetTitanTextGenerationConfig()); 117 | } 118 | else if (_model is Model.Jurassic2) 119 | { 120 | response = await BedrockRuntimeClient.InvokeJurassic2Async((Model.Jurassic2)_model, textPrompt, _modelConfigurator.GetJurassic2TextGenerationConfig()); 121 | } 122 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 123 | { 124 | response = await BedrockRuntimeClient.InvokeClaudeAsync((Model.Claude)_model, textPrompt, _modelConfigurator.GetClaudeTextGenerationConfig()); 125 | } 126 | else if (_model is Model.Claude) 127 | { 128 | ClaudeMessage message = new ClaudeMessage() { Role = "user", Content = new[] { new ClaudeTextContent() { Text = textPrompt } } }; 129 | response = await BedrockRuntimeClient.InvokeClaudeMessagesAsync((Model.Claude)_model, message, _modelConfigurator.GetClaudeMessagesConfig()); 130 | } 131 | else if (_model is Model.CommandText) 132 | { 133 | response = await BedrockRuntimeClient.InvokeCommandV14Async((Model.CommandText)_model, textPrompt, _modelConfigurator.GetCommandTextGenerationConfig()); 134 | } 135 | else if (_model is Model.Llama) 136 | { 137 | response = await BedrockRuntimeClient.InvokeLlamaAsync((Model.Llama)_model, textPrompt, _modelConfigurator.GetLlamaTextGenerationConfig()); 138 | } 139 | else if (_model is Model.Mistral) 140 | { 141 | response = await BedrockRuntimeClient.InvokeMistralAsync((Model.Mistral)_model, textPrompt, _modelConfigurator.GetMistralTextGenerationConfig()); 142 | } 143 | return response?.GetResponse(); 144 | } 145 | 146 | private async IAsyncEnumerable InvokeModelWithStreamingAsync(Prompt prompt) 147 | { 148 | string textPrompt = new StringBuilder().AppendJoin(' ', prompt.Select(x => x.Prompt)).ToString(); 149 | if (_model is Model.TitanText) 150 | { 151 | await foreach (var chunk in BedrockRuntimeClient.InvokeTitanTextG1WithResponseStreamAsync((Model.TitanText)_model, textPrompt, _modelConfigurator.GetTitanTextGenerationConfig())) 152 | { 153 | yield return chunk.GetResponse(); 154 | } 155 | } 156 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 157 | { 158 | await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeWithResponseStreamAsync((Model.Claude)_model, textPrompt, _modelConfigurator.GetClaudeTextGenerationConfig())) 159 | { 160 | yield return chunk.GetResponse(); 161 | } 162 | } 163 | else if (_model is Model.Claude) 164 | { 165 | ClaudeMessage message = new ClaudeMessage() { Role = "user", Content = new[] { new ClaudeTextContent() { Text = textPrompt } } }; 166 | await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeMessagesWithResponseStreamAsync((Model.Claude)_model, message, _modelConfigurator.GetClaudeMessagesConfig())) 167 | { 168 | yield return chunk.GetResponse(); 169 | } 170 | } 171 | else if (_model is Model.CommandText) 172 | { 173 | await foreach (var chunk in BedrockRuntimeClient.InvokeCommandV14WithResponseStreamAsync((Model.CommandText)_model, textPrompt, _modelConfigurator.GetCommandTextGenerationConfig())) 174 | { 175 | yield return chunk.GetResponse(); 176 | } 177 | } 178 | else if (_model is Model.Llama) 179 | { 180 | await foreach (var chunk in BedrockRuntimeClient.InvokeLlamaWithResponseStreamAsync((Model.Llama)_model, textPrompt, _modelConfigurator.GetLlamaTextGenerationConfig())) 181 | { 182 | yield return chunk.GetResponse(); 183 | } 184 | } 185 | else if (_model is Model.Mistral) 186 | { 187 | await foreach (var chunk in BedrockRuntimeClient.InvokeMistralWithResponseStreamAsync((Model.Mistral)_model, textPrompt, _modelConfigurator.GetMistralTextGenerationConfig())) 188 | { 189 | yield return chunk.GetResponse(); 190 | } 191 | } 192 | } 193 | } 194 | 195 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Resources/Images/dotnet_bot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/ChatPlayground.razor: -------------------------------------------------------------------------------- 1 | @page "/chat-playground" 2 | @using Amazon.Bedrock.Model; 3 | @using Amazon.Bedrock; 4 | @using Amazon.BedrockRuntime; 5 | @using System.Text; 6 | @using DotnetFMPlayground.App.Components 7 | @using DotnetFMPlayground.Core.Models; 8 | @using Rockhead.Extensions 9 | @using Rockhead.Extensions.Anthropic 10 | @inject AmazonBedrockRuntimeClient BedrockRuntimeClient 11 | @inject AmazonBedrockClient BedrockClient 12 | @inject IJSRuntime JS 13 | 14 | Chat Playground 15 | 16 | 17 | 18 | 19 | 20 | @foreach (var item in promptItems) 21 | { 22 | string label = item.Type == PromptItemType.User ? "Human" : "Assistant"; 23 | 24 | @item.Prompt 25 | 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | @if (isThinking) 35 | { 36 | 37 | Thinking... 38 | } 39 | else 40 | { 41 | 42 | Reset 43 | } 44 | 45 | 46 | 47 | 48 | 49 | 50 | @code { 51 | 52 | #region MODEL_CONFIGURATION_FIELDS 53 | private ModelConfigurator _modelConfigurator; 54 | 55 | private Model _model; 56 | 57 | private bool _streaming = false; 58 | 59 | private bool _messagesAPI = false; 60 | #endregion 61 | 62 | 63 | 64 | 65 | 66 | private MudTextField promptField; 67 | 68 | private ICollection promptItems = new List(); 69 | 70 | private bool isThinking = false; 71 | 72 | 73 | private async Task Reset(MouseEventArgs e) 74 | { 75 | await promptField.SetText(""); 76 | promptItems.Clear(); 77 | } 78 | 79 | private async Task OnPromptChanged(string inputValue) 80 | { 81 | if (string.IsNullOrEmpty(inputValue)) 82 | return; 83 | 84 | isThinking = true; 85 | 86 | Prompt prompt = new(); 87 | prompt.AddRange(promptItems); 88 | // at this point, you don't want to add newUserPrompt to the PromptItems collection because it would update the display while we don't have yet 89 | // the assistant answer 90 | PromptItem newUserPrompt = new(PromptItemType.User, inputValue); 91 | prompt.Add(newUserPrompt); 92 | 93 | string textResponse = string.Empty; 94 | try 95 | { 96 | if (_model.StreamingSupported && _streaming) 97 | { 98 | bool firstChunk = true; 99 | 100 | PromptItem outputPromptItem = new PromptItem(PromptItemType.FMAnswer, textResponse); 101 | await foreach (var chunk in InvokeModelWithStreamingAsync(prompt)) 102 | { 103 | if (firstChunk) 104 | { 105 | // now we can clear the PromptField and add the user prompt and the AI answer to the PromptItems collection to refresh the UI 106 | await promptField.Clear(); 107 | promptItems.Add(newUserPrompt); 108 | promptItems.Add(outputPromptItem); 109 | firstChunk = false; 110 | } 111 | outputPromptItem.Prompt += chunk; 112 | StateHasChanged(); 113 | await JS.InvokeVoidAsync("scrollToElement", "PromptId"); 114 | } 115 | isThinking = false; 116 | } 117 | else 118 | { 119 | textResponse = await InvokeModelAsync(prompt); 120 | // now we can clear the PromptField and add the user prompt and the AI answer to the PromptItems collection to refresh the UI 121 | await promptField.Clear(); 122 | promptItems.Add(newUserPrompt); 123 | promptItems.Add(new PromptItem(PromptItemType.FMAnswer, textResponse)); 124 | isThinking = false; 125 | StateHasChanged(); 126 | await JS.InvokeVoidAsync("scrollToElement", "PromptId"); 127 | } 128 | } 129 | catch (Exception e ) 130 | { 131 | Console.WriteLine(e); 132 | StateHasChanged(); 133 | } 134 | } 135 | 136 | #region MODEL_CONFIGURATION_EVENT_CALLBACK 137 | private void OnSelectedModelChanged(Model model) 138 | { 139 | _model = model; 140 | } 141 | 142 | private void OnStreamingChanged(bool value) 143 | { 144 | _streaming = value; 145 | } 146 | 147 | private void OnMessagesAPIChanged(bool value) 148 | { 149 | _messagesAPI = value; 150 | } 151 | 152 | private void OnAddPromptFormat(string value) 153 | { 154 | } 155 | #endregion 156 | 157 | #region INVOKE_REGION 158 | private async Task InvokeModelAsync(Prompt prompt) 159 | { 160 | IFoundationModelResponse? response = null; 161 | StringBuilder textPromptBuilder = new StringBuilder(); 162 | if (_model is Model.TitanText) 163 | { 164 | foreach (var promptItem in prompt) 165 | { 166 | string role = promptItem.Type == PromptItemType.User ? "User" : "Bot"; 167 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 168 | } 169 | textPromptBuilder.Append("Bot: "); 170 | response = await BedrockRuntimeClient.InvokeTitanTextG1Async((Model.TitanText)_model, textPromptBuilder.ToString(), _modelConfigurator?.GetTitanTextGenerationConfig()); 171 | } 172 | else if (_model is Model.Jurassic2) 173 | { 174 | foreach (var promptItem in prompt) 175 | { 176 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 177 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 178 | } 179 | textPromptBuilder.Append("assistant: "); 180 | response = await BedrockRuntimeClient.InvokeJurassic2Async((Model.Jurassic2)_model, textPromptBuilder.ToString(), _modelConfigurator.GetJurassic2TextGenerationConfig()); 181 | } 182 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 183 | { 184 | foreach(var promptItem in prompt) 185 | { 186 | string role = promptItem.Type == PromptItemType.User ? "Human" : "Assistant"; 187 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 188 | } 189 | textPromptBuilder.Append("Assistant: "); 190 | response = await BedrockRuntimeClient.InvokeClaudeAsync((Model.Claude)_model, textPromptBuilder.ToString(), _modelConfigurator.GetClaudeTextGenerationConfig()); 191 | } 192 | else if (_model is Model.Claude) 193 | { 194 | ClaudeMessagesConfig? claudeMessagesConfig = _modelConfigurator.GetClaudeMessagesConfig(); 195 | claudeMessagesConfig ??= new ClaudeMessagesConfig() { MaxTokens = 200, TopP=0.01f }; 196 | foreach(var promptItem in prompt) 197 | { 198 | string role = promptItem.Type == PromptItemType.User ? "user" : "assistant"; 199 | claudeMessagesConfig.Messages.Add(new ClaudeMessage() { Role = role, Content = new[] { new ClaudeTextContent() { Text = promptItem.Prompt } } }); 200 | } 201 | ClaudeMessage message = claudeMessagesConfig.Messages.Last(); 202 | claudeMessagesConfig.Messages.Remove(message); 203 | response = await BedrockRuntimeClient.InvokeClaudeMessagesAsync((Model.Claude)_model, message, claudeMessagesConfig); 204 | } 205 | else if (_model is Model.CommandText) 206 | { 207 | foreach (var promptItem in prompt) 208 | { 209 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 210 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 211 | } 212 | textPromptBuilder.Append("assistant: "); 213 | response = await BedrockRuntimeClient.InvokeCommandV14Async((Model.CommandText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetCommandTextGenerationConfig()); 214 | } 215 | else if (_model is Model.Llama213BChatV1 || _model is Model.Llama270BChatV1) 216 | { 217 | foreach (var promptItem in prompt) 218 | { 219 | if (promptItem.Type == PromptItemType.User) 220 | { 221 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 222 | } 223 | else 224 | { 225 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 226 | } 227 | } 228 | response = await BedrockRuntimeClient.InvokeLlamaAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig()); 229 | } 230 | else if (_model is Model.Llama38BInstructV1 || _model is Model.Llama370BInstructV1) 231 | { 232 | textPromptBuilder.Append("<|begin_of_text|>"); 233 | foreach (var promptItem in prompt) 234 | { 235 | if (promptItem.Type == PromptItemType.User) 236 | { 237 | textPromptBuilder.Append($"<|start_header_id|>user<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 238 | } 239 | else 240 | { 241 | textPromptBuilder.Append($"<|start_header_id|>assistant<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 242 | } 243 | } 244 | textPromptBuilder.Append("<|start_header_id|>assistant<|end_header_id|>"); 245 | response = await BedrockRuntimeClient.InvokeLlamaAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig()); 246 | } 247 | else if (_model is Model.Mistral) 248 | { 249 | foreach (var promptItem in prompt) 250 | { 251 | if (promptItem.Type == PromptItemType.User) 252 | { 253 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 254 | } 255 | else 256 | { 257 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 258 | } 259 | } 260 | response = await BedrockRuntimeClient.InvokeMistralAsync((Model.Mistral)_model, textPromptBuilder.ToString(), _modelConfigurator.GetMistralTextGenerationConfig()); 261 | } 262 | return response?.GetResponse(); 263 | } 264 | 265 | private async IAsyncEnumerable InvokeModelWithStreamingAsync(Prompt prompt) 266 | { 267 | StringBuilder textPromptBuilder = new StringBuilder(); 268 | if (_model is Model.TitanText) 269 | { 270 | foreach (var promptItem in prompt) 271 | { 272 | string role = promptItem.Type == PromptItemType.User ? "User" : "Bot"; 273 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 274 | } 275 | textPromptBuilder.Append("Bot: "); 276 | await foreach (var chunk in BedrockRuntimeClient.InvokeTitanTextG1WithResponseStreamAsync((Model.TitanText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetTitanTextGenerationConfig())) 277 | { 278 | yield return chunk.GetResponse(); 279 | } 280 | } 281 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 282 | { 283 | foreach (var promptItem in prompt) 284 | { 285 | string role = promptItem.Type == PromptItemType.User ? "Human" : "Assistant"; 286 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 287 | } 288 | textPromptBuilder.Append("Assistant: "); 289 | await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeWithResponseStreamAsync((Model.Claude)_model, textPromptBuilder.ToString(), _modelConfigurator.GetClaudeTextGenerationConfig())) 290 | { 291 | yield return chunk.GetResponse(); 292 | } 293 | } 294 | else if (_model is Model.Claude) 295 | { 296 | ClaudeMessagesConfig? claudeMessagesConfig = _modelConfigurator.GetClaudeMessagesConfig(); 297 | claudeMessagesConfig ??= new ClaudeMessagesConfig() { MaxTokens = 200, TopP = 0.01f }; 298 | foreach (var promptItem in prompt) 299 | { 300 | string role = promptItem.Type == PromptItemType.User ? "user" : "assistant"; 301 | claudeMessagesConfig.Messages.Add(new ClaudeMessage() { Role = role, Content = new[] { new ClaudeTextContent() { Text = promptItem.Prompt } } }); 302 | } 303 | ClaudeMessage message = claudeMessagesConfig.Messages.Last(); 304 | claudeMessagesConfig.Messages.Remove(message); await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeMessagesWithResponseStreamAsync((Model.Claude)_model, message, claudeMessagesConfig)) 305 | { 306 | yield return chunk.GetResponse(); 307 | } 308 | } 309 | else if (_model is Model.CommandText) 310 | { 311 | foreach (var promptItem in prompt) 312 | { 313 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 314 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 315 | } 316 | textPromptBuilder.Append("assistant: "); 317 | await foreach (var chunk in BedrockRuntimeClient.InvokeCommandV14WithResponseStreamAsync((Model.CommandText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetCommandTextGenerationConfig())) 318 | { 319 | yield return chunk.GetResponse(); 320 | } 321 | } 322 | else if (_model is Model.Llama213BChatV1 || _model is Model.Llama270BChatV1) 323 | { 324 | foreach (var promptItem in prompt) 325 | { 326 | if (promptItem.Type == PromptItemType.User) 327 | { 328 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 329 | } 330 | else 331 | { 332 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 333 | } 334 | } 335 | await foreach (var chunk in BedrockRuntimeClient.InvokeLlamaWithResponseStreamAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig())) 336 | { 337 | yield return chunk.GetResponse(); 338 | } 339 | } 340 | else if (_model is Model.Llama) 341 | { 342 | textPromptBuilder.Append("<|begin_of_text|>"); 343 | foreach (var promptItem in prompt) 344 | { 345 | if (promptItem.Type == PromptItemType.User) 346 | { 347 | textPromptBuilder.Append($"<|start_header_id|>user<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 348 | } 349 | else 350 | { 351 | textPromptBuilder.Append($"<|start_header_id|>assistant<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 352 | } 353 | } 354 | textPromptBuilder.Append("<|start_header_id|>assistant<|end_header_id|>"); 355 | await foreach (var chunk in BedrockRuntimeClient.InvokeLlamaWithResponseStreamAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig())) 356 | { 357 | yield return chunk.GetResponse(); 358 | } 359 | } 360 | else if (_model is Model.Mistral) 361 | { 362 | foreach (var promptItem in prompt) 363 | { 364 | if (promptItem.Type == PromptItemType.User) 365 | { 366 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 367 | } 368 | else 369 | { 370 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 371 | } 372 | } 373 | await foreach (var chunk in BedrockRuntimeClient.InvokeMistralWithResponseStreamAsync((Model.Mistral)_model, textPromptBuilder.ToString(), _modelConfigurator.GetMistralTextGenerationConfig())) 374 | { 375 | yield return chunk.GetResponse(); 376 | } 377 | } 378 | } 379 | #endregion 380 | } 381 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Pages/VoiceChatPlayground.razor: -------------------------------------------------------------------------------- 1 | @page "/voicechat-playground" 2 | @using Amazon.Bedrock.Model; 3 | @using DotnetFMPlayground.App.Components 4 | @using DotnetFMPlayground.Core.Models; 5 | @using Amazon.Bedrock; 6 | @using Amazon.BedrockRuntime; 7 | @using Rockhead.Extensions 8 | @using Rockhead.Extensions.Anthropic 9 | @using System.Text 10 | @inject AmazonBedrockRuntimeClient BedrockRuntimeClient 11 | @inject AmazonBedrockClient BedrockClient 12 | @inject IJSRuntime JS 13 | @inject ISpeechRecognitionService recognitionService 14 | @inject ISpeechSynthesisService synthesisService 15 | 16 | Voice Chat Playground 17 | 18 | 19 | 20 | 21 | 22 | @foreach (var item in promptItems) 23 | { 24 | string label = item.Type == PromptItemType.User ? "User" : "Assistant"; 25 | 26 | @item.Prompt 27 | 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @if(isListening) 38 | { 39 | 40 | Listening 41 | } 42 | else 43 | { 44 | 45 | Talk 46 | } 47 | 48 | Stop speech synthesis 49 | 50 | 51 | 52 | 53 | 54 | 55 | @code { 56 | #region MODEL_CONFIGURATION_FIELDS 57 | private ModelConfigurator _modelConfigurator; 58 | 59 | private Model _model; 60 | 61 | private bool _streaming = false; 62 | 63 | private bool _messagesAPI = false; 64 | #endregion 65 | 66 | private System.Timers.Timer _timer = new System.Timers.Timer(100); 67 | 68 | private bool isListening = false; 69 | private bool isTalkEnable = true; 70 | 71 | private bool isThinking = false; 72 | 73 | private MudTextField promptField; 74 | 75 | private ICollection promptItems = new List(); 76 | 77 | protected override async Task OnInitializedAsync() 78 | { 79 | _timer.Elapsed += OnElapsed; 80 | await base.OnInitializedAsync(); 81 | StateHasChanged(); 82 | } 83 | 84 | private async void OnElapsed(object sender, System.Timers.ElapsedEventArgs e) 85 | { 86 | if (!synthesisService.Speaking.Result) 87 | { 88 | await EndSpeechSynthesis(); 89 | } 90 | } 91 | 92 | private async Task StartSpeechRecognition(MouseEventArgs e) 93 | { 94 | isListening = true; 95 | isTalkEnable = false; 96 | IDisposable recognitionServiceDisposable = await recognitionService.RecognizeSpeechAsync("en-us", OnRecognized); 97 | } 98 | 99 | private async Task StopSpeechSynthesis(MouseEventArgs e) 100 | { 101 | if(await synthesisService.Speaking) 102 | { 103 | await synthesisService.CancelAsync(); 104 | await EndSpeechSynthesis(); 105 | } 106 | } 107 | 108 | private async Task EndSpeechSynthesis() 109 | { 110 | _timer.Stop(); 111 | isTalkEnable = true; 112 | await this.InvokeAsync(StateHasChanged); 113 | } 114 | 115 | private async Task OnRecognized(string e) 116 | { 117 | await recognitionService.CancelSpeechRecognitionAsync(true); 118 | await promptField.SetText(e); 119 | } 120 | 121 | private async Task OnPromptChanged(string inputValue) 122 | { 123 | if (string.IsNullOrEmpty(inputValue)) 124 | return; 125 | 126 | Prompt prompt = new(); 127 | prompt.AddRange(promptItems); 128 | // at this point, you don't want to add newUserPrompt to the PromptItems collection because it would update the display while we don't have yet 129 | // the assistant answer 130 | PromptItem newUserPrompt = new(PromptItemType.User, inputValue); 131 | prompt.Add(newUserPrompt); 132 | 133 | string textResponse = string.Empty; 134 | try 135 | { 136 | isListening = false; 137 | if (_model.StreamingSupported && _streaming) 138 | { 139 | bool firstChunk = true; 140 | 141 | PromptItem outputPromptItem = new PromptItem(PromptItemType.FMAnswer, textResponse); 142 | await foreach (var chunk in InvokeModelWithStreamingAsync(prompt)) 143 | { 144 | if (firstChunk) 145 | { 146 | // now we can clear the PromptField and add the user prompt and the AI answer to the PromptItems collection to refresh the UI 147 | await promptField.Clear(); 148 | promptItems.Add(newUserPrompt); 149 | promptItems.Add(outputPromptItem); 150 | firstChunk = false; 151 | } 152 | outputPromptItem.Prompt += chunk; 153 | StateHasChanged(); 154 | await JS.InvokeVoidAsync("scrollToElement", "PromptId"); 155 | } 156 | textResponse = outputPromptItem.Prompt; 157 | } 158 | else 159 | { 160 | textResponse = await InvokeModelAsync(prompt); 161 | // now we can clear the PromptField and add the user prompt and the AI answer to the PromptItems collection to refresh the UI 162 | await promptField.Clear(); 163 | promptItems.Add(newUserPrompt); 164 | promptItems.Add(new PromptItem(PromptItemType.FMAnswer, textResponse)); 165 | StateHasChanged(); 166 | await JS.InvokeVoidAsync("scrollToElement", "PromptId"); 167 | } 168 | } 169 | catch (Exception e) 170 | { 171 | Console.WriteLine(e); 172 | StateHasChanged(); 173 | } 174 | 175 | isThinking = false; 176 | var utterance = new SpeechSynthesisUtterance 177 | { 178 | Lang = "en-us", 179 | Text = textResponse 180 | }; 181 | 182 | await synthesisService.SpeakAsync(utterance); 183 | _timer.Start(); 184 | } 185 | 186 | #region MODEL_CONFIGURATION_EVENT_CALLBACK 187 | private void OnSelectedModelChanged(Model model) 188 | { 189 | _model = model; 190 | } 191 | 192 | private void OnStreamingChanged(bool value) 193 | { 194 | _streaming = value; 195 | } 196 | 197 | private void OnMessagesAPIChanged(bool value) 198 | { 199 | _messagesAPI = value; 200 | } 201 | 202 | private void OnAddPromptFormat(string value) 203 | { 204 | } 205 | #endregion 206 | 207 | #region INVOKE_REGION 208 | private async Task InvokeModelAsync(Prompt prompt) 209 | { 210 | IFoundationModelResponse? response = null; 211 | StringBuilder textPromptBuilder = new StringBuilder(); 212 | if (_model is Model.TitanText) 213 | { 214 | foreach (var promptItem in prompt) 215 | { 216 | string role = promptItem.Type == PromptItemType.User ? "User" : "Bot"; 217 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 218 | } 219 | textPromptBuilder.Append("Bot: "); 220 | response = await BedrockRuntimeClient.InvokeTitanTextG1Async((Model.TitanText)_model, textPromptBuilder.ToString(), _modelConfigurator?.GetTitanTextGenerationConfig()); 221 | } 222 | else if (_model is Model.Jurassic2) 223 | { 224 | foreach (var promptItem in prompt) 225 | { 226 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 227 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 228 | } 229 | textPromptBuilder.Append("assistant: "); 230 | response = await BedrockRuntimeClient.InvokeJurassic2Async((Model.Jurassic2)_model, textPromptBuilder.ToString(), _modelConfigurator.GetJurassic2TextGenerationConfig()); 231 | } 232 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 233 | { 234 | foreach (var promptItem in prompt) 235 | { 236 | string role = promptItem.Type == PromptItemType.User ? "Human" : "Assistant"; 237 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 238 | } 239 | textPromptBuilder.Append("Assistant: "); 240 | response = await BedrockRuntimeClient.InvokeClaudeAsync((Model.Claude)_model, textPromptBuilder.ToString(), _modelConfigurator.GetClaudeTextGenerationConfig()); 241 | } 242 | else if (_model is Model.Claude) 243 | { 244 | ClaudeMessagesConfig? claudeMessagesConfig = _modelConfigurator.GetClaudeMessagesConfig(); 245 | claudeMessagesConfig ??= new ClaudeMessagesConfig() { MaxTokens = 200, TopP = 0.01f }; 246 | foreach (var promptItem in prompt) 247 | { 248 | string role = promptItem.Type == PromptItemType.User ? "user" : "assistant"; 249 | claudeMessagesConfig.Messages.Add(new ClaudeMessage() { Role = role, Content = new[] { new ClaudeTextContent() { Text = promptItem.Prompt } } }); 250 | } 251 | ClaudeMessage message = claudeMessagesConfig.Messages.Last(); 252 | claudeMessagesConfig.Messages.Remove(message); 253 | response = await BedrockRuntimeClient.InvokeClaudeMessagesAsync((Model.Claude)_model, message, claudeMessagesConfig); 254 | } 255 | else if (_model is Model.CommandText) 256 | { 257 | foreach (var promptItem in prompt) 258 | { 259 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 260 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 261 | } 262 | textPromptBuilder.Append("assistant: "); 263 | response = await BedrockRuntimeClient.InvokeCommandV14Async((Model.CommandText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetCommandTextGenerationConfig()); 264 | } 265 | else if (_model is Model.Llama213BChatV1 || _model is Model.Llama270BChatV1) 266 | { 267 | foreach (var promptItem in prompt) 268 | { 269 | if (promptItem.Type == PromptItemType.User) 270 | { 271 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 272 | } 273 | else 274 | { 275 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 276 | } 277 | } 278 | response = await BedrockRuntimeClient.InvokeLlamaAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig()); 279 | } 280 | else if (_model is Model.Llama38BInstructV1 || _model is Model.Llama370BInstructV1) 281 | { 282 | textPromptBuilder.Append("<|begin_of_text|>"); 283 | foreach (var promptItem in prompt) 284 | { 285 | if (promptItem.Type == PromptItemType.User) 286 | { 287 | textPromptBuilder.Append($"<|start_header_id|>user<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 288 | } 289 | else 290 | { 291 | textPromptBuilder.Append($"<|start_header_id|>assistant<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 292 | } 293 | } 294 | textPromptBuilder.Append("<|start_header_id|>assistant<|end_header_id|>"); 295 | response = await BedrockRuntimeClient.InvokeLlamaAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig()); 296 | } 297 | else if (_model is Model.Mistral) 298 | { 299 | foreach (var promptItem in prompt) 300 | { 301 | if (promptItem.Type == PromptItemType.User) 302 | { 303 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 304 | } 305 | else 306 | { 307 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 308 | } 309 | } 310 | response = await BedrockRuntimeClient.InvokeMistralAsync((Model.Mistral)_model, textPromptBuilder.ToString(), _modelConfigurator.GetMistralTextGenerationConfig()); 311 | } 312 | return response?.GetResponse(); 313 | } 314 | 315 | private async IAsyncEnumerable InvokeModelWithStreamingAsync(Prompt prompt) 316 | { 317 | StringBuilder textPromptBuilder = new StringBuilder(); 318 | if (_model is Model.TitanText) 319 | { 320 | foreach (var promptItem in prompt) 321 | { 322 | string role = promptItem.Type == PromptItemType.User ? "User" : "Bot"; 323 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 324 | } 325 | textPromptBuilder.Append("Bot: "); 326 | await foreach (var chunk in BedrockRuntimeClient.InvokeTitanTextG1WithResponseStreamAsync((Model.TitanText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetTitanTextGenerationConfig())) 327 | { 328 | yield return chunk.GetResponse(); 329 | } 330 | } 331 | else if (_model is Model.ClaudeTextCompletionSupport && !_messagesAPI) 332 | { 333 | foreach (var promptItem in prompt) 334 | { 335 | string role = promptItem.Type == PromptItemType.User ? "Human" : "Assistant"; 336 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 337 | } 338 | textPromptBuilder.Append("Assistant: "); 339 | await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeWithResponseStreamAsync((Model.Claude)_model, textPromptBuilder.ToString(), _modelConfigurator.GetClaudeTextGenerationConfig())) 340 | { 341 | yield return chunk.GetResponse(); 342 | } 343 | } 344 | else if (_model is Model.Claude) 345 | { 346 | ClaudeMessagesConfig? claudeMessagesConfig = _modelConfigurator.GetClaudeMessagesConfig(); 347 | claudeMessagesConfig ??= new ClaudeMessagesConfig() { MaxTokens = 200, TopP = 0.01f }; 348 | foreach (var promptItem in prompt) 349 | { 350 | string role = promptItem.Type == PromptItemType.User ? "user" : "assistant"; 351 | claudeMessagesConfig.Messages.Add(new ClaudeMessage() { Role = role, Content = new[] { new ClaudeTextContent() { Text = promptItem.Prompt } } }); 352 | } 353 | ClaudeMessage message = claudeMessagesConfig.Messages.Last(); 354 | claudeMessagesConfig.Messages.Remove(message); await foreach (var chunk in BedrockRuntimeClient.InvokeClaudeMessagesWithResponseStreamAsync((Model.Claude)_model, message, claudeMessagesConfig)) 355 | { 356 | yield return chunk.GetResponse(); 357 | } 358 | } 359 | else if (_model is Model.CommandText) 360 | { 361 | foreach (var promptItem in prompt) 362 | { 363 | string role = promptItem.Type == PromptItemType.User ? "human" : "assistant"; 364 | textPromptBuilder.Append($"{role}: {promptItem.Prompt}\n\n"); 365 | } 366 | textPromptBuilder.Append("assistant: "); 367 | await foreach (var chunk in BedrockRuntimeClient.InvokeCommandV14WithResponseStreamAsync((Model.CommandText)_model, textPromptBuilder.ToString(), _modelConfigurator.GetCommandTextGenerationConfig())) 368 | { 369 | yield return chunk.GetResponse(); 370 | } 371 | } 372 | else if (_model is Model.Llama213BChatV1 || _model is Model.Llama270BChatV1) 373 | { 374 | foreach (var promptItem in prompt) 375 | { 376 | if (promptItem.Type == PromptItemType.User) 377 | { 378 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 379 | } 380 | else 381 | { 382 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 383 | } 384 | } 385 | await foreach (var chunk in BedrockRuntimeClient.InvokeLlamaWithResponseStreamAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig())) 386 | { 387 | yield return chunk.GetResponse(); 388 | } 389 | } 390 | else if (_model is Model.Llama) 391 | { 392 | textPromptBuilder.Append("<|begin_of_text|>"); 393 | foreach (var promptItem in prompt) 394 | { 395 | if (promptItem.Type == PromptItemType.User) 396 | { 397 | textPromptBuilder.Append($"<|start_header_id|>user<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 398 | } 399 | else 400 | { 401 | textPromptBuilder.Append($"<|start_header_id|>assistant<|end_header_id|>{promptItem.Prompt}<|eot_id|>\n"); 402 | } 403 | } 404 | textPromptBuilder.Append("<|start_header_id|>assistant<|end_header_id|>"); 405 | await foreach (var chunk in BedrockRuntimeClient.InvokeLlamaWithResponseStreamAsync((Model.Llama)_model, textPromptBuilder.ToString(), _modelConfigurator.GetLlamaTextGenerationConfig())) 406 | { 407 | yield return chunk.GetResponse(); 408 | } 409 | } 410 | else if (_model is Model.Mistral) 411 | { 412 | foreach (var promptItem in prompt) 413 | { 414 | if (promptItem.Type == PromptItemType.User) 415 | { 416 | textPromptBuilder.Append($"\n[INST]\n{promptItem.Prompt}[/INST]\n"); 417 | } 418 | else 419 | { 420 | textPromptBuilder.Append($"{promptItem.Prompt}\n"); 421 | } 422 | } 423 | await foreach (var chunk in BedrockRuntimeClient.InvokeMistralWithResponseStreamAsync((Model.Mistral)_model, textPromptBuilder.ToString(), _modelConfigurator.GetMistralTextGenerationConfig())) 424 | { 425 | yield return chunk.GetResponse(); 426 | } 427 | } 428 | } 429 | #endregion 430 | } 431 | -------------------------------------------------------------------------------- /src/DotnetFMPlayground.App/Components/ModelConfigurator.razor: -------------------------------------------------------------------------------- 1 | @using Amazon.Bedrock; 2 | @using Amazon.Bedrock.Model; 3 | @using Rockhead.Extensions 4 | @using Rockhead.Extensions.Amazon; 5 | @using Rockhead.Extensions.Anthropic; 6 | @using Rockhead.Extensions.AI21Labs; 7 | @using Rockhead.Extensions.Cohere; 8 | @using Rockhead.Extensions.Meta; 9 | @using Rockhead.Extensions.MistralAI; 10 | @inject AmazonBedrockClient BedrockClient 11 | @inherits MudComponentBase; 12 | 13 | 14 | 15 | @if (_foundationModels != null) 16 | { 17 | foreach (var item in _foundationModels) 18 | { 19 | 20 | } 21 | } 22 | 23 | 24 | @if (PromptFormatAvailable()) 25 | { 26 | Prompt Format Template 27 | } 28 | @if (MessagesAPIAvailable()) 29 | { 30 | 31 | } 32 | @if (_model != null && _model.StreamingSupported) 33 | { 34 | 35 | } 36 | 37 | @if (_model != null && _model is Model.Claude) 38 | { 39 | 40 | } 41 | else if (_model != null && _model is Model.TitanText) 42 | { 43 | 44 | } 45 | else if (_model != null && _model is Model.Jurassic2) 46 | { 47 | 48 | } 49 | else if (_model != null && _model is Model.CommandText) 50 | { 51 | 52 | } 53 | else if (_model != null && _model is Model.Llama) 54 | { 55 | 56 | } 57 | else if (_model != null && _model is Model.Mistral) 58 | { 59 | 60 | } 61 | 62 | 63 | @code { 64 | private IEnumerable _foundationModels; 65 | 66 | private FoundationModelSummary _selectedModel; 67 | 68 | private Func _selectConverter = fms => fms == null ? "" : String.Concat(fms?.ModelName, " (", fms?.ModelId, ")"); 69 | 70 | private Model _model; 71 | 72 | private bool _streaming = false; 73 | 74 | private bool _messagesAPI = false; 75 | 76 | private InferenceParametersBase? _inferenceParametersBase = null; 77 | 78 | [Parameter, EditorRequired] public required string OutputModality { get; set; } 79 | 80 | protected override async Task OnInitializedAsync() 81 | { 82 | _foundationModels = (await BedrockClient.ListFoundationModelsAsync(new ListFoundationModelsRequest())).ModelSummaries.Where(x => x.OutputModalities.Contains(OutputModality) && Model.IsSupported(x.ModelId)); 83 | _selectedModel = _foundationModels.FirstOrDefault(); 84 | await UpdateModel(); 85 | await base.OnParametersSetAsync(); 86 | } 87 | 88 | private bool MessagesAPIAvailable() => 89 | _model switch 90 | { 91 | Model.ClaudeTextCompletionSupport => true, 92 | _ => false 93 | }; 94 | 95 | private bool PromptFormatAvailable() => 96 | _model switch 97 | { 98 | Model.ClaudeTextCompletionSupport => true, 99 | Model.Llama213BChatV1 => true, 100 | Model.Llama270BChatV1 => true, 101 | Model.Llama38BInstructV1 => true, 102 | Model.Llama370BInstructV1 => true, 103 | Model.Mistral => true, 104 | _ => false 105 | }; 106 | 107 | private const string _claudeTextGenerationTemplate = "Human: {{user_message}}\n\nAssistant:"; 108 | private const string _llama2ChatPromptTemplate = "\n[INST]\n<>\n{{ system_prompt }}\n<>\n{{ user_message }}\n[/INST]"; 109 | private const string _llama3InstructPromptTemplate = "<|begin_of_text|><|start_header_id|>system<|end_header_id|>{{ system_prompt }}<|eot_id|>\n<|start_header_id|>user<|end_header_id|>{{ user_message }}<|eot_id|>\n<|start_header_id|>assistant<|end_header_id|>"; 110 | private const string _mistralInstructTemplate = "[INST]{{ user_message }}[/INST]"; 111 | private string GetPromptFormat() => 112 | _model switch 113 | { 114 | Model.ClaudeTextCompletionSupport => _claudeTextGenerationTemplate, 115 | Model.Llama213BChatV1 => _llama2ChatPromptTemplate, 116 | Model.Llama270BChatV1 => _llama2ChatPromptTemplate, 117 | Model.Llama38BInstructV1 => _llama3InstructPromptTemplate, 118 | Model.Llama370BInstructV1 => _llama3InstructPromptTemplate, 119 | Model.Mistral => _mistralInstructTemplate, 120 | _ => string.Empty 121 | }; 122 | 123 | [Parameter] public EventCallback OnAddPromptFormat { get; set; } 124 | 125 | private async Task OnPromptFormatTemplateClick(MouseEventArgs evt) 126 | { 127 | await OnAddPromptFormat.InvokeAsync(GetPromptFormat()); 128 | } 129 | 130 | [Parameter] public EventCallback OnModelChanged { get; set; } 131 | 132 | private async Task OnSelectedModelChanged(FoundationModelSummary model) 133 | { 134 | _selectedModel = model; 135 | await UpdateModel(); 136 | } 137 | 138 | private async Task UpdateModel() 139 | { 140 | _model = Model.Parse(_selectedModel.ModelId); 141 | _streaming = false; 142 | _messagesAPI = false; 143 | await OnModelChanged.InvokeAsync(_model); 144 | await StreamingChanged(); 145 | await MessagesAPIChanged(); 146 | } 147 | 148 | [Parameter] public EventCallback OnStreamingChanged { get; set; } 149 | 150 | private async Task OnCheckedChanged(bool value) 151 | { 152 | _streaming = value; 153 | await StreamingChanged(); 154 | } 155 | 156 | private async Task StreamingChanged() 157 | { 158 | await OnStreamingChanged.InvokeAsync(_streaming); 159 | } 160 | 161 | [Parameter] public EventCallback OnMessagesAPIChanged { get; set; } 162 | 163 | private async Task OnMessagesAPICheckedChanged(bool value) 164 | { 165 | _messagesAPI = value; 166 | await MessagesAPIChanged(); 167 | } 168 | 169 | private async Task MessagesAPIChanged() 170 | { 171 | await OnMessagesAPIChanged.InvokeAsync(_messagesAPI); 172 | } 173 | 174 | public ClaudeTextGenerationConfig? GetClaudeTextGenerationConfig() 175 | { 176 | if (_inferenceParametersBase != null && 177 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 178 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_p") 179 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_k") 180 | || _inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample"))) 181 | { 182 | float? temperature = null; 183 | float? topP = null; 184 | int? topK = null; 185 | int maxTokensToSample = 200; 186 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 187 | { 188 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 189 | } 190 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_p")) 191 | { 192 | topP = (float)_inferenceParametersBase.InferenceParameters["top_p"]; 193 | } 194 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_k")) 195 | { 196 | topK = (int)_inferenceParametersBase.InferenceParameters["top_k"]; 197 | } 198 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample")) 199 | { 200 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["max_tokens_to_sample"]; 201 | } 202 | return new ClaudeTextGenerationConfig() 203 | { 204 | Temperature = temperature, 205 | TopP = topP, 206 | TopK = topK, 207 | MaxTokensToSample = maxTokensToSample 208 | }; 209 | } 210 | else 211 | { 212 | return null; 213 | } 214 | 215 | } 216 | 217 | public ClaudeMessagesConfig? GetClaudeMessagesConfig() 218 | { 219 | if (_inferenceParametersBase != null && 220 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 221 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_p") 222 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_k") 223 | || _inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample"))) 224 | { 225 | 226 | float? temperature = null; 227 | float? topP = null; 228 | int? topK = null; 229 | int maxTokensToSample = 200; 230 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 231 | { 232 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 233 | } 234 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_p")) 235 | { 236 | topP = (float)_inferenceParametersBase.InferenceParameters["top_p"]; 237 | } 238 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_k")) 239 | { 240 | topK = (int)_inferenceParametersBase.InferenceParameters["top_k"]; 241 | } 242 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample")) 243 | { 244 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["max_tokens_to_sample"]; 245 | } 246 | return new ClaudeMessagesConfig() 247 | { 248 | Temperature = temperature, 249 | TopP = topP, 250 | TopK = topK, 251 | MaxTokens = maxTokensToSample 252 | }; 253 | } 254 | else 255 | { 256 | return null; 257 | } 258 | } 259 | 260 | public TitanTextGenerationConfig? GetTitanTextGenerationConfig() 261 | { 262 | if (_inferenceParametersBase != null && 263 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 264 | || _inferenceParametersBase.InferenceParameters.ContainsKey("topP") 265 | || _inferenceParametersBase.InferenceParameters.ContainsKey("topK") 266 | || _inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount"))) 267 | { 268 | 269 | float? temperature = null; 270 | float? topP = null; 271 | int maxTokensToSample = 512; 272 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 273 | { 274 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 275 | } 276 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("topP")) 277 | { 278 | topP = (float)_inferenceParametersBase.InferenceParameters["topP"]; 279 | } 280 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount")) 281 | { 282 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["maxTokenCount"]; 283 | } 284 | return new TitanTextGenerationConfig() 285 | { 286 | Temperature = temperature ?? 0, 287 | TopP = topP ?? 1, 288 | MaxTokenCount = maxTokensToSample 289 | }; 290 | } 291 | else 292 | { 293 | return null; 294 | } 295 | } 296 | 297 | public Jurassic2TextGenerationConfig? GetJurassic2TextGenerationConfig() 298 | { 299 | if (_inferenceParametersBase != null && 300 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 301 | || _inferenceParametersBase.InferenceParameters.ContainsKey("topP") 302 | || _inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount"))) 303 | { 304 | float? temperature = null; 305 | float? topP = null; 306 | int maxTokensToSample = 512; 307 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 308 | { 309 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 310 | } 311 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("topP")) 312 | { 313 | topP = (float)_inferenceParametersBase.InferenceParameters["topP"]; 314 | } 315 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount")) 316 | { 317 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["maxTokenCount"]; 318 | } 319 | return new Jurassic2TextGenerationConfig() 320 | { 321 | Temperature = temperature ?? 0, 322 | TopP = topP ?? 1, 323 | MaxTokens = maxTokensToSample 324 | }; 325 | } 326 | else 327 | { 328 | return null; 329 | } 330 | } 331 | 332 | public CommandTextGenerationConfig? GetCommandTextGenerationConfig() 333 | { 334 | if (_inferenceParametersBase != null && 335 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 336 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_p") 337 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_k") 338 | || _inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample"))) 339 | { 340 | float? temperature = null; 341 | float? topP = null; 342 | int? topK = null; 343 | int? maxTokensToSample = null; 344 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 345 | { 346 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 347 | } 348 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_p")) 349 | { 350 | topP = (float)_inferenceParametersBase.InferenceParameters["top_p"]; 351 | } 352 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_k")) 353 | { 354 | topK = (int)_inferenceParametersBase.InferenceParameters["top_k"]; 355 | } 356 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample")) 357 | { 358 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["max_tokens_to_sample"]; 359 | } 360 | return new CommandTextGenerationConfig() 361 | { 362 | Temperature = temperature, 363 | P = topP, 364 | K = topK, 365 | MaxTokens = maxTokensToSample 366 | }; 367 | } 368 | else 369 | { 370 | return null; 371 | } 372 | } 373 | 374 | public LlamaTextGenerationConfig? GetLlamaTextGenerationConfig() 375 | { 376 | if (_inferenceParametersBase != null && 377 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 378 | || _inferenceParametersBase.InferenceParameters.ContainsKey("topP") 379 | || _inferenceParametersBase.InferenceParameters.ContainsKey("topK") 380 | || _inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount"))) 381 | { 382 | float? temperature = null; 383 | float? topP = null; 384 | int maxTokensToSample = 200; 385 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 386 | { 387 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 388 | } 389 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("topP")) 390 | { 391 | topP = (float)_inferenceParametersBase.InferenceParameters["topP"]; 392 | } 393 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("maxTokenCount")) 394 | { 395 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["maxTokenCount"]; 396 | } 397 | return new LlamaTextGenerationConfig() 398 | { 399 | Temperature = temperature ?? 0, 400 | TopP = topP ?? 1, 401 | MaxGenLen = maxTokensToSample 402 | }; 403 | } 404 | else 405 | { 406 | return null; 407 | } 408 | } 409 | 410 | public MistralTextGenerationConfig? GetMistralTextGenerationConfig() 411 | { 412 | if (_inferenceParametersBase != null && 413 | (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature") 414 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_p") 415 | || _inferenceParametersBase.InferenceParameters.ContainsKey("top_k") 416 | || _inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample"))) 417 | { 418 | float? temperature = null; 419 | float? topP = null; 420 | int? topK = null; 421 | int? maxTokensToSample = null; 422 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("temperature")) 423 | { 424 | temperature = (float)_inferenceParametersBase.InferenceParameters["temperature"]; 425 | } 426 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_p")) 427 | { 428 | topP = (float)_inferenceParametersBase.InferenceParameters["top_p"]; 429 | } 430 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("top_k")) 431 | { 432 | topK = (int)_inferenceParametersBase.InferenceParameters["top_k"]; 433 | } 434 | if (_inferenceParametersBase.InferenceParameters.ContainsKey("max_tokens_to_sample")) 435 | { 436 | maxTokensToSample = (int)_inferenceParametersBase.InferenceParameters["max_tokens_to_sample"]; 437 | } 438 | return new MistralTextGenerationConfig() 439 | { 440 | Temperature = temperature, 441 | TopP = topP, 442 | TopK = topK, 443 | MaxTokens = maxTokensToSample 444 | }; 445 | } 446 | else 447 | { 448 | return null; 449 | } 450 | } 451 | } 452 | --------------------------------------------------------------------------------