Skip to content

Commit 2054f73

Browse files
committed
search grounding WIP
1 parent 7894526 commit 2054f73

File tree

6 files changed

+466
-28
lines changed

6 files changed

+466
-28
lines changed

packages/firebase_vertexai/firebase_vertexai/lib/firebase_vertexai.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@ export 'src/error.dart'
5353
ServerException,
5454
UnsupportedUserLocation;
5555
export 'src/firebase_vertexai.dart' show FirebaseVertexAI;
56-
export 'src/function_calling.dart'
56+
export 'src/tool.dart'
5757
show
5858
FunctionCallingConfig,
5959
FunctionCallingMode,
6060
FunctionDeclaration,
61+
GoogleSearch,
6162
Tool,
6263
ToolConfig;
6364
export 'src/imagen_api.dart'

packages/firebase_vertexai/firebase_vertexai/lib/src/api.dart

Lines changed: 178 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ final class Candidate {
169169
// TODO: token count?
170170
// ignore: public_member_api_docs
171171
Candidate(this.content, this.safetyRatings, this.citationMetadata,
172-
this.finishReason, this.finishMessage);
172+
this.finishReason, this.finishMessage,
173+
{this.groundingMetadata});
173174

174175
/// Generated content returned from the model.
175176
final Content content;
@@ -194,6 +195,8 @@ final class Candidate {
194195
/// Message for finish reason.
195196
final String? finishMessage;
196197

198+
final GroundingMetadata? groundingMetadata;
199+
197200
/// The concatenation of the text parts of [content], if any.
198201
///
199202
/// If this candidate was finished for a reason of [FinishReason.recitation]
@@ -225,6 +228,59 @@ final class Candidate {
225228
}
226229
}
227230

231+
final class Segment {
232+
Segment(
233+
{required this.partIndex,
234+
required this.startIndex,
235+
required this.endIndex,
236+
required this.text});
237+
238+
final int partIndex;
239+
final int startIndex;
240+
final int endIndex;
241+
final String text;
242+
}
243+
244+
final class WebGroundingChunk {
245+
WebGroundingChunk({this.uri, this.title, this.domain});
246+
247+
final String? uri;
248+
final String? title;
249+
final String? domain;
250+
}
251+
252+
final class GroundingChunk {
253+
GroundingChunk({this.web});
254+
255+
final WebGroundingChunk? web;
256+
}
257+
258+
final class GroundingSupport {
259+
GroundingSupport({this.segment, required this.groundingChunkIndices});
260+
261+
final Segment? segment;
262+
final List<int> groundingChunkIndices;
263+
}
264+
265+
final class SearchEntrypoint {
266+
SearchEntrypoint({this.renderedContent});
267+
268+
final String? renderedContent;
269+
}
270+
271+
final class GroundingMetadata {
272+
GroundingMetadata(
273+
{this.searchEntrypoint,
274+
required this.groundingChunks,
275+
required this.groundingSupport,
276+
required this.webSearchQueries});
277+
278+
final SearchEntrypoint? searchEntrypoint;
279+
final List<GroundingChunk> groundingChunks;
280+
final List<GroundingSupport> groundingSupport;
281+
final List<String> webSearchQueries;
282+
}
283+
228284
/// Safety rating for a piece of content.
229285
///
230286
/// The safety rating contains the category of harm and the harm probability
@@ -938,29 +994,33 @@ Candidate _parseCandidate(Object? jsonObject) {
938994
}
939995

