Skip to content

Commit 7249d3a

Browse files
authored
[VertexAI] More Swift 6 (#14499)
1 parent d61b46a commit 7249d3a

File tree

12 files changed

+55
-32
lines changed

12 files changed

+55
-32
lines changed

.github/workflows/vertexai.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,14 @@ jobs:
129129
strategy:
130130
matrix:
131131
target: [ios]
132-
os: [macos-14]
132+
os: [macos-14, macos-15]
133133
include:
134134
- os: macos-14
135135
xcode: Xcode_15.2
136+
warnings: --allow-warnings
137+
- os: macos-15
138+
xcode: Xcode_16.2
139+
warnings:
136140
runs-on: ${{ matrix.os }}
137141
steps:
138142
- uses: actions/checkout@v4
@@ -144,7 +148,7 @@ jobs:
144148
- name: Xcode
145149
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
146150
- name: Build and test
147-
run: scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb FirebaseVertexAI.podspec --platforms=${{ matrix.target }}
151+
run: scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb FirebaseVertexAI.podspec --platforms=${{ matrix.target }} ${{ matrix.warnings }}
148152

149153
sample:
150154
strategy:

FirebaseVertexAI/Sources/Chat.swift

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Foundation
1717
/// An object that represents a back-and-forth chat with a model, capturing the history and saving
1818
/// the context in memory between each message sent.
1919
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
20-
public class Chat {
20+
public final class Chat: Sendable {
2121
private let model: GenerativeModel
2222

2323
/// Initializes a new chat representing a 1:1 conversation between model and user.
@@ -26,9 +26,28 @@ public class Chat {
2626
self.history = history
2727
}
2828

29+
private let historyLock = NSLock()
30+
#if compiler(>=6)
31+
private nonisolated(unsafe) var _history: [ModelContent] = []
32+
#else
33+
private var _history: [ModelContent] = []
34+
#endif
2935
/// The previous content from the chat that has been successfully sent and received from the
3036
/// model. This will be provided to the model for each message sent as context for the discussion.
31-
public var history: [ModelContent]
37+
public var history: [ModelContent] {
38+
get {
39+
historyLock.withLock { _history }
40+
}
41+
set {
42+
historyLock.withLock { _history = newValue }
43+
}
44+
}
45+
46+
private func appendHistory(contentsOf: [ModelContent]) {
47+
historyLock.withLock {
48+
_history.append(contentsOf: contentsOf)
49+
}
50+
}
3251

3352
/// Sends a message using the existing history of this chat as context. If successful, the message
3453
/// and response will be added to the history. If unsuccessful, history will remain unchanged.
@@ -66,7 +85,7 @@ public class Chat {
6685
let toAdd = ModelContent(role: "model", parts: reply.parts)
6786

6887
// Append the request and successful result to history, then return the value.
69-
history.append(contentsOf: newContent)
88+
appendHistory(contentsOf: newContent)
7089
history.append(toAdd)
7190
return result
7291
}
@@ -88,16 +107,16 @@ public class Chat {
88107
@available(macOS 12.0, *)
89108
public func sendMessageStream(_ content: [ModelContent]) throws
90109
-> AsyncThrowingStream<GenerateContentResponse, Error> {
110+
// Ensure that the new content has the role set.
111+
let newContent: [ModelContent] = content.map(populateContentRole(_:))
112+
113+
// Send the history alongside the new message as context.
114+
let request = history + newContent
115+
let stream = try model.generateContentStream(request)
91116
return AsyncThrowingStream { continuation in
92117
Task {
93118
var aggregatedContent: [ModelContent] = []
94119

95-
// Ensure that the new content has the role set.
96-
let newContent: [ModelContent] = content.map(populateContentRole(_:))
97-
98-
// Send the history alongside the new message as context.
99-
let request = history + newContent
100-
let stream = try model.generateContentStream(request)
101120
do {
102121
for try await chunk in stream {
103122
// Capture any content that's streaming. This should be populated if there's no error.
@@ -115,12 +134,11 @@ public class Chat {
115134
}
116135

117136
// Save the request.
118-
history.append(contentsOf: newContent)
137+
appendHistory(contentsOf: newContent)
119138

120139
// Aggregate the content to add it to the history before we finish.
121-
let aggregated = aggregatedChunks(aggregatedContent)
122-
history.append(aggregated)
123-
140+
let aggregated = self.aggregatedChunks(aggregatedContent)
141+
self.history.append(aggregated)
124142
continuation.finish()
125143
}
126144
}

FirebaseVertexAI/Sources/FirebaseInfo.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414

1515
import Foundation
1616

17-
import FirebaseAppCheckInterop
18-
import FirebaseAuthInterop
19-
import FirebaseCore
17+
// TODO: Remove `@preconcurrency` when possible.
18+
@preconcurrency import FirebaseAppCheckInterop
19+
@preconcurrency import FirebaseAuthInterop
20+
@preconcurrency import FirebaseCore
2021

2122
/// Firebase data used by VertexAI
2223
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
23-
struct FirebaseInfo {
24+
struct FirebaseInfo: Sendable {
2425
let appCheck: AppCheckInterop?
2526
let auth: AuthInterop?
2627
let projectID: String

FirebaseVertexAI/Sources/FunctionCalling.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import Foundation
1919
/// This `FunctionDeclaration` is a representation of a block of code that can be used as a ``Tool``
2020
/// by the model and executed by the client.
2121
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
22-
public struct FunctionDeclaration {
22+
public struct FunctionDeclaration: Sendable {
2323
/// The name of the function.
2424
let name: String
2525

@@ -55,7 +55,7 @@ public struct FunctionDeclaration {
5555
/// A `Tool` is a piece of code that enables the system to interact with external systems to perform
5656
/// an action, or set of actions, outside of knowledge and scope of the model.
5757
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
58-
public struct Tool {
58+
public struct Tool: Sendable {
5959
/// A list of `FunctionDeclarations` available to the model.
6060
let functionDeclarations: [FunctionDeclaration]?
6161

@@ -89,7 +89,7 @@ public struct Tool {
8989

9090
/// Configuration for specifying function calling behavior.
9191
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
92-
public struct FunctionCallingConfig {
92+
public struct FunctionCallingConfig: Sendable {
9393
/// Defines the execution behavior for function calling by defining the execution mode.
9494
enum Mode: String {
9595
case auto = "AUTO"
@@ -135,7 +135,7 @@ public struct FunctionCallingConfig {
135135

136136
/// Tool configuration for any `Tool` specified in the request.
137137
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
138-
public struct ToolConfig {
138+
public struct ToolConfig: Sendable {
139139
let functionCallingConfig: FunctionCallingConfig?
140140

141141
public init(functionCallingConfig: FunctionCallingConfig? = nil) {

FirebaseVertexAI/Sources/GenerateContentRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
18-
struct GenerateContentRequest {
18+
struct GenerateContentRequest: Sendable {
1919
/// Model name.
2020
let model: String
2121
let contents: [ModelContent]

FirebaseVertexAI/Sources/GenerationConfig.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Foundation
1717
/// A struct defining model parameters to be used when sending generative AI
1818
/// requests to the backend model.
1919
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
20-
public struct GenerationConfig {
20+
public struct GenerationConfig: Sendable {
2121
/// Controls the degree of randomness in token selection.
2222
let temperature: Float?
2323

FirebaseVertexAI/Sources/GenerativeAIRequest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
18-
protocol GenerativeAIRequest: Encodable {
18+
protocol GenerativeAIRequest: Sendable, Encodable {
1919
associatedtype Response: Decodable
2020

2121
var url: URL { get }
@@ -26,7 +26,7 @@ protocol GenerativeAIRequest: Encodable {
2626
/// Configuration parameters for sending requests to the backend.
2727
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
2828
// TODO(#14405): Make the `apiVersion` constructor public in Firebase 12 with a default of `.v1`.
29-
public struct RequestOptions {
29+
public struct RequestOptions: Sendable {
3030
/// The request’s timeout interval in seconds; if not specified uses the default value for a
3131
/// `URLRequest`.
3232
let timeout: TimeInterval

FirebaseVertexAI/Sources/GenerativeAIService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct GenerativeAIService {
6969

7070
@available(macOS 12.0, *)
7171
func loadRequestStream<T: GenerativeAIRequest>(request: T)
72-
-> AsyncThrowingStream<T.Response, Error> {
72+
-> AsyncThrowingStream<T.Response, Error> where T: Sendable {
7373
return AsyncThrowingStream { continuation in
7474
Task {
7575
let urlRequest: URLRequest

FirebaseVertexAI/Sources/GenerativeModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import Foundation
1919
/// A type that represents a remote multimodal model (like Gemini), with the ability to generate
2020
/// content based on various input types.
2121
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
22-
public final class GenerativeModel {
22+
public final class GenerativeModel: Sendable {
2323
/// The resource name of the model in the backend; has the format "models/model-name".
2424
let modelResourceName: String
2525

FirebaseVertexAI/Sources/Safety.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public struct SafetyRating: Equatable, Hashable, Sendable {
146146
/// A type used to specify a threshold for harmful content, beyond which the model will return a
147147
/// fallback response instead of generated content.
148148
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
149-
public struct SafetySetting {
149+
public struct SafetySetting: Sendable {
150150
/// Block at and beyond a specified ``SafetyRating/HarmProbability``.
151151
public struct HarmBlockThreshold: EncodableProtoEnum, Sendable {
152152
enum Kind: String {

0 commit comments

Comments
 (0)