cactus-react-native 0.1.0 → 0.1.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.
Files changed (45) hide show
  1. package/android/src/main/CMakeLists.txt +0 -4
  2. package/android/src/main/java/com/cactus/Cactus.java +179 -42
  3. package/android/src/main/java/com/cactus/LlamaContext.java +22 -0
  4. package/android/src/main/jni.cpp +53 -1
  5. package/android/src/main/jniLibs/arm64-v8a/libcactus.so +0 -0
  6. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8.so +0 -0
  7. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2.so +0 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod.so +0 -0
  9. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod_i8mm.so +0 -0
  10. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_i8mm.so +0 -0
  11. package/android/src/main/jniLibs/x86_64/libcactus.so +0 -0
  12. package/android/src/main/jniLibs/x86_64/libcactus_x86_64.so +0 -0
  13. package/android/src/newarch/java/com/cactus/CactusModule.java +20 -0
  14. package/android/src/oldarch/java/com/cactus/CactusModule.java +20 -0
  15. package/ios/CMakeLists.txt +2 -0
  16. package/ios/Cactus.mm +80 -0
  17. package/ios/CactusContext.h +6 -0
  18. package/ios/CactusContext.mm +27 -0
  19. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus.h +18 -0
  20. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +39 -0
  21. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  22. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/cactus.h +18 -0
  23. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/cactus_ffi.h +39 -0
  24. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  25. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/cactus.h +18 -0
  26. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/cactus_ffi.h +39 -0
  27. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/cactus +0 -0
  28. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/cactus.h +18 -0
  29. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/cactus_ffi.h +39 -0
  30. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  31. package/lib/commonjs/NativeCactus.js +1 -0
  32. package/lib/commonjs/NativeCactus.js.map +1 -1
  33. package/lib/commonjs/index.js +29 -0
  34. package/lib/commonjs/index.js.map +1 -1
  35. package/lib/module/NativeCactus.js +2 -0
  36. package/lib/module/NativeCactus.js.map +1 -1
  37. package/lib/module/index.js +29 -0
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/typescript/NativeCactus.d.ts +10 -0
  40. package/lib/typescript/NativeCactus.d.ts.map +1 -1
  41. package/lib/typescript/index.d.ts +17 -1
  42. package/lib/typescript/index.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/src/NativeCactus.ts +22 -0
  45. package/src/index.ts +36 -0
@@ -3,11 +3,8 @@ cmake_minimum_required(VERSION 3.10)
3
3
  project(cactus)
4
4
 
5
5
  set(CMAKE_CXX_STANDARD 17)
6
-
7
- # Use cactus as the C++ source directory
8
6
  set(SOURCE_DIR ${CMAKE_SOURCE_DIR}/../../../../cactus)
9
7
 
10
- # Include directories to match cactus core structure
11
8
  include_directories(${SOURCE_DIR})
12
9
  include_directories(${SOURCE_DIR}/ggml-cpu)
13
10
  include_directories(${SOURCE_DIR}/minja)
@@ -127,7 +124,6 @@ function(build_library target_name cpu_flags)
127
124
 
128
125
  endfunction()
129
126
 
130
- # Default target (no specific CPU features)
131
127
  build_library("cactus" "")
132
128
 
133
129
  if (${ANDROID_ABI} STREQUAL "arm64-v8a")
@@ -21,11 +21,14 @@ import java.util.Random;
21
21
  import java.io.File;
22
22
  import java.io.FileInputStream;
23
23
  import java.io.PushbackInputStream;
24
+ import java.util.concurrent.ExecutorService;
25
+ import java.util.concurrent.Executors;
24
26
 
