From 223d48e7b635d1cd65c6732e0d90c0797e8067b3 Mon Sep 17 00:00:00 2001 From: Jesse Date: Thu, 23 Oct 2025 10:13:50 -0700 Subject: [PATCH 1/2] init SAAL Lab --- Runtime/OpenAIApi.cs | 62 +++++++++++++++++++++++-------------- Samples~/ChatGPT/ChatGPT.cs | 17 ++++++++-- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/Runtime/OpenAIApi.cs b/Runtime/OpenAIApi.cs index 5dd54f6..376cdbb 100644 --- a/Runtime/OpenAIApi.cs +++ b/Runtime/OpenAIApi.cs @@ -37,13 +37,27 @@ private Configuration Configuration /// OpenAI API base path for requests. private const string BASE_PATH = "https://api.openai.com/v1"; - - public OpenAIApi(string apiKey = null, string organization = null) + private string basePath = BASE_PATH; + + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static void AcceptAllCertificates() + { + System.Net.ServicePointManager.ServerCertificateValidationCallback = + delegate { return true; }; + } + + public OpenAIApi(string apiKey = null, string baseUrl = null, string organization = null) { + AcceptAllCertificates(); if (apiKey != null) { configuration = new Configuration(apiKey, organization); } + if (baseUrl != null) + { + basePath = baseUrl; + } } /// Used for serializing and deserializing PascalCase request object fields into snake_case format for JSON. Ignores null fields when creating JSON strings. @@ -202,7 +216,7 @@ private byte[] CreatePayload(T request) /// public async Task ListModels() { - var path = $"{BASE_PATH}/models"; + var path = $"{basePath}/models"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -213,7 +227,7 @@ public async Task ListModels() /// See public async Task RetrieveModel(string id) { - var path = $"{BASE_PATH}/models/{id}"; + var path = $"{basePath}/models/{id}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -224,7 +238,7 @@ public async Task RetrieveModel(string id) /// See public async Task CreateChatCompletion(CreateChatCompletionRequest request) { - var path = $"{BASE_PATH}/chat/completions"; + var path = $"{basePath}/chat/completions"; var payload = CreatePayload(request); return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, payload); @@ -240,7 +254,7 @@ public async Task CreateChatCompletion(CreateChatC public void CreateChatCompletionAsync(CreateChatCompletionRequest request, Action> onResponse, Action onComplete, CancellationTokenSource token) { request.Stream = true; - var path = $"{BASE_PATH}/chat/completions"; + var path = $"{basePath}/chat/completions"; var payload = CreatePayload(request); DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, onResponse, onComplete, token, payload); @@ -253,7 +267,7 @@ public void CreateChatCompletionAsync(CreateChatCompletionRequest request, Actio /// See public async Task CreateImage(CreateImageRequest request) { - var path = $"{BASE_PATH}/images/generations"; + var path = $"{basePath}/images/generations"; var payload = CreatePayload(request); return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, payload); } @@ -265,7 +279,7 @@ public async Task CreateImage(CreateImageRequest request) /// See public async Task CreateImageEdit(CreateImageEditRequest request) { - var path = $"{BASE_PATH}/images/edits"; + var path = $"{basePath}/images/edits"; var form = new List(); form.AddFile(request.Image, "image", "image/png"); @@ -285,7 +299,7 @@ public async Task CreateImageEdit(CreateImageEditRequest re /// See public async Task CreateImageVariation(CreateImageVariationRequest request) { - var path = $"{BASE_PATH}/images/variations"; + var path = $"{basePath}/images/variations"; var form = new List(); form.AddFile(request.Image, "image", "image/png"); @@ -304,7 +318,7 @@ public async Task CreateImageVariation(CreateImageVariation /// See public async Task CreateEmbeddings(CreateEmbeddingsRequest request) { - var path = $"{BASE_PATH}/embeddings"; + var path = $"{basePath}/embeddings"; var payload = CreatePayload(request); return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, payload); } @@ -316,7 +330,7 @@ public async Task CreateEmbeddings(CreateEmbeddingsReq /// See public async Task CreateAudioTranscription(CreateAudioTranscriptionsRequest request) { - var path = $"{BASE_PATH}/audio/transcriptions"; + var path = $"{basePath}/audio/transcriptions"; var form = new List(); if (string.IsNullOrEmpty(request.File)) @@ -343,7 +357,7 @@ public async Task CreateAudioTranscription(CreateAudioTrans /// See public async Task CreateAudioTranslation(CreateAudioTranslationRequest request) { - var path = $"{BASE_PATH}/audio/translations"; + var path = $"{basePath}/audio/translations"; var form = new List(); if (string.IsNullOrEmpty(request.File)) @@ -368,7 +382,7 @@ public async Task CreateAudioTranslation(CreateAudioTransla /// See public async Task ListFiles() { - var path = $"{BASE_PATH}/files"; + var path = $"{basePath}/files"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -381,7 +395,7 @@ public async Task ListFiles() /// See public async Task CreateFile(CreateFileRequest request) { - var path = $"{BASE_PATH}/files"; + var path = $"{basePath}/files"; var form = new List(); form.AddFile(request.File, "file", "application/json"); @@ -397,7 +411,7 @@ public async Task CreateFile(CreateFileRequest request) /// See public async Task DeleteFile(string id) { - var path = $"{BASE_PATH}/files/{id}"; + var path = $"{basePath}/files/{id}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbDELETE); } @@ -408,7 +422,7 @@ public async Task DeleteFile(string id) /// See public async Task RetrieveFile(string id) { - var path = $"{BASE_PATH}/files/{id}"; + var path = $"{basePath}/files/{id}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -419,7 +433,7 @@ public async Task RetrieveFile(string id) /// See public async Task DownloadFile(string id) { - var path = $"{BASE_PATH}/files/{id}/content"; + var path = $"{basePath}/files/{id}/content"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -431,7 +445,7 @@ public async Task DownloadFile(string id) /// See public async Task CreateFineTune(CreateFineTuneRequest request) { - var path = $"{BASE_PATH}/fine-tunes"; + var path = $"{basePath}/fine-tunes"; var payload = CreatePayload(request); return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, payload); } @@ -442,7 +456,7 @@ public async Task CreateFineTune(CreateFineTuneRequest request) /// See public async Task ListFineTunes() { - var path = $"{BASE_PATH}/fine-tunes"; + var path = $"{basePath}/fine-tunes"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -453,7 +467,7 @@ public async Task ListFineTunes() /// See public async Task RetrieveFineTune(string id) { - var path = $"{BASE_PATH}/fine-tunes/{id}"; + var path = $"{basePath}/fine-tunes/{id}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -464,7 +478,7 @@ public async Task RetrieveFineTune(string id) /// See public async Task CancelFineTune(string id) { - var path = $"{BASE_PATH}/fine-tunes/{id}/cancel"; + var path = $"{basePath}/fine-tunes/{id}/cancel"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST); } @@ -479,7 +493,7 @@ public async Task CancelFineTune(string id) /// See public async Task ListFineTuneEvents(string id, bool stream = false) { - var path = $"{BASE_PATH}/fine-tunes/{id}/events?stream={stream}"; + var path = $"{basePath}/fine-tunes/{id}/events?stream={stream}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbGET); } @@ -490,7 +504,7 @@ public async Task ListFineTuneEvents(string id, bool /// See public async Task DeleteFineTunedModel(string model) { - var path = $"{BASE_PATH}/models/{model}"; + var path = $"{basePath}/models/{model}"; return await DispatchRequest(path, UnityWebRequest.kHttpVerbDELETE); } @@ -501,7 +515,7 @@ public async Task DeleteFineTunedModel(string model) /// See public async Task CreateModeration(CreateModerationRequest request) { - var path = $"{BASE_PATH}/moderations"; + var path = $"{basePath}/moderations"; var payload = CreatePayload(request); return await DispatchRequest(path, UnityWebRequest.kHttpVerbPOST, payload); } diff --git a/Samples~/ChatGPT/ChatGPT.cs b/Samples~/ChatGPT/ChatGPT.cs index 294d14b..8a41985 100644 --- a/Samples~/ChatGPT/ChatGPT.cs +++ b/Samples~/ChatGPT/ChatGPT.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; +using UnityEngine.Networking; namespace OpenAI { @@ -14,16 +15,28 @@ public class ChatGPT : MonoBehaviour [SerializeField] private RectTransform received; private float height; - private OpenAIApi openai = new OpenAIApi(); + private OpenAIApi openai = new OpenAIApi("123", "http://ailadgx-gpu40.utsarr.net:2530/v1"); private List messages = new List(); private string prompt = "Act as a random stranger in a chat room and reply to the questions. Don't break character. Don't ever mention that you are an AI model."; private void Start() { + Debug.Log("Testing connection..."); + TestConnection(); button.onClick.AddListener(SendReply); } + private async void TestConnection() + { + using (var request = UnityWebRequest.Get("http://ailadgx-gpu40.utsarr.net:2530/v1/models")) + { + var operation = request.SendWebRequest(); + while (!operation.isDone) await System.Threading.Tasks.Task.Yield(); + Debug.Log($"Status: {request.responseCode}, Error: {request.error}, Result: {request.downloadHandler.text}"); + } + } + private void AppendMessage(ChatMessage message) { scroll.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); @@ -58,7 +71,7 @@ private async void SendReply() // Complete the instruction var completionResponse = await openai.CreateChatCompletion(new CreateChatCompletionRequest() { - Model = "gpt-4o-mini", + Model = "Qwen/Qwen3-VL-8B-Instruct-FP8", Messages = messages }); From 216187bf70bcdc042bbf5d8ea04cb3d6eee81a9e Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Mon, 27 Oct 2025 10:19:11 -0500 Subject: [PATCH 2/2] Add vision --- Runtime/DataTypes.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Runtime/DataTypes.cs b/Runtime/DataTypes.cs index b94e497..f015382 100644 --- a/Runtime/DataTypes.cs +++ b/Runtime/DataTypes.cs @@ -86,7 +86,7 @@ public class OpenAIModelResponse : OpenAIModel, IResponse public sealed class CreateChatCompletionRequest { public string Model { get; set; } - public List Messages { get; set; } + public List Messages { get; set; } public float? Temperature { get; set; } = 1; public int N { get; set; } = 1; public bool Stream { get; set; } = false; @@ -120,8 +120,8 @@ public struct CreateChatCompletionResponse : IResponse public struct ChatChoice { - public ChatMessage Message { get; set; } - public ChatMessage Delta { get; set; } + public ImageChatMessage Message { get; set; } + public ImageChatMessage Delta { get; set; } public int? Index { get; set; } public string FinishReason { get; set; } public string Logprobs { get; set; } @@ -133,6 +133,20 @@ public struct ChatMessage public string Content { get; set; } } + public class ImageChatMessage + { + public string Role { get; set; } + public string Content { get; set; } + public string ImageBase64 { get; set; } + + public ImageChatMessage(string role, string content, string imageBase64 = null) + { + Role = role; + Content = content; + ImageBase64 = imageBase64; + } + } + #endregion #region Audio Transcriptions Data Types