Closed sibbl closed 4 months ago
Hey @sibbl I think this can be supported with a little more time, honestly was in a bit of a hurry, and Stephen's work is much more mature than this library is. In the meantime I'd suggest the function approach, but I'm going to actively work on exploring an approach under the hood that's more in-depth - I just didn't have time to flesh everything out I wanted to before getting to a point where I felt like it was usable enough to get some good value. Perhaps in hindsight it should have been a pre-release.
@sibbl I've gone down the path of JsonNode
for 3.1.0. This more what you had in mind? Keep in mind I uses classes (see #26 for a more advanced example that mirrors the docs) and serialized, but there are other ways to construct the JsonNode
, and this implements the full spec pretty much.
[TestMethod]
public async Task TestBasicToolManual()
{
var client = new AnthropicClient();
var messages = new List<Message>
{
new Message()
{
Role = RoleType.User,
Content = "What is the weather in San Francisco, CA in fahrenheit?"
}
};
var inputschema = new InputSchema()
{
Type = "object",
Properties = new Dictionary<string, Property>()
{
{ "location", new Property() { Type = "string", Description = "The location of the weather" } },
{
"tempType", new Property()
{
Type = "string", Enum = Enum.GetNames(typeof(TempType)),
Description = "The unit of temperature, celsius or fahrenheit"
}
}
},
Required = new List<string>() { "location", "tempType" }
};
JsonSerializerOptions JsonSerializationOptions = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter() },
ReferenceHandler = ReferenceHandler.IgnoreCycles,
};
string jsonString = JsonSerializer.Serialize(inputschema, JsonSerializationOptions);
var tools = new List<Common.Tool>()
{
new Common.Tool(new Function("GetWeather", "This function returns the weather for a given location",
JsonNode.Parse(jsonString)))
};
var parameters = new MessageParameters()
{
Messages = messages,
MaxTokens = 2048,
Model = AnthropicModels.Claude3Sonnet,
Stream = false,
Temperature = 1.0m
};
var res = await client.Messages.GetClaudeMessageAsync(parameters, tools);
messages.Add(res.Content.AsAssistantMessage());
var toolUse = res.Content.FirstOrDefault(c => c.Type == ContentType.tool_use) as ToolUseContent;
var id = toolUse.Id;
var input = toolUse.Input;
var param1 = toolUse.Input["location"].ToString();
var param2 = Enum.Parse<TempType>(toolUse.Input["tempType"].ToString());
var weather = await GetWeather(param1, param2);
messages.Add(new Message()
{
Role = RoleType.User,
Content = new[] { new ToolResultContent()
{
ToolUseId = id,
Content = weather
}
}});
var finalResult = await client.Messages.GetClaudeMessageAsync(parameters);
Assert.IsTrue(finalResult.FirstMessage.Text.Contains("72 degrees"));
}
Thoughts?
@tghamm thanks a lot for coming back so quickly!
This goes very much into the direction which I'd like to use, awesome!
This would solve my two main usages:
JsonNode
. For example via the JsonSchema.Net.Generation
package.@sibbl Great! I want to do a few more checks today but I'll likely release 3.1.0 later today that includes all of this and a few other things I found along the way. Appreciate your input!
Closing as Complete with the latest release, feel free to open any new issues with anything you find!
Hi,
I'd like to create tools using the
public Function(string name, string description = null, JsonNode parameters = null)
constructor and then add them.However, the internal method
GenerateJsonToolsFromCommonTools
fails asfunction.MethodInfo
is then null and it cannot iterate over it in the foreach while preparing the request to send to the API.Is this way of providing a function via its JSON Schema representation in a JsonNode even supported, or should I use the function approach?
Thanks a lot for the library and your help!