FunctionCallingSnippets.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // Copyright 2024 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseAI
  15. import FirebaseCore
  16. import XCTest
  17. // These snippet tests are intentionally skipped in CI jobs; see the README file in this directory
  18. // for instructions on running them manually.
  19. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  20. final class FunctionCallingSnippets: XCTestCase {
  21. override func setUpWithError() throws {
  22. try FirebaseApp.configureDefaultAppForSnippets()
  23. }
  24. override func tearDown() async throws {
  25. await FirebaseApp.deleteDefaultAppForSnippets()
  26. }
  27. func testFunctionCalling() async throws {
  28. // This function calls a hypothetical external API that returns
  29. // a collection of weather information for a given location on a given date.
  30. func fetchWeather(city: String, state: String, date: String) -> JSONObject {
  31. // TODO(developer): Write a standard function that would call an external weather API.
  32. // For demo purposes, this hypothetical response is hardcoded here in the expected format.
  33. return [
  34. "temperature": .number(38),
  35. "chancePrecipitation": .string("56%"),
  36. "cloudConditions": .string("partlyCloudy"),
  37. ]
  38. }
  39. let fetchWeatherTool = FunctionDeclaration(
  40. name: "fetchWeather",
  41. description: "Get the weather conditions for a specific city on a specific date.",
  42. parameters: [
  43. "location": .object(
  44. properties: [
  45. "city": .string(description: "The city of the location."),
  46. "state": .string(description: "The US state of the location."),
  47. ],
  48. description: """
  49. The name of the city and its state for which to get the weather. Only cities in the
  50. USA are supported.
  51. """
  52. ),
  53. "date": .string(
  54. description: """
  55. The date for which to get the weather. Date must be in the format: YYYY-MM-DD.
  56. """
  57. ),
  58. ]
  59. )
  60. // Initialize the Vertex AI service and the generative model.
  61. // Use a model that supports function calling, like a Gemini 1.5 model.
  62. let model = FirebaseAI.firebaseAI().generativeModel(
  63. modelName: "gemini-2.0-flash",
  64. // Provide the function declaration to the model.
  65. tools: [.functionDeclarations([fetchWeatherTool])]
  66. )
  67. let chat = model.startChat()
  68. let prompt = "What was the weather in Boston on October 17, 2024?"
  69. // Send the user's question (the prompt) to the model using multi-turn chat.
  70. let response = try await chat.sendMessage(prompt)
  71. var functionResponses = [FunctionResponsePart]()
  72. // When the model responds with one or more function calls, invoke the function(s).
  73. for functionCall in response.functionCalls {
  74. if functionCall.name == "fetchWeather" {
  75. // TODO(developer): Handle invalid arguments.
  76. guard case let .object(location) = functionCall.args["location"] else { fatalError() }
  77. guard case let .string(city) = location["city"] else { fatalError() }
  78. guard case let .string(state) = location["state"] else { fatalError() }
  79. guard case let .string(date) = functionCall.args["date"] else { fatalError() }
  80. functionResponses.append(FunctionResponsePart(
  81. name: functionCall.name,
  82. response: fetchWeather(city: city, state: state, date: date)
  83. ))
  84. }
  85. // TODO(developer): Handle other potential function calls, if any.
  86. }
  87. // Send the response(s) from the function back to the model so that the model can use it
  88. // to generate its final response.
  89. let finalResponse = try await chat.sendMessage(
  90. [ModelContent(role: "function", parts: functionResponses)]
  91. )
  92. // Log the text response.
  93. print(finalResponse.text ?? "No text in response.")
  94. }
  95. }