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

Betalgo.OpenAI 7.1.4 -- Betalgo.OpenAI.Utilities 7.0.2 #351

Merged
merged 10 commits into from
Aug 6, 2023
12 changes: 7 additions & 5 deletions OpenAI.Playground/OpenAI.Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net6.0;netstandard2.0</TargetFrameworks>
<TargetFramework>net7.0</TargetFramework>
<!--<TargetFrameworks>net7.0;net6.0;netstandard2.0</TargetFrameworks>-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<ImplicitUsings>enable</ImplicitUsings>
Expand All @@ -21,18 +22,19 @@
</Content>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<!--<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<PackageReference Include="LaserCatEyes.HttpClientListener" Version="6.0.0" />
</ItemGroup>
</ItemGroup>-->

<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="LaserCatEyes.HttpClientListener" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net7.0'">
<!--<ItemGroup Condition="'$(TargetFramework)' != 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
Expand All @@ -42,7 +44,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="7.0.1" />
</ItemGroup>
</ItemGroup>-->

<ItemGroup>
<ProjectReference Include="..\OpenAI.SDK\OpenAI.csproj" />
Expand Down
20 changes: 10 additions & 10 deletions OpenAI.Playground/TestHelpers/ChatCompletionTestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ public static async Task RunChatFunctionCallTest(IOpenAIService sdk)

var fn1 = new FunctionDefinitionBuilder("get_current_weather", "Get the current weather")
.AddParameter("location", PropertyDefinition.DefineString("The city and state, e.g. San Francisco, CA"))
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"},"The temperature unit to use. Infer this from the users location."))
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"}, "The temperature unit to use. Infer this from the users location."))
.Validate()
.Build();

var fn2 = new FunctionDefinitionBuilder("get_n_day_weather_forecast", "Get an N-day weather forecast")
.AddParameter("location", new() { Type = "string",Description = "The city and state, e.g. San Francisco, CA"})
.AddParameter("location", new PropertyDefinition {Type = "string", Description = "The city and state, e.g. San Francisco, CA"})
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"}, "The temperature unit to use. Infer this from the users location."))
.AddParameter("num_days", PropertyDefinition.DefineInteger("The number of days to forecast"))
.Validate()
Expand All @@ -130,7 +130,7 @@ public static async Task RunChatFunctionCallTest(IOpenAIService sdk)
ChatMessage.FromSystem("Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."),
ChatMessage.FromUser("Give me a weather report for Chicago, USA, for the next 5 days.")
},
Functions = new List<FunctionDefinition> { fn1, fn2, fn3, fn4 },
Functions = new List<FunctionDefinition> {fn1, fn2, fn3, fn4},
// optionally, to force a specific function:
// FunctionCall = new Dictionary<string, string> { { "name", "get_current_weather" } },
MaxTokens = 50,
Expand Down Expand Up @@ -188,17 +188,17 @@ public static async Task RunChatFunctionCallTestAsStream(IOpenAIService sdk)

var fn1 = new FunctionDefinitionBuilder("get_current_weather", "Get the current weather")
.AddParameter("location", PropertyDefinition.DefineString("The city and state, e.g. San Francisco, CA"))
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"},"The temperature unit to use. Infer this from the users location."))
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"}, "The temperature unit to use. Infer this from the users location."))
.Validate()
.Build();

var fn2 = new FunctionDefinitionBuilder("get_n_day_weather_forecast", "Get an N-day weather forecast")
.AddParameter("location", new PropertyDefinition{ Type = "string",Description = "The city and state, e.g. San Francisco, CA"})
.AddParameter("location", new PropertyDefinition {Type = "string", Description = "The city and state, e.g. San Francisco, CA"})
.AddParameter("format", PropertyDefinition.DefineEnum(new List<string> {"celsius", "fahrenheit"}, "The temperature unit to use. Infer this from the users location."))
.AddParameter("num_days", PropertyDefinition.DefineInteger("The number of days to forecast"))
.Validate()
.Build();

