@dvai-bridge/capacitor-mlx 4.0.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/DVAICapacitorMLX.podspec +32 -0
- package/LICENSE +51 -0
- package/README.md +199 -0
- package/dist/index.cjs +34 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/ios/Package.swift +35 -0
- package/ios/Sources/DVAICapacitorMLX/Plugin.swift +84 -0
- package/ios/Tests/DVAICapacitorMLXTests/SmokeTest.swift +14 -0
- package/package.json +61 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
3
|
+
|
|
4
|
+
# Note: DVAICapacitorMLX intentionally has no CocoaPods integration
|
|
5
|
+
# because mlx-swift-lm + mlx-swift transitive deps don't publish
|
|
6
|
+
# CocoaPods specs. CocoaPods consumers should use the .llama or
|
|
7
|
+
# .coreml backends via DVAICapacitorLlama / DVAICapacitorCoreML, or
|
|
8
|
+
# integrate via SwiftPM where MLX is available.
|
|
9
|
+
#
|
|
10
|
+
# This podspec file exists for parity with the other capacitor-*
|
|
11
|
+
# packages so `pod lib lint` doesn't trip on its absence — the
|
|
12
|
+
# embedded source files compile and the pod links into the app, but
|
|
13
|
+
# any attempt to call DVAIBridgeMLX.start() will throw because the
|
|
14
|
+
# `MLXLMCommon` symbols aren't in the link line.
|
|
15
|
+
#
|
|
16
|
+
# When mlx-swift-lm publishes a CocoaPods spec (or we vendor the full
|
|
17
|
+
# stack the way we did for swift-transformers in DVAIBridge.podspec),
|
|
18
|
+
# update s.dependency to add it and remove this caveat.
|
|
19
|
+
|
|
20
|
+
Pod::Spec.new do |s|
|
|
21
|
+
s.name = 'DVAICapacitorMLX'
|
|
22
|
+
s.version = package['version']
|
|
23
|
+
s.summary = package['description']
|
|
24
|
+
s.license = 'Custom (See LICENSE)'
|
|
25
|
+
s.homepage = package['repository']['url']
|
|
26
|
+
s.author = package['author']
|
|
27
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
28
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m,mm}'
|
|
29
|
+
s.ios.deployment_target = '17.0'
|
|
30
|
+
s.swift_version = '5.9'
|
|
31
|
+
s.dependency 'Capacitor'
|
|
32
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Deep Voice Ai Limited - Software License Agreement
|
|
2
|
+
|
|
3
|
+
**Version 1.0.0**
|
|
4
|
+
|
|
5
|
+
This License Agreement governs the use of the DVAI-Bridge software (the "Software"). By downloading, installing, or using the Software, you agree to be bound by the terms of this License.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. LICENSE GRANTS
|
|
10
|
+
|
|
11
|
+
### 1.1 Development and Personal Use (Free Tier)
|
|
12
|
+
Deep Voice Ai Limited ("Licensor") grants you a non-exclusive, non-transferable, royalty-free license to use the Software solely for:
|
|
13
|
+
- Internal development and testing purposes.
|
|
14
|
+
- Non-commercial personal projects.
|
|
15
|
+
- Academic and non-profit research.
|
|
16
|
+
|
|
17
|
+
### 1.2 Commercial Use (Paid Tier)
|
|
18
|
+
Any use of the Software for **Commercial Purposes** requires a separate, paid Commercial License from Licensor. "Commercial Purposes" include:
|
|
19
|
+
- Use in production environments.
|
|
20
|
+
- Integration into revenue-generating products or services.
|
|
21
|
+
- Distribution to third-party customers for a fee.
|
|
22
|
+
- Use by an entity with more than $100,000 USD in annual revenue.
|
|
23
|
+
|
|
24
|
+
To obtain a Commercial License, contact `info@deepvoiceai.co` or visit `https://deepvoiceai.co/licensing`.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. RESTRICTIONS
|
|
29
|
+
Except as expressly permitted, you may not:
|
|
30
|
+
- Sublicense, rent, lease, or resell the Software without express permission.
|
|
31
|
+
- Remove any proprietary notices or branding from the Software.
|
|
32
|
+
- Use the Software for any illegal or malicious purposes.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 3. INTELLECTUAL PROPERTY
|
|
37
|
+
The Software is owned by **Deep Voice Ai Limited** and is protected by copyright and intellectual property laws. This agreement does not transfer ownership of the Software.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 4. NO WARRANTY
|
|
42
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 5. GOVERNING LAW
|
|
47
|
+
This License shall be governed by and construed in accordance with the laws of the jurisdiction where Deep Voice Ai Limited is registered.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
© 2026 Deep Voice Ai Limited. All rights reserved.
|
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# DVAI-Bridge
|
|
4
|
+
|
|
5
|
+
<!-- [](https://github.com/Westenets/dvai-bridge/actions/workflows/smoke-real-models.yml) -->
|
|
6
|
+
|
|
7
|
+
[](LICENSE)      
|
|
8
|
+
|
|
9
|
+
> **The local OpenAI server you embed inside your app.**
|
|
10
|
+
> One library. One HTTP wire. Every platform. Zero install for your users.
|
|
11
|
+
|
|
12
|
+
**Docs:** [dvai-bridge.deepvoiceai.co](https://dvai-bridge.deepvoiceai.co)
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { DVAI } from "@dvai-bridge/core";
|
|
16
|
+
import OpenAI from "openai";
|
|
17
|
+
|
|
18
|
+
const dvai = new DVAI({ backend: "transformers" });
|
|
19
|
+
await dvai.initialize();
|
|
20
|
+
|
|
21
|
+
const openai = new OpenAI({ baseURL: dvai.baseUrl, apiKey: "ignored" });
|
|
22
|
+
await openai.chat.completions.create({
|
|
23
|
+
model: dvai.transformersModelId,
|
|
24
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's it. A real OpenAI-compatible server is now running inside your app's
|
|
29
|
+
own process. Point any OpenAI client — LangChain, the OpenAI SDK, the Vercel
|
|
30
|
+
AI SDK, anything — at `dvai.baseUrl` and your agent code keeps working.
|
|
31
|
+
|
|
32
|
+
Built by **[Deep Voice AI](https://deepvoiceai.co)**.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Why it exists
|
|
37
|
+
|
|
38
|
+
Local AI works beautifully on a laptop with **Ollama + LangChain**. Then you
|
|
39
|
+
try to ship the app and your users don't have Ollama. Mobile can't run it.
|
|
40
|
+
Corporate IT won't add another daemon. So you reinvent the same plumbing —
|
|
41
|
+
spawn an inference engine, bind a port, translate to OpenAI HTTP, handle
|
|
42
|
+
CORS, manage lifecycle, wrap the accelerator of the day per platform — and
|
|
43
|
+
do it all over again for every target OS.
|
|
44
|
+
|
|
45
|
+
DVAI-Bridge is that plumbing, packaged as a library, for every client
|
|
46
|
+
platform.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## What you get
|
|
51
|
+
|
|
52
|
+
- **One OpenAI HTTP surface.** Bound on `127.0.0.1` (or `0.0.0.0` for
|
|
53
|
+
device-to-device). Streaming, embeddings, models, recovery — all built in.
|
|
54
|
+
- **Six SDKs.** `@dvai-bridge/core` + `react` + `vanilla` + `capacitor`,
|
|
55
|
+
`DVAIBridge` (Swift / iOS), `co.deepvoiceai:dvai-bridge` (Kotlin / Android),
|
|
56
|
+
`@dvai-bridge/react-native`, `dvai_bridge` (Flutter), `co.deepvoiceai.dvai-bridge` (.NET).
|
|
57
|
+
- **Nine backends.** WebLLM, Transformers.js, llama.cpp, Apple Foundation
|
|
58
|
+
Models, MLX, CoreML / ANE, MediaPipe LLM, LiteRT, ONNX Runtime GenAI —
|
|
59
|
+
selected per-platform, invisible to your agent code.
|
|
60
|
+
- **Native acceleration** wherever it runs: WebGPU in browsers, CUDA / Metal
|
|
61
|
+
/ Vulkan / DirectML on desktop, ANE / Metal / MLX on iOS, NNAPI / QNN
|
|
62
|
+
Hexagon / GPU delegate on Android.
|
|
63
|
+
- **Multimodal.** Text, image, audio, video — declarative loader for
|
|
64
|
+
cutting-edge models (Gemma 4, LLaVA, Idefics) without waiting for library
|
|
65
|
+
updates.
|
|
66
|
+
- **Distributed inference (v3.0+).** Phone too slow? Offload to your laptop
|
|
67
|
+
on the same Wi-Fi via mDNS pairing — same OpenAI wire, transparent to
|
|
68
|
+
your code. Internet path via a self-hostable rendezvous server.
|
|
69
|
+
- **DVAI Hub (v3.1+).** A first-party desktop utility that turns any device
|
|
70
|
+
into a strong-peer for the rest of your fleet. Brand-neutral install via
|
|
71
|
+
Homebrew / winget / GitHub Releases, OR fork it for your own branded
|
|
72
|
+
companion. Routes through Ollama / LM Studio / vLLM / llama-server /
|
|
73
|
+
llamafile if you've already got those running.
|
|
74
|
+
- **Zero user install.** It's a library, not a daemon. `npm install`,
|
|
75
|
+
`cocoapods`, gradle — your CI already has the muscle for it.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Supported platforms
|
|
80
|
+
|
|
81
|
+
| Stack | Package | Backends |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| Browser (React, Vue, Svelte, vanilla JS) | `@dvai-bridge/core` + `react` / `vanilla` | WebLLM (WebGPU), Transformers.js (WebGPU / WASM SIMD) |
|
|
84
|
+
| Node / Bun / Electron | `@dvai-bridge/core` | Transformers.js, native llama.cpp |
|
|
85
|
+
| Capacitor hybrid mobile | `@dvai-bridge/capacitor` + backend slice | Native llama.cpp (Metal iOS, Vulkan / CPU Android) |
|
|
86
|
+
| iOS native (Swift) | `DVAIBridge` (SPM / CocoaPods) | llama.cpp (Metal), CoreML / ANE, Apple Foundation Models, MLX |
|
|
87
|
+
| Android native (Kotlin / Java) | `co.deepvoiceai:dvai-bridge` (AAR) | llama.cpp, MediaPipe LLM, LiteRT, NNAPI / QNN |
|
|
88
|
+
| React Native (≥0.77, TurboModule) | `@dvai-bridge/react-native` | All iOS + Android backends (delegates) |
|
|
89
|
+
| Flutter (≥3.39) | `dvai_bridge` (pub.dev) | All iOS + Android backends (Pigeon channels) |
|
|
90
|
+
| .NET 10 LTS (MAUI / Avalonia / WinUI / Catalyst / desktop) | `co.deepvoiceai.dvai-bridge*` (NuGet) | iOS / Android delegate to native; desktop = llama.cpp + ONNX Runtime GenAI + ML.NET |
|
|
91
|
+
|
|
92
|
+
Full quickstart per platform: [dvai-bridge.deepvoiceai.co/guide/getting-started](https://dvai-bridge.deepvoiceai.co/guide/getting-started)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// React
|
|
100
|
+
import { DVAIProvider, useDVAI } from "@dvai-bridge/react";
|
|
101
|
+
<DVAIProvider config={{ backend: "transformers" }}>
|
|
102
|
+
<Chat />
|
|
103
|
+
</DVAIProvider>;
|
|
104
|
+
function Chat() {
|
|
105
|
+
const { isReady, baseUrl } = useDVAI();
|
|
106
|
+
return isReady ? <div>Local AI live at {baseUrl}</div> : <Loading />;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```swift
|
|
111
|
+
// iOS
|
|
112
|
+
let server = try await DVAIBridge.shared.start()
|
|
113
|
+
// server.baseUrl = "http://127.0.0.1:38883/v1"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```kotlin
|
|
117
|
+
// Android
|
|
118
|
+
val server = DVAIBridge.start(context)
|
|
119
|
+
// server.baseUrl = "http://127.0.0.1:38883/v1"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```dart
|
|
123
|
+
// Flutter
|
|
124
|
+
final state = await DVAIBridge.instance.start(
|
|
125
|
+
backend: BackendKind.auto,
|
|
126
|
+
modelPath: '/path/to/model.gguf',
|
|
127
|
+
);
|
|
128
|
+
// state.baseUrl = "http://127.0.0.1:38883/v1"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```csharp
|
|
132
|
+
// .NET
|
|
133
|
+
var server = await DVAIBridge.Shared.StartAsync(new StartOptions {
|
|
134
|
+
Backend = BackendKind.Auto,
|
|
135
|
+
ModelPath = "/path/to/model.gguf",
|
|
136
|
+
});
|
|
137
|
+
// server.BaseUrl = "http://127.0.0.1:38883/v1"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Multimodal, streaming, embeddings, distributed offload, the Hub —
|
|
141
|
+
everything's at the [docs site](https://dvai-bridge.deepvoiceai.co).
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## What's new in v3.1
|
|
146
|
+
|
|
147
|
+
- **DVAI Hub** — Tauri desktop utility that's the strong-peer side of v3
|
|
148
|
+
distributed inference. `brew install deepvoiceai/dvai-hub/dvai-hub` (or
|
|
149
|
+
`winget install DeepVoiceAI.DVAIHub`) → mobile apps on the same Wi-Fi
|
|
150
|
+
pair with it and offload heavy inference. [Guide →](https://dvai-bridge.deepvoiceai.co/guide/dvai-hub)
|
|
151
|
+
- **External-engine bridge.** Hub surfaces Ollama / LM Studio / vLLM /
|
|
152
|
+
llama-server / llamafile as additional backend pools so paired apps
|
|
153
|
+
serve from whatever's already cached. Opt-in per engine.
|
|
154
|
+
- **Strict substitution policy.** Models with mismatched family / version /
|
|
155
|
+
size / type are refused by default; quant-only mismatches gated behind a
|
|
156
|
+
per-pairing `preferBetterQuant` flag. No silent mis-routing.
|
|
157
|
+
- **HMAC-signed identity** on `/v1/chat/completions`. Per-app audit logs
|
|
158
|
+
surface who served what, with structured `(appId, peerDeviceId,
|
|
159
|
+
engine, requestedModel, servedModel, outcome)` rows.
|
|
160
|
+
- **Library finalization.** `httpBindHost` (LAN bind), `chatCompletionInterceptor`
|
|
161
|
+
(extension point), HMAC primitives re-exported, `/v1/dvai/*` routes
|
|
162
|
+
actually dispatched, TransformersBackend Node-mode device fix.
|
|
163
|
+
[Migration v3.0 → v3.1 →](https://dvai-bridge.deepvoiceai.co/migration/v3.0-to-v3.1)
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Robustness
|
|
168
|
+
|
|
169
|
+
Streaming-correct (SSE passthrough + blank-chunk detection), generation
|
|
170
|
+
timeout, automatic engine-state recovery on fatal errors, port fallback,
|
|
171
|
+
worker offloading, Private Network Access ready, CORS configured. The
|
|
172
|
+
boring substrate so your agent code never has to think about it.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Licensing
|
|
177
|
+
|
|
178
|
+
Dual: **free for development & personal use** on `localhost` (verified at
|
|
179
|
+
runtime). **Commercial use** requires a license key — `info@deepvoiceai.co`.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
PRs welcome.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pnpm install
|
|
189
|
+
pnpm build
|
|
190
|
+
bash scripts/build-all.sh # full matrix (auto-skips per-host)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the PR flow. Per-platform
|
|
194
|
+
contributor docs (iOS / Android / RN / Flutter / .NET) under
|
|
195
|
+
[`docs/development/`](./docs/development/).
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
© Deep Voice AI Limited. All rights reserved.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DVAIBridgeMLX: () => DVAIBridgeMLX,
|
|
24
|
+
default: () => index_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_core = require("@capacitor/core");
|
|
28
|
+
var DVAIBridgeMLX = (0, import_core.registerPlugin)("DVAIBridgeMLX");
|
|
29
|
+
var index_default = DVAIBridgeMLX;
|
|
30
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
31
|
+
0 && (module.exports = {
|
|
32
|
+
DVAIBridgeMLX
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\r\nimport type { NativePluginInterface } from \"@dvai-bridge/capacitor\";\r\n\r\nconst DVAIBridgeMLX = registerPlugin<NativePluginInterface>(\"DVAIBridgeMLX\");\r\n\r\nexport default DVAIBridgeMLX;\r\nexport { DVAIBridgeMLX };\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA+B;AAG/B,IAAM,oBAAgB,4BAAsC,eAAe;AAE3E,IAAO,gBAAQ;","names":[]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\r\nimport type { NativePluginInterface } from \"@dvai-bridge/capacitor\";\r\n\r\nconst DVAIBridgeMLX = registerPlugin<NativePluginInterface>(\"DVAIBridgeMLX\");\r\n\r\nexport default DVAIBridgeMLX;\r\nexport { DVAIBridgeMLX };\r\n"],"mappings":";AAAA,SAAS,sBAAsB;AAG/B,IAAM,gBAAgB,eAAsC,eAAe;AAE3E,IAAO,gBAAQ;","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "DVAICapacitorMLX",
|
|
6
|
+
platforms: [.iOS(.v17), .macOS(.v14)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(name: "DVAICapacitorMLX", targets: ["DVAICapacitorMLX"]),
|
|
9
|
+
],
|
|
10
|
+
dependencies: [
|
|
11
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm", branch: "main"),
|
|
12
|
+
// Path dep to the MLX core package's ROOT (not /ios). Identity
|
|
13
|
+
// derived from path's last dir = "dvai-bridge-ios-mlx-core".
|
|
14
|
+
.package(path: "../../dvai-bridge-ios-mlx-core"),
|
|
15
|
+
],
|
|
16
|
+
targets: [
|
|
17
|
+
.target(
|
|
18
|
+
name: "DVAICapacitorMLX",
|
|
19
|
+
dependencies: [
|
|
20
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
21
|
+
.product(name: "Cordova", package: "capacitor-swift-pm"),
|
|
22
|
+
.product(name: "DVAIMLXCore", package: "dvai-bridge-ios-mlx-core"),
|
|
23
|
+
],
|
|
24
|
+
path: "Sources/DVAICapacitorMLX"
|
|
25
|
+
),
|
|
26
|
+
.testTarget(
|
|
27
|
+
name: "DVAICapacitorMLXTests",
|
|
28
|
+
dependencies: [
|
|
29
|
+
"DVAICapacitorMLX",
|
|
30
|
+
.product(name: "DVAIMLXCore", package: "dvai-bridge-ios-mlx-core"),
|
|
31
|
+
],
|
|
32
|
+
path: "Tests/DVAICapacitorMLXTests"
|
|
33
|
+
),
|
|
34
|
+
]
|
|
35
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import DVAIMLXCore
|
|
3
|
+
|
|
4
|
+
#if canImport(Capacitor)
|
|
5
|
+
import Capacitor
|
|
6
|
+
|
|
7
|
+
@objc(DVAIBridgeMLXPlugin)
|
|
8
|
+
public class DVAIBridgeMLXPlugin: CAPPlugin {
|
|
9
|
+
private let state = MLXPluginState()
|
|
10
|
+
|
|
11
|
+
public override func load() {
|
|
12
|
+
super.load()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// MARK: - Lifecycle
|
|
16
|
+
|
|
17
|
+
@objc func start(_ call: CAPPluginCall) {
|
|
18
|
+
let opts: [String: Any] = (call.options as? [String: Any]) ?? [:]
|
|
19
|
+
Task { [weak self] in
|
|
20
|
+
guard let self else { return }
|
|
21
|
+
self.notifyListeners("progress", data: ["phase": "load"])
|
|
22
|
+
do {
|
|
23
|
+
let result = try await self.state.start(opts: opts)
|
|
24
|
+
self.notifyListeners("progress", data: ["phase": "ready"])
|
|
25
|
+
call.resolve(result)
|
|
26
|
+
} catch {
|
|
27
|
+
self.notifyListeners("progress", data: [
|
|
28
|
+
"phase": "error",
|
|
29
|
+
"message": error.localizedDescription,
|
|
30
|
+
])
|
|
31
|
+
call.reject(error.localizedDescription)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@objc func stop(_ call: CAPPluginCall) {
|
|
37
|
+
Task {
|
|
38
|
+
do {
|
|
39
|
+
try await state.stop()
|
|
40
|
+
call.resolve()
|
|
41
|
+
} catch {
|
|
42
|
+
call.reject(error.localizedDescription)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc func status(_ call: CAPPluginCall) {
|
|
48
|
+
Task {
|
|
49
|
+
let info = await state.statusInfo()
|
|
50
|
+
call.resolve(info)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// MARK: - Model download / cache
|
|
55
|
+
//
|
|
56
|
+
// mlx-swift-lm uses HuggingFace Hub's local cache (~/Library/Caches/.../
|
|
57
|
+
// huggingface). We don't expose download/list/delete here because:
|
|
58
|
+
// - Downloads happen implicitly on the first start() with a given
|
|
59
|
+
// modelPath (the HF id). Subsequent starts are cache-hits.
|
|
60
|
+
// - Listing / deleting cached MLX models is a HF-Hub-cache operation,
|
|
61
|
+
// not something the bridge itself controls.
|
|
62
|
+
// Phase 3D may expose pass-through helpers; for now the methods reject
|
|
63
|
+
// by design.
|
|
64
|
+
|
|
65
|
+
private static let modelMgmtNotApplicable =
|
|
66
|
+
"MLX backend uses HuggingFace Hub's local cache; explicit download/list/delete is not exposed in this version. Pass an HF model id via the `modelPath` start option and let MLX handle caching."
|
|
67
|
+
|
|
68
|
+
@objc func downloadModel(_ call: CAPPluginCall) {
|
|
69
|
+
call.reject(Self.modelMgmtNotApplicable)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@objc func listCachedModels(_ call: CAPPluginCall) {
|
|
73
|
+
call.reject(Self.modelMgmtNotApplicable)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@objc func deleteCachedModel(_ call: CAPPluginCall) {
|
|
77
|
+
call.reject(Self.modelMgmtNotApplicable)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@objc func cacheDir(_ call: CAPPluginCall) {
|
|
81
|
+
call.reject(Self.modelMgmtNotApplicable)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
#endif
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Smoke test for DVAICapacitorMLX. Just verifies the bundle loads and
|
|
2
|
+
// the underlying MLXPluginState boots into the not-running state.
|
|
3
|
+
|
|
4
|
+
import XCTest
|
|
5
|
+
@testable import DVAICapacitorMLX
|
|
6
|
+
import DVAIMLXCore
|
|
7
|
+
|
|
8
|
+
final class SmokeTest: XCTestCase {
|
|
9
|
+
func testMLXPluginStateInitiallyNotRunning() async {
|
|
10
|
+
let state = MLXPluginState()
|
|
11
|
+
let info = await state.statusInfo()
|
|
12
|
+
XCTAssertEqual(info["running"] as? Bool, false)
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dvai-bridge/capacitor-mlx",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"registry": "https://registry.npmjs.org/",
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "DVAI-Bridge Capacitor plugin: MLX (Apple Silicon GPU/Neural Engine) LLM inference via mlx-swift-lm, with embedded HTTP server. iOS 17+ at link time; Apple-Silicon-only at runtime.",
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"ios/Sources",
|
|
23
|
+
"ios/Tests",
|
|
24
|
+
"ios/Package.swift",
|
|
25
|
+
"DVAICapacitorMLX.podspec",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"dvai-bridge",
|
|
31
|
+
"capacitor",
|
|
32
|
+
"mlx",
|
|
33
|
+
"apple-silicon",
|
|
34
|
+
"llm",
|
|
35
|
+
"on-device"
|
|
36
|
+
],
|
|
37
|
+
"author": "Deep Chakraborty <https://github.com/dk013>",
|
|
38
|
+
"license": "Custom (See LICENSE)",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/westenets/dvai-bridge.git"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@capacitor/core": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
|
45
|
+
"@dvai-bridge/capacitor": "4.0.0",
|
|
46
|
+
"@dvai-bridge/ios-mlx-core": "4.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@dvai-bridge/capacitor": "4.0.0"
|
|
50
|
+
},
|
|
51
|
+
"capacitor": {
|
|
52
|
+
"ios": {
|
|
53
|
+
"src": "ios"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"mac:build": "pwsh -File ../../scripts/mac-build.ps1 -Action build -Target capacitor-mlx",
|
|
59
|
+
"mac:test": "pwsh -File ../../scripts/mac-build.ps1 -Action test -Target capacitor-mlx"
|
|
60
|
+
}
|
|
61
|
+
}
|