diff --git a/FirebaseVertexAI/CHANGELOG.md b/FirebaseVertexAI/CHANGELOG.md index 7390ef0c57c..c048c49ebc4 100644 --- a/FirebaseVertexAI/CHANGELOG.md +++ b/FirebaseVertexAI/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- [added] Added `Decodable` conformance for `FunctionResponse`. (#13606) + # 11.2.0 - [fixed] Resolved a decoding error for citations without a `uri` and added support for decoding `title` fields, which were previously ignored. (#13518) diff --git a/FirebaseVertexAI/Sources/FunctionCalling.swift b/FirebaseVertexAI/Sources/FunctionCalling.swift index 95087f16ce1..3c88279c6df 100644 --- a/FirebaseVertexAI/Sources/FunctionCalling.swift +++ b/FirebaseVertexAI/Sources/FunctionCalling.swift @@ -193,4 +193,4 @@ extension FunctionCallingConfig.Mode: Encodable {} extension ToolConfig: Encodable {} -extension FunctionResponse: Encodable {} +extension FunctionResponse: Codable {} diff --git a/FirebaseVertexAI/Sources/ModelContent.swift b/FirebaseVertexAI/Sources/ModelContent.swift index f8695aafc47..3262a4eba15 100644 --- a/FirebaseVertexAI/Sources/ModelContent.swift +++ b/FirebaseVertexAI/Sources/ModelContent.swift @@ -179,10 +179,13 @@ extension ModelContent.Part: Codable { self = .data(mimetype: mimetype, bytes) } else if values.contains(.functionCall) { self = try .functionCall(values.decode(FunctionCall.self, forKey: .functionCall)) + } else if values.contains(.functionResponse) { + self = try .functionResponse(values.decode(FunctionResponse.self, forKey: .functionResponse)) } else { - throw DecodingError.dataCorrupted(.init( - codingPath: [CodingKeys.text, CodingKeys.inlineData], - debugDescription: "No text, inline data or function call was found." + let unexpectedKeys = values.allKeys.map { $0.stringValue } + throw DecodingError.dataCorrupted(DecodingError.Context( + codingPath: values.codingPath, + debugDescription: "Unexpected ModelContent.Part type(s): \(unexpectedKeys)" )) } } diff --git a/FirebaseVertexAI/Tests/Unit/ModelContentTests.swift b/FirebaseVertexAI/Tests/Unit/ModelContentTests.swift index 8eb02045361..67175af739b 100644 --- a/FirebaseVertexAI/Tests/Unit/ModelContentTests.swift +++ b/FirebaseVertexAI/Tests/Unit/ModelContentTests.swift @@ -19,6 +19,7 @@ import XCTest @available(iOS 15.0, macOS 11.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) final class ModelContentTests: XCTestCase { + let decoder = JSONDecoder() let encoder = JSONEncoder() override func setUp() { @@ -27,7 +28,35 @@ final class ModelContentTests: XCTestCase { ) } - // MARK: ModelContent.Part Encoding + // MARK: - ModelContent.Part Decoding + + func testDecodeFunctionResponsePart() throws { + let functionName = "test-function-name" + let resultParameter = "test-result-parameter" + let resultValue = "test-result-value" + let json = """ + { + "functionResponse" : { + "name" : "\(functionName)", + "response" : { + "\(resultParameter)" : "\(resultValue)" + } + } + } + """ + let jsonData = try XCTUnwrap(json.data(using: .utf8)) + + let part = try decoder.decode(ModelContent.Part.self, from: jsonData) + + guard case let .functionResponse(functionResponse) = part else { + XCTFail("Decoded Part was not a FunctionResponse.") + return + } + XCTAssertEqual(functionResponse.name, functionName) + XCTAssertEqual(functionResponse.response, [resultParameter: .string(resultValue)]) + } + + // MARK: - ModelContent.Part Encoding func testEncodeFileDataPart() throws { let mimeType = "image/jpeg"