1414
1515import 'content.dart' ;
1616import 'error.dart' ;
17- import 'function_calling .dart' show Tool, ToolConfig;
17+ import 'tool .dart' show Tool, ToolConfig;
1818import 'schema.dart' ;
1919
2020/// Response for Count Tokens
@@ -187,7 +187,8 @@ final class Candidate {
187187 // TODO: token count?
188188 // ignore: public_member_api_docs
189189 Candidate (this .content, this .safetyRatings, this .citationMetadata,
190- this .finishReason, this .finishMessage);
190+ this .finishReason, this .finishMessage,
191+ {this .groundingMetadata});
191192
192193 /// Generated content returned from the model.
193194 final Content content;
@@ -212,6 +213,9 @@ final class Candidate {
212213 /// Message for finish reason.
213214 final String ? finishMessage;
214215
216+ /// Metadata returned to the client when grounding is enabled.
217+ final GroundingMetadata ? groundingMetadata;
218+
215219 /// The concatenation of the text parts of [content] , if any.
216220 ///
217221 /// If this candidate was finished for a reason of [FinishReason.recitation]
@@ -243,6 +247,144 @@ final class Candidate {
243247 }
244248}
245249
250+ /// Represents a specific segment within a [Content] , often used to pinpoint
251+ /// the exact location of text or data that grounding information refers to.
252+ final class Segment {
253+ Segment (
254+ {required this .partIndex,
255+ required this .startIndex,
256+ required this .endIndex,
257+ required this .text});
258+
259+ /// The zero-based index of the [Part] object within the `parts` array of its
260+ /// parent [Content] object.
261+ ///
262+ /// This identifies which part of the content the segment belongs to.
263+ final int partIndex;
264+
265+ /// The zero-based start index of the segment within the specified [Part] ,
266+ /// measured in UTF-8 bytes.
267+ ///
268+ /// This offset is inclusive, starting from 0 at the beginning of the
269+ /// part's content.
270+ final int startIndex;
271+
272+ /// The zero-based end index of the segment within the specified [Part] ,
273+ /// measured in UTF-8 bytes.
274+ ///
275+ /// This offset is exclusive, meaning the character at this index is not
276+ /// included in the segment.
277+ final int endIndex;
278+
279+ /// The text corresponding to the segment from the response.
280+ final String text;
281+ }
282+
283+ /// A grounding chunk sourced from the web.
284+ final class WebGroundingChunk {
285+ WebGroundingChunk ({this .uri, this .title, this .domain});
286+
287+ /// The URI of the retrieved web page.
288+ final String ? uri;
289+
290+ /// The title of the retrieved web page.
291+ final String ? title;
292+
293+ /// The domain of the original URI from which the content was retrieved.
294+ ///
295+ /// This field is only populated when using the Vertex AI Gemini API.
296+ final String ? domain;
297+ }
298+
299+ /// Represents a chunk of retrieved data that supports a claim in the model's
300+ /// response.
301+ ///
302+ /// This is part of the grounding information provided when grounding is
303+ /// enabled.
304+ final class GroundingChunk {
305+ GroundingChunk ({this .web});
306+
307+ /// Contains details if the grounding chunk is from a web source.
308+ final WebGroundingChunk ? web;
309+ }
310+
311+ /// Provides information about how a specific segment of the model's response
312+ /// is supported by the retrieved grounding chunks.
313+ final class GroundingSupport {
314+ GroundingSupport (
315+ {required this .segment, required this .groundingChunkIndices});
316+
317+ /// Specifies the segment of the model's response content that this
318+ /// grounding support pertains to.
319+ final Segment segment;
320+
321+ /// A list of indices that refer to specific [GroundingChunk] s within the
322+ /// [GroundingMetadata.groundingChunks] array.
323+ ///
324+ /// These referenced chunks are the sources that
325+ /// support the claim made in the associated `segment` of the response.
326+ /// For example, an array `[1, 3, 4]`
327+ /// means that `groundingChunks[1]` , `groundingChunks[3]` , and
328+ /// `groundingChunks[4]` are the
329+ /// retrieved content supporting this part of the response.
330+ final List <int > groundingChunkIndices;
331+ }
332+
333+ /// Google Search entry point for web searches.
334+ final class SearchEntryPoint {
335+ SearchEntryPoint ({required this .renderedContent});
336+
337+ /// An HTML/CSS snippet that **must** be embedded in an app to display a
338+ /// Google Search entry point for follow-up web searches related to the
339+ /// model's "Grounded Response".
340+ ///
341+ /// To ensure proper rendering, it's recommended to display this content
342+ /// within a `WebView` .
343+ final String renderedContent;
344+ }
345+
346+ /// Metadata returned to the client when grounding is enabled.
347+ ///
348+ /// > Important: If using Grounding with Google Search, you are required to
349+ /// comply with the "Grounding with Google Search" usage requirements for your
350+ /// chosen API provider:
351+ /// [Gemini Developer API] (https://ai.google.dev/gemini-api/terms#grounding-with-google-search)
352+ /// or Vertex AI Gemini API (see [Service Terms] (https://cloud.google.com/terms/service-terms)
353+ /// section within the Service Specific Terms).
354+ final class GroundingMetadata {
355+ GroundingMetadata (
356+ {this .searchEntryPoint,
357+ required this .groundingChunks,
358+ required this .groundingSupport,
359+ required this .webSearchQueries});
360+
361+ /// Google Search entry point for web searches.
362+ ///
363+ /// This contains an HTML/CSS snippet that **must** be embedded in an app to
364+ // display a Google Search entry point for follow-up web searches related to
365+ // the model's "Grounded Response".
366+ final SearchEntryPoint ? searchEntryPoint;
367+
368+ /// A list of [GroundingChunk] s.
369+ ///
370+ /// Each chunk represents a piece of retrieved content (e.g., from a web
371+ /// page) that the model used to ground its response.
372+ final List <GroundingChunk > groundingChunks;
373+
374+ /// A list of [GroundingSupport] s.
375+ ///
376+ /// Each object details how specific segments of the
377+ /// model's response are supported by the `groundingChunks` .
378+ final List <GroundingSupport > groundingSupport;
379+
380+ /// A list of web search queries that the model performed to gather the
381+ /// grounding information.
382+ ///
383+ /// These can be used to allow users to explore the search results
384+ /// themselves.
385+ final List <String > webSearchQueries;
386+ }
387+
246388/// Safety rating for a piece of content.
247389///
248390/// The safety rating contains the category of harm and the harm probability
@@ -1027,29 +1169,33 @@ Candidate _parseCandidate(Object? jsonObject) {
10271169 }
10281170
10291171 return Candidate (
1030- jsonObject.containsKey ('content' )
1031- ? parseContent (jsonObject['content' ] as Object )
1032- : Content (null , []),
1033- switch (jsonObject) {
1034- {'safetyRatings' : final List <Object ?> safetyRatings} =>
1035- safetyRatings.map (_parseSafetyRating).toList (),
1036- _ => null
1037- },
1038- switch (jsonObject) {
1039- {'citationMetadata' : final Object citationMetadata} =>
1040- _parseCitationMetadata (citationMetadata),
1041- _ => null
1042- },
1043- switch (jsonObject) {
1044- {'finishReason' : final Object finishReason} =>
1045- FinishReason ._parseValue (finishReason),
1046- _ => null
1047- },
1048- switch (jsonObject) {
1049- {'finishMessage' : final String finishMessage} => finishMessage,
1050- _ => null
1051- },
1052- );
1172+ jsonObject.containsKey ('content' )
1173+ ? parseContent (jsonObject['content' ] as Object )
1174+ : Content (null , []),
1175+ switch (jsonObject) {
1176+ {'safetyRatings' : final List <Object ?> safetyRatings} =>
1177+ safetyRatings.map (_parseSafetyRating).toList (),
1178+ _ => null
1179+ },
1180+ switch (jsonObject) {
1181+ {'citationMetadata' : final Object citationMetadata} =>
1182+ _parseCitationMetadata (citationMetadata),
1183+ _ => null
1184+ },
1185+ switch (jsonObject) {
1186+ {'finishReason' : final Object finishReason} =>
1187+ FinishReason ._parseValue (finishReason),
1188+ _ => null
1189+ },
1190+ switch (jsonObject) {
1191+ {'finishMessage' : final String finishMessage} => finishMessage,
1192+ _ => null
1193+ },
1194+ groundingMetadata: switch (jsonObject) {
1195+ {'groundingMetadata' : final Object groundingMetadata} =>
1196+ _parseGroundingMetadata (groundingMetadata),
1197+ _ => null
1198+ });
10531199}
10541200
10551201PromptFeedback _parsePromptFeedback (Object jsonObject) {
@@ -1163,3 +1309,114 @@ Citation _parseCitationSource(Object? jsonObject) {
11631309 jsonObject['license' ] as String ? ,
11641310 );
11651311}
1312+
1313+ GroundingMetadata _parseGroundingMetadata (Object ? jsonObject) {
1314+ if (jsonObject is ! Map ) {
1315+ throw unhandledFormat ('GroundingMetadata' , jsonObject);
1316+ }
1317+
1318+ final searchEntryPoint = switch (jsonObject) {
1319+ {'searchEntryPoint' : final Object ? searchEntryPoint} =>
1320+ _parseSearchEntryPoint (searchEntryPoint),
1321+ _ => null ,
1322+ };
1323+ final groundingChunks = switch (jsonObject) {
1324+ {'groundingChunks' : final List <Object ?> groundingChunks} =>
1325+ groundingChunks.map (_parseGroundingChunk).toList (),
1326+ _ => null ,
1327+ } ??
1328+ [];
1329+ // Filters out null elements, which are returned from _parseGroundingSupport when
1330+ // segment is null.
1331+ final groundingSupport = switch (jsonObject) {
1332+ {'groundingSupport' : final List <Object ?> groundingSupport} =>
1333+ groundingSupport
1334+ .map (_parseGroundingSupport)
1335+ .whereType <GroundingSupport >()
1336+ .toList (),
1337+ _ => null ,
1338+ } ??
1339+ [];
1340+ final webSearchQueries = switch (jsonObject) {
1341+ {'webSearchQueries' : final List <String >? webSearchQueries} =>
1342+ webSearchQueries,
1343+ _ => null ,
1344+ } ??
1345+ [];
1346+
1347+ return GroundingMetadata (
1348+ searchEntryPoint: searchEntryPoint,
1349+ groundingChunks: groundingChunks,
1350+ groundingSupport: groundingSupport,
1351+ webSearchQueries: webSearchQueries);
1352+ }
1353+
1354+ Segment _parseSegment (Object ? jsonObject) {
1355+ if (jsonObject is ! Map ) {
1356+ throw unhandledFormat ('Segment' , jsonObject);
1357+ }
1358+
1359+ return Segment (
1360+ partIndex: (jsonObject['partIndex' ] as int ? ) ?? 0 ,
1361+ startIndex: (jsonObject['startIndex' ] as int ? ) ?? 0 ,
1362+ endIndex: (jsonObject['endIndex' ] as int ? ) ?? 0 ,
1363+ text: (jsonObject['text' ] as String ? ) ?? '' );
1364+ }
1365+
1366+ WebGroundingChunk _parseWebGroundingChunk (Object ? jsonObject) {
1367+ if (jsonObject is ! Map ) {
1368+ throw unhandledFormat ('WebGroundingChunk' , jsonObject);
1369+ }
1370+
1371+ return WebGroundingChunk (
1372+ uri: jsonObject['uri' ] as String ? ,
1373+ title: jsonObject['title' ] as String ? ,
1374+ domain: jsonObject['domain' ] as String ? ,
1375+ );
1376+ }
1377+
1378+ GroundingChunk _parseGroundingChunk (Object ? jsonObject) {
1379+ if (jsonObject is ! Map ) {
1380+ throw unhandledFormat ('GroundingChunk' , jsonObject);
1381+ }
1382+
1383+ return GroundingChunk (
1384+ web: jsonObject['web' ] != null
1385+ ? _parseWebGroundingChunk (jsonObject['web' ])
1386+ : null ,
1387+ );
1388+ }
1389+
1390+ GroundingSupport ? _parseGroundingSupport (Object ? jsonObject) {
1391+ if (jsonObject is ! Map ) {
1392+ throw unhandledFormat ('GroundingSupport' , jsonObject);
1393+ }
1394+
1395+ final segment = switch (jsonObject) {
1396+ {'segment' : final Object ? segment} => _parseSegment (segment),
1397+ _ => null ,
1398+ };
1399+ if (segment == null ) {
1400+ return null ;
1401+ }
1402+
1403+ return GroundingSupport (
1404+ segment: segment,
1405+ groundingChunkIndices:
1406+ (jsonObject['groundingChunkIndices' ] as List <int >? ) ?? []);
1407+ }
1408+
1409+ SearchEntryPoint _parseSearchEntryPoint (Object ? jsonObject) {
1410+ if (jsonObject is ! Map ) {
1411+ throw unhandledFormat ('SearchEntryPoint' , jsonObject);
1412+ }
1413+
1414+ final renderedContent = jsonObject['renderedContent' ] as String ? ;
1415+ if (renderedContent == null ) {
1416+ throw unhandledFormat ('SearchEntryPoint' , jsonObject);
1417+ }
1418+
1419+ return SearchEntryPoint (
1420+ renderedContent: renderedContent,
1421+ );
1422+ }
0 commit comments