tghamm/Anthropic.SDK

[Feature request] Tool support

Closed this issue ยท 10 comments

Since Anthropic just released the public beta of tools yesterday, I'd be happy to see support in this library. See Anthropic docs: https://docs.anthropic.com/claude/docs/tool-use

I could imagine some similar C# support for functions like in the OpenAI-DotNet package where C# functions can be used for the request and which can also be invoked from the result, so that you hardly have to deal with fiddling around with JSON schema in C#.

What do you think?

On it! ๐Ÿ™‚

Seconded. Thanks for making this great library!

WIP, should have something that tests out pretty well in a few more days, thoughts?

[Function("This function returns the weather for a given location")]
public static async Task<string> GetWeather([FunctionParameter("Location of the weather", true)]string location)
{
    return "72 degrees and sunny";
}


[TestMethod]
public async Task TestBasicTool()
{
    var client = new AnthropicClient();
    var messages = new List<Message>
    {
        new Message()
        {
            Role = RoleType.User,
            Content = "What is the weather in San Francisco, CA?"
        }
    };
    var tools = Common.Tool.GetAllAvailableTools(includeDefaults: false, forceUpdate: true, clearCache: true);

    var parameters = new MessageParameters()
    {
        Messages = messages,
        MaxTokens = 2048,
        Model = AnthropicModels.Claude3Sonnet,
        Stream = false,
        Temperature = 1.0m,
    };
    var res = await client.Messages.GetClaudeMessageAsync(parameters, tools.ToList());
    
    messages.Add(res.Content.AsAssistantMessage());

    foreach (var toolCall in res.ToolCalls)
    {
        var response = await toolCall.InvokeAsync<string>();
        
        messages.Add(new Message(toolCall, response));
    }

    var finalResult = await client.Messages.GetClaudeMessageAsync(parameters);

    Assert.IsTrue(finalResult.FirstMessage.Text.Contains("72 degrees and sunny"));
}

Looks good! Cool that you can pull it straight from real methods. Just wanted to make sure that I can also just generate Tool objects from scratch? I need to roll my own tools in some cases. For example:
Tool tool = new Tool("functionName", new List<Parameter> {new Parameter("paramDescription", true)});

Also can the tools collection just be a member of the MessageParameters? I think that's how others do it. Is there a reason it's separate from all the other parameters?

Yeah, that'll be an option as well, something similar to:

var tools = new List<Tool>
{
    Tool.GetOrCreateTool(objectInstance, "TheNameOfTheMethodToCall"),
    Tool.FromFunc("a_custom_name_for_your_function", ()=> { /* Some logic to run */ })
};

This was mostly my first crack at wiring up the internals to abstract away all the JSON manipulation.

Tool support has been added, along with some other changes to keep pace with Anthropic. Hope it helps, and feel free to raise any issues - did the best I could - wanted to get something out there relatively quick for everyone.

Awesome, thanks for the quick addition!

Amazing, will try it out today!

Some thoughts after giving it a try:

First, according to Anthropic's docs, they support JSON schema for the input_schema property:

Tools are specified in the tools top-level parameter of the API request. Each tool definition includes:

  • name: The name of the tool. Must match the regex ^[a-zA-Z0-9_-]{1,64}$.
  • description: A detailed plaintext description of what the tool does, when it should be used, and how it behaves.
  • input_schema: A JSON Schema object defining the expected parameters for the tool.

You created some classes yourself which is a very simple subset of the JSON schema. Was this an active decision not to go the same route as the OpenAI package I linked above, where they just went with JsonNode and its full flexibility and JSON Schema support?

Secondly, this also lead to the bug I've found and tracked in issue #24

Finally, ToolContentResult seems to be missing the is_error field as described in Anthropic's docs:

And here's an example of returning an error result:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "ConnectionError: the weather service API is not available (HTTP 500)",
      "is_error": true
    }
  ]
}

I hope it's okay to send this feedback here and not directly open a new issue for each. I only decided to open a separate one for the bug in order to be able to track it.

@sibbl Thanks for the feedback, it wasn't active (permanent) decision, it was just a function of the scale/time of getting something out to support an 80/20 scenario. Your feedback is super helpful, and I'll be actively working on a 3.1 milestone so that the library can better support the full JSON schema.