From a5af4636b9c8a4ea61c7a01797a3a99c1c480282 Mon Sep 17 00:00:00 2001
From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com>
Date: Mon, 1 Jul 2024 10:49:01 -0500
Subject: [PATCH] fix: enforce anthropic's name limitations
---
src/AnthropicClient/Models/Tool.cs | 50 ++++++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/AnthropicClient/Models/Tool.cs b/src/AnthropicClient/Models/Tool.cs
index fd40d12..3b1d07a 100644
--- a/src/AnthropicClient/Models/Tool.cs
+++ b/src/AnthropicClient/Models/Tool.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
using AnthropicClient.Utils;
@@ -11,8 +12,13 @@ namespace AnthropicClient.Models;
///
public class Tool
{
+ // Anthropic imposes a limit on tool names. Tool names must be...
+ // - between 1 and 64 characters long
+ // - contain only letters, numbers, underscores, and hyphens
+ private readonly Regex _nameRegex = new(@"^[a-zA-Z0-9_-]{1,64}$");
+
///
- /// Gets the name of the tool.
+ /// Gets the name of the tool. This name will conform to the Anthropic tool naming rules.
///
public string Name { get; }
@@ -32,14 +38,24 @@ public class Tool
///
[JsonIgnore]
public AnthropicFunction Function { get; }
+
+ ///
+ /// Gets the display name of the tool.
+ ///
+ [JsonIgnore]
+ public string DisplayName { get; }
internal Tool(string name, string description, AnthropicFunction function)
{
ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name));
ArgumentValidator.ThrowIfNullOrWhitespace(description, nameof(description));
ArgumentValidator.ThrowIfNull(function, nameof(function));
+
+ var sanitizedName = SanitizeName(name);
+ ThrowIfNameIsInvalid(sanitizedName);
- Name = name;
+ Name = sanitizedName;
+ DisplayName = name;
Description = description;
Function = function;
InputSchema = JsonSchemaGenerator.GenerateInputSchema(function);
@@ -54,7 +70,9 @@ public class Tool
/// The name of the method.
/// Thrown when , , , or is null.
/// Thrown when the method is not found in the type.
+ /// Thrown when the name of the tool is invalid.
/// 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)
{
var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
@@ -76,7 +94,9 @@ public class Tool
/// The name of the method.
/// Thrown when , , , or is null.
/// Thrown when the method is not found in the type.
+ /// Thrown when the name of the tool is invalid.
/// 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)
{
var method = instance.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
@@ -97,7 +117,9 @@ public class Tool
/// The description of the tool.
/// The function.
/// Thrown when , , or is null.
+ /// Thrown when the name of the tool is invalid.
/// 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)
{
return new Tool(name, description, new AnthropicFunction(func.Method, func.Target));
@@ -112,9 +134,33 @@ public class Tool
/// The description of the tool.
/// The function.
/// Thrown when , , or is null.
+ /// Thrown when the name of the tool is invalid.
/// 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)
{
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;
+ }
+
+ private void ThrowIfNameIsInvalid(string name)
+ {
+ if (_nameRegex.IsMatch(name) is false)
+ {
+ throw new ArgumentException("Tool name must be between 1 and 64 characters long and contain only letters, numbers, underscores, and hyphens.", nameof(name));
+ }
+ }
}
\ No newline at end of file