diff --git a/packages/firebase_ai/firebase_ai/lib/src/live_api.dart b/packages/firebase_ai/firebase_ai/lib/src/live_api.dart index fdbe2c63d0af..7b5757e92bf9 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/live_api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/live_api.dart @@ -116,7 +116,11 @@ class LiveServerContent implements LiveServerMessage { /// [modelTurn] (optional): The content generated by the model. /// [turnComplete] (optional): Indicates if the turn is complete. /// [interrupted] (optional): Indicates if the generation was interrupted. - LiveServerContent({this.modelTurn, this.turnComplete, this.interrupted}); + LiveServerContent( + {this.modelTurn, + this.turnComplete, + this.interrupted, + this.groundingMetadata}); // TODO(cynthia): Add accessor for media content /// The content generated by the model. @@ -129,6 +133,9 @@ class LiveServerContent implements LiveServerMessage { /// Whether generation was interrupted. If true, indicates that a /// client message has interrupted current model final bool? interrupted; + + /// Metadata specifies sources used to ground generated content. + final GroundingMetadata? groundingMetadata; } /// A tool call in a live stream. @@ -170,10 +177,13 @@ class LiveServerToolCallCancellation implements LiveServerMessage { /// ongoing generation. class LiveServerResponse { // ignore: public_member_api_docs - LiveServerResponse({required this.message}); + LiveServerResponse({required this.message, this.usageMetadata}); /// The server message generated by the live model. final LiveServerMessage message; + + /// Usage metadata about the response(s). + final UsageMetadata? usageMetadata; } /// Represents realtime input from the client in a live stream. @@ -286,7 +296,11 @@ class LiveClientToolResponse { /// - A [LiveServerResponse] object representing the parsed message. LiveServerResponse parseServerResponse(Object jsonObject) { LiveServerMessage message = _parseServerMessage(jsonObject); - return LiveServerResponse(message: message); + UsageMetadata? usageMetadata; + if (jsonObject case {'usageMetadata': final Object usageMetadataJson}) { + usageMetadata = parseUsageMetadata(usageMetadataJson); + } + return LiveServerResponse(message: message, usageMetadata: usageMetadata); } LiveServerMessage _parseServerMessage(Object jsonObject) { @@ -295,7 +309,6 @@ LiveServerMessage _parseServerMessage(Object jsonObject) { } Map json = jsonObject as Map; - if (json.containsKey('serverContent')) { final serverContentJson = json['serverContent'] as Map; Content? modelTurn; @@ -306,7 +319,16 @@ LiveServerMessage _parseServerMessage(Object jsonObject) { if (serverContentJson.containsKey('turnComplete')) { turnComplete = serverContentJson['turnComplete'] as bool; } - return LiveServerContent(modelTurn: modelTurn, turnComplete: turnComplete); + GroundingMetadata? groundingMetadata; + if (serverContentJson.containsKey('groundingMetadata')) { + final groundingMetadataJson = + serverContentJson['groundingMetadata'] as Map; + groundingMetadata = parseGroundingMetadata(groundingMetadataJson); + } + return LiveServerContent( + modelTurn: modelTurn, + turnComplete: turnComplete, + groundingMetadata: groundingMetadata); } else if (json.containsKey('toolCall')) { final toolContentJson = json['toolCall'] as Map; List functionCalls = [];