@dr33m/react-native-litert-lm 0.6.3 → 0.6.5

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.
@@ -800,7 +800,7 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
800
800
  }
801
801
  conversation = null
802
802
  }
803
- // Map tools capture tool calls for JS instead of executing natively
803
+ // Map tools. The engine only parses calls; JS runs them (see `automaticToolCalling`).
804
804
  val lmTools: List<ToolProvider>? = tools?.map { toolDef ->
805
805
  val apiTool = object : OpenApiTool {
806
806
  override fun getToolDescriptionJsonString(): String {
@@ -812,12 +812,9 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
812
812
  return fullDesc.toString()
813
813
  }
814
814
  override fun execute(paramsJsonString: String): String {
815
- Log.d(TAG, "Tool called: ${toolDef.name} with args: $paramsJsonString")
816
- pendingToolCalls.add(ToolCall(
817
- name = toolDef.name,
818
- argumentsJson = paramsJsonString
819
- ))
820
- return "{\"status\": \"pending\", \"message\": \"Tool execution delegated to application\"}"
815
+ // Never invoked: automatic tool calling is off, so tool
816
+ // execution happens on the JS side.
817
+ return "{}"
821
818
  }
822
819
  }
823
820
  tool(apiTool)
@@ -834,6 +831,17 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
834
831
  systemInstruction = systemPrompt?.let { Contents.of(Content.Text(it)) },
835
832
  initialMessages = initialMessages,
836
833
  tools = lmTools ?: emptyList(),
834
+ /*
835
+ * Off, ported from upstream (hung-yueh 2cc938a). The SDK default runs
836
+ * each parsed call against `execute()` above, which was a stub, and
837
+ * feeds that stub's reply straight back to the model in the same
838
+ * turn. The model then answered the stub: prose claiming a result it
839
+ * never had ("I have removed it…"), streamed to the reader, then
840
+ * discarded once JS ran the real tool and generated again. A wasted
841
+ * generation per call, and its tokens stayed in the KV cache.
842
+ * With it off, calls arrive on `Message.toolCalls` instead.
843
+ */
844
+ automaticToolCalling = false,
837
845
  // Available since LiteRT-LM 0.15.0; before that the Kotlin SDK had no
838
846
  // way to express it and every Android reply ran to the engine default.
839
847
  maxOutputToken = maxOutputTokens,
@@ -1023,7 +1031,8 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
1023
1031
  history = history,
1024
1032
  userMessage = userTextRepresentation,
1025
1033
  onStatsReady = { stats -> lastStats = stats },
1026
- onFailure = { e -> errorRef.set(e) }
1034
+ onFailure = { e -> errorRef.set(e) },
1035
+ onToolCalls = { calls -> captureToolCalls(calls) }
1027
1036
  )
1028
1037
 
1029
1038
  try {
@@ -1061,6 +1070,7 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
1061
1070
  .joinToString("") { it.text }
1062
1071
 
1063
1072
  val thinkingText = responseMsg.channels["thought"] ?: ""
1073
+ captureToolCalls(responseMsg.toolCalls)
1064
1074
 
1065
1075
  history.add(Message(Role.MODEL, response))
1066
1076
 
@@ -1094,6 +1104,15 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
1094
1104
  }
1095
1105
  }
1096
1106
 
1107
+ /** Records SDK-parsed calls in the shape JS receives on `ExecuteResult.toolCalls`. */
1108
+ private fun captureToolCalls(calls: List<com.google.ai.edge.litertlm.ToolCall>?) {
1109
+ for (call in calls.orEmpty()) {
1110
+ val argumentsJson = org.json.JSONObject(call.arguments).toString()
1111
+ Log.d(TAG, "Tool called: ${call.name} with args: $argumentsJson")
1112
+ pendingToolCalls.add(ToolCall(name = call.name, argumentsJson = argumentsJson))
1113
+ }
1114
+ }
1115
+
1097
1116
  override fun sendToolResponse(
1098
1117
  responses: Array<ToolResponse>,
1099
1118
  onToken: ((token: String, done: Boolean) -> Unit)?
@@ -18,6 +18,8 @@ internal class StreamingCallbackListener(
18
18
  private val onStatsReady: (GenerationStats) -> Unit,
19
19
  private val onFailure: ((Throwable) -> Unit)? = null,
20
20
  private val onThinkingToken: ((String) -> Unit)? = null,
21
+ /** Tool calls the engine parsed out of this reply. They arrive on the final message. */
22
+ private val onToolCalls: ((List<com.google.ai.edge.litertlm.ToolCall>) -> Unit)? = null,
21
23
  ) : com.google.ai.edge.litertlm.MessageCallback {
22
24
 
23
25
  private val startTime = System.nanoTime()
@@ -29,6 +31,8 @@ internal class StreamingCallbackListener(
29
31
  .filterIsInstance<Content.Text>()
30
32
  .joinToString("") { it.text }
31
33
 
34
+ if (message.toolCalls.isNotEmpty()) onToolCalls?.invoke(message.toolCalls)
35
+
32
36
  // Capture thinking from the "thought" channel
33
37
  val thinkingChunk = message.channels["thought"]
34
38
  if (!thinkingChunk.isNullOrEmpty()) {
@@ -431,15 +431,16 @@ public class HybridLiteRTLM: HybridLiteRTLMSpec_base, HybridLiteRTLMSpec_protoco
431
431
 
432
432
  litert_lm_session_config_set_max_output_tokens(sessionConfig, Int32(self.maxOutputTokens))
433
433
 
434
- var sampler = LiteRtLmSamplerParams()
435
- sampler.type = kLiteRtLmSamplerTypeTopP
436
- sampler.top_k = Int32(self.topK)
437
- sampler.top_p = Float(self.topP)
438
- sampler.temperature = Float(self.temperature)
439
- sampler.seed = 0
440
- withUnsafePointer(to: &sampler) { samplerPtr in
441
- litert_lm_session_config_set_sampler_params(sessionConfig, samplerPtr)
442
- }
434
+ // v0.15: sampler params are opaque — built through create/setters
435
+ // instead of a stack struct. set_sampler_params copies the values into
436
+ // the session config, so the params can be freed when this scope ends.
437
+ guard let sampler = litert_lm_sampler_params_create(kLiteRtLmSamplerTypeTopP) else { return }
438
+ defer { litert_lm_sampler_params_delete(sampler) }
439
+ litert_lm_sampler_params_set_top_k(sampler, Int32(self.topK))
440
+ litert_lm_sampler_params_set_top_p(sampler, Float(self.topP))
441
+ litert_lm_sampler_params_set_temperature(sampler, Float(self.temperature))
442
+ litert_lm_sampler_params_set_seed(sampler, 0)
443
+ litert_lm_session_config_set_sampler_params(sessionConfig, sampler)
443
444
 
444
445
  litert_lm_conversation_config_set_session_config(convConfig, sessionConfig)
445
446
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dr33m/react-native-litert-lm",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "litertLm": {
5
5
  "version": "0.15.0",
6
6
  "androidMavenVersion": "0.15.0",