Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/MSBuild/Packaging.targets
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/overview -->
<EnablePackageValidation>true</EnablePackageValidation>

<PackageValidationBaselineVersion Condition=" '$(Stage)' == 'normal' ">9.8.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion Condition="'$(Stage)' == 'normal' and '$(PackageValidationBaselineVersion)' == ''">9.10.0</PackageValidationBaselineVersion>
</PropertyGroup>

<!-- Verify that the minimum supported TFM is actually used. -->
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup Label="Version settings">
<MajorVersion>9</MajorVersion>
<MinorVersion>10</MinorVersion>
<PatchVersion>0</PatchVersion>
<PatchVersion>1</PatchVersion>
<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
<PreReleaseVersionIteration>1</PreReleaseVersionIteration>
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
Expand Down
1 change: 1 addition & 0 deletions eng/packages/General.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="System.Numerics.Tensors" Version="$(SystemNumericsTensorsVersion)" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Runtime.Caching" Version="$(SystemRuntimeCachingVersion)" />
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
Expand Down
1 change: 0 additions & 1 deletion eng/packages/TestOnly.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<PackageVersion Include="Polly.Testing" Version="8.4.2" />
<PackageVersion Include="StrongNamer" Version="0.2.5" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerVersion)" />
<PackageVersion Include="System.Numerics.Tensors" Version="$(SystemNumericsTensorsVersion)" />
<PackageVersion Include="Verify.Xunit" Version="28.15.0" />
<PackageVersion Include="Xunit.Combinatorial" Version="1.6.24" />
<PackageVersion Include="xunit.extensibility.execution" Version="$(XUnitVersion)" />
Expand Down
2 changes: 1 addition & 1 deletion src/Libraries/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4533,7 +4533,7 @@ dotnet_diagnostic.S3994.severity = none
# Title : URI return values should not be strings
# Category : Major Code Smell
# Help Link: https://rules.sonarsource.com/csharp/RSPEC-3995
dotnet_diagnostic.S3995.severity = warning
dotnet_diagnostic.S3995.severity = suggestion

# Title : URI properties should not be strings
# Category : Major Code Smell
Expand Down
12 changes: 10 additions & 2 deletions src/Libraries/Microsoft.Extensions.AI.Abstractions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

## NOT YET RELEASED

- Updated `HostedMcpServerTool` to allow for non-`Uri` server addresses, in order to enable built-in names.
- Updated `HostedMcpServerTool` to replace the header collection with an `AuthorizationToken` property.

## 9.10.0

- Added protected copy constructors to options types (e.g. `ChatOptions`).
- Added `[Experimental]` support for background responses, such that non-streaming responses are allowed to be pollable and responses / response updates can be tagged with continuation tokens to support later resumption.
- Updated `AIFunctionFactory.Create` to produce better default names for lambdas and local functions.
- Fixed `AIJsonUtilities.DefaultOptions` to handle the built-in `[Experimental]` `AIContent` types, like `FunctionApprovalRequestContent`.
- Fixed `ToChatResponse{Async}` to factor `ChatResponseUpdate.AuthorName` into message boundary detection.
- Fixed `ToChatResponse{Async}` to not overwrite `ChatMessage/ChatResponse.CreatedAt` with older timestamps during coalescing.
- Fixed `EmbeddingGeneratorOptions`/`SpeechToTextOptions` `Clone` methods to correctly copy all properties.
- Fixed `ToChatResponse` to not overwrite `ChatMessage/ChatResponse.CreatedAt` with older timestamps during coalescing.
- Added `[Experimental]` support for background responses, such that non-streaming responses are allowed to be pollable, and such that responses and response updates can be tagged with continuation tokens to support later resumption.

## 9.9.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,24 @@ protected ChatOptions(ChatOptions? other)
public IList<string>? StopSequences { get; set; }