940996
return Candidate(
941-
jsonObject.containsKey('content')
942-
? parseContent(jsonObject['content'] as Object)
943-
: Content(null, []),
944-
switch (jsonObject) {
945-
{'safetyRatings': final List<Object?> safetyRatings} =>
946-
safetyRatings.map(_parseSafetyRating).toList(),
947-
_ => null
948-
},
949-
switch (jsonObject) {
950-
{'citationMetadata': final Object citationMetadata} =>
951-
_parseCitationMetadata(citationMetadata),
952-
_ => null
953-
},
954-
switch (jsonObject) {
955-
{'finishReason': final Object finishReason} =>
956-
FinishReason._parseValue(finishReason),
957-
_ => null
958-
},
959-
switch (jsonObject) {
960-
{'finishMessage': final String finishMessage} => finishMessage,
961-
_ => null
962-
},
963-
);
997+
jsonObject.containsKey('content')
998+
? parseContent(jsonObject['content'] as Object)
999+
: Content(null, []),
1000+
switch (jsonObject) {
1001+
{'safetyRatings': final List<Object?> safetyRatings} =>
1002+
safetyRatings.map(_parseSafetyRating).toList(),
1003+
_ => null
1004+
},
1005+
switch (jsonObject) {
1006+
{'citationMetadata': final Object citationMetadata} =>
1007+
_parseCitationMetadata(citationMetadata),
1008+
_ => null
1009+
},
1010+
switch (jsonObject) {
1011+
{'finishReason': final Object finishReason} =>
1012+
FinishReason._parseValue(finishReason),
1013+
_ => null
1014+
},
1015+
switch (jsonObject) {
1016+
{'finishMessage': final String finishMessage} => finishMessage,
1017+
_ => null
1018+
},
1019+
groundingMetadata: switch (jsonObject) {
1020+
{'groundingMetadata': final Object groundingMetadata} =>
1021+
_parseGroundingMetadata(groundingMetadata),
1022+
_ => null
1023+
});
9641024
}
9651025

9661026
PromptFeedback _parsePromptFeedback(Object jsonObject) {
@@ -1074,3 +1134,97 @@ Citation _parseCitationSource(Object? jsonObject) {
10741134
jsonObject['license'] as String?,
10751135
);
10761136
}
1137+
1138+
GroundingMetadata _parseGroundingMetadata(Object? jsonObject) {
1139+
if (jsonObject is! Map) {
1140+
throw unhandledFormat('GroundingMetadata', jsonObject);
1141+
}
1142+
1143+
final searchEntrypoint = switch (jsonObject) {
1144+
{'searchEntrypoint': final Object? searchEntrypoint} =>
1145+
_parseSearchEntrypoint(searchEntrypoint),
1146+
_ => null,
1147+
};
1148+
final groundingChunks = switch (jsonObject) {
1149+
{'groundingChunks': final List<Object?> groundingChunks} =>
1150+
groundingChunks.map(_parseGroundingChunk).toList(),
1151+
_ => null,
1152+
} ??
1153+
[];
1154+
final groundingSupport = switch (jsonObject) {
1155+
{'groundingSupport': final List<Object?> groundingSupport} =>
1156+
groundingSupport.map(_parseGroundingSupport).toList(),
1157+
_ => null,
1158+
} ??
1159+
[];
1160+
final webSearchQueries = switch (jsonObject) {
1161+
{'webSearchQueries': final List<String>? webSearchQueries} =>
1162+
webSearchQueries,
1163+
_ => null,
1164+
} ??
1165+
[];
1166+
1167+
return GroundingMetadata(
1168+
searchEntrypoint: searchEntrypoint,
1169+
groundingChunks: groundingChunks,
1170+
groundingSupport: groundingSupport,
1171+
webSearchQueries: webSearchQueries);
1172+
}
1173+
1174+
Segment _parseSegment(Object? jsonObject) {
1175+
if (jsonObject is! Map) {
1176+
throw unhandledFormat('Segment', jsonObject);
1177+
}
1178+
1179+
return Segment(
1180+
partIndex: (jsonObject['partIndex'] as int?) ?? 0,
1181+
startIndex: (jsonObject['startIndex'] as int?) ?? 0,
1182+
endIndex: (jsonObject['endIndex'] as int?) ?? 0,
1183+
text: (jsonObject['text'] as String?) ?? '');
1184+
}
1185+
1186+
WebGroundingChunk _parseWebGroundingChunk(Object? jsonObject) {
1187+
if (jsonObject is! Map) {
1188+
throw unhandledFormat('WebGroundingChunk', jsonObject);
1189+
}
1190+
1191+
return WebGroundingChunk(
1192+
uri: jsonObject['uri'] as String?,
1193+
title: jsonObject['title'] as String?,
1194+
domain: jsonObject['domain'] as String?,
1195+
);
1196+
}
1197+
1198+
GroundingChunk _parseGroundingChunk(Object? jsonObject) {
1199+
if (jsonObject is! Map) {
1200+
throw unhandledFormat('GroundingChunk', jsonObject);
1201+
}
1202+
1203+
return GroundingChunk(
1204+
web: jsonObject['web'] != null
1205+
? _parseWebGroundingChunk(jsonObject['web'])
1206+
: null,
1207+
);
1208+
}
1209+
1210+
GroundingSupport _parseGroundingSupport(Object? jsonObject) {
1211+
if (jsonObject is! Map) {
1212+
throw unhandledFormat('GroundingSupport', jsonObject);
1213+
}
1214+
1215+
return GroundingSupport(
1216+
segment: jsonObject['segment'] != null
1217+
? _parseSegment(jsonObject['segment'])
1218+
: null,
1219+
groundingChunkIndices:
1220+
(jsonObject['groundingChunkIndices'] as List<int>?) ?? []);
1221+
}
1222+
1223+
SearchEntrypoint _parseSearchEntrypoint(Object? jsonObject) {
1224+
if (jsonObject is! Map) {
1225+
throw unhandledFormat('SearchEntrypoint', jsonObject);
1226+
}
1227+
1228+
return SearchEntrypoint(
1229+
renderedContent: (jsonObject['renderedContent'] as String?) ?? '');
1230+
}

