@capgo/capacitor-llm 8.0.19 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ // Copyright 2026 Google LLC
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // https://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import Foundation
16
+ import OSLog
17
+ import CLiteRTLM
18
+
19
+ /// Data struct to hold benchmark information. Note that this is an experimental API and may change
20
+ /// in the future.
21
+ public struct BenchmarkInfo {
22
+ /// The time in seconds to initialize the engine and the conversation.
23
+ public let initTimeInSecond: Double
24
+
25
+ /// The time in seconds to the first token.
26
+ public let timeToFirstTokenInSecond: Double
27
+
28
+ /// The number of tokens in the last prefill turn.
29
+ public let lastPrefillTokenCount: Int
30
+
31
+ /// The number of tokens in the last decode turn.
32
+ public let lastDecodeTokenCount: Int
33
+
34
+ /// The number of tokens processed per second in the last prefill.
35
+ public let lastPrefillTokensPerSecond: Double
36
+
37
+ /// The number of tokens processed per second in the last decode.
38
+ public let lastDecodeTokensPerSecond: Double
39
+ }
40
+
41
+ /// Runs a benchmark on the LiteRT-LM engine.
42
+ ///
43
+ /// **Note:** This function can take a significant amount of time depending on the model size,
44
+ /// device hardware, and the number of prefill and decode tokens. It is strongly recommended to
45
+ /// call this method on a background thread to avoid blocking the main thread.
46
+ ///
47
+ /// - Parameters:
48
+ /// - modelPath: The path to the model file.
49
+ /// - backend: The backend to use for the engine.
50
+ /// - prefillTokens: The number of tokens to prefill.
51
+ /// - decodeTokens: The number of tokens to decode.
52
+ /// - cacheDir: The directory for placing cache files. Set to ":nocache" to disable caching.
53
+ /// - Returns: The benchmark info.
54
+ /// - Throws: A `LiteRTLMError` if the engine fails to initialize or generate benchmark info.
55
+ public func benchmark(
56
+ modelPath: String,
57
+ backend: Backend,
58
+ prefillTokens: Int = 256,
59
+ decodeTokens: Int = 256,
60
+ cacheDir: String? = nil
61
+ ) async throws -> BenchmarkInfo {
62
+ ExperimentalFlags.optIntoExperimentalAPIs()
63
+
64
+ // temporarily enable benchmark flag so that Conversation API correctly passes checks later
65
+ let previousBenchmarkFlag = ExperimentalFlags.enableBenchmark
66
+ ExperimentalFlags.enableBenchmark = true
67
+ defer {
68
+ ExperimentalFlags.enableBenchmark = previousBenchmarkFlag
69
+ }
70
+
71
+ let engineConfig = try EngineConfig(
72
+ modelPath: modelPath,
73
+ backend: backend,
74
+ maxNumTokens: max(prefillTokens, decodeTokens) + 32,
75
+ cacheDir: cacheDir
76
+ )
77
+ let engine = Engine(engineConfig: engineConfig)
78
+ try await engine.initializeForBenchmark(prefillTokens: prefillTokens, decodeTokens: decodeTokens)
79
+
80
+ let conversation = try await engine.createConversation()
81
+ _ = try await conversation.sendMessage(Message("Engine ignore this message in this mode."))
82
+ return try conversation.getBenchmarkInfo()
83
+ }
@@ -0,0 +1,41 @@
1
+ // Copyright 2026 Google LLC
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // https://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import Foundation
16
+ import CLiteRTLM
17
+
18
+ /// Provides information about capabilities and features supported by a LiteRT-LM file.
19
+ public class Capabilities {
20
+ private let handle: OpaquePointer?
21
+
22
+ /// Loads a LiteRT-LM file from the given path.
23
+ /// Returns nil if the file cannot be opened.
24
+ public init?(modelPath: String) {
25
+ guard let handle = litert_lm_loaded_file_create(modelPath) else {
26
+ return nil
27
+ }
28
+ self.handle = handle
29
+ }
30
+
31
+ /// Checks if the loaded LiteRT-LM file supports speculative decoding.
32
+ public func hasSpeculativeDecodingSupport() -> Bool {
33
+ return litert_lm_loaded_file_has_speculative_decoding_support(handle)
34
+ }
35
+
36
+ deinit {
37
+ if let handle = handle {
38
+ litert_lm_loaded_file_delete(handle)
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,172 @@
1
+ // Copyright 2026 Google LLC
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // https://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import Foundation
16
+
17
+ /// The backend to use for the LiteRT-LM engine.
18
+ ///
19
+ /// Swift version of the C++'s `litert::lm::Backend`.
20
+ public enum Backend: Equatable {
21
+ /// CPU LiteRT backend.
22
+ case cpu(threadCount: Int? = nil)
23
+ /// GPU LiteRT backend.
24
+ case gpu
25
+
26
+ public var rawValue: String {
27
+ switch self {
28
+ case .cpu: return "cpu"
29
+ case .gpu: return "gpu"
30
+ }
31
+ }
32
+
33
+ public init?(rawValue: String) {
34
+ switch rawValue {
35
+ case "cpu": self = .cpu()
36
+ case "gpu": self = .gpu
37
+ default: return nil
38
+ }
39
+ }
40
+ }
41
+
42
+ /// Configuration for the LiteRT-LM engine.
43
+ public struct EngineConfig {
44
+ /// The file path to the LiteRT-LM model.
45
+ public let modelPath: String
46
+ /// The backend to use for the engine.
47
+ public let backend: Backend
48
+ /// The backend to use for the vision executor. If `nil`, vision executor will
49
+ /// not be initialized.
50
+ public let visionBackend: Backend?
51
+ /// The backend to use for the audio executor. If `nil`, audio executor will
52
+ /// not be initialized.
53
+ public let audioBackend: Backend?
54
+ /// The maximum number of the sum of input and output tokens. It is equivalent
55
+ /// to the size of the kv-cache. When `nil`, use the default value from the model or the engine.
56
+ public let maxNumTokens: Int?
57
+ /// The directory for placing cache files. It should be a directory where the
58
+ /// application has write access. If `nil`, it uses the directory of the `modelPath`.
59
+ public let cacheDir: String?
60
+
61
+ /// - Parameters:
62
+ /// - modelPath: The file path to the LiteRT-LM model.
63
+ /// - backend: The backend to use for the engine.
64
+ /// - visionBackend: The backend to use for the vision executor. If `nil`, vision executor
65
+ /// will not be initialized.
66
+ /// - audioBackend: The backend to use for the audio executor. If `nil`, audio executor will
67
+ /// not be initialized.
68
+ /// - maxNumTokens: The maximum number of the sum of input and output tokens. It is
69
+ /// equivalent to the size of the kv-cache. When `nil`, use the default value from the
70
+ /// model or the engine.
71
+ /// - cacheDir: The directory for placing cache files. It should be a directory where the
72
+ /// application has write access. If `nil`, it uses the directory of the `modelPath`.
73
+ /// - Throws: `LiteRTLMError` if `maxNumTokens` is less than or equal to 0.
74
+ public init(
75
+ modelPath: String, backend: Backend = .cpu(), visionBackend: Backend? = nil,
76
+ audioBackend: Backend? = nil,
77
+ maxNumTokens: Int? = nil,
78
+ cacheDir: String? = nil
79
+ ) throws {
80
+ if let maxNumTokens, maxNumTokens <= 0 {
81
+ throw LiteRTLMError.config(.invalidMaxNumTokens)
82
+ }
83
+ self.modelPath = modelPath
84
+ self.backend = backend
85
+ self.visionBackend = visionBackend
86
+ self.audioBackend = audioBackend
87
+ self.maxNumTokens = maxNumTokens
88
+ self.cacheDir = cacheDir
89
+ }
90
+ }
91
+
92
+ /// Configuration for the sampling process.
93
+ public struct SamplerConfig {
94
+
95
+ /// The number of most likely tokens (top logits) to consider at each step of sampling.
96
+ public let topK: Int
97
+
98
+ /// The cumulative probability threshold for nucleus sampling.
99
+ public let topP: Float
100
+
101
+ /// The temperature to use for sampling.
102
+ public let temperature: Float
103
+
104
+ /// The seed to use for randomization. Default to 0 (same default as engine code).
105
+ public let seed: Int
106
+
107
+ /// - Parameters:
108
+ /// - topK: The number of most likely tokens (top logits) to consider at each step of sampling.
109
+ /// - topP: The cumulative probability threshold for nucleus sampling.
110
+ /// - temperature: The temperature to use for sampling.
111
+ /// - seed: The seed to use for randomization. Default to 0 (same default as engine code).
112
+ /// - Throws: `LiteRTLMError` if `topK` is less than or equal to 0, `topP` is not in [0, 1], or
113
+ /// `temperature` is less than 0.
114
+ public init(
115
+ topK: Int,
116
+ topP: Float,
117
+ temperature: Float,
118
+ seed: Int = 0
119
+ ) throws {
120
+ if topK <= 0 {
121
+ throw LiteRTLMError.config(.invalidTopK)
122
+ }
123
+ if topP < 0 || topP > 1 {
124
+ throw LiteRTLMError.config(.invalidTopP)
125
+ }
126
+ if temperature < 0 {
127
+ throw LiteRTLMError.config(.invalidTemperature)
128
+ }
129
+
130
+ self.topK = topK
131
+ self.topP = topP
132
+ self.temperature = temperature
133
+ self.seed = seed
134
+ }
135
+ }
136
+
137
+ /// Configuration fo the LiteRT-LM `Conversation`.
138
+ public struct ConversationConfig {
139
+ // The system message to be used in the conversation.
140
+ public let systemMessage: Message?
141
+
142
+ // The initial messages to populate the conversation history.
143
+ public let initialMessages: [Message]
144
+
145
+ // The list of tool instances to be used in the conversation.
146
+ public let tools: [Tool]
147
+
148
+ // Configuration for the sampling process.
149
+ // If `nil`, then uses the engine's default values.
150
+ public let samplerConfig: SamplerConfig?
151
+
152
+ /// - Parameters:
153
+ /// - systemMessage: The system message to be used in the conversation.
154
+ /// - initialMessages: The initial messages to populate the conversation history.
155
+ /// - tools: The list of tool instances to be used in the conversation.
156
+ /// - samplerConfig: Configuration for the sampling process. If `nil`, then uses the engine's
157
+ /// default values.
158
+ public init(
159
+ systemMessage: Message? = nil,
160
+ initialMessages: [Message] = [],
161
+ tools: [Tool] = [],
162
+ samplerConfig: SamplerConfig? = nil
163
+ ) {
164
+ self.systemMessage = systemMessage.map { msg in
165
+ msg.role == .system
166
+ ? msg : Message(contents: msg.contents, role: .system, channels: msg.channels)
167
+ }
168
+ self.initialMessages = initialMessages
169
+ self.tools = tools
170
+ self.samplerConfig = samplerConfig
171
+ }
172
+ }