/// <summary>
/// Gets or sets a flag to indicate whether a single response is allowed to include multiple tool calls.
/// If <see langword="false"/>, the <see cref="IChatClient"/> is asked to return a maximum of one tool call per request.
/// If <see langword="true"/>, there is no limit.
/// If <see langword="null"/>, the provider may select its own default.
/// Gets or sets a value that indicates whether a single response is allowed to include multiple tool calls.
/// </summary>
/// <value>
/// <see langword="true"/> for no limit. <see langword="false"/> if the <see cref="IChatClient"/> is asked to return a maximum of one tool call per request. If <see langword="null"/>, the provider can select its own default.
/// </value>
/// <remarks>
/// <para>
/// When used with function calling middleware, this does not affect the ability to perform multiple function calls in sequence.
/// It only affects the number of function calls within a single iteration of the function calling loop.
/// </para>
/// <para>
/// The underlying provider is not guaranteed to support or honor this flag. For example it may choose to ignore it and return multiple tool calls regardless.
/// The underlying provider is not guaranteed to support or honor this flag. For example it might choose to ignore it and return multiple tool calls regardless.
/// </para>
/// </remarks>
public bool? AllowMultipleToolCalls { get; set; }

/// <summary>Gets or sets the tool mode for the chat request.</summary>
/// <remarks>The default value is <see langword="null"/>, which is treated the same as <see cref="ChatToolMode.Auto"/>.</remarks>
/// <value>The default is <see langword="null"/>, which is treated the same as <see cref="ChatToolMode.Auto"/>.</value>
public ChatToolMode? ToolMode { get; set; }

