tghamm / Anthropic.SDK

An unofficial C#/.NET SDK for accessing the Anthropic Claude API
https://www.nuget.org/packages/Anthropic.SDK
MIT License
55 stars 10 forks source link

[Feature request] Tool support #19

Closed sibbl closed 4 months ago

sibbl commented 5 months ago

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?

tghamm commented 5 months ago

On it! 🙂

nganju98 commented 5 months ago

Seconded. Thanks for making this great library!

tghamm commented 4 months ago

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"));
}
nganju98 commented 4 months ago

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?

tghamm commented 4 months ago

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.

tghamm commented 4 months ago

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.

sibbl commented 4 months ago

Awesome, thanks for the quick addition!

nganju98 commented 4 months ago

Amazing, will try it out today!

sibbl commented 4 months ago

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.

tghamm commented 4 months ago

@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.