Skip to content

Commit 13f2c10

Browse files
committed
feat: chat and text completion stream support
1 parent 5924018 commit 13f2c10

File tree

2 files changed

+96
-5
lines changed

2 files changed

+96
-5
lines changed

Runtime/DataTypes.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ public sealed class CreateChatCompletionRequest
8383
public string Model { get; set; }
8484
public List<ChatMessage> Messages { get; set; }
8585
public float? Temperature { get; set; } = 1;
86-
public int? N { get; set; } = 1;
87-
public bool? Stream { get; set; } = false;
86+
public int N { get; set; } = 1;
87+
public bool Stream { get; set; } = false;
8888
public string Stop { get; set; }
8989
public int? MaxTokens { get; set; }
9090
public float? PresencePenalty { get; set; } = 0;
@@ -107,6 +107,7 @@ public struct CreateChatCompletionResponse : IResponse
107107
public struct ChatChoice
108108
{
109109
public ChatMessage Message { get; set; }
110+
public ChatMessage Delta { get; set; }
110111
public int? Index { get; set; }
111112
public string FinishReason { get; set; }
112113
}
@@ -116,6 +117,7 @@ public struct ChatMessage
116117
public string Role { get; set; }
117118
public string Content { get; set; }
118119
}
120+
119121
#endregion
120122

121123
#region Audio Transcriptions Data Types
@@ -159,8 +161,8 @@ public sealed class CreateCompletionRequest
159161
public int? MaxTokens { get; set; } = 16;
160162
public float? Temperature { get; set; } = 1;
161163
public float? TopP { get; set; } = 1;
162-
public int? N { get; set; } = 1;
163-
public bool? Stream { get; set; } = false;
164+
public int N { get; set; } = 1;
165+
public bool Stream { get; set; } = false;
164166
public int? Logpropbs { get; set; }
165167
public bool? Echo { get; set; } = false;
166168
public string Stop { get; set; }

Runtime/OpenAIApi.cs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
using System;
2+
using System.IO;
13
using UnityEngine;
24
using System.Text;
35
using Newtonsoft.Json;
6+
using System.Threading;
47
using System.Globalization;
58
using System.Threading.Tasks;
69
using UnityEngine.Networking;
710
using System.Collections.Generic;
8-
using System.IO;
911
using Newtonsoft.Json.Serialization;
12+
using System.Linq;
1013

1114
namespace OpenAI
1215
{
@@ -87,6 +90,61 @@ private async Task<T> DispatchRequest<T>(string path, string method, byte[] payl
8790

8891
return data;
8992
}
93+
94+
/// <summary>
95+
/// Dispatches an HTTP request to the specified path with the specified method and optional payload.
96+
/// </summary>
97+
/// <param name="path">The path to send the request to.</param>
98+
/// <param name="method">The HTTP method to use for the request.</param>
99+
/// <param name="onResponse">A callback function to be called when a response is updated.</param>
100+
/// <param name="onComplete">A callback function to be called when the request is complete.</param>
101+
/// <param name="token">A cancellation token to cancel the request.</param>
102+
/// <param name="payload">An optional byte array of json payload to include in the request.</param>
103+
private async void DispatchRequest<T>(string path, string method, Action<List<T>> onResponse, Action onComplete, CancellationTokenSource token, byte[] payload = null) where T: IResponse
104+
{
105+
using (var request = UnityWebRequest.Put(path, payload))
106+
{
107+
request.method = method;
108+
request.SetHeaders(Configuration, ContentType.ApplicationJson);
109+
110+
var asyncOperation = request.SendWebRequest();
111+
112+
do
113+
{
114+
List<T> dataList = new List<T>();
115+
string[] lines = request.downloadHandler.text.Split('\n').Where(line => line != "").ToArray();
116+
117+
foreach (string line in lines)
118+
{
119+
var value = line.Replace("data: ", "");
120+
121+
if (value.Contains("[DONE]"))
122+
{
123+
onComplete?.Invoke();
124+
break;
125+
}
126+
127+
var data = JsonConvert.DeserializeObject<T>(value, jsonSerializerSettings);
128+
129+
if (data?.Error != null)
130+
{
131+
ApiError error = data.Error;
132+
Debug.LogError($"Error Message: {error.Message}\nError Type: {error.Type}\n");
133+
}
134+
else
135+
{
136+
dataList.Add(data);
137+
}
138+
}
139+
onResponse?.Invoke(dataList);
140+
141+
await Task.Yield();
142+
}
143+
while (!asyncOperation.isDone && !token.IsCancellationRequested);
144+
145+
onComplete?.Invoke();
146+
}
147+
}
90148

91149
/// <summary>
92150
/// Dispatches an HTTP request to the specified path with a multi-part data form.
@@ -167,6 +225,21 @@ public async Task<CreateCompletionResponse> CreateCompletion(CreateCompletionReq
167225
return await DispatchRequest<CreateCompletionResponse>(path, UnityWebRequest.kHttpVerbPOST, payload);
168226
}
169227

228+
/// <summary>
229+
/// Creates a chat completion request as in ChatGPT.
230+
/// </summary>
231+
/// <param name="request">See <see cref="CreateChatCompletionRequest"/></param>
232+
/// <param name="onResponse">Callback function that will be called when stream response is updated.</param>
233+
/// <param name="onComplete">Callback function that will be called when stream response is completed.</param>
234+
/// <param name="token">Cancellation token to cancel the request.</param>
235+
public void CreateCompletionAsync(CreateCompletionRequest request, Action<List<CreateCompletionResponse>> onResponse, Action onComplete, CancellationTokenSource token)
236+
{
237+
var path = $"{BASE_PATH}/completions";
238+
var payload = CreatePayload(request);
239+
240+
DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, onResponse, onComplete, token, payload);
241+
}
242+
170243
/// <summary>
171244
/// Creates a chat completion request as in ChatGPT.
172245
/// </summary>
@@ -176,9 +249,25 @@ public async Task<CreateChatCompletionResponse> CreateChatCompletion(CreateChatC
176249
{
177250
var path = $"{BASE_PATH}/chat/completions";
178251
var payload = CreatePayload(request);
252+
179253
return await DispatchRequest<CreateChatCompletionResponse>(path, UnityWebRequest.kHttpVerbPOST, payload);
180254
}
181255

256+
/// <summary>
257+
/// Creates a chat completion request as in ChatGPT.
258+
/// </summary>
259+
/// <param name="request">See <see cref="CreateChatCompletionRequest"/></param>
260+
/// <param name="onResponse">Callback function that will be called when stream response is updated.</param>
261+
/// <param name="onComplete">Callback function that will be called when stream response is completed.</param>
262+
/// <param name="token">Cancellation token to cancel the request.</param>
263+
public void CreateChatCompletionAsync(CreateChatCompletionRequest request, Action<List<CreateChatCompletionResponse>> onResponse, Action onComplete, CancellationTokenSource token)
264+
{
265+
var path = $"{BASE_PATH}/chat/completions";
266+
var payload = CreatePayload(request);
267+
268+
DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, onResponse, onComplete, token, payload);
269+
}
270+
182271
/// <summary>
183272
/// Creates a new edit for the provided input, instruction, and parameters.
184273
/// </summary>

0 commit comments

Comments
 (0)