25
27
  public class Cactus implements LifecycleEventListener {
26
28
  public static final String NAME = "Cactus";
27
29
 
28
30
  private ReactApplicationContext reactContext;
31
+ private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
29
32
 
30
33
  public Cactus(ReactApplicationContext reactContext) {
31
34
  reactContext.addLifecycleEventListener(this);
@@ -95,7 +98,7 @@ public class Cactus implements LifecycleEventListener {
95
98
  }
96
99
  promise.resolve(result);
97
100
  }
98
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
101
+ }.executeOnExecutor(singleThreadExecutor);
99
102
  }
100
103
 
101
104
  public void initContext(double id, final ReadableMap params, final Promise promise) {
@@ -139,7 +142,7 @@ public class Cactus implements LifecycleEventListener {
139
142
  promise.resolve(result);
140
143
  tasks.remove(this);
141
144
  }
142
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
145
+ }.executeOnExecutor(singleThreadExecutor);
143
146
  tasks.put(task, "initContext");
144
147
  }
145
148
 
@@ -178,7 +181,7 @@ public class Cactus implements LifecycleEventListener {
178
181
  promise.resolve(result);
179
182
  tasks.remove(this);
180
183
  }
181
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
184
+ }.executeOnExecutor(singleThreadExecutor);
182
185
  tasks.put(task, "getFormattedChat-" + contextId);
183
186
  }
184
187
 
@@ -211,7 +214,7 @@ public class Cactus implements LifecycleEventListener {
211
214
  promise.resolve(result);
212
215
  tasks.remove(this);
213
216
  }
214
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
217
+ }.executeOnExecutor(singleThreadExecutor);
215
218
  tasks.put(task, "loadSession-" + contextId);
216
219
  }
217
220
 
@@ -244,12 +247,12 @@ public class Cactus implements LifecycleEventListener {
244
247
  promise.resolve(result);
245
248
  tasks.remove(this);
246
249
  }
247
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
250
+ }.executeOnExecutor(singleThreadExecutor);
248
251
  tasks.put(task, "saveSession-" + contextId);
249
252
  }
250
253
 
251
254
  public void completion(double id, final ReadableMap params, final Promise promise) {
252
- Log.d(NAME, "🟢 BRIDGE: completion() method called with contextId=" + (int)id);
255
+ Log.d(NAME, "BRIDGE: completion() method called with contextId=" + (int)id);
253
256
  final int contextId = (int) id;
254
257
  AsyncTask task = new AsyncTask<Void, Void, WritableMap>() {
255
258
  private Exception exception;
@@ -260,20 +263,20 @@ public class Cactus implements LifecycleEventListener {
260
263
  try {
261
264
  LlamaContext context = contexts.get(contextId);
262
265
  if (context == null) {
263
- Log.e(NAME, "BRIDGE: Context not found for id=" + contextId);
266
+ Log.e(NAME, "BRIDGE: Context not found for id=" + contextId);
264
267
  throw new Exception("Context not found");
265
268
  }
266
- Log.d(NAME, "BRIDGE: Context found, checking if predicting...");
269
+ Log.d(NAME, "BRIDGE: Context found, checking if predicting...");
267
270
  if (context.isPredicting()) {
268
- Log.e(NAME, "BRIDGE: Context is busy (predicting)");
271
+ Log.e(NAME, "BRIDGE: Context is busy (predicting)");
269
272
  throw new Exception("Context is busy");
270
273
  }
271
- Log.d(NAME, "🚀 BRIDGE: About to call context.completion()...");
274
+ Log.d(NAME, "BRIDGE: About to call context.completion()...");
272
275
  WritableMap result = context.completion(params);
273
- Log.d(NAME, "BRIDGE: context.completion() returned successfully");
276
+ Log.d(NAME, "BRIDGE: context.completion() returned successfully");
274
277
  return result;
275
278
  } catch (Exception e) {
276
- Log.e(NAME, "BRIDGE: Exception in doInBackground: " + e.getMessage());
279
+ Log.e(NAME, "BRIDGE: Exception in doInBackground: " + e.getMessage());
277
280
  exception = e;
278
281
  }
279
282
  return null;
@@ -281,19 +284,19 @@ public class Cactus implements LifecycleEventListener {
281
284
 
282
285
  @Override
283
286
  protected void onPostExecute(WritableMap result) {
284
- Log.d(NAME, "📤 BRIDGE: onPostExecute called");
287
+ Log.d(NAME, "BRIDGE: onPostExecute called");
285
288
  if (exception != null) {
286
- Log.e(NAME, "BRIDGE: Rejecting promise with exception: " + exception.getMessage());
289
+ Log.e(NAME, "BRIDGE: Rejecting promise with exception: " + exception.getMessage());
287
290
  promise.reject(exception);
288
291
  return;
289
292
  }
290
- Log.d(NAME, "BRIDGE: Resolving promise with result");
293
+ Log.d(NAME, "BRIDGE: Resolving promise with result");
291
294
  promise.resolve(result);
292
295
  tasks.remove(this);
293
- Log.d(NAME, "🏁 BRIDGE: completion() finished successfully");
296
+ Log.d(NAME, "BRIDGE: completion() finished successfully");
294
297
  }
295
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
296
- Log.d(NAME, "📋 BRIDGE: AsyncTask queued for execution");
298
+ }.executeOnExecutor(singleThreadExecutor);
299
+ Log.d(NAME, "BRIDGE: AsyncTask queued for execution");
297
300
  tasks.put(task, "completion-" + contextId);
298
301
  }
299
302
 
@@ -332,7 +335,7 @@ public class Cactus implements LifecycleEventListener {
332
335
  promise.resolve(result);
333
336
  tasks.remove(this);
334
337
  }
335
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
338
+ }.executeOnExecutor(singleThreadExecutor);
336
339
  tasks.put(task, "stopCompletion-" + contextId);
337
340
  }
