Skip to content

Commit d08713f

Browse files
committed
Update IChatClient with support from latest bedrock runtime / M.E.AI
- Adds support for multi-modal tool returns. - Adds support for citations with URIs. - Adds a ton of tests verifying IChatClient behavior around the underlying IAmazonBedrockRuntime.
1 parent 9998ae8 commit d08713f

File tree

7 files changed

+3679
-28
lines changed

7 files changed

+3679
-28
lines changed

extensions/src/AWSSDK.Extensions.Bedrock.MEAI/AWSSDK.Extensions.Bedrock.MEAI.NetFramework.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</Choose>
3838

3939
<ItemGroup>
40-
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.9.1" />
40+
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="10.0.1" />
4141
</ItemGroup>
4242

4343
<ItemGroup>

extensions/src/AWSSDK.Extensions.Bedrock.MEAI/AWSSDK.Extensions.Bedrock.MEAI.NetStandard.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</Choose>
4242

4343
<ItemGroup>
44-
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.9.1" />
44+
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="10.0.1" />
4545
</ItemGroup>
4646

4747
<ItemGroup>

extensions/src/AWSSDK.Extensions.Bedrock.MEAI/AWSSDK.Extensions.Bedrock.MEAI.nuspec

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@
1414
<dependencies>
1515
<group targetFramework="net472">
1616
<dependency id="AWSSDK.Core" version="4.0.3.3" />
17-
<dependency id="AWSSDK.BedrockRuntime" version="4.0.11.1" />
18-
<dependency id="Microsoft.Extensions.AI.Abstractions" version="9.9.1" />
17+
<dependency id="AWSSDK.BedrockRuntime" version="4.0.13" />
18+
<dependency id="Microsoft.Extensions.AI.Abstractions" version="10.0.1" />
1919
</group>
2020
<group targetFramework="netstandard2.0">
2121
<dependency id="AWSSDK.Core" version="4.0.3.3" />
22-
<dependency id="AWSSDK.BedrockRuntime" version="4.0.11.1" />
23-
<dependency id="Microsoft.Extensions.AI.Abstractions" version="9.9.1" />
22+
<dependency id="AWSSDK.BedrockRuntime" version="4.0.13" />
23+
<dependency id="Microsoft.Extensions.AI.Abstractions" version="10.0.1" />
2424
</group>
2525
<group targetFramework="net8.0">
2626
<dependency id="AWSSDK.Core" version="4.0.3.3" />
27-
<dependency id="AWSSDK.BedrockRuntime" version="4.0.11.1" />
28-
<dependency id="Microsoft.Extensions.AI.Abstractions" version="9.9.1" />
27+
<dependency id="AWSSDK.BedrockRuntime" version="4.0.13" />
28+
<dependency id="Microsoft.Extensions.AI.Abstractions" version="10.0.1" />
2929
</group>
3030
</dependencies>
3131
</metadata>

extensions/src/AWSSDK.Extensions.Bedrock.MEAI/BedrockChatClient.cs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ public async Task<ChatResponse> GetResponseAsync(
108108
TextContent tc = new(citations.Content[i]?.Text) { RawRepresentation = citations.Content[i] };
109109
tc.Annotations = [new CitationAnnotation()
110110
{
111+
Snippet = citations.Citations[i].SourceContent?.Select(c => c.Text).FirstOrDefault() ?? citations.Citations[i].Source,
111112
Title = citations.Citations[i].Title,
112-
Snippet = citations.Citations[i].SourceContent?.Select(c => c.Text).FirstOrDefault(),
113+
Url = Uri.TryCreate(citations.Citations[i].Location?.Web?.Url, UriKind.Absolute, out Uri? uri) ? uri : null,
113114
}];
114115
result.Contents.Add(tc);
115116
}
@@ -398,8 +399,7 @@ private static List<SystemContentBlock> CreateSystem(List<SystemContentBlock>? r
398399
});
399400
}
400401

