using System.Reflection;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
///
/// Interface that a class can implement to be used to create a tool.
///
public interface ITool
{
///
/// Gets the name of the tool. Should not be null or empty.
///
public string Name { get; }
///
/// Gets the description of the tool. Should not be null or empty.
///
public string Description { get; }
///
/// Gets the input schema of the tool. Should not be null.
///
public MethodInfo Function { get; }
}
///
/// Represents a tool that can be used in the chat.
///
public class Tool
{
///
/// Gets the name of the tool. This name will conform to the Anthropic tool naming rules.
///
public string Name { get; }
///
/// Gets the description of the tool.
///
public string Description { get; }
///
/// Gets the input schema of the tool.
///
[JsonPropertyName("input_schema")]
public JsonObject InputSchema { get; }
///
/// Gets the function of the tool.
///
[JsonIgnore]
public AnthropicFunction Function { get; }
///
/// Gets the display name of the 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));
ArgumentValidator.ThrowIfNullOrWhitespace(description, nameof(description));
ArgumentValidator.ThrowIfNull(function, nameof(function));
// Anthropic imposes a limit on tool names. Tool names must be...
// - between 1 and 64 characters long
// - contain only letters, numbers, underscores, and hyphens
var sanitizedName = SanitizeName(name);
Name = sanitizedName;
DisplayName = name;
Description = description;
Function = function;
InputSchema = JsonSchemaGenerator.GenerateInputSchema(function);
}
///
/// Creates a tool from a type that implements .
///
/// The type that implements .
/// Thrown when the name or description of the tool is null or empty.
/// Thrown when the function of the tool is null.
/// The created tool as instance of .
/// The implementation of must have a parameterless constructor.
public static Tool CreateFromClass() 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));
}
///
/// Creates a tool from a static method.
///
/// The name of the tool.
/// The description of the tool.
/// The type that contains the method.
/// The name of the method.
/// Thrown when is null or empty.
/// Thrown when is null.
/// Thrown when the method is not found in the type.
/// The created tool as instance of .
/// The name of the tool will be sanitized to conform to the Anthropic tool naming rules.
public static Tool CreateFromStaticMethod(string name, string description, Type type, string methodName)
{
ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName));
ArgumentValidator.ThrowIfNull(type, nameof(type));
var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
if (method is null)
{
throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'.", nameof(methodName));
}
return new Tool(name, description, new AnthropicFunction(method));
}
///
/// Creates a tool from an instance method.
///
/// The name of the tool.
/// The description of the tool.
/// The instance that contains the method.
/// The name of the method.
/// Thrown when is null or empty.
/// Thrown when is null.
/// Thrown when is not found in the type of .
/// The created tool as instance of .
/// The name of the tool will be sanitized to conform to the Anthropic tool naming rules.
public static Tool CreateFromInstanceMethod(string name, string description, object instance, string methodName)
{
ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName));
ArgumentValidator.ThrowIfNull(instance, nameof(instance));
var method = instance.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
if (method is null)
{
throw new ArgumentException($"Method '{methodName}' not found in type '{instance.GetType().FullName}'.", nameof(methodName));
}
return new Tool(name, description, new AnthropicFunction(method, instance));
}
///
/// Creates a tool from a function.
///
/// The type of the result for the delegate.
/// The name of the tool.
/// The description of the tool.
/// The function.
/// Thrown when is null.
/// The created tool as instance of .
/// The name of the tool will be sanitized to conform to the Anthropic tool naming rules.
public static Tool CreateFromFunction(string name, string description, Func func)
{
ArgumentValidator.ThrowIfNull(func, nameof(func));
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
}
///
/// Creates a tool from a function.
///
/// The type of the first parameter for the delegate.
/// The type of the result for the delegate.
/// The name of the tool.
/// The description of the tool.
/// The function.
/// Thrown when is null.
/// The created tool as instance of .
/// The name of the tool will be sanitized to conform to the Anthropic tool naming rules.
public static Tool CreateFromFunction(string name, string description, Func func)
{
ArgumentValidator.ThrowIfNull(func, nameof(func));
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
}
///
/// Creates a tool from a function.
///
/// The type of the first parameter for the delegate.
/// The type of the second parameter for the delegate.
/// The type of the result for the delegate.
/// The name of the tool.
/// The description of the tool.
/// The function.
/// Thrown when is null.
/// The created tool as instance of .
/// The name of the tool will be sanitized to conform to the Anthropic tool naming rules.
public static Tool CreateFromFunction(string name, string description, Func func)
{
ArgumentValidator.ThrowIfNull(func, nameof(func));
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
}
private static string SanitizeName(string name)
{
var sanitizedName = name.Trim();
if (sanitizedName.Length > 64)
{
sanitizedName = sanitizedName.Substring(0, 64);
}
sanitizedName = new Regex("[^a-zA-Z0-9_-]").Replace(sanitizedName, "_");
return sanitizedName;
}
}