338
341
 
@@ -364,7 +367,7 @@ public class Cactus implements LifecycleEventListener {
364
367
  promise.resolve(result);
365
368
  tasks.remove(this);
366
369
  }
367
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
370
+ }.executeOnExecutor(singleThreadExecutor);
368
371
  tasks.put(task, "tokenize-" + contextId);
369
372
  }
370
373
 
@@ -396,7 +399,7 @@ public class Cactus implements LifecycleEventListener {
396
399
  promise.resolve(result);
397
400
  tasks.remove(this);
398
401
  }
399
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
402
+ }.executeOnExecutor(singleThreadExecutor);
400
403
  tasks.put(task, "tokenize-" + contextId);
401
404
  }
402
405
 
@@ -428,7 +431,7 @@ public class Cactus implements LifecycleEventListener {
428
431
  promise.resolve(result);
429
432
  tasks.remove(this);
430
433
  }
431
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
434
+ }.executeOnExecutor(singleThreadExecutor);
432
435
  tasks.put(task, "detokenize-" + contextId);
433
436
  }
434
437
 
@@ -460,7 +463,7 @@ public class Cactus implements LifecycleEventListener {
460
463
  promise.resolve(result);
461
464
  tasks.remove(this);
462
465
  }
463
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
466
+ }.executeOnExecutor(singleThreadExecutor);
464
467
  tasks.put(task, "embedding-" + contextId);
465
468
  }
466
469
 
@@ -492,7 +495,7 @@ public class Cactus implements LifecycleEventListener {
492
495
  promise.resolve(result);
493
496
  tasks.remove(this);
494
497
  }
495
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
498
+ }.executeOnExecutor(singleThreadExecutor);
496
499
  tasks.put(task, "bench-" + contextId);
497
500
  }
498
501
 
@@ -525,7 +528,7 @@ public class Cactus implements LifecycleEventListener {
525
528
  return;
526
529
  }
527
530
  }
528
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
531
+ }.executeOnExecutor(singleThreadExecutor);
529
532
  tasks.put(task, "applyLoraAdapters-" + contextId);
530
533
  }
531
534
 
