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

handle openai internal errors, return header varlues in baseresponse … #454

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
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
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: 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>