Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7.4.3 #456

Merged
merged 8 commits into from
Dec 11, 2023
7 changes: 5 additions & 2 deletions OpenAI.Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,16 @@
// |-----------------------------------------------------------------------|

await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk);
//await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk);

// Vision
//await VisionTestHelper.RunSimpleVisionTest(sdk);
//await VisionTestHelper.RunSimpleVisionStreamTest(sdk);
//await VisionTestHelper.RunSimpleVisionTestUsingBase64EncodedImage(sdk);

//await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk);
// Tools
//await ChatCompletionTestHelper.RunChatFunctionCallTest(sdk);
//await ChatCompletionTestHelper.RunChatFunctionCallTestAsStream(sdk);
//await FineTuningJobTestHelper.RunCaseStudyIsTheModelMakingUntrueStatements(sdk);

// Whisper
//await AudioTestHelper.RunSimpleAudioCreateTranscriptionTest(sdk);
Expand All @@ -73,9 +74,11 @@
//await EmbeddingTestHelper.RunSimpleEmbeddingTest(sdk);
//////await FileTestHelper.RunSimpleFileTest(sdk); //will delete all of your files
//////await FineTuningTestHelper.CleanUpAllFineTunings(sdk); //!!!!! will delete all fine-tunings
//await FineTuningJobTestHelper.RunCaseStudyIsTheModelMakingUntrueStatements(sdk);
//await FineTuningTestHelper.RunCaseStudyIsTheModelMakingUntrueStatements(sdk);
//await TokenizerTestHelper.RunTokenizerTest();
//await TokenizerTestHelper.RunTokenizerCountTest();
//await TokenizerTestHelper.RunTokenizerTestCrClean();

Console.WriteLine("Press any key to exit...");
Console.ReadLine();
4 changes: 2 additions & 2 deletions OpenAI.Playground/TestHelpers/ImageTestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public static async Task RunSimpleCreateImageTest(IOpenAIService sdk)
var imageResult = await sdk.Image.CreateImage(new ImageCreateRequest
{
Prompt = "Laser cat eyes",
N = 2,
Size = StaticValues.ImageStatics.Size.Size256,
N = 1,
Size = StaticValues.ImageStatics.Size.Size1024,
ResponseFormat = StaticValues.ImageStatics.ResponseFormat.Url,
User = "TestUser"
});
Expand Down
88 changes: 77 additions & 11 deletions OpenAI.SDK/Extensions/HttpclientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@

namespace OpenAI.Extensions;

public static class HttpClientExtensions
internal static class HttpClientExtensions
{
public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default)
public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default) where TResponse : BaseResponse, new()
{
var response = await client.PostAsJsonAsync(uri, requestModel, new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
}, cancellationToken);
return await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken) ?? throw new InvalidOperationException();
return await HandleResponseContent<TResponse>(response, cancellationToken);
}

public static async Task<TResponse> PostAndReadAsDataAsync<TResponse,TData>(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default) where TResponse : DataBaseResponse<TData>, new()
public static string? GetHeaderValue(this HttpResponseHeaders headers, string headerName)
{
return headers.Contains(headerName) ? headers.GetValues(headerName).FirstOrDefault() : null;
}

public static async Task<TResponse> PostAndReadAsDataAsync<TResponse, TData>(this HttpClient client, string uri, object? requestModel, CancellationToken cancellationToken = default) where TResponse : DataBaseResponse<TData>, new()
{
var response = await client.PostAsJsonAsync(uri, requestModel, new JsonSerializerOptions
{
Expand All @@ -26,7 +31,7 @@ public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClien

if (!response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken) ?? throw new InvalidOperationException();
return await HandleResponseContent<TResponse>(response, cancellationToken);
}

TData data;
Expand All @@ -47,7 +52,7 @@ public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClien
throw new NotSupportedException("Unsupported type for TData");
}

return new TResponse { Data = data };
return new() { Data = data };
}


Expand Down Expand Up @@ -88,16 +93,16 @@ private static HttpResponseMessage SendRequestPreNet6(HttpClient client, HttpReq
private static HttpRequestMessage CreatePostEventStreamRequest(string uri, HttpContent content)
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
request.Headers.Accept.Add(new("text/event-stream"));
request.Content = content;

return request;
}

public static async Task<TResponse> PostFileAndReadAsAsync<TResponse>(this HttpClient client, string uri, HttpContent content, CancellationToken cancellationToken = default)
public static async Task<TResponse> PostFileAndReadAsAsync<TResponse>(this HttpClient client, string uri, HttpContent content, CancellationToken cancellationToken = default) where TResponse : BaseResponse, new()
{
var response = await client.PostAsync(uri, content, cancellationToken);
return await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken) ?? throw new InvalidOperationException();
return await HandleResponseContent<TResponse>(response, cancellationToken);
}

