Skip to content

Commit

Permalink
Add OpenRouterClient; Fix models validation issue
Browse files Browse the repository at this point in the history
  • Loading branch information
rodion-m committed Nov 24, 2023
1 parent 4a5cfdb commit c6e5d6d
Show file tree
Hide file tree
Showing 22 changed files with 198 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.2.0</Version>
<Version>3.3.0</Version>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private static ChatService CreateChatService(IServiceProvider provider)
.ValidateOnStart();
services.AddOptions<ChatGPTConfig>()
.BindConfiguration(completionsConfigSectionPath)
.Configure(_ => { }) //optional
.Configure(_ => { }) //make optional
.ValidateDataAnnotations()
.ValidateOnStart();

Expand Down
1 change: 1 addition & 0 deletions src/OpenAI.ChatGpt/ChatService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;
using OpenAI.ChatGpt.Extensions;
using OpenAI.ChatGpt.Interfaces;
using OpenAI.ChatGpt.Internal;
using OpenAI.ChatGpt.Models;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace OpenAI.ChatGpt;
namespace OpenAI.ChatGpt.Extensions;

[Fody.ConfigureAwait(false)]
public static class AsyncEnumerableExtensions
internal static class AsyncEnumerableExtensions
{
internal static async IAsyncEnumerable<T> ConfigureExceptions<T>(
this IAsyncEnumerable<T> stream,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text.Json;
using OpenAI.ChatGpt.Exceptions;

namespace OpenAI.ChatGpt;
namespace OpenAI.ChatGpt.Extensions;

[Fody.ConfigureAwait(false)]
internal static class HttpClientExtensions
Expand Down
11 changes: 10 additions & 1 deletion src/OpenAI.ChatGpt/Models/ChatCompletion/ChatCompletionModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,15 @@ public static string FromString(string model)
return model;
}

// TODO move to IOpenAiClient
[Obsolete("This method will be removed in the next major version. Use DisableModelNameValidation from IOpenAiClient instead.")]
public static void DisableModelNameValidation()
{
Interlocked.CompareExchange(ref _validateModelName, 0, 1);
}

// TODO move to IOpenAiClient
[Obsolete("This method will be removed in the next major version. Use EnableModelNameValidation from IOpenAiClient instead.")]
public static void EnableModelNameValidation()
{
Interlocked.CompareExchange(ref _validateModelName, 1, 0);
Expand All @@ -211,8 +215,13 @@ public static void EnsureMaxTokensIsSupported(string model, int maxTokens)
if (maxTokens < 1) throw new ArgumentOutOfRangeException(nameof(maxTokens));
if (!MaxTokensLimits.TryGetValue(model, out var limit))
{
throw new ArgumentException($"Invalid model: {model}", nameof(model));
if (_validateModelName == 1)
{
throw new ArgumentException($"Invalid model: {model}", nameof(model));
}
return;
}

if (maxTokens > limit)
{
throw new ArgumentOutOfRangeException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ChatCompletionMessage
public string Role { get; init; }

/// <summary>The message text</summary>
[JsonPropertyName("content")]
[JsonPropertyName("content"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Content { get; set; }

private List<ChatCompletionMessage>? _messages;
Expand Down
41 changes: 17 additions & 24 deletions src/OpenAI.ChatGpt/Models/PersistentChatMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,32 @@

namespace OpenAI.ChatGpt.Models;

public class PersistentChatMessage : ChatCompletionMessage
{
public Guid Id { get; init; }
public string UserId { get; set; }
public Guid TopicId { get; set; }
public DateTimeOffset CreatedAt { get; set; }

public PersistentChatMessage(
Guid id,
public class PersistentChatMessage(Guid id,
string userId,
Guid topicId,
DateTimeOffset createdAt,
string role,
string content) : base(role, content)
{
if (role == null) throw new ArgumentNullException(nameof(role));
if (content == null) throw new ArgumentNullException(nameof(content));
Id = id;
UserId = userId ?? throw new ArgumentNullException(nameof(userId));
TopicId = topicId;
CreatedAt = createdAt;
}

string content)
: ChatCompletionMessage(role, content)
{
public Guid Id { get; init; } = id;
public string UserId { get; set; } = userId ?? throw new ArgumentNullException(nameof(userId));
public Guid TopicId { get; set; } = topicId;
public DateTimeOffset CreatedAt { get; set; } = createdAt;

public PersistentChatMessage(
Guid id,
string userId,
Guid topicId,
DateTimeOffset createdAt,
ChatCompletionMessage message) : base(message.Role, message.Content)
ChatCompletionMessage message)
: this(
id,
userId ?? throw new ArgumentNullException(nameof(userId)),
topicId,
createdAt,
message.Role,
message.Content)
{
Id = id;
UserId = userId ?? throw new ArgumentNullException(nameof(userId));
TopicId = topicId;
CreatedAt = createdAt;
}
}
1 change: 1 addition & 0 deletions src/OpenAI.ChatGpt/OpenAiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using OpenAI.ChatGpt.Exceptions;
using OpenAI.ChatGpt.Extensions;
using OpenAI.ChatGpt.Models.ChatCompletion;
using OpenAI.ChatGpt.Models.ChatCompletion.Messaging;

Expand Down
29 changes: 29 additions & 0 deletions src/OpenAI.ChatGpt/OpenRouterClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using OpenAI.ChatGpt.Models.ChatCompletion;

namespace OpenAI.ChatGpt;

/// <summary>
/// OpenRouter.ai client
/// </summary>
/// <remarks>
/// Docs: https://openrouter.ai/docs
/// </remarks>
public class OpenRouterClient : OpenAiClient
{
private const string DefaultHost = "https://openrouter.ai/api/v1/";

public OpenRouterClient(string apiKey, string? host = DefaultHost)
: base(apiKey, host ?? DefaultHost)
{
#pragma warning disable CS0618 // Type or member is obsolete
ChatCompletionModels.DisableModelNameValidation();
#pragma warning restore CS0618 // Type or member is obsolete
}

public OpenRouterClient(HttpClient httpClient) : base(httpClient)
{
#pragma warning disable CS0618 // Type or member is obsolete
ChatCompletionModels.DisableModelNameValidation();
#pragma warning restore CS0618 // Type or member is obsolete
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace OpenAI.ChatGpt.IntegrationTests;

[Collection("OpenAiTestCollection")] //to prevent parallel execution
public class ChatGptEntityFrameworkIntegrationTests
{
[Fact]
Expand Down
1 change: 1 addition & 0 deletions tests/OpenAI.ChatGpt.IntegrationTests/ChatGptTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace OpenAI.ChatGpt.IntegrationTests;

[Collection("OpenAiTestCollection")] //to prevent parallel execution
public class ChatGptTests
{
[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;
using OpenAI.ChatGpt.Modules.Translator;

namespace OpenAI.ChatGpt.IntegrationTests;

public class ChatGptTranslatorServiceTests
[Collection("OpenAiTestCollection")] //to prevent parallel execution
public class ChatGptTranslatorServiceTests : IClassFixture<OpenAiClientFixture>
{
private readonly IOpenAiClient _client = new OpenAiClient(Helpers.GetOpenAiKey());

private readonly IOpenAiClient _client;
private const string GtpModel = ChatCompletionModels.Gpt4Turbo;

public ChatGptTranslatorServiceTests(OpenAiClientFixture fixture)
{
_client = fixture.Client;
}

[Fact]
public async Task Translate_from_English_to_Russian()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
namespace OpenAI.ChatGpt.IntegrationTests.OpenAiClientTests;
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

public class AzureOpenAiClientTests
namespace OpenAI.ChatGpt.IntegrationTests.ClientTests;

public class AzureOpenAiClientTests : IClassFixture<AzureOpenAiClientFixture>
{
private readonly ITestOutputHelper _outputHelper;
private readonly IOpenAiClient _client;

public AzureOpenAiClientTests(ITestOutputHelper outputHelper)
public AzureOpenAiClientTests(ITestOutputHelper outputHelper, AzureOpenAiClientFixture fixture)
{
_outputHelper = outputHelper;
var endpointUrl = Helpers.GetValueFromConfiguration("AZURE_OPENAI_ENDPOINT_URL");
var azureKey = Helpers.GetValueFromConfiguration("AZURE_OPENAI_API_KEY");
var deploymentName = Helpers.GetValueFromConfiguration("AZURE_OPENAI_DEPLOYMENT_NAME");
_client = new AzureOpenAiClient(endpointUrl, deploymentName, azureKey, "2023-12-01-preview");
_client = fixture.Client;
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
namespace OpenAI.ChatGpt.IntegrationTests.OpenAiClientTests;
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

namespace OpenAI.ChatGpt.IntegrationTests.ClientTests;

[Collection("OpenAiTestCollection")] //to prevent parallel execution
public class ChatCompletionsApiTests
public class ChatCompletionsApiTests : IClassFixture<OpenAiClientFixture>
{
private readonly ITestOutputHelper _outputHelper;
private readonly OpenAiClient _client;
private readonly IOpenAiClient _client;

public ChatCompletionsApiTests(ITestOutputHelper outputHelper)
public ChatCompletionsApiTests(ITestOutputHelper outputHelper, OpenAiClientFixture fixture)
{
_outputHelper = outputHelper;
_client = new OpenAiClient(Helpers.GetOpenAiKey());
_client = fixture.Client;
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

namespace OpenAI.ChatGpt.IntegrationTests.ClientTests;

public class ChatCompletionsVisionTests : IClassFixture<OpenAiClientFixture>
{
private readonly ITestOutputHelper _outputHelper;
private readonly IOpenAiClient _client;

public ChatCompletionsVisionTests(ITestOutputHelper outputHelper, OpenAiClientFixture fixture)
{
_outputHelper = outputHelper;
_client = fixture.Client;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

public class AzureOpenAiClientFixture
{
public IOpenAiClient Client { get; }

public AzureOpenAiClientFixture()
{
var endpointUrl = Helpers.GetRequiredValueFromConfiguration("AZURE_OPENAI_ENDPOINT_URL");
var azureKey = Helpers.GetRequiredValueFromConfiguration("AZURE_OPENAI_API_KEY");
var deploymentName = Helpers.GetRequiredValueFromConfiguration("AZURE_OPENAI_DEPLOYMENT_NAME");
Client = new AzureOpenAiClient(endpointUrl, deploymentName, azureKey, "2023-12-01-preview");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

public class OpenAiClientFixture
{
public IOpenAiClient Client { get; private set; }
= new OpenAiClient(Helpers.GetOpenAiKey());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

public class OpenRouterClientFixture
{
public IOpenAiClient Client { get; private set; }
= new OpenRouterClient(Helpers.GetOpenRouterKey());
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
using OpenAI.ChatGpt.Modules.StructuredResponse;
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;
using OpenAI.ChatGpt.Modules.StructuredResponse;

namespace OpenAI.ChatGpt.IntegrationTests.OpenAiClientTests;
namespace OpenAI.ChatGpt.IntegrationTests.ClientTests;

public class OpenAiClientGetStructuredResponseTests
[Collection("OpenAiTestCollection")] //to prevent parallel execution
public class OpenAiClientGetStructuredResponseTests : IClassFixture<OpenAiClientFixture>
{
private readonly OpenAiClient _client = new(Helpers.GetOpenAiKey());
private readonly IOpenAiClient _client;

public OpenAiClientGetStructuredResponseTests(OpenAiClientFixture clientFixture)
{
_client = clientFixture.Client;
}

[Theory]
[InlineData(ChatCompletionModels.Gpt3_5_Turbo)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using OpenAI.ChatGpt.IntegrationTests.ClientTests.Fixtures;

namespace OpenAI.ChatGpt.IntegrationTests.ClientTests;

public class OpenRouterClientTests
{
public class ChatCompletionsApiTests : IClassFixture<OpenRouterClientFixture>
{
private readonly ITestOutputHelper _outputHelper;
private readonly IOpenAiClient _client;

public ChatCompletionsApiTests(ITestOutputHelper outputHelper, OpenRouterClientFixture fixture)
{
_outputHelper = outputHelper;
_client = fixture.Client;
}

[Fact]
public async void Get_response_from_mistral7B_for_one_message()
{
string model = "mistralai/mistral-7b-instruct";
var dialog = Dialog.StartAsUser("Who are you?");
string response = await _client.GetChatCompletions(dialog, 80, model: model);
_outputHelper.WriteLine(response);
response.Should().NotBeNullOrEmpty();
}
}
}
Loading

0 comments on commit c6e5d6d

Please sign in to comment.