From 5c5a629d4a49cf4b5dda477ed0ec786d22f86dcc Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Mon, 11 Nov 2024 16:47:28 -0500 Subject: [PATCH 1/2] [Vertex AI] Add POC for initializing function declarations from JSON --- .../Sources/FunctionCalling.swift | 18 +++++++++++++++- .../Sources/Types/Internal/DataType.swift | 2 +- .../Sources/Types/Public/Schema.swift | 4 ++-- .../Snippets/FunctionCallingSnippets.swift | 21 +++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/FirebaseVertexAI/Sources/FunctionCalling.swift b/FirebaseVertexAI/Sources/FunctionCalling.swift index 60514a6f5f4..c5f98b5b091 100644 --- a/FirebaseVertexAI/Sources/FunctionCalling.swift +++ b/FirebaseVertexAI/Sources/FunctionCalling.swift @@ -48,6 +48,22 @@ public struct FunctionDeclaration { nullable: false ) } + + /// Constructs a new `FunctionDeclaration`. + /// + /// - Parameters: + /// - jsonString: A string representing a function declaration in JSON format; see the [REST + /// documentation]( https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/Tool#FunctionDeclaration) + /// for details. + public init(jsonString: String) throws { + guard let jsonData = jsonString.data(using: .utf8) else { + throw DecodingError.dataCorrupted(DecodingError.Context( + codingPath: [], + debugDescription: "Could not parse JSON string as UTF8." + )) + } + self = try JSONDecoder().decode(Self.self, from: jsonData) + } } /// A helper tool that the model may use when generating responses. @@ -146,7 +162,7 @@ public struct ToolConfig { // MARK: - Codable Conformance @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) -extension FunctionDeclaration: Encodable { +extension FunctionDeclaration: Codable { enum CodingKeys: String, CodingKey { case name case description diff --git a/FirebaseVertexAI/Sources/Types/Internal/DataType.swift b/FirebaseVertexAI/Sources/Types/Internal/DataType.swift index f995eacddf2..66cbf0b7d32 100644 --- a/FirebaseVertexAI/Sources/Types/Internal/DataType.swift +++ b/FirebaseVertexAI/Sources/Types/Internal/DataType.swift @@ -37,4 +37,4 @@ enum DataType: String { // MARK: - Codable Conformance -extension DataType: Encodable {} +extension DataType: Codable {} diff --git a/FirebaseVertexAI/Sources/Types/Public/Schema.swift b/FirebaseVertexAI/Sources/Types/Public/Schema.swift index a5fd2cdd0fb..ee0dd7d15ef 100644 --- a/FirebaseVertexAI/Sources/Types/Public/Schema.swift +++ b/FirebaseVertexAI/Sources/Types/Public/Schema.swift @@ -19,7 +19,7 @@ import Foundation /// These types can be objects, but also primitives and arrays. Represents a select subset of an /// [OpenAPI 3.0 schema object](https://spec.openapis.org/oas/v3.0.3#schema). @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) -public class Schema { +public final class Schema { /// Modifiers describing the expected format of a string `Schema`. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) public struct StringFormat: EncodableProtoEnum { @@ -319,7 +319,7 @@ public class Schema { // MARK: - Codable Conformance @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) -extension Schema: Encodable { +extension Schema: Codable { enum CodingKeys: String, CodingKey { case dataType = "type" case format diff --git a/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift b/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift index 492574dc11d..29cc228ada3 100644 --- a/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift +++ b/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift @@ -29,6 +29,27 @@ final class FunctionCallingSnippets: XCTestCase { await FirebaseApp.deleteDefaultAppForSnippets() } + func testFunctionDeclaration_jsonString() throws { + let json = """ + { + "name": "getWeather", + "description": "gets the weather for a requested city", + "parameters": { + "type": "OBJECT", + "properties": { + "city": { + "type": "STRING" + } + } + } + } + """ + + let functionDeclaration = try FunctionDeclaration(jsonString: json) + + print(functionDeclaration) + } + func testFunctionCalling() async throws { // This function calls a hypothetical external API that returns // a collection of weather information for a given location on a given date. From 5a232685ddc4b8a12b35afc99a86754bf120fbc6 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Mon, 11 Nov 2024 17:40:09 -0500 Subject: [PATCH 2/2] Add Equatable conformance and check equality in test --- .../Sources/FunctionCalling.swift | 2 +- .../Sources/Types/Public/Schema.swift | 12 +++++ .../Snippets/FunctionCallingSnippets.swift | 21 -------- .../Unit/Types/FunctionDeclarationTests.swift | 48 +++++++++++++++++++ 4 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 FirebaseVertexAI/Tests/Unit/Types/FunctionDeclarationTests.swift diff --git a/FirebaseVertexAI/Sources/FunctionCalling.swift b/FirebaseVertexAI/Sources/FunctionCalling.swift index c5f98b5b091..e488debe3be 100644 --- a/FirebaseVertexAI/Sources/FunctionCalling.swift +++ b/FirebaseVertexAI/Sources/FunctionCalling.swift @@ -19,7 +19,7 @@ import Foundation /// This `FunctionDeclaration` is a representation of a block of code that can be used as a ``Tool`` /// by the model and executed by the client. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) -public struct FunctionDeclaration { +public struct FunctionDeclaration: Equatable { /// The name of the function. let name: String diff --git a/FirebaseVertexAI/Sources/Types/Public/Schema.swift b/FirebaseVertexAI/Sources/Types/Public/Schema.swift index ee0dd7d15ef..fa456fa67cf 100644 --- a/FirebaseVertexAI/Sources/Types/Public/Schema.swift +++ b/FirebaseVertexAI/Sources/Types/Public/Schema.swift @@ -331,3 +331,15 @@ extension Schema: Codable { case requiredProperties = "required" } } + +// MARK: - Equatable Conformance + +@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) +extension Schema: Equatable { + public static func == (lhs: Schema, rhs: Schema) -> Bool { + return lhs.dataType == rhs.dataType && lhs.format == rhs.format + && lhs.description == rhs.description && lhs.nullable == rhs.nullable + && lhs.enumValues == rhs.enumValues && lhs.items == rhs.items + && lhs.properties == rhs.properties && lhs.requiredProperties == rhs.requiredProperties + } +} diff --git a/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift b/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift index 29cc228ada3..492574dc11d 100644 --- a/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift +++ b/FirebaseVertexAI/Tests/Unit/Snippets/FunctionCallingSnippets.swift @@ -29,27 +29,6 @@ final class FunctionCallingSnippets: XCTestCase { await FirebaseApp.deleteDefaultAppForSnippets() } - func testFunctionDeclaration_jsonString() throws { - let json = """ - { - "name": "getWeather", - "description": "gets the weather for a requested city", - "parameters": { - "type": "OBJECT", - "properties": { - "city": { - "type": "STRING" - } - } - } - } - """ - - let functionDeclaration = try FunctionDeclaration(jsonString: json) - - print(functionDeclaration) - } - func testFunctionCalling() async throws { // This function calls a hypothetical external API that returns // a collection of weather information for a given location on a given date. diff --git a/FirebaseVertexAI/Tests/Unit/Types/FunctionDeclarationTests.swift b/FirebaseVertexAI/Tests/Unit/Types/FunctionDeclarationTests.swift new file mode 100644 index 00000000000..df87c9dde74 --- /dev/null +++ b/FirebaseVertexAI/Tests/Unit/Types/FunctionDeclarationTests.swift @@ -0,0 +1,48 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import FirebaseVertexAI +import XCTest + +@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) +final class FunctionDeclarationTests: XCTestCase { + func testFunctionDeclaration_jsonString() throws { + let json = """ + { + "name": "getWeather", + "description": "gets the weather for a requested city", + "parameters": { + "type": "OBJECT", + "properties": { + "city": { + "type": "STRING", + "nullable": false + } + }, + "nullable": false, + "required": ["city"] + } + } + """ + let expectedFunctionDeclaration = FunctionDeclaration( + name: "getWeather", + description: "gets the weather for a requested city", + parameters: ["city": .string()] + ) + + let functionDeclaration = try FunctionDeclaration(jsonString: json) + + XCTAssertEqual(functionDeclaration, expectedFunctionDeclaration) + } +}