支持Assistants流API的C# OpenAI库

目录

介绍

如何使用?

类体系结构

OpenAIClient

XXX测量仪

XXXAsync

XXXResponse

RestApi响应

QueryParameter

结论


介绍

本周,OpenAI推出了GPT4-turboGPTGPT Store等。他们还发布了名为Assistant API的新API。它是新的,没有C#库。所以,我开发了它。

新增功能!!!(2024-5-07)

我添加了矢量文件,在聊天完成时使用。请检查一下!

如何使用?

通过Nuget下载

HigLabo.OpenAI

那是HigLabo.OpenAI

您可以在以下位置查看示例代码:

主类是OpenAIClient 。为OpenAI API创建OpenAIClient类。

var apiKey = "your api key of OpenAI";
var cl = new OpenAIClient(apiKey);

对于Azure终结点。

var apiKey = "your api key of OpenAI";
var cl = new OpenAIClient(new AzureSettings
(apiKey, "https://tinybetter-work-for-our-future.openai.azure.com/", "MyDeploymentName"));

调用ChatCompletion终结点。

var cl = new OpenAIClient("API KEY");
var p = new ChatCompletionsParameter();
p.Messages.Add(new ChatMessage(ChatMessageRole.User, $"How to enjoy coffee?"));
p.Model = "gpt-4";
var res = await cl.ChatCompletionsAsync(p);
foreach (var choice in res.Choices)
{
    Console.Write(choice.Message.Content);
}

Console.WriteLine();
Console.WriteLine();
Console.WriteLine("----------------------------------------");
Console.WriteLine("Total tokens: " + res.Usage.Total_Tokens);

使用具有服务器发送事件的ChatCompletion终结点。

var cl = new OpenAIClient("API KEY");
var result = new ChatCompletionStreamResult();
await foreach (string text in cl.ChatCompletionsStreamAsync("How to enjoy coffee?", "gpt-4", result, CancellationToken.None))
{
    Console.Write(text);
}
Console.WriteLine();
Console.WriteLine("Finish reason: " + result.GetFinishReason());

带有函数调用的ChatCompletion

var cl = new OpenAIClient("API KEY");
var p = new ChatCompletionsParameter();
//ChatGPT can correct Newyork,Sanflansisco to New york and San Flancisco.
p.Messages.Add(new ChatMessage(ChatMessageRole.User, 
 $"I want to know the whether of these locations. Newyork, Sanflansisco, Paris, Tokyo."));
p.Model = "gpt-4";

{
    var tool = new ToolObject("function");
    tool.Function = new FunctionObject();
    tool.Function.Name = "getWhether";
    tool.Function.Description = "This service can get whether of specified location.";
    tool.Function.Parameters = new
    {
        type = "object",
        properties = new
        {
            locationList = new
            {
                type = "array",
                description = "Location list that you want to know.",
                items = new
                {
                    type = "string",
                }
            }
        }
    };
    p.Tools = new List<ToolObject>();
    p.Tools.Add(tool);
}
{
    var tool = new ToolObject("function");
    tool.Function = new FunctionObject();
    tool.Function.Name = "getLatLong";
    tool.Function.Description = 
         "This service can get latitude and longitude of specified location.";
    tool.Function.Parameters = new
    {
        type = "object",
        properties = new
        {
            locationList = new
            {
                type = "array",
                description = "Location list that you want to know.",
                items = new
                {
                    type = "string",
                }
            }
        }
    };
    p.Tools = new List<ToolObject>();
    p.Tools.Add(tool);
}

var result = new ChatCompletionStreamResult();
await foreach (var text in cl.ChatCompletionsStreamAsync(p, result, CancellationToken.None))
{
    Console.Write(text);
}
Console.WriteLine();

foreach (var f in result.GetFunctionCallList())
{
    Console.WriteLine("Function name is " + f.Name);
    Console.WriteLine("Arguments is " + f.Arguments);
}

通过ChatCompletion终结点使用视觉API

var cl = new OpenAIClient("API KEY");
var p = new ChatCompletionsParameter();

var message = new ChatImageMessage(ChatMessageRole.User);
message.AddTextContent("Please describe this image.");
message.AddImageFile(Path.Combine(Environment.CurrentDirectory, "Image", "Pond.jpg"));
p.Messages.Add(message);
p.Model = "gpt-4-vision-preview";
p.Max_Tokens = 300;

