Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions packages/firebase_ai/firebase_ai/lib/src/base_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import 'dart:async';
import 'dart:convert';

import 'package:firebase_app_check/firebase_app_check.dart';
import 'package:firebase_auth/firebase_auth.dart';
Expand Down Expand Up @@ -375,7 +376,7 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel {
T Function(Map<String, Object?>) parse) {
Map<String, Object?> body = {};
if (inputs != null) {
body['inputs'] = inputs;
body['inputs'] = _serializeTemplateInputs(inputs);
}
if (history != null) {
body['history'] = history.map((c) => c.toJson()).toList();
Expand Down Expand Up @@ -405,7 +406,7 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel {
T Function(Map<String, Object?>) parse) {
Map<String, Object?> body = {};
if (inputs != null) {
body['inputs'] = inputs;
body['inputs'] = _serializeTemplateInputs(inputs);
}
if (history != null) {
body['history'] = history.map((c) => c.toJson()).toList();
Expand All @@ -428,4 +429,26 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel {
/// Returns the template name for the given [templateId].
String templateName(String templateId) =>
_templateUri.templateName(templateId);

Map<String, Object?> _serializeTemplateInputs(Map<String, Object?> inputs) {
return inputs.map((key, value) {
return MapEntry(key, _serializeTemplateInputValue(value));
});
}

Object? _serializeTemplateInputValue(Object? value) {
return switch (value) {
InlineDataPart(:final mimeType, :final bytes) => {
'isInline': true,
'mimeType': mimeType,
'contents': base64Encode(bytes),
},
Map<Object?, Object?>() => value.map((key, nestedValue) {
return MapEntry(key, _serializeTemplateInputValue(nestedValue));
}),
List<Object?>() =>
value.map(_serializeTemplateInputValue).toList(growable: false),
_ => value,
};
}
}
28 changes: 28 additions & 0 deletions packages/firebase_ai/firebase_ai/test/server_template_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import 'dart:convert';
import 'dart:typed_data';

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_ai/src/base_model.dart';
Expand Down Expand Up @@ -86,6 +87,33 @@ void main() {
expect(response.text, 'Some response');
});

test('generateContent serializes inline image inputs', () async {
final mockHttp = MockClient((request) async {
final body = jsonDecode(request.body) as Map<String, Object?>;
expect(body['inputs'], {
'screenshot': {
'isInline': true,
'mimeType': 'image/jpeg',
'contents': base64Encode([1, 2, 3]),
},
});
return http.Response(jsonEncode(_arbitraryGenerateContentResponse), 200,
headers: {'content-type': 'application/json'});
});

final model = createModel(mockHttp);
final response = await model.generateContent(
templateId,
inputs: {
'screenshot': InlineDataPart(
'image/jpeg',
Uint8List.fromList([1, 2, 3]),
),
},
);
expect(response.text, 'Some response');
});

test('generateContent with TemplateToolConfig passes retrievalConfig',
() async {
final mockHttp = MockClient((request) async {
Expand Down
Loading