Skip to content

Commit 5018f42

Browse files
committed
feat: add Function Calling snippets
1 parent e3258a9 commit 5018f42

File tree

6 files changed

+371
-58
lines changed

6 files changed

+371
-58
lines changed

firebase-ai/app/src/main/java/com/google/firebase/example/ailogic/java/CommonSnippets.java

Lines changed: 172 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.google.firebase.example.ailogic.java;
22

33
import androidx.lifecycle.ViewModel;
4+
45
import com.google.common.util.concurrent.FutureCallback;
56
import com.google.common.util.concurrent.Futures;
67
import com.google.common.util.concurrent.ListenableFuture;
@@ -9,18 +10,33 @@
910
import com.google.firebase.ai.java.ChatFutures;
1011
import com.google.firebase.ai.java.GenerativeModelFutures;
1112
import com.google.firebase.ai.type.Content;
13+
import com.google.firebase.ai.type.FunctionCallPart;
14+
import com.google.firebase.ai.type.FunctionResponsePart;
1215
import com.google.firebase.ai.type.GenerateContentResponse;
16+
import com.google.firebase.ai.type.Schema;
17+
import com.google.firebase.ai.type.TextPart;
18+
import com.google.firebase.ai.type.Tool;
19+
import com.google.firebase.ai.type.FunctionDeclaration;
20+
1321
import java.util.Arrays;
22+
import java.util.Collections;
1423
import java.util.List;
24+
import java.util.Map;
1525
import java.util.concurrent.Executor;
26+
import java.util.concurrent.Executors;
27+
28+
import kotlinx.serialization.json.JsonElement;
29+
import kotlinx.serialization.json.JsonElementKt;
30+
import kotlinx.serialization.json.JsonObject;
31+
import kotlinx.serialization.json.JsonPrimitive;
32+
1633
import org.reactivestreams.Publisher;
1734
import org.reactivestreams.Subscriber;
1835
import org.reactivestreams.Subscription;
1936

