feat: create tool from class that implements ITool
This commit is contained in:
@@ -7,6 +7,27 @@ using AnthropicClient.Utils;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Interface that a class can implement to be used to create a tool.
|
||||
/// </summary>
|
||||
public interface ITool
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the tool. Should not be null or empty.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the tool. Should not be null or empty.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input schema of the tool. Should not be null.
|
||||
/// </summary>
|
||||
public MethodInfo Function { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a tool that can be used in the chat.
|
||||
/// </summary>
|
||||
@@ -40,6 +61,18 @@ public class Tool
|
||||
[JsonIgnore]
|
||||
public string DisplayName { get; }
|
||||
|
||||
[JsonConstructor]
|
||||
internal Tool()
|
||||
{
|
||||
var func = () => {};
|
||||
|
||||
Name = string.Empty;
|
||||
Description = string.Empty;
|
||||
InputSchema = [];
|
||||
Function = new AnthropicFunction(func.Method);
|
||||
DisplayName = string.Empty;
|
||||
}
|
||||
|
||||
internal Tool(string name, string description, AnthropicFunction function)
|
||||
{
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name));
|
||||
@@ -58,6 +91,25 @@ public class Tool
|
||||
InputSchema = JsonSchemaGenerator.GenerateInputSchema(function);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tool from a type that implements <see cref="ITool"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that implements <see cref="ITool"/>.</typeparam>
|
||||
/// <exception cref="ArgumentException">Thrown when the name or description of the tool is null or empty.</exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the function of the tool is null.</exception>
|
||||
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
||||
/// <remarks>The implementation of <see cref="ITool"/> must have a parameterless constructor.</remarks>
|
||||
public static Tool CreateFromClass<T>() where T : ITool, new()
|
||||
{
|
||||
var tool = new T();
|
||||
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(tool.Name, nameof(tool.Name));
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(tool.Description, nameof(tool.Description));
|
||||
ArgumentValidator.ThrowIfNull(tool.Function, nameof(tool.Function));
|
||||
|
||||
return new Tool(tool.Name, tool.Description, new AnthropicFunction(tool.Function));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tool from a static method.
|
||||
/// </summary>
|
||||
|
||||
@@ -246,6 +246,67 @@ public class ToolTests : SerializationTest
|
||||
t => t.IgnoringCyclicReferences()
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithToolWhoseNameIsNull_ItShouldThrowException()
|
||||
{
|
||||
var action = () => Tool.CreateFromClass<ToolWithNullName>();
|
||||
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithToolWhoseNameIsEmpty_ItShouldThrowException()
|
||||
{
|
||||
var action = () => Tool.CreateFromClass<ToolWithEmptyName>();
|
||||
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithToolWhoseDescriptionIsNull_ItShouldThrowException()
|
||||
{
|
||||
var action = () => Tool.CreateFromClass<ToolWithNullDescription>();
|
||||
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithToolWhoseDescriptionIsEmpty_ItShouldThrowException()
|
||||
{
|
||||
var action = () => Tool.CreateFromClass<ToolWithEmptyDescription>();
|
||||
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithToolWhoseFunctionIsNull_ItShouldThrowException()
|
||||
{
|
||||
var action = () => Tool.CreateFromClass<ToolWithNullFunction>();
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromClass_WhenCalledWithProperTool_ItShouldReturnTool()
|
||||
{
|
||||
var tool = Tool.CreateFromClass<ProperTool>();
|
||||
|
||||
var expectedSchema = new JsonObject()
|
||||
{
|
||||
["type"] = "object",
|
||||
};
|
||||
|
||||
tool.Name.Should().Be("Name");
|
||||
tool.DisplayName.Should().Be("Name");
|
||||
tool.Description.Should().Be("Description");
|
||||
tool.Function.Method.Name.Should().Be(nameof(ProperTool.GetWeather));
|
||||
tool.Function.Instance.Should().BeNull();
|
||||
tool.InputSchema.Should().BeEquivalentTo(
|
||||
expectedSchema,
|
||||
t => t.IgnoringCyclicReferences()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TestClass
|
||||
@@ -254,4 +315,83 @@ class TestClass
|
||||
|
||||
public static bool TestStaticMethod() => true;
|
||||
public bool TestInstanceMethod() => _result;
|
||||
}
|
||||
|
||||
class ProperTool : ITool
|
||||
{
|
||||
public string Name => "Name";
|
||||
|
||||
public string Description { get; } = "Description";
|
||||
|
||||
public MethodInfo Function => typeof(ProperTool).GetMethod(nameof(GetWeather))!;
|
||||
|
||||
public static string GetWeather()
|
||||
{
|
||||
return "Sunny";
|
||||
}
|
||||
}
|
||||
|
||||
class ToolWithNullName : ITool
|
||||
{
|
||||
public string Name => null!;
|
||||
|
||||
public string Description { get; } = "Description";
|
||||
|
||||
public MethodInfo Function => typeof(ToolWithNullName).GetMethod(nameof(Tool))!;
|
||||
|
||||
public static string GetWeather()
|
||||
{
|
||||
return "Sunny";
|
||||
}
|
||||
}
|
||||
|
||||
class ToolWithEmptyName : ITool
|
||||
{
|
||||
public string Name => string.Empty;
|
||||
|
||||
public string Description { get; } = "Description";
|
||||
|
||||
public MethodInfo Function => typeof(ToolWithEmptyName).GetMethod(nameof(Tool))!;
|
||||
|
||||
public static string GetWeather()
|
||||
{
|
||||
return "Sunny";
|
||||
}
|
||||
}
|
||||
|
||||
class ToolWithNullDescription : ITool
|
||||
{
|
||||
public string Name => "Name";
|
||||
|
||||
public string Description => null!;
|
||||
|
||||
public MethodInfo Function => typeof(ToolWithNullDescription).GetMethod(nameof(Tool))!;
|
||||
|
||||
public static string GetWeather()
|
||||
{
|
||||
return "Sunny";
|
||||
}
|
||||
}
|
||||
|
||||
class ToolWithEmptyDescription : ITool
|
||||
{
|
||||
public string Name => "Name";
|
||||
|
||||
public string Description => string.Empty;
|
||||
|
||||
public MethodInfo Function => typeof(ToolWithEmptyDescription).GetMethod(nameof(Tool))!;
|
||||
|
||||
public static string GetWeather()
|
||||
{
|
||||
return "Sunny";
|
||||
}
|
||||
}
|
||||
|
||||
class ToolWithNullFunction : ITool
|
||||
{
|
||||
public string Name => "Name";
|
||||
|
||||
public string Description => "Description";
|
||||
|
||||
public MethodInfo Function => null!;
|
||||
}
|
||||
Reference in New Issue
Block a user