-
Notifications
You must be signed in to change notification settings - Fork 868
Update to Microsoft.Extensions.AI.Abstractions 9.8.0 #3960
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
base: development
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -83,6 +83,7 @@ public async Task<ChatResponse> GetResponseAsync( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ChatMessage result = new() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CreatedAt = DateTimeOffset.UtcNow, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RawRepresentation = response.Output?.Message, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Role = ChatRole.Assistant, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MessageId = Guid.NewGuid().ToString("N"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -97,6 +98,21 @@ public async Task<ChatResponse> GetResponseAsync( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.Contents.Add(new TextContent(text) { RawRepresentation = content }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (content.CitationsContent is { } citations) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int count = Math.Min(citations.Citations?.Count ?? 0, citations.Content?.Count ?? 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for (int i = 0; i < count; i++) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TextContent tc = new(citations.Content![i]?.Text) { RawRepresentation = citations.Content![i] }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tc.Annotations = [new CitationAnnotation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Title = citations.Citations![i].Title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Snippet = citations.Citations![i].SourceContent?.Select(c => c.Text).FirstOrDefault(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.Contents.Add(tc); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential null reference exception when citations.Content[i] is null. The null-forgiving operator ! is used but citations.Content[i] could still be null even after the count check.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential null reference exception when citations.Citations[i] is null. The null-forgiving operator ! is used but citations.Citations[i] could still be null even after the count check.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential null reference exception when citations.Citations[i] is null. The null-forgiving operator ! is used but citations.Citations[i] could still be null even after the count check.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (content.ReasoningContent is { ReasoningText.Text: not null } reasoningContent) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TextReasoningContent trc = new(reasoningContent.ReasoningText.Text) { RawRepresentation = content }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -126,7 +142,11 @@ public async Task<ChatResponse> GetResponseAsync( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (content.Document is { Source.Bytes: { } documentBytes, Format: { } documentFormat }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.Contents.Add(new DataContent(documentBytes.ToArray(), GetMimeType(documentFormat)) { RawRepresentation = content }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.Contents.Add(new DataContent(documentBytes.ToArray(), GetMimeType(documentFormat)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RawRepresentation = content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Name = content.Document.Name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (content.ToolUse is { } toolUse) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -143,7 +163,7 @@ public async Task<ChatResponse> GetResponseAsync( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return new(result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CreatedAt = DateTimeOffset.UtcNow, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CreatedAt = result.CreatedAt, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FinishReason = response.StopReason is not null ? GetChatFinishReason(response.StopReason) : null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RawRepresentation = response, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ResponseId = Guid.NewGuid().ToString("N"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -205,14 +225,26 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (contentBlockDelta.Delta.Text is string text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yield return new(ChatRole.Assistant, text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ChatResponseUpdate textUpdate = new(ChatRole.Assistant, text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CreatedAt = DateTimeOffset.UtcNow, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MessageId = messageId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RawRepresentation = update, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FinishReason = finishReason, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ResponseId = responseId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (contentBlockDelta.Delta.Citation is { } citation && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(citation.Title is not null || citation.SourceContent is { Count: > 0 })) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
textUpdate.Contents[0].Annotations = [new CitationAnnotation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Title = citation.Title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Snippet = citation.SourceContent?.Select(c => c.Text).FirstOrDefault(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
yield return textUpdate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (contentBlockDelta.Delta.ReasoningContent is { Text: not null } reasoningContent) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -468,6 +500,7 @@ private static List<ContentBlock> CreateContents(ChatMessage message) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Source = new() { Bytes = new(dc.Data.ToArray()) }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Format = docFormat, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Name = dc.Name ?? "file", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,197 @@ | ||||||||||||||||||||||||
/* | ||||||||||||||||||||||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||||||||||||||||||||||||
* You may not use this file except in compliance with the License. | ||||||||||||||||||||||||
* A copy of the License is located at | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* http://aws.amazon.com/apache2.0 | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* or in the "license" file accompanying this file. This file is distributed | ||||||||||||||||||||||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||||||||||||||||||||||||
* express or implied. See the License for the specific language governing | ||||||||||||||||||||||||
* permissions and limitations under the License. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
using Amazon.BedrockRuntime.Model; | ||||||||||||||||||||||||
using Microsoft.Extensions.AI; | ||||||||||||||||||||||||
using System; | ||||||||||||||||||||||||
using System.Diagnostics; | ||||||||||||||||||||||||
using System.Diagnostics.CodeAnalysis; | ||||||||||||||||||||||||
using System.IO; | ||||||||||||||||||||||||
using System.Text.Json; | ||||||||||||||||||||||||
using System.Text.Json.Nodes; | ||||||||||||||||||||||||
using System.Threading; | ||||||||||||||||||||||||
using System.Threading.Tasks; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
namespace Amazon.BedrockRuntime; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
[Experimental("MEAI001")] | ||||||||||||||||||||||||
internal sealed partial class BedrockImageGenerator : IImageGenerator | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
/// <summary>The wrapped <see cref="IAmazonBedrockRuntime"/> instance.</summary> | ||||||||||||||||||||||||
private readonly IAmazonBedrockRuntime _runtime; | ||||||||||||||||||||||||
/// <summary>Default model ID to use when no model is specified in the request.</summary> | ||||||||||||||||||||||||
private readonly string? _modelId; | ||||||||||||||||||||||||
/// <summary>Metadata describing the image generator.</summary> | ||||||||||||||||||||||||
private readonly ImageGeneratorMetadata _metadata; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/// <summary> | ||||||||||||||||||||||||
/// Initializes a new instance of the <see cref="BedrockImageGenerator"/> class. | ||||||||||||||||||||||||
/// </summary> | ||||||||||||||||||||||||
/// <param name="runtime">The <see cref="IAmazonBedrockRuntime"/> instance to wrap.</param> | ||||||||||||||||||||||||
/// <param name="defaultModelId">Model ID to use as the default when no model ID is specified in a request.</param> | ||||||||||||||||||||||||
public BedrockImageGenerator(IAmazonBedrockRuntime runtime, string? defaultModelId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
Debug.Assert(runtime is not null); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
_runtime = runtime!; | ||||||||||||||||||||||||
_modelId = defaultModelId; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
_metadata = new(AmazonBedrockRuntimeExtensions.ProviderName, defaultModelId: defaultModelId); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void Dispose() | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
// Do not dispose of _runtime, as this instance doesn't own it. | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/// <inheritdoc /> | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/// <inheritdoc /> | ||||||||||||||||||||||||
public object? GetService(Type serviceType, object? serviceKey) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (serviceType is null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
throw new ArgumentNullException(nameof(serviceType)); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return | ||||||||||||||||||||||||
serviceKey is not null ? null : | ||||||||||||||||||||||||
serviceType == typeof(ImageGeneratorMetadata) ? _metadata : | ||||||||||||||||||||||||
serviceType.IsInstanceOfType(_runtime) ? _runtime : | ||||||||||||||||||||||||
serviceType.IsInstanceOfType(this) ? this : | ||||||||||||||||||||||||
null; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public async Task<ImageGenerationResponse> GenerateAsync( | ||||||||||||||||||||||||
ImageGenerationRequest request, ImageGenerationOptions? options = null, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (request is null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
throw new ArgumentNullException(nameof(request)); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
int numImages = options?.Count ?? 1; | ||||||||||||||||||||||||
if (numImages < 1) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
throw new ArgumentOutOfRangeException(nameof(options), "The number of images must be at least 1."); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
InvokeModelRequest invokeRequest = options?.RawRepresentationFactory?.Invoke(this) as InvokeModelRequest ?? new(); | ||||||||||||||||||||||||
invokeRequest.ModelId ??= options?.ModelId ?? _modelId; | ||||||||||||||||||||||||
invokeRequest.Accept ??= "application/json"; | ||||||||||||||||||||||||
invokeRequest.ContentType ??= "application/json"; | ||||||||||||||||||||||||
if (invokeRequest.Body is null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
JsonObject body = new(); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Each model has its own way of specifying the prompt and image generation parameters, unfortunately. | ||||||||||||||||||||||||
// The following logic handles the most common cases today, but may need to be extended for | ||||||||||||||||||||||||
// future models. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (invokeRequest.ModelId?.IndexOf("stability", StringComparison.OrdinalIgnoreCase) >= 0) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
// Stability AI models | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stability models should also permit a starting image we should pass that in for edit. https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-diffusion-3-text-image.html |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (invokeRequest.ModelId?.IndexOf("stable-diffusion", StringComparison.OrdinalIgnoreCase) >= 0) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
JsonArray textPrompts = new(); | ||||||||||||||||||||||||
for (int i = 0; i < numImages; i++) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
textPrompts.Add((JsonNode)new JsonObject { ["text"] = request.Prompt ?? "" }); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
body["text_prompts"] = textPrompts; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (options?.ImageSize?.Width is int width && options.ImageSize?.Height is int height) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
body["width"] = width; | ||||||||||||||||||||||||
body["height"] = height; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
else | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
body["prompt"] = request.Prompt ?? ""; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
else | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
// Amazon models (e.g. Titan, Nova Canvas) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These also support a starting image -- https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-image.html |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
body["taskType"] = "TEXT_IMAGE"; | ||||||||||||||||||||||||
body["textToImageParams"] = new JsonObject { ["text"] = request.Prompt ?? "" }; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
JsonObject imageGenerationConfig = new() | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
["seed"] = | ||||||||||||||||||||||||
#if NET | ||||||||||||||||||||||||
Random.Shared.Next(), | ||||||||||||||||||||||||
#else | ||||||||||||||||||||||||
new Random().Next(), | ||||||||||||||||||||||||
#endif | ||||||||||||||||||||||||
}; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (options?.ImageSize?.Width is int width && options.ImageSize?.Height is int height) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
imageGenerationConfig["width"] = width; | ||||||||||||||||||||||||
imageGenerationConfig["height"] = height; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (numImages > 1) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
imageGenerationConfig["numberOfImages"] = numImages; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
body["imageGenerationConfig"] = imageGenerationConfig; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
invokeRequest.Body = new MemoryStream(JsonSerializer.SerializeToUtf8Bytes(body, BedrockJsonContext.Default.JsonNode)); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
InvokeModelResponse rawResponse = await _runtime.InvokeModelAsync(invokeRequest, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
ImageGenerationResponse result = new() { RawRepresentation = rawResponse }; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
using JsonDocument doc = JsonDocument.Parse(rawResponse.Body); | ||||||||||||||||||||||||
JsonElement root = doc.RootElement; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (root.TryGetProperty("artifacts", out JsonElement artifactElement) && artifactElement.ValueKind == JsonValueKind.Array) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
foreach (var element in artifactElement.EnumerateArray()) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (element.TryGetProperty("base64", out JsonElement base64Element) && | ||||||||||||||||||||||||
base64Element.ValueKind == JsonValueKind.String) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
result.Contents.Add(new DataContent(Convert.FromBase64String(base64Element.GetString()!), "image/png")); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MIME type is hardcoded as 'image/png' but different models may return different image formats. Consider determining the actual format from the response or making it configurable.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
else if (root.TryGetProperty("images", out JsonElement imagesElement) && imagesElement.ValueKind == JsonValueKind.Array) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
foreach (var image in imagesElement.EnumerateArray()) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (image.ValueKind == JsonValueKind.String) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
result.Contents.Add(new DataContent(Convert.FromBase64String(image.GetString()!), "image/png")); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MIME type is hardcoded as 'image/png' but different models may return different image formats. Consider determining the actual format from the response or making it configurable.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (result.Contents is not { Count: > 0 }) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
throw new InvalidOperationException("Image generation did not produce any images."); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return result; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
#if !NET | ||
namespace System.Diagnostics.CodeAnalysis; | ||
|
||
// Polyfill for [Experimental] | ||
|
||
[AttributeUsage( | ||
AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | | ||
AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | | ||
AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, | ||
Inherited = false)] | ||
internal sealed class ExperimentalAttribute : Attribute | ||
{ | ||
public ExperimentalAttribute(string diagnosticId) => DiagnosticId = diagnosticId; | ||
public string DiagnosticId { get; } | ||
public string? Message { get; set; } | ||
public string? UrlFormat { get; set; } | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ | |
</PropertyGroup> | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add tests for the citation changes? |
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.7.0" /> | ||
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.8.0" /> | ||
<PackageReference Include="xunit" Version="2.9.2" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> | ||
</ItemGroup> | ||
|
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.
im not a huge fan of the ! null checks everywhere (unless there was a specific reason you needed them?). wondering if we can do something like below instead?
i havent tested it but it gives the general idea
same in other places