@zerogpu/expo-android-sdk 0.0.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 +97 -0
- package/android/build.gradle +51 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/ai/zerogpu/expo/ZeroGpuAndroidSdkModule.kt +55 -0
- package/app.plugin.js +4 -0
- package/build/ZeroGpuAndroidSdkModule.d.ts +13 -0
- package/build/ZeroGpuAndroidSdkModule.d.ts.map +1 -0
- package/build/ZeroGpuAndroidSdkModule.js +4 -0
- package/build/ZeroGpuAndroidSdkModule.js.map +1 -0
- package/build/index.d.ts +20 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +23 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +13 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ZeroGpuAndroidSdk.podspec +21 -0
- package/ios/ZeroGpuAndroidSdkModule.swift +17 -0
- package/package.json +68 -0
- package/plugin/build/index.d.ts +3 -0
- package/plugin/build/index.js +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# @zerogpu/expo-android-sdk
|
|
2
|
+
|
|
3
|
+
Expo / React Native module that turns an **Android** device into an on-device
|
|
4
|
+
inference node on the [ZeroGPU](https://zerogpu.ai) network. It wraps the native
|
|
5
|
+
`ai.zerogpu:android-sdk` AAR and exposes a tiny JS API.
|
|
6
|
+
|
|
7
|
+
> **Android-only.** iOS builds link a no-op stub so cross-platform apps still
|
|
8
|
+
> compile, but the device only acts as a node on Android. The native library ships
|
|
9
|
+
> `arm64-v8a` (phones) and `x86_64` (emulators) only.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx expo install @zerogpu/expo-android-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Add the config plugin to `app.json` / `app.config.js`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"expo": {
|
|
22
|
+
"plugins": ["@zerogpu/expo-android-sdk"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then regenerate the native project:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx expo prebuild
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The plugin wires up the two app-level Android needs automatically:
|
|
34
|
+
|
|
35
|
+
- a `pickFirst '**/libc++_shared.so'` packaging rule (the AAR bundles native libs
|
|
36
|
+
from ONNX Runtime **and** llama.cpp, both of which ship `libc++_shared.so`), and
|
|
37
|
+
- raises `minSdkVersion` to **26** if your app is lower (never lowers it).
|
|
38
|
+
|
|
39
|
+
Permissions (`INTERNET`, `ACCESS_NETWORK_STATE`) and `largeHeap` come from the AAR
|
|
40
|
+
manifest and merge in automatically — nothing to add.
|
|
41
|
+
|
|
42
|
+
> Requires an Expo app using `expo-modules-core` (managed or bare). This is not a
|
|
43
|
+
> plain-React-Native library.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { start, stop } from '@zerogpu/expo-android-sdk';
|
|
49
|
+
|
|
50
|
+
// Start the node (typically once, e.g. in your root component / on app launch).
|
|
51
|
+
await start('zgpu-sdk-xxxxxxxx'); // your operator key from https://zerogpu.ai
|
|
52
|
+
|
|
53
|
+
// ...later, on teardown:
|
|
54
|
+
await stop();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Keep the operator key out of source control (e.g. via `expo-constants` /
|
|
58
|
+
environment config), just like any secret.
|
|
59
|
+
|
|
60
|
+
`start()` is idempotent, and the native SDK manages the foreground/background socket
|
|
61
|
+
itself — you do **not** need to wire it to `AppState`. On iOS both calls resolve as
|
|
62
|
+
no-ops.
|
|
63
|
+
|
|
64
|
+
### Options
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
await start('zgpu-sdk-…', {
|
|
68
|
+
baseUrl: 'https://devices.zerogpu.ai', // default
|
|
69
|
+
env: 'production', // default
|
|
70
|
+
enableTelemetry: true, // default
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Verifying it works
|
|
75
|
+
|
|
76
|
+
Run the app on an `arm64-v8a` phone or an `x86_64` emulator (Android 8.0+), then
|
|
77
|
+
watch logcat:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
adb logcat -s ZeroGpuSdk:* ZeroGpuWebSocket:*
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You want to see `status":"idle"` and `WebSocket ready for tasks`. Send an inference
|
|
84
|
+
request to `https://api.zerogpu.ai/v1/chat/completions` with your **API key** and
|
|
85
|
+
match the response `id` to the `requestId` in logcat — see the SDK's
|
|
86
|
+
[INTEGRATION.md](../app/INTEGRATION.md) for the full end-to-end check.
|
|
87
|
+
|
|
88
|
+
## Versioning
|
|
89
|
+
|
|
90
|
+
This package is published from the same release as the AAR and always carries the
|
|
91
|
+
**same version** as `ai.zerogpu:android-sdk`. The AAR coordinate is derived from this
|
|
92
|
+
package's own `package.json`, so an installed npm version always pulls the matching
|
|
93
|
+
AAR.
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
Apache-2.0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import groovy.json.JsonSlurper
|
|
2
|
+
|
|
3
|
+
apply plugin: 'com.android.library'
|
|
4
|
+
apply plugin: 'kotlin-android'
|
|
5
|
+
|
|
6
|
+
group = 'ai.zerogpu.expo'
|
|
7
|
+
version = '0.0.2'
|
|
8
|
+
|
|
9
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
|
+
apply from: expoModulesCorePlugin
|
|
11
|
+
applyKotlinExpoModulesCorePlugin()
|
|
12
|
+
useCoreDependencies()
|
|
13
|
+
useExpoPublishing()
|
|
14
|
+
|
|
15
|
+
buildscript {
|
|
16
|
+
// Lets the consuming app override versions we declare here.
|
|
17
|
+
ext.safeExtGet = { prop, fallback ->
|
|
18
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Single source of truth for the wrapped AAR version: the package.json this module
|
|
23
|
+
// ships in. In a consumer app this resolves inside node_modules/@zerogpu/expo-android-sdk,
|
|
24
|
+
// so the npm package and the AAR it pulls always match — no drift possible.
|
|
25
|
+
def zeroGpuSdkVersion = new JsonSlurper().parse(file("../package.json")).version
|
|
26
|
+
|
|
27
|
+
android {
|
|
28
|
+
namespace "ai.zerogpu.expo"
|
|
29
|
+
defaultConfig {
|
|
30
|
+
// The native SDK requires API 26+. Never lower than the SDK's own minSdk.
|
|
31
|
+
minSdkVersion Math.max(safeExtGet("minSdkVersion", 26) as int, 26)
|
|
32
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
33
|
+
versionCode 1
|
|
34
|
+
versionName version.toString()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
compileSdk safeExtGet("compileSdkVersion", 34)
|
|
38
|
+
|
|
39
|
+
// The consuming app must add the app-level pickFirst for libc++_shared.so — the
|
|
40
|
+
// config plugin does that automatically at prebuild time.
|
|
41
|
+
|
|
42
|
+
publishing {
|
|
43
|
+
singleVariant("release") {
|
|
44
|
+
withSourcesJar()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
dependencies {
|
|
50
|
+
implementation "ai.zerogpu:android-sdk:${zeroGpuSdkVersion}"
|
|
51
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package ai.zerogpu.expo
|
|
2
|
+
|
|
3
|
+
import ai.zerogpu.android.sdk.ZeroGpuSdk
|
|
4
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
5
|
+
import expo.modules.kotlin.modules.Module
|
|
6
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expo module that bridges the native ZeroGPU Android SDK to JavaScript.
|
|
10
|
+
*
|
|
11
|
+
* The SDK drives its own foreground/background lifecycle via ProcessLifecycleOwner,
|
|
12
|
+
* so the bridge only constructs the instance and calls start()/stop(). Because
|
|
13
|
+
* ZeroGpuSdk is single-use (not restartable after stop()), we hold a nullable
|
|
14
|
+
* reference and always build a fresh instance on start().
|
|
15
|
+
*/
|
|
16
|
+
class ZeroGpuAndroidSdkModule : Module() {
|
|
17
|
+
// null == not started.
|
|
18
|
+
private var sdk: ZeroGpuSdk? = null
|
|
19
|
+
|
|
20
|
+
override fun definition() = ModuleDefinition {
|
|
21
|
+
// Must match requireNativeModule('ZeroGpuAndroidSdk') on the JS side.
|
|
22
|
+
Name("ZeroGpuAndroidSdk")
|
|
23
|
+
|
|
24
|
+
AsyncFunction("start") { operatorKey: String, options: Map<String, Any?>? ->
|
|
25
|
+
if (sdk != null) return@AsyncFunction // idempotent: already running
|
|
26
|
+
|
|
27
|
+
val appCtx = appContext.reactContext?.applicationContext
|
|
28
|
+
?: throw Exceptions.ReactContextLost()
|
|
29
|
+
|
|
30
|
+
sdk = ZeroGpuSdk(
|
|
31
|
+
context = appCtx,
|
|
32
|
+
operatorKey = operatorKey,
|
|
33
|
+
baseUrl = (options?.get("baseUrl") as? String) ?: DEFAULT_BASE_URL,
|
|
34
|
+
env = (options?.get("env") as? String) ?: DEFAULT_ENV,
|
|
35
|
+
enableTelemetry = (options?.get("enableTelemetry") as? Boolean) ?: true,
|
|
36
|
+
).also { it.start() }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
AsyncFunction("stop") {
|
|
40
|
+
sdk?.stop()
|
|
41
|
+
sdk = null // drop the dead instance; a later start() builds a fresh one
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Safety net if the JS context is torn down without an explicit stop().
|
|
45
|
+
OnDestroy {
|
|
46
|
+
sdk?.stop()
|
|
47
|
+
sdk = null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private companion object {
|
|
52
|
+
const val DEFAULT_BASE_URL = "https://devices.zerogpu.ai"
|
|
53
|
+
const val DEFAULT_ENV = "production"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Config-plugin entrypoint. Expo resolves `app.plugin.js` at the package root when
|
|
2
|
+
// this package name appears in an app's `plugins` array. The real implementation is
|
|
3
|
+
// compiled from plugin/src -> plugin/build by `npm run build:plugin`.
|
|
4
|
+
module.exports = require('./plugin/build');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ZeroGpuOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Native-module contract. Backed by the Android implementation
|
|
4
|
+
* (`ai.zerogpu.expo.ZeroGpuAndroidSdkModule`) and a no-op iOS stub, so both
|
|
5
|
+
* methods resolve on every platform.
|
|
6
|
+
*/
|
|
7
|
+
export interface ZeroGpuAndroidSdkNativeModule {
|
|
8
|
+
start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;
|
|
9
|
+
stop(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
declare const _default: ZeroGpuAndroidSdkNativeModule;
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=ZeroGpuAndroidSdkModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZeroGpuAndroidSdkModule.d.ts","sourceRoot":"","sources":["../src/ZeroGpuAndroidSdkModule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;;AAGD,wBAAuF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZeroGpuAndroidSdkModule.js","sourceRoot":"","sources":["../src/ZeroGpuAndroidSdkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAcxD,oEAAoE;AACpE,eAAe,mBAAmB,CAAgC,mBAAmB,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nimport type { ZeroGpuOptions } from './types';\n\n/**\n * Native-module contract. Backed by the Android implementation\n * (`ai.zerogpu.expo.ZeroGpuAndroidSdkModule`) and a no-op iOS stub, so both\n * methods resolve on every platform.\n */\nexport interface ZeroGpuAndroidSdkNativeModule {\n start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;\n stop(): Promise<void>;\n}\n\n// The name must match `Name(...)` in the native module definitions.\nexport default requireNativeModule<ZeroGpuAndroidSdkNativeModule>('ZeroGpuAndroidSdk');\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ZeroGpuOptions } from './types';
|
|
2
|
+
export type { ZeroGpuOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Start the ZeroGPU node. The device registers with the ZeroGPU backend using the
|
|
5
|
+
* operator key, downloads its assigned model(s), and serves on-device inference
|
|
6
|
+
* while the app is foregrounded (the native SDK manages the socket lifecycle).
|
|
7
|
+
*
|
|
8
|
+
* Idempotent: calling `start` again while already running is a no-op. On iOS this
|
|
9
|
+
* resolves without doing anything — the SDK is Android-only.
|
|
10
|
+
*
|
|
11
|
+
* @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).
|
|
12
|
+
* @param options Optional base URL / environment / telemetry overrides.
|
|
13
|
+
*/
|
|
14
|
+
export declare function start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Stop the ZeroGPU node: close the socket, release model adapters, and shut the
|
|
17
|
+
* SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stop(): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ZeroGpuAndroidSdkModule from './ZeroGpuAndroidSdkModule';
|
|
2
|
+
/**
|
|
3
|
+
* Start the ZeroGPU node. The device registers with the ZeroGPU backend using the
|
|
4
|
+
* operator key, downloads its assigned model(s), and serves on-device inference
|
|
5
|
+
* while the app is foregrounded (the native SDK manages the socket lifecycle).
|
|
6
|
+
*
|
|
7
|
+
* Idempotent: calling `start` again while already running is a no-op. On iOS this
|
|
8
|
+
* resolves without doing anything — the SDK is Android-only.
|
|
9
|
+
*
|
|
10
|
+
* @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).
|
|
11
|
+
* @param options Optional base URL / environment / telemetry overrides.
|
|
12
|
+
*/
|
|
13
|
+
export function start(operatorKey, options) {
|
|
14
|
+
return ZeroGpuAndroidSdkModule.start(operatorKey, options);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Stop the ZeroGPU node: close the socket, release model adapters, and shut the
|
|
18
|
+
* SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.
|
|
19
|
+
*/
|
|
20
|
+
export function stop() {
|
|
21
|
+
return ZeroGpuAndroidSdkModule.stop();
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;AAKhE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,WAAmB,EAAE,OAAwB;IACjE,OAAO,uBAAuB,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI;IAClB,OAAO,uBAAuB,CAAC,IAAI,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import ZeroGpuAndroidSdkModule from './ZeroGpuAndroidSdkModule';\nimport type { ZeroGpuOptions } from './types';\n\nexport type { ZeroGpuOptions } from './types';\n\n/**\n * Start the ZeroGPU node. The device registers with the ZeroGPU backend using the\n * operator key, downloads its assigned model(s), and serves on-device inference\n * while the app is foregrounded (the native SDK manages the socket lifecycle).\n *\n * Idempotent: calling `start` again while already running is a no-op. On iOS this\n * resolves without doing anything — the SDK is Android-only.\n *\n * @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).\n * @param options Optional base URL / environment / telemetry overrides.\n */\nexport function start(operatorKey: string, options?: ZeroGpuOptions): Promise<void> {\n return ZeroGpuAndroidSdkModule.start(operatorKey, options);\n}\n\n/**\n * Stop the ZeroGPU node: close the socket, release model adapters, and shut the\n * SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.\n */\nexport function stop(): Promise<void> {\n return ZeroGpuAndroidSdkModule.stop();\n}\n"]}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional overrides for {@link start}. All fields default to production values
|
|
3
|
+
* inside the native SDK, so a typical caller only passes the operator key.
|
|
4
|
+
*/
|
|
5
|
+
export type ZeroGpuOptions = {
|
|
6
|
+
/** Device-registration base URL. Defaults to `https://devices.zerogpu.ai`. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** Environment name. Defaults to `production`. */
|
|
9
|
+
env?: string;
|
|
10
|
+
/** Whether the SDK reports anonymous telemetry. Defaults to `true`. */
|
|
11
|
+
enableTelemetry?: boolean;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Optional overrides for {@link start}. All fields default to production values\n * inside the native SDK, so a typical caller only passes the operator key.\n */\nexport type ZeroGpuOptions = {\n /** Device-registration base URL. Defaults to `https://devices.zerogpu.ai`. */\n baseUrl?: string;\n /** Environment name. Defaults to `production`. */\n env?: string;\n /** Whether the SDK reports anonymous telemetry. Defaults to `true`. */\n enableTelemetry?: boolean;\n};\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Pod::Spec.new do |s|
|
|
2
|
+
s.name = 'ZeroGpuAndroidSdk'
|
|
3
|
+
s.version = '0.0.2'
|
|
4
|
+
s.summary = 'iOS no-op stub for @zerogpu/expo-android-sdk (the SDK is Android-only).'
|
|
5
|
+
s.description = 'Keeps cross-platform Expo apps building on iOS. start()/stop() resolve without doing anything; the device is only a ZeroGPU node on Android.'
|
|
6
|
+
s.author = 'ZeroGPU'
|
|
7
|
+
s.homepage = 'https://github.com/zerogpu/zerogpu-android-sdk'
|
|
8
|
+
s.platforms = { :ios => '13.4', :tvos => '13.4' }
|
|
9
|
+
s.source = { git: 'https://github.com/zerogpu/zerogpu-android-sdk.git' }
|
|
10
|
+
s.static_framework = true
|
|
11
|
+
s.license = { :type => 'Apache-2.0' }
|
|
12
|
+
|
|
13
|
+
s.dependency 'ExpoModulesCore'
|
|
14
|
+
|
|
15
|
+
s.pod_target_xcconfig = {
|
|
16
|
+
'DEFINES_MODULE' => 'YES',
|
|
17
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
s.source_files = '**/*.{h,m,mm,swift,hpp,cpp}'
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
// The ZeroGPU SDK is Android-only. This iOS module exists solely so cross-platform
|
|
4
|
+
// Expo apps compile and link; both functions resolve immediately as no-ops.
|
|
5
|
+
public class ZeroGpuAndroidSdkModule: Module {
|
|
6
|
+
public func definition() -> ModuleDefinition {
|
|
7
|
+
Name("ZeroGpuAndroidSdk")
|
|
8
|
+
|
|
9
|
+
AsyncFunction("start") { (_ operatorKey: String, _ options: [String: Any]?) in
|
|
10
|
+
// No-op on iOS.
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
AsyncFunction("stop") {
|
|
14
|
+
// No-op on iOS.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zerogpu/expo-android-sdk",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Expo/React Native module that runs a ZeroGPU on-device inference node on Android by wrapping the native ai.zerogpu:android-sdk AAR.",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p tsconfig.json",
|
|
9
|
+
"build:plugin": "tsc -p plugin/tsconfig.json",
|
|
10
|
+
"clean": "expo-module clean",
|
|
11
|
+
"lint": "expo-module lint",
|
|
12
|
+
"test": "expo-module test",
|
|
13
|
+
"prepare": "npm run build && npm run build:plugin",
|
|
14
|
+
"prepublishOnly": "npm run build && npm run build:plugin",
|
|
15
|
+
"expo-module": "expo-module"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"zerogpu",
|
|
19
|
+
"expo",
|
|
20
|
+
"react-native",
|
|
21
|
+
"android",
|
|
22
|
+
"on-device",
|
|
23
|
+
"inference",
|
|
24
|
+
"slm"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/zerogpu/zerogpu-android-sdk.git",
|
|
29
|
+
"directory": "expo-android-sdk"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/zerogpu/zerogpu-android-sdk/issues"
|
|
33
|
+
},
|
|
34
|
+
"author": "ZeroGPU <hello@zerogpu.ai>",
|
|
35
|
+
"license": "Apache-2.0",
|
|
36
|
+
"homepage": "https://github.com/zerogpu/zerogpu-android-sdk/tree/develop/expo-android-sdk",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"registry": "https://registry.npmjs.org/"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"build",
|
|
43
|
+
"plugin/build",
|
|
44
|
+
"android",
|
|
45
|
+
"ios",
|
|
46
|
+
"app.plugin.js",
|
|
47
|
+
"expo-module.config.json",
|
|
48
|
+
"!android/build",
|
|
49
|
+
"!android/.gradle"
|
|
50
|
+
],
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"expo": "*",
|
|
53
|
+
"react": "*",
|
|
54
|
+
"react-native": "*"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/react": "~18.2.79",
|
|
58
|
+
"expo": "^51.0.0",
|
|
59
|
+
"expo-module-scripts": "^3.5.2",
|
|
60
|
+
"expo-modules-core": "^1.12.0",
|
|
61
|
+
"react": "18.2.0",
|
|
62
|
+
"react-native": "0.74.5",
|
|
63
|
+
"typescript": "^5.3.3"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@expo/config-plugins": "^8.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
4
|
+
const pkg = require('../../package.json');
|
|
5
|
+
/** The minimum Android API level the native SDK requires. */
|
|
6
|
+
const MIN_SDK_VERSION = 26;
|
|
7
|
+
/** Marker so the packaging block is injected exactly once (survives `prebuild --clean`). */
|
|
8
|
+
const PICK_FIRST_MARKER = '// @zerogpu/expo-android-sdk: pickFirst libc++_shared';
|
|
9
|
+
/**
|
|
10
|
+
* The AAR bundles native `.so` files from both ONNX Runtime and llama.cpp, each
|
|
11
|
+
* shipping their own `libc++_shared.so`. Without `pickFirst`, the app-module merge
|
|
12
|
+
* fails on the duplicate. This block is app-module-level and does not propagate from
|
|
13
|
+
* the AAR, so the plugin must inject it into the consuming app's build.gradle.
|
|
14
|
+
*/
|
|
15
|
+
const withPickFirstLibcxx = (config) => (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => {
|
|
16
|
+
if (cfg.modResults.language !== 'groovy') {
|
|
17
|
+
throw new Error('@zerogpu/expo-android-sdk config plugin only supports Groovy android/app/build.gradle.');
|
|
18
|
+
}
|
|
19
|
+
let contents = cfg.modResults.contents;
|
|
20
|
+
if (contents.includes(PICK_FIRST_MARKER)) {
|
|
21
|
+
return cfg; // already applied — idempotent
|
|
22
|
+
}
|
|
23
|
+
const block = [
|
|
24
|
+
'',
|
|
25
|
+
` ${PICK_FIRST_MARKER}`,
|
|
26
|
+
' packagingOptions {',
|
|
27
|
+
' jniLibs {',
|
|
28
|
+
" pickFirst '**/libc++_shared.so'",
|
|
29
|
+
' }',
|
|
30
|
+
' }',
|
|
31
|
+
].join('\n');
|
|
32
|
+
// Insert immediately after the opening of the top-level `android {` block.
|
|
33
|
+
let inserted = false;
|
|
34
|
+
contents = contents.replace(/android\s*\{/, (match) => {
|
|
35
|
+
inserted = true;
|
|
36
|
+
return `${match}\n${block}`;
|
|
37
|
+
});
|
|
38
|
+
if (!inserted) {
|
|
39
|
+
throw new Error('@zerogpu/expo-android-sdk: could not find an `android { }` block in android/app/build.gradle.');
|
|
40
|
+
}
|
|
41
|
+
cfg.modResults.contents = contents;
|
|
42
|
+
return cfg;
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Raise `android.minSdkVersion` to at least {@link MIN_SDK_VERSION}. Only ever raises,
|
|
46
|
+
* never lowers, so an app already on a higher minSdk (or `expo-build-properties`) is
|
|
47
|
+
* left alone. The Expo Android template reads this gradle property for its minSdk.
|
|
48
|
+
*/
|
|
49
|
+
const withMinSdk = (config) => (0, config_plugins_1.withGradleProperties)(config, (cfg) => {
|
|
50
|
+
const key = 'android.minSdkVersion';
|
|
51
|
+
const existing = cfg.modResults.find((item) => item.type === 'property' && item.key === key);
|
|
52
|
+
const current = existing ? parseInt(existing.value, 10) || 0 : 0;
|
|
53
|
+
const next = Math.max(current, MIN_SDK_VERSION);
|
|
54
|
+
if (existing) {
|
|
55
|
+
existing.value = String(next);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
cfg.modResults.push({ type: 'property', key, value: String(next) });
|
|
59
|
+
}
|
|
60
|
+
return cfg;
|
|
61
|
+
});
|
|
62
|
+
const withZeroGpu = (config) => {
|
|
63
|
+
config = withPickFirstLibcxx(config);
|
|
64
|
+
config = withMinSdk(config);
|
|
65
|
+
return config;
|
|
66
|
+
};
|
|
67
|
+
// Guarantees the plugin runs once even if listed multiple times.
|
|
68
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withZeroGpu, pkg.name, pkg.version);
|