@@ -559,7 +562,7 @@ public class Cactus implements LifecycleEventListener {
559
562
  }
560
563
  promise.resolve(null);
561
564
  }
562
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
565
+ }.executeOnExecutor(singleThreadExecutor);
563
566
  tasks.put(task, "removeLoraAdapters-" + contextId);
564
567
  }
565
568
 
@@ -590,7 +593,7 @@ public class Cactus implements LifecycleEventListener {
590
593
  }
591
594
  promise.resolve(result);
592
595
  }
593
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
596
+ }.executeOnExecutor(singleThreadExecutor);
594
597
  tasks.put(task, "getLoadedLoraAdapters-" + contextId);
595
598
  }
596
599
 
@@ -632,7 +635,7 @@ public class Cactus implements LifecycleEventListener {
632
635
  promise.resolve(null);
633
636
  tasks.remove(this);
634
637
  }
635
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
638
+ }.executeOnExecutor(singleThreadExecutor);
636
639
  tasks.put(task, "releaseContext-" + contextId);
637
640
  }
638
641
 
@@ -659,7 +662,7 @@ public class Cactus implements LifecycleEventListener {
659
662
  promise.resolve(null);
660
663
  tasks.remove(this);
661
664
  }
662
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
665
+ }.executeOnExecutor(singleThreadExecutor);
663
666
  tasks.put(task, "releaseAllContexts");
664
667
  }
665
668
 
@@ -718,7 +721,7 @@ public class Cactus implements LifecycleEventListener {
718
721
  promise.resolve(result);
719
722
  tasks.remove(this);
720
723
  }
721
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
724
+ }.executeOnExecutor(singleThreadExecutor);
722
725
  tasks.put(task, "initMultimodal-" + contextId);
723
726
  }
724
727
 
@@ -750,7 +753,7 @@ public class Cactus implements LifecycleEventListener {
750
753
  promise.resolve(result);
751
754
  tasks.remove(this);
752
755
  }
753
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
756
+ }.executeOnExecutor(singleThreadExecutor);
754
757
  tasks.put(task, "isMultimodalEnabled-" + contextId);
755
758
  }
756
759
 
@@ -782,7 +785,7 @@ public class Cactus implements LifecycleEventListener {
782
785
  promise.resolve(result);
783
786
  tasks.remove(this);
784
787
  }
785
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
788
+ }.executeOnExecutor(singleThreadExecutor);
786
789
  tasks.put(task, "isMultimodalSupportVision-" + contextId);
787
790
  }
788
791
 
@@ -814,7 +817,7 @@ public class Cactus implements LifecycleEventListener {
814
817
  promise.resolve(result);
815
818
  tasks.remove(this);
816
819
  }
817
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
820
+ }.executeOnExecutor(singleThreadExecutor);
818
821
  tasks.put(task, "isMultimodalSupportAudio-" + contextId);
819
822
  }
820
823
 
@@ -846,7 +849,7 @@ public class Cactus implements LifecycleEventListener {
846
849
  promise.resolve(null);
847
850
  tasks.remove(this);
848
851
  }
849
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
852
+ }.executeOnExecutor(singleThreadExecutor);
850
853
  tasks.put(task, "releaseMultimodal-" + contextId);
851
854
  }
852
855
 
@@ -881,7 +884,7 @@ public class Cactus implements LifecycleEventListener {
881
884
  promise.resolve(result);
882
885
  tasks.remove(this);
883
886
  }
884
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
887
+ }.executeOnExecutor(singleThreadExecutor);
885
888
  tasks.put(task, "multimodalCompletion-" + contextId);
886
889
  }
887
890
 
@@ -913,7 +916,7 @@ public class Cactus implements LifecycleEventListener {
913
916
  promise.resolve(result);
914
917
  tasks.remove(this);
915
918
  }
916
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
919
+ }.executeOnExecutor(singleThreadExecutor);
917
920
  tasks.put(task, "initVocoder-" + contextId);