/// <summary>Gets or sets the list of tools to include with a chat request.</summary>
Expand All @@ -165,12 +165,12 @@ protected ChatOptions(ChatOptions? other)
/// and polled for completion by non-streaming APIs.
/// </para>
/// <para>
/// When this property is set to true, non-streaming APIs may start a background operation and return an initial
/// When this property is set to <see langword="true" />, non-streaming APIs have permission to start a background operation and return an initial
/// response with a continuation token. Subsequent calls to the same API should be made in a polling manner with
/// the continuation token to get the final result of the operation.
/// </para>
/// <para>
/// When this property is set to true, streaming APIs may also start a background operation and begin streaming
/// When this property is set to <see langword="true" />, streaming APIs are also permitted to start a background operation and begin streaming
/// response updates until the operation is completed. If the streaming connection is interrupted, the
/// continuation token obtained from the last update that has one should be supplied to a subsequent call to the same streaming API
/// to resume the stream from the point of interruption and continue receiving updates until the operation is completed.
Expand All @@ -189,10 +189,10 @@ protected ChatOptions(ChatOptions? other)
/// This property is used for background responses that can be activated via the <see cref="AllowBackgroundResponses"/>
/// property if the <see cref="IChatClient"/> implementation supports them.
/// Streamed background responses, such as those returned by default by <see cref="IChatClient.GetStreamingResponseAsync"/>,
/// can be resumed if interrupted. This means that a continuation token obtained from the <see cref="ChatResponseUpdate.ContinuationToken"/>
/// can be resumed if interrupted. This means that a continuation token obtained from the <see cref="ChatResponseUpdate.ContinuationToken"/>
/// of an update just before the interruption occurred can be passed to this property to resume the stream from the point of interruption.
/// Non-streamed background responses, such as those returned by <see cref="IChatClient.GetResponseAsync"/>,
/// can be polled for completion by obtaining the token from the <see cref="ChatResponse.ContinuationToken"/> property
/// can be polled for completion by obtaining the token from the <see cref="ChatResponse.ContinuationToken"/> property
/// and passing it to this property on subsequent calls to <see cref="IChatClient.GetResponseAsync"/>.
/// </remarks>
[Experimental("MEAI001")]
Expand All @@ -203,17 +203,17 @@ protected ChatOptions(ChatOptions? other)
/// Gets or sets a callback responsible for creating the raw representation of the chat options from an underlying implementation.
/// </summary>
/// <remarks>
/// The underlying <see cref="IChatClient" /> implementation may have its own representation of options.
/// The underlying <see cref="IChatClient" /> implementation might have its own representation of options.
/// When <see cref="IChatClient.GetResponseAsync" /> or <see cref="IChatClient.GetStreamingResponseAsync" />
/// is invoked with a <see cref="ChatOptions" />, that implementation may convert the provided options into
/// is invoked with a <see cref="ChatOptions" />, that implementation might convert the provided options into
/// its own representation in order to use it while performing the operation. For situations where a consumer knows
/// which concrete <see cref="IChatClient" /> is being used and how it represents options, a new instance of that
/// implementation-specific options type may be returned by this callback, for the <see cref="IChatClient" />
/// implementation to use instead of creating a new instance. Such implementations may mutate the supplied options
/// implementation-specific options type can be returned by this callback for the <see cref="IChatClient" />
/// implementation to use, instead of creating a new instance. Such implementations might mutate the supplied options
/// instance further based on other settings supplied on this <see cref="ChatOptions" /> instance or from other inputs,
/// like the enumerable of <see cref="ChatMessage"/>s, therefore, it is <b>strongly recommended</b> to not return shared instances
/// like the enumerable of <see cref="ChatMessage"/>s. Therefore, it is <b>strongly recommended</b> to not return shared instances
/// and instead make the callback return a new instance on each call.
/// This is typically used to set an implementation-specific setting that isn't otherwise exposed from the strongly-typed
/// This is typically used to set an implementation-specific setting that isn't otherwise exposed from the strongly typed
/// properties on <see cref="ChatOptions" />.
/// </remarks>
[JsonIgnore]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ namespace Microsoft.Extensions.AI;
/// <summary>Represents the response to a chat request.</summary>
/// <remarks>
/// <see cref="ChatResponse"/> provides one or more response messages and metadata about the response.
/// A typical response will contain a single message, however a response may contain multiple messages
/// A typical response will contain a single message, however a response might contain multiple messages
/// in a variety of scenarios. For example, if automatic function calling is employed, such that a single
/// request to a <see cref="IChatClient"/> may actually generate multiple roundtrips to an inner <see cref="IChatClient"/>
/// it uses, all of the involved messages may be surfaced as part of the final <see cref="ChatResponse"/>.
/// request to a <see cref="IChatClient"/> might actually generate multiple round-trips to an inner <see cref="IChatClient"/>
/// it uses, all of the involved messages might be surfaced as part of the final <see cref="ChatResponse"/>.
/// </remarks>
public class ChatResponse
{
Expand Down Expand Up @@ -69,8 +69,7 @@ public IList<ChatMessage> Messages
/// the input messages supplied to <see cref="IChatClient.GetResponseAsync"/> need only be the additional messages beyond
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
/// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
/// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter. Note that the value may
/// or may not differ on every response, depending on whether the underlying provider uses a fixed ID for each conversation
/// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter. Note that the value might differ on every response, depending on whether the underlying provider uses a fixed ID for each conversation
/// or updates it for each message.
/// </remarks>
/// <related type="Article" href="https://learn.microsoft.com/dotnet/ai/microsoft-extensions-ai#stateless-vs-stateful-clients">Stateless vs. stateful clients.</related>
Expand All @@ -95,7 +94,7 @@ public IList<ChatMessage> Messages
/// and the result of the response has not been obtained yet. If the response has completed and the result has been obtained,
/// the token will be <see langword="null"/>.
/// <para>
/// This property should be used in conjunction with <see cref="ChatOptions.ContinuationToken"/> to
/// This property should be used in conjunction with <see cref="ChatOptions.ContinuationToken"/> to
/// continue to poll for the completion of the response. Pass this token to
/// <see cref="ChatOptions.ContinuationToken"/> on subsequent calls to <see cref="IChatClient.GetResponseAsync"/>
/// to poll for completion.
Expand All @@ -121,7 +120,7 @@ public IList<ChatMessage> Messages
public override string ToString() => Text;

/// <summary>Creates an array of <see cref="ChatResponseUpdate" /> instances that represent this <see cref="ChatResponse" />.</summary>
/// <returns>An array of <see cref="ChatResponseUpdate" /> instances that may be used to represent this <see cref="ChatResponse" />.</returns>
/// <returns>An array of <see cref="ChatResponseUpdate" /> instances that can be used to represent this <see cref="ChatResponse" />.</returns>
public ChatResponseUpdate[] ToChatResponseUpdates()
{
ChatResponseUpdate? extra = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,32 @@ internal static void CoalesceTextContent(IList<AIContent> contents)
contents,
mergeSingle: false,
canMerge: static (r1, r2) => string.IsNullOrEmpty(r1.ProtectedData), // we allow merging if the first item has no ProtectedData, even if the second does
static (contents, start, end) => new(MergeText(contents, start, end)) { AdditionalProperties = contents[start].AdditionalProperties?.Clone() });
static (contents, start, end) =>
{
TextReasoningContent content = new(MergeText(contents, start, end))
{
AdditionalProperties = contents[start].AdditionalProperties?.Clone()
};

#if DEBUG
for (int i = start; i < end - 1; i++)
{
Debug.Assert(contents[i] is TextReasoningContent { ProtectedData: null }, "Expected all but the last to have a null ProtectedData");
}
#endif

if (((TextReasoningContent)contents[end - 1]).ProtectedData is { } protectedData)
{
content.ProtectedData = protectedData;
}

return content;
});

