Skip to content

Commit 6148c13

Browse files
authored
[Vertex AI] Make uri optional in Citation and add title field (#13520)
1 parent 22099aa commit 6148c13

File tree

4 files changed

+54
-17
lines changed

4 files changed

+54
-17
lines changed

FirebaseVertexAI/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Unreleased
2+
- [fixed] Resolved a decoding error for citations without a `uri` and added
3+
support for decoding `title` fields, which were previously ignored. (#13518)
4+
15
# 10.29.0
26
- [feature] Added community support for watchOS. (#13215)
37

FirebaseVertexAI/Sources/GenerateContentResponse.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,11 @@ public struct Citation {
125125
/// The exclusive end of a sequence in a model response that derives from a cited source.
126126
public let endIndex: Int
127127

128-
/// A link to the cited source.
129-
public let uri: String
128+
/// A link to the cited source, if available.
129+
public let uri: String?
130+
131+
/// The title of the cited source, if available.
132+
public let title: String?
130133

131134
/// The license the cited source work is distributed under, if specified.
132135
public let license: String?
@@ -303,15 +306,30 @@ extension Citation: Decodable {
303306
case startIndex
304307
case endIndex
305308
case uri
309+
case title
306310
case license
307311
}
308312

309313
public init(from decoder: any Decoder) throws {
310314
let container = try decoder.container(keyedBy: CodingKeys.self)
311315
startIndex = try container.decodeIfPresent(Int.self, forKey: .startIndex) ?? 0
312316
endIndex = try container.decode(Int.self, forKey: .endIndex)
313-
uri = try container.decode(String.self, forKey: .uri)
314-
license = try container.decodeIfPresent(String.self, forKey: .license)
317+
if let uri = try container.decodeIfPresent(String.self, forKey: .uri), !uri.isEmpty {
318+
self.uri = uri
319+
} else {
320+
uri = nil
321+
}
322+
if let title = try container.decodeIfPresent(String.self, forKey: .title), !title.isEmpty {
323+
self.title = title
324+
} else {
325+
title = nil
326+
}
327+
if let license = try container.decodeIfPresent(String.self, forKey: .license),
328+
!license.isEmpty {
329+
self.license = license
330+
} else {
331+
license = nil
332+
}
315333
}
316334
}
317335

FirebaseVertexAI/Tests/Unit/ChatTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ final class ChatTests: XCTestCase {
3939

4040
// Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
4141
// https://developer.apple.com/documentation/foundation/urlprotocol for details.
42-
guard #unavailable(watchOS 2) else {
42+
#if os(watchOS)
4343
throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
44-
}
44+
#endif // os(watchOS)
4545
MockURLProtocol.requestHandler = { request in
4646
let response = HTTPURLResponse(
4747
url: request.url!,

FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,20 @@ final class GenerativeModelTests: XCTestCase {
116116
XCTAssertEqual(citationSource1.uri, "https://www.example.com/some-citation-1")
117117
XCTAssertEqual(citationSource1.startIndex, 0)
118118
XCTAssertEqual(citationSource1.endIndex, 128)
119+
XCTAssertNil(citationSource1.title)
119120
XCTAssertNil(citationSource1.license)
120121
let citationSource2 = try XCTUnwrap(citationMetadata.citationSources[1])
121-
XCTAssertEqual(citationSource2.uri, "https://www.example.com/some-citation-2")
122+
XCTAssertEqual(citationSource2.title, "some-citation-2")
122123
XCTAssertEqual(citationSource2.startIndex, 130)
123124
XCTAssertEqual(citationSource2.endIndex, 265)
125+
XCTAssertNil(citationSource2.uri)
124126
XCTAssertNil(citationSource2.license)
125127
let citationSource3 = try XCTUnwrap(citationMetadata.citationSources[2])
126128
XCTAssertEqual(citationSource3.uri, "https://www.example.com/some-citation-3")
127129
XCTAssertEqual(citationSource3.startIndex, 272)
128130
XCTAssertEqual(citationSource3.endIndex, 431)
129131
XCTAssertEqual(citationSource3.license, "mit")
132+
XCTAssertNil(citationSource3.title)
130133
}
131134

132135
func testGenerateContent_success_quoteReply() async throws {
@@ -951,13 +954,25 @@ final class GenerativeModelTests: XCTestCase {
951954
XCTAssertEqual(lastCandidate.finishReason, .stop)
952955
XCTAssertEqual(citations.count, 6)
953956
XCTAssertTrue(citations
954-
.contains(where: {
955-
$0.startIndex == 574 && $0.endIndex == 705 && !$0.uri.isEmpty && $0.license == ""
956-
}))
957+
.contains {
958+
$0.startIndex == 0 && $0.endIndex == 128
959+
&& $0.uri == "https://www.example.com/some-citation-1" && $0.title == nil
960+
&& $0.license == nil
961+
})
957962
XCTAssertTrue(citations
958-
.contains(where: {
959-
$0.startIndex == 899 && $0.endIndex == 1026 && !$0.uri.isEmpty && $0.license == ""
960-
}))
963+
.contains {
964+
$0.startIndex == 130 && $0.endIndex == 265 && $0.uri == nil
965+
&& $0.title == "some-citation-2" && $0.license == nil
966+
})
967+
XCTAssertTrue(citations
968+
.contains {
969+
$0.startIndex == 272 && $0.endIndex == 431
970+
&& $0.uri == "https://www.example.com/some-citation-3" && $0.title == nil
971+
&& $0.license == "mit"
972+
})
973+
XCTAssertFalse(citations.contains { $0.uri?.isEmpty ?? false })
974+
XCTAssertFalse(citations.contains { $0.title?.isEmpty ?? false })
975+
XCTAssertFalse(citations.contains { $0.license?.isEmpty ?? false })
961976
}
962977

963978
func testGenerateContentStream_appCheck_validToken() async throws {
@@ -1283,9 +1298,9 @@ final class GenerativeModelTests: XCTestCase {
12831298
)) {
12841299
// Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
12851300
// https://developer.apple.com/documentation/foundation/urlprotocol for details.
1286-
guard #unavailable(watchOS 2) else {
1301+
#if os(watchOS)
12871302
throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
1288-
}
1303+
#endif // os(watchOS)
12891304
return { request in
12901305
// This is *not* an HTTPURLResponse
12911306
let response = URLResponse(
@@ -1309,9 +1324,9 @@ final class GenerativeModelTests: XCTestCase {
13091324
)) {
13101325
// Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
13111326
// https://developer.apple.com/documentation/foundation/urlprotocol for details.
1312-
guard #unavailable(watchOS 2) else {
1327+
#if os(watchOS)
13131328
throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
1314-
}
1329+
#endif // os(watchOS)
13151330
let fileURL = try XCTUnwrap(Bundle.module.url(forResource: name, withExtension: ext))
13161331
return { request in
13171332
let requestURL = try XCTUnwrap(request.url)

0 commit comments

Comments
 (0)