var fn3 = new FunctionDefinitionBuilder("get_current_datetime", "Get the current date and time, e.g. 'Saturday, June 24, 2023 6:14:14 PM'")
.Build();

Expand All @@ -214,14 +214,14 @@ public static async Task RunChatFunctionCallTestAsStream(IOpenAIService sdk)
Messages = new List<ChatMessage>
{
ChatMessage.FromSystem("Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."),

// to test weather forecast functions:
ChatMessage.FromUser("Give me a weather report for Chicago, USA, for the next 5 days."),
ChatMessage.FromUser("Give me a weather report for Chicago, USA, for the next 5 days.")

// or to test array functions, use this instead:
// ChatMessage.FromUser("The combination is: One. Two. Three. Four. Five."),
},
Functions = new List<FunctionDefinition> { fn1, fn2, fn3, fn4 },
Functions = new List<FunctionDefinition> {fn1, fn2, fn3, fn4},
// optionally, to force a specific function:
// FunctionCall = new Dictionary<string, string> { { "name", "get_current_weather" } },
MaxTokens = 50,
Expand All @@ -242,7 +242,7 @@ public static async Task RunChatFunctionCallTestAsStream(IOpenAIService sdk)
Message:
Function call: identify_number_sequence
values: [1, 2, 3, 4, 5]
*/
*/

await foreach (var completionResult in completionResults)
{
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.Playground/TestHelpers/CompletionTestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,4 @@ public static async Task RunSimpleCompletionStreamTestWithCancellationToken(IOpe
throw;
}
}
}
}
31 changes: 26 additions & 5 deletions OpenAI.SDK/Extensions/HttpclientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,38 @@ public static HttpResponseMessage PostAsStreamAsync(this HttpClient client, stri

var content = JsonContent.Create(requestModel, null, settings);

using var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
request.Content = content;
using var request = CreatePostEventStreamRequest(uri, content);

#if NET6_0_OR_GREATER
return client.Send(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
try
{
return client.Send(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}
catch (PlatformNotSupportedException)
{
using var newRequest = CreatePostEventStreamRequest(uri, content);

return SendRequestPreNet6(client, newRequest, cancellationToken);
}
#else
return SendRequestPreNet6(client, request, cancellationToken);
#endif
}

private static HttpResponseMessage SendRequestPreNet6(HttpClient client, HttpRequestMessage request, CancellationToken cancellationToken)
{
var responseTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
var response = responseTask.GetAwaiter().GetResult();
return response;
#endif
}

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.Content = content;

return request;
}

public static async Task<TResponse> PostFileAndReadAsAsync<TResponse>(this HttpClient client, string uri, HttpContent content, CancellationToken cancellationToken = default)
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.SDK/Extensions/OpenAIServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static IHttpClientBuilder AddOpenAIService(this IServiceCollection servic

return services.AddHttpClient<IOpenAIService, OpenAIService>();
}

public static IHttpClientBuilder AddOpenAIService<TServiceInterface>(this IServiceCollection services, string name, Action<OpenAiOptions>? setupAction = null)
where TServiceInterface : class, IOpenAIService
{
Expand Down
30 changes: 18 additions & 12 deletions OpenAI.SDK/Managers/OpenAIChatCompletions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using OpenAI.Extensions;
using System.Runtime.CompilerServices;
using System.Text.Json;
using OpenAI.Extensions;
using OpenAI.Interfaces;
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels.ResponseModels;
using System.Runtime.CompilerServices;
using System.Text.Json;

namespace OpenAI.Managers;

Expand Down Expand Up @@ -82,19 +82,18 @@ public async IAsyncEnumerable<ChatCompletionCreateResponse> CreateCompletionAsSt

/// <summary>
/// This helper class attempts to reassemble a function call response
/// that was split up across several streamed chunks.
/// that was split up across several streamed chunks.
/// Note that this only works for the first message in each response,
/// and ignores the others; if OpenAI ever changes their response format
/// this will need to be adjusted.
/// </summary>
private class ReassemblyContext
{
private FunctionCall? FnCall = null;
private FunctionCall? FnCall;

public bool IsFnAssemblyActive => FnCall != null;



/// <summary>
/// Detects if a response block is a part of a multi-chunk
/// streamed function call response. As long as that's true,
Expand All @@ -105,7 +104,10 @@ private class ReassemblyContext
public void Process(ChatCompletionCreateResponse block)
{
var firstChoice = block.Choices?.FirstOrDefault();
if (firstChoice == null) { return; } // not a valid state? nothing to do
if (firstChoice == null)
{
return;
} // not a valid state? nothing to do

var isStreamingFnCall = IsStreamingFunctionCall();
var justStarted = false;
Expand Down Expand Up @@ -136,12 +138,16 @@ public void Process(ChatCompletionCreateResponse block)
}

// Returns true if we're actively streaming, and also have a partial function call in the response
bool IsStreamingFunctionCall() =>
firstChoice.FinishReason == null && // actively streaming, and
firstChoice.Message?.FunctionCall != null; // have a function call
bool IsStreamingFunctionCall()
{
return firstChoice.FinishReason == null && // actively streaming, and
firstChoice.Message?.FunctionCall != null;
} // have a function call

string ExtractArgsSoFar() =>
block.Choices?.FirstOrDefault()?.Message?.FunctionCall?.Arguments ?? "";
string ExtractArgsSoFar()
{
return block.Choices?.FirstOrDefault()?.Message?.FunctionCall?.Arguments ?? "";
}
}
}
}
1 change: 0 additions & 1 deletion OpenAI.SDK/Managers/OpenAIFile.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Net.Http.Json;
using OpenAI.Extensions;
using OpenAI.Interfaces;
using OpenAI.ObjectModels.ResponseModels;
using OpenAI.ObjectModels.ResponseModels.FileResponseModels;
using OpenAI.ObjectModels.SharedModels;

