33import Foundation
44import MLXLMCommon
55
6+ // TODO dkoski -- make the example code be synchronous?
7+
68/// Container for embedder models that guarantees single threaded access.
79///
810/// Wrap models used by e.g. the UI in a ModelContainer. Callers can access
@@ -27,52 +29,52 @@ import MLXLMCommon
2729/// }
2830/// ```
2931public final class EmbedderModelContainer : Sendable {
30- private let context : SerialAccessContainer < EmbedderModelContext >
32+ private let context : EmbedderModelContext
3133
3234 public var configuration : ModelConfiguration {
33- get async {
34- await context. read { $0. configuration }
35- }
35+ context. configuration
3636 }
3737
3838 public var tokenizer : Tokenizer {
39- get async {
40- await context. read { $0. tokenizer }
41- }
39+ context. tokenizer
4240 }
4341
4442 public var poolingStrategy : Pooling . Strategy {
45- get async {
46- await context. read { $0. pooling. strategy }
47- }
43+ context. pooling. strategy
4844 }
4945
5046 public init ( context: consuming EmbedderModelContext ) {
51- self . context = . init ( context)
47+ self . context = context
5248 }
5349
5450 /// Perform an action on the ``EmbedderModelContext``.
5551 /// Callers _must_ eval any `MLXArray` before returning as `MLXArray` is not `Sendable`.
5652 ///
57- /// - Note: The closure receives `EmbedderModelContext` which is not `Sendable`. This is intentional -
58- /// the closure runs within the actor's isolation, ensuring thread-safe access to the model.
5953 /// - Note: The `sending` keyword indicates the return value is transferred (not shared) across
6054 /// isolation boundaries, allowing non-Sendable types to be safely returned.
6155 public func perform< R: Sendable > (
6256 _ action: @Sendable ( EmbedderModelContext) async throws -> sending R
6357 ) async rethrows -> sending R {
64- try await context. read {
65- try await action ( $0)
66- }
58+ try await action ( context)
59+ }
60+
61+ /// Perform an action on the ``EmbedderModelContext``.
62+ ///
63+ /// This is the synchronous form of ``perform(_:)`` and has
64+ /// fewer restrictions.
65+ public func perform< R> (
66+ _ action: @Sendable ( EmbedderModelContext) throws -> R
67+ ) rethrows -> R {
68+ try action ( context)
6769 }
6870
6971 @available ( * , deprecated, message: " use perform(_: (EmbedderModelContext) -> R) instead " )
7072 public func perform< R: Sendable > (
7173 _ action: @Sendable ( EmbeddingModel, Tokenizer, Pooling) async throws -> sending R
7274 ) async rethrows -> sending R {
73- try await context . read {
74- try await action ( $0 . model, $0 . tokenizer, $0 . pooling)
75- }
75+ try await action (
76+ context . model, context . tokenizer, context . pooling
77+ )
7678 }
7779
7880 /// Perform an action on the ``EmbedderModelContext`` with additional (non `Sendable`) context values.
@@ -83,32 +85,32 @@ public final class EmbedderModelContainer: Sendable {
8385 _ action: @Sendable ( EmbedderModelContext, V) async throws -> R
8486 ) async rethrows -> sending R {
8587 let values = SendableBox ( values)
86- return try await context. read {
87- try await action ( $0, values. consume ( ) )
88- }
88+ return try await action ( context, values. consume ( ) )
8989 }
9090
9191 /// Update the owned `EmbedderModelContext`.
9292 /// - Parameter action: update action
93+ @available (
94+ * , unavailable,
95+ message: " mutate EmbedderModelContext before passing to EmbedderModelContainer "
96+ )
9397 public func update( _ action: @Sendable ( inout EmbedderModelContext ) -> Void ) async {
94- await context. update {
95- action ( & $0)
96- }
98+ fatalError ( " update not supported " )
9799 }
98100
99101 // MARK: - Thread-safe convenience methods
100102
101103 /// The resolved local model directory for the loaded container.
102104 public var modelDirectory : URL {
103- get async throws {
104- try ( await configuration) . modelDirectory
105+ get throws {
106+ try configuration. modelDirectory
105107 }
106108 }
107109
108110 /// The resolved local tokenizer directory for the loaded container.
109111 public var tokenizerDirectory : URL {
110- get async throws {
111- try ( await configuration) . tokenizerDirectory
112+ get throws {
113+ try configuration. tokenizerDirectory
112114 }
113115 }
114116}
0 commit comments