var result = new ChatCompletionStreamResult();
await foreach (var text in cl.ChatCompletionsStreamAsync(p, result, CancellationToken.None))
{
    Console.Write(text);
}

上传文件进行微调或传递给助手。

var p = new FileUploadParameter();
p.File.SetFile("my_file.pdf", File.ReadAllBytes("D:\\Data\\my_file.pdf"));
p.Purpose = "assistants";
var res = await client.FileUploadAsync(p);
Console.WriteLine(res);

图像生成。

var cl = new OpenAIClient("API KEY");
var res = await cl.ImagesGenerationsAsync("Blue sky and green field.");
foreach (var item in res.Data)
{
    Console.WriteLine(item.Url);
}

通过API创建Assistant

var cl = new OpenAIClient("API KEY");
var p = new AssistantCreateParameter();
p.Name = "Legal tutor";
p.Instructions = "You are a personal legal tutor. 
                  Write and run code to legal questions based on passed files.";
p.Model = "gpt-4-1106-preview";

p.Tools = new List<ToolObject>();
p.Tools.Add(new ToolObject("code_interpreter"));
p.Tools.Add(new ToolObject("retrieval"));

var res = await cl.AssistantCreateAsync(p);
Console.WriteLine(res);

将文件添加到助手。

var cl = new OpenAIClient("API KEY");
var res = await cl.FilesAsync();
foreach (var item in res.Data)
{
    if (item.Purpose == "assistants")
    {
        var res1 = await cl.AssistantFileCreateAsync(id, item.Id);
    }
}

调用Assistant流式处理API

var assistantId = "your assistant Id";
var threadId = "your thread Id";
if (threadId.Length == 0)
{
    var res = await cl.ThreadCreateAsync();
    threadId = res.Id;
}
{
    var p = new MessageCreateParameter();
    p.Thread_Id = threadId;
    p.Role = "user";
    p.Content = "Hello! I want to know how to use OpenAI assistant API 
                 to get stream response.";
    var res = await cl.MessageCreateAsync(p);
}
{
    var p = new RunCreateParameter();
    p.Assistant_Id = assistantId;
    p.Thread_Id = threadId;
    p.Stream = true;
    var result = new AssistantMessageStreamResult();
    await foreach (string text in cl.RunCreateStreamAsync
                  (p, result, CancellationToken.None))
    {
        Console.Write(text);
    }
    Console.WriteLine();
    // You can get each server sent event value by these property.
    Console.WriteLine(JsonConvert.SerializeObject(result.Thread));
    Console.WriteLine(JsonConvert.SerializeObject(result.Run));
    Console.WriteLine(JsonConvert.SerializeObject(result.RunStep));
    Console.WriteLine(JsonConvert.SerializeObject(result.Message));
}

函数调用并将工具输出提交到Assistant API

var cl = new OpenAIClient("API KEY");

var p0 = new MessageCreateParameter();
p0.Thread_Id = threadId;
p0.Role = "user";
p0.Content = $"I want to know the whether of Tokyo.";
var res = await cl.MessageCreateAsync(p0);

var p = new RunCreateParameter();
p.Assistant_Id = assistantId;
p.Thread_Id = threadId;
p.Tools = new List<ToolObject>();
p.Tools.Add(CreateGetWheatherTool());

var result = new AssistantMessageStreamResult();
await foreach (string text in cl.RunCreateStreamAsync(p, result, CancellationToken.None))
{
    Console.Write(text);
}
Console.WriteLine();

Console.WriteLine(JsonConvert.SerializeObject(result.Run));

if (result.Run != null)
{
    if (result.Run.Status == "requires_action" &&
        result.Run.Required_Action != null)
    {
        var p1 = new SubmitToolOutputsParameter();
        p1.Thread_Id = threadId;
        p1.Run_Id = result.Run.Id;
        p1.Tool_Outputs = new();
        foreach (var toolCall in result.Run.Required_Action.GetToolCallList())
        {
            Console.WriteLine(toolCall.ToString());

            //Pass output from calling your function. 
            var output = new ToolOutput();
            output.Tool_Call_Id = toolCall.Id;
            var o = new
            {
                Wheather = "Cloud",
                Temperature = "20℃",
                Forecast = "Rain after 3 hours",
            };
            output.Output = $"{toolCall.Function.Arguments} is {JsonConvert.SerializeObject(o)}";
            p1.Tool_Outputs.Add(output);
        }
        await foreach (var text in cl.SubmitToolOutputsStreamAsync(p1))
        {
            Console.Write(text);
        }
    }
}