public static async Task<string> PostFileAndReadAsStringAsync(this HttpClient client, string uri, HttpContent content, CancellationToken cancellationToken = default)
Expand All @@ -106,10 +111,71 @@ public static async Task<string> PostFileAndReadAsStringAsync(this HttpClient cl
return await response.Content.ReadAsStringAsync(cancellationToken) ?? throw new InvalidOperationException();
}

public static async Task<TResponse> DeleteAndReadAsAsync<TResponse>(this HttpClient client, string uri, CancellationToken cancellationToken = default)
public static async Task<TResponse> DeleteAndReadAsAsync<TResponse>(this HttpClient client, string uri, CancellationToken cancellationToken = default) where TResponse : BaseResponse, new()
{
var response = await client.DeleteAsync(uri, cancellationToken);
return await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken) ?? throw new InvalidOperationException();
return await HandleResponseContent<TResponse>(response, cancellationToken);
}

private static async Task<TResponse> HandleResponseContent<TResponse>(this HttpResponseMessage response, CancellationToken cancellationToken) where TResponse : BaseResponse, new()
{
TResponse result;

if (!response.Content.Headers.ContentType?.MediaType?.Equals("application/json", StringComparison.OrdinalIgnoreCase) ?? true)
{
result = new()
{
Error = new()
{
MessageObject = await response.Content.ReadAsStringAsync(cancellationToken)
}
};
}
else
{
result = await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken) ??
throw new InvalidOperationException();
}

result.HttpStatusCode = response.StatusCode;
result.HeaderValues = new()
{
Date = response.Headers.Date,
Connection = response.Headers.Connection?.ToString(),
AccessControlAllowOrigin = response.Headers.GetHeaderValue("access-control-allow-origin"),
CacheControl = response.Headers.GetHeaderValue("cache-control"),
Vary = response.Headers.Vary?.ToString(),
XRequestId = response.Headers.GetHeaderValue("x-request-id"),
StrictTransportSecurity = response.Headers.GetHeaderValue("strict-transport-security"),
CFCacheStatus = response.Headers.GetHeaderValue("cf-cache-status"),
SetCookie = response.Headers.Contains("set-cookie") ? response.Headers.GetValues("set-cookie").ToList() : null,
Server = response.Headers.Server?.ToString(),
CF_RAY = response.Headers.GetHeaderValue("cf-ray"),
AltSvc = response.Headers.GetHeaderValue("alt-svc"),
All = response.Headers.ToDictionary(x => x.Key, x => x.Value.AsEnumerable()),

RateLimits = new()
{
LimitRequests = response.Headers.GetHeaderValue("x-ratelimit-limit-requests"),
LimitTokens = response.Headers.GetHeaderValue("x-ratelimit-limit-tokens"),
LimitTokensUsageBased = response.Headers.GetHeaderValue("x-ratelimit-limit-tokens_usage_based"),
RemainingRequests = response.Headers.GetHeaderValue("x-ratelimit-remaining-requests"),
RemainingTokens = response.Headers.GetHeaderValue("x-ratelimit-remaining-tokens"),
RemainingTokensUsageBased = response.Headers.GetHeaderValue("x-ratelimit-remaining-tokens_usage_based"),
ResetRequests = response.Headers.GetHeaderValue("x-ratelimit-reset-requests"),
ResetTokens = response.Headers.GetHeaderValue("x-ratelimit-reset-tokens"),
ResetTokensUsageBased = response.Headers.GetHeaderValue("x-ratelimit-reset-tokens_usage_based")
},

OpenAI = new()
{
Model = response.Headers.GetHeaderValue("openai-model"),
Organization = response.Headers.GetHeaderValue("openai-organization"),
ProcessingMs = response.Headers.GetHeaderValue("openai-processing-ms"),
Version = response.Headers.GetHeaderValue("openai-version")
}
};
return result;
}

#if NETSTANDARD2_0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/// ID of the model to use. One of the available TTS models: tts-1 or tts-1-hd
/// </summary>
[JsonPropertyName("model")]
public string Model { get; set; }

Check warning on line 12 in OpenAI.SDK/ObjectModels/RequestModels/AudioCreateSpeechRequest.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Nullability of reference types in type of parameter 'value' of 'void AudioCreateSpeechRequest.Model.set' doesn't match implicitly implemented member 'void IModel.Model.set' (possibly because of nullability attributes).

/// <summary>
/// The text to generate audio for. The maximum length is 4096 characters.
Expand All @@ -27,7 +27,7 @@
/// The format to audio in. Supported formats are mp3, opus, aac, and flac
/// Defaults to mp3
/// </summary>
[JsonPropertyName("responseFormat")]
[JsonPropertyName("response_format")]
public string? ResponseFormat { get; set; }

/// <summary>
Expand Down
82 changes: 46 additions & 36 deletions OpenAI.SDK/ObjectModels/ResponseModels/BaseResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace OpenAI.ObjectModels.ResponseModels;
Expand All @@ -8,46 +9,55 @@ public record BaseResponse
[JsonPropertyName("object")] public string? ObjectTypeName { get; set; }
public bool Successful => Error == null;
[JsonPropertyName("error")] public Error? Error { get; set; }
public HttpStatusCode HttpStatusCode { get; set; }
public ResponseHeaderValues? HeaderValues { get; set; }
}