2037
public class CommonSnippets extends ViewModel {
2138
private Executor executor;
22-
private GenerativeModel ai = FirebaseAI.getInstance()
23-
.generativeModel("gemini-2.5-flash");
39+
private GenerativeModel ai = FirebaseAI.getInstance().generativeModel("gemini-2.5-flash");
2440
private GenerativeModelFutures model = GenerativeModelFutures.from(ai);
2541

2642
public void chat() {
@@ -55,18 +71,21 @@ void chatNonStreaming() {
5571

5672
// Send the message
5773
ListenableFuture<GenerateContentResponse> response = chat.sendMessage(message);
58-
Futures.addCallback(response, new FutureCallback<GenerateContentResponse>() {
59-
@Override
60-
public void onSuccess(GenerateContentResponse result) {
61-
String resultText = result.getText();
62-
System.out.println(resultText);
63-
}
64-
65-
@Override
66-
public void onFailure(Throwable t) {
67-
t.printStackTrace();
68-
}
69-
}, executor);
74+
Futures.addCallback(
75+
response,
76+
new FutureCallback<GenerateContentResponse>() {
77+
@Override
78+
public void onSuccess(GenerateContentResponse result) {
79+
String resultText = result.getText();
80+
System.out.println(resultText);
81+
}
82+
83+
@Override
84+
public void onFailure(Throwable t) {
85+
t.printStackTrace();
86+
}
87+
},
88+
executor);
7089
// [END chat_non_streaming]
7190
}
7291

@@ -96,39 +115,155 @@ void chatStreaming() {
96115
Content message = messageBuilder.build();
97116

98117
// Send the message
99-
Publisher<GenerateContentResponse> streamingResponse =
100-
chat.sendMessageStream(message);
118+
Publisher<GenerateContentResponse> streamingResponse = chat.sendMessageStream(message);
101119

102120
final String[] fullResponse = {""};
103121

104-
streamingResponse.subscribe(new Subscriber<GenerateContentResponse>() {
122+
streamingResponse.subscribe(
123+
new Subscriber<GenerateContentResponse>() {
105124

106-
@Override
107-
public void onNext(GenerateContentResponse generateContentResponse) {
108-
String chunk = generateContentResponse.getText();
109-
fullResponse[0] += chunk;
110-
}
125+
@Override
126+
public void onNext(GenerateContentResponse generateContentResponse) {
127+
String chunk = generateContentResponse.getText();
128+
fullResponse[0] += chunk;
129+
}
111130

131+
@Override
132+
public void onComplete() {
133+
System.out.println(fullResponse[0]);
134+
}
112135

113-
@Override
114-
public void onComplete() {
115-
System.out.println(fullResponse[0]);
116-
}
136+
// ... other methods omitted for brevity
117137

118-
// ... other methods omitted for brevity
138+
// [START_EXCLUDE]
139+
@Override
140+
public void onSubscribe(Subscription s) {
141+
}
119142

120-
// [START_EXCLUDE]
121-
@Override
122-
public void onSubscribe(Subscription s) {
143+
@Override
144+
public void onError(Throwable t) {
145+
}
123146

124-
}
147+
// [END_EXCLUDE]
148+
});
149+
// [END chat_streaming]
150+
}
125151

126-
@Override
127-
public void onError(Throwable t) {
152+
public void functionCalling() {
153+
// [START function_calling_create_function_declaration]
154+
FunctionDeclaration fetchWeatherTool =
155+
new FunctionDeclaration(
156+
"fetchWeather",
157+
"Get the weather conditions for a specific city on a specific date.",
158+
Map.of(
159+
"location",
160+
Schema.obj(
161+
Map.of(
162+
"city", Schema.str("The city of the location."),
163+
"state", Schema.str("The US state of the location."))),
164+
"date",
165+
Schema.str(
166+
"The date for which to get the weather. "
167+
+ "Date must be in the format: YYYY-MM-DD.")),
168+
Collections.emptyList());
169+
// [END function_calling_create_function_declaration]
128170

129-
}
130-
// [END_EXCLUDE]
131-
});
132-
// [END chat_streaming]
171+
// [START function_calling_generate_function_call]
172+
String prompt = "What was the weather in Boston on October 17, 2024?";
173+
ChatFutures chatFutures = model.startChat();
174+
// Send the user's question (the prompt) to the model using multi-turn chat.
175+
ListenableFuture<GenerateContentResponse> response =
176+
chatFutures.sendMessage(new Content("user", List.of(new TextPart(prompt))));
177+
178+
ListenableFuture<JsonObject> handleFunctionCallFuture =
179+
Futures.transform(
180+
response,
181+
result -> {
182+
for (FunctionCallPart functionCall : result.getFunctionCalls()) {
183+
if (functionCall.getName().equals("fetchWeather")) {
184+
Map<String, JsonElement> args = functionCall.getArgs();
185+
JsonObject locationJsonObject = JsonElementKt.getJsonObject(args.get("location"));
186+
String city =
187+
JsonElementKt.getContentOrNull(
188+
JsonElementKt.getJsonPrimitive(locationJsonObject.get("city")));
189+
String state =
190+
JsonElementKt.getContentOrNull(
191+
JsonElementKt.getJsonPrimitive(locationJsonObject.get("state")));
192+
Location location = new Location(city, state);
193+
194+
String date =
195+
JsonElementKt.getContentOrNull(
196+
JsonElementKt.getJsonPrimitive(args.get("date")));
197+
return fetchWeather(location, date);
198+
}
199+
}
200+
return null;
201+
},
202+
Executors.newSingleThreadExecutor());
203+
// [END function_calling_generate_function_call]
204+
205+
// [START function_calling_pass_back_function_response]
206+
ListenableFuture<GenerateContentResponse> modelResponseFuture =
207+
Futures.transformAsync(
208+
handleFunctionCallFuture,
209+
// Send the response(s) from the function back to the model
210+
// so that the model can use it to generate its final response.
211+
functionCallResult ->
212+
chatFutures.sendMessage(
213+
new Content(
214+
"function",
215+
List.of(new FunctionResponsePart("fetchWeather", functionCallResult)))),
216+
Executors.newSingleThreadExecutor());
217+
218+
Futures.addCallback(
219+
modelResponseFuture,
220+
new FutureCallback<GenerateContentResponse>() {
221+
@Override
222+
public void onSuccess(GenerateContentResponse result) {
223+
if (result.getText() != null) {
224+
// Log the text response.
225+
System.out.println(result.getText());
226+
}
227+
}
228+
229+
@Override
230+
public void onFailure(Throwable t) {
231+
// handle error
232+
}
233+
},
234+
Executors.newSingleThreadExecutor());
235+
// [END function_calling_pass_back_function_response]
236+
}
237+
238+
// [START function_calling_write_function]
239+
// This function calls a hypothetical external API that returns
240+
// a collection of weather information for a given location on a given date.
241+
// `location` is an object of the form { city: string, state: string }
242+
public JsonObject fetchWeather(Location location, String date) {
243+
244+
// TODO(developer): Write a standard function that would call to an external weather API.
245+
246+
// For demo purposes, this hypothetical response is hardcoded here in the expected format.
247+
return new JsonObject(
248+
Map.of(
249+
"temperature",
250+
JsonElementKt.JsonPrimitive(38),
251+
"chancePrecipitation",
252+
JsonElementKt.JsonPrimitive("56%"),
253+
"cloudConditions",
254+
JsonElementKt.JsonPrimitive("partlyCloudy")
255+
)
256+
);
257+
}
258+
// [END function_calling_write_function]
259+
}
260+
261+
class Location {
262+
public String city;
263+
public String state;
264+
265+
public Location(String city, String state) {
266+
this.city = city;
267+
this.state = state;
133268
}
134269
}
Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
package com.google.firebase.example.ailogic.java;
22

33
import androidx.lifecycle.ViewModel;
4+
import com.google.firebase.ai.FirebaseAI;
5+
import com.google.firebase.ai.java.GenerativeModelFutures;
6+
import com.google.firebase.ai.type.GenerativeBackend;
7+
import com.google.firebase.ai.type.Tool;
8+
import com.google.firebase.ai.type.FunctionDeclaration;
9+
import java.util.List;
410

511
public class GoogleAISnippets extends ViewModel {
6-
7-
}
12+
void functionCalling(FunctionDeclaration fetchWeatherTool) {
13+
// [START function_calling_specify_declaration_during_init]
14+
// Initialize the Gemini Developer API backend service
15+
// Create a `GenerativeModel` instance with a model that supports your use case
16+
GenerativeModelFutures model =
17+
GenerativeModelFutures.from(
18+
FirebaseAI.getInstance(GenerativeBackend.googleAI())
19+
.generativeModel(
20+
"gemini-2.5-flash",
21+
null,
22+
null,
23+
// Provide the function declaration to the model.
24+
List.of(Tool.functionDeclarations(List.of(fetchWeatherTool)))));
25+
// [END function_calling_specify_declaration_during_init]
26+
}
27+
}

