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

Commit b7067d2

Browse files
authored
Merge pull request #17 from YarikVor/main
Added FileManager and Moderation
2 parents cce0c6a + aecff97 commit b7067d2

35 files changed

+762
-167
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/FileTest.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System.Diagnostics;
2+
using ManagedCode.OpenAI.Client;
3+
using ManagedCode.OpenAI.Files.Abstractions;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
7+
namespace ManagedCode.OpenAI.Tests;
8+
9+
10+
11+
public class FileTest
12+
{
13+
private readonly ITestOutputHelper _output;
14+
private readonly IGptClient _client = Mocks.Client();
15+
private readonly IFileManager _fileManager;
16+
17+
18+
private const string fileContent =
19+
@"{""prompt"":""This is a test"", ""completion"":""This is a test""}";
20+
21+
22+
public FileTest(ITestOutputHelper output)
23+
{
24+
_output = output;
25+
_fileManager = _client.FileManager();
26+
}
27+
28+
[Fact]
29+
public async Task UploadFile_Success()
30+
{
31+
const string fileName = "test.txt";
32+
33+
34+
var file = await _fileManager.CreateFileAsync(fileContent, fileName);
35+
36+
Log($"File id: {file.Id}");
37+
38+
Assert.False(string.IsNullOrWhiteSpace(file.Id));
39+
Assert.Equal(fileName, file.Filename);
40+
Assert.Equal(fileContent.Length, file.Bytes);
41+
}
42+
43+
44+
// Needs premium account
45+
// [Fact]
46+
public async Task ContentFile_Success()
47+
{
48+
string fileId = await _fileManager.FileListAsync()
49+
.ContinueWith(t => t.Result[0].Id);
50+
51+
var content = await _fileManager.FileContentAsync(fileId);
52+
53+
Log($"File content: {content}");
54+
55+
Assert.Equal(fileContent, content);
56+
}
57+
58+
[Fact]
59+
public async Task FileList_Success()
60+
{
61+
const string fileName = "test.txt";
62+
63+
var newFile = await _fileManager.CreateFileAsync(fileContent, fileName);
64+
Assert.NotNull(newFile);
65+
66+
var files = await _fileManager.FileListAsync();
67+
Assert.NotEmpty(files);
68+
69+
70+
foreach (var file in files)
71+
{
72+
Log($"File: {file.Id} - {file.Filename}");
73+
}
74+
75+
var lastFiles = files.First(e => e.Id == newFile.Id);
76+
77+
Assert.Equal(newFile.Id, lastFiles.Id);
78+
Assert.Equal(newFile.Filename, lastFiles.Filename);
79+
Assert.Equal(newFile.Bytes, lastFiles.Bytes);
80+
}
81+
82+
[Fact]
83+
public async Task DeleteFile_Success()
84+
{
85+
const string fileName = "test.txt";
86+
87+
var newFile = await _fileManager.CreateFileAsync(fileContent, fileName);
88+
Assert.NotNull(newFile);
89+
90+
//Waiting for file to be deleted
91+
Thread.Sleep(5000);
92+
93+
var deleted = await _fileManager.DeleteFileAsync(newFile);
94+
Log(deleted.ToString());
95+
Assert.True(deleted);
96+
97+
var files = await _fileManager.FileListAsync();
98+
Assert.NotEqual(newFile.Id, files.Last().Id);
99+
}
100+
101+
[Fact]
102+
public async Task FileInfo_Success()
103+
{
104+
const string fileName = "test.txt";
105+
106+
var newFile = await _fileManager.CreateFileAsync(fileContent, fileName);
107+
Assert.NotNull(newFile);
108+
109+
var fileInfo = await _fileManager.FileInfoAsync(newFile.Id);
110+
Assert.NotNull(fileInfo);
111+
112+
Assert.Equal(newFile.Id, fileInfo.Id);
113+
Assert.Equal(newFile.Filename, fileInfo.Filename);
114+
Assert.Equal(newFile.Bytes, fileInfo.Bytes);
115+
}
116+
117+
118+
119+
void Log(string log)
120+
{
121+
_output.WriteLine(log);
122+
}
123+
}

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: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using ManagedCode.OpenAI.API.Edit;
22
using ManagedCode.OpenAI.API.Image;
3+
using ManagedCode.OpenAI.API.Moderations;
4+
using ManagedCode.OpenAI.Files.Models;
35