918
921
  }
919
922
 
@@ -945,7 +948,7 @@ public class Cactus implements LifecycleEventListener {
945
948
  promise.resolve(result);
946
949
  tasks.remove(this);
947
950
  }
948
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
951
+ }.executeOnExecutor(singleThreadExecutor);
949
952
  tasks.put(task, "isVocoderEnabled-" + contextId);
950
953
  }
951
954
 
@@ -977,7 +980,7 @@ public class Cactus implements LifecycleEventListener {
977
980
  promise.resolve(result);
978
981
  tasks.remove(this);
979
982
  }
980
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
983
+ }.executeOnExecutor(singleThreadExecutor);
981
984
  tasks.put(task, "getTTSType-" + contextId);
982
985
  }
983
986
 
@@ -1009,7 +1012,7 @@ public class Cactus implements LifecycleEventListener {
1009
1012
  promise.resolve(result);
1010
1013
  tasks.remove(this);
1011
1014
  }
1012
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1015
+ }.executeOnExecutor(singleThreadExecutor);
1013
1016
  tasks.put(task, "getFormattedAudioCompletion-" + contextId);
1014
1017
  }
1015
1018
 
@@ -1041,7 +1044,7 @@ public class Cactus implements LifecycleEventListener {
1041
1044
  promise.resolve(result);
1042
1045
  tasks.remove(this);
1043
1046
  }
1044
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1047
+ }.executeOnExecutor(singleThreadExecutor);
1045
1048
  tasks.put(task, "getAudioCompletionGuideTokens-" + contextId);
1046
1049
  }
1047
1050
 
@@ -1073,7 +1076,7 @@ public class Cactus implements LifecycleEventListener {
1073
1076
  promise.resolve(result);
1074
1077
  tasks.remove(this);
1075
1078
  }
1076
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1079
+ }.executeOnExecutor(singleThreadExecutor);
1077
1080
  tasks.put(task, "decodeAudioTokens-" + contextId);
1078
1081
  }
1079
1082
 
@@ -1105,7 +1108,141 @@ public class Cactus implements LifecycleEventListener {
1105
1108
  promise.resolve(null);
1106
1109
  tasks.remove(this);
1107
1110
  }
1108
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1111
+ }.executeOnExecutor(singleThreadExecutor);
1109
1112
  tasks.put(task, "releaseVocoder-" + contextId);
1110
1113
  }
