Search code examples
unity-game-engineopenai-api

Unity using API openai Error en la solicitud: HTTP/1.1 400 Bad Request


I am trying to use the openai API in a new unity project. However try whatever I try I always get the same 400 bad request error

I have tried both options interleaving between simple JSOM and using wwwform but always the same error. The reason why I use classes is to be able to use the utility to transform to JSON because otherwise it can't be done.

I have a two versions of my code:

Version1

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class OpenAICommunication : MonoBehaviour
{
    private string apiKey = "sk-....."; // hide apikey
    private string apiUrl = "https://api.openai.com/v1/completions";

    private void Update()
    {
        if(Input.GetButtonDown("Jump"))
            AskQuestion("Hola! Este es un juego de unity!");
    }

    IEnumerator SendQuestion(string question)
    {
        MessagesList messages = new MessagesList();
        messages.list = new Message[] { new Message() };
        messages.list[0].role = "user";
        messages.list[0].content = question;
        ApiCall apiCall = new ApiCall();
        apiCall.model = "text-davinci-003";
        apiCall.prompt = question;

        string json = JsonUtility.ToJson(apiCall);
        Debug.Log("JSON: " + json);

        // Configurar el encabezado de autorizaci�n
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "Authorization", "Bearer " + apiKey }
        };
        // headers.Add("Authorization", "Bearer " + apiKey);
        
        // Enviar la solicitud POST a la API de OpenAI
        using (UnityWebRequest www = UnityWebRequest.PostWwwForm(apiUrl, json))
        {
            foreach (var header in headers)
            {
                Debug.Log("HEADER: " + header.Key + header.Value);
                www.SetRequestHeader(header.Key, header.Value);
            }

            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError("Error en la solicitud: " + www.error);
            }
            else
            {
                string responseJson = www.downloadHandler.text;
                // Procesar la respuesta JSON aqu� (extraer y mostrar la respuesta del asistente)
                Debug.Log("Response: " + responseJson);
            }
        }
    }

    public void AskQuestion(string question)
    {
        StartCoroutine(SendQuestion(question));
    }
}

[System.Serializable]
public class Message
{
    public string role;
    public string content;
}
public class MessagesList
{
    public Message[] list;
}
public class ApiCall
{
    public string model;
    public string prompt;
}

Version2

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class OpenAICommunication : MonoBehaviour
{
    private string apiKey = "sk-...."; // hide apikey
    private string apiUrl = "https://api.openai.com/v1/chat/completions";

    private void Update()
    {
        if (Input.GetButtonDown("Jump"))
            AskQuestion("Hola! Este es un juego de unity!");
    }

    IEnumerator SendQuestion(string question)
    {
        // Configurar el encabezado de autorizaci�n
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "Authorization", "Bearer " + apiKey }
        };
        // headers.Add("Authorization", "Bearer " + apiKey);

        MessagesList messages = new MessagesList();
        messages.list = new Message[] { new Message() };
        messages.list[0].role = "user";
        messages.list[0].content = question;
        string json = JsonUtility.ToJson(messages);

        // elimino la parte del string que no corresponde para formatear bien el json.
        // lo eliminado es: { list:  .... y el final }
        json = json.Substring(8, json.Length - 8 - 1);
        // verifico y es correcto.
        Debug.Log("JSON: " + json);

        // Preparar los datos para la solicitud POST utilizo la de chat también para probar pero no cambia nada...
        WWWForm form = new WWWForm();
        form.AddField("model", "gpt-3.5-turbo");
        form.AddField("messages", json);

        // Enviar la solicitud POST a la API de OpenAI
        using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, form))
        {
            foreach (var header in headers)
            {
                Debug.Log("HEADER: " + header.Key + header.Value);
                www.SetRequestHeader(header.Key, header.Value);
            }

            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError("Error en la solicitud: " + www.error);
            }
            else
            {
                string responseJson = www.downloadHandler.text;
                // Procesar la respuesta JSON aqu� (extraer y mostrar la respuesta del asistente)
                Debug.Log("Response: " + responseJson);
            }
        }
    }

    public void AskQuestion(string question)
    {
        StartCoroutine(SendQuestion(question));
    }
}

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

I use both variations of the code with two of the openai urls but always get the same error.

ERROR:

Error en la solicitud: HTTP/1.1 400 Bad Request
0x00007ff62ae8229d (Unity) StackWalker::GetCurrentCallstack
0x00007ff62ae87249 (Unity) StackWalker::ShowCallstack
0x00007ff62be533a1 (Unity) GetStacktrace
0x00007ff62c514c02 (Unity) DebugStringToFile
0x00007ff629d98736 (Unity) DebugLogHandler_CUSTOM_Internal_Log
0x0000025ca0dfc363 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.DebugLogHandler:Internal_Log (UnityEngine.LogType,UnityEngine.LogOption,string,UnityEngine.Object)
0x0000025ca0dfc27b (Mono JIT Code) UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
0x0000025ca0dfbfc0 (Mono JIT Code) UnityEngine.Logger:Log (UnityEngine.LogType,object)
0x0000025c7c685ab5 (Mono JIT Code) UnityEngine.Debug:LogError (object)
0x0000025c7c67b363 (Mono JIT Code) OpenAICommunication/<SendQuestion>d__3:MoveNext () (at D:/Projects/Unity/AI_Project/Assets/Scripts/OpenAICommunication.cs:57)
0x0000025c7c67ab00 (Mono JIT Code) UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
0x0000025c7c67ac2f (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_object_intptr (object,intptr,intptr,intptr)
0x00007ffcf45ee0d4 (mono-2.0-bdwgc) mono_jit_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/mini/mini-runtime.c:3445)
0x00007ffcf452eb74 (mono-2.0-bdwgc) do_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3066)
0x00007ffcf452ed0c (mono-2.0-bdwgc) mono_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3113)
0x00007ff62ad99724 (Unity) scripting_method_invoke
0x00007ff62ad77944 (Unity) ScriptingInvocation::Invoke
0x00007ff62ad4041a (Unity) Coroutine::Run
0x00007ff62ad3de0f (Unity) Coroutine::ContinueCoroutine
0x00007ff62a9fb653 (Unity) AsyncOperation::InvokeCoroutine
0x00007ff62b33d7bc (Unity) UnityWebRequestAsyncOperation::InvokeCoroutine
0x00007ff62b33d9a1 (Unity) UnityWebRequestProto<UnityWebRequestTransport,AtomicRefCounter,RedirectHelper,ResponseHelper,DownloadHandler,UploadHandler,CertificateHandler,HeaderHelper,AsyncOperation>::Job_InvokeCoroutine
0x00007ff62a9aed9a (Unity) BackgroundJobQueue::ExecuteMainThreadJobs
0x00007ff62aa3127c (Unity) `InitPlayerLoopCallbacks'::`2'::EarlyUpdateExecuteMainThreadJobsRegistrator::Forward
0x00007ff62aa1133a (Unity) ExecutePlayerLoop
0x00007ff62aa114c6 (Unity) ExecutePlayerLoop
0x00007ff62aa17d85 (Unity) PlayerLoop
0x00007ff62b9dac6f (Unity) PlayerLoopController::InternalUpdateScene
0x00007ff62b9e784d (Unity) PlayerLoopController::UpdateSceneIfNeededFromMainLoop
0x00007ff62b9e5b51 (Unity) Application::TickTimer
0x00007ff62be59cda (Unity) MainMessageLoop
0x00007ff62be5f540 (Unity) WinMain
0x00007ff62d248bae (Unity) __scrt_common_main_seh
0x00007ffd46ea7614 (KERNEL32) BaseThreadInitThunk
0x00007ffd47c026b1 (ntdll) RtlUserThreadStart

I tried the API in my terminal and it worked fine with the -k prefix.

I don't know why I have the 400 error in unity.


Solution

  • I ended up generating a server with NodeJS to see what I got from unity. Then I discovered that the POST was sending this information in data:

    Received data: %7b%22model%22%3a%22gpt-3.5-turbo%22%2c%22messages%22%3a%5b%7b%22role%22%3a%22s ystem%22%2c%22content%22%3a%22Tu%20heres%20el%20master%20en%20un%20juego%20de%20roles%22%7d%2c %7b%22role%22%3a%22user%22%2c%22content%22%3a%22Hola%21%20Este%20es%20un%20juego%20de%20unity% 21%22%7d%5d%7d Erorr al formatear el json

    Then I found a comment on the web that someone said that sometimes it was better to generate the byte string personally and not automatically, the answer was the following:

    Received data: {"model":"gpt-3.5-turbo","messages":[{"role":"system","content":"Tu heres el ma ster en un juego de roles"},{"role":"user","content":"Hola! Este es un juego de unity!"}]}

    This is the code that finally worked for any of the options in the original post.

    using (UnityWebRequest www = UnityWebRequest.PostWwwForm(apiUrl, json))
        {
            www.method = "POST"; //alternative
            //funtional code
            byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
            www.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
            www.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
            //end funtional code
    
            foreach (var header in headers)
            {
                www.SetRequestHeader(header.Key, header.Value);
            }
    
            yield return www.SendWebRequest();
    
            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError("Error en la solicitud: " + www.error);
            }
            else
            {
                string responseJson = www.downloadHandler.text;
                // Procesar la respuesta JSON aqu� (extraer y mostrar la respuesta del asistente)
                Debug.Log("Response: " + responseJson);
            }
        }