@@ -3,90 +3,93 @@ package com.google.firebase.example.vertexai.kotlin
33import androidx.lifecycle.ViewModel
44import androidx.lifecycle.viewModelScope
55import com.google.firebase.Firebase
6- import com.google.firebase.vertexai.GenerativeModel
7- import com.google.firebase.vertexai.type.FunctionResponsePart
8- import com.google.firebase.vertexai.type.InvalidStateException
6+ import com.google.firebase.vertexai.type.FunctionDeclaration
97import com.google.firebase.vertexai.type.Schema
108import com.google.firebase.vertexai.type.Tool
11- import com.google.firebase.vertexai.type.content
12- import com.google.firebase.vertexai.type.defineFunction
139import com.google.firebase.vertexai.vertexAI
1410import kotlinx.coroutines.launch
15- import org.json.JSONObject
11+ import kotlinx.serialization.json.JsonObject
12+ import kotlinx.serialization.json.JsonPrimitive
13+ import kotlinx.serialization.json.jsonObject
14+ import kotlinx.serialization.json.jsonPrimitive
1615
1716class FunctionCallViewModel : ViewModel () {
1817
1918 // [START vertexai_fc_create_function]
20- suspend fun makeApiRequest (
21- currencyFrom : String ,
22- currencyTo : String
23- ): JSONObject {
24- // This hypothetical API returns a JSON such as:
25- // {"base":"USD","rates":{"SEK": 10.99}}
26- return JSONObject ().apply {
27- put(" base" , currencyFrom)
28- put(" rates" , hashMapOf(currencyTo to 10.99 ))
29- }
19+ // This function calls a hypothetical external API that returns
20+ // a collection of weather information for a given location on a given date.
21+ // `location` is an object of the form { city: string, state: string }
22+ data class Location (val city : String , val state : String )
23+
24+ suspend fun fetchWeather (location : Location , date : String ): JsonObject {
25+
26+ // TODO(developer): Write a standard function that would call to an external weather API.
27+
28+ // For demo purposes, this hypothetical response is hardcoded here in the expected format.
29+ return JsonObject (mapOf (
30+ " temperature" to JsonPrimitive (38 ),
31+ " chancePrecipitation" to JsonPrimitive (" 56%" ),
32+ " cloudConditions" to JsonPrimitive (" partlyCloudy" )
33+ ))
3034 }
3135 // [END vertexai_fc_create_function]
3236
3337 // [START vertexai_fc_func_declaration]
34- val getExchangeRate = defineFunction(
35- name = " getExchangeRate" ,
36- description = " Get the exchange rate for currencies between countries" ,
37- Schema .str(" currencyFrom" , " The currency to convert from." ),
38- Schema .str(" currencyTo" , " The currency to convert to." )
39- ) { from, to ->
40- // Call the function that you declared above
41- makeApiRequest(from, to)
42- }
38+ val fetchWeatherTool = FunctionDeclaration (
39+ " fetchWeather" ,
40+ " Get the weather conditions for a specific city on a specific date." ,
41+ mapOf (
42+ " location" to Schema .obj(
43+ mapOf (
44+ " city" to Schema .string(" The city of the location." ),
45+ " state" to Schema .string(" The US state of the location." ),
46+ ),
47+ description = " The name of the city and its state for which " +
48+ " to get the weather. Only cities in the " +
49+ " USA are supported."
50+ ),
51+ " date" to Schema .string(" The date for which to get the weather." +
52+ " Date must be in the format: YYYY-MM-DD."
53+ ),
54+ ),
55+ )
4356 // [END vertexai_fc_func_declaration]
4457
4558 // [START vertexai_fc_init]
4659 // Initialize the Vertex AI service and the generative model
47- // Use a model that supports function calling, like Gemini 1.0 Pro.
48- val generativeModel = Firebase .vertexAI.generativeModel(
49- modelName = " gemini-1.0-pro " ,
50- // Specify the function declaration.
51- tools = listOf (Tool (listOf (getExchangeRate )))
60+ // Use a model that supports function calling, like a Gemini 1.5 model
61+ val model = Firebase .vertexAI.generativeModel(
62+ modelName = " gemini-1.5-flash " ,
63+ // Provide the function declaration to the model .
64+ tools = listOf (Tool .functionDeclarations (listOf (fetchWeatherTool )))
5265 )
5366 // [END vertexai_fc_init]
5467
5568 // [START vertexai_fc_generate]
5669 fun generateFunctionCall () {
5770 viewModelScope.launch {
58- val chat = generativeModel.startChat()
71+ val prompt = " What was the weather in Boston on October 17, 2024?"
72+ val chat = model.startChat()
73+ // Send the user's question (the prompt) to the model using multi-turn chat.
74+ val result = chat.sendMessage(prompt)
5975
60- val prompt = " How much is 50 US dollars worth in Swedish krona?"
76+ val functionCalls = result.functionCalls
77+ // When the model responds with one or more function calls, invoke the function(s).
78+ val fetchWeatherCall = functionCalls.find { it.name == " fetchWeather" }
6179
62- // Send the message to the generative model
63- var response = chat.sendMessage(prompt)
64-
65- // Check if the model responded with a function call
66- response.functionCalls.firstOrNull()?.let { functionCall ->
67- // Try to retrieve the stored lambda from the model's tools and
68- // throw an exception if the returned function was not declared
69- val matchedFunction = generativeModel.tools?.flatMap { it.functionDeclarations }
70- ?.first { it.name == functionCall.name }
71- ? : throw InvalidStateException (" Function not found: ${functionCall.name} " )
72-
73- // Call the lambda retrieved above
74- val apiResponse: JSONObject = matchedFunction.execute(functionCall)
75-
76- // Send the API response back to the generative model
77- // so that it generates a text response that can be displayed to the user
78- response = chat.sendMessage(
79- content(role = " function" ) {
80- part(FunctionResponsePart (functionCall.name, apiResponse))
81- }
80+ // Forward the structured input data prepared by the model
81+ // to the hypothetical external API.
82+ val functionResponse = fetchWeatherCall?.let {
83+ // Alternatively, if your `Location` class is marked as @Serializable, you can use
84+ // val location = Json.decodeFromJsonElement<Location>(it.args["location"]!!)
85+ val location = Location (
86+ it.args[" location" ]!! .jsonObject[" city" ]!! .jsonPrimitive.content,
87+ it.args[" location" ]!! .jsonObject[" state" ]!! .jsonPrimitive.content
8288 )
83- }
84-
85- // Whenever the model responds with text, show it in the UI
86- response.text?.let { modelResponse ->
87- println (modelResponse)
89+ val date = it.args[" date" ]!! .jsonPrimitive.content
90+ fetchWeather(location, date)
8891 }
8992 }
9093 }
9194 // [END vertexai_fc_generate]
92- }
95+ }
0 commit comments