1114
+
1115
+ public void generateResponse(double id, final String userMessage, final double maxTokens, final Promise promise) {
1116
+ final int contextId = (int) id;
1117
+ AsyncTask task = new AsyncTask<Void, Void, String>() {
1118
+ private Exception exception;
1119
+
1120
+ @Override
1121
+ protected String doInBackground(Void... voids) {
1122
+ try {
1123
+ LlamaContext context = contexts.get(contextId);
1124
+ if (context == null) {
1125
+ throw new Exception("Context not found");
1126
+ }
1127
+ if (context.isPredicting()) {
1128
+ throw new Exception("Context is busy");
1129
+ }
1130
+ return context.generateResponse(userMessage, (int) maxTokens);
1131
+ } catch (Exception e) {
1132
+ exception = e;
1133
+ }
1134
+ return null;
1135
+ }
1136
+
1137
+ @Override
1138
+ protected void onPostExecute(String result) {
1139
+ if (exception != null) {
1140
+ promise.reject(exception);
1141
+ return;
1142
+ }
1143
+ promise.resolve(result);
1144
+ tasks.remove(this);
1145
+ }
1146
+ }.executeOnExecutor(singleThreadExecutor);
1147
+ tasks.put(task, "generateResponse-" + contextId);
1148
+ }
1149
+
1150
+ public void continueConversation(double id, final String userMessage, final double maxTokens, final Promise promise) {
1151
+ final int contextId = (int) id;
1152
+ AsyncTask task = new AsyncTask<Void, Void, WritableMap>() {
1153
+ private Exception exception;
1154
+
1155
+ @Override
1156
+ protected WritableMap doInBackground(Void... voids) {
1157
+ try {
1158
+ LlamaContext context = contexts.get(contextId);
1159
+ if (context == null) {
1160
+ throw new Exception("Context not found");
1161
+ }
1162
+ if (context.isPredicting()) {
1163
+ throw new Exception("Context is busy");
1164
+ }
1165
+ return context.continueConversation(userMessage, (int) maxTokens);
1166
+ } catch (Exception e) {
1167
+ exception = e;
1168
+ }
1169
+ return null;
1170
+ }
1171
+
1172
+ @Override
1173
+ protected void onPostExecute(WritableMap result) {
1174
+ if (exception != null) {
1175
+ promise.reject(exception);
1176
+ return;
1177
+ }
1178
+ promise.resolve(result);
1179
+ tasks.remove(this);
1180
+ }
1181
+ }.executeOnExecutor(singleThreadExecutor);
1182
+ tasks.put(task, "continueConversation-" + contextId);
1183
+ }
1184
+
1185
+ public void clearConversation(double id, final Promise promise) {
1186
+ final int contextId = (int) id;
1187
+ AsyncTask task = new AsyncTask<Void, Void, Void>() {
1188
+ private Exception exception;
1189
+
1190
+ @Override
1191
+ protected Void doInBackground(Void... voids) {
1192
+ try {
1193
+ LlamaContext context = contexts.get(contextId);
1194
+ if (context == null) {
1195
+ throw new Exception("Context not found");
1196
+ }
1197
+ context.clearConversation();
1198
+ } catch (Exception e) {
1199
+ exception = e;
1200
+ }
1201
+ return null;
1202
+ }
1203
+
1204
+ @Override
1205
+ protected void onPostExecute(Void result) {
1206
+ if (exception != null) {
1207
+ promise.reject(exception);
1208
+ return;
1209
+ }
1210
+ promise.resolve(null);
1211
+ tasks.remove(this);
1212
+ }
1213
+ }.executeOnExecutor(singleThreadExecutor);
1214
+ tasks.put(task, "clearConversation-" + contextId);
1215
+ }
1216
+
1217
+ public void isConversationActive(double id, final Promise promise) {
1218
+ final int contextId = (int) id;
1219
+ AsyncTask task = new AsyncTask<Void, Void, Boolean>() {
1220
+ private Exception exception;
1221
+
1222
+ @Override
1223
+ protected Boolean doInBackground(Void... voids) {
1224
+ try {
1225
+ LlamaContext context = contexts.get(contextId);
1226
+ if (context == null) {
1227
+ throw new Exception("Context not found");
1228
+ }
1229
+ return context.isConversationActive();
1230
+ } catch (Exception e) {
1231
+ exception = e;
1232
+ }
1233
+ return false;
1234
+ }
1235
+
1236
+ @Override
1237
+ protected void onPostExecute(Boolean result) {
1238
+ if (exception != null) {
1239
+ promise.reject(exception);
1240
+ return;
1241
+ }
1242
+ promise.resolve(result);
1243
+ tasks.remove(this);
1244
+ }
1245
+ }.executeOnExecutor(singleThreadExecutor);
1246
+ tasks.put(task, "isConversationActive-" + contextId);
1247
+ }
1111
1248
  }
@@ -524,6 +524,22 @@ public class LlamaContext {
524
524
  releaseVocoder(this.context);
525
525
  }
526
526
 
