Skip to content

Commit 18a227f

Browse files
committed
update toJson for Part to make sure thought and thoughtSignature is sending back
1 parent e4a0652 commit 18a227f

File tree

4 files changed

+236
-99
lines changed

4 files changed

+236
-99
lines changed

packages/firebase_ai/firebase_ai/lib/src/api.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ final class Candidate {
259259
///
260260
/// If this candidate was finished for a reason of [FinishReason.recitation]
261261
/// or [FinishReason.safety], accessing this text will throw a
262-
/// [GenerativeAIException].
262+
/// [FirebaseAIException].
263263
///
264264
/// If [content] contains any text parts, this value is the concatenation of
265265
/// the text.

packages/firebase_ai/firebase_ai/lib/src/chat.dart

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
// // Copyright 2024 Google LLC
2-
// //
3-
// // Licensed under the Apache License, Version 2.0 (the "License");
4-
// // you may not use this file except in compliance with the License.
5-
// // You may obtain a copy of the License at
6-
// //
7-
// // http://www.apache.org/licenses/LICENSE-2.0
8-
// //
9-
// // Unless required by applicable law or agreed to in writing, software
10-
// // distributed under the License is distributed on an "AS IS" BASIS,
11-
// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12-
// // See the License for the specific language governing permissions and
13-
// // limitations under the License.
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
1414

1515
import 'dart:async';
1616

packages/firebase_ai/firebase_ai/lib/src/content.dart

Lines changed: 148 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import 'dart:convert';
1616
import 'dart:developer';
1717
import 'dart:typed_data';
1818

19+
import 'package:meta/meta.dart';
20+
1921
import 'api.dart';
2022
import 'error.dart';
2123

@@ -120,9 +122,11 @@ Part parsePart(Object? jsonObject) {
120122
if (executableCode is Map &&
121123
executableCode.containsKey('language') &&
122124
executableCode.containsKey('code')) {
123-
return ExecutableCodePart(
125+
return ExecutableCodePart._(
124126
language: CodeLanguage.parseValue(executableCode['language'] as String),
125127
code: executableCode['code'] as String,
128+
isThought: isThought,
129+
thoughtSignature: thoughtSignature,
126130
);
127131
} else {
128132
throw unhandledFormat('executableCode', executableCode);
@@ -133,9 +137,11 @@ Part parsePart(Object? jsonObject) {
133137
if (codeExecutionResult is Map &&
134138
codeExecutionResult.containsKey('outcome') &&
135139
codeExecutionResult.containsKey('output')) {
136-
return CodeExecutionResultPart(
140+
return CodeExecutionResultPart._(
137141
outcome: Outcome.parseValue(codeExecutionResult['outcome'] as String),
138142
output: codeExecutionResult['output'] as String,
143+
isThought: isThought,
144+
thoughtSignature: thoughtSignature,
139145
);
140146
} else {
141147
throw unhandledFormat('codeExecutionResult', codeExecutionResult);
@@ -188,7 +194,11 @@ sealed class Part {
188194
final String? _thoughtSignature;
189195

190196
/// Convert the [Part] content to json format.
191-
Object toJson();
197+
Object toJson() => {
198+
if (isThought case final isThought?) 'thought': isThought,
199+
if (_thoughtSignature case final thoughtSignature?)
200+
'thoughtSignature': thoughtSignature,
201+
};
192202
}
193203

194204
/// A [Part] that contains unparsable data.
@@ -200,7 +210,10 @@ final class UnknownPart extends Part {
200210
final Map<String, Object?> data;
201211

202212
@override
203-
Object toJson() => data;
213+
Object toJson() {
214+
final superJson = super.toJson() as Map<String, Object?>;
215+
return <String, Object?>{...superJson, ...data};
216+
}
204217
}
205218

206219
/// A [Part] with the text content.
@@ -211,6 +224,18 @@ final class TextPart extends Part {
211224
isThought: isThought,
212225
thoughtSignature: null,
213226
);
227+
228+
@visibleForTesting
229+
// ignore: public_member_api_docs
230+
const TextPart.forTest(
231+
this.text, {
232+
bool? isThought,
233+
String? thoughtSignature,
234+
}) : super(
235+
isThought: isThought,
236+
thoughtSignature: thoughtSignature,
237+
);
238+
214239
const TextPart._(
215240
this.text, {
216241
bool? isThought,
@@ -223,7 +248,10 @@ final class TextPart extends Part {
223248
/// The text content of the [Part]
224249
final String text;
225250
@override
226-
Object toJson() => {'text': text};
251+
Object toJson() {
252+
final superJson = super.toJson() as Map<String, Object?>;
253+
return <String, Object?>{...superJson, 'text': text};
254+
}
227255
}
228256

229257
/// A [Part] with the byte content of a file.
@@ -238,6 +266,20 @@ final class InlineDataPart extends Part {
238266
isThought: isThought,
239267
thoughtSignature: null,
240268
);
269+
270+
@visibleForTesting
271+
// ignore: public_member_api_docs
272+
const InlineDataPart.forTest(
273+
this.mimeType,
274+
this.bytes, {
275+
this.willContinue,
276+
bool? isThought,
277+
String? thoughtSignature,
278+
}) : super(
279+
isThought: isThought,
280+
thoughtSignature: thoughtSignature,
281+
);
282+
241283
const InlineDataPart._(
242284
this.mimeType,
243285
this.bytes, {
@@ -259,13 +301,17 @@ final class InlineDataPart extends Part {
259301
/// Whether there's more inline data coming for streaming.
260302
final bool? willContinue;
261303
@override
262-
Object toJson() => {
263-
'inlineData': {
264-
'data': base64Encode(bytes),
265-
'mimeType': mimeType,
266-
if (willContinue != null) 'willContinue': willContinue,
267-
}
268-
};
304+
Object toJson() {
305+
final superJson = super.toJson() as Map<String, Object?>;
306+
return <String, Object?>{
307+
...superJson,
308+
'inlineData': {
309+
'data': base64Encode(bytes),
310+
'mimeType': mimeType,
311+
if (willContinue != null) 'willContinue': willContinue,
312+
},
313+
};
314+
}
269315

270316
/// The representation of the data in media streaming chunk.
271317
Object toMediaChunkJson() => {
@@ -289,6 +335,20 @@ final class FunctionCall extends Part {
289335
isThought: isThought,
290336
thoughtSignature: null,
291337
);
338+
339+
@visibleForTesting
340+
// ignore: public_member_api_docs
341+
const FunctionCall.forTest(
342+
this.name,
343+
this.args, {
344+
this.id,
345+
bool? isThought,
346+
String? thoughtSignature,
347+
}) : super(
348+
isThought: isThought,
349+
thoughtSignature: thoughtSignature,
350+
);
351+
292352
const FunctionCall._(
293353
this.name,
294354
this.args, {
@@ -313,13 +373,17 @@ final class FunctionCall extends Part {
313373
final String? id;
314374

315375
@override
316-
Object toJson() => {
317-
'functionCall': {
318-
'name': name,
319-
'args': args,
320-
if (id != null) 'id': id,
321-
}
322-
};
376+
Object toJson() {
377+
final superJson = super.toJson() as Map<String, Object?>;
378+
return <String, Object?>{
379+
...superJson,
380+
'functionCall': {
381+
'name': name,
382+
'args': args,
383+
if (id != null) 'id': id,
384+
},
385+
};
386+
}
323387
}
324388

325389
/// The response class for [FunctionCall]
@@ -350,13 +414,17 @@ final class FunctionResponse extends Part {
350414
final String? id;
351415

352416
@override
353-
Object toJson() => {
354-
'functionResponse': {
355-
'name': name,
356-
'response': response,
357-
if (id != null) 'id': id,
358-
}
359-
};
417+
Object toJson() {
418+
final superJson = super.toJson() as Map<String, Object?>;
419+
return <String, Object?>{
420+
...superJson,
421+
'functionResponse': {
422+
'name': name,
423+
'response': response,
424+
if (id != null) 'id': id,
425+
},
426+
};
427+
}
360428
}
361429

362430
/// A [Part] with Firebase Storage uri as prompt content
@@ -370,6 +438,19 @@ final class FileData extends Part {
370438
isThought: isThought,
371439
thoughtSignature: null,
372440
);
441+
442+
@visibleForTesting
443+
// ignore: public_member_api_docs
444+
const FileData.forTest(
445+
this.mimeType,
446+
this.fileUri, {
447+
bool? isThought,
448+
String? thoughtSignature,
449+
}) : super(
450+
isThought: isThought,
451+
thoughtSignature: thoughtSignature,
452+
);
453+
373454
const FileData._(
374455
this.mimeType,
375456
this.fileUri, {
@@ -388,9 +469,13 @@ final class FileData extends Part {
388469
final String fileUri;
389470

390471
@override
391-
Object toJson() => {
392-
'file_data': {'file_uri': fileUri, 'mime_type': mimeType}
393-
};
472+
Object toJson() {
473+
final superJson = super.toJson() as Map<String, Object?>;
474+
return <String, Object?>{
475+
...superJson,
476+
'file_data': {'file_uri': fileUri, 'mime_type': mimeType},
477+
};
478+
}
394479
}
395480

396481
/// A `Part` that represents the code that is executed by the model.
@@ -405,16 +490,30 @@ final class ExecutableCodePart extends Part {
405490
thoughtSignature: null,
406491
);
407492

493+
ExecutableCodePart._({
494+
required this.language,
495+
required this.code,
496+
bool? isThought,
497+
String? thoughtSignature,
498+
}) : super(
499+
isThought: isThought,
500+
thoughtSignature: thoughtSignature,
501+
);
502+
408503
/// The programming language of the code.
409504
final CodeLanguage language;
410505

411506
/// The source code to be executed.
412507
final String code;
413508

414509
@override
415-
Object toJson() => {
416-
'executableCode': {'language': language.toJson(), 'code': code}
417-
};
510+
Object toJson() {
511+
final superJson = super.toJson() as Map<String, Object?>;
512+
return <String, Object?>{
513+
...superJson,
514+
'executableCode': {'language': language.toJson(), 'code': code},
515+
};
516+
}
418517
}
419518

420519
/// A `Part` that represents the code execution result from the model.
@@ -429,14 +528,28 @@ final class CodeExecutionResultPart extends Part {
429528
thoughtSignature: null,
430529
);
431530

531+
CodeExecutionResultPart._({
532+
required this.outcome,
533+
required this.output,
534+
bool? isThought,
535+
String? thoughtSignature,
536+
}) : super(
537+
isThought: isThought,
538+
thoughtSignature: thoughtSignature,
539+
);
540+
432541
/// The result of the execution.
433542
final Outcome outcome;
434543

435544
/// The stdout from the code execution, or an error message if it failed.
436545
final String output;
437546

438547
@override
439-
Object toJson() => {
440-
'codeExecutionResult': {'outcome': outcome.toJson(), 'output': output}
441-
};
548+
Object toJson() {
549+
final superJson = super.toJson() as Map<String, Object?>;
550+
return <String, Object?>{
551+
...superJson,
552+
'codeExecutionResult': {'outcome': outcome.toJson(), 'output': output},
553+
};
554+
}
442555
}

0 commit comments

Comments
 (0)