@capgo/capacitor-llm 8.1.0 → 8.1.2
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/README.md +14 -12
- package/android/src/main/java/ee/forgr/capgo_llm/LLM.java +20 -6
- package/android/src/main/java/ee/forgr/capgo_llm/LLMPlugin.java +3 -1
- package/dist/docs.json +14 -2
- package/dist/esm/definitions.d.ts +8 -2
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/LLMPlugin/LLMPlugin.swift +27 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ On-device LLM support for Capacitor.
|
|
|
11
11
|
|
|
12
12
|
Current platform strategy:
|
|
13
13
|
|
|
14
|
-
- iOS: Apple Intelligence by default, plus LiteRT-LM `.litertlm` custom models when the
|
|
14
|
+
- iOS: Apple Intelligence by default, plus LiteRT-LM `.litertlm` custom models in SwiftPM integrations when the path ends in `.litertlm` or `modelType: 'litertlm'` is passed
|
|
15
15
|
- Android: Gemini Nano system model on supported devices where it is already available, LiteRT-LM for `.litertlm` bundles, and a compatibility fallback for legacy MediaPipe `.task` models
|
|
16
16
|
- Web: Gemma 4 web models through `@mediapipe/tasks-genai`
|
|
17
17
|
|
|
@@ -56,7 +56,9 @@ Custom iOS LiteRT-LM path:
|
|
|
56
56
|
|
|
57
57
|
- Available only when the plugin is integrated into the iOS app through Swift Package Manager
|
|
58
58
|
- Uses the official LiteRT-LM Swift API and prebuilt iOS xcframework for `.litertlm` models
|
|
59
|
+
- Selected only when the path ends in `.litertlm` or `modelType: 'litertlm'` is passed
|
|
59
60
|
- CocoaPods builds keep Apple Intelligence and the legacy MediaPipe `.task` compatibility path
|
|
61
|
+
- Other custom iOS model types keep the legacy MediaPipe compatibility path for backward compatibility
|
|
60
62
|
|
|
61
63
|
Example:
|
|
62
64
|
|
|
@@ -158,11 +160,10 @@ await CapgoLLM.sendMessage({
|
|
|
158
160
|
message: 'Explain why local inference is useful on mobile.',
|
|
159
161
|
});
|
|
160
162
|
```
|
|
161
|
-
|
|
162
163
|
## Notes
|
|
163
164
|
|
|
164
165
|
- Android now prefers LiteRT-LM and Gemma 4 style `.litertlm` bundles.
|
|
165
|
-
- iOS LiteRT-LM custom-model support now uses the official LiteRT-LM Swift API and prebuilt iOS binaries,
|
|
166
|
+
- iOS LiteRT-LM custom-model support now uses the official LiteRT-LM Swift API and prebuilt iOS binaries, is available only in SwiftPM integrations of this plugin, and is selected only for explicit `.litertlm` models.
|
|
166
167
|
- CocoaPods builds on iOS should use Apple Intelligence or the legacy MediaPipe `.task` compatibility path.
|
|
167
168
|
- Web uses Gemma 4 `*-web.task` artifacts through `@mediapipe/tasks-genai`.
|
|
168
169
|
- Apple Intelligence remains the preferred default on iOS where available.
|
|
@@ -237,7 +238,7 @@ setModel(options: ModelOptions) => Promise<void>
|
|
|
237
238
|
```
|
|
238
239
|
|
|
239
240
|
Sets the model configuration
|
|
240
|
-
- iOS: Use "Apple Intelligence" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager
|
|
241
|
+
- iOS: Use "Apple Intelligence" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager, and are selected only when `modelType: 'litertlm'` is passed or the path ends in `.litertlm`.
|
|
241
242
|
- Android: Use "Gemini Nano" for the AICore system model on supported devices where it is already available. Prefer LiteRT-LM `.litertlm` bundles for custom models; legacy MediaPipe `.task` models are still supported
|
|
242
243
|
- Web: Provide a web-ready model asset for `@mediapipe/tasks-genai` such as Gemma 4 `*-web.task`
|
|
243
244
|
|
|
@@ -380,14 +381,15 @@ Get the native Capacitor plugin version.
|
|
|
380
381
|
Model configuration options
|
|
381
382
|
Only `path` is required. All other properties are optional overrides.
|
|
382
383
|
|
|
383
|
-
| Prop | Type
|
|
384
|
-
| ----------------- |
|
|
385
|
-
| **`path`** | <code>string</code>
|
|
386
|
-
| **`modelType`** | <code>string</code>
|
|
387
|
-
| **`maxTokens`** | <code>number</code>
|
|
388
|
-
| **`topk`** | <code>number</code>
|
|
389
|
-
| **`temperature`** | <code>number</code>
|
|
390
|
-
| **`randomSeed`** | <code>number</code>
|
|
384
|
+
| Prop | Type | Description | Since |
|
|
385
|
+
| ----------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
386
|
+
| **`path`** | <code>string</code> | Model path, "Apple Intelligence" for the Apple system model on iOS, or "Gemini Nano" for the Android AICore system model when already available on the device. On iOS, custom `.litertlm` models require the plugin to be integrated through Swift Package Manager. Gemma 4 examples use `.litertlm` on mobile and `*-web.task` on web. | |
|
|
387
|
+
| **`modelType`** | <code>string</code> | Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path. On iOS, LiteRT-LM is selected only when this resolves to `litertlm`; all other custom types keep the legacy MediaPipe compatibility path. | |
|
|
388
|
+
| **`maxTokens`** | <code>number</code> | Maximum number of tokens the model handles | |
|
|
389
|
+
| **`topk`** | <code>number</code> | Number of tokens the model considers at each step | |
|
|
390
|
+
| **`temperature`** | <code>number</code> | Amount of randomness in generation (0.0-1.0) | |
|
|
391
|
+
| **`randomSeed`** | <code>number</code> | Optional. Random seed for generation. | |
|
|
392
|
+
| **`backend`** | <code>'gpu' \| 'cpu'</code> | Optional. LiteRT-LM engine backend for iOS (SwiftPM) and Android. Use `cpu` for stable long generations. When omitted, iOS prefers CPU then falls back to GPU; Android uses CPU. | 8.2.0 |
|
|
391
393
|
|
|
392
394
|
|
|
393
395
|
#### DownloadModelResult
|
|
@@ -84,9 +84,10 @@ public class LLM {
|
|
|
84
84
|
Integer topk,
|
|
85
85
|
Float temperature,
|
|
86
86
|
Integer randomSeed,
|
|
87
|
+
String backend,
|
|
87
88
|
ModelLoadCallback callback
|
|
88
89
|
) {
|
|
89
|
-
ModelLoadRequest request = nextLoadRequest(path, modelType, maxTokens, topk, temperature, randomSeed);
|
|
90
|
+
ModelLoadRequest request = nextLoadRequest(path, modelType, maxTokens, topk, temperature, randomSeed, backend);
|
|
90
91
|
resetModelState();
|
|
91
92
|
|
|
92
93
|
android.util.Log.d(
|
|
@@ -111,10 +112,11 @@ public class LLM {
|
|
|
111
112
|
Integer maxTokens,
|
|
112
113
|
Integer topk,
|
|
113
114
|
Float temperature,
|
|
114
|
-
Integer randomSeed
|
|
115
|
+
Integer randomSeed,
|
|
116
|
+
String backend
|
|
115
117
|
) {
|
|
116
118
|
currentLoadId += 1;
|
|
117
|
-
return new ModelLoadRequest(path, modelType, maxTokens, topk, temperature, randomSeed, currentLoadId);
|
|
119
|
+
return new ModelLoadRequest(path, modelType, maxTokens, topk, temperature, randomSeed, backend, currentLoadId);
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
private synchronized boolean isCurrentLoad(long loadId) {
|
|
@@ -210,11 +212,12 @@ public class LLM {
|
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
private Engine initializeLiteRtModel(String actualPath, ModelLoadRequest request) {
|
|
215
|
+
Backend primaryBackend = resolveLiteRtBackend(request.backend);
|
|
213
216
|
EngineConfig config = new EngineConfig(
|
|
214
217
|
actualPath,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
+
primaryBackend,
|
|
219
|
+
primaryBackend,
|
|
220
|
+
primaryBackend,
|
|
218
221
|
request.maxTokens,
|
|
219
222
|
context.getCacheDir().getAbsolutePath()
|
|
220
223
|
);
|
|
@@ -223,6 +226,14 @@ public class LLM {
|
|
|
223
226
|
return loadedEngine;
|
|
224
227
|
}
|
|
225
228
|
|
|
229
|
+
private Backend resolveLiteRtBackend(String backend) {
|
|
230
|
+
if (backend != null && "gpu".equalsIgnoreCase(backend.trim())) {
|
|
231
|
+
return new Backend.GPU();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return new Backend.CPU();
|
|
235
|
+
}
|
|
236
|
+
|
|
226
237
|
private LlmInference initializeMediaPipeModel(String actualPath, ModelLoadRequest request) {
|
|
227
238
|
LlmInferenceOptions options = LlmInferenceOptions.builder()
|
|
228
239
|
.setModelPath(actualPath)
|
|
@@ -820,6 +831,7 @@ public class LLM {
|
|
|
820
831
|
private final Integer topk;
|
|
821
832
|
private final Float temperature;
|
|
822
833
|
private final Integer randomSeed;
|
|
834
|
+
private final String backend;
|
|
823
835
|
private final long loadId;
|
|
824
836
|
|
|
825
837
|
private ModelLoadRequest(
|
|
@@ -829,6 +841,7 @@ public class LLM {
|
|
|
829
841
|
Integer topk,
|
|
830
842
|
Float temperature,
|
|
831
843
|
Integer randomSeed,
|
|
844
|
+
String backend,
|
|
832
845
|
long loadId
|
|
833
846
|
) {
|
|
834
847
|
this.modelPath = modelPath;
|
|
@@ -837,6 +850,7 @@ public class LLM {
|
|
|
837
850
|
this.topk = topk;
|
|
838
851
|
this.temperature = temperature;
|
|
839
852
|
this.randomSeed = randomSeed;
|
|
853
|
+
this.backend = backend;
|
|
840
854
|
this.loadId = loadId;
|
|
841
855
|
}
|
|
842
856
|
}
|
|
@@ -9,7 +9,7 @@ import com.getcapacitor.annotation.CapacitorPlugin;
|
|
|
9
9
|
@CapacitorPlugin(name = "CapgoLLM")
|
|
10
10
|
public class LLMPlugin extends Plugin {
|
|
11
11
|
|
|
12
|
-
private final String pluginVersion = "8.1.
|
|
12
|
+
private final String pluginVersion = "8.1.2";
|
|
13
13
|
|
|
14
14
|
private LLM llm;
|
|
15
15
|
|
|
@@ -106,6 +106,7 @@ public class LLMPlugin extends Plugin {
|
|
|
106
106
|
Float temperature = call.getFloat("temperature", 0.8f);
|
|
107
107
|
Integer randomSeed = call.getInt("randomSeed", 0);
|
|
108
108
|
String modelType = call.getString("modelType");
|
|
109
|
+
String backend = call.getString("backend");
|
|
109
110
|
|
|
110
111
|
llm.setModel(
|
|
111
112
|
path,
|
|
@@ -114,6 +115,7 @@ public class LLMPlugin extends Plugin {
|
|
|
114
115
|
topk,
|
|
115
116
|
temperature,
|
|
116
117
|
randomSeed,
|
|
118
|
+
backend,
|
|
117
119
|
new LLM.ModelLoadCallback() {
|
|
118
120
|
@Override
|
|
119
121
|
public void onSuccess() {
|
package/dist/docs.json
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"text": "Promise that resolves when model is loaded"
|
|
82
82
|
}
|
|
83
83
|
],
|
|
84
|
-
"docs": "Sets the model configuration\n- iOS: Use \"Apple Intelligence\" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager
|
|
84
|
+
"docs": "Sets the model configuration\n- iOS: Use \"Apple Intelligence\" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager, and are selected only when `modelType: 'litertlm'` is passed or the path ends in `.litertlm`.\n- Android: Use \"Gemini Nano\" for the AICore system model on supported devices where it is already available. Prefer LiteRT-LM `.litertlm` bundles for custom models; legacy MediaPipe `.task` models are still supported\n- Web: Provide a web-ready model asset for `@mediapipe/tasks-genai` such as Gemma 4 `*-web.task`",
|
|
85
85
|
"complexTypes": [
|
|
86
86
|
"ModelOptions"
|
|
87
87
|
],
|
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
{
|
|
344
344
|
"name": "modelType",
|
|
345
345
|
"tags": [],
|
|
346
|
-
"docs": "Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path.
|
|
346
|
+
"docs": "Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path. On iOS, LiteRT-LM is selected only when this resolves to `litertlm`; all other custom types keep the legacy MediaPipe compatibility path.",
|
|
347
347
|
"complexTypes": [],
|
|
348
348
|
"type": "string | undefined"
|
|
349
349
|
},
|
|
@@ -374,6 +374,18 @@
|
|
|
374
374
|
"docs": "Optional. Random seed for generation.",
|
|
375
375
|
"complexTypes": [],
|
|
376
376
|
"type": "number | undefined"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"name": "backend",
|
|
380
|
+
"tags": [
|
|
381
|
+
{
|
|
382
|
+
"text": "8.2.0",
|
|
383
|
+
"name": "since"
|
|
384
|
+
}
|
|
385
|
+
],
|
|
386
|
+
"docs": "Optional. LiteRT-LM engine backend for iOS (SwiftPM) and Android.\nUse `cpu` for stable long generations. When omitted, iOS prefers CPU then falls back to GPU; Android uses CPU.",
|
|
387
|
+
"complexTypes": [],
|
|
388
|
+
"type": "'gpu' | 'cpu' | undefined"
|
|
377
389
|
}
|
|
378
390
|
]
|
|
379
391
|
},
|
|
@@ -28,7 +28,7 @@ export interface LLMPlugin {
|
|
|
28
28
|
}>;
|
|
29
29
|
/**
|
|
30
30
|
* Sets the model configuration
|
|
31
|
-
* - iOS: Use "Apple Intelligence" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager
|
|
31
|
+
* - iOS: Use "Apple Intelligence" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager, and are selected only when `modelType: 'litertlm'` is passed or the path ends in `.litertlm`.
|
|
32
32
|
* - Android: Use "Gemini Nano" for the AICore system model on supported devices where it is already available. Prefer LiteRT-LM `.litertlm` bundles for custom models; legacy MediaPipe `.task` models are still supported
|
|
33
33
|
* - Web: Provide a web-ready model asset for `@mediapipe/tasks-genai` such as Gemma 4 `*-web.task`
|
|
34
34
|
* @param options - The model configuration
|
|
@@ -178,7 +178,7 @@ export interface ReadinessChangeEvent {
|
|
|
178
178
|
export interface ModelOptions {
|
|
179
179
|
/** Model path, "Apple Intelligence" for the Apple system model on iOS, or "Gemini Nano" for the Android AICore system model when already available on the device. On iOS, custom `.litertlm` models require the plugin to be integrated through Swift Package Manager. Gemma 4 examples use `.litertlm` on mobile and `*-web.task` on web. */
|
|
180
180
|
path: string;
|
|
181
|
-
/** Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path.
|
|
181
|
+
/** Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path. On iOS, LiteRT-LM is selected only when this resolves to `litertlm`; all other custom types keep the legacy MediaPipe compatibility path. */
|
|
182
182
|
modelType?: string;
|
|
183
183
|
/** Maximum number of tokens the model handles */
|
|
184
184
|
maxTokens?: number;
|
|
@@ -188,4 +188,10 @@ export interface ModelOptions {
|
|
|
188
188
|
temperature?: number;
|
|
189
189
|
/** Optional. Random seed for generation. */
|
|
190
190
|
randomSeed?: number;
|
|
191
|
+
/**
|
|
192
|
+
* Optional. LiteRT-LM engine backend for iOS (SwiftPM) and Android.
|
|
193
|
+
* Use `cpu` for stable long generations. When omitted, iOS prefers CPU then falls back to GPU; Android uses CPU.
|
|
194
|
+
* @since 8.2.0
|
|
195
|
+
*/
|
|
196
|
+
backend?: 'gpu' | 'cpu';
|
|
191
197
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * LLM Plugin interface for interacting with on-device language models\n */\nexport interface LLMPlugin {\n /**\n * Creates a new chat session\n * @returns Promise with chat id and optional instructions\n */\n createChat(): Promise<{ id: string; instructions?: string }>;\n\n /**\n * Sends a message to the AI in a specific chat session\n * @param options - The chat id and message to send\n * @returns Promise that resolves when message is sent\n */\n sendMessage(options: { chatId: string; message: string }): Promise<void>;\n\n /**\n * Gets the readiness status of the LLM\n * @returns Promise with readiness status string\n */\n getReadiness(): Promise<{ readiness: string }>;\n\n /**\n * Sets the model configuration\n * - iOS: Use \"Apple Intelligence\" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * LLM Plugin interface for interacting with on-device language models\n */\nexport interface LLMPlugin {\n /**\n * Creates a new chat session\n * @returns Promise with chat id and optional instructions\n */\n createChat(): Promise<{ id: string; instructions?: string }>;\n\n /**\n * Sends a message to the AI in a specific chat session\n * @param options - The chat id and message to send\n * @returns Promise that resolves when message is sent\n */\n sendMessage(options: { chatId: string; message: string }): Promise<void>;\n\n /**\n * Gets the readiness status of the LLM\n * @returns Promise with readiness status string\n */\n getReadiness(): Promise<{ readiness: string }>;\n\n /**\n * Sets the model configuration\n * - iOS: Use \"Apple Intelligence\" as path for the system model. Custom LiteRT-LM `.litertlm` models are supported on iOS only when this plugin is integrated through Swift Package Manager, and are selected only when `modelType: 'litertlm'` is passed or the path ends in `.litertlm`.\n * - Android: Use \"Gemini Nano\" for the AICore system model on supported devices where it is already available. Prefer LiteRT-LM `.litertlm` bundles for custom models; legacy MediaPipe `.task` models are still supported\n * - Web: Provide a web-ready model asset for `@mediapipe/tasks-genai` such as Gemma 4 `*-web.task`\n * @param options - The model configuration\n * @returns Promise that resolves when model is loaded\n */\n setModel(options: ModelOptions): Promise<void>;\n\n /**\n * Downloads a model from a URL and saves it to the appropriate location\n * - iOS: Downloads to the app's documents directory\n * - Android: Downloads to the app's files directory\n * @param options - The download configuration\n * @returns Promise with download result including the saved path\n */\n downloadModel(options: DownloadModelOptions): Promise<DownloadModelResult>;\n\n /**\n * Adds a listener for text received from AI\n * @param eventName - Event name 'textFromAi'\n * @param listenerFunc - Callback function for text events\n * @returns Promise with remove function to unsubscribe\n */\n addListener(\n eventName: 'textFromAi',\n listenerFunc: (event: TextFromAiEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Adds a listener for AI completion events\n * @param eventName - Event name 'aiFinished'\n * @param listenerFunc - Callback function for finish events\n * @returns Promise with remove function to unsubscribe\n */\n addListener(\n eventName: 'aiFinished',\n listenerFunc: (event: AiFinishedEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Adds a listener for generation failures that happen after streaming starts\n * @param eventName - Event name 'generationError'\n * @param listenerFunc - Callback function for generation errors\n * @returns Promise with remove function to unsubscribe\n */\n addListener(\n eventName: 'generationError',\n listenerFunc: (event: GenerationErrorEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Adds a listener for model download progress events\n * @param eventName - Event name 'downloadProgress'\n * @param listenerFunc - Callback function for progress events\n * @returns Promise with remove function to unsubscribe\n */\n addListener(\n eventName: 'downloadProgress',\n listenerFunc: (event: DownloadProgressEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Adds a listener for readiness status changes\n * @param eventName - Event name 'readinessChange'\n * @param listenerFunc - Callback function for readiness events\n * @returns Promise with remove function to unsubscribe\n */\n addListener(\n eventName: 'readinessChange',\n listenerFunc: (event: ReadinessChangeEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @throws Error if getting the version fails\n * @since 1.0.0\n * @example\n * ```typescript\n * const { version } = await CapgoLLM.getPluginVersion();\n * console.log('Plugin version:', version);\n * ```\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n\n/**\n * Event data for text received from AI\n */\nexport interface TextFromAiEvent {\n /** The text content from AI - this is an incremental chunk, not the full text */\n text: string;\n /** The chat session ID */\n chatId: string;\n /** Whether this is a complete chunk (true) or partial streaming data (false) */\n isChunk?: boolean;\n}\n\n/**\n * Event data for AI completion\n */\nexport interface AiFinishedEvent {\n /** The chat session ID that finished */\n chatId: string;\n}\n\n/**\n * Event data for generation failures\n * `chatId` is optional and may be omitted when the failure is not tied to a specific chat.\n */\nexport interface GenerationErrorEvent {\n /** Optional. The chat session ID that failed, when available. */\n chatId?: string;\n /** Error message describing the failure */\n error: string;\n}\n\n/**\n * Options for downloading a model\n * Only `url` is required. `companionUrl` and `filename` are optional.\n */\nexport interface DownloadModelOptions {\n /** URL of the model file to download */\n url: string;\n /** Optional: URL of a companion file for legacy model formats */\n companionUrl?: string;\n /** Optional: Custom filename (defaults to filename from URL) */\n filename?: string;\n}\n\n/**\n * Result of model download\n */\nexport interface DownloadModelResult {\n /** Path where the model was saved */\n path: string;\n /** Path where the companion file was saved (if applicable) */\n companionPath?: string;\n}\n\n/**\n * Event data for download progress\n */\nexport interface DownloadProgressEvent {\n /** Percentage of download completed (0-100) */\n progress: number;\n /** Total bytes to download */\n totalBytes?: number;\n /** Bytes downloaded so far */\n downloadedBytes?: number;\n}\n\n/**\n * Event data for readiness status changes\n */\nexport interface ReadinessChangeEvent {\n /** The readiness status */\n readiness: string;\n}\n\n/**\n * Model configuration options\n * Only `path` is required. All other properties are optional overrides.\n */\nexport interface ModelOptions {\n /** Model path, \"Apple Intelligence\" for the Apple system model on iOS, or \"Gemini Nano\" for the Android AICore system model when already available on the device. On iOS, custom `.litertlm` models require the plugin to be integrated through Swift Package Manager. Gemma 4 examples use `.litertlm` on mobile and `*-web.task` on web. */\n path: string;\n /** Optional. Model file type/extension (for example `task`, `bin`, `litertlm`, or `gemini-nano`). If not provided, it is extracted from the path. On iOS, LiteRT-LM is selected only when this resolves to `litertlm`; all other custom types keep the legacy MediaPipe compatibility path. */\n modelType?: string;\n /** Maximum number of tokens the model handles */\n maxTokens?: number;\n /** Number of tokens the model considers at each step */\n topk?: number;\n /** Amount of randomness in generation (0.0-1.0) */\n temperature?: number;\n /** Optional. Random seed for generation. */\n randomSeed?: number;\n /**\n * Optional. LiteRT-LM engine backend for iOS (SwiftPM) and Android.\n * Use `cpu` for stable long generations. When omitted, iOS prefers CPU then falls back to GPU; Android uses CPU.\n * @since 8.2.0\n */\n backend?: 'gpu' | 'cpu';\n}\n"]}
|
|
@@ -17,7 +17,7 @@ import MediaPipeTasksGenAI
|
|
|
17
17
|
|
|
18
18
|
@objc(LLMPlugin)
|
|
19
19
|
public class LLMPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
20
|
-
private let pluginVersion: String = "8.1.
|
|
20
|
+
private let pluginVersion: String = "8.1.2"
|
|
21
21
|
private let liteRtTopP: Float = 0.95
|
|
22
22
|
|
|
23
23
|
public let identifier = "LLMPlugin"
|
|
@@ -80,6 +80,7 @@ public class LLMPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
80
80
|
let temperature = call.getFloat("temperature") ?? 0.8
|
|
81
81
|
let randomSeed = call.getInt("randomSeed") ?? 0
|
|
82
82
|
let modelType = call.getString("modelType")
|
|
83
|
+
let backend = call.getString("backend")
|
|
83
84
|
|
|
84
85
|
modelPath = path
|
|
85
86
|
isReady = false
|
|
@@ -112,7 +113,11 @@ public class LLMPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
112
113
|
Task {
|
|
113
114
|
do {
|
|
114
115
|
let modelURL = try resolveModelURL(path: path, modelType: modelType)
|
|
115
|
-
let engine = try await initializeLiteRtEngine(
|
|
116
|
+
let engine = try await initializeLiteRtEngine(
|
|
117
|
+
modelURL: modelURL,
|
|
118
|
+
maxTokens: maxTokens,
|
|
119
|
+
backend: backend
|
|
120
|
+
)
|
|
116
121
|
liteRtEngine = engine
|
|
117
122
|
liteRtTopk = topk
|
|
118
123
|
liteRtTemperature = temperature
|
|
@@ -407,11 +412,7 @@ public class LLMPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
407
412
|
case "task":
|
|
408
413
|
return .mediaPipe
|
|
409
414
|
default:
|
|
410
|
-
#if canImport(LiteRTLM)
|
|
411
|
-
return .liteRtLm
|
|
412
|
-
#else
|
|
413
415
|
return .mediaPipe
|
|
414
|
-
#endif
|
|
415
416
|
}
|
|
416
417
|
}
|
|
417
418
|
|
|
@@ -528,7 +529,11 @@ private extension LLMPlugin {
|
|
|
528
529
|
|
|
529
530
|
#if canImport(LiteRTLM)
|
|
530
531
|
private extension LLMPlugin {
|
|
531
|
-
func initializeLiteRtEngine(
|
|
532
|
+
func initializeLiteRtEngine(
|
|
533
|
+
modelURL: URL,
|
|
534
|
+
maxTokens: Int,
|
|
535
|
+
backend: String?
|
|
536
|
+
) async throws -> LiteRTLM.Engine {
|
|
532
537
|
guard let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
|
|
533
538
|
throw NSError(
|
|
534
539
|
domain: "CapgoLLM",
|
|
@@ -537,20 +542,25 @@ private extension LLMPlugin {
|
|
|
537
542
|
)
|
|
538
543
|
}
|
|
539
544
|
|
|
540
|
-
let
|
|
545
|
+
let cacheDir = cacheDirectory.path
|
|
546
|
+
let makeConfig = { (selectedBackend: LiteRTLM.Backend) throws -> LiteRTLM.EngineConfig in
|
|
541
547
|
try LiteRTLM.EngineConfig(
|
|
542
548
|
modelPath: modelURL.path,
|
|
543
|
-
backend:
|
|
549
|
+
backend: selectedBackend,
|
|
544
550
|
maxNumTokens: maxTokens,
|
|
545
|
-
cacheDir:
|
|
546
|
-
),
|
|
547
|
-
try LiteRTLM.EngineConfig(
|
|
548
|
-
modelPath: modelURL.path,
|
|
549
|
-
backend: .cpu(),
|
|
550
|
-
maxNumTokens: maxTokens,
|
|
551
|
-
cacheDir: cacheDirectory.path
|
|
551
|
+
cacheDir: cacheDir
|
|
552
552
|
)
|
|
553
|
-
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
let configs: [LiteRTLM.EngineConfig]
|
|
556
|
+
switch backend?.lowercased() {
|
|
557
|
+
case "cpu":
|
|
558
|
+
configs = [try makeConfig(.cpu())]
|
|
559
|
+
case "gpu":
|
|
560
|
+
configs = [try makeConfig(.gpu), try makeConfig(.cpu())]
|
|
561
|
+
default:
|
|
562
|
+
configs = [try makeConfig(.cpu()), try makeConfig(.gpu)]
|
|
563
|
+
}
|
|
554
564
|
|
|
555
565
|
var lastError: Error?
|
|
556
566
|
for config in configs {
|