Fix ToJsonObject serialization failure with anonymous types and add support for custom JsonSerializerOptions (#1113)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> Co-authored-by: Stephen Toub <stoub@microsoft.com>
This commit is contained in:
parent
0585c27d53
commit
46d30444a8
4 changed files with 366 additions and 42 deletions
|
|
@ -73,16 +73,19 @@ public abstract partial class McpServer : McpSession
|
|||
/// </summary>
|
||||
/// <param name="messages">The messages to send as part of the request.</param>
|
||||
/// <param name="chatOptions">The options to use for the request, including model parameters and constraints.</param>
|
||||
/// <param name="serializerOptions">The <see cref="JsonSerializerOptions"/> to use for serializing user-provided objects. If <see langword="null"/>, <see cref="McpJsonUtilities.DefaultOptions"/> is used.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task containing the chat response from the model.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="messages"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">The client does not support sampling.</exception>
|
||||
/// <exception cref="McpException">The request failed or the client returned an error response.</exception>
|
||||
public async Task<ChatResponse> SampleAsync(
|
||||
IEnumerable<ChatMessage> messages, ChatOptions? chatOptions = default, CancellationToken cancellationToken = default)
|
||||
IEnumerable<ChatMessage> messages, ChatOptions? chatOptions = default, JsonSerializerOptions? serializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(messages);
|
||||
|
||||
serializerOptions ??= McpJsonUtilities.DefaultOptions;
|
||||
|
||||
StringBuilder? systemPrompt = null;
|
||||
|
||||
if (chatOptions?.Instructions is { } instructions)
|
||||
|
|
@ -148,7 +151,7 @@ public abstract partial class McpServer : McpSession
|
|||
Name = af.Name,
|
||||
Description = af.Description,
|
||||
InputSchema = af.JsonSchema,
|
||||
Meta = af.AdditionalProperties.ToJsonObject(),
|
||||
Meta = af.AdditionalProperties.ToJsonObject(serializerOptions),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -172,13 +175,13 @@ public abstract partial class McpServer : McpSession
|
|||
Temperature = chatOptions?.Temperature,
|
||||
ToolChoice = toolChoice,
|
||||
Tools = tools,
|
||||
Meta = chatOptions?.AdditionalProperties?.ToJsonObject(),
|
||||
Meta = chatOptions?.AdditionalProperties?.ToJsonObject(serializerOptions),
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
List<AIContent> responseContents = [];
|
||||
foreach (var block in result.Content)
|
||||
{
|
||||
if (block.ToAIContent() is { } content)
|
||||
if (block.ToAIContent(serializerOptions) is { } content)
|
||||
{
|
||||
responseContents.Add(content);
|
||||
}
|
||||
|
|
@ -202,13 +205,14 @@ public abstract partial class McpServer : McpSession
|
|||
/// <summary>
|
||||
/// Creates an <see cref="IChatClient"/> wrapper that can be used to send sampling requests to the client.
|
||||
/// </summary>
|
||||
/// <param name="serializerOptions">The <see cref="JsonSerializerOptions"/> to use for serialization. If <see langword="null"/>, <see cref="McpJsonUtilities.DefaultOptions"/> is used.</param>
|
||||
/// <returns>The <see cref="IChatClient"/> that can be used to issue sampling requests to the client.</returns>
|
||||
/// <exception cref="InvalidOperationException">The client does not support sampling.</exception>
|
||||
public IChatClient AsSamplingChatClient()
|
||||
public IChatClient AsSamplingChatClient(JsonSerializerOptions? serializerOptions = null)
|
||||
{
|
||||
ThrowIfSamplingUnsupported();
|
||||
|
||||
return new SamplingChatClient(this);
|
||||
return new SamplingChatClient(this, serializerOptions ?? McpJsonUtilities.DefaultOptions);
|
||||
}
|
||||
|
||||
/// <summary>Gets an <see cref="ILogger"/> on which logged messages will be sent as notifications to the client.</summary>
|
||||
|
|
@ -520,13 +524,14 @@ public abstract partial class McpServer : McpSession
|
|||
}
|
||||
|
||||
/// <summary>Provides an <see cref="IChatClient"/> implementation that's implemented via client sampling.</summary>
|
||||
private sealed class SamplingChatClient(McpServer server) : IChatClient
|
||||
private sealed class SamplingChatClient(McpServer server, JsonSerializerOptions serializerOptions) : IChatClient
|
||||
{
|
||||
private readonly McpServer _server = server;
|
||||
private readonly JsonSerializerOptions _serializerOptions = serializerOptions;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? chatOptions = null, CancellationToken cancellationToken = default) =>
|
||||
_server.SampleAsync(messages, chatOptions, cancellationToken);
|
||||
_server.SampleAsync(messages, chatOptions, _serializerOptions, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync(
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ public sealed class McpServerOptions
|
|||
/// The default maximum number of tokens to use for sampling requests. The default value is 1000 tokens.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This value is used in <see cref="McpServer.SampleAsync(IEnumerable{Microsoft.Extensions.AI.ChatMessage}, Microsoft.Extensions.AI.ChatOptions?, CancellationToken)"/>
|
||||
/// This value is used in <see cref="McpServer.SampleAsync(IEnumerable{Microsoft.Extensions.AI.ChatMessage}, Microsoft.Extensions.AI.ChatOptions?, System.Text.Json.JsonSerializerOptions?, CancellationToken)"/>
|
||||
/// when <see cref="Microsoft.Extensions.AI.ChatOptions.MaxOutputTokens"/> is not set in the request options.
|
||||
/// </remarks>
|
||||
public int MaxSamplingOutputTokens { get; set; } = 1000;
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ namespace ModelContextProtocol.Server;
|
|||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="AIContent"/></term>
|
||||
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
|
||||
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="string"/></term>
|
||||
|
|
@ -111,7 +111,7 @@ namespace ModelContextProtocol.Server;
|
|||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="IEnumerable{AIContent}"/> of <see cref="AIContent"/></term>
|
||||
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
|
||||
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="IEnumerable{ContentBlock}"/> of <see cref="ContentBlock"/></term>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ namespace ModelContextProtocol.Server;
|
|||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="AIContent"/></term>
|
||||
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
|
||||
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="string"/></term>
|
||||
|
|
@ -106,7 +106,7 @@ namespace ModelContextProtocol.Server;
|
|||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="IEnumerable{AIContent}"/> of <see cref="AIContent"/></term>
|
||||
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
|
||||
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="IEnumerable{Content}"/> of <see cref="ContentBlock"/></term>
|
||||
|
|
|
|||
Loading…
Reference in a new issue