Skip to content

Commit da12c8d

Browse files
committed
[Firebase AI] Add the test json files
1 parent 75ebdd4 commit da12c8d

31 files changed

+737
-14
lines changed

firebaseai/testapp/Assets/Firebase/Sample/FirebaseAI/UIHandlerAutomated.cs

Lines changed: 146 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace Firebase.Sample.FirebaseAI {
2727
using System.Threading.Tasks;
2828
using Google.MiniJSON;
2929
using UnityEngine;
30-
using UnityEngine.Video;
30+
using UnityEngine.Networking;
3131
using System.IO;
3232
#if INCLUDE_FIREBASE_AUTH
3333
using Firebase.Auth;
@@ -70,6 +70,8 @@ protected override void Start() {
7070
TestCountTokens,
7171
TestYoutubeLink,
7272
TestGenerateImage,
73+
TestImagenGenerateImage,
74+
TestImagenGenerateImageOptions
7375
};
7476
// Set of tests that only run the single time.
7577
Func<Task>[] singleTests = {
@@ -86,6 +88,9 @@ protected override void Start() {
8688
InternalTestBasicResponseLongUsageMetadata,
8789
InternalTestGoogleAIBasicReplyShort,
8890
InternalTestGoogleAICitations,
91+
InternalTestGenerateImagesBase64,
92+
InternalTestGenerateImagesAllFiltered,
93+
InternalTestGenerateImagesBase64SomeFiltered,
8994
};
9095

9196
// Create the set of tests, combining the above lists.
@@ -443,7 +448,7 @@ async Task TestEnumSchemaResponse(Backend backend) {
443448
generationConfig: new GenerationConfig(
444449
responseMimeType: "text/x.enum",
445450
responseSchema: Schema.Enum(new string[] { enumValue })));
446-
451+
447452
var response = await model.GenerateContentAsync(
448453
"Hello, I am testing setting the response schema to an enum.");
449454

@@ -459,7 +464,7 @@ async Task TestAnyOfSchemaResponse(Backend backend) {
459464
Schema.AnyOf(new[] { Schema.Int(), Schema.String() }),
460465
minItems: 2,
461466
maxItems: 6)));
462-
467+
463468
var response = await model.GenerateContentAsync(
464469
"Hello, I am testing setting the response schema with an array, cause you give me some random values.");
465470