firebase-ai/app/src/main/java/com/google/firebase/example/ailogic/java/VertexAISnippets.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
import androidx.lifecycle.ViewModel;
44

5-
public class VertexAISnippets extends ViewModel {
5+
import com.google.firebase.ai.type.GenerativeBackend;
6+
import com.google.firebase.ai.FirebaseAI;
7+
import com.google.firebase.ai.java.GenerativeModelFutures;
8+
import com.google.firebase.ai.type.Tool;
9+
import com.google.firebase.ai.type.FunctionDeclaration;
10+
11+
import java.util.List;
612

7-
}
13+
public class VertexAISnippets extends ViewModel {
14+
void functionCalling(FunctionDeclaration fetchWeatherTool) {
15+
// [START function_calling_specify_declaration_during_init]
16+
// Initialize the Vertex AI Gemini API backend service
17+
// Optionally specify the location to access the model (`global` is recommended)
18+
// Create a `GenerativeModel` instance with a model that supports your use case
19+
GenerativeModelFutures model =
20+
GenerativeModelFutures.from(
21+
FirebaseAI.getInstance(GenerativeBackend.vertexAI("global"))
22+
.generativeModel(
23+
"gemini-2.5-flash",
24+
null,
25+
null,
26+
// Provide the function declaration to the model.
27+
List.of(Tool.functionDeclarations(List.of(fetchWeatherTool)))));
28+
// [END function_calling_specify_declaration_during_init]
29+
}
30+
}

0 commit comments

Comments
 (0)