From bcde796209a836b7f473a5da412387f5a539ec15 Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 14 Mar 2024 20:17:22 +0900 Subject: [PATCH] To DOING --- README.md | 243 ++++++++++++++++++++++++++++----- sandbox/ConsoleApp1/Program.cs | 114 ++++++++-------- 2 files changed, 272 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 2544c94..4b0b0eb 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Unofficial [Anthropic Claude API](https://www.anthropic.com/api) client for .NET. -We have built a C# API similar to the official [Python SDK](https://github.com/anthropics/anthropic-sdk-python) and [TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript). [Function calling generator](#function-calling) via C# Source Generator has also been implemented. It supports netstandard2.1, net6.0, and net8.0. If you want to use it in Unity, please reference it from [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). +We have built a C# API similar to the official [Python SDK](https://github.com/anthropics/anthropic-sdk-python) and [TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript). It supports netstandard2.1, net6.0, and net8.0. If you want to use it in Unity, please reference it from [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). +In addition to the pure client SDK, it also includes a C# Source Generator for performing Function Calling, similar to [anthropic-tools](https://github.com/anthropics/anthropic-tools/). Installation --- @@ -33,6 +34,8 @@ var message = await anthropic.Messages.CreateAsync(new() Console.WriteLine(message); ``` +Claudia is designed to have a similar look and feel to the official client, particularly the TypeScript SDK. However, it does not use `object`, `dynamic`, or `Dictionary`, and everything is strongly typed. By utilizing [C# 9.0 Target-typed new expressions](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new) and [C# 12 Collection expressions](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/collection-expressions), it can be written in a simple manner. + Streaming Messages --- We provide support for streaming responses using Server Sent Events (SSE). @@ -57,7 +60,7 @@ await foreach (var messageStreamEvent in stream) If you need to cancel a stream, you can pass the `CancellationToken` to `CreateStreamAsync`. -Types of MessageStreamEvents are here [IMessageStreamEvent](https://github.com/Cysharp/Claudia/blob/main/src/Claudia/IMessageStreamEvent.cs). +The Stream returns an `IAsyncEnumerable`, allowing it to be enumerated using `await foreach`. The implementation types of `IMessageStreamEvent` can be found in [IMessageStreamEvent.cs](https://github.com/Cysharp/Claudia/blob/main/src/Claudia/IMessageStreamEvent.cs). For example, outputs the text results. @@ -376,36 +379,215 @@ var message = await anthropic.Messages.CreateAsync(new() Console.WriteLine(message); ``` +Currently, there are four types of uploadable binaries: `image/jpeg`, `image/png`, `image/gif`, and `image/webp`. For example, if you want to upload a markdown file, it's best to read its contents and send it as text. If you want to upload a PDF, you can either convert it to text or an image before sending. Presentation files like pptx can also be sent as images, and Claude will interpret the content and extract the text for you. + +System and Temperature +--- +Other optional properties of `MessageRequest` include `System`, `Metadata`, `StopSequences`, `Temperature`, `TopP`, and `TopK`. + +```csharp +var message = await anthropic.Messages.CreateAsync(new() +{ + Model = Models.Claude3Haiku, + MaxTokens = 1024, + System = SystemPrompts.Claude3, + Temperature = 0.4, + Messages = [ + new() { Role = Roles.User, Content = "Hello, Claude" }, + ], +}); +``` + +`SystemPrompts.Claude3` is a string constant for the System Prompt used in the Official Chat UI. Of course, you can also set any arbitrary System Prompt. + +Save / Load +--- +TODO:... + Function Calling --- -Static methods belonging to the partial class with `[ClaudiaFunction]` can generate system messages corresponding to Claude's function calling, parsing of replies and function calls, and XML messages of results. +Claude supports Function Calling. The [Anthropic Cookbook](https://github.com/anthropics/anthropic-cookbook) provides examples of Function Calling. To achieve this, complex XML generation and parsing processing, as well as execution based on the parsed results, are required. -The description to convey information to Claude is automatically retrieved from the Document Comment. +With Claudia, you only need to define static methods annotated with `[ClaudiaFunction]`, and the C# Source Generator automatically generates the necessary code, including parsers and system messages. ```csharp public static partial class FunctionTools { + // Sample of anthropic-tools https://github.com/anthropics/anthropic-tools#basetool + /// - /// Date of target location. + /// Retrieve the current time of day in Hour-Minute-Second format for a specified time zone. Time zones should be written in standard formats such as UTC, US/Pacific, Europe/London. /// - /// TimeZone of localtion like 'Tokeyo Standard Time', 'Eastern Standard Time', etc. - /// + /// The time zone to get the current time for, such as UTC, US/Pacific, Europe/London. [ClaudiaFunction] - public static DateTime Today(string timeZoneId) + public static string TimeOfDay(string timeZone) { - return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, timeZoneId); + var time = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, timeZone); + return time.ToString("HH:mm:ss"); } +} +``` + +The `partial class` includes the generated `.SystemPrompt` and `.InvokeAsync(MessageResponse)`. + +Function Calling requires two requests to Claude. The flow is as follows: "Initial request to Claude with available tools in System Prompt -> Execute functions based on the message containing the necessary tools -> Include the results in a new message and send another request to Claude." + +```csharp +// `FunctionTools.SystemPrompt` contains the XML used to inform Claude about the available tools. +// This XML is generated from the method's XML documentation comments. +/* +In this environment you have access to a set of tools you can use to answer the user's question. +... +You may call them like this: +... +Here are the tools available: + + + TimeOfDay + Retrieve the current time of day in Hour-Minute-Second format for a specified time zone. Time zones should be written in standard formats such as UTC, US/Pacific, Europe/London. + + + timeZone + string + The time zone to get the current time for, such as UTC, US/Pacific, Europe/London. + + + + +*/ +// Console.WriteLine(FunctionTools.SystemPrompt); + +var input = new Message { Role = Roles.User, Content = "What time is it in Los Angeles?" }; +var message = await anthropic.Messages.CreateAsync(new() +{ + Model = Models.Claude3Haiku, + MaxTokens = 1024, + System = FunctionTools.SystemPrompt, // set generated prompt + StopSequences = [StopSequnces.CloseFunctionCalls], // set as stop sequence + Messages = [input], +}); + +// Claude returns xml to invoke tool +/* + + + TimeOfDay + + US/Pacific + + +*/ +// Console.WriteLine(message); + +// `FunctionTools.InvokeAsync`, which is automatically generated, parses the function name and parameters from the `MessageResponse`, +// executes the corresponding function, and generates XML to inform Claude about the function execution results. +var partialAssistantMessage = await FunctionTools.InvokeAsync(message); + +// By passing this message to Claude as the beginning of the Assistant's response, +// it will provide a continuation that takes into account the function execution results. +/* + + + TimeOfDay + + US/Pacific + + + + + + TimeOfDay + 03:27:03 + + +*/ +// Console.WriteLine(partialAssistantMessage); + +var callResult = await anthropic.Messages.CreateAsync(new() +{ + Model = Models.Claude3Haiku, + MaxTokens = 1024, + System = FunctionTools.SystemPrompt, + Messages = [ + input, // User: "What time is it in Los Angeles?" + new() { Role = Roles.Assistant, Content = partialAssistantMessage! } // set as Assistant + ], +}); + +// The current time in Los Angeles (US/Pacific time zone) is 03:36:04. +Console.WriteLine(callResult); +``` + +For the initial request, specifying `StopSequences.CloseFunctionCalls` is efficient. Also, if you want to include your own System Prompt, it's a good idea to concatenate it with the generated SystemPrompt. + +The return type of `ClaudiaFunction` can also be specified as `Task` or `ValueTask`. This allows you to execute a variety of tasks, such as HTTP requests or database requests. For example, a function that retrieves the content of a specified webpage can be defined as shown above. + +```csharp +public static partial class FunctionTools +{ + // ... /// - /// Sum of two integer parameters. + /// Retrieves the HTML from the specified URL. /// - /// parameter1. - /// parameter2. + /// The URL to retrieve the HTML from. [ClaudiaFunction] - public static int Sum(int x, int y) + static async Task GetHtmlFromWeb(string url) { - return x + y; + using var client = new HttpClient(); + return await client.GetStringAsync(url); } +} +``` + +```csharp +var input = new Message +{ + Role = Roles.User, + Content = """ + Could you summarize this page in three line? + https://docs.anthropic.com/claude/docs/intro-to-claude +""" +}; + +var message = await anthropic.Messages.CreateAsync(new() +{ + Model = Models.Claude3Haiku, + MaxTokens = 1024, + System = FunctionTools.SystemPrompt, // set generated prompt + StopSequences = [StopSequnces.CloseFunctionCalls], // set as stop sequence + Messages = [input], +}); + +var partialAssistantMessage = await FunctionTools.InvokeAsync(message); + +var callResult = await anthropic.Messages.CreateAsync(new() +{ + Model = Models.Claude3Haiku, + MaxTokens = 1024, + System = FunctionTools.SystemPrompt, + Messages = [ + input, + new() { Role = Roles.Assistant, Content = partialAssistantMessage! } // set as Assistant + ], +}); + +// The page can be summarized in three lines: +// 1. Claude is a family of large language models developed by Anthropic designed to revolutionize the way you interact with AI. +// 2. This documentation is designed to help you get the most out of Claude, with clear explanations, examples, best practices, and links to additional resources. +// 3. Claude excels at a wide variety of tasks involving language, reasoning, analysis, coding, and more, and the documentation covers key capabilities, getting started with prompting, and using the API. +Console.WriteLine(callResult); +``` + +Multiple functions can be defined, and they can be executed multiple times in a single request. + +```csharp +public static partial class FunctionTools +{ + [ClaudiaFunction] + public static string TimeOfDay(string timeZone) //... + + // Sample of https://github.com/anthropics/anthropic-cookbook/blob/main/function_calling/function_calling.ipynb /// /// Calculator function for doing basic arithmetic. @@ -429,40 +611,39 @@ public static partial class FunctionTools } ``` -For example, use the following Specify the generated `(target partial class).SystemPrompt` as the system prompt and `StopSequnces.CloseFunctionCalls`(``) as StopSequences. - ```csharp -var anthropic = new Anthropic(); - -var userInput = "Please tell me the current time in Tokyo and the current time in the UK." + - "Also, while you're at it, please tell me what 1,984,135 * 9,343,116 equals."; +var input = new Message +{ + Role = Roles.User, + Content = """ + What time is it in Seattle and Tokyo? + Incidentally multiply 1,984,135 by 9,343,116. +""" +}; var message = await anthropic.Messages.CreateAsync(new() { - Model = Models.Claude3Opus, + Model = Models.Claude3Haiku, MaxTokens = 1024, - System = FunctionTools.SystemPrompt, - StopSequences = [StopSequnces.CloseFunctionCalls], - Messages = [ - new() { Role = Roles.User, Content = userInput }, - ], + System = FunctionTools.SystemPrompt, // set generated prompt + StopSequences = [StopSequnces.CloseFunctionCalls], // set as stop sequence + Messages = [input], }); var partialAssistantMessage = await FunctionTools.InvokeAsync(message); -Console.WriteLine(partialAssistantMessage); - var callResult = await anthropic.Messages.CreateAsync(new() { - Model = Models.Claude3Opus, + Model = Models.Claude3Haiku, MaxTokens = 1024, System = FunctionTools.SystemPrompt, Messages = [ - new() { Role = Roles.User, Content = userInput }, - new() { Role = Roles.Assistant, Content = partialAssistantMessage! }, + input, + new() { Role = Roles.Assistant, Content = partialAssistantMessage! } // set as Assistant ], }); +// TODO: show requested result Console.WriteLine(callResult); ``` diff --git a/sandbox/ConsoleApp1/Program.cs b/sandbox/ConsoleApp1/Program.cs index cdb13ef..d25a2ad 100644 --- a/sandbox/ConsoleApp1/Program.cs +++ b/sandbox/ConsoleApp1/Program.cs @@ -13,20 +13,57 @@ var anthropic = new Anthropic(); -var userInput = """ -Translate and summarize this Japanese site to English. -https://scrapbox.io/hadashiA/ZLogger_v2%E3%81%AE%E6%96%B0%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%83%81%E3%83%A3%E3%83%BC%E3%83%89%E3%83%AD%E3%82%AE%E3%83%B3%E3%82%B0%E4%BD%93%E9%A8%93 -"""; +//var userInput = """ +//Translate and summarize this Japanese site to English. +//https://scrapbox.io/hadashiA/ZLogger_v2%E3%81%AE%E6%96%B0%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%83%81%E3%83%A3%E3%83%BC%E3%83%89%E3%83%AD%E3%82%AE%E3%83%B3%E3%82%B0%E4%BD%93%E9%A8%93 +//"""; + +//var message = await anthropic.Messages.CreateAsync(new() +//{ +// Model = Models.Claude3Haiku, +// MaxTokens = 1024, +// System = SystemPrompts.Claude3 + "\n" + FunctionTools.SystemPrompt, +// StopSequences = [StopSequnces.CloseFunctionCalls], +// Messages = [ +// new() { Role = Roles.User, Content = userInput }, +// ], +//}); + +//var partialAssistantMessage = await FunctionTools.InvokeAsync(message); + +//var callResult = await anthropic.Messages.CreateAsync(new() +//{ +// Model = Models.Claude3Haiku, +// MaxTokens = 1024, +// System = SystemPrompts.Claude3 + "\n" + FunctionTools.SystemPrompt + "\n" + "Return message from assistant should be humanreadable so don't use xml tags, and json.", +// Messages = [ +// new() { Role = Roles.User, Content = userInput }, +// new() { Role = Roles.Assistant, Content = partialAssistantMessage! }, +// ], +//}); + +//Console.WriteLine(callResult); + + + + + +var input = new Message +{ + Role = Roles.User, + Content = """ + What time is it in Seattle and Tokyo? + Incidentally multiply 1,984,135 by 9,343,116. +""" +}; var message = await anthropic.Messages.CreateAsync(new() { Model = Models.Claude3Haiku, MaxTokens = 1024, - System = SystemPrompts.Claude3 + "\n" + FunctionTools.SystemPrompt, - StopSequences = [StopSequnces.CloseFunctionCalls], - Messages = [ - new() { Role = Roles.User, Content = userInput }, - ], + System = FunctionTools.SystemPrompt, // set generated prompt + StopSequences = [StopSequnces.CloseFunctionCalls], // set as stop sequence + Messages = [input], }); var partialAssistantMessage = await FunctionTools.InvokeAsync(message); @@ -35,38 +72,16 @@ Translate and summarize this Japanese site to English. { Model = Models.Claude3Haiku, MaxTokens = 1024, - System = SystemPrompts.Claude3 + "\n" + FunctionTools.SystemPrompt + "\n" + "Return message from assistant should be humanreadable so don't use xml tags, and json.", + System = FunctionTools.SystemPrompt, Messages = [ - new() { Role = Roles.User, Content = userInput }, - new() { Role = Roles.Assistant, Content = partialAssistantMessage! }, + input, + new() { Role = Roles.Assistant, Content = partialAssistantMessage! } // set as Assistant ], }); Console.WriteLine(callResult); - - - - - - - - - - - - - - - - - - - - - - //var systemPrompt = """ //In this environment you have access to a set of tools you can use to answer the user's question. @@ -308,17 +323,21 @@ Translate and summarize this Japanese site to English. public static partial class FunctionTools { + // Sample of anthropic-tools https://github.com/anthropics/anthropic-tools#basetool + /// - /// Date of target location. + /// Retrieve the current time of day in Hour-Minute-Second format for a specified time zone. Time zones should be written in standard formats such as UTC, US/Pacific, Europe/London. /// - /// TimeZone of localtion like 'Tokeyo Standard Time', 'Eastern Standard Time', etc. - /// + /// The time zone to get the current time for, such as UTC, US/Pacific, Europe/London. [ClaudiaFunction] - public static DateTime Today(string timeZoneId) + public static string TimeOfDay(string timeZone) { - return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, timeZoneId); + var time = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, timeZone); + return time.ToString("HH:mm:ss"); } + // Sample of https://github.com/anthropics/anthropic-cookbook/blob/main/function_calling/function_calling.ipynb + /// /// Calculator function for doing basic arithmetic. /// Supports addition, subtraction, multiplication @@ -340,28 +359,15 @@ static double DoPairwiseArithmetic(double firstOperand, double secondOperand, st } /// - /// Get html from target url. + /// Retrieves the HTML from the specified URL. /// - /// Url of public internet for get html. + /// The URL to retrieve the HTML from. [ClaudiaFunction] static async Task GetHtmlFromWeb(string url) { using var client = new HttpClient(); return await client.GetStringAsync(url); } - - ///// - ///// demo - ///// - ///// t - ///// g - ///// o - ///// - //[ClaudiaFunction] - //static int Demo(TimeSpan ts, Guid guid, DateTimeOffset dtofset) - //{ - // return 0; - //} }