2024-06-30 12:01:58 -05:00
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text.Json.Nodes;
|
2024-06-27 23:26:53 -05:00
|
|
|
using System.Text.Json.Serialization;
|
2024-07-01 10:49:01 -05:00
|
|
|
using System.Text.RegularExpressions;
|
2024-06-27 23:26:53 -05:00
|
|
|
|
|
|
|
|
using AnthropicClient.Utils;
|
|
|
|
|
|
|
|
|
|
namespace AnthropicClient.Models;
|
|
|
|
|
|
2024-07-03 13:57:51 -05:00
|
|
|
/// <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; }
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 23:26:53 -05:00
|
|
|
/// <summary>
|
2024-07-03 16:57:27 -05:00
|
|
|
/// Represents a tool that can be used.
|
2024-06-27 23:26:53 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public class Tool
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2024-07-01 10:49:01 -05:00
|
|
|
/// Gets the name of the tool. This name will conform to the Anthropic tool naming rules.
|
2024-06-27 23:26:53 -05:00
|
|
|
/// </summary>
|
2024-06-30 12:01:58 -05:00
|
|
|
public string Name { get; }
|
2024-06-27 23:26:53 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the description of the tool.
|
|
|
|
|
/// </summary>
|
2024-06-30 12:01:58 -05:00
|
|
|
public string Description { get; }
|
2024-06-27 23:26:53 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the input schema of the tool.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[JsonPropertyName("input_schema")]
|
2024-06-30 13:57:10 -05:00
|
|
|
public JsonObject InputSchema { get; }
|
2024-06-27 23:26:53 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// Gets the function of the tool.
|
2024-06-27 23:26:53 -05:00
|
|
|
/// </summary>
|
2024-06-30 12:01:58 -05:00
|
|
|
[JsonIgnore]
|
|
|
|
|
public AnthropicFunction Function { get; }
|
2024-07-01 10:49:01 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the display name of the tool.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[JsonIgnore]
|
|
|
|
|
public string DisplayName { get; }
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-07-03 13:57:51 -05:00
|
|
|
[JsonConstructor]
|
|
|
|
|
internal Tool()
|
|
|
|
|
{
|
|
|
|
|
var func = () => {};
|
|
|
|
|
|
|
|
|
|
Name = string.Empty;
|
|
|
|
|
Description = string.Empty;
|
|
|
|
|
InputSchema = [];
|
|
|
|
|
Function = new AnthropicFunction(func.Method);
|
|
|
|
|
DisplayName = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
internal Tool(string name, string description, AnthropicFunction function)
|
2024-06-27 23:26:53 -05:00
|
|
|
{
|
2024-06-30 14:56:11 -05:00
|
|
|
ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name));
|
|
|
|
|
ArgumentValidator.ThrowIfNullOrWhitespace(description, nameof(description));
|
2024-06-30 12:01:58 -05:00
|
|
|
ArgumentValidator.ThrowIfNull(function, nameof(function));
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-07-01 10:52:57 -05:00
|
|
|
// Anthropic imposes a limit on tool names. Tool names must be...
|
|
|
|
|
// - between 1 and 64 characters long
|
|
|
|
|
// - contain only letters, numbers, underscores, and hyphens
|
2024-07-01 10:49:01 -05:00
|
|
|
var sanitizedName = SanitizeName(name);
|
2024-06-27 23:26:53 -05:00
|
|
|
|
2024-07-01 10:49:01 -05:00
|
|
|
Name = sanitizedName;
|
|
|
|
|
DisplayName = name;
|
2024-06-27 23:26:53 -05:00
|
|
|
Description = description;
|
2024-06-30 12:01:58 -05:00
|
|
|
Function = function;
|
|
|
|
|
InputSchema = JsonSchemaGenerator.GenerateInputSchema(function);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-07-03 13:57:51 -05:00
|
|
|
/// 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));
|
|
|
|
|
|
2024-07-06 08:46:40 -05:00
|
|
|
return new Tool(tool.Name, tool.Description, new AnthropicFunction(tool.Function, tool));
|
2024-07-03 13:57:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// Creates a tool from a static method.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the tool.</param>
|
|
|
|
|
/// <param name="description">The description of the tool.</param>
|
|
|
|
|
/// <param name="type">The type that contains the method.</param>
|
|
|
|
|
/// <param name="methodName">The name of the method.</param>
|
2024-07-01 11:54:28 -05:00
|
|
|
/// <exception cref="ArgumentException">Thrown when <paramref name="methodName"/> is null or empty.</exception>
|
|
|
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is null.</exception>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// <exception cref="ArgumentException">Thrown when the method is not found in the type.</exception>
|
|
|
|
|
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
2024-07-01 10:49:01 -05:00
|
|
|
/// <remarks>The name of the tool will be sanitized to conform to the Anthropic tool naming rules.</remarks>
|
2024-06-30 12:01:58 -05:00
|
|
|
public static Tool CreateFromStaticMethod(string name, string description, Type type, string methodName)
|
|
|
|
|
{
|
2024-07-01 11:54:28 -05:00
|
|
|
ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName));
|
|
|
|
|
ArgumentValidator.ThrowIfNull(type, nameof(type));
|
|
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a tool from an instance method.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the tool.</param>
|
|
|
|
|
/// <param name="description">The description of the tool.</param>
|
|
|
|
|
/// <param name="instance">The instance that contains the method.</param>
|
|
|
|
|
/// <param name="methodName">The name of the method.</param>
|
2024-07-01 11:54:28 -05:00
|
|
|
/// <exception cref="ArgumentException">Thrown when <paramref name="methodName"/> is null or empty.</exception>
|
|
|
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="instance"/> is null.</exception>
|
|
|
|
|
/// <exception cref="ArgumentException">Thrown when <paramref name="methodName"/> is not found in the type of <paramref name="instance"/>.</exception>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
2024-07-01 10:49:01 -05:00
|
|
|
/// <remarks>The name of the tool will be sanitized to conform to the Anthropic tool naming rules.</remarks>
|
2024-06-30 12:01:58 -05:00
|
|
|
public static Tool CreateFromInstanceMethod(string name, string description, object instance, string methodName)
|
|
|
|
|
{
|
2024-07-01 11:54:28 -05:00
|
|
|
ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName));
|
|
|
|
|
ArgumentValidator.ThrowIfNull(instance, nameof(instance));
|
|
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
var method = instance.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a tool from a function.
|
|
|
|
|
/// </summary>
|
2024-06-30 14:56:11 -05:00
|
|
|
/// <typeparam name="TResult">The type of the result for the delegate.</typeparam>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// <param name="name">The name of the tool.</param>
|
|
|
|
|
/// <param name="description">The description of the tool.</param>
|
|
|
|
|
/// <param name="func">The function.</param>
|
2024-07-01 11:54:28 -05:00
|
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="func"/> is null.</exception>
|
2024-06-30 12:01:58 -05:00
|
|
|
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
2024-07-01 10:49:01 -05:00
|
|
|
/// <remarks>The name of the tool will be sanitized to conform to the Anthropic tool naming rules.</remarks>
|
2024-06-30 12:01:58 -05:00
|
|
|
public static Tool CreateFromFunction<TResult>(string name, string description, Func<TResult> func)
|
|
|
|
|
{
|
2024-07-01 11:54:28 -05:00
|
|
|
ArgumentValidator.ThrowIfNull(func, nameof(func));
|
|
|
|
|
|
2024-06-30 12:01:58 -05:00
|
|
|
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
|
2024-06-27 23:26:53 -05:00
|
|
|
}
|
2024-06-30 13:57:10 -05:00
|
|
|
|
2024-06-30 14:56:11 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a tool from a function.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T1">The type of the first parameter for the delegate.</typeparam>
|
|
|
|
|
/// <typeparam name="TResult">The type of the result for the delegate.</typeparam>
|
|
|
|
|
/// <param name="name">The name of the tool.</param>
|
|
|
|
|
/// <param name="description">The description of the tool.</param>
|
|
|
|
|
/// <param name="func">The function.</param>
|
2024-07-01 11:54:28 -05:00
|
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="func"/> is null.</exception>
|
2024-06-30 14:56:11 -05:00
|
|
|
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
2024-07-01 10:49:01 -05:00
|
|
|
/// <remarks>The name of the tool will be sanitized to conform to the Anthropic tool naming rules.</remarks>
|
2024-06-30 13:57:10 -05:00
|
|
|
public static Tool CreateFromFunction<T1, TResult>(string name, string description, Func<T1, TResult> func)
|
|
|
|
|
{
|
2024-07-01 11:54:28 -05:00
|
|
|
ArgumentValidator.ThrowIfNull(func, nameof(func));
|
|
|
|
|
|
2024-06-30 13:57:10 -05:00
|
|
|
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
|
|
|
|
|
}
|
2024-07-01 10:49:01 -05:00
|
|
|
|
2024-07-01 12:33:48 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a tool from a function.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T1">The type of the first parameter for the delegate.</typeparam>
|
|
|
|
|
/// <typeparam name="T2">The type of the second parameter for the delegate.</typeparam>
|
|
|
|
|
/// <typeparam name="TResult">The type of the result for the delegate.</typeparam>
|
|
|
|
|
/// <param name="name">The name of the tool.</param>
|
|
|
|
|
/// <param name="description">The description of the tool.</param>
|
|
|
|
|
/// <param name="func">The function.</param>
|
|
|
|
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="func"/> is null.</exception>
|
|
|
|
|
/// <returns>The created tool as instance of <see cref="Tool"/>.</returns>
|
|
|
|
|
/// <remarks>The name of the tool will be sanitized to conform to the Anthropic tool naming rules.</remarks>
|
|
|
|
|
public static Tool CreateFromFunction<T1, T2, TResult>(string name, string description, Func<T1, T2, TResult> func)
|
|
|
|
|
{
|
|
|
|
|
ArgumentValidator.ThrowIfNull(func, nameof(func));
|
|
|
|
|
|
|
|
|
|
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
|
|
|
|
|
}
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-07-01 12:33:48 -05:00
|
|
|
|
2024-07-01 10:49:01 -05:00
|
|
|
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;
|
|
|
|
|
}
|
2024-06-27 23:26:53 -05:00
|
|
|
}
|