Skip to content

Commit 0784ce1

Browse files
committed
Squashed commit of the following:
commit 0a669a9 Author: Sercan Altundas <srcnalt@users.noreply.github.com> Date: Tue Feb 21 00:25:24 2023 +0200 chore: tests are updated commit 3cbf63b Author: Sercan Altundas <srcnalt@users.noreply.github.com> Date: Tue Feb 21 00:25:13 2023 +0200 feat: form field helper methods are updated commit 5517326 Author: Sercan Altundas <srcnalt@users.noreply.github.com> Date: Tue Feb 21 00:24:51 2023 +0200 feat: form request updated
1 parent 5ee0efb commit 0784ce1

File tree

5 files changed

+42
-66
lines changed

5 files changed

+42
-66
lines changed

Runtime/OpenAIApi.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
using Newtonsoft.Json;
44
using System.Threading.Tasks;
55
using UnityEngine.Networking;
6+
using System.Collections.Generic;
67
using Newtonsoft.Json.Serialization;
78

8-
#if !UNITY_WEBGL
9-
using System.Net.Http;
10-
#endif
11-
129
namespace OpenAI
1310
{
1411
public class OpenAIApi
@@ -88,24 +85,32 @@ private async Task<T> DispatchRequest<T>(string path, string method, byte[] payl
8885
return data;
8986
}
9087

91-
#if !UNITY_WEBGL
9288
/// <summary>
9389
/// Dispatches an HTTP request to the specified path with a multi-part data form.
9490
/// </summary>
9591
/// <param name="path">The path to send the request to.</param>
9692
/// <param name="form">A multi-part data form to upload with the request.</param>
9793
/// <typeparam name="T">Response type of the request.</typeparam>
9894
/// <returns>A Task containing the response from the request as the specified type.</returns>
99-
private async Task<T> DispatchRequest<T>(string path, MultipartFormDataContent form) where T: IResponse
95+
private async Task<T> DispatchRequest<T>(string path, List<IMultipartFormSection> form) where T: IResponse
10096
{
101-
var client = new HttpClient();
102-
client.SetHeaders(Configuration, ContentType.MultipartFormData);
103-
104-
var response = await client.PostAsync(path, form);
105-
var content = await response.Content.ReadAsStringAsync();
97+
T data;
10698

107-
var data = JsonConvert.DeserializeObject<T>(content, jsonSerializerSettings);
99+
using (var request = new UnityWebRequest(path, "POST"))
100+
{
101+
request.SetHeaders(Configuration);
102+
var boundary = UnityWebRequest.GenerateBoundary();
103+
var formSections = UnityWebRequest.SerializeFormSections(form, boundary);
104+
var contentType = $"{ContentType.MultipartFormData}; boundary={Encoding.UTF8.GetString(boundary)}";
105+
request.uploadHandler = new UploadHandlerRaw(formSections) {contentType = contentType};
106+
request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
107+
var asyncOperation = request.SendWebRequest();
108108

109+
while (!asyncOperation.isDone) await Task.Yield();
110+
111+
data = JsonConvert.DeserializeObject<T>(request.downloadHandler.text, jsonSerializerSettings);
112+
}
113+
109114
if (data != null && data.Error != null)
110115
{
111116
ApiError error = data.Error;
@@ -114,7 +119,6 @@ private async Task<T> DispatchRequest<T>(string path, MultipartFormDataContent f
114119

115120
return data;
116121
}
117-
#endif
118122

119123
/// <summary>
120124
/// Create byte array payload from the given request object that contains the parameters.
@@ -184,7 +188,6 @@ public async Task<CreateImageResponse> CreateImage(CreateImageRequest request)
184188
return await DispatchRequest<CreateImageResponse>(path, UnityWebRequest.kHttpVerbPOST, payload);
185189
}
186190

187-
#if !UNITY_WEBGL
188191
/// <summary>
189192
/// Creates an edited or extended image given an original image and a prompt.
190193
/// </summary>
@@ -193,16 +196,15 @@ public async Task<CreateImageResponse> CreateImage(CreateImageRequest request)
193196
public async Task<CreateImageResponse> CreateImageEdit(CreateImageEditRequest request)
194197
{
195198
var path = $"{BASE_PATH}/images/edits";
196-
197-
var form = new MultipartFormDataContent();
199+
200+
var form = new List<IMultipartFormSection>();
198201
form.AddImage(request.Image, "image");
199202
form.AddImage(request.Mask, "mask");
200203
form.AddValue(request.Prompt, "prompt");
201204
form.AddValue(request.N, "n");
202205
form.AddValue(request.Size, "size");
203206
form.AddValue(request.ResponseFormat, "response_format");
204-
form.AddValue(request.User, "user");
205-
207+
206208
return await DispatchRequest<CreateImageResponse>(path, form);
207209
}
208210

@@ -215,15 +217,15 @@ public async Task<CreateImageResponse> CreateImageVariation(CreateImageVariation
215217
{
216218
var path = $"{BASE_PATH}/images/variations";
217219

218-
var form = new MultipartFormDataContent();
220+
var form = new List<IMultipartFormSection>();
219221
form.AddImage(request.Image, "image");
220222
form.AddValue(request.N, "n");
221223
form.AddValue(request.Size, "size");
222224
form.AddValue(request.ResponseFormat, "response_format");
223225
form.AddValue(request.User, "user");
226+
224227
return await DispatchRequest<CreateImageResponse>(path, form);
225228
}
226-
#endif
227229

228230
/// <summary>
229231
/// Creates an embedding vector representing the input text.
@@ -247,7 +249,6 @@ public async Task<ListFilesResponse> ListFiles()
247249
return await DispatchRequest<ListFilesResponse>(path, UnityWebRequest.kHttpVerbGET);
248250
}
249251

250-
#if !UNITY_WEBGL
251252
/// <summary>
252253
/// Upload a file that contains document(s) to be used across various endpoints/features.
253254
/// Currently, the size of all the files uploaded by one organization can be up to 1 GB.
@@ -259,13 +260,12 @@ public async Task<OpenAIFile> CreateFile(CreateFileRequest request)
259260
{
260261
var path = $"{BASE_PATH}/files";
261262

262-
var form = new MultipartFormDataContent();
263+
var form = new List<IMultipartFormSection>();
263264
form.AddJsonl(request.File, "file");
264265
form.AddValue(request.Purpose, "purpose");
265266

266267
return await DispatchRequest<OpenAIFileResponse>(path, form);
267268
}
268-
#endif
269269

270270
/// <summary>
271271
/// Delete a file.

Runtime/Utils/ExtensionMethods.cs

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,74 @@
11
using System.IO;
22
using UnityEngine.Networking;
3-
4-
#if !UNITY_WEBGL
5-
using System.Net.Http;
6-
using System.Net.Http.Headers;
7-
using UnityEngine;
8-
#endif
3+
using System.Collections.Generic;
94

105
namespace OpenAI
116
{
127
public static class ExtensionMethods
138
{
14-
#if !UNITY_WEBGL
159
/// <summary>
1610
/// Read a PNG file and add it to this form.
1711
/// </summary>
18-
/// <param name="form">This form.</param>
12+
/// <param name="form">List of multipart form sections.</param>
1913
/// <param name="path">Path of the file to read.</param>
2014
/// <param name="name">Name of the form field.</param>
21-
public static void AddImage(this MultipartFormDataContent form, string path, string name)
15+
public static void AddImage(this List<IMultipartFormSection> form, string path, string name)
2216
{
2317
if (path != null)
2418
{
25-
var imageContent = new ByteArrayContent(File.ReadAllBytes(path));
26-
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
27-
form.Add(imageContent, name, $"{name}.png");
19+
var data = File.ReadAllBytes(path);
20+
var fileName = Path.GetFileName(path);
21+
form.Add(new MultipartFormFileSection(name, data, fileName, "image/png"));
2822
}
2923
}
3024

3125
/// <summary>
3226
/// Read a JSONL file and add it to this form.
3327
/// </summary>
34-
/// <param name="form">This form.</param>
28+
/// <param name="form">List of multipart form sections.</param>
3529
/// <param name="path">Path of the file to read.</param>
3630
/// <param name="name">Name of the form field.</param>
37-
public static void AddJsonl(this MultipartFormDataContent form, string path, string name)
31+
public static void AddJsonl(this List<IMultipartFormSection> form, string path, string name)
3832
{
3933
if (path != null)
4034
{
41-
var fileContent = new ByteArrayContent(File.ReadAllBytes(path));
42-
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
43-
form.Add(fileContent, name, Path.GetFileName(path));
35+
var data = File.ReadAllBytes(path);
36+
var fileName = Path.GetFileName(path);
37+
form.Add(new MultipartFormFileSection(name, data, fileName, "application/json"));
4438
}
4539
}
4640

4741
/// <summary>
4842
/// Add a primitive value to the form.
4943
/// </summary>
50-
/// <param name="form">This form.</param>
44+
/// <param name="form">List of multipart form sections.</param>
5145
/// <param name="value">Value of the form field.</param>
5246
/// <param name="name">Name of the form field.</param>
53-
public static void AddValue(this MultipartFormDataContent form, object value, string name)
47+
public static void AddValue(this List<IMultipartFormSection> form, object value, string name)
5448
{
5549
if (value != null)
5650
{
57-
form.Add(new StringContent(value.ToString()), name);
51+
form.Add(new MultipartFormDataSection(name, value.ToString()));
5852
}
5953
}
6054

6155
/// <summary>
6256
/// Set headers of the HTTP request with user credentials.
6357
/// </summary>
64-
/// <param name="client">this HttpClient</param>
58+
/// <param name="request">this UnityWebRequest</param>
6559
/// <param name="configuration">Configuration file that contains user credentials.</param>
6660
/// <param name="type">The value of the Accept header for an HTTP request.</param>
67-
public static void SetHeaders(this HttpClient client, Configuration configuration, string type)
61+
public static void SetHeaders(this UnityWebRequest request, Configuration configuration, string type = null)
6862
{
6963
if (configuration.Auth.Organization != null)
7064
{
71-
client.DefaultRequestHeaders.Add("OpenAI-Organization", configuration.Auth.Organization);
65+
request.SetRequestHeader("OpenAI-Organization", configuration.Auth.Organization);
7266
}
73-
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", configuration.Auth.ApiKey);
74-
client.DefaultRequestHeaders.Accept.Clear();
75-
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(type));
76-
}
77-
#endif
78-
79-
// TODO: summary
80-
public static void SetHeaders(this UnityWebRequest request, Configuration configuration, string type)
81-
{
82-
if (configuration.Auth.Organization != null)
67+
if (type != null)
8368
{
84-
request.SetRequestHeader("OpenAI-Organization", configuration.Auth.Organization);
69+
request.SetRequestHeader("Content-Type", type);
8570
}
8671
request.SetRequestHeader("Authorization", "Bearer " + configuration.Auth.ApiKey);
87-
request.SetRequestHeader("Content-Type", type);
8872
}
8973
}
9074
}

Tests/FilesApiTests.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ public class FilesApiTests
88
{
99
private OpenAIApi openai = new OpenAIApi();
1010
private string createdFileId;
11-
1211

13-
#if !UNITY_WEBGL
1412
[Test, Order(0)]
1513
public async Task Create_File()
1614
{
@@ -24,7 +22,6 @@ public async Task Create_File()
2422

2523
createdFileId = file.Id;
2624
}
27-
#endif
2825

2926
[Test, Order(1)]
3027
public async Task List_Files()

Tests/FineTunesApiTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ private async Task DeleteTrainingFile()
1818
await openai.DeleteFile(trainingFileId);
1919
}
2020

21-
#if !UNITY_WEBGL
2221
private async Task CreateTrainingFile()
2322
{
2423
var req = new CreateFileRequest
@@ -65,7 +64,6 @@ public async Task Cancel_FineTune()
6564

6665
await DeleteTrainingFile();
6766
}
68-
#endif
6967

7068
[Test, Order(1)]
7169
public async Task List_FineTunes()

Tests/ImagesApiTests.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ public async Task Create_Image()
2020
var res = await openai.CreateImage(req);
2121
Assert.NotNull(res.Data);
2222
}
23-
2423

25-
#if !UNITY_WEBGL
2624
[Test]
2725
public async Task Create_Image_Edit()
2826
{
@@ -50,6 +48,5 @@ public async Task Create_Image_Variation()
5048
var res = await openai.CreateImageVariation(req);
5149
Assert.NotNull(res.Data);
5250
}
53-
#endif
5451
}
5552
}

0 commit comments

Comments
 (0)