UnityでChatGPT APIを利用する

UnityからChatGPTのAPIを利用するサンプルを作ってみました。

https://github.com/mikito/unity-chat-gpt-sample

以下のような簡易的なUIで動作の確認が行えます。
![スクリーンショット_2023-03-05_3_59_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/9014f206-b7ac-6d6e-0fe6-d78ec1669864.png)

OpenAIのAPI Keyの発行や、ChatGPT APIの利用についての概要は、以下の記事を参考にしていただければと思います。

https://qiita.com/mikito/items/b69f38c54b362c20e9e6

### API通信部分のコード

以下、簡単ですがAPI通信部分の全コードです。一応、サンプル外でも使い回しやすいように、OpenAIChatCompletionAPIというクラスに切り出してあります。

```cs:OpenAIChatCompletionAPI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;

// https://platform.openai.com/docs/api-reference/chat/create
public class OpenAIChatCompletionAPI
{
    const string API_URL = "https://api.openai.com/v1/chat/completions";
    string apiKey;
    JsonSerializerSettings settings = new JsonSerializerSettings();


    public OpenAIChatCompletionAPI(string apiKey)
    {
        this.apiKey = apiKey;
        settings.NullValueHandling = NullValueHandling.Ignore;
    }

    public RequestHandler CreateCompletionRequest(RequestData requestData)
    {
        var json = JsonConvert.SerializeObject(requestData, settings);

        byte[] data = System.Text.Encoding.UTF8.GetBytes(json);

        var request = new UnityWebRequest(API_URL, "POST");
        request.SetRequestHeader("Authorization", $"Bearer {apiKey}");
        request.SetRequestHeader("Content-Type", "application/json");
        request.uploadHandler = new UploadHandlerRaw(data);
        request.downloadHandler = new DownloadHandlerBuffer();

        return new RequestHandler(request);
    }

    public class RequestHandler
    {
        public bool IsCompleted { get; private set; }
        public bool IsError => Error != null;
        public string Error { get; private set; }
        public ResponseData Response { get; private set; }

        UnityWebRequest request;

        public RequestHandler(UnityWebRequest request)
        {
            this.request = request;
        }

        public IEnumerator Send()
        {
            using (request)
            {
                yield return request.SendWebRequest();

                if (request.result != UnityWebRequest.Result.Success)
                {
                    Error = "[OpenAIChatCompletionAPI] " + request.error + "\n\n" + request.downloadHandler.text;
                }
                else
                {
                    Response = JsonConvert.DeserializeObject<ResponseData>(request.downloadHandler.text);
                }
            }
        }
    }

    [System.Serializable]
    public class RequestData
    {
        public string model = "gpt-3.5-turbo";
        public List<Message> messages;
        public float? temperature = null; // [0.0 - 2.0]
        public float? top_p = null;
        public int? n = null;
        public bool? stream = null;
        public List<string> stop = null;
        public int? max_tokens = null;
        public float? presence_penalty = null;
        public float? frequency_penalty = null;
        public Dictionary<int, int> logit_bias = null;
        public string user = null;
    }

    [System.Serializable]
    public class Message
    {
        public string role;
        public string content;
    }

    [System.Serializable]
    public class Usage
    {
        public int prompt_tokens;
        public int completion_tokens;
        public int total_tokens;
    }

    [System.Serializable]
    public class Choice
    {
        public Message message;
        public string finish_reason;
        public int index;
    }

    [System.Serializable]
    public class ResponseData
    {
        public string id;
        public string @object;
        public int created;
        public string model;
        public Usage usage;
        public List<Choice> choices;
    }
}

```

利用方法は、以下のようにOpenAIChatCompletionAPIにAPI Keyを渡し、リクエストためのMessageやその他プロパティを設定すればOKです。

```cs
    void Start()
    {
        StartCoroutine(ChatCompletionRequest());
    }

    IEnumerator ChatCompletionRequest()
    {
        var chatCompletionAPI = new OpenAIChatCompletionAPI("[OPEN_AI_API_KEY]");

        List<OpenAIChatCompletionAPI.Message> messages = new List<OpenAIChatCompletionAPI.Message>()
        {
            new OpenAIChatCompletionAPI.Message(){role = "system", content = "あなたは優秀なAIアシスタントです。"},
            new OpenAIChatCompletionAPI.Message(){role = "user", content = "今から質問に答えてください"},
        };

        var request = chatCompletionAPI.CreateCompletionRequest(
            new OpenAIChatCompletionAPI.RequestData() { messages = messages }
        );

        yield return request.Send();

        var message = request.Response.choices[0].message;

        Debug.Log(message.content); // 推論された応答
    }
```

UniTaskの場合
-------
UniTaskを利用する場合は以下のブランチの実装の方がシンプルで良いかと思います。

https://github.com/mikito/unity-chat-gpt-sample/tree/unitask

```cs:OpenAIChatCompletionAPI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.Threading;
using Cysharp.Threading.Tasks;

// https://platform.openai.com/docs/api-reference/chat/create
public class OpenAIChatCompletionAPI
{
    ...

    public async UniTask<ResponseData> CreateCompletionRequest(RequestData requestData, CancellationToken cancellationToken)
    {
        var json = JsonConvert.SerializeObject(requestData, settings);

        byte[] data = System.Text.Encoding.UTF8.GetBytes(json);

        using (var request = new UnityWebRequest(API_URL, "POST"))
        {
            request.SetRequestHeader("Authorization", $"Bearer {apiKey}");
            request.SetRequestHeader("Content-Type", "application/json");
            request.uploadHandler = new UploadHandlerRaw(data);
            request.downloadHandler = new DownloadHandlerBuffer();

            await request.SendWebRequest().WithCancellation(cancellationToken);
            return JsonConvert.DeserializeObject<ResponseData>(request.downloadHandler.text);
        }
    }

    ...
```

こちらの利用方法は以下になります。

```cs
    void Start()
    {
        ChatCompletionRequest().Forget();
    }

    async UniTask ChatCompletionRequest()
    {
        var chatCompletionAPI = new OpenAIChatCompletionAPI("[OPEN_AI_API_KEY]");

        List<OpenAIChatCompletionAPI.Message> messages = new List<OpenAIChatCompletionAPI.Message>()
        {
            new OpenAIChatCompletionAPI.Message(){role = "system", content = "あなたは優秀なAIアシスタントです。"},
            new OpenAIChatCompletionAPI.Message(){role = "user", content = "今から質問に答えてください"},
        };

        var response = await chatCompletionAPI.CreateCompletionRequest(
            new OpenAIChatCompletionAPI.RequestData() { messages = messages },
            this.GetCancellationTokenOnDestroy()
        );

        var message = request.Response.choices[0].message;
        Debug.Log(message.content); // 推論された応答
    }

```


以上、何かしらに活用いただければ幸いです。