Skip to content

Commit af99e2b

Browse files
committed
Refactor + Support hierarchical tool selection
1 parent 41932a7 commit af99e2b

File tree

6 files changed

+1467
-187
lines changed

6 files changed

+1467
-187
lines changed

src/Libraries/Microsoft.Extensions.AI/ToolGrouping/AIToolGroup.cs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics.CodeAnalysis;
7+
using System.Threading;
8+
using System.Threading.Tasks;
79
using Microsoft.Shared.Diagnostics;
810

911
namespace Microsoft.Extensions.AI;
@@ -12,30 +14,77 @@ namespace Microsoft.Extensions.AI;
1214
/// Represents a logical grouping of tools that can be dynamically expanded.
1315
/// </summary>
1416
/// <remarks>
15-
/// A <see cref="AIToolGroup"/> supplies a stable ordered list of <see cref="AITool"/> instances.
16-
/// Group membership is determined by reference equality of the tool instances.
17+
/// <para>
18+
/// A <see cref="AIToolGroup"/> is an <see cref="AITool"/> that supplies an ordered list of <see cref="AITool"/> instances
19+
/// via the <see cref="GetToolsAsync"/> method. This enables grouping tools together for organizational purposes
20+
/// and allows for dynamic tool selection based on context.
21+
/// </para>
22+
/// <para>
23+
/// Tool groups can be used independently or in conjunction with <see cref="ToolGroupingChatClient"/> to implement
24+
/// hierarchical tool selection, where groups are initially collapsed and can be expanded on demand.
25+
/// </para>
1726
/// </remarks>
1827
[Experimental("MEAI001")]
19-
public sealed class AIToolGroup
28+
public abstract class AIToolGroup : AITool
2029
{
30+
private readonly string _name;
31+
private readonly string _description;
32+
2133
/// <summary>Initializes a new instance of the <see cref="AIToolGroup"/> class.</summary>
2234
/// <param name="name">Group name (identifier used by the expansion function).</param>
2335
/// <param name="description">Human readable description of the group.</param>
24-
/// <param name="tools">Ordered tools contained in the group.</param>
25-
/// <exception cref="ArgumentNullException">If any argument is null.</exception>
26-
public AIToolGroup(string name, string description, IReadOnlyList<AITool> tools)
36+
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception>
37+
protected AIToolGroup(string name, string description)
2738
{
28-
Name = Throw.IfNull(name);
29-
Description = description ?? string.Empty;
30-
Tools = Throw.IfNull(tools);
39+
_name = Throw.IfNull(name);
40+
_description = Throw.IfNull(description);
3141
}
3242

3343
/// <summary>Gets the group name.</summary>
34-
public string Name { get; }
44+
public override string Name => _name;
3545

3646
/// <summary>Gets the group description.</summary>
37-
public string Description { get; }
47+
public override string Description => _description;
3848

39-
/// <summary>Gets the ordered tools belonging to the group.</summary>
40-
public IReadOnlyList<AITool> Tools { get; }
49+
/// <summary>Creates a tool group with a static list of tools.</summary>
50+
/// <param name="name">Group name (identifier used by the expansion function).</param>
51+
/// <param name="description">Human readable description of the group.</param>
52+
/// <param name="tools">Ordered tools contained in the group.</param>
53+
/// <returns>An <see cref="AIToolGroup"/> instance containing the specified tools.</returns>
54+
/// <exception cref="ArgumentNullException"><paramref name="name"/> or <paramref name="tools"/> is <see langword="null"/>.</exception>
55+
public static AIToolGroup Create(string name, string description, IReadOnlyList<AITool> tools)
56+
{
57+
_ = Throw.IfNull(name);
58+
_ = Throw.IfNull(tools);
59+
return new StaticAIToolGroup(name, description, tools);
60+
}
61+
62+
/// <summary>
63+
/// Asynchronously retrieves the ordered list of tools belonging to this group.
64+
/// </summary>
65+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
66+
/// <returns>A <see cref="ValueTask{TResult}"/> representing the asynchronous operation, containing the ordered list of tools in the group.</returns>
67+
/// <remarks>
68+
/// The returned list may contain other <see cref="AIToolGroup"/> instances, enabling hierarchical tool organization.
69+
/// Implementations should ensure the returned list is stable and deterministic for a given group instance.
70+
/// </remarks>
71+
public abstract ValueTask<IReadOnlyList<AITool>> GetToolsAsync(CancellationToken cancellationToken = default);
72+
73+
/// <summary>A tool group implementation that returns a static list of tools.</summary>
74+
private sealed class StaticAIToolGroup : AIToolGroup
75+
{
76+
private readonly IReadOnlyList<AITool> _tools;
77+
78+
public StaticAIToolGroup(string name, string description, IReadOnlyList<AITool> tools)
79+
: base(name, description)
80+
{
81+
_tools = tools;
82+
}
83+
84+
public override ValueTask<IReadOnlyList<AITool>> GetToolsAsync(CancellationToken cancellationToken = default)
85+
{
86+
cancellationToken.ThrowIfCancellationRequested();
87+
return new ValueTask<IReadOnlyList<AITool>>(_tools);
88+
}
89+
}
4190
}

src/Libraries/Microsoft.Extensions.AI/ToolGrouping/ChatClientBuilderToolGroupingExtensions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ public static class ChatClientBuilderToolGroupingExtensions
1616
/// <param name="configure">Configuration delegate.</param>
1717
/// <returns>The builder for chaining.</returns>
1818
/// <remarks>Should appear before tool reduction and function invocation middleware.</remarks>
19-
public static ChatClientBuilder UseToolGrouping(this ChatClientBuilder builder, Action<ToolGroupingOptions> configure)
19+
public static ChatClientBuilder UseToolGrouping(this ChatClientBuilder builder, Action<ToolGroupingOptions>? configure = null)
2020
{
2121
_ = Throw.IfNull(builder);
22-
_ = Throw.IfNull(configure);
2322
var opts = new ToolGroupingOptions();
24-
configure(opts);
23+
configure?.Invoke(opts);
2524
return builder.Use(inner => new ToolGroupingChatClient(inner, opts));
2625
}
2726
}

0 commit comments

Comments
 (0)