Skip to content

Commit c2f4dde

Browse files
committed
Move AIFunction properties down to AITool
1 parent 3e72c37 commit c2f4dde

File tree

8 files changed

+84
-29
lines changed

8 files changed

+84
-29
lines changed
Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,55 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Text;
7+
using Microsoft.Shared.Collections;
8+
49
namespace Microsoft.Extensions.AI;
510

11+
#pragma warning disable S1694 // An abstract class should have both abstract and concrete methods
12+
613
/// <summary>Represents a tool that can be specified to an AI service.</summary>
7-
public class AITool
14+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
15+
public abstract class AITool
816
{
917
/// <summary>Initializes a new instance of the <see cref="AITool"/> class.</summary>
1018
protected AITool()
1119
{
1220
}
21+
22+
/// <summary>Gets the name of the tool.</summary>
23+
public virtual string Name => GetType().Name;
24+
25+
/// <summary>Gets a description of the tool, suitable for use in describing the purpose to a model.</summary>
26+
public virtual string Description => string.Empty;
27+
28+
/// <summary>Gets any additional properties associated with the tool.</summary>
29+
public virtual IReadOnlyDictionary<string, object?> AdditionalProperties => EmptyReadOnlyDictionary<string, object?>.Instance;
30+
31+
/// <inheritdoc/>
32+
public override string ToString() => Name;
33+
34+
/// <summary>Gets the string to display in the debugger for this instance.</summary>
35+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
36+
private string DebuggerDisplay
37+
{
38+
get
39+
{
40+
StringBuilder sb = new(Name);
41+
42+
if (Description is string description && !string.IsNullOrEmpty(description))
43+
{
44+
_ = sb.Append(" (").Append(description).Append(')');
45+
}
46+
47+
foreach (var entry in AdditionalProperties)
48+
{
49+
_ = sb.Append(", ").Append(entry.Key).Append(" = ").Append(entry.Value);
50+
}
51+
52+
return sb.ToString();
53+
}
54+
}
1355
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
5+
using Microsoft.Shared.Collections;
6+
47
namespace Microsoft.Extensions.AI;
58

69
/// <summary>Represents a tool that can be specified to an AI service to enable it to execute code it generates.</summary>
@@ -11,10 +14,11 @@ namespace Microsoft.Extensions.AI;
1114
public class CodeInterpreterTool : AITool
1215
{
1316
/// <summary>Initializes a new instance of the <see cref="CodeInterpreterTool"/> class.</summary>
14-
public CodeInterpreterTool()
17+
public CodeInterpreterTool(IReadOnlyDictionary<string, object?>? additionalProperties = null)
1518
{
19+
AdditionalProperties = additionalProperties ?? EmptyReadOnlyDictionary<string, object?>.Instance;
1620
}
1721

18-
/// <summary>Gets or sets additional properties that impact the behavior of the tool.</summary>
19-
public AdditionalPropertiesDictionary? AdditionalProperties { get; set; }
22+
/// <inheritdoc/>
23+
public override IReadOnlyDictionary<string, object?> AdditionalProperties { get; }
2024
}

src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunction.cs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5-
using System.Diagnostics;
65
using System.Reflection;
76
using System.Text.Json;
87
using System.Threading;
@@ -12,15 +11,8 @@
1211
namespace Microsoft.Extensions.AI;
1312

1413
/// <summary>Represents a function that can be described to an AI service and invoked.</summary>
15-
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1614
public abstract class AIFunction : AITool
1715
{
18-
/// <summary>Gets the name of the function.</summary>
19-
public abstract string Name { get; }
20-
21-
/// <summary>Gets a description of the function, suitable for use in describing the purpose to a model.</summary>
22-
public abstract string Description { get; }
23-
2416
/// <summary>Gets a JSON Schema describing the function and its input parameters.</summary>
2517
/// <remarks>
2618
/// <para>
@@ -56,11 +48,8 @@ public abstract class AIFunction : AITool
5648
/// </remarks>
5749
public virtual MethodInfo? UnderlyingMethod => null;
5850

59-
/// <summary>Gets any additional properties associated with the function.</summary>
60-
public virtual IReadOnlyDictionary<string, object?> AdditionalProperties => EmptyReadOnlyDictionary<string, object?>.Instance;
61-
6251
/// <summary>Gets a <see cref="JsonSerializerOptions"/> that can be used to marshal function parameters.</summary>
63-
public virtual JsonSerializerOptions? JsonSerializerOptions => AIJsonUtilities.DefaultOptions;
52+
public virtual JsonSerializerOptions JsonSerializerOptions => AIJsonUtilities.DefaultOptions;
6453

6554
/// <summary>Invokes the <see cref="AIFunction"/> and returns its result.</summary>
6655
/// <param name="arguments">The arguments to pass to the function's invocation.</param>
@@ -75,18 +64,11 @@ public abstract class AIFunction : AITool
7564
return InvokeCoreAsync(arguments, cancellationToken);
7665
}
7766

78-
/// <inheritdoc/>
79-
public override string ToString() => Name;
80-
8167
/// <summary>Invokes the <see cref="AIFunction"/> and returns its result.</summary>
8268
/// <param name="arguments">The arguments to pass to the function's invocation.</param>
8369
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
8470
/// <returns>The result of the function's execution.</returns>
8571
protected abstract Task<object?> InvokeCoreAsync(
8672
IEnumerable<KeyValuePair<string, object?>> arguments,
8773
CancellationToken cancellationToken);
88-
89-
/// <summary>Gets the string to display in the debugger for this instance.</summary>
90-
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
91-
private string DebuggerDisplay => string.IsNullOrWhiteSpace(Description) ? Name : $"{Name} ({Description})";
9274
}

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIAssistantClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,10 @@ strictObj is bool strictValue ?
222222

