Skip to content

Commit

Permalink
overload method of UploadMedia to support stream types #38
Browse files Browse the repository at this point in the history
  • Loading branch information
jochenkirstaetter committed Oct 13, 2024
1 parent 105a55d commit 4716f4e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
49 changes: 48 additions & 1 deletion src/Mscc.GenerativeAI/GenerativeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ public async Task<string> TransferOwnership(string model, string emailAddress)
/// Uploads a file to the File API backend.
/// </summary>
/// <param name="uri">URI or path to the file to upload.</param>
/// <param name="displayName">A name displazed for the uploaded file.</param>
/// <param name="displayName">A name displayed for the uploaded file.</param>
/// <param name="resumable">Flag indicating whether to use resumable upload.</param>
/// <param name="cancellationToken">A cancellation token to cancel the upload.</param>
/// <returns>A URI of the uploaded file.</returns>
Expand Down Expand Up @@ -557,6 +557,53 @@ public async Task<UploadMediaResponse> UploadFile(string uri,
}
}

public async Task<UploadMediaResponse> UploadFile(Stream stream,
string displayName,
string mimeType,
bool resumable = false,
CancellationToken cancellationToken = default)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (stream.Length > Constants.MaxUploadFileSize) throw new MaxUploadFileSizeException(nameof(stream));
if (string.IsNullOrEmpty(mimeType)) throw new ArgumentException(nameof(mimeType));
if (string.IsNullOrEmpty(displayName)) throw new ArgumentException(nameof(displayName));

var totalBytes = stream.Length;
var request = new UploadMediaRequest()
{
File = new FileRequest()
{
DisplayName = displayName
}
};

var url = $"{EndpointGoogleAi}/upload/{Version}/files"; // v1beta3 // ?key={apiKey}
if (resumable)
{
url = $"{EndpointGoogleAi}/resumable/upload/{Version}/files"; // v1beta3 // ?key={apiKey}
}
url = ParseUrl(url).AddQueryString(new Dictionary<string, string?>()
{
["alt"] = "json",
["uploadType"] = "multipart"
});
string json = Serialize(request);

var multipartContent = new MultipartContent("related");
multipartContent.Add(new StringContent(json, Encoding.UTF8, Constants.MediaType));
multipartContent.Add(new StreamContent(stream, (int)Constants.ChunkSize)
{
Headers = {
ContentType = new MediaTypeHeaderValue(mimeType),
ContentLength = totalBytes
}
});

var response = await Client.PostAsync(url, multipartContent, cancellationToken);
await response.EnsureSuccessAsync();
return await Deserialize<UploadMediaResponse>(response);
}

/// <summary>
/// Lists the metadata for Files owned by the requesting project.
/// </summary>
Expand Down
38 changes: 38 additions & 0 deletions tests/Mscc.GenerativeAI/GoogleAi_Gemini15Pro_Should.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,44 @@ public async void Upload_File_WithResume_Using_FileAPI()
File.Delete(filePath);
}

[Theory]
[InlineData("scones.jpg", "Set of blueberry scones", "image/jpeg")]
[InlineData("cat.jpg", "Wildcat on snow", "image/jpeg")]
[InlineData("cat.jpg", "Cat in the snow", "image/jpeg")]
[InlineData("image.jpg", "Sample drawing", "image/jpeg")]
[InlineData("animals.mp4", "Zootopia in da house", "video/mp4")]
[InlineData("sample.mp3", "State_of_the_Union_Address_30_January_1961", "audio/mp3")]
[InlineData("pixel.mp3", "Pixel Feature Drops: March 2023", "audio/mp3")]
[InlineData("gemini.pdf",
"Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context", "application/pdf")]
public async void Upload_Stream_Using_FileAPI(string filename, string displayName, string mimeType)
{
// Arrange
var filePath = Path.Combine(Environment.CurrentDirectory, "payload", filename);
IGenerativeAI genAi = new GoogleAI(_fixture.ApiKey);
var model = genAi.GenerativeModel(_model);

// Act
using (var fs = new FileStream(filePath, FileMode.Open))
{
var response = await model.UploadFile(fs, displayName, mimeType);

// Assert
response.Should().NotBeNull();
response.File.Should().NotBeNull();
response.File.Name.Should().NotBeNull();
response.File.DisplayName.Should().Be(displayName);
// response.File.MimeType.Should().Be("image/jpeg");
// response.File.CreateTime.Should().BeGreaterThan(DateTime.Now.Add(TimeSpan.FromHours(48)));
// response.File.ExpirationTime.Should().NotBeNull();
// response.File.UpdateTime.Should().NotBeNull();
response.File.SizeBytes.Should().BeGreaterThan(0);
response.File.Sha256Hash.Should().NotBeNull();
response.File.Uri.Should().NotBeNull();
_output.WriteLine($"Uploaded file '{response?.File.DisplayName}' as: {response?.File.Uri}");
}
}

[Fact]
public async void List_Files()
{
Expand Down

0 comments on commit 4716f4e

Please sign in to comment.