public record RateLimitInfo
{
public string? LimitRequests { get; set; }
public string? LimitTokens { get; set; }
public string? LimitTokensUsageBased { get; set; }
public string? RemainingRequests { get; set; }
public string? RemainingTokens { get; set; }
public string? RemainingTokensUsageBased { get; set; }
public string? ResetRequests { get; set; }
public string? ResetTokens { get; set; }
public string? ResetTokensUsageBased { get; set; }
}

public record OpenAIInfo
{
public string? Model { get; set; }
public string? Organization { get; set; }
public string? ProcessingMs { get; set; }
public string? Version { get; set; }
}

public record ResponseHeaderValues
{
public DateTimeOffset? Date { get; set; }
public string? Connection { get; set; }
public string? AccessControlAllowOrigin { get; set; }
public string? CacheControl { get; set; }
public string? Vary { get; set; }
public string? XRequestId { get; set; }
public string? StrictTransportSecurity { get; set; }
public string? CFCacheStatus { get; set; }
public List<string>? SetCookie { get; set; }
public string? Server { get; set; }
public string? CF_RAY { get; set; }
public string? AltSvc { get; set; }
public Dictionary<string, IEnumerable<string>>? All { get; set; }

public RateLimitInfo? RateLimits { get; set; }
public OpenAIInfo? OpenAI { get; set; }
}

public record DataBaseResponse<T> : BaseResponse
{
[JsonPropertyName("data")] public T? Data { get; set; }
}
//public record Error
//{
// [JsonPropertyName("code")] public string? Code { get; set; }

// [JsonPropertyName("message")] public object? MessageRaw { get; set; }
// [JsonIgnore]
// public List<string>? Messages
// {
// get
// {
// if (MessageRaw?.GetType() == typeof(string))
// {
// return new List<string> {MessageRaw.ToString()!};
// }
// return MessageRaw?.GetType() == typeof(List<string>) ? (List<string>) MessageRaw : null;
// }
// }
// [JsonIgnore]
// public string? Message
// {
// get
// {
// if (MessageRaw?.GetType() == typeof(string))
// {
// return MessageRaw.ToString();
// }
// return MessageRaw?.GetType() == typeof(List<string>) ? string.Join(Environment.NewLine,(List<string>) MessageRaw) : null;
// }
// }

// [JsonPropertyName("param")] public string? Param { get; set; }

// [JsonPropertyName("type")] public string? Type { get; set; }
//}

public class Error
{
Expand All @@ -71,7 +81,7 @@ public object MessageObject
{
case string s:
Message = s;
Messages = new List<string?> {s};
Messages = new() { s };
break;
case List<object> list when list.All(i => i is JsonElement):
Messages = list.Cast<JsonElement>().Select(e => e.GetString()).ToList();
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.SDK/OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<PackageIcon>OpenAI-Betalgo.png</PackageIcon>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Title>OpenAI SDK by Betalgo</Title>
<Version>7.4.2</Version>
<Version>7.4.3</Version>
<Authors>Tolga Kayhan, Betalgo</Authors>
<Company>Betalgo Up Ltd.</Company>
<Product>OpenAI ChatGPT, Whisper, GPT-4 and DALL·E dotnet SDK</Product>
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.SDK/OpenAiOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class OpenAiOptions
{
private const string OpenAiDefaultApiVersion = "v1";
private const string OpenAiDefaultBaseDomain = "https://api.openai.com/";
private const string AzureOpenAiDefaultApiVersion = "2023-09-01-preview";
private const string AzureOpenAiDefaultApiVersion = "2023-12-01-preview";


/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions OpenAI.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=logits/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Logprobs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=moderations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=openai/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Probs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ratelimit/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rerank/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
6 changes: 6 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ I will always be using the latest libraries, and future releases will frequently
I am incredibly busy. If I forgot your name, please accept my apologies and let me know so I can add it to the list.

## Changelog
### 7.4.3
- Fixed the response format of AudioCreateSpeechRequest.
- Updated Azure OpenAI version to `2023-12-01-preview`, which now supports dall-e 3.
- Added the ability to retrieve header values from the base response, such as ratelimit, etc. Please note that this feature is experimental and may change in the future.
- Semi-Breaking change:
- The SDK will now attempt to handle 500 errors and other similar errors from the OpenAI server. Previously, an exception was thrown in such cases. Now, the SDK will try to read the response and return it as an error message. This change provides more visibility to developers and helps them understand the cause of the error.
### 7.4.2
- Let's start with breaking changes:
- OpenAI has replaced function calling with tools. We have made the necessary changes to our code. This is not a major change; now you just have a wrapper around your function calling, which is named as "tool". The Playground provides an example. Please take a look to see how you can update your code.
Expand Down
Loading