46
namespace ManagedCode.OpenAI.API
57
{
@@ -18,5 +20,27 @@ internal interface IOpenAiWebClient : IDisposable
1820
Task<ImageResponseDto> GenerateImageAsync(GenerateImageRequestDto request);
1921
Task<ImageResponseDto> EditImageAsync(EditImageRequestDto request);
2022
Task<ImageResponseDto> VariationImageAsync(VariationImageRequestDto request);
23+
24+
#region Files
25+
26+
Task<FilesInfoResponseDto> FilesInfoAsync();
27+
Task<FileInfoDto> CreateFileAsync(string content, string fileName, string purpose = "fine-tune");
28+
Task<FileInfoDto> CreateFileAsync(Stream content, string fileName, string purpose = "fine-tune");
29+
Task<FileInfoDto> CreateFileAsync(byte[] content, string fileName, string purpose = "fine-tune");
30+
Task<FileInfoDto> CreateFileAsync(ReadOnlyMemory<byte> content, string fileName, string purpose = "fine-tune");
31+
Task<FileDeleteResponseDto> DeleteFileAsync(string fileId);
32+
Task<FileInfoDto> FileInfoAsync(string fileId);
33+
34+
// TODO: This may be a stream
35+
// I don't know what response type is returned here
36+
Task<string> GetContentFromFileAsync(string fileId);
37+
38+
#endregion
39+
40+
#region Moderations
41+
42+
Task<ModerationResponseDto> ModerationAsync(ModerationRequestDto request);
43+
44+
#endregion
2145
}
22-
}
46+
}

ManagedCode.OpenAI/API/Chat/ChatChoiceDto.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,4 @@ public class ChatChoiceDto
1212

1313
[JsonPropertyName("finish_reason")]
1414
public string FinishReason { get; set; }
15-
}
16-
17-
public class MessageDto
18-
{
19-
[JsonPropertyName("role")]
20-
public string Role { get; set; }
21-
22-
[JsonPropertyName("content")]
23-
public string Content { get; set; }
2415
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace ManagedCode.OpenAI.API;
4+
5+
public class MessageDto
6+
{
7+
[JsonPropertyName("role")]
8+
public string Role { get; set; }
9+
10+
[JsonPropertyName("content")]
11+
public string Content { get; set; }
12+
}

ManagedCode.OpenAI/Files/Models/FileDeleteResult.cs renamed to ManagedCode.OpenAI/API/File/FileDeleteResponseDto.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace ManagedCode.OpenAI.Files.Models;
44

5-
public class FileDeleteResult
5+
internal class FileDeleteResponseDto
66
{
77
[JsonPropertyName("id")]
88
public string Id { get; set; }

ManagedCode.OpenAI/Files/Models/FileInfo.cs renamed to ManagedCode.OpenAI/API/File/FileInfoDto.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace ManagedCode.OpenAI.Files.Models;
44

5-
public class FileInfo
5+
internal class FileInfoDto
66
{
77
[JsonPropertyName("id")]
88
public string Id { get; set; }
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+
using ManagedCode.OpenAI.Files.Abstractions;
3+
4+
namespace ManagedCode.OpenAI.Files.Models;
5+
6+
internal class FilesInfoResponseDto
7+
{
8+
[JsonPropertyName("data")]
9+
public FileInfoDto[] Data { get; set; }
10+
11+
[JsonPropertyName("object")]
12+
public string Object { get; set; }
13+
}
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+
}

0 commit comments

Comments
 (0)