Expand Down
24 changes: 12 additions & 12 deletions OpenAI.SDK/Managers/OpenAIService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ namespace OpenAI.Managers;
//TODO Find a way to show default request values in documentation
public partial class OpenAIService : IOpenAIService, IDisposable
{
private readonly bool _disposeHttpClient;
private readonly IOpenAiEndpointProvider _endpointProvider;
private readonly HttpClient _httpClient;
private string? _defaultModelId;
private bool _disposeHttpClient;

[ActivatorUtilitiesConstructor]
public OpenAIService(IOptions<OpenAiOptions> settings,HttpClient httpClient)
public OpenAIService(IOptions<OpenAiOptions> settings, HttpClient httpClient)
: this(settings.Value, httpClient)
{
}
Expand Down Expand Up @@ -60,6 +60,15 @@ public OpenAIService(OpenAiOptions settings, HttpClient? httpClient = null)
_defaultModelId = settings.DefaultModelId;
}

/// <summary>
/// Method to dispose the HttpContext if created internally.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}


/// <inheritdoc />
public IModelService Models => this;
Expand Down Expand Up @@ -109,15 +118,6 @@ public void SetDefaultModelId(string modelId)
return _defaultModelId;
}

/// <summary>
/// Method to dispose the HttpContext if created internally.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
Expand All @@ -128,4 +128,4 @@ protected virtual void Dispose(bool disposing)
}
}
}
}
}
23 changes: 14 additions & 9 deletions OpenAI.SDK/ObjectModels/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ public enum Model
CodeCushmanV1,

CodeDavinciV2,

[Obsolete("Use Gpt_3_5_Turbo instead")]
ChatGpt3_5Turbo,
Gpt_3_5_Turbo,

[Obsolete("Use Gpt_3_5_Turbo_0301 instead")]
ChatGpt3_5Turbo0301,
Gpt_3_5_Turbo_0301,
Expand Down Expand Up @@ -133,15 +135,16 @@ public enum Subject
public static string Gpt_4_32k_0314 => "gpt-4-32k-0314";