223223
var functionParameters = BinaryData.FromBytes(
224224
JsonSerializer.SerializeToUtf8Bytes(
225-
JsonSerializer.Deserialize(aiFunction.Metadata.Schema, OpenAIJsonContext.Default.OpenAIChatToolJson)!,
225+
JsonSerializer.Deserialize(aiFunction.JsonSchema, OpenAIJsonContext.Default.OpenAIChatToolJson)!,
226226
OpenAIJsonContext.Default.OpenAIChatToolJson));
227227

228-
runOptions.ToolsOverride.Add(ToolDefinition.CreateFunction(aiFunction.Metadata.Name, aiFunction.Metadata.Description, functionParameters, strict));
228+
runOptions.ToolsOverride.Add(ToolDefinition.CreateFunction(aiFunction.Name, aiFunction.Description, functionParameters, strict));
229229
break;
230230

231231
case CodeInterpreterTool:

src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ static bool IsAsyncMethod(MethodInfo method)
339339
JsonTypeInfo typeInfo = options.SerializerOptions.GetTypeInfo(parameterType);
340340

341341
// Create a marshaller that simply looks up the parameter by name in the arguments dictionary.
342-
return (IReadOnlyDictionary<string, object?> arguments, AIFunctionContext? _) =>
342+
return (arguments, _) =>
343343
{
344344
// If the parameter has an argument specified in the dictionary, return that argument.
345345
if (arguments.TryGetValue(parameter.Name, out object? value))

src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactoryCreateOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public AIJsonSchemaCreateOptions JsonSchemaCreateOptions
5555
public string? Description { get; set; }
5656

5757
/// <summary>
58-
/// Gets or sets additional values to store on the resulting <see cref="AIFunction.AdditionalProperties" /> property.
58+
/// Gets or sets additional values to store on the resulting <see cref="AITool.AdditionalProperties" /> property.
5959
/// </summary>
6060
/// <remarks>
6161
/// This property can be used to provide arbitrary information about the function.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Xunit;
5+
6+
namespace Microsoft.Extensions.AI;
7+
8+
public class AIToolTests
9+
{
10+
[Fact]
11+
public void Constructor_Roundtrips()
12+
{
13+
DerivedAITool tool = new();
14+
Assert.Equal(nameof(DerivedAITool), tool.Name);
15+
Assert.Equal(nameof(DerivedAITool), tool.ToString());
16+
Assert.Empty(tool.Description);
17+
Assert.Empty(tool.AdditionalProperties);
18+
}
19+
20+
private sealed class DerivedAITool : AITool;
21+
}

test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/CodeInterpreterToolTests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ public class CodeInterpreterToolTests
1111
public void Constructor_Roundtrips()
1212
{
1313
var tool = new CodeInterpreterTool();
14-
Assert.Null(tool.AdditionalProperties);
14+
Assert.Equal(nameof(CodeInterpreterTool), tool.Name);
15+
Assert.Empty(tool.Description);
16+
Assert.Empty(tool.AdditionalProperties);
17+
Assert.Equal(nameof(CodeInterpreterTool), tool.ToString());
1518

1619
var props = new AdditionalPropertiesDictionary();
17-
tool.AdditionalProperties = props;
20+
tool = new CodeInterpreterTool(props);
21+
Assert.Equal(nameof(CodeInterpreterTool), tool.Name);
22+
Assert.Empty(tool.Description);
1823
Assert.Same(props, tool.AdditionalProperties);
24+
Assert.Equal(nameof(CodeInterpreterTool), tool.ToString());
1925
}
2026
}

0 commit comments

Comments
 (0)