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

Commit 5572572

Browse files
committed
Added ORGANIZATION constructor parameter for OpenAiWebClient
Added FileClient & ImageClient as parameters for GptClient ModerationBuilder modified to include required parameters
1 parent 364ed99 commit 5572572

File tree

12 files changed

+143
-111
lines changed

12 files changed

+143
-111
lines changed

ManagedCode.OpenAI.Tests/FileTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class FileTest
2222
public FileTest(ITestOutputHelper output)
2323
{
2424
_output = output;
25-
_fileClient = _client.FileManager();
25+
_fileClient = _client.FileClient;
2626
}
2727

2828
[Fact]

ManagedCode.OpenAI.Tests/ImageTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public ImageTests(ITestOutputHelper output)
2323
[Fact]
2424
public async Task GenerateImage_Success()
2525
{
26-
var image = await _client.GenerateImage("Red dragon")
26+
var image = await _client.ImageClient.GenerateImage("Red dragon")
2727
.SetImageResolution(ImageResolution._512x512).GenerateAsync();
2828

2929
Log($"Image url: {image.Content}");
@@ -34,7 +34,7 @@ public async Task GenerateImage_Success()
3434
public async Task EditImage_Success()
3535
{
3636
var edited = await _client
37-
.EditImage("change color to blue",
37+
.ImageClient.EditImage("change color to blue",
3838
x=> x.FromBytes(Properties.Resources.Dog))
3939
.AsUrl()
4040
.EditAsync();

ManagedCode.OpenAI.Tests/ModerationTests.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class ModerationTests
1010
{
1111
private readonly ITestOutputHelper _output;
1212
private readonly IGptClient _client = Mocks.Client();
13-
private IModerationBuilder ModerationBuilder => _client.ModerationBuilder();
13+
private IModerationBuilder ModerationBuilder => _client.Moderation();
1414

1515
public ModerationTests(ITestOutputHelper output)
1616
{
@@ -21,8 +21,7 @@ public ModerationTests(ITestOutputHelper output)
2121
public async Task CreateModeration_Success()
2222
{
2323
var moderation = await ModerationBuilder
24-
.AddInput("I kill you")
25-
.ExecuteAsync();
24+
.ExecuteAsync("I kill you");
2625

2726
Assert.NotNull(moderation);
2827

@@ -36,17 +35,15 @@ public async Task CreateModeration_Success()
3635
[Fact]
3736
public async Task CreateMultipleModeration_Success()
3837
{
39-
var moderations = await ModerationBuilder
40-
.AddInput("I kill you")
41-
.AddInput("You are a bad man")
42-
.ExecuteMultipleAsync();
38+
var moderation = await ModerationBuilder
39+
.ExecuteMultipleAsync("I kill you", "You are a bad man");
4340

44-
Assert.NotNull(moderations);
41+
Assert.NotNull(moderation);
4542

46-
Assert.Equal(2, moderations.Length);
43+
Assert.Equal(2, moderation.Length);
4744

4845
Log("Moderations have next content:");
49-
Log(ToJson(moderations));
46+
Log(ToJson(moderation));
5047
}
5148

5249

ManagedCode.OpenAI/API/OpenAIWebClient.cs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ internal class OpenAiWebClient : IOpenAiWebClient
2424
private const string URL_IMAGE_GENERATION = "images/generations";
2525
private const string URL_IMAGE_EDIT = "images/edits";
2626
private const string URL_IMAGE_VARIATION = "images/variations";
27-
27+
2828
private const string URL_FILES = "files";
2929
private const string URL_FILE = "files/{0}";
3030
private const string URL_FILE_CONTEXT = "files/{0}/content";
3131

3232
private const string URL_MODERATION = "moderations";
33-
33+
3434

3535
private readonly HttpClient _httpClient;
3636

@@ -43,6 +43,16 @@ public OpenAiWebClient(string apiKey)
4343
_httpClient.BaseAddress = new Uri(URL_BASE);
4444
}
4545

46+
public OpenAiWebClient(string apiKey, string organization)
47+
{
48+
_httpClient = new HttpClient();
49+
_httpClient.DefaultRequestHeaders.Add(AUTHORIZATION,
50+
string.Format(AUTHORIZATION_FORMAT, apiKey));
51+
52+
_httpClient.DefaultRequestHeaders.Add(ORGANIZATION, organization);
53+
_httpClient.BaseAddress = new Uri(URL_BASE);
54+
}
55+
4656

4757
public async Task<ModelsResponseDto> ModelsAsync()
4858
{
@@ -100,7 +110,6 @@ public async Task<ImageResponseDto> EditImageAsync(EditImageRequestDto request)
100110

101111
form.Add(new StringContent(request.Description), "prompt");
102112

103-
104113
var response = await _httpClient.PostAsync(URL_IMAGE_EDIT, form);
105114
return await ReadAsync<ImageResponseDto>(response);
106115
}
@@ -121,7 +130,6 @@ public async Task<ImageResponseDto> VariationImageAsync(VariationImageRequestDto
121130
return await ReadAsync<ImageResponseDto>(response);
122131
}
123132

124-
125133
public void Dispose()
126134
{
127135
_httpClient.Dispose();
@@ -152,24 +160,20 @@ private Dictionary<StringContent, string> ToImageRequestParameters(BaseImageRequ
152160
if (!string.IsNullOrWhiteSpace(request.Size))
153161
result.Add(new StringContent(request.Size), "size");
154162

155-
156163
if (!string.IsNullOrWhiteSpace(request.ResponseFormat))
157164
result.Add(new StringContent(request.ResponseFormat), "response_format");
158165

159-
160166
if (!string.IsNullOrWhiteSpace(request.User))
161167
result.Add(new StringContent(request.User), "user");
162168

163-
//todo implement 'N' parameter
164-
//if (request.N.HasValue)
165-
// result.Add();
166-
169+
if (request.N.HasValue)
170+
result.Add(new StringContent(request.N.Value.ToString()), "m");
167171
return result;
168172
}
169173

170174
#region Files
171175

172-
176+
173177
public async Task<FilesInfoResponseDto> FilesInfoAsync()
174178
{
175179
var httpResponseMessage = await _httpClient.GetAsync(URL_FILES);
@@ -181,13 +185,13 @@ private async Task<FileInfoDto> CreateFileAsync(HttpContent content, string file
181185
MultipartFormDataContent multipartFormDataContent = new();
182186
multipartFormDataContent.Add(new StringContent(purpose), "purpose");
183187
multipartFormDataContent.Add(content, "file", fileName);
184-
188+
185189
var httpResponseMessage = await _httpClient.PostAsync(URL_FILES, multipartFormDataContent);
186190

187191
return await ReadAsync<FileInfoDto>(httpResponseMessage);
188192
}
189193

190-
public async Task<FileInfoDto> CreateFileAsync(Stream content, string fileName, string purpose = "fine-tune")
194+
public async Task<FileInfoDto> CreateFileAsync(Stream content, string fileName, string purpose = "fine-tune")
191195
{
192196
StreamContent streamContent = new(content);
193197
return await CreateFileAsync(streamContent, fileName, purpose);
@@ -198,27 +202,27 @@ public async Task<FileInfoDto> CreateFileAsync(string content, string fileName,
198202
StringContent stringContent = new(content);
199203
return await CreateFileAsync(stringContent, fileName, purpose);
200204
}
201-
205+
202206
public async Task<FileInfoDto> CreateFileAsync(byte[] content, string fileName, string purpose = "fine-tune")
203207
{
204208
ByteArrayContent byteArrayContent = new(content);
205209
return await CreateFileAsync(byteArrayContent, fileName, purpose);
206210
}
207-
208-
public async Task<FileInfoDto> CreateFileAsync(ReadOnlyMemory<byte> content, string fileName, string purpose =
211+
212+
public async Task<FileInfoDto> CreateFileAsync(ReadOnlyMemory<byte> content, string fileName, string purpose =
209213
"fine-tune")
210214
{
211215
ReadOnlyMemoryContent readOnlyMemoryContent = new(content);
212216
return await CreateFileAsync(readOnlyMemoryContent, fileName, purpose);
213217
}
214-
215-
218+
219+
216220

217221
public async Task<FileDeleteResponseDto> DeleteFileAsync(string fileId)
218222
{
219223
string resultUrl = string.Format(URL_FILE, fileId);
220224
var httpResponseMessage = await _httpClient.DeleteAsync(resultUrl);
221-
225+
222226
return await ReadAsync<FileDeleteResponseDto>(httpResponseMessage);
223227
}
224228

@@ -232,21 +236,21 @@ public async Task<FileInfoDto> FileInfoAsync(string fileId)
232236
}
233237

234238

235-
239+
236240
// TODO: It is not known what the result of the query returns
237241
public async Task<string> GetContentFromFileAsync(string fileId)
238242
{
239243
string resultUrl = string.Format(URL_FILE_CONTEXT, fileId);
240244
var httpResponseMessage = await _httpClient.GetAsync(resultUrl);
241-
245+
242246
OpenAIExceptions.ThrowsIfError(httpResponseMessage.StatusCode);
243247
return await httpResponseMessage.Content.ReadAsStringAsync();
244248
}
245249

246250
#endregion
247-
251+
248252
#region Moderations
249-
253+
250254
public async Task<ModerationResponseDto> ModerationAsync(ModerationRequestDto request)
251255
{
252256
var httpResponseMessage = await _httpClient.PostAsJsonAsync(URL_MODERATION, request);

ManagedCode.OpenAI/Client/Abstractions/IGptClient.cs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,19 @@ namespace ManagedCode.OpenAI.Client
1111
public interface IGptClient
1212
{
1313
public IGptClientConfiguration Configuration { get; }
14+
15+
public IImageClient ImageClient { get; }
16+
public IFileClient FileClient { get; }
17+
1418
void Configure(IGptClientConfiguration configuration);
1519
void Configure(Func<IGptClientConfigurationBuilder, IGptClientConfiguration> configuration);
1620

1721
public Task<IModel[]> GetModelsAsync();
1822
public Task<IModel> GetModelAsync(string modelId);
1923

2024
IGptChat OpenChat(IChatMessageParameters defaultMessageParameters, IChatSession session);
21-
2225
ICompletionBuilder Completion();
2326
IEditBuilder Edit(string input, string instruction);
24-
25-
IGenerateImageBuilder GenerateImage(string description);
26-
27-
IEditImageBuilder EditImage(string description, string imageBase64);
28-
29-
IVariationImageBuilder VariationImage(string imageBase64);
30-
31-
IFileClient FileManager();
32-
33-
IModerationBuilder ModerationBuilder();
34-
27+
IModerationBuilder Moderation();
3528
}
3629
}

ManagedCode.OpenAI/Client/GptClient.cs

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,46 @@ namespace ManagedCode.OpenAI.Client
1010
{
1111
public class GptClient : IGptClient
1212
{
13-
private readonly IOpenAiWebClient _webClient;
13+
private IOpenAiWebClient _webClient = null!;
1414

1515
public GptClient(string apiKey)
1616
{
17-
_webClient = new OpenAiWebClient(apiKey);
18-
Configuration = new DefaultGptClientConfiguration();
17+
Init(apiKey, default, new DefaultGptClientConfiguration());
1918
}
2019

2120
public GptClient(string apiKey, IGptClientConfiguration configuration)
2221
{
23-
_webClient = new OpenAiWebClient(apiKey);
22+
Init(apiKey, default, configuration);
23+
}
24+
25+
public GptClient(string apiKey, string organization)
26+
{
27+
Init(apiKey, organization, new DefaultGptClientConfiguration());
28+
}
29+
30+
public GptClient(string apiKey, string organization, IGptClientConfiguration configuration)
31+
{
32+
Init(apiKey, organization, configuration);
33+
}
34+
35+
private GptClient(){}
36+
37+
private void Init(string apiKey, string? organization, IGptClientConfiguration configuration)
38+
{
39+
var webClient = string.IsNullOrWhiteSpace(organization)
40+
? new OpenAiWebClient(apiKey)
41+
: new OpenAiWebClient(apiKey, organization);
42+
43+
_webClient = webClient;
2444
Configuration = configuration;
45+
ImageClient = new ImageClient(_webClient);
46+
FileClient = new FileClient(_webClient);
2547
}
2648

27-
public IGptClientConfiguration Configuration { get; private set; }
49+
50+
public IGptClientConfiguration Configuration { get; private set; } = null!;
51+
public IImageClient ImageClient { get; private set; } = null!;
52+
public IFileClient FileClient { get; private set; } = null!;
2853

2954
public void Configure(IGptClientConfiguration configuration)
3055
{
@@ -49,7 +74,6 @@ public async Task<IModel> GetModelAsync(string modelId)
4974
return model.ToModel();
5075
}
5176

52-
5377
public IGptChat OpenChat(IChatMessageParameters defaultMessageParameters, IChatSession session)
5478
{
5579
return new GptChat(_webClient, session, defaultMessageParameters);
@@ -65,27 +89,7 @@ public IEditBuilder Edit(string input, string instruction)
6589
return new EditBuilder(_webClient, Configuration.ModelId, input, instruction);
6690
}
6791

68-
public IGenerateImageBuilder GenerateImage(string description)
69-
{
70-
return new GenerateImageBuilder(_webClient, description);
71-
}
72-
73-
public IEditImageBuilder EditImage(string description, string imageBase64)
74-
{
75-
return new EditImageBuilder(_webClient, description, imageBase64);
76-
}
77-
78-
public IVariationImageBuilder VariationImage(string imageBase64)
79-
{
80-
return new VariationImageBuilder(_webClient, imageBase64);
81-
}
82-
83-
public IFileClient FileManager()
84-
{
85-
return new FileClient(_webClient);
86-
}
87-
88-
public IModerationBuilder ModerationBuilder()
92+
public IModerationBuilder Moderation()
8993
{
9094
return new ModerationBuilder(_webClient);
9195
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace ManagedCode.OpenAI.Image
2+
{
3+
public interface IImageClient
4+
{
5+
public IGenerateImageBuilder GenerateImage(string description);
6+
7+
public IEditImageBuilder EditImage(string description, string imageBase64);
8+
9+
public IVariationImageBuilder VariationImage(string imageBase64);
10+
11+
}
12+
}

ManagedCode.OpenAI/Image/Extensions/ImageClientEx.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ namespace ManagedCode.OpenAI.Image
44
{
55
public static class ImageClientEx
66
{
7-
public static IEditImageBuilder EditImage(this IGptClient client,
7+
public static IEditImageBuilder EditImage(this IImageClient client,
88
string instruction, Func<IImageLoader, string> image)
99
{
1010
var loader = new DefaultImageLoader();
1111
return client.EditImage(instruction, image.Invoke(loader));
1212
}
1313

14-
public static IVariationImageBuilder VariationImage(this IGptClient client,
14+
public static IVariationImageBuilder VariationImage(this IImageClient client,
1515
Func<IImageLoader, string> image)
1616
{
1717
var loader = new DefaultImageLoader();

0 commit comments

Comments
 (0)