527
+ public String generateResponse(String userMessage, int maxTokens) {
528
+ return generateResponse(this.context, userMessage, maxTokens);
529
+ }
530
+
531
+ public WritableMap continueConversation(String userMessage, int maxTokens) {
532
+ return continueConversation(this.context, userMessage, maxTokens);
533
+ }
534
+
535
+ public void clearConversation() {
536
+ clearConversation(this.context);
537
+ }
538
+
539
+ public boolean isConversationActive() {
540
+ return isConversationActive(this.context);
541
+ }
542
+
527
543
  static {
528
544
  Log.d(NAME, "Primary ABI: " + Build.SUPPORTED_ABIS[0]);
529
545
 
@@ -775,4 +791,10 @@ public class LlamaContext {
775
791
  protected static native WritableArray getAudioCompletionGuideTokens(long contextPtr, String textToSpeak);
776
792
  protected static native WritableArray decodeAudioTokens(long contextPtr, int[] tokens);
777
793
  protected static native void releaseVocoder(long contextPtr);
794
+
795
+ // New conversation management methods
796
+ protected static native String generateResponse(long contextPtr, String userMessage, int maxTokens);
797
+ protected static native WritableMap continueConversation(long contextPtr, String userMessage, int maxTokens);
798
+ protected static native void clearConversation(long contextPtr);
799
+ protected static native boolean isConversationActive(long contextPtr);
778
800
  }
@@ -1563,7 +1563,59 @@ JNIEXPORT void JNICALL
1563
1563
  Java_com_cactus_LlamaContext_unsetLog(JNIEnv *env, jobject thiz) {
1564
1564
  UNUSED(env);
1565
1565
  UNUSED(thiz);
1566
- llama_log_set(cactus_log_callback_default, NULL);
1566
+ llama_log_set(cactus_log_callback_default, nullptr);
1567
+ }
1568
+
1569
+ JNIEXPORT jstring JNICALL
1570
+ Java_com_cactus_LlamaContext_generateResponse(
1571
+ JNIEnv *env, jobject thiz, jlong context_ptr, jstring user_message, jint max_tokens) {
1572
+ UNUSED(thiz);
1573
+ auto llama = context_map[(long) context_ptr];
1574
+ const char *user_message_chars = env->GetStringUTFChars(user_message, nullptr);
1575
+
1576
+ std::string result = llama->generateResponse(user_message_chars, max_tokens);
1577
+ llama->is_predicting = false;
1578
+
1579
+ env->ReleaseStringUTFChars(user_message, user_message_chars);
1580
+ return env->NewStringUTF(result.c_str());
1581
+ }
1582
+
1583
+ JNIEXPORT jobject JNICALL
1584
+ Java_com_cactus_LlamaContext_continueConversation(
1585
+ JNIEnv *env, jobject thiz, jlong context_ptr, jstring user_message, jint max_tokens) {
1586
+ UNUSED(thiz);
1587
+ auto llama = context_map[(long) context_ptr];
1588
+ const char *user_message_chars = env->GetStringUTFChars(user_message, nullptr);
1589
+
1590
+ cactus::conversation_result result = llama->continueConversation(user_message_chars, max_tokens);
1591
+ llama->is_predicting = false;
1592
+
1593
+ auto result_map = createWriteableMap(env);
1594
+ putString(env, result_map, "text", result.text.c_str());
1595
+ putInt(env, result_map, "time_to_first_token", result.time_to_first_token.count());
1596
+ putInt(env, result_map, "total_time", result.total_time.count());
1597
+ putInt(env, result_map, "tokens_generated", result.tokens_generated);
1598
+
1599
+ env->ReleaseStringUTFChars(user_message, user_message_chars);
1600
+ return reinterpret_cast<jobject>(result_map);
1601
+ }
1602
+
1603
+ JNIEXPORT void JNICALL
1604
+ Java_com_cactus_LlamaContext_clearConversation(
1605
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
1606
+ UNUSED(thiz);
1607
+ UNUSED(env);
1608
+ auto llama = context_map[(long) context_ptr];
1609
+ llama->clearConversation();
1610
+ }
1611
+
1612
+ JNIEXPORT jboolean JNICALL
1613
+ Java_com_cactus_LlamaContext_isConversationActive(
1614
+ JNIEnv *env, jobject thiz, jlong context_ptr) {
1615
+ UNUSED(thiz);
1616
+ UNUSED(env);
1617
+ auto llama = context_map[(long) context_ptr];
1618
+ return llama->isConversationActive();
1567
1619
  }
1568
1620
 
1569
1621
  } // extern "C"