static string MergeText(IList<AIContent> contents, int start, int end)
{
Debug.Assert(end - start > 1, "Expected multiple contents to merge");

StringBuilder sb = new();
for (int i = start; i < end; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace Microsoft.Extensions.AI;
/// The relationship between <see cref="ChatResponse"/> and <see cref="ChatResponseUpdate"/> is
/// codified in the <see cref="ChatResponseExtensions.ToChatResponseAsync"/> and
/// <see cref="ChatResponse.ToChatResponseUpdates"/>, which enable bidirectional conversions
/// between the two. Note, however, that the provided conversions may be lossy, for example if multiple
/// between the two. Note, however, that the provided conversions might be lossy, for example, if multiple
/// updates all have different <see cref="RawRepresentation"/> objects whereas there's only one slot for
/// such an object available in <see cref="ChatResponse.RawRepresentation"/>. Similarly, if different
/// updates provide different values for properties like <see cref="ModelId"/>,
Expand Down Expand Up @@ -100,12 +100,12 @@ public IList<AIContent> Contents

/// <summary>Gets or sets the ID of the message of which this update is a part.</summary>
/// <remarks>
/// A single streaming response may be composed of multiple messages, each of which may be represented
/// A single streaming response might be composed of multiple messages, each of which might be represented
/// by multiple updates. This property is used to group those updates together into messages.
///
/// Some providers may consider streaming responses to be a single message, and in that case
/// the value of this property may be the same as the response ID.
///
/// Some providers might consider streaming responses to be a single message, and in that case
/// the value of this property might be the same as the response ID.
///
/// This value is used when <see cref="ChatResponseExtensions.ToChatResponseAsync(IAsyncEnumerable{ChatResponseUpdate}, System.Threading.CancellationToken)"/>
/// groups <see cref="ChatResponseUpdate"/> instances into <see cref="ChatMessage"/> instances.
/// The value must be unique to each call to the underlying provider, and must be shared by
Expand All @@ -119,7 +119,7 @@ public IList<AIContent> Contents
/// the input messages supplied to <see cref="IChatClient.GetStreamingResponseAsync"/> need only be the additional messages beyond
/// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
/// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
/// (and this streaming message) as part of the <c>messages</c> parameter. Note that the value may or may not differ on every
/// (and this streaming message) as part of the <c>messages</c> parameter. Note that the value might differ on every
/// response, depending on whether the underlying provider uses a fixed ID for each conversation or updates it for each message.
/// </remarks>
public string? ConversationId { get; set; }
Expand All @@ -138,9 +138,9 @@ public IList<AIContent> Contents

/// <summary>Gets or sets the continuation token for resuming the streamed chat response of which this update is a part.</summary>
/// <remarks>
/// <see cref="IChatClient"/> implementations that support background responses will return
/// a continuation token on each update if background responses are allowed in <see cref="ChatOptions.AllowBackgroundResponses"/>
/// except of the last update, for which the token will be <see langword="null"/>.
/// <see cref="IChatClient"/> implementations that support background responses return
/// a continuation token on each update if background responses are allowed in <see cref="ChatOptions.AllowBackgroundResponses"/>.
/// However, for the last update, the token will be <see langword="null"/>.
/// <para>
/// This property should be used for stream resumption, where the continuation token of the latest received update should be
/// passed to <see cref="ChatOptions.ContinuationToken"/> on subsequent calls to <see cref="IChatClient.GetStreamingResponseAsync"/>
Expand Down
Loading
Loading