From 8b662961c15fec92193a8a645cf6a8e89b78c39d Mon Sep 17 00:00:00 2001 From: Ernie Date: Wed, 21 Feb 2024 12:15:16 -0800 Subject: [PATCH 1/5] Fix revised_prompt parameter name so it will be correctly deserialized. --- OpenAI_API/Images/ImageResult.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/OpenAI_API/Images/ImageResult.cs b/OpenAI_API/Images/ImageResult.cs index 5c9770e..d2f2aa0 100644 --- a/OpenAI_API/Images/ImageResult.cs +++ b/OpenAI_API/Images/ImageResult.cs @@ -51,10 +51,11 @@ public class Data [JsonProperty("b64_json")] public string Base64Data { get; set; } - /// - /// The prompt that was used to generate the image, if there was any revision to the prompt. - /// - public string RevisedPrompt { get; set; } + /// + /// The prompt that was used to generate the image, if there was any revision to the prompt. + /// + [JsonProperty("revised_prompt")] + public string RevisedPrompt { get; set; } } } From 0f4384be3627661b703ba85ba64c278e6e5ddd43 Mon Sep 17 00:00:00 2001 From: Ernie Date: Sun, 25 Feb 2024 10:14:15 -0800 Subject: [PATCH 2/5] whitespace --- OpenAI_API/Images/ImageResult.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenAI_API/Images/ImageResult.cs b/OpenAI_API/Images/ImageResult.cs index d2f2aa0..a871a47 100644 --- a/OpenAI_API/Images/ImageResult.cs +++ b/OpenAI_API/Images/ImageResult.cs @@ -52,9 +52,9 @@ public class Data public string Base64Data { get; set; } /// - /// The prompt that was used to generate the image, if there was any revision to the prompt. - /// - [JsonProperty("revised_prompt")] + /// The prompt that was used to generate the image, if there was any revision to the prompt. + /// + [JsonProperty("revised_prompt")] public string RevisedPrompt { get; set; } } From 85da80bf87b58a850d51280785bf59a110f0ac89 Mon Sep 17 00:00:00 2001 From: Ernie Date: Sun, 25 Feb 2024 10:26:26 -0800 Subject: [PATCH 3/5] formatting --- OpenAI_API/Images/ImageResult.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenAI_API/Images/ImageResult.cs b/OpenAI_API/Images/ImageResult.cs index a871a47..17e6e87 100644 --- a/OpenAI_API/Images/ImageResult.cs +++ b/OpenAI_API/Images/ImageResult.cs @@ -49,13 +49,13 @@ public class Data /// The base64-encoded image data as returned by the API /// [JsonProperty("b64_json")] - public string Base64Data { get; set; } + public string Base64Data { get; set; } - /// + /// /// The prompt that was used to generate the image, if there was any revision to the prompt. /// [JsonProperty("revised_prompt")] - public string RevisedPrompt { get; set; } + public string RevisedPrompt { get; set; } } } From 4fce673a82223110d188212e43f23c491ac6722d Mon Sep 17 00:00:00 2001 From: Ernie Date: Fri, 1 Mar 2024 20:20:26 -0800 Subject: [PATCH 4/5] fix a few little things, icon location, reporting etc. --- OpenAI_API/EndpointBase.cs | 785 ++++++++++++++++--------------- OpenAI_API/Images/ImageResult.cs | 2 +- 2 files changed, 397 insertions(+), 390 deletions(-) diff --git a/OpenAI_API/EndpointBase.cs b/OpenAI_API/EndpointBase.cs index d981c7e..3a27fa4 100644 --- a/OpenAI_API/EndpointBase.cs +++ b/OpenAI_API/EndpointBase.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; + using System; using System.Collections.Generic; using System.Diagnostics; @@ -11,233 +12,233 @@ namespace OpenAI_API { - /// - /// A base object for any OpenAI API endpoint, encompassing common functionality - /// - public abstract class EndpointBase - { - private const string UserAgent = "okgodoit/dotnet_openai_api"; - - /// - /// The internal reference to the API, mostly used for authentication - /// - protected readonly OpenAIAPI _Api; - - /// - /// Constructor of the api endpoint base, to be called from the contructor of any devived classes. Rather than instantiating any endpoint yourself, access it through an instance of . - /// - /// - internal EndpointBase(OpenAIAPI api) - { - this._Api = api; - } - - /// - /// The name of the endpoint, which is the final path segment in the API URL. Must be overriden in a derived class. - /// - protected abstract string Endpoint { get; } - - /// - /// Gets the URL of the endpoint, based on the base OpenAI API URL followed by the endpoint name. For example "https://api.openai.com/v1/completions" - /// - protected string Url - { - get - { - return string.Format(_Api.ApiUrlFormat, _Api.ApiVersion, Endpoint); - } - } - - /// - /// Gets an HTTPClient with the appropriate authorization and other headers set - /// - /// The fully initialized HttpClient - /// Thrown if there is no valid authentication. Please refer to for details. - protected HttpClient GetClient() - { - if (_Api.Auth?.ApiKey is null) - { - throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for details."); - } - - HttpClient client; - var clientFactory = _Api.HttpClientFactory; - if (clientFactory != null) - { - client = clientFactory.CreateClient(); - } - else - { - client = new HttpClient(); - } - - client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _Api.Auth.ApiKey); - // Further authentication-header used for Azure openAI service - client.DefaultRequestHeaders.Add("api-key", _Api.Auth.ApiKey); - client.DefaultRequestHeaders.Add("User-Agent", UserAgent); - if (!string.IsNullOrEmpty(_Api.Auth.OpenAIOrganization)) client.DefaultRequestHeaders.Add("OpenAI-Organization", _Api.Auth.OpenAIOrganization); - - return client; - } - - /// - /// Formats a human-readable error message relating to calling the API and parsing the response - /// - /// The full content returned in the http response - /// The http response object itself - /// The name of the endpoint being used - /// Additional details about the endpoint of this request (optional) - /// A human-readable string error message. - protected string GetErrorMessage(string resultAsString, HttpResponseMessage response, string name, string description = "") - { - return $"Error at {name} ({description}) with HTTP status code: {response.StatusCode}. Content: {resultAsString ?? ""}"; - } - - - /// - /// Sends an HTTP request and returns the response. Does not do any parsing, but does do error handling. - /// - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. - /// (optional) A json-serializable object to include in the request body. - /// (optional) If true, streams the response. Otherwise waits for the entire response before returning. - /// The HttpResponseMessage of the response, which is confirmed to be successful. - /// Throws an exception if a non-success HTTP response was returned - private async Task HttpRequestRaw(string url = null, HttpMethod verb = null, object postData = null, bool streaming = false) - { - if (string.IsNullOrEmpty(url)) - url = this.Url; - - if (verb == null) - verb = HttpMethod.Get; - - using var client = GetClient(); - - HttpResponseMessage response = null; - string resultAsString = null; - HttpRequestMessage req = new HttpRequestMessage(verb, url); - - if (postData != null) - { - if (postData is HttpContent) - { - req.Content = postData as HttpContent; - } - else - { - string jsonContent = JsonConvert.SerializeObject(postData, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); - var stringContent = new StringContent(jsonContent, UnicodeEncoding.UTF8, "application/json"); - req.Content = stringContent; - } - } - response = await client.SendAsync(req, streaming ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead); - - if (response.IsSuccessStatusCode) - { - return response; - } - else - { - try - { - resultAsString = await response.Content.ReadAsStringAsync(); - } - catch (Exception readError) - { - resultAsString = "Additionally, the following error was thrown when attemping to read the response content: " + readError.ToString(); - } - - if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - throw new AuthenticationException("OpenAI rejected your authorization, most likely due to an invalid API Key. Try checking your API Key and see https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for guidance. Full API response follows: " + resultAsString); - } - else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) - { - throw new HttpRequestException("OpenAI had an internal server error, which can happen occasionally. Please retry your request. " + GetErrorMessage(resultAsString, response, Endpoint, url)); - } - else - { - var errorToThrow = new HttpRequestException(GetErrorMessage(resultAsString, response, Endpoint, url)); - - var parsedError = JsonConvert.DeserializeObject(resultAsString); - try - { - errorToThrow.Data.Add("message", parsedError.Error.Message); - errorToThrow.Data.Add("type", parsedError.Error.ErrorType); - errorToThrow.Data.Add("param", parsedError.Error.Parameter); - errorToThrow.Data.Add("code", parsedError.Error.ErrorCode); - } - catch (Exception parsingError) - { - throw new HttpRequestException(errorToThrow.Message, parsingError); - } - throw errorToThrow; - } - } - } - - /// - /// Sends an HTTP Get request and return the string content of the response without parsing, and does error handling. - /// - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. - /// (optional) A json-serializable object to include in the request body. - /// The text string of the response, which is confirmed to be successful. - /// Throws an exception if a non-success HTTP response was returned - internal async Task HttpGetContent(string url = null, HttpMethod verb = null, object postData = null) - { - var response = await HttpRequestRaw(url, verb, postData); - return await response.Content.ReadAsStringAsync(); - } - - /// - /// Sends an HTTP request and return the raw content stream of the response without parsing, and does error handling. - /// - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. - /// (optional) A json-serializable object to include in the request body. - /// The response content stream, which is confirmed to be successful. - /// Throws an exception if a non-success HTTP response was returned - internal async Task HttpRequest(string url = null, HttpMethod verb = null, object postData = null) - { - var response = await HttpRequestRaw(url, verb, postData); - return await response.Content.ReadAsStreamAsync(); - } - - - /// - /// Sends an HTTP Request and does initial parsing - /// - /// The -derived class for the result - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. - /// (optional) A json-serializable object to include in the request body. - /// An awaitable Task with the parsed result of type - /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. - private async Task HttpRequest(string url = null, HttpMethod verb = null, object postData = null) where T : ApiResultBase - { - var response = await HttpRequestRaw(url, verb, postData); - string resultAsString = await response.Content.ReadAsStringAsync(); - - var res = JsonConvert.DeserializeObject(resultAsString); - try - { - res.Organization = response.Headers.GetValues("Openai-Organization").FirstOrDefault(); - res.RequestId = response.Headers.GetValues("X-Request-ID").FirstOrDefault(); - res.ProcessingTime = TimeSpan.FromMilliseconds(int.Parse(response.Headers.GetValues("Openai-Processing-Ms").First())); - res.OpenaiVersion = response.Headers.GetValues("Openai-Version").FirstOrDefault(); - if (string.IsNullOrEmpty(res.Model)) - res.Model = response.Headers.GetValues("Openai-Model").FirstOrDefault(); - } - catch (Exception e) - { - Debug.Print($"Issue parsing metadata of OpenAi Response. Url: {url}, Error: {e.ToString()}, Response: {resultAsString}. This is probably ignorable."); - } - - return res; - } - - /* + /// + /// A base object for any OpenAI API endpoint, encompassing common functionality + /// + public abstract class EndpointBase + { + private const string UserAgent = "okgodoit/dotnet_openai_api"; + + /// + /// The internal reference to the API, mostly used for authentication + /// + protected readonly OpenAIAPI _Api; + + /// + /// Constructor of the api endpoint base, to be called from the contructor of any devived classes. Rather than instantiating any endpoint yourself, access it through an instance of . + /// + /// + internal EndpointBase(OpenAIAPI api) + { + this._Api = api; + } + + /// + /// The name of the endpoint, which is the final path segment in the API URL. Must be overriden in a derived class. + /// + protected abstract string Endpoint { get; } + + /// + /// Gets the URL of the endpoint, based on the base OpenAI API URL followed by the endpoint name. For example "https://api.openai.com/v1/completions" + /// + protected string Url + { + get + { + return string.Format(_Api.ApiUrlFormat, _Api.ApiVersion, Endpoint); + } + } + + /// + /// Gets an HTTPClient with the appropriate authorization and other headers set + /// + /// The fully initialized HttpClient + /// Thrown if there is no valid authentication. Please refer to for details. + protected HttpClient GetClient() + { + if (_Api.Auth?.ApiKey is null) + { + throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for details."); + } + + HttpClient client; + var clientFactory = _Api.HttpClientFactory; + if (clientFactory != null) + { + client = clientFactory.CreateClient(); + } + else + { + client = new HttpClient(); + } + + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _Api.Auth.ApiKey); + // Further authentication-header used for Azure openAI service + client.DefaultRequestHeaders.Add("api-key", _Api.Auth.ApiKey); + client.DefaultRequestHeaders.Add("User-Agent", UserAgent); + if (!string.IsNullOrEmpty(_Api.Auth.OpenAIOrganization)) client.DefaultRequestHeaders.Add("OpenAI-Organization", _Api.Auth.OpenAIOrganization); + + return client; + } + + /// + /// Formats a human-readable error message relating to calling the API and parsing the response + /// + /// The full content returned in the http response + /// The http response object itself + /// The name of the endpoint being used + /// Additional details about the endpoint of this request (optional) + /// A human-readable string error message. + protected string GetErrorMessage(string resultAsString, HttpResponseMessage response, string name, string description = "") + { + return $"Error at {name} ({description}) with HTTP status code: {response.StatusCode}. Content: {resultAsString ?? ""}"; + } + + /// + /// Sends an HTTP request and returns the response. Does not do any parsing, but does do error handling. + /// + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. + /// (optional) A json-serializable object to include in the request body. + /// (optional) If true, streams the response. Otherwise waits for the entire response before returning. + /// The HttpResponseMessage of the response, which is confirmed to be successful. + /// Throws an exception if a non-success HTTP response was returned + private async Task HttpRequestRaw(string url = null, HttpMethod verb = null, object postData = null, bool streaming = false) + { + if (string.IsNullOrEmpty(url)) + url = this.Url; + + if (verb == null) + verb = HttpMethod.Get; + + using var client = GetClient(); + + HttpResponseMessage response = null; + string resultAsString = null; + HttpRequestMessage req = new HttpRequestMessage(verb, url); + + if (postData != null) + { + if (postData is HttpContent) + { + req.Content = postData as HttpContent; + } + else + { + string jsonContent = JsonConvert.SerializeObject(postData, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); + var stringContent = new StringContent(jsonContent, UnicodeEncoding.UTF8, "application/json"); + req.Content = stringContent; + } + } + response = await client.SendAsync(req, streaming ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead); + + if (response.IsSuccessStatusCode) + { + return response; + } + else + { + try + { + resultAsString = await response.Content.ReadAsStringAsync(); + } + catch (Exception readError) + { + resultAsString = "Additionally, the following error was thrown when attemping to read the response content: " + readError.ToString(); + } + + if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + throw new AuthenticationException("OpenAI rejected your authorization, most likely due to an invalid API Key. Try checking your API Key and see https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for guidance. Full API response follows: " + resultAsString); + } + else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) + { + throw new HttpRequestException("OpenAI had an internal server error, which can happen occasionally. Please retry your request. " + GetErrorMessage(resultAsString, response, Endpoint, url)); + } + else + { + var errorToThrow = new HttpRequestException(GetErrorMessage(resultAsString, response, Endpoint, url)); + + var parsedError = JsonConvert.DeserializeObject(resultAsString); + try + { + errorToThrow.Data.Add("message", parsedError.Error.Message); + errorToThrow.Data.Add("type", parsedError.Error.ErrorType); + errorToThrow.Data.Add("param", parsedError.Error.Parameter); + errorToThrow.Data.Add("code", parsedError.Error.ErrorCode); + } + catch (Exception parsingError) + { + throw new HttpRequestException(errorToThrow.Message, parsingError); + } + throw errorToThrow; + } + } + } + + /// + /// Sends an HTTP Get request and return the string content of the response without parsing, and does error handling. + /// + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. + /// (optional) A json-serializable object to include in the request body. + /// The text string of the response, which is confirmed to be successful. + /// Throws an exception if a non-success HTTP response was returned + internal async Task HttpGetContent(string url = null, HttpMethod verb = null, object postData = null) + { + var response = await HttpRequestRaw(url, verb, postData); + return await response.Content.ReadAsStringAsync(); + } + + /// + /// Sends an HTTP request and return the raw content stream of the response without parsing, and does error handling. + /// + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. + /// (optional) A json-serializable object to include in the request body. + /// The response content stream, which is confirmed to be successful. + /// Throws an exception if a non-success HTTP response was returned + internal async Task HttpRequest(string url = null, HttpMethod verb = null, object postData = null) + { + var response = await HttpRequestRaw(url, verb, postData); + return await response.Content.ReadAsStreamAsync(); + } + + /// + /// Sends an HTTP Request and does initial parsing + /// + /// The -derived class for the result + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. + /// (optional) A json-serializable object to include in the request body. + /// An awaitable Task with the parsed result of type + /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. + private async Task HttpRequest(string url = null, HttpMethod verb = null, object postData = null) where T : ApiResultBase + { + var response = await HttpRequestRaw(url, verb, postData); + string resultAsString = await response.Content.ReadAsStringAsync(); + + var res = JsonConvert.DeserializeObject(resultAsString); + + try + { + + res.Organization = response.Headers.GetValues("Openai-Organization").FirstOrDefault(); + res.RequestId = response.Headers.GetValues("X-Request-ID").FirstOrDefault(); + res.ProcessingTime = TimeSpan.FromMilliseconds(int.Parse(response.Headers.GetValues("Openai-Processing-Ms").First())); + res.OpenaiVersion = response.Headers.GetValues("Openai-Version").FirstOrDefault(); + if (string.IsNullOrEmpty(res.Model)) + res.Model = response.Headers.GetValues("Openai-Model").FirstOrDefault(); + } + catch (Exception e) + { + Debug.Print($"Issue parsing metadata of OpenAi Response. Url: {url}, Error: {e.ToString()}, Response: {resultAsString}. This is probably ignorable."); + } + + return res; + } + + /* /// /// Sends an HTTP Request, supporting a streaming response /// @@ -271,61 +272,58 @@ private async Task StreamingHttpRequest(string url = null, HttpMethod verb } */ - /// - /// Sends an HTTP Get request and does initial parsing - /// - /// The -derived class for the result - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// An awaitable Task with the parsed result of type - /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. - internal async Task HttpGet(string url = null) where T : ApiResultBase - { - return await HttpRequest(url, HttpMethod.Get); - } - - /// - /// Sends an HTTP Post request and does initial parsing - /// - /// The -derived class for the result - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) A json-serializable object to include in the request body. - /// An awaitable Task with the parsed result of type - /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. - internal async Task HttpPost(string url = null, object postData = null) where T : ApiResultBase - { - return await HttpRequest(url, HttpMethod.Post, postData); - } - - /// - /// Sends an HTTP Delete request and does initial parsing - /// - /// The -derived class for the result - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) A json-serializable object to include in the request body. - /// An awaitable Task with the parsed result of type - /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. - internal async Task HttpDelete(string url = null, object postData = null) where T : ApiResultBase - { - return await HttpRequest(url, HttpMethod.Delete, postData); - } - - - /// - /// Sends an HTTP Put request and does initial parsing - /// - /// The -derived class for the result - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) A json-serializable object to include in the request body. - /// An awaitable Task with the parsed result of type - /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. - internal async Task HttpPut(string url = null, object postData = null) where T : ApiResultBase - { - return await HttpRequest(url, HttpMethod.Put, postData); - } - - - - /* + /// + /// Sends an HTTP Get request and does initial parsing + /// + /// The -derived class for the result + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// An awaitable Task with the parsed result of type + /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. + internal async Task HttpGet(string url = null) where T : ApiResultBase + { + return await HttpRequest(url, HttpMethod.Get); + } + + /// + /// Sends an HTTP Post request and does initial parsing + /// + /// The -derived class for the result + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) A json-serializable object to include in the request body. + /// An awaitable Task with the parsed result of type + /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. + internal async Task HttpPost(string url = null, object postData = null) where T : ApiResultBase + { + return await HttpRequest(url, HttpMethod.Post, postData); + } + + /// + /// Sends an HTTP Delete request and does initial parsing + /// + /// The -derived class for the result + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) A json-serializable object to include in the request body. + /// An awaitable Task with the parsed result of type + /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. + internal async Task HttpDelete(string url = null, object postData = null) where T : ApiResultBase + { + return await HttpRequest(url, HttpMethod.Delete, postData); + } + + /// + /// Sends an HTTP Put request and does initial parsing + /// + /// The -derived class for the result + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) A json-serializable object to include in the request body. + /// An awaitable Task with the parsed result of type + /// Throws an exception if a non-success HTTP response was returned or if the result couldn't be parsed. + internal async Task HttpPut(string url = null, object postData = null) where T : ApiResultBase + { + return await HttpRequest(url, HttpMethod.Put, postData); + } + + /* /// /// Sends an HTTP request and handles a streaming response. Does basic line splitting and error handling. /// @@ -359,111 +357,120 @@ private async IAsyncEnumerable HttpStreamingRequestRaw(string url = null } */ - - /// - /// Sends an HTTP request and handles a streaming response. Does basic line splitting and error handling. - /// - /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. - /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. - /// (optional) A json-serializable object to include in the request body. - /// The HttpResponseMessage of the response, which is confirmed to be successful. - /// Throws an exception if a non-success HTTP response was returned - protected async IAsyncEnumerable HttpStreamingRequest(string url = null, HttpMethod verb = null, object postData = null) where T : ApiResultBase - { - var response = await HttpRequestRaw(url, verb, postData, true); - - string organization = null; - string requestId = null; - TimeSpan processingTime = TimeSpan.Zero; - string openaiVersion = null; - string modelFromHeaders = null; - - try - { - organization = response.Headers.GetValues("Openai-Organization").FirstOrDefault(); - requestId = response.Headers.GetValues("X-Request-ID").FirstOrDefault(); - processingTime = TimeSpan.FromMilliseconds(int.Parse(response.Headers.GetValues("Openai-Processing-Ms").First())); - openaiVersion = response.Headers.GetValues("Openai-Version").FirstOrDefault(); - modelFromHeaders = response.Headers.GetValues("Openai-Model").FirstOrDefault(); - } - catch (Exception e) - { - Debug.Print($"Issue parsing metadata of OpenAi Response. Url: {url}, Error: {e.ToString()}. This is probably ignorable."); - } - - string resultAsString = ""; - - using (var stream = await response.Content.ReadAsStreamAsync()) - using (StreamReader reader = new StreamReader(stream)) - { - string line; - while ((line = await reader.ReadLineAsync()) != null) - { - resultAsString += line + Environment.NewLine; - - if (line.StartsWith("data:")) - line = line.Substring("data:".Length); - - line = line.TrimStart(); - - if (line == "[DONE]") - { - yield break; - } - else if (line.StartsWith(":")) - { } - else if (!string.IsNullOrWhiteSpace(line)) - { - var res = JsonConvert.DeserializeObject(line); - - res.Organization = organization; - res.RequestId = requestId; - res.ProcessingTime = processingTime; - res.OpenaiVersion = openaiVersion; - if (string.IsNullOrEmpty(res.Model)) - res.Model = modelFromHeaders; - - yield return res; - } - } - } - } - - internal class ApiErrorResponse - { - /// - /// The error details - /// - [JsonProperty("error")] - public ApiErrorResponseError Error { get; set; } - } - internal class ApiErrorResponseError - { - /// - /// The error message - /// - [JsonProperty("message")] - - public string Message { get; set; } - - /// - /// The type of error - /// - [JsonProperty("type")] - public string ErrorType { get; set; } - - /// - /// The parameter that caused the error - /// - [JsonProperty("param")] - - public string Parameter { get; set; } - - /// - /// The error code - /// - [JsonProperty("code")] - public string ErrorCode { get; set; } - } - } + /// + /// Sends an HTTP request and handles a streaming response. Does basic line splitting and error handling. + /// + /// (optional) If provided, overrides the url endpoint for this request. If omitted, then will be used. + /// (optional) The HTTP verb to use, for example "". If omitted, then "GET" is assumed. + /// (optional) A json-serializable object to include in the request body. + /// The HttpResponseMessage of the response, which is confirmed to be successful. + /// Throws an exception if a non-success HTTP response was returned + protected async IAsyncEnumerable HttpStreamingRequest(string url = null, HttpMethod verb = null, object postData = null) where T : ApiResultBase + { + var response = await HttpRequestRaw(url, verb, postData, true); + + string organization = null; + string requestId = null; + TimeSpan processingTime = TimeSpan.Zero; + string openaiVersion = null; + string modelFromHeaders = null; + var headerNameHolder = ""; + try + { + headerNameHolder = "Openai-Organization"; + organization = response.Headers.GetValues(headerNameHolder).FirstOrDefault(); + + headerNameHolder = "X-Request-ID"; + requestId = response.Headers.GetValues(headerNameHolder).FirstOrDefault(); + + headerNameHolder = "Openai-Processing-Ms"; + processingTime = TimeSpan.FromMilliseconds(int.Parse(response.Headers.GetValues(headerNameHolder).First())); + + headerNameHolder = "Openai-Version"; + openaiVersion = response.Headers.GetValues(headerNameHolder).FirstOrDefault(); + + headerNameHolder = "Openai-Model"; + modelFromHeaders = response.Headers.GetValues(headerNameHolder).FirstOrDefault(); + } + catch (Exception e) + { + Debug.Print($"Issue parsing metadata of OpenAi Response while trying to get header name: {headerNameHolder}. Url: {url}, Error: {e.ToString()}. This is probably ignorable. Data gotten so far: organization={organization}, requestId={requestId}, processingTime ={processingTime}, openaiVersion={openaiVersion},modelFromHeaders={modelFromHeaders},"); + + } + + string resultAsString = ""; + + using (var stream = await response.Content.ReadAsStreamAsync()) + using (StreamReader reader = new StreamReader(stream)) + { + string line; + while ((line = await reader.ReadLineAsync()) != null) + { + resultAsString += line + Environment.NewLine; + + if (line.StartsWith("data:")) + line = line.Substring("data:".Length); + + line = line.TrimStart(); + + if (line == "[DONE]") + { + yield break; + } + else if (line.StartsWith(":")) + { } + else if (!string.IsNullOrWhiteSpace(line)) + { + var res = JsonConvert.DeserializeObject(line); + + res.Organization = organization; + res.RequestId = requestId; + res.ProcessingTime = processingTime; + res.OpenaiVersion = openaiVersion; + if (string.IsNullOrEmpty(res.Model)) + res.Model = modelFromHeaders; + + yield return res; + } + } + } + } + + internal class ApiErrorResponse + { + /// + /// The error details + /// + [JsonProperty("error")] + public ApiErrorResponseError Error { get; set; } + } + internal class ApiErrorResponseError + { + /// + /// The error message + /// + [JsonProperty("message")] + + public string Message { get; set; } + + /// + /// The type of error + /// + [JsonProperty("type")] + public string ErrorType { get; set; } + + /// + /// The parameter that caused the error + /// + [JsonProperty("param")] + + public string Parameter { get; set; } + + /// + /// The error code + /// + [JsonProperty("code")] + public string ErrorCode { get; set; } + } + } } diff --git a/OpenAI_API/Images/ImageResult.cs b/OpenAI_API/Images/ImageResult.cs index 17e6e87..6817baf 100644 --- a/OpenAI_API/Images/ImageResult.cs +++ b/OpenAI_API/Images/ImageResult.cs @@ -49,7 +49,7 @@ public class Data /// The base64-encoded image data as returned by the API /// [JsonProperty("b64_json")] - public string Base64Data { get; set; } + public string Base64Data { get; set; } /// /// The prompt that was used to generate the image, if there was any revision to the prompt. From eb905171fa0c42d7cc8fe2b3d8b9cbb021e5699b Mon Sep 17 00:00:00 2001 From: Ernie French Date: Fri, 3 May 2024 15:33:54 -0700 Subject: [PATCH 5/5] added vivid. --- OpenAI_API/Images/ImageGenerationRequest.cs | 58 ++++++++++++++++----- OpenAI_API/OpenAI_API.csproj | 8 +++ 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/OpenAI_API/Images/ImageGenerationRequest.cs b/OpenAI_API/Images/ImageGenerationRequest.cs index 98a8277..9483053 100644 --- a/OpenAI_API/Images/ImageGenerationRequest.cs +++ b/OpenAI_API/Images/ImageGenerationRequest.cs @@ -14,6 +14,7 @@ public class ImageGenerationRequest { private int? numOfImages = 1; private ImageSize size = ImageSize._1024; + private string style = "vivid"; private string quality = "standard"; /// @@ -65,10 +66,36 @@ public ImageSize Size set => size = value; } - /// - /// By default, images are generated at `standard` quality, but when using DALL·E 3 you can set quality to `hd` for enhanced detail. Square, standard quality images are the fastest to generate. - /// - [JsonProperty("quality", NullValueHandling=NullValueHandling.Ignore)] + /// + /// Adding this, upstream is missing this value too. What else is there?? revisedText was VITAL yet not added by upstream yet. + /// + [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)] + public string Style + { + get + { + return style; + } + set + { + switch (value.ToLower().Trim()) + { + case "vivid": + style = "vivid"; + break; + case "natural": + style = "natural"; + break; + default: + throw new ArgumentException("Style must be either 'vivid' or 'natural'."); + } + } + } + + /// + /// By default, images are generated at `standard` quality, but when using DALL·E 3 you can set quality to `hd` for enhanced detail. Square, standard quality images are the fastest to generate. + /// + [JsonProperty("quality", NullValueHandling=NullValueHandling.Ignore)] public string Quality { get @@ -109,26 +136,29 @@ public ImageGenerationRequest() } - /// - /// Creates a new with the specified parameters - /// - /// A text description of the desired image(s). The maximum length is 1000 characters. - /// The model to use for this request. Defaults to DALL-E 2. - /// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024. - /// By default, images are generated at `standard` quality, but when using DALL·E 3 you can set quality to `hd` for enhanced detail. - /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - /// The format in which the generated images are returned. Must be one of url or b64_json. - public ImageGenerationRequest( + /// + /// Creates a new with the specified parameters + /// + /// A text description of the desired image(s). The maximum length is 1000 characters. + /// The model to use for this request. Defaults to DALL-E 2. + /// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024. + /// By default, images are generated at `standard` quality, but when using DALL·E 3 you can set quality to `hd` for enhanced detail. + /// natural or vivid, part of the openAI API + /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. + /// The format in which the generated images are returned. Must be one of url or b64_json. + public ImageGenerationRequest( string prompt, Model model, ImageSize size = null, string quality = "standard", + string style = "vivid", string user = null, ImageResponseFormat responseFormat = null) { this.Prompt = prompt; this.Model = model ?? OpenAI_API.Models.Model.DALLE2; this.Quality = quality ?? "standard"; + this.Style = style ?? "vivid"; this.User = user; this.Size = size ?? ImageSize._1024; this.ResponseFormat = responseFormat ?? ImageResponseFormat.Url; diff --git a/OpenAI_API/OpenAI_API.csproj b/OpenAI_API/OpenAI_API.csproj index 75dc2c3..8f2e9df 100644 --- a/OpenAI_API/OpenAI_API.csproj +++ b/OpenAI_API/OpenAI_API.csproj @@ -37,6 +37,14 @@ nuget_logo.png + + 1701;1702;1591 + + + + 1701;1702;1591 + +