Skip to content
This repository was archived by the owner on Feb 12, 2025. It is now read-only.

Commit aecff97

Browse files
committed
Added Moderation
1 parent 1a2d7a5 commit aecff97

File tree

19 files changed

+345
-3
lines changed

19 files changed

+345
-3
lines changed

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,4 +863,9 @@ FodyWeavers.xsd
863863
### VisualStudio Patch ###
864864
# Additional files built by Visual Studio
865865

866-
# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudio,visualstudiocode,intellij,intellij+all,rider,angular,dotnetcore,aspnetcore,xamarinstudio
866+
# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudio,visualstudiocode,intellij,intellij+all,rider,angular,dotnetcore,aspnetcore,xamarinstudio
867+
868+
869+
870+
# Ignore fields
871+
ManagedCode.OpenAI.Client.Experiments/

ManagedCode.OpenAI.Tests/Mocks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace ManagedCode.OpenAI.Tests
44
{
55
internal static class Mocks
66
{
7-
private const string API_KEY = "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";
7+
private const string API_KEY = "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";
88

99
public static IGptClient Client()
1010
{
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using ManagedCode.OpenAI.Client;
2+
using ManagedCode.OpenAI.Files.Abstractions;
3+
using ManagedCode.OpenAI.Moderations.Abstractions;
4+
using Newtonsoft.Json;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace ManagedCode.OpenAI.Tests;
9+
10+
public class ModerationTests
11+
{
12+
private readonly ITestOutputHelper _output;
13+
private readonly IGptClient _client = Mocks.Client();
14+
private IModerationBuilder ModerationBuilder => _client.ModerationBuilder();
15+
16+
public ModerationTests(ITestOutputHelper output)
17+
{
18+
_output = output;
19+
}
20+
21+
[Fact]
22+
public async Task CreateModeration_Success()
23+
{
24+
var moderation = await ModerationBuilder
25+
.AddInput("I kill you")
26+
.ExecuteAsync();
27+
28+
Assert.NotNull(moderation);
29+
30+
Assert.NotNull(moderation.Categories);
31+
Assert.NotNull(moderation.CateroryScores);
32+
33+
Log("Moderation has next content:");
34+
Log(ToJson(moderation));
35+
}
36+
37+
[Fact]
38+
public async Task CreateMultipleModeration_Success()
39+
{
40+
var moderations = await ModerationBuilder
41+
.AddInput("I kill you")
42+
.AddInput("You are a bad man")
43+
.ExecuteMultipleAsync();
44+
45+
Assert.NotNull(moderations);
46+
47+
Assert.Equal(2, moderations.Length);
48+
49+
Log("Moderations have next content:");
50+
Log(ToJson(moderations));
51+
}
52+
53+
54+
55+
56+
57+
58+
void Log(object obj) => Log(obj.ToString());
59+
60+
void Log(string str) => _output.WriteLine(str);
61+
62+
string ToJson(object obj) => JsonConvert.SerializeObject(obj, Formatting.Indented);
63+
}

ManagedCode.OpenAI/API/Abstractions/IOpenAiWebClient.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using ManagedCode.OpenAI.API.Edit;
22
using ManagedCode.OpenAI.API.Image;
3+
using ManagedCode.OpenAI.API.Moderations;
34
using ManagedCode.OpenAI.Files.Models;
45

56
namespace ManagedCode.OpenAI.API
@@ -35,5 +36,11 @@ internal interface IOpenAiWebClient : IDisposable
3536
Task<string> GetContentFromFileAsync(string fileId);
3637

3738
#endregion
39+
40+
#region Moderations
41+
42+
Task<ModerationResponseDto> ModerationAsync(ModerationRequestDto request);
43+
44+
#endregion
3845
}
3946
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ManagedCode.OpenAI.API.Moderations;
4+
5+
public class CategoryDto<TResult> where TResult : struct
6+
{
7+
[JsonPropertyName("hate")]
8+
public TResult Hate { get; set; }
9+
10+
[JsonPropertyName("hate/threatening")]
11+
public TResult HateThreatening { get; set; }
12+
13+
[JsonPropertyName("self-harm")]
14+
public TResult SelfHarm { get; set; }
15+
16+
[JsonPropertyName("sexual")]
17+
public TResult Sexual { get; set; }
18+
19+
[JsonPropertyName("sexual/minors")]
20+
public TResult SexualMinors { get; set; }
21+
22+
[JsonPropertyName("violence")]
23+
public TResult Violence { get; set; }
24+
25+
[JsonPropertyName("violence/graphic")]
26+
public TResult ViolenceGraphic { get; set; }
27+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ManagedCode.OpenAI.API.Moderations;
4+
5+
public class CategoryResultDto
6+
{
7+
[JsonPropertyName("categories")]
8+
public CategoryDto<bool> Categories { get; set; }
9+
10+
[JsonPropertyName("category_scores")]
11+
public CategoryDto<float> CategoryScores { get; set; }
12+
13+
[JsonPropertyName("flagged")]
14+
public bool Flagged { get; set; }
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ManagedCode.OpenAI.API;
4+
5+
internal class ModerationRequestDto
6+
{
7+
[JsonPropertyName("input")]
8+
public List<string> Input { get; set; } = new List<string>();
9+
10+
[JsonPropertyName("model")]
11+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
12+
public string Model { get; set; }
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ManagedCode.OpenAI.API.Moderations;
4+
5+
public class ModerationResponseDto
6+
{
7+
[JsonPropertyName("id")]
8+
public string Id { get; set; }
9+
10+
[JsonPropertyName("model")]
11+
public string Model { get; set; }
12+
13+
[JsonPropertyName("results")]
14+
public CategoryResultDto[] Results { get; set; }
15+
}

ManagedCode.OpenAI/API/OpenAIWebClient.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
using System.Net.Http.Headers;
22
using ManagedCode.OpenAI.Exceptions;
33
using System.Net.Http.Json;
4+
using System.Text;
45
using System.Text.Json;
56
using ManagedCode.OpenAI.API.Edit;
67
using ManagedCode.OpenAI.API.Image;
8+
using ManagedCode.OpenAI.API.Moderations;
79
using ManagedCode.OpenAI.Files.Models;
10+
using ManagedCode.OpenAI.Moderations.Abstractions;
811

912
namespace ManagedCode.OpenAI.API;
1013

@@ -27,6 +30,8 @@ internal class OpenAiWebClient : IOpenAiWebClient
2730
private const string URL_FILE = "files/{0}";
2831
private const string URL_FILE_CONTEXT = "files/{0}/content";
2932

33+
private const string URL_MODERATION = "moderations";
34+
3035

3136
private readonly HttpClient _httpClient;
3237

@@ -240,4 +245,15 @@ public async Task<string> GetContentFromFileAsync(string fileId)
240245
}
241246

242247
#endregion
248+
249+
#region Moderations
250+
251+
public async Task<ModerationResponseDto> ModerationAsync(ModerationRequestDto request)
252+
{
253+
var httpResponseMessage = await _httpClient.PostAsJsonAsync(URL_MODERATION, request);
254+
255+
return await ReadAsync<ModerationResponseDto>(httpResponseMessage);
256+
}
257+
258+
#endregion
243259
}

ManagedCode.OpenAI/Client/Abstractions/IGptClient.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using ManagedCode.OpenAI.Edit;
44
using ManagedCode.OpenAI.Files.Abstractions;
55
using ManagedCode.OpenAI.Image;
6+
using ManagedCode.OpenAI.Moderations.Abstractions;
67

78
namespace ManagedCode.OpenAI.Client
89
{
@@ -28,6 +29,8 @@ public interface IGptClient
2829
IVariationImageBuilder VariationImage(string imageBase64);
2930

3031
IFileManager FileManager();
32+
33+
IModerationBuilder ModerationBuilder();
3134

3235
}
3336
}

0 commit comments

Comments
 (0)