/// <summary>
/// Snapshot of gpt-4 from June 13th 2023 with function calling data. Unlike gpt-4, this model will not receive updates,
/// and will be deprecated 3 months after a new version is released.
/// Snapshot of gpt-4 from June 13th 2023 with function calling data. Unlike gpt-4, this model will not receive
/// updates,
/// and will be deprecated 3 months after a new version is released.
/// 8,192 tokens Up to Sep 2021
/// </summary>
public static string Gpt_4_0613 => "gpt-4-0613";

/// <summary>
/// Snapshot of gpt-4-32 from June 13th 2023. Unlike gpt-4-32k, this model will not receive updates,
/// and will be deprecated 3 months after a new version is released.
/// Snapshot of gpt-4-32 from June 13th 2023. Unlike gpt-4-32k, this model will not receive updates,
/// and will be deprecated 3 months after a new version is released.
/// 32,768 tokens Up to Sep 2021
/// </summary>
public static string Gpt_4_32k_0613 => "gpt-4-32k-0613";
Expand Down Expand Up @@ -199,6 +202,7 @@ public enum Subject
/// </summary>
[Obsolete("Use Gpt_3_5_Turbo instead, this is just a naming change,the field will be removed in next versions")]
public static string ChatGpt3_5Turbo => "gpt-3.5-turbo";

/// <summary>
/// Most capable GPT-3.5 model and optimized for chat at 1/10th the cost of text-davinci-003. Will be updated with our
/// latest model iteration.
Expand All @@ -207,7 +211,7 @@ public enum Subject
public static string Gpt_3_5_Turbo => "gpt-3.5-turbo";

/// <summary>
/// Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context.
/// Same capabilities as the standard gpt-3.5-turbo model but with 4 times the context.
/// 16,384 tokens Up to Sep 2021
/// </summary>
public static string Gpt_3_5_Turbo_16k => "gpt-3.5-turbo-16k";
Expand All @@ -219,6 +223,7 @@ public enum Subject
/// </summary>
[Obsolete("Use Gpt_3_5_Turbo_0301 instead, this is just a naming change,the field will be removed in next versions")]
public static string ChatGpt3_5Turbo0301 => "gpt-3.5-turbo-0301";

/// <summary>
/// Snapshot of gpt-3.5-turbo from March 1st 2023. Unlike gpt-3.5-turbo, this model will not receive updates, and will
/// only be supported for a three month period ending on June 1st 2023.
Expand All @@ -227,15 +232,15 @@ public enum Subject
public static string Gpt_3_5_Turbo_0301 => "gpt-3.5-turbo-0301";

/// <summary>
/// Snapshot of gpt-3.5-turbo from June 13th 2023 with function calling data. Unlike gpt-3.5-turbo,
/// this model will not receive updates, and will be deprecated 3 months after a new version is released.
/// Snapshot of gpt-3.5-turbo from June 13th 2023 with function calling data. Unlike gpt-3.5-turbo,
/// this model will not receive updates, and will be deprecated 3 months after a new version is released.
/// 4,096 tokens Up to Sep 2021
/// </summary>
public static string Gpt_3_5_Turbo_0613 => "gpt-3.5-turbo-0613";

/// <summary>
/// Snapshot of gpt-3.5-turbo from June 13th 2023 with function calling data. Unlike gpt-3.5-turbo,
/// this model will not receive updates, and will be deprecated 3 months after a new version is released.
/// Snapshot of gpt-3.5-turbo from June 13th 2023 with function calling data. Unlike gpt-3.5-turbo,
/// this model will not receive updates, and will be deprecated 3 months after a new version is released.
/// 4,096 tokens Up to Sep 2021
/// </summary>
public static string Gpt_3_5_Turbo_16k_0613 => "gpt-3.5-turbo-16k-0613";
Expand Down
Loading
Loading