@@ -184,6 +184,26 @@ public class CactusModule extends NativeCactusSpec {
184
184
  cactus.releaseVocoder(id, promise);
185
185
  }
186
186
 
187
+ @ReactMethod
188
+ public void generateResponse(double id, String userMessage, Double maxTokens, Promise promise) {
189
+ cactus.generateResponse(id, userMessage, maxTokens != null ? maxTokens.doubleValue() : 200.0, promise);
190
+ }
191
+
192
+ @ReactMethod
193
+ public void continueConversation(double id, String userMessage, Double maxTokens, Promise promise) {
194
+ cactus.continueConversation(id, userMessage, maxTokens != null ? maxTokens.doubleValue() : 200.0, promise);
195
+ }
196
+
197
+ @ReactMethod
198
+ public void clearConversation(double id, Promise promise) {
199
+ cactus.clearConversation(id, promise);
200
+ }
201
+
202
+ @ReactMethod
203
+ public void isConversationActive(double id, Promise promise) {
204
+ cactus.isConversationActive(id, promise);
205
+ }
206
+
187
207
  @ReactMethod
188
208
  public void releaseContext(double id, Promise promise) {
189
209
  cactus.releaseContext(id, promise);
@@ -183,6 +183,26 @@ public class CactusModule extends ReactContextBaseJavaModule {
183
183
  cactus.releaseVocoder(id, promise);
184
184
  }
185
185
 
186
+ @ReactMethod
187
+ public void generateResponse(double id, final String userMessage, final Double maxTokens, final Promise promise) {
188
+ cactus.generateResponse(id, userMessage, maxTokens != null ? maxTokens.doubleValue() : 200.0, promise);
189
+ }
190
+
191
+ @ReactMethod
192
+ public void continueConversation(double id, final String userMessage, final Double maxTokens, final Promise promise) {
193
+ cactus.continueConversation(id, userMessage, maxTokens != null ? maxTokens.doubleValue() : 200.0, promise);
194
+ }
195
+
196
+ @ReactMethod
197
+ public void clearConversation(double id, final Promise promise) {
198
+ cactus.clearConversation(id, promise);
199
+ }
200
+
201
+ @ReactMethod
202
+ public void isConversationActive(double id, final Promise promise) {
203
+ cactus.isConversationActive(id, promise);
204
+ }
205
+
186
206
  @ReactMethod
187
207
  public void releaseContext(double id, Promise promise) {
188
208
  cactus.releaseContext(id, promise);
@@ -24,6 +24,7 @@ set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cactus)
24
24
  # Define public headers
25
25
  set(PUBLIC_HEADERS
26
26
  ${SOURCE_DIR}/cactus.h
27
+ ${SOURCE_DIR}/cactus_ffi.h
27
28
  ${SOURCE_DIR}/llama.h
28
29
  ${SOURCE_DIR}/llama-impl.h
29
30
  ${SOURCE_DIR}/ggml.h
@@ -42,6 +43,7 @@ add_library(cactus SHARED
42
43
  ${SOURCE_DIR}/cactus_tts.cpp
43
44
  ${SOURCE_DIR}/cactus_bench.cpp
44
45
  ${SOURCE_DIR}/cactus_chat.cpp
46
+ ${SOURCE_DIR}/cactus_ffi.cpp
45
47
  ${SOURCE_DIR}/tools/mtmd/mtmd.cpp
46
48
  ${SOURCE_DIR}/tools/mtmd/mtmd-audio.cpp
47
49
  ${SOURCE_DIR}/tools/mtmd/clip.cpp