类体系结构

主要类别有:

  • OpenAIClient
  • XXXParameter
  • XXXAsync
  • XXXResponse
  • RestApiResponse
  • QueryParameter

OpenAIClient

此类管理api密钥和调用终结点。

可以在intellisense中将所有终结点视为方法。

我为所有端点提供具有必需参数的方法。这些是调用终结点的最简单方法。

var res = await cl.AudioTranscriptionsAsync("GoodMorningItFineDayToday.mp3"
    , new MemoryStream(File.ReadAllBytes("D:\\Data\\Dev\\GoodMorningItFineDayToday.mp3"))
    , "whisper-1");

OpenAI提供三种类型的端点。这些内容类型是jsonform-dataserver-sent-event。例子就在那里。

Json端点:

表单数据端点:

流终结点:

因此,我提供了SendJsonAsyncSendFormDataAsyncGetStreamAsync的方法来调用这些端点。这些类管理http标头和content-type和正确处理响应。

您可以使用传递参数对象来调用端点。

var p = new AudioTranscriptionsParameter();
p.File.SetFile("GoodMorningItFineDayToday.mp3", 
new MemoryStream(File.ReadAllBytes("D:\\Data\\Dev\\GoodMorningItFineDayToday.mp3")));
p.Model = "whisper-1";
var res = await cl.SendFormDataAsync<AudioTranscriptionsParameter, 
          AudioTranscriptionsResponse>(p, CancellationToken.None);

但是这个方法需要参数和响应的类型,所以我提供了易于使用的方法。

var p = new AudioTranscriptionsParameter();
p.File.SetFile("GoodMorningItFineDayToday.mp3", 
new MemoryStream(File.ReadAllBytes("D:\\Data\\Dev\\GoodMorningItFineDayToday.mp3")));
p.Model = "whisper-1";
var res = await cl.AudioTranscriptionsAsync(p);

XXX测量仪

我提供了表示端点所有值的参数类。

例如,这是创建助手终结点。

这是AssistantCreateParameter类。

/// <summary>
/// Create an assistant with a model and instructions.
/// <seealso href="https://api.openai.com/v1/assistants">
/// https://api.openai.com/v1/assistants</seealso>
/// </summary>
public partial class AssistantCreateParameter : RestApiParameter, IRestApiParameter
{
    string IRestApiParameter.HttpMethod { get; } = "POST";
    /// <summary>
    /// ID of the model to use. You can use the List models API to see all of 
    /// your available models, or see our Model overview for descriptions of them.
    /// </summary>
    public string Model { get; set; } = "";
    /// <summary>
    /// The name of the assistant. The maximum length is 256 characters.
    /// </summary>
    public string? Name { get; set; }
    /// <summary>
    /// The description of the assistant. The maximum length is 512 characters.
    /// </summary>
    public string? Description { get; set; }
    /// <summary>
    /// The system instructions that the assistant uses. 
    /// The maximum length is 32768 characters.
    /// </summary>
    public string? Instructions { get; set; }
    /// <summary>
    /// A list of tool enabled on the assistant. 
    /// There can be a maximum of 128 tools per assistant. 
    /// Tools can be of types code_interpreter, retrieval, or function.
    /// </summary>
    public List<ToolObject>? Tools { get; set; }
    /// <summary>
    /// A list of file IDs attached to this assistant. 
    /// There can be a maximum of 20 files attached to the assistant. 
    /// Files are ordered by their creation date in ascending order.
    /// </summary>
    public List<string>? File_Ids { get; set; }
    /// <summary>
    /// Set of 16 key-value pairs that can be attached to an object. 
    /// This can be useful for storing additional information about the object 
    /// in a structured format. Keys can be a maximum of 64 characters long 
    /// and values can be a maximum of 512 characters long.
    /// </summary>
    public object? Metadata { get; set; }

    string IRestApiParameter.GetApiPath()
    {
        return $"/assistants";
    }
    public override object GetRequestBody()
    {
        return new {
            model = this.Model,
            name = this.Name,
            description = this.Description,
            instructions = this.Instructions,
            tools = this.Tools,
            file_ids = this.File_Ids,
            metadata = this.Metadata,
        };
    }
}

这些参数类是从API文档生成的。您可以在Github上看到实际的生成器代码。

XXXAsync

生成这些方法。我生成了四种方法,您可以轻松调用这些方法。

