@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.
- package/CapgoCapacitorLlm.podspec +1 -9
- package/Package.swift +21 -3
- package/README.md +137 -163
- package/android/build.gradle +3 -2
- package/android/src/main/java/ee/forgr/capgo_llm/LLM.java +605 -283
- package/android/src/main/java/ee/forgr/capgo_llm/LLMPlugin.java +36 -63
- package/dist/docs.json +66 -35
- package/dist/esm/definitions.d.ts +28 -20
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +6 -0
- package/dist/esm/web.js +68 -39
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +68 -39
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +68 -39
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/LLMPlugin/LLMPlugin.swift +384 -613
- package/ios/Sources/LiteRTLM/Benchmark.swift +83 -0
- package/ios/Sources/LiteRTLM/Capabilities.swift +41 -0
- package/ios/Sources/LiteRTLM/Config.swift +172 -0
- package/ios/Sources/LiteRTLM/Conversation.swift +472 -0
- package/ios/Sources/LiteRTLM/Engine.swift +208 -0
- package/ios/Sources/LiteRTLM/ExperimentalFlags.swift +142 -0
- package/ios/Sources/LiteRTLM/LiteRTLMError.swift +156 -0
- package/ios/Sources/LiteRTLM/Message.swift +225 -0
- package/ios/Sources/LiteRTLM/Tool.swift +291 -0
- package/ios/Sources/LiteRTLM/ToolManager.swift +152 -0
- package/package.json +6 -5
|
@@ -0,0 +1,291 @@
|
|
|
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
|
+
/// A protocol that defines what types can be used as parameters in a LiteRT-LM tool.
|
|
18
|
+
public protocol ToolParameterValue: Codable {
|
|
19
|
+
/// Returns the JSON schema representation for this type.
|
|
20
|
+
static func getJsonSchema() -> [String: Any]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
extension String: ToolParameterValue {
|
|
24
|
+
public static func getJsonSchema() -> [String: Any] { ["type": "string"] }
|
|
25
|
+
}
|
|
26
|
+
extension Int: ToolParameterValue {
|
|
27
|
+
public static func getJsonSchema() -> [String: Any] { ["type": "integer"] }
|
|
28
|
+
}
|
|
29
|
+
extension Bool: ToolParameterValue {
|
|
30
|
+
public static func getJsonSchema() -> [String: Any] { ["type": "boolean"] }
|
|
31
|
+
}
|
|
32
|
+
extension Double: ToolParameterValue {
|
|
33
|
+
public static func getJsonSchema() -> [String: Any] { ["type": "number"] }
|
|
34
|
+
}
|
|
35
|
+
extension Float: ToolParameterValue {
|
|
36
|
+
public static func getJsonSchema() -> [String: Any] { ["type": "number"] }
|
|
37
|
+
}
|
|
38
|
+
extension Optional: ToolParameterValue where Wrapped: ToolParameterValue {
|
|
39
|
+
public static func getJsonSchema() -> [String: Any] {
|
|
40
|
+
var schema = Wrapped.getJsonSchema()
|
|
41
|
+
schema["nullable"] = true
|
|
42
|
+
return schema
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
extension Array: ToolParameterValue where Element: ToolParameterValue {
|
|
46
|
+
public static func getJsonSchema() -> [String: Any] {
|
|
47
|
+
["type": "array", "items": Element.getJsonSchema()]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// A non-generic protocol allowing `ToolManager` to inspect `ToolParam` properties at runtime.
|
|
52
|
+
protocol ToolParamProtocol {
|
|
53
|
+
var description: String { get }
|
|
54
|
+
var isRequired: Bool { get }
|
|
55
|
+
|
|
56
|
+
/// Type-erased access to the underlying storage (default value), used for reflection.
|
|
57
|
+
/// This allows `ToolManager` to read default values without knowing the specific generic `Value` type.
|
|
58
|
+
var wrappedValueAny: Any? { get }
|
|
59
|
+
|
|
60
|
+
func getFullSchema() -> [String: Any]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// A property wrapper that defines a parameter for a LiteRT-LM tool.
|
|
64
|
+
///
|
|
65
|
+
/// Use this property wrapper to mark properties of a `Tool` as parameters that can be passed to
|
|
66
|
+
/// the tool. The `description` will be used to generate the tool's schema.
|
|
67
|
+
///
|
|
68
|
+
/// Example:
|
|
69
|
+
/// ```
|
|
70
|
+
/// // Example of required parameters:
|
|
71
|
+
/// @ToolParam(description: "The title of the event.")
|
|
72
|
+
/// var title: String
|
|
73
|
+
/// @ToolParam(description: "The date and time of the event in format YYYY-MM-DDTHH:MM:SS.")
|
|
74
|
+
/// var dateTime: String
|
|
75
|
+
///
|
|
76
|
+
/// // Example of a parameter with a default value:
|
|
77
|
+
/// @ToolParam(description: "The duration of the event in minutes. Default is 30.")
|
|
78
|
+
/// var durationMinutes: Int = 30
|
|
79
|
+
///
|
|
80
|
+
/// // Example of an optional parameter:
|
|
81
|
+
/// @ToolParam(description: "The topic of the event.")
|
|
82
|
+
/// var topic: String?
|
|
83
|
+
/// ```
|
|
84
|
+
@propertyWrapper
|
|
85
|
+
public struct ToolParam<Value: ToolParameterValue>: Decodable, ToolParamProtocol {
|
|
86
|
+
|
|
87
|
+
/// Holds the actual value of the parameter.
|
|
88
|
+
private var storage: Value?
|
|
89
|
+
|
|
90
|
+
/// A description of the parameter. This is used to to describe the parameter in the schema.
|
|
91
|
+
public let description: String
|
|
92
|
+
|
|
93
|
+
/// Whether the parameter has a default value provided by the developer when declaring the tool.
|
|
94
|
+
private let hasDefaultValue: Bool
|
|
95
|
+
|
|
96
|
+
/// Initializes a parameter with an optional default value.
|
|
97
|
+
public init(wrappedValue: Value? = nil, description: String) {
|
|
98
|
+
self.storage = wrappedValue
|
|
99
|
+
self.description = description
|
|
100
|
+
self.hasDefaultValue = wrappedValue != nil
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// The value of the parameter.
|
|
104
|
+
///
|
|
105
|
+
/// Returns the stored value if available (from a default or decoded input).
|
|
106
|
+
/// Returns `nil` if no value is present and the type `Value` is Optional.
|
|
107
|
+
public var wrappedValue: Value {
|
|
108
|
+
get {
|
|
109
|
+
// If we have data, return it.
|
|
110
|
+
if let value = storage {
|
|
111
|
+
return value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If storage is empty, check if it's an Optional.
|
|
115
|
+
if let nilValue = Optional<Any>.none as? Value {
|
|
116
|
+
return nilValue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// It's not optional and we have no data. Throw an error.
|
|
120
|
+
fatalError("ToolParam of type \(Value.self) was not set and has no default value.")
|
|
121
|
+
}
|
|
122
|
+
set { storage = newValue }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Provides type-erased access to the underlying storage of the parameter.
|
|
126
|
+
///
|
|
127
|
+
/// Unlike `wrappedValue`, this property returns `nil` if no value is set, instead of crashing.
|
|
128
|
+
/// This is essential for `ToolManager` to safely inspect default values via reflection (Mirror)
|
|
129
|
+
/// before the decoding process occurs.
|
|
130
|
+
var wrappedValueAny: Any? {
|
|
131
|
+
return storage
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
func getFullSchema() -> [String: Any] {
|
|
135
|
+
var schema = Value.getJsonSchema()
|
|
136
|
+
if !description.isEmpty {
|
|
137
|
+
schema["description"] = description
|
|
138
|
+
}
|
|
139
|
+
return schema
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
var isRequired: Bool {
|
|
143
|
+
// If `nil` can be cast to `Value`, then `Value` must be an Optional type.
|
|
144
|
+
let isOptional = Optional<Any>.none as? Value != nil
|
|
145
|
+
return !isOptional && !hasDefaultValue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Decodes the parameter value from the decoder.
|
|
149
|
+
///
|
|
150
|
+
/// This initializer is required to conform to the `Decodable` protocol. Since the `Tool` protocol
|
|
151
|
+
/// inherits from `Decodable`, any property wrapper used within a `Tool` must also be `Decodable`.
|
|
152
|
+
///
|
|
153
|
+
/// When `ToolManager` executes a tool, it uses `JSONDecoder` to decode the JSON arguments
|
|
154
|
+
/// provided by the model into a `Tool` instance. This initializer is called automatically during
|
|
155
|
+
/// that process to populate the `ToolParam`'s internal storage with the value provided by the
|
|
156
|
+
/// model.
|
|
157
|
+
public init(from decoder: Decoder) throws {
|
|
158
|
+
let container = try decoder.singleValueContainer()
|
|
159
|
+
|
|
160
|
+
if container.decodeNil() {
|
|
161
|
+
if let nilValue = Optional<Any>.none as? Value {
|
|
162
|
+
self.storage = nilValue
|
|
163
|
+
} else {
|
|
164
|
+
throw DecodingError.valueNotFound(
|
|
165
|
+
Value.self,
|
|
166
|
+
.init(
|
|
167
|
+
codingPath: decoder.codingPath,
|
|
168
|
+
debugDescription: "Received null for non-optional parameter"))
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
self.storage = try container.decode(Value.self)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
self.description = ""
|
|
175
|
+
self.hasDefaultValue = true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// Example of how to define tools:
|
|
180
|
+
/// - Adopt the `Tool` protocol to define a struct/class as a tool.
|
|
181
|
+
/// - Use `@ToolParam` to define the parameters of the tool.
|
|
182
|
+
/// - The allowed parameter types are: String, Int, Bool, Float, Double, and Array of them.
|
|
183
|
+
/// - The return type of `run()` is `Any` and will be converted to JSON/String back to the model.
|
|
184
|
+
/// - Use the Swift Optional type (e.g., `String?`) to indicate that a parameter is optional.
|
|
185
|
+
///
|
|
186
|
+
/// ```swift
|
|
187
|
+
/// struct GetCurrentWeatherTool: Tool {
|
|
188
|
+
/// static let name = "get_current_weather"
|
|
189
|
+
/// static let description = "Get the current weather"
|
|
190
|
+
///
|
|
191
|
+
/// @ToolParam(description: "The city and state, e.g. San Francisco, CA")
|
|
192
|
+
/// var location: String
|
|
193
|
+
///
|
|
194
|
+
/// @ToolParam(description: "The temperature unit to use")
|
|
195
|
+
/// var unit: String = "celsius"
|
|
196
|
+
///
|
|
197
|
+
/// func run() async throws -> Any {
|
|
198
|
+
/// // In a real application, you would call a weather API here. This is just an example.
|
|
199
|
+
/// let temperature = getTemperature(for: location, in: unit)
|
|
200
|
+
///
|
|
201
|
+
/// return [
|
|
202
|
+
/// "temperature": temperature,
|
|
203
|
+
/// "unit": unit,
|
|
204
|
+
/// ]
|
|
205
|
+
/// }
|
|
206
|
+
/// }
|
|
207
|
+
/// ```
|
|
208
|
+
/// A protocol a struct/class must conform to be used as a tool by the LiteRT-LM model.
|
|
209
|
+
///
|
|
210
|
+
/// - SeeAlso: `ToolParam` for defining parameters of the tool.
|
|
211
|
+
public protocol Tool: Decodable {
|
|
212
|
+
/// The unique name of the tool.
|
|
213
|
+
static var name: String { get }
|
|
214
|
+
/// A description of what the tool does.
|
|
215
|
+
static var description: String { get }
|
|
216
|
+
|
|
217
|
+
/// A required zero-argument initializer.
|
|
218
|
+
init()
|
|
219
|
+
/// The logic to execute when the tool is called.
|
|
220
|
+
func run() async throws -> Any
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
extension Tool {
|
|
224
|
+
/// Generates an OpenAPI-compliant JSON schema for the tool.
|
|
225
|
+
public func getSchema() -> [String: Any] {
|
|
226
|
+
var properties: [String: Any] = [:]
|
|
227
|
+
var requiredFields: [String] = []
|
|
228
|
+
|
|
229
|
+
let mirror = Mirror(reflecting: self)
|
|
230
|
+
let useSnakeCase = ExperimentalFlags.convertCamelToSnakeCaseInToolDescription
|
|
231
|
+
|
|
232
|
+
for child in mirror.children {
|
|
233
|
+
guard let label = child.label else { continue }
|
|
234
|
+
|
|
235
|
+
// Clean the name (remove the _ prefix and handle snake_case)
|
|
236
|
+
let cleanName = label.cleanPropertyLabel(useSnakeCase: useSnakeCase)
|
|
237
|
+
|
|
238
|
+
// Check if this property is a ToolParam
|
|
239
|
+
if let param = child.value as? ToolParamProtocol {
|
|
240
|
+
properties[cleanName] = param.getFullSchema()
|
|
241
|
+
if param.isRequired {
|
|
242
|
+
requiredFields.append(cleanName)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let toolName = useSnakeCase ? Self.name.camelToSnakeCase() : Self.name
|
|
248
|
+
|
|
249
|
+
var parameters: [String: Any] = [
|
|
250
|
+
"type": "object",
|
|
251
|
+
"properties": properties,
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
if !requiredFields.isEmpty {
|
|
255
|
+
parameters["required"] = requiredFields
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
var schema: [String: Any] = [
|
|
259
|
+
"type": "function",
|
|
260
|
+
"function": [
|
|
261
|
+
"name": toolName,
|
|
262
|
+
"description": Self.description,
|
|
263
|
+
],
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
if !properties.isEmpty {
|
|
267
|
+
var functionBody = schema["function"] as! [String: Any]
|
|
268
|
+
functionBody["parameters"] = parameters
|
|
269
|
+
schema["function"] = functionBody
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return schema
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
extension String {
|
|
277
|
+
/// Converts a camelCase string to snake_case.
|
|
278
|
+
func camelToSnakeCase() -> String {
|
|
279
|
+
let regex = try! NSRegularExpression(pattern: "([a-z0-9])([A-Z])", options: [])
|
|
280
|
+
let range = NSRange(location: 0, length: self.utf16.count)
|
|
281
|
+
return regex.stringByReplacingMatches(
|
|
282
|
+
in: self, options: [], range: range, withTemplate: "$1_$2"
|
|
283
|
+
).lowercased()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// Cleans a property label (removes '_' prefix) and optionally converts to snake_case.
|
|
287
|
+
func cleanPropertyLabel(useSnakeCase: Bool) -> String {
|
|
288
|
+
let cleaned = self.hasPrefix("_") ? String(self.dropFirst()) : self
|
|
289
|
+
return useSnakeCase ? cleaned.camelToSnakeCase() : cleaned
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
/// Manages the discovery, schema generation, and execution of tools.
|
|
18
|
+
public class ToolManager {
|
|
19
|
+
|
|
20
|
+
/// A map of tool names to their corresponding Swift Types.
|
|
21
|
+
/// We store the `Type` so we can instantiate new copies via Codable.
|
|
22
|
+
private let toolRegistry: [String: Tool.Type]
|
|
23
|
+
|
|
24
|
+
/// The JSON schema string representing all registered tools.
|
|
25
|
+
/// This is computed once during initialization.
|
|
26
|
+
public let toolsJsonDescription: String
|
|
27
|
+
|
|
28
|
+
/// Initializes the ToolManager with a list of tool instances.
|
|
29
|
+
///
|
|
30
|
+
/// - Parameter tools: A list of instantiated tools (e.g. `[GetWeatherTool()]`).
|
|
31
|
+
/// These instances are used *only* for schema generation. Execution uses new instances.
|
|
32
|
+
public init(tools: [Tool]) {
|
|
33
|
+
var registry: [String: Tool.Type] = [:]
|
|
34
|
+
var schemaList: [[String: Any]] = []
|
|
35
|
+
let useSnakeCase = ExperimentalFlags.convertCamelToSnakeCaseInToolDescription
|
|
36
|
+
|
|
37
|
+
for tool in tools {
|
|
38
|
+
let name = type(of: tool).name
|
|
39
|
+
let toolNameInModel = useSnakeCase ? name.camelToSnakeCase() : name
|
|
40
|
+
registry[toolNameInModel] = type(of: tool)
|
|
41
|
+
|
|
42
|
+
// Use the provided instance to generate the schema via Mirror
|
|
43
|
+
schemaList.append(tool.getSchema())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
self.toolRegistry = registry
|
|
47
|
+
|
|
48
|
+
// Serialize the schema list to a JSON string
|
|
49
|
+
if let data = try? JSONSerialization.data(withJSONObject: schemaList, options: []),
|
|
50
|
+
let jsonString = String(data: data, encoding: .utf8)
|
|
51
|
+
{
|
|
52
|
+
self.toolsJsonDescription = jsonString
|
|
53
|
+
} else {
|
|
54
|
+
self.toolsJsonDescription = "[]"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Executes a tool based on the name and JSON arguments provided by the model.
|
|
59
|
+
///
|
|
60
|
+
/// - Parameters:
|
|
61
|
+
/// - name: The name of the tool to execute.
|
|
62
|
+
/// - arguments: The arguments as a dictionary.
|
|
63
|
+
/// - Returns: The result of the tool execution. This will be a JSON-compatible type (String, Number, Bool, Array, Dictionary).
|
|
64
|
+
public func execute(name: String, arguments: [String: Any]) async throws -> Any {
|
|
65
|
+
guard let ToolType = toolRegistry[name] else {
|
|
66
|
+
throw LiteRTLMError.tool(.notFound(name: name))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 1. Create a template instance to access default values.
|
|
70
|
+
// This allows us to handle optional parameters that the model might omit from its output.
|
|
71
|
+
// Swift Codable requires all keys to be present.
|
|
72
|
+
// By manually merging defaults, we ensure decoding succeeds.
|
|
73
|
+
let defaultTool = ToolType.init()
|
|
74
|
+
var defaults: [String: Any] = [:]
|
|
75
|
+
|
|
76
|
+
// 2. Reflect to extract default values.
|
|
77
|
+
let mirror = Mirror(reflecting: defaultTool)
|
|
78
|
+
let useSnakeCase = ExperimentalFlags.convertCamelToSnakeCaseInToolDescription
|
|
79
|
+
|
|
80
|
+
for child in mirror.children {
|
|
81
|
+
guard let label = child.label else { continue }
|
|
82
|
+
|
|
83
|
+
// Clean the property wrapper name (remove _ prefix and handle snake_case)
|
|
84
|
+
let paramName = label.cleanPropertyLabel(useSnakeCase: useSnakeCase)
|
|
85
|
+
|
|
86
|
+
if let toolParam = child.value as? ToolParamProtocol {
|
|
87
|
+
if let defaultValue = toolParam.wrappedValueAny {
|
|
88
|
+
defaults[paramName] = defaultValue
|
|
89
|
+
} else if !toolParam.isRequired {
|
|
90
|
+
// If it's not required (i.e. optional or has default),
|
|
91
|
+
// but currently nil, we should still provide a null value
|
|
92
|
+
// so the decoder doesn't fail on missing key.
|
|
93
|
+
defaults[paramName] = NSNull()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 3. Merge provided arguments over the defaults.
|
|
99
|
+
// The `arguments` take precedence.
|
|
100
|
+
let mergedArgs = defaults.merging(arguments) { (_, new) in new }
|
|
101
|
+
|
|
102
|
+
let argsData = try JSONSerialization.data(withJSONObject: mergedArgs)
|
|
103
|
+
|
|
104
|
+
let decoder = JSONDecoder()
|
|
105
|
+
if useSnakeCase {
|
|
106
|
+
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Create a new tool instance from the JSON arguments.
|
|
110
|
+
// This populates the @ToolParam properties automatically.
|
|
111
|
+
let toolInstance = try decoder.decode(ToolType, from: argsData)
|
|
112
|
+
|
|
113
|
+
// Run the tool
|
|
114
|
+
let result = try await toolInstance.run()
|
|
115
|
+
|
|
116
|
+
// Normalize the result to ensure it is strictly JSON-compatible (mimicking Android logic)
|
|
117
|
+
return normalizeResult(result)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Helper to recursively convert any result into a JSON-Serialization friendly format.
|
|
121
|
+
/// - Collections: Recursively normalized.
|
|
122
|
+
/// - Primitives: Passed through.
|
|
123
|
+
/// - Custom Objects: Converted to String.
|
|
124
|
+
private func normalizeResult(_ value: Any) -> Any {
|
|
125
|
+
// Handle Dictionaries
|
|
126
|
+
if let dict = value as? [String: Any] {
|
|
127
|
+
var normalizedDict: [String: Any] = [:]
|
|
128
|
+
for (key, val) in dict {
|
|
129
|
+
normalizedDict[key] = normalizeResult(val)
|
|
130
|
+
}
|
|
131
|
+
return normalizedDict
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle Arrays
|
|
135
|
+
if let array = value as? [Any] {
|
|
136
|
+
return array.map { normalizeResult($0) }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Handle Primitives (String, Number, Bool)
|
|
140
|
+
if value is String || value is Int || value is Double || value is Float || value is Bool {
|
|
141
|
+
return value
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle Void
|
|
145
|
+
if value is Void {
|
|
146
|
+
return ""
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Fallback for Custom Objects
|
|
150
|
+
return String(describing: value)
|
|
151
|
+
}
|
|
152
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-llm",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Adds support for LLM locally runned for Capacitor",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -32,9 +32,12 @@
|
|
|
32
32
|
"llm",
|
|
33
33
|
"local",
|
|
34
34
|
"gemma",
|
|
35
|
+
"gemma4",
|
|
35
36
|
"gemma3",
|
|
36
37
|
"gemma3-270m",
|
|
37
38
|
"gemma2-2b",
|
|
39
|
+
"litert",
|
|
40
|
+
"litert-lm",
|
|
38
41
|
"apple-intelligence",
|
|
39
42
|
"gpt"
|
|
40
43
|
],
|
|
@@ -53,9 +56,7 @@
|
|
|
53
56
|
"clean": "rimraf ./dist",
|
|
54
57
|
"watch": "tsc --watch",
|
|
55
58
|
"prepublishOnly": "bun run build",
|
|
56
|
-
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
|
|
57
|
-
"example:install": "cd example-app && bun install --frozen-lockfile",
|
|
58
|
-
"example:build": "bun run build && cd example-app && bun install --frozen-lockfile && bun run build"
|
|
59
|
+
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@capacitor/android": "^8.0.0",
|
|
@@ -80,7 +81,7 @@
|
|
|
80
81
|
},
|
|
81
82
|
"peerDependencies": {
|
|
82
83
|
"@capacitor/core": ">=8.0.0",
|
|
83
|
-
"@mediapipe/tasks-genai": "^0.10.
|
|
84
|
+
"@mediapipe/tasks-genai": "^0.10.24"
|
|
84
85
|
},
|
|
85
86
|
"prettier": "@ionic/prettier-config",
|
|
86
87
|
"swiftlint": "@ionic/swiftlint-config",
|