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
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ public string? ChatThreadId
/// </remarks>
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.
/// </summary>
/// <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.
/// </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>
public ChatToolMode? ToolMode { get; set; }
Expand Down Expand Up @@ -125,6 +142,7 @@ public virtual ChatOptions Clone()
Seed = Seed,
ResponseFormat = ResponseFormat,
ModelId = ModelId,
AllowMultipleToolCalls = AllowMultipleToolCalls,
ToolMode = ToolMode,
AdditionalProperties = AdditionalProperties?.Clone(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ private static ChatCompletionOptions ToOpenAIOptions(ChatOptions? options)
result.TopP = options.TopP;
result.PresencePenalty = options.PresencePenalty;
result.Temperature = options.Temperature;
result.AllowParallelToolCalls = options.AllowMultipleToolCalls;
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
result.Seed = options.Seed;
#pragma warning restore OPENAI001
Expand All @@ -510,11 +511,6 @@ private static ChatCompletionOptions ToOpenAIOptions(ChatOptions? options)

if (options.AdditionalProperties is { Count: > 0 } additionalProperties)
{
if (additionalProperties.TryGetValue(nameof(result.AllowParallelToolCalls), out bool allowParallelToolCalls))
{
result.AllowParallelToolCalls = allowParallelToolCalls;
}

if (additionalProperties.TryGetValue(nameof(result.AudioOptions), out ChatAudioOptions? audioOptions))
{
result.AudioOptions = audioOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,11 @@ private static ResponseCreationOptions ToOpenAIResponseCreationOptions(ChatOptio
result.PreviousResponseId = options.ConversationId;
result.TopP = options.TopP;
result.Temperature = options.Temperature;
result.ParallelToolCallsEnabled = options.AllowMultipleToolCalls;

// Handle loosely-typed properties from AdditionalProperties.
if (options.AdditionalProperties is { Count: > 0 } additionalProperties)
{
if (additionalProperties.TryGetValue(nameof(result.ParallelToolCallsEnabled), out bool allowParallelToolCalls))
{
result.ParallelToolCallsEnabled = allowParallelToolCalls;
}

if (additionalProperties.TryGetValue(nameof(result.EndUserId), out string? endUserId))
{
result.EndUserId = endUserId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,24 @@ public void Constructor_Parameterless_PropsDefaulted()
Assert.Null(options.ResponseFormat);
Assert.Null(options.ModelId);
Assert.Null(options.StopSequences);
Assert.Null(options.AllowMultipleToolCalls);
Assert.Null(options.ToolMode);
Assert.Null(options.Tools);
Assert.Null(options.AdditionalProperties);

ChatOptions clone = options.Clone();
Assert.Null(options.ConversationId);
Assert.Null(clone.ConversationId);
Assert.Null(clone.Temperature);
Assert.Null(clone.MaxOutputTokens);
Assert.Null(clone.TopP);
Assert.Null(clone.TopK);
Assert.Null(clone.FrequencyPenalty);
Assert.Null(clone.PresencePenalty);
Assert.Null(options.Seed);
Assert.Null(clone.Seed);
Assert.Null(clone.ResponseFormat);
Assert.Null(clone.ModelId);
Assert.Null(clone.StopSequences);
Assert.Null(clone.AllowMultipleToolCalls);
Assert.Null(clone.ToolMode);
Assert.Null(clone.Tools);
Assert.Null(clone.AdditionalProperties);
Expand Down Expand Up @@ -78,6 +80,7 @@ public void Properties_Roundtrip()
options.ResponseFormat = ChatResponseFormat.Json;
options.ModelId = "modelId";
options.StopSequences = stopSequences;
options.AllowMultipleToolCalls = true;
options.ToolMode = ChatToolMode.RequireAny;
options.Tools = tools;
options.AdditionalProperties = additionalProps;
Expand All @@ -93,22 +96,24 @@ public void Properties_Roundtrip()
Assert.Same(ChatResponseFormat.Json, options.ResponseFormat);
Assert.Equal("modelId", options.ModelId);
Assert.Same(stopSequences, options.StopSequences);
Assert.True(options.AllowMultipleToolCalls);
Assert.Same(ChatToolMode.RequireAny, options.ToolMode);
Assert.Same(tools, options.Tools);
Assert.Same(additionalProps, options.AdditionalProperties);

ChatOptions clone = options.Clone();
Assert.Equal("12345", options.ConversationId);
Assert.Equal("12345", clone.ConversationId);
Assert.Equal(0.1f, clone.Temperature);
Assert.Equal(2, clone.MaxOutputTokens);
Assert.Equal(0.3f, clone.TopP);
Assert.Equal(42, clone.TopK);
Assert.Equal(0.4f, clone.FrequencyPenalty);
Assert.Equal(0.5f, clone.PresencePenalty);
Assert.Equal(12345, options.Seed);
Assert.Equal(12345, clone.Seed);
Assert.Same(ChatResponseFormat.Json, clone.ResponseFormat);
Assert.Equal("modelId", clone.ModelId);
Assert.Equal(stopSequences, clone.StopSequences);
Assert.True(clone.AllowMultipleToolCalls);
Assert.Same(ChatToolMode.RequireAny, clone.ToolMode);
Assert.Equal(tools, clone.Tools);
Assert.Equal(additionalProps, clone.AdditionalProperties);
Expand Down Expand Up @@ -141,6 +146,7 @@ public void JsonSerialization_Roundtrips()
options.ResponseFormat = ChatResponseFormat.Json;
options.ModelId = "modelId";
options.StopSequences = stopSequences;
options.AllowMultipleToolCalls = false;
options.ToolMode = ChatToolMode.RequireAny;
options.Tools =
[
Expand All @@ -166,6 +172,7 @@ public void JsonSerialization_Roundtrips()
Assert.Equal("modelId", deserialized.ModelId);
Assert.NotSame(stopSequences, deserialized.StopSequences);
Assert.Equal(stopSequences, deserialized.StopSequences);
Assert.False(deserialized.AllowMultipleToolCalls);
Assert.Equal(ChatToolMode.RequireAny, deserialized.ToolMode);
Assert.Null(deserialized.Tools);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ public async Task NonStronglyTypedOptions_AllSent()

Assert.NotNull(await client.GetResponseAsync("hello", new()
{
AllowMultipleToolCalls = false,
AdditionalProperties = new()
{
["StoredOutputEnabled"] = true,
Expand All @@ -329,7 +330,6 @@ public async Task NonStronglyTypedOptions_AllSent()
["LogitBiases"] = new Dictionary<int, int> { { 12, 34 } },
["IncludeLogProbabilities"] = true,
["TopLogProbabilityCount"] = 42,
["AllowParallelToolCalls"] = false,
["EndUserId"] = "12345",
},
}));
Expand Down
Loading