@@ -658,7 +663,7 @@ async Task TestYoutubeLink(Backend backend) {
658663
async Task TestGenerateImage(Backend backend) {
659664
var model = GetFirebaseAI(backend).GetGenerativeModel("gemini-2.0-flash-exp",
660665
generationConfig: new GenerationConfig(
661-
responseModalities: new [] { ResponseModality.Text, ResponseModality.Image })
666+
responseModalities: new[] { ResponseModality.Text, ResponseModality.Image })
662667
);
663668

664669
GenerateContentResponse response = await model.GenerateContentAsync(
@@ -683,6 +688,58 @@ async Task TestGenerateImage(Backend backend) {
683688
Assert($"Missing expected modalities. Text: {foundText}, Image: {foundImage}", foundText && foundImage);
684689
}
685690

691+
async Task TestImagenGenerateImage(Backend backend) {
692+
var model = GetFirebaseAI(backend).GetImagenModel("imagen-3.0-generate-002");
693+
694+
var response = await model.GenerateImagesAsync(
695+
"Generate an image of a cartoon dog.");
696+
697+
// We can't easily test if the image is correct, but can check other random data.
698+
AssertEq("FilteredReason", response.FilteredReason, null);
699+
AssertEq("Image Count", response.Images.Count, 1);
700+
701+
AssertEq($"Image MimeType", response.Images[0].MimeType, "image/png");
702+
703+
var texture = response.Images[0].AsTexture2D();
704+
Assert($"Image as Texture2D", texture != null);
705+
// By default the image should be Square 1x1, so check for that.
706+
Assert($"Image Height > 0", texture.height > 0);
707+
AssertEq($"Image Height = Width", texture.height, texture.width);
708+
}
709+
710+
async Task TestImagenGenerateImageOptions(Backend backend) {
711+
var model = GetFirebaseAI(backend).GetImagenModel(
712+
modelName: "imagen-3.0-generate-002",
713+
generationConfig: new ImagenGenerationConfig(
714+
// negativePrompt and addWatermark are not supported on this version of the model.
715+
numberOfImages: 2,
716+
aspectRatio: ImagenAspectRatio.Landscape4x3,
717+
imageFormat: ImagenImageFormat.Jpeg(50)
718+
),
719+
safetySettings: new ImagenSafetySettings(
720+
safetyFilterLevel: ImagenSafetySettings.SafetyFilterLevel.BlockLowAndAbove,
721+
personFilterLevel: ImagenSafetySettings.PersonFilterLevel.BlockAll),
722+
requestOptions: new RequestOptions(timeout: TimeSpan.FromMinutes(1)));
723+
724+
var response = await model.GenerateImagesAsync(
725+
"Generate an image of a cartoon dog.");
726+
727+
// We can't easily test if the image is correct, but can check other random data.
728+
AssertEq("FilteredReason", response.FilteredReason, null);
729+
AssertEq("Image Count", response.Images.Count, 2);
730+
731+
for (int i = 0; i < 2; i++) {
732+
AssertEq($"Image {i} MimeType", response.Images[i].MimeType, "image/jpeg");
733+
734+
var texture = response.Images[i].AsTexture2D();
735+
Assert($"Image {i} as Texture2D", texture != null);
736+
// By default the image should be Landscape 4x3, so check for that.
737+
Assert($"Image {i} Height > 0", texture.height > 0);
738+
Assert($"Image {i} Height < Width {texture.height} < {texture.width}",
739+
texture.height < texture.width);
740+
}
741+
}
742+
686743
// Test providing a file from a GCS bucket (Firebase Storage) to the model.
687744
async Task TestReadFile() {
688745
// GCS is currently only supported with VertexAI.
@@ -737,17 +794,43 @@ async Task TestReadSecureFile() {
737794

738795
// The url prefix to use when fetching test data to use from the separate GitHub repo.
739796
readonly string testDataUrl =
740-
"https://raw.githubusercontent.com/FirebaseExtended/vertexai-sdk-test-data/3737ae1fe9c5ecbd55abdeabc273ef4f392cbf19/mock-responses/";
797+
"https://raw.githubusercontent.com/FirebaseExtended/vertexai-sdk-test-data/47becf9101d11ea3c568bf60b12f1c8ed9fb684e/mock-responses/";
741798
readonly HttpClient httpClient = new();
742799

800+
private Task<string> LoadStreamingAsset(string fullPath) {
801+
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
802+
UnityWebRequest request = UnityWebRequest.Get(fullPath);
803+
request.SendWebRequest().completed += (_) => {
804+
if (request.result == UnityWebRequest.Result.Success) {
805+
tcs.SetResult(request.downloadHandler.text);
806+
} else {
807+
tcs.SetResult(null);
808+
}
809+
};
810+
return tcs.Task;
811+
}
812+
743813
// Gets the Json test data from the given filename, potentially downloading from a GitHub repo.
744814
private async Task<Dictionary<string, object>> GetJsonTestData(string filename) {
745-
// TODO: Check if the file is available locally first
815+
string jsonString = null;
816+
// First, try to load the file from StreamingAssets
817+
string localPath = Path.Combine(Application.streamingAssetsPath, "TestData", filename);
818+
if (localPath.StartsWith("jar") || localPath.StartsWith("http")) {
819+
// Special case to access StreamingAsset content on Android
820+
jsonString = await LoadStreamingAsset(localPath);
821+
} else if (File.Exists(localPath)) {
822+
jsonString = File.ReadAllText(localPath);
823+
}
746824

747-
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, testDataUrl + filename));
748-
response.EnsureSuccessStatusCode();
749-
750-
string jsonString = await response.Content.ReadAsStringAsync();
825+
if (string.IsNullOrEmpty(jsonString)) {
826+
DebugLog("Failed to load from local file, downloading...");
827+
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, testDataUrl + filename));
828+
response.EnsureSuccessStatusCode();
829+
830+
jsonString = await response.Content.ReadAsStringAsync();
831+
} else {
832+
DebugLog("Using local file");
833+
}
751834

752835
return Json.Deserialize(jsonString) as Dictionary<string, object>;
753836
}
@@ -862,7 +945,7 @@ async Task InternalTestBasicReplyShort() {
862945
async Task InternalTestCitations() {
863946
Dictionary<string, object> json = await GetVertexJsonTestData("unary-success-citations.json");
864947
GenerateContentResponse response = GenerateContentResponse.FromJson(json, FirebaseAI.Backend.InternalProvider.VertexAI);
865-
948+
866949
ValidateTextPart(response, "Some information cited from an external source");
867950

868951
CitationMetadata? metadata = response.Candidates.First().CitationMetadata;
@@ -1050,7 +1133,7 @@ async Task InternalTestBasicResponseLongUsageMetadata() {
10501133
// Test that parsing a basic short reply from Google AI endpoint works as expected.
10511134
// https://github.com/FirebaseExtended/vertexai-sdk-test-data/blob/main/mock-responses/googleai/unary-success-basic-reply-short.txt
10521135
async Task InternalTestGoogleAIBasicReplyShort() {
1053-
Dictionary<string, object> json = await GetGoogleAIJsonTestData("unary-success-basic-reply-short.txt"); //
1136+
Dictionary<string, object> json = await GetGoogleAIJsonTestData("unary-success-basic-reply-short.json"); //
10541137
GenerateContentResponse response = GenerateContentResponse.FromJson(json, FirebaseAI.Backend.InternalProvider.GoogleAI);
10551138

10561139
ValidateTextPart(response, "Google's headquarters, also known as the Googleplex, is located in **Mountain View, California**.\n");
@@ -1076,12 +1159,12 @@ async Task InternalTestGoogleAIBasicReplyShort() {
10761159
// Test parsing a Google AI format response with citations.
10771160
// Based on: https://github.com/FirebaseExtended/vertexai-sdk-test-data/blob/main/mock-responses/googleai/unary-success-citations.txt
10781161
async Task InternalTestGoogleAICitations() {
1079-
Dictionary<string, object> json = await GetGoogleAIJsonTestData("unary-success-citations.txt");
1162+
Dictionary<string, object> json = await GetGoogleAIJsonTestData("unary-success-citations.json");
10801163
GenerateContentResponse response = GenerateContentResponse.FromJson(json, FirebaseAI.Backend.InternalProvider.GoogleAI);
10811164

10821165
// Validate Text Part (check start and end)
10831166
string expectedStart = "Okay, let's break down quantum mechanics.";
1084-
string expectedEnd = "foundation for many technologies, including:\n";
1167+
string expectedEnd = "area of physics!";
10851168
Assert("Candidate count", response.Candidates.Count() == 1);
10861169
Candidate candidate = response.Candidates.First();
10871170
AssertEq("Content role", candidate.Content.Role, "model");
@@ -1153,5 +1236,54 @@ async Task InternalTestGoogleAICitations() {
11531236
AssertEq("CandidatesTokensDetails[0].Modality", candidatesDetails[0].Modality, ContentModality.Text);
11541237
AssertEq("CandidatesTokensDetails[0].TokenCount", candidatesDetails[0].TokenCount, 1667);
11551238
}
1239+
1240+
async Task InternalTestGenerateImagesBase64() {
1241+
Dictionary<string, object> json = await GetVertexJsonTestData("unary-success-generate-images-base64.json");
1242+
var response = ImagenGenerationResponse<ImagenInlineImage>.FromJson(json);
1243+
1244+
AssertEq("FilteredReason", response.FilteredReason, null);
1245+
AssertEq("Image Count", response.Images.Count, 4);
1246+
1247+
for (int i = 0; i < response.Images.Count; i++) {
1248+
var image = response.Images[i];
1249+
AssertEq($"Image {i} MimeType", image.MimeType, "image/png");
1250+
Assert($"Image {i} Length: {image.Data.Length}", image.Data.Length > 0);
1251+
1252+
var texture = image.AsTexture2D();
1253+
Assert($"Failed to convert Image {i}", texture != null);
1254+
}
1255+
}
1256+
1257+
async Task InternalTestGenerateImagesAllFiltered() {
1258+
Dictionary<string, object> json = await GetVertexJsonTestData("unary-failure-generate-images-all-filtered.json");
1259+
var response = ImagenGenerationResponse<ImagenInlineImage>.FromJson(json);
1260+
1261+
AssertEq("FilteredReason", response.FilteredReason,
1262+
"Unable to show generated images. All images were filtered out because " +
1263+
"they violated Vertex AI's usage guidelines. You will not be charged for " +
1264+
"blocked images. Try rephrasing the prompt. If you think this was an error, " +
1265+
"send feedback. Support codes: 39322892, 29310472");
1266+
AssertEq("Image Count", response.Images.Count, 0);
1267+
}
1268+
1269+
async Task InternalTestGenerateImagesBase64SomeFiltered() {
1270+
Dictionary<string, object> json = await GetVertexJsonTestData("unary-failure-generate-images-base64-some-filtered.json");
1271+
var response = ImagenGenerationResponse<ImagenInlineImage>.FromJson(json);
1272+
1273+
AssertEq("FilteredReason", response.FilteredReason,
1274+
"Your current safety filter threshold filtered out 2 generated images. " +
1275+
"You will not be charged for blocked images. Try rephrasing the prompt. " +
1276+
"If you think this was an error, send feedback.");
1277+
AssertEq("Image Count", response.Images.Count, 2);
1278+
1279+
for (int i = 0; i < response.Images.Count; i++) {
1280+
var image = response.Images[i];
1281+
AssertEq($"Image {i} MimeType", image.MimeType, "image/png");
1282+
Assert($"Image {i} Length: {image.Data.Length}", image.Data.Length > 0);
1283+
1284+
var texture = image.AsTexture2D();
1285+
Assert($"Failed to convert Image {i}", texture != null);
1286+
}
1287+
}
11561288
}
11571289
}

firebaseai/testapp/Assets/StreamingAssets.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

firebaseai/testapp/Assets/StreamingAssets/TestData.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

firebaseai/testapp/Assets/StreamingAssets/TestData/googleai.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"candidates": [
3+
{
4+
"content": {
5+
"parts": [
6+
{
7+
"text": "Google's headquarters, also known as the Googleplex, is located in **Mountain View, California**.\n"
8+
}
9+
],
10+
"role": "model"
11+
},
12+
"finishReason": "STOP",
13+
"safetyRatings": [
14+
{
15+
"category": "HARM_CATEGORY_HATE_SPEECH",
16+
"probability": "NEGLIGIBLE"
17+
},
18+
{
19+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
20+
"probability": "NEGLIGIBLE"
21+
},
22+
{
23+
"category": "HARM_CATEGORY_HARASSMENT",
24+
"probability": "NEGLIGIBLE"
25+
},
26+
{
27+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
28+
"probability": "NEGLIGIBLE"
29+
}
30+
],
31+
"avgLogprobs": -0.048741644079034981
32+
}
33+
],
34+
"usageMetadata": {
35+
"promptTokenCount": 7,
36+
"candidatesTokenCount": 22,
37+
"totalTokenCount": 29,
38+
"promptTokensDetails": [
39+
{
40+
"modality": "TEXT",
41+
"tokenCount": 7
42+
}
43+
],
44+
"candidatesTokensDetails": [
45+
{
46+
"modality": "TEXT",
47+
"tokenCount": 22
48+
}
49+
]
50+
},
51+
"modelVersion": "gemini-2.0-flash"
52+
}

firebaseai/testapp/Assets/StreamingAssets/TestData/googleai/unary-success-basic-reply-short.json.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)