feat: create tool from class that implements ITool
This commit is contained in:
@@ -7,6 +7,27 @@ using AnthropicClient.Utils;
|
|||||||
|
|
||||||
namespace AnthropicClient.Models;
|
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>
|
/// <summary>
|
||||||
/// Represents a tool that can be used in the chat.
|
/// Represents a tool that can be used in the chat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,6 +61,18 @@ public class Tool
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string DisplayName { get; }
|
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)
|
internal Tool(string name, string description, AnthropicFunction function)
|
||||||
{
|
{
|
||||||
ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name));
|
ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name));
|
||||||
@@ -58,6 +91,25 @@ public class Tool
|
|||||||
InputSchema = JsonSchemaGenerator.GenerateInputSchema(function);
|
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>
|
/// <summary>
|
||||||
/// Creates a tool from a static method.
|
/// Creates a tool from a static method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -246,6 +246,67 @@ public class ToolTests : SerializationTest
|
|||||||
t => t.IgnoringCyclicReferences()
|
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
|
class TestClass
|
||||||
@@ -255,3 +316,82 @@ class TestClass
|
|||||||
public static bool TestStaticMethod() => true;
|
public static bool TestStaticMethod() => true;
|
||||||
public bool TestInstanceMethod() => _result;
|
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