@dr33m/react-native-litert-lm 0.6.1 → 0.6.3
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.
|
@@ -728,6 +728,9 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
728
728
|
* held and believed in.
|
|
729
729
|
*/
|
|
730
730
|
fun releaseUnderMemoryPressure() {
|
|
731
|
+
// Nothing loaded is nothing to free, and saying otherwise turns one
|
|
732
|
+
// emergency into a page of identical warnings.
|
|
733
|
+
if (engine == null) return
|
|
731
734
|
Log.w(TAG, "Releasing engine under memory pressure; instance stays reloadable")
|
|
732
735
|
cleanupInternal()
|
|
733
736
|
}
|
|
@@ -736,6 +739,9 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
736
739
|
Log.d(TAG, "Closing resources")
|
|
737
740
|
isClosed = true
|
|
738
741
|
cleanupInternal()
|
|
742
|
+
// A closed instance can never load again, so it has no business being
|
|
743
|
+
// woken by the next memory emergency.
|
|
744
|
+
LiteRTLMRegistry.unregister(this)
|
|
739
745
|
}
|
|
740
746
|
|
|
741
747
|
private fun cleanupInternal() {
|
|
@@ -743,14 +749,36 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
743
749
|
// for it while holding the lock would block loadModel behind it.
|
|
744
750
|
cancelInFlightGeneration()
|
|
745
751
|
synchronized(initLock) {
|
|
752
|
+
/*
|
|
753
|
+
* Dropped first, closed second.
|
|
754
|
+
*
|
|
755
|
+
* These used to be nulled after their `close()` calls, inside one
|
|
756
|
+
* try. A close that threw — and closing a conversation that has
|
|
757
|
+
* just been cancelled is exactly when one does — skipped the rest
|
|
758
|
+
* of the block and left a retired engine still referenced. Nothing
|
|
759
|
+
* downstream could tell: `isReady()` reads `engine != null`, so the
|
|
760
|
+
* instance went on reporting itself loaded, and the next
|
|
761
|
+
* `resetConversation()` called `createConversation` on a closed
|
|
762
|
+
* engine and threw "Engine is not initialized" from deep in the SDK.
|
|
763
|
+
*
|
|
764
|
+
* Letting go of the references first makes that impossible: after
|
|
765
|
+
* this block the instance is unloaded whatever the native side did.
|
|
766
|
+
*/
|
|
767
|
+
val retiredConversation = conversation
|
|
768
|
+
val retiredEngine = engine
|
|
769
|
+
conversation = null
|
|
770
|
+
engine = null
|
|
771
|
+
loadedModelPath = null
|
|
772
|
+
|
|
773
|
+
try {
|
|
774
|
+
retiredConversation?.close()
|
|
775
|
+
} catch (e: Exception) {
|
|
776
|
+
Log.w(TAG, "Error closing conversation: ${e.message}")
|
|
777
|
+
}
|
|
746
778
|
try {
|
|
747
|
-
|
|
748
|
-
conversation = null
|
|
749
|
-
engine?.close() // Direct call
|
|
750
|
-
engine = null
|
|
751
|
-
loadedModelPath = null
|
|
779
|
+
retiredEngine?.close()
|
|
752
780
|
} catch (e: Exception) {
|
|
753
|
-
Log.
|
|
781
|
+
Log.w(TAG, "Error closing engine: ${e.message}")
|
|
754
782
|
}
|
|
755
783
|
}
|
|
756
784
|
}
|
|
@@ -819,7 +847,12 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
819
847
|
// - Kotlin: Not yet available — track at https://github.com/google-ai-edge/LiteRT-LM
|
|
820
848
|
//
|
|
821
849
|
// Once the Kotlin SDK exposes this, wire it via ConversationConfig here.
|
|
822
|
-
|
|
850
|
+
// Checked rather than forced: this runs from `resetConversation`, which
|
|
851
|
+
// JS calls on paths that do not first ask whether anything is loaded.
|
|
852
|
+
// A named error beats a null-pointer dereference from inside the SDK.
|
|
853
|
+
val activeEngine = engine
|
|
854
|
+
?: throw RuntimeException("Cannot create a conversation: no model is loaded.")
|
|
855
|
+
conversation = activeEngine.createConversation(convConfig)
|
|
823
856
|
}
|
|
824
857
|
|
|
825
858
|
|
|
@@ -960,7 +993,17 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
960
993
|
|
|
961
994
|
val userMsg = LiteRTMessage.user(Contents.of(contents))
|
|
962
995
|
|
|
963
|
-
|
|
996
|
+
// Always stated, never omitted.
|
|
997
|
+
//
|
|
998
|
+
// `enable_thinking` is a chat-template variable, not a Gemma
|
|
999
|
+
// feature, and templates test it as "defined and false". Qwen3's
|
|
1000
|
+
// is exactly that shape, so leaving the key out is not the same
|
|
1001
|
+
// as setting it to false: undefined falls through to the
|
|
1002
|
+
// template's own default, which for a reasoning model is to
|
|
1003
|
+
// reason. Omitting it meant thinking could be turned on but
|
|
1004
|
+
// never off, and the reasoning arrived inline in the answer.
|
|
1005
|
+
val extraContext: Map<String, String> =
|
|
1006
|
+
mapOf("enable_thinking" to enableThinking.toString())
|
|
964
1007
|
|
|
965
1008
|
if (onToken != null) {
|
|
966
1009
|
// ── Streaming path ────────────────────────────────────────────────
|
|
@@ -21,6 +21,23 @@ object LiteRTLMRegistry {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Drop a retired instance.
|
|
26
|
+
*
|
|
27
|
+
* The set holds weak keys, so a dead instance does leave eventually — but
|
|
28
|
+
* only once GC gets to it, and until then it is still iterated and still
|
|
29
|
+
* logged against. Every `loadModel()` mints a fresh HybridLiteRTLM, so a
|
|
30
|
+
* few reloads were enough for one memory emergency to report five engines
|
|
31
|
+
* released when only the last of them held anything. Closing is a definite
|
|
32
|
+
* end, so it is a better moment to forget an instance than a collection
|
|
33
|
+
* that may not have happened yet.
|
|
34
|
+
*/
|
|
35
|
+
fun unregister(instance: HybridLiteRTLM) {
|
|
36
|
+
synchronized(instances) {
|
|
37
|
+
instances.remove(instance)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
/**
|
|
25
42
|
* Whether a trim level is a real emergency worth dropping engines for.
|
|
26
43
|
*
|