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/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/Images/ImageResult.cs b/OpenAI_API/Images/ImageResult.cs
index 5c9770e..6817baf 100644
--- a/OpenAI_API/Images/ImageResult.cs
+++ b/OpenAI_API/Images/ImageResult.cs
@@ -54,6 +54,7 @@ public class Data
///
/// 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; }
}
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
+
+