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. /// 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, tool)); } /// /// 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; } }