-
Notifications
You must be signed in to change notification settings - Fork 862
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Microsoft.Extensions.AI's IChatClient / IEmbeddingGenerator for IAmazonBedrockRuntime #3545
base: v4-development
Are you sure you want to change the base?
Conversation
…meClient This enables AmazonBedrockRuntimeClient to be used as a Microsoft.Extensions.AI.IChatClient, such that it can implicitly be used by any consumer that operates on an IChatClient, and with any middleware written in terms of IChatClient, such as those components in the Microsoft.Extensions.AI package that provide support for automatic function invocation, OpenTelemetry, logging, distributed caching, and more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @stephentoub for the PR. Looks like a really interesting integration. The AWSSDK. packages are meant to be the low level service clients with limited dependencies. Can you create a new project under the extensions folder https://github.com/aws/aws-sdk-net/tree/main/extensions and put the code changes you put in AWSSDK.Bedrock into that project. The new project would have a project dependency on AWSSDK.Bedrock. I'm thinking the project could be named AWSSDK.Extensions.IChatClient.Bedrock unless you have a better product name.
Ok. As a core .NET library I thought the dependency would be acceptable, but I can separate it out into a separate project. I would like to leave the IAsyncEnumerable implementation in place, though; that's important for scale / correctness, and without it, examples like those at https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/csharp_bedrock-runtime_code_examples.html are problematic, because while they're trying to be async, they're actually doing the bulk of the work doing synchronous I/O. |
Agree the code adding |
(Haven't forgotten about this, just been busy with the .NET 9 release this week. Will update the PR soon.) |
@stephentoub We figured as much. Great talk today! |
@normj, updated. I created a new AWSSDK.Extensions.Bedrock.MEAI project, which now includes both the IChatClient implementation and an implementation of IEmbeddingGenerator. As they're now not in the same assembly as AmazonBedrockRuntimeClient, I instead exposed them via AsChatClient and AsEmbeddingGenerator extension methods off of IAmazonBedrockRuntime, which is the same general shape we've used for other cases with other providers. |
(I should note, M.E.AI.Abstractions is still preview, and we do expect minor breaking changes here and there. I'll aim to submit PRs to keep this assembly up-to-date with the latest.) |
I'd included a video showing using IChatClient. Here's an example using IEmbeddingGenerator: using Amazon;
using Amazon.BedrockRuntime;
using Microsoft.Extensions.AI;
using System.Numerics.Tensors;
var generator = new AmazonBedrockRuntimeClient(
Environment.GetEnvironmentVariable("AI:Bedrock:AccessKey"),
Environment.GetEnvironmentVariable("AI:Bedrock:SecretAccessKey"),
RegionEndpoint.USEast1).AsEmbeddingGenerator("amazon.titan-embed-text-v1");
var inputEmbedding = await generator.GenerateEmbeddingVectorAsync("What is an amphibian?");
var embeddings = await generator.GenerateAndZipAsync(
[
"Cos'è un anfibio?",
"A frog is an amphibian.",
"Frogs, toads, and salamanders are all examples.",
"Amphibians are four-limbed and ectothermic vertebrates of the class Amphibia.",
"They are four-limbed and ectothermic vertebrates.",
"A frog is green.",
"A tree is green.",
"It's not easy bein' green.",
"A dog is a mammal.",
"A dog is a man's best friend.",
"You ain't never had a friend like me.",
"Rachel, Monica, Phoebe, Joey, Chandler, Ross"
]);
foreach (var e in embeddings
.Select(e => (e.Value, Similarity: TensorPrimitives.CosineSimilarity(inputEmbedding.Span, e.Embedding.Vector.Span)))
.OrderByDescending(e => e.Similarity))
{
Console.WriteLine($"{e.Similarity:0.000}: {e.Value}");
} resulting in:
|
@stephentoub Thanks for the update. Going through it but bare with me while I get a better understanding of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comment only, overall LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comments are non-blocking, but would like to see what norm thinks on the lang version. That and the nullable reference types are the two main differences I see between this extensions package and the others. Don't see an issue with either
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
|
||
<LangVersion>Latest</LangVersion> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll let @normj chime in here, but normally we don't set the LangVersion
for NetStandard
and NetFramework
projects, but since this is an extension package I think there is more flexibility here. For example, I see the null forgiving operator sprinkled in the PR but if we dropped the LangVersion
we wouldn't be able to use that. (For NetStandard2.0 at least). I don't see a problem with it myself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. My preference is always to use the latest language version and to keep the code as modern as possible, but I'm happy to adapt to whatever you'd like here.
I'd recommend this talk from .NET Conf 2024 last week: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to make a few changes to the example code provided in the description for it to work, but other than that the change looks good. Thanks for the contribution!
In the video in the description? Yeah, that was from the original design, before I was asked to move it to a separate library, at which point it needed a method to create the implementation. I assume the only change was needing to call AsChatClient? Or something else as well?
You're welcome! Looking forward to being able to reference the nuget package when it's available. |
Yup I had to add |
I wouldn't have expected that to actually be necessary... was it? |
ah I could've sworn it was complaining about not being able to convert |
Cool, thanks |
Description
This enables
AmazonBedrockRuntimeClient
to be used as aMicrosoft.Extensions.AI.IChatClient
, such that it can implicitly be used by any consumer that operates on anIChatClient
, and with any middleware written in terms ofIChatClient
, such as those components in theMicrosoft.Extensions.AI
package that provide support for automatic function invocation, OpenTelemetry, logging, distributed caching, and more.https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/
Motivation and Context
IChatClient
was recently introduced as a core .NET abstraction for representing AI chat completion services. AmazonBedrockRuntimeClient is a natural fit for implementing this interface, such that application and library code can be written in terms ofIChatClient
and "just work" with AWS.Testing
Ran manually ad-hoc tests against the implementation, e.g.
Recording.2024-11-05.213108.mp4
Screenshots (if appropriate)
Types of changes
Checklist
License