@dvai-bridge/android 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/LICENSE +51 -0
- package/README.md +199 -0
- package/android/build.gradle +165 -0
- package/android/gradle.properties +5 -0
- package/android/settings.gradle +1 -0
- package/android/src/androidTest/java/co/deepvoiceai/bridge/RealModelIntegrationTest.kt +162 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/co/deepvoiceai/bridge/BackendKind.kt +21 -0
- package/android/src/main/java/co/deepvoiceai/bridge/BackendSelector.kt +28 -0
- package/android/src/main/java/co/deepvoiceai/bridge/BoundServer.kt +39 -0
- package/android/src/main/java/co/deepvoiceai/bridge/DVAIBridge.kt +642 -0
- package/android/src/main/java/co/deepvoiceai/bridge/DVAIBridgeConfig.kt +119 -0
- package/android/src/main/java/co/deepvoiceai/bridge/DVAIBridgeError.kt +51 -0
- package/android/src/main/java/co/deepvoiceai/bridge/OffloadProxy.kt +574 -0
- package/android/src/main/java/co/deepvoiceai/bridge/ProgressBroadcaster.kt +55 -0
- package/android/src/main/java/co/deepvoiceai/bridge/ProgressEvent.kt +25 -0
- package/android/src/main/java/co/deepvoiceai/bridge/ReactiveState.kt +60 -0
- package/android/src/main/java/co/deepvoiceai/bridge/license/Audience.kt +134 -0
- package/android/src/main/java/co/deepvoiceai/bridge/license/Discovery.kt +146 -0
- package/android/src/main/java/co/deepvoiceai/bridge/license/LicenseTypes.kt +158 -0
- package/android/src/main/java/co/deepvoiceai/bridge/license/LicenseValidator.kt +400 -0
- package/android/src/main/java/co/deepvoiceai/bridge/license/PublicKeys.kt +91 -0
- package/android/src/test/java/co/deepvoiceai/bridge/BackendSelectorTest.kt +76 -0
- package/android/src/test/java/co/deepvoiceai/bridge/CapabilityPrecheckTest.kt +117 -0
- package/android/src/test/java/co/deepvoiceai/bridge/DVAIBridgeAPIShapeTest.kt +86 -0
- package/android/src/test/java/co/deepvoiceai/bridge/OffloadProxyDecisionTest.kt +144 -0
- package/android/src/test/java/co/deepvoiceai/bridge/OffloadProxyForwardingTest.kt +327 -0
- package/android/src/test/java/co/deepvoiceai/bridge/ProgressBroadcasterTest.kt +56 -0
- package/android/src/test/java/co/deepvoiceai/bridge/license/LicenseValidatorTest.kt +539 -0
- package/package.json +19 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
package co.deepvoiceai.bridge
|
|
2
|
+
|
|
3
|
+
import co.deepvoiceai.bridge.shared.core.CorsConfig
|
|
4
|
+
import co.deepvoiceai.bridge.shared.core.offload.OffloadConfig
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options accepted by [DVAIBridge.start]. Mirrors the iOS DVAIBridge
|
|
8
|
+
* `StartOptions` struct + the Capacitor JS shim's StartOptions.
|
|
9
|
+
*
|
|
10
|
+
* @param backend Which backend to use. [BackendKind.Auto] resolves
|
|
11
|
+
* via [BackendSelector] at start-time.
|
|
12
|
+
* @param modelPath Filesystem path to the model checkpoint. Required
|
|
13
|
+
* for Llama (.gguf), MediaPipe (.task), and LiteRT
|
|
14
|
+
* (.tflite / .litertlm) backends.
|
|
15
|
+
* @param tokenizerPath Filesystem path to a directory containing
|
|
16
|
+
* tokenizer.json (and optional tokenizer_config.json).
|
|
17
|
+
* Required for the LiteRT backend; ignored otherwise.
|
|
18
|
+
* @param mmprojPath Optional multimodal projector path for Llama
|
|
19
|
+
* (vision/audio LLMs).
|
|
20
|
+
* @param chatTemplate Optional Jinja chat template override (Llama backend
|
|
21
|
+
* only). Falls back to the model's bundled template.
|
|
22
|
+
* @param gpuLayers Llama backend: number of transformer layers to
|
|
23
|
+
* offload to GPU (Vulkan / OpenCL on supported
|
|
24
|
+
* devices). 99 = all layers, 0 = CPU only. Ignored
|
|
25
|
+
* by other backends.
|
|
26
|
+
* @param contextSize Context window in tokens. Defaults to 2048.
|
|
27
|
+
* @param threads CPU thread count for the inference loop. Llama
|
|
28
|
+
* uses this directly; LiteRT/MediaPipe pick their
|
|
29
|
+
* own threading by default.
|
|
30
|
+
* @param embeddingMode Llama backend: open the model in
|
|
31
|
+
* embedding-extraction mode rather than completion
|
|
32
|
+
* mode. Mutually exclusive with chat completion.
|
|
33
|
+
* @param visionEnabled MediaPipe backend: open the LiteRT-LM EngineConfig
|
|
34
|
+
* with `visionBackend` enabled.
|
|
35
|
+
* @param temperature LiteRT backend: sampling temperature (0 = greedy).
|
|
36
|
+
* @param topP LiteRT backend: nucleus sampling cutoff (1 = disabled).
|
|
37
|
+
* @param topK LiteRT backend: top-K truncation (0 = disabled).
|
|
38
|
+
* @param maxNewTokens LiteRT backend: hard cap on tokens generated per request.
|
|
39
|
+
* @param httpBasePort First port the HTTP server tries to bind. Defaults
|
|
40
|
+
* to 38883 (matches the rest of the dvai-bridge family).
|
|
41
|
+
* @param httpMaxPortAttempts Number of consecutive ports to try before giving
|
|
42
|
+
* up. Defaults to 16.
|
|
43
|
+
* @param corsOrigin CORS allow-origin policy. See [CorsConfig].
|
|
44
|
+
* Defaults to wildcard.
|
|
45
|
+
* @param modelId Optional override for the model id surfaced via
|
|
46
|
+
* `/v1/models`. Defaults to the file name minus
|
|
47
|
+
* extension when null.
|
|
48
|
+
*/
|
|
49
|
+
data class StartOptions(
|
|
50
|
+
val backend: BackendKind = BackendKind.Auto,
|
|
51
|
+
val modelPath: String? = null,
|
|
52
|
+
val tokenizerPath: String? = null,
|
|
53
|
+
val mmprojPath: String? = null,
|
|
54
|
+
val chatTemplate: String? = null,
|
|
55
|
+
val gpuLayers: Int = 99,
|
|
56
|
+
val contextSize: Int = 2048,
|
|
57
|
+
val threads: Int = 4,
|
|
58
|
+
val embeddingMode: Boolean = false,
|
|
59
|
+
val visionEnabled: Boolean = false,
|
|
60
|
+
val temperature: Float = 0f,
|
|
61
|
+
val topP: Float = 1f,
|
|
62
|
+
val topK: Int = 0,
|
|
63
|
+
val maxNewTokens: Int = 512,
|
|
64
|
+
val httpBasePort: Int = 38883,
|
|
65
|
+
val httpMaxPortAttempts: Int = 16,
|
|
66
|
+
val corsOrigin: CorsConfig = CorsConfig.Wildcard,
|
|
67
|
+
val modelId: String? = null,
|
|
68
|
+
/**
|
|
69
|
+
* Phase 3 — opt-in distributed inference / device offload. When
|
|
70
|
+
* `enabled = true`, [DVAIBridge] spins up an [NsdDiscovery] +
|
|
71
|
+
* [NsdAdvertiser], a [CapabilityCache], and a [PairingPolicy]
|
|
72
|
+
* whose `requests` Flow is exposed via `DVAIBridge.pairingRequests`
|
|
73
|
+
* for the host UI. Default null = behave exactly like v2.x.
|
|
74
|
+
*/
|
|
75
|
+
val offload: OffloadConfig? = null,
|
|
76
|
+
/**
|
|
77
|
+
* v3.3 — offline JWT license validator config. When non-null, the
|
|
78
|
+
* SDK loads the JWT from this filesystem path and verifies it at
|
|
79
|
+
* startup. Auto-discovery (assets/, res/raw/, filesDir/) runs when
|
|
80
|
+
* BOTH this AND [licenseToken] are null.
|
|
81
|
+
*
|
|
82
|
+
* Production Android builds without a valid license throw
|
|
83
|
+
* [co.deepvoiceai.bridge.license.LicenseRequiredError] from
|
|
84
|
+
* [DVAIBridge.start]. Debug builds (`hostBuildConfigDebug = true`
|
|
85
|
+
* or `ApplicationInfo.FLAG_DEBUGGABLE`) skip validation entirely.
|
|
86
|
+
*/
|
|
87
|
+
val licenseKeyPath: String? = null,
|
|
88
|
+
/**
|
|
89
|
+
* v3.3 — inline JWT license token. Overrides every other discovery
|
|
90
|
+
* source. Useful for CI / test contexts where reading a file isn't
|
|
91
|
+
* practical and operators inject via env var or build config.
|
|
92
|
+
*/
|
|
93
|
+
val licenseToken: String? = null,
|
|
94
|
+
/**
|
|
95
|
+
* v3.3 — the host app's `BuildConfig.DEBUG` value, passed through to
|
|
96
|
+
* the license validator's dev-mode bypass. When true the validator
|
|
97
|
+
* returns `FreeDev` without trying to verify anything; when false (or
|
|
98
|
+
* null) the validator falls back to `ApplicationInfo.FLAG_DEBUGGABLE`.
|
|
99
|
+
*
|
|
100
|
+
* Pass `BuildConfig.DEBUG` from your app module here — the validator
|
|
101
|
+
* lives in this library module whose own `BuildConfig.DEBUG` never
|
|
102
|
+
* reflects the host app's state.
|
|
103
|
+
*/
|
|
104
|
+
val hostBuildConfigDebug: Boolean? = null,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/** Options for [DVAIBridge.downloadModel]. */
|
|
108
|
+
data class DownloadOptions(
|
|
109
|
+
val url: String,
|
|
110
|
+
val sha256: String,
|
|
111
|
+
val destFilename: String,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
/** Result of a successful [DVAIBridge.downloadModel] call. */
|
|
115
|
+
data class DownloadResult(
|
|
116
|
+
val path: String,
|
|
117
|
+
val sha256: String,
|
|
118
|
+
val sizeBytes: Long,
|
|
119
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package co.deepvoiceai.bridge
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public error surface for the DVAIBridge Android SDK. Mirrors iOS
|
|
5
|
+
* `DVAIBridgeError` 1:1 in case shape so cross-platform consumers see
|
|
6
|
+
* identical failure modes.
|
|
7
|
+
*
|
|
8
|
+
* Uses a sealed Exception hierarchy rather than a sealed-class +
|
|
9
|
+
* data-classes pair so consumers can `try { ... } catch (e:
|
|
10
|
+
* DVAIBridgeError.ModelLoadFailed) { ... }`.
|
|
11
|
+
*/
|
|
12
|
+
sealed class DVAIBridgeError(message: String, cause: Throwable? = null) : Exception(message, cause) {
|
|
13
|
+
/** [DVAIBridge.start] called twice without a [DVAIBridge.stop] in between. */
|
|
14
|
+
class AlreadyStarted(
|
|
15
|
+
val currentBackend: BackendKind,
|
|
16
|
+
val baseUrl: String,
|
|
17
|
+
) : DVAIBridgeError(
|
|
18
|
+
"DVAIBridge is already running ($currentBackend at $baseUrl). Call stop() first.",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
/** [StartOptions] is malformed (e.g. unsupported modelPath extension under Auto). */
|
|
22
|
+
class ConfigurationInvalid(reason: String) : DVAIBridgeError("Configuration invalid: $reason")
|
|
23
|
+
|
|
24
|
+
/** Backend rejected the model file or tokenizer at load time. */
|
|
25
|
+
class ModelLoadFailed(reason: String, cause: Throwable? = null) : DVAIBridgeError("Model load failed: $reason", cause)
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Backend can't run in the current environment (e.g. asking for
|
|
29
|
+
* MediaPipe on a device too old for LiteRT-LM, or asking for the
|
|
30
|
+
* upcoming `.foundation` Apple-Models backend on Android — it doesn't
|
|
31
|
+
* exist). The umbrella throws this when [BackendSelector] resolves to
|
|
32
|
+
* something the runtime doesn't support.
|
|
33
|
+
*/
|
|
34
|
+
class BackendUnavailable(val backend: BackendKind, reason: String) : DVAIBridgeError(
|
|
35
|
+
"Backend $backend is not available: $reason",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
/** Generic backend failure (HTTP server bind, model inference exception). */
|
|
39
|
+
class BackendError(underlying: Throwable) : DVAIBridgeError(
|
|
40
|
+
"Backend error: ${underlying.message ?: underlying::class.qualifiedName}",
|
|
41
|
+
underlying,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/** [DVAIBridge.downloadModel] downloaded the file but the sha256 didn't match. */
|
|
45
|
+
class ChecksumMismatch(expected: String, actual: String) : DVAIBridgeError(
|
|
46
|
+
"Downloaded file sha256 mismatch: expected $expected, got $actual",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
/** [DVAIBridge.downloadModel] failed before completion (network, disk, HTTP error). */
|
|
50
|
+
class DownloadFailed(reason: String, cause: Throwable? = null) : DVAIBridgeError("Download failed: $reason", cause)
|
|
51
|
+
}
|