packages/firebase_vertexai/firebase_vertexai/lib/src/base_model.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import 'package:web_socket_channel/io.dart';
2626
import 'api.dart';
2727
import 'client.dart';
2828
import 'content.dart';
29-
import 'function_calling.dart';
29+
import 'tool.dart';
3030
import 'imagen_api.dart';
3131
import 'imagen_content.dart';
3232
import 'live_api.dart';

packages/firebase_vertexai/firebase_vertexai/lib/src/function_calling.dart renamed to packages/firebase_vertexai/firebase_vertexai/lib/src/tool.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ import 'schema.dart';
2121
/// knowledge and scope of the model.
2222
final class Tool {
2323
// ignore: public_member_api_docs
24-
Tool._(this._functionDeclarations);
24+
Tool._(this._functionDeclarations, this._googleSearch);
2525

2626
/// Returns a [Tool] instance with list of [FunctionDeclaration].
2727
static Tool functionDeclarations(
2828
List<FunctionDeclaration> functionDeclarations) {
29-
return Tool._(functionDeclarations);
29+
return Tool._(functionDeclarations, null);
30+
}
31+
32+
static Tool googleSearch({GoogleSearch googleSearch = const GoogleSearch()}) {
33+
return Tool._(null, googleSearch);
3034
}
3135

3236
/// A list of `FunctionDeclarations` available to the model that can be used
@@ -38,15 +42,25 @@ final class Tool {
3842
/// [FunctionResponse]
3943
/// with the role "function" generation context for the next model turn.
4044
final List<FunctionDeclaration>? _functionDeclarations;
45+
final GoogleSearch? _googleSearch;
4146

4247
/// Convert to json object.
4348
Map<String, Object> toJson() => {
4449
if (_functionDeclarations case final _functionDeclarations?)
4550
'functionDeclarations':
4651
_functionDeclarations.map((f) => f.toJson()).toList(),
52+
if (_googleSearch case final _googleSearch?)
53+
'googleSearch': _googleSearch.toJson()
4754
};
4855
}
4956

57+
final class GoogleSearch {
58+
const GoogleSearch();
59+
60+
// Convert to json object.
61+
Map<String, Object> toJson() => {};
62+
}
63+
5064
/// Structured representation of a function declaration as defined by the
5165
/// [OpenAPI 3.03 specification](https://spec.openapis.org/oas/v3.0.3).
5266
///

0 commit comments

Comments
 (0)