AssistantCreate终结点的示例。

public async ValueTask<AssistantCreateResponse> AssistantCreateAsync(string model)
public async ValueTask<AssistantCreateResponse> 
    AssistantCreateAsync(string model, CancellationToken cancellationToken)
public async ValueTask<AssistantCreateResponse> 
    AssistantCreateAsync(AssistantCreateParameter parameter)
public async ValueTask<AssistantCreateResponse> 
    AssistantCreateAsync(AssistantCreateParameter parameter, 
    CancellationToken cancellationToken)

从本质上讲,有两种类型的方法。

一种是传递值仅需要参数。

AssistantCreate终结点所需的模型。因此,我生成:

public async ValueTask<AssistantCreateResponse> AssistantCreateAsync(string model)

此方法易于使用,只需使用必需的参数即可调用终结点。

另一个可以使用所有参数值调用api端点。

您可以像这样创建参数:

var p = new AssistantCreateParameter();
p.Name = "Legal tutor";
p.Instructions = "You are a personal legal tutor. 
                  Write and run code to legal questions based on passed files.";
p.Model = "gpt-4-1106-preview";

p.Tools = new List<ToolObject>();
p.Tools.Add(new ToolObject("code_interpreter"));
p.Tools.Add(new ToolObject("retrieval"));

并将其传递给方法。

var res = await cl.AssistantCreateAsync(p);

XXXResponse

响应类表示api端点的实际响应数据。

例如,Retrieve assistant终结点返回assistant对象。

我提供AssistantObjectResponse。(我创建了这个类,而不是代码生成。)

public class AssistantObjectResponse: RestApiResponse
{
    public string Id { get; set; } = "";
    public int Created_At { get; set; }
    public DateTimeOffset CreateTime
    {
        get
        {
            return new DateTimeOffset
               (DateTime.UnixEpoch.AddSeconds(this.Created_At), TimeSpan.Zero);
        }
    }
    public string Name { get; set; } = "";
    public string Description { get; set; } = "";
    public string Model { get; set; } = "";
    public string Instructions { get; set; } = "";
    public List<ToolObject> Tools { get; set; } = new();
    public List<string>? File_Ids { get; set; }
    public object? MetaData { get; set; }

    public override string ToString()
    {
        return $"{this.Id}\r\n{this.Name}\r\n{this.Instructions}";
    }
}

您可以获取api endpoint的响应值。

RestApi响应

有时,您希望获取响应的元数据。您可以获取响应文本、创建此响应的请求对象等。

这是RestApiResponse类。

public abstract class RestApiResponse : IRestApiResponse
{
    object? IRestApiResponse.Parameter
    {
        get { return _Parameter; }
    }
    HttpRequestMessage IRestApiResponse.Request
    {
        get { return _Request!; }
    }
    string IRestApiResponse.RequestBodyText
    {
        get { return _RequestBodyText; }
    }
    HttpStatusCode IRestApiResponse.StatusCode
    {
        get { return _StatusCode; }
    }
    Dictionary<String, String> IRestApiResponse.Headers
    {
        get { return _Headers; }
    }
    string IRestApiResponse.ResponseBodyText
    {
        get { return _ResponseBodyText; }
    }
}

这些属性是隐藏属性。您可以通过投射到RestApiResponse类来访问它:

var p = new AssistantCreateParameter();
p.Name = "Legal tutor";
p.Instructions = "You are a personal legal tutor. 
                  Write and run code to legal questions based on passed files.";
p.Model = "gpt-4-1106-preview";

var res = await cl.AssistantCreateAsync(p);
var iRes = res as RestApiResponse;
var responseText = iRes.ResponseBodyText;
Dictionary<string, string> responseHeaders = iRes.Headers;
var parameter = iRes.Parameter as AssistantCreateParameter;

您可以通过RestApiResponse将响应文本记录到您自己的日志数据库中。

QueryParameter

一些api端点提供过滤器、分页功能。您可以按QueryParameter类指定条件。

您可以按如下方式指定顺序:

var p = new MessagesParameter();
p.Thread_Id = "thread_xxxxxxxxxxxx";
p.QueryParameter.Order = "asc";

结论

我对OpenAI GPTGPT商店等感到非常兴奋。如果你也对OpenAI感兴趣,我的库可能会对你的工作有所帮助。

https://www.codeproject.com/Articles/5372480/Csharp-OpenAI-Library-that-Supports-Assistants-str

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值