401-
foreach (var message in messages
402-
.Where(m => m.Role == ChatRole.System && m.Contents.Any(c => c is TextContent)))
402+
foreach (var message in messages.Where(m => m.Role == ChatRole.System && m.Contents.Any(c => c is TextContent)))
403403
{
404404
system.Add(new SystemContentBlock()
405405
{
@@ -500,6 +500,10 @@ private static List<ContentBlock> CreateContents(ChatMessage message)
500500
{
501501
switch (content)
502502
{
503+
case AIContent when content.RawRepresentation is ContentBlock cb:
504+
contents.Add(cb);
505+
break;
506+
503507
case TextContent tc:
504508
if (message.Role == ChatRole.Assistant)
505509
{
@@ -582,32 +586,54 @@ private static List<ContentBlock> CreateContents(ChatMessage message)
582586
break;
583587

584588
case FunctionResultContent frc:
585-
Document result = frc.Result switch
586-
{
587-
int i => i,
588-
long l => l,
589-
float f => f,
590-
double d => d,
591-
string s => s,
592-
bool b => b,
593-
JsonElement json => ToDocument(json),
594-
{ } other => ToDocument(JsonSerializer.SerializeToElement(other, BedrockJsonContext.DefaultOptions.GetTypeInfo(other.GetType()))),
595-
_ => default,
596-
};
597-
598589
contents.Add(new()
599590
{
600591
ToolResult = new()
601592
{
602593
ToolUseId = frc.CallId,
603-
Content = [new() { Json = new Document(new Dictionary<string, Document>() { ["result"] = result }) }],
594+
Content = ToToolResultContentBlocks(frc.Result),
604595
},
605596
});
606597
break;
607598
}
608599

600+
static List<ToolResultContentBlock> ToToolResultContentBlocks(object? result) =>
601+
result switch
602+
{
603+
AIContent aic => [ToolResultContentBlockFromAIContent(aic)],
604+
IEnumerable<AIContent> aics => [.. aics.Select(ToolResultContentBlockFromAIContent)],
605+
string s => [new () { Text = s }],
606+
_ => [new()
607+
{
608+
Json = new Document(new Dictionary<string, Document>()
609+
{
610+
["result"] = result switch
611+
{
612+
int i => i,
613+
long l => l,
614+
float f => f,
615+
double d => d,
616+
bool b => b,
617+
JsonElement json => ToDocument(json),
618+
{ } other => ToDocument(JsonSerializer.SerializeToElement(other, BedrockJsonContext.DefaultOptions.GetTypeInfo(other.GetType()))),
619+
_ => default,
620+
}
621+
})
622+
}],
623+
};
624+
625+
static ToolResultContentBlock ToolResultContentBlockFromAIContent(AIContent aic) =>
626+
aic switch
627+
{
628+
TextContent tc => new() { Text = tc.Text },
629+
TextReasoningContent trc => new() { Text = trc.Text },
630+
DataContent dc when GetImageFormat(dc.MediaType) is { } imageFormat => new() { Image = new() { Source = new() { Bytes = new(dc.Data.ToArray()) }, Format = imageFormat } },
631+
DataContent dc when GetVideoFormat(dc.MediaType) is { } videoFormat => new() { Video = new() { Source = new() { Bytes = new(dc.Data.ToArray()) }, Format = videoFormat } },
632+
DataContent dc when GetDocumentFormat(dc.MediaType) is { } docFormat => new() { Document = new() { Source = new() { Bytes = new(dc.Data.ToArray()) }, Format = docFormat, Name = dc.Name ?? "file" } },
633+
_ => ToToolResultContentBlocks(JsonSerializer.SerializeToElement(aic, BedrockJsonContext.DefaultOptions.GetTypeInfo(typeof(object)))).First(),
634+
};
609635

610-
if (content.AdditionalProperties?.TryGetValue(nameof(ContentBlock.CachePoint), out var maybeCachePoint) == true)
636+
if (content.AdditionalProperties?.TryGetValue(nameof(ContentBlock.CachePoint), out var maybeCachePoint) is true)
611637
{
612638
if (maybeCachePoint is CachePointBlock cachePointBlock)
613639
{

0 commit comments

Comments
 (0)