14
14
15
15
import 'package:google_generative_ai/google_generative_ai.dart' as google_ai;
16
16
// ignore: implementation_imports, tightly coupled packages
17
- import 'package:google_generative_ai/src/vertex_hooks.dart' ;
17
+ import 'package:google_generative_ai/src/vertex_hooks.dart' as google_ai_hooks ;
18
18
19
19
import 'vertex_content.dart' ;
20
20
@@ -47,32 +47,36 @@ extension GoogleAICountTokensResponseConversion
47
47
/// Extension on [google_ai.CountTokensResponse] to access extra fields
48
48
extension CountTokensResponseFields on google_ai.CountTokensResponse {
49
49
/// Total billable Characters for the prompt.
50
- int ? get totalBillableCharacters =>
51
- countTokensResponseFields (this )? ['totalBillableCharacters' ] as int ? ;
50
+ int ? get totalBillableCharacters => google_ai_hooks
51
+ . countTokensResponseFields (this )? ['totalBillableCharacters' ] as int ? ;
52
52
}
53
53
54
54
/// Response from the model; supports multiple candidates.
55
55
final class GenerateContentResponse {
56
56
/// Constructor
57
- GenerateContentResponse (this .candidates, this .promptFeedback);
57
+ GenerateContentResponse (this .candidates, this .promptFeedback,
58
+ {this .usageMetadata});
58
59
59
60
/// Candidate responses from the model.
60
61
final List <Candidate > candidates;
61
62
62
63
/// Returns the prompt's feedback related to the content filters.
63
64
final PromptFeedback ? promptFeedback;
64
65
66
+ /// Meta data for the response
67
+ final UsageMetadata ? usageMetadata;
68
+
65
69
/// The text content of the first part of the first of [candidates] , if any.
66
70
///
67
71
/// If the prompt was blocked, or the first candidate was finished for a reason
68
72
/// of [FinishReason.recitation] or [FinishReason.safety] , accessing this text
69
- /// will throw a [GenerativeAIException] .
73
+ /// will throw a [google_ai. GenerativeAIException] .
70
74
///
71
- /// If the first candidate's content starts with a text part , this value is
72
- /// that text.
75
+ /// If the first candidate's content contains any text parts , this value is
76
+ /// the concatenation of the text.
73
77
///
74
- /// If there are no candidates, or if the first candidate does not start with
75
- /// a text part , this value is `null` .
78
+ /// If there are no candidates, or if the first candidate does not contain any
79
+ /// text parts , this value is `null` .
76
80
String ? get text {
77
81
return switch (candidates) {
78
82
[] => switch (promptFeedback) {
@@ -101,8 +105,12 @@ final class GenerateContentResponse {
101
105
? ': $finishMessage '
102
106
: '' ),
103
107
),
108
+ // Special case for a single TextPart to avoid iterable chain.
104
109
[Candidate (content: Content (parts: [TextPart (: final text)])), ...] =>
105
110
text,
111
+ [Candidate (content: Content (: final parts)), ...]
112
+ when parts.any ((p) => p is TextPart ) =>
113
+ parts.whereType <TextPart >().map ((p) => p.text).join (),
106
114
[Candidate (), ...] => null ,
107
115
};
108
116
}
@@ -124,6 +132,7 @@ extension GoogleAIGenerateContentResponseConversion
124
132
GenerateContentResponse toVertex () => GenerateContentResponse (
125
133
candidates.map ((c) => c.toVertex ()).toList (),
126
134
promptFeedback? .toVertex (),
135
+ usageMetadata: usageMetadata? .toVertex (),
127
136
);
128
137
}
129
138
@@ -239,6 +248,35 @@ extension GoogleAIPromptFeedback on google_ai.PromptFeedback {
239
248
);
240
249
}
241
250
251
+ /// Metadata on the generation request's token usage.
252
+ final class UsageMetadata {
253
+ /// Constructor
254
+ UsageMetadata ({
255
+ this .promptTokenCount,
256
+ this .candidatesTokenCount,
257
+ this .totalTokenCount,
258
+ });
259
+
260
+ /// Number of tokens in the prompt.
261
+ final int ? promptTokenCount;
262
+
263
+ /// Total number of tokens across the generated candidates.
264
+ final int ? candidatesTokenCount;
265
+
266
+ /// Total token count for the generation request (prompt + candidates).
267
+ final int ? totalTokenCount;
268
+ }
269
+
270
+ /// Conversion utilities for [google_ai.UsageMetadata] .
271
+ extension GoogleAIUsageMetadata on google_ai.UsageMetadata {
272
+ /// Returns this as a [UsageMetadata] .
273
+ UsageMetadata toVertex () => UsageMetadata (
274
+ promptTokenCount: promptTokenCount,
275
+ candidatesTokenCount: candidatesTokenCount,
276
+ totalTokenCount: totalTokenCount,
277
+ );
278
+ }
279
+
242
280
/// Response candidate generated from a [GenerativeModel] .
243
281
final class Candidate {
244
282
// TODO: token count?
@@ -323,6 +361,7 @@ enum BlockReason {
323
361
other ('OTHER' );
324
362
325
363
const BlockReason (this ._jsonString);
364
+ // ignore: unused_element
326
365
static BlockReason _parseValue (String jsonObject) {
327
366
return switch (jsonObject) {
328
367
'BLOCK_REASON_UNSPECIFIED' => BlockReason .unspecified,
@@ -374,6 +413,7 @@ enum HarmCategory {
374
413
dangerousContent ('HARM_CATEGORY_DANGEROUS_CONTENT' );
375
414
376
415
const HarmCategory (this ._jsonString);
416
+ // ignore: unused_element
377
417
static HarmCategory _parseValue (Object jsonObject) {
378
418
return switch (jsonObject) {
379
419
'HARM_CATEGORY_UNSPECIFIED' => HarmCategory .unspecified,
@@ -444,6 +484,7 @@ enum HarmProbability {
444
484
445
485
const HarmProbability (this ._jsonString);
446
486
487
+ // ignore: unused_element
447
488
static HarmProbability _parseValue (Object jsonObject) {
448
489
return switch (jsonObject) {
449
490
'UNSPECIFIED' => HarmProbability .unspecified,
@@ -551,6 +592,7 @@ enum FinishReason {
551
592
/// Convert to json format
552
593
String toJson () => _jsonString;
553
594
595
+ // ignore: unused_element
554
596
static FinishReason _parseValue (Object jsonObject) {
555
597
return switch (jsonObject) {
556
598
'UNSPECIFIED' => FinishReason .unspecified,
@@ -867,132 +909,28 @@ extension TaskTypeConversion on TaskType {
867
909
868
910
/// Parse to [GenerateContentResponse] from json object.
869
911
GenerateContentResponse parseGenerateContentResponse (Object jsonObject) {
870
- return switch (jsonObject) {
871
- {'candidates' : final List <Object ?> candidates} => GenerateContentResponse (
872
- candidates.map (_parseCandidate).toList (),
873
- switch (jsonObject) {
874
- {'promptFeedback' : final promptFeedback? } =>
875
- _parsePromptFeedback (promptFeedback),
876
- _ => null
877
- }),
878
- {'promptFeedback' : final promptFeedback? } =>
879
- GenerateContentResponse ([], _parsePromptFeedback (promptFeedback)),
880
- _ => throw FormatException (
881
- 'Unhandled GenerateContentResponse format' , jsonObject)
882
- };
912
+ google_ai.GenerateContentResponse response =
913
+ google_ai_hooks.parseGenerateContentResponse (jsonObject);
914
+ return response.toVertex ();
883
915
}
884
916
885
917
/// Parse to [CountTokensResponse] from json object.
886
918
CountTokensResponse parseCountTokensResponse (Object jsonObject) {
887
- return switch (jsonObject) {
888
- {'totalTokens' : final int totalTokens} => CountTokensResponse (totalTokens),
889
- _ =>
890
- throw FormatException ('Unhandled CountTokensResponse format' , jsonObject)
891
- };
919
+ google_ai.CountTokensResponse response =
920
+ google_ai_hooks.parseCountTokensResponse (jsonObject);
921
+ return response.toVertex ();
892
922
}
893
923
894
924
/// Parse to [EmbedContentResponse] from json object.
895
925
EmbedContentResponse parseEmbedContentResponse (Object jsonObject) {
896
- return switch (jsonObject) {
897
- {'embedding' : final Object embedding} =>
898
- EmbedContentResponse (_parseContentEmbedding (embedding)),
899
- _ =>
900
- throw FormatException ('Unhandled EmbedContentResponse format' , jsonObject)
901
- };
926
+ google_ai.EmbedContentResponse response =
927
+ google_ai_hooks.parseEmbedContentResponse (jsonObject);
928
+ return response.toVertex ();
902
929
}
903
930
904
- Candidate _parseCandidate (Object ? jsonObject) {
905
- if (jsonObject is ! Map ) {
906
- throw FormatException ('Unhandled Candidate format' , jsonObject);
907
- }
908
-
909
- return Candidate (
910
- jsonObject.containsKey ('content' )
911
- ? parseContent (jsonObject['content' ] as Object )
912
- : Content (null , []),
913
- switch (jsonObject) {
914
- {'safetyRatings' : final List <Object ?> safetyRatings} =>
915
- safetyRatings.map (_parseSafetyRating).toList (),
916
- _ => null
917
- },
918
- switch (jsonObject) {
919
- {'citationMetadata' : final Object citationMetadata} =>
920
- _parseCitationMetadata (citationMetadata),
921
- _ => null
922
- },
923
- switch (jsonObject) {
924
- {'finishReason' : final Object finishReason} =>
925
- FinishReason ._parseValue (finishReason),
926
- _ => null
927
- },
928
- switch (jsonObject) {
929
- {'finishMessage' : final String finishMessage} => finishMessage,
930
- _ => null
931
- },
932
- );
933
- }
934
-
935
- PromptFeedback _parsePromptFeedback (Object jsonObject) {
936
- return switch (jsonObject) {
937
- {
938
- 'safetyRatings' : final List <Object ?> safetyRatings,
939
- } =>
940
- PromptFeedback (
941
- switch (jsonObject) {
942
- {'blockReason' : final String blockReason} =>
943
- BlockReason ._parseValue (blockReason),
944
- _ => null ,
945
- },
946
- switch (jsonObject) {
947
- {'blockReasonMessage' : final String blockReasonMessage} =>
948
- blockReasonMessage,
949
- _ => null ,
950
- },
951
- safetyRatings.map (_parseSafetyRating).toList ()),
952
- _ => throw FormatException ('Unhandled PromptFeedback format' , jsonObject),
953
- };
954
- }
955
-
956
- SafetyRating _parseSafetyRating (Object ? jsonObject) {
957
- return switch (jsonObject) {
958
- {
959
- 'category' : final Object category,
960
- 'probability' : final Object probability
961
- } =>
962
- SafetyRating (HarmCategory ._parseValue (category),
963
- HarmProbability ._parseValue (probability)),
964
- _ => throw FormatException ('Unhandled SafetyRating format' , jsonObject),
965
- };
966
- }
967
-
968
- ContentEmbedding _parseContentEmbedding (Object ? jsonObject) {
969
- return switch (jsonObject) {
970
- {'values' : final List <Object ?> values} => ContentEmbedding (< double > [
971
- ...values.cast <double >(),
972
- ]),
973
- _ => throw FormatException ('Unhandled ContentEmbedding format' , jsonObject),
974
- };
975
- }
976
-
977
- CitationMetadata _parseCitationMetadata (Object ? jsonObject) {
978
- return switch (jsonObject) {
979
- {'citationSources' : final List <Object ?> citationSources} =>
980
- CitationMetadata (citationSources.map (_parseCitationSource).toList ()),
981
- _ => throw FormatException ('Unhandled CitationMetadata format' , jsonObject),
982
- };
983
- }
984
-
985
- CitationSource _parseCitationSource (Object ? jsonObject) {
986
- if (jsonObject is ! Map ) {
987
- throw FormatException ('Unhandled CitationSource format' , jsonObject);
988
- }
989
-
990
- final uriString = jsonObject['uri' ] as String ? ;
991
-
992
- return CitationSource (
993
- jsonObject['startIndex' ] as int ? ,
994
- jsonObject['endIndex' ] as int ? ,
995
- uriString != null ? Uri .parse (uriString) : null ,
996
- jsonObject['license' ] as String ? ,
997
- );
931
+ /// Parse to [BatchEmbedContentsResponse] from json object.
932
+ BatchEmbedContentsResponse parseBatchEmbedContentsResponse (Object jsonObject) {
933
+ google_ai.BatchEmbedContentsResponse response =
934
+ google_ai_hooks.parseBatchEmbedContentsResponse (jsonObject);
935
+ return response.toVertex ();
998
936
}
0 commit comments