react-native-nobodywho 1.0.0 → 1.1.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/Nobodywho.podspec CHANGED
@@ -8,35 +8,33 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1
8
8
 
9
9
  framework_name = "NobodywhoFramework.xcframework"
10
10
  framework_dir = File.join(__dir__, framework_name)
11
+ zip_name = "#{framework_name}.zip"
12
+ zip_path = File.join(__dir__, zip_name)
13
+ url = "https://github.com/nobodywho-ooo/nobodywho/releases/download/nobodywho-react-native-#{version}/#{zip_name}"
11
14
 
12
- # Download the xcframework from GitHub Releases if not already present
13
- unless File.exist?(framework_dir)
14
- zip_name = "#{framework_name}.zip"
15
- zip_path = File.join(__dir__, zip_name)
16
- url = "https://github.com/nobodywho-ooo/nobodywho/releases/download/nobodywho-react-native-#{version}/#{zip_name}"
17
-
18
- puts "[NobodyWho] Downloading xcframework from #{url}"
15
+ # Always download a fresh xcframework to ensure it matches the package version.
16
+ FileUtils.rm_rf(framework_dir) if File.exist?(framework_dir)
19
17
 
20
- # Download using curl (available on all macOS systems)
21
- system("curl", "-L", "-f", "-o", zip_path, url) or
22
- raise "Failed to download NobodyWho xcframework.\n" \
23
- "URL: #{url}\n" \
24
- "Check that the release exists: https://github.com/nobodywho-ooo/nobodywho/releases/tag/nobodywho-react-native-#{version}\n" \
25
- "For local development, manually place the xcframework at: #{framework_dir}"
18
+ puts "[NobodyWho] Downloading xcframework from #{url}"
26
19
 
27
- puts "[NobodyWho] Extracting xcframework..."
28
- system("unzip", "-o", "-q", zip_path, "-d", __dir__) or
29
- raise "Failed to extract #{zip_name}"
20
+ system("curl", "-L", "-f", "-o", zip_path, url) or
21
+ raise "Failed to download NobodyWho xcframework.\n" \
22
+ "URL: #{url}\n" \
23
+ "Check that the release exists: https://github.com/nobodywho-ooo/nobodywho/releases/tag/nobodywho-react-native-#{version}\n" \
24
+ "For local development, manually place the xcframework at: #{framework_dir}"
30
25
 
31
- File.delete(zip_path) if File.exist?(zip_path)
26
+ puts "[NobodyWho] Extracting xcframework..."
27
+ system("unzip", "-o", "-q", zip_path, "-d", __dir__) or
28
+ raise "Failed to extract #{zip_name}"
32
29
 
33
- unless File.exist?(framework_dir)
34
- raise "xcframework not found after extraction: #{framework_dir}"
35
- end
30
+ File.delete(zip_path) if File.exist?(zip_path)
36
31
 
37
- puts "[NobodyWho] xcframework ready at #{framework_dir}"
32
+ unless File.exist?(framework_dir)
33
+ raise "xcframework not found after extraction: #{framework_dir}"
38
34
  end
39
35
 
36
+ puts "[NobodyWho] xcframework ready at #{framework_dir}"
37
+
40
38
  Pod::Spec.new do |s|
41
39
  s.name = "Nobodywho"
42
40
  s.version = package["version"]
package/README.md CHANGED
@@ -119,14 +119,14 @@ See the [Sampling documentation](https://docs.nobodywho.ooo/react-native/samplin
119
119
 
120
120
  ---
121
121
 
122
- ## Vision
122
+ ## Vision & Hearing
123
123
 
124
- Vision support lets you include images in your prompts, so the model can analyze and describe visual content alongside text.
124
+ Provide image and audio information to your LLM.
125
125
 
126
126
  To enable this, you need two model files:
127
127
 
128
- - A **vision-language LLM** (usually has `VL` in the name)
129
- - A matching **projection model** that converts images into tokens the LLM can process (usually has `mmproj` in the name)
128
+ - A multimodal LLM, so the LLM can consume image-tokens or/and audio-tokens
129
+ - A matching projection model, which converts images to image-tokens or/and audio to audio-tokens (usually has `mmproj` in the name)
130
130
 
131
131
  Pass the projection model when loading your model, then use `Prompt` to compose prompts that mix text and images:
132
132
 
@@ -135,17 +135,20 @@ import { Chat, Prompt } from "react-native-nobodywho";
135
135
 
136
136
  const chat = await Chat.fromPath({
137
137
  modelPath: "/path/to/vision-model.gguf",
138
- imageModelPath: "/path/to/mmproj.gguf",
138
+ projectionModelPath: "/path/to/mmproj.gguf",
139
139
  });
140
140
 
141
- const prompt = new Prompt([
142
- Prompt.Text("What do you see in this image?"),
143
- Prompt.Image("/path/to/photo.png"),
144
- ]);
145
-
146
- const response = await chat.ask(prompt).completed();
141
+ const response = await chat
142
+ .ask(
143
+ new Prompt([
144
+ Prompt.Text("Tell me what you see in the image and what you hear in the audio."),
145
+ Prompt.Image("/path/to/dog.png"),
146
+ Prompt.Audio("/path/to/sound.mp3"),
147
+ ]),
148
+ )
149
+ .completed();
147
150
  ```
148
151
 
149
- You can pass multiple images and interleave text between them. If the model performs poorly, try reordering the text and image parts — this can make a noticeable difference. If images consume too much context, increase `contextSize` or preprocess images with compression.
152
+ You can pass multiple images/audio files and interleave text between them. If the model performs poorly, try reordering the text, audio and image parts — this can make a noticeable difference. If images consume too much context, increase `contextSize` or preprocess images with compression.
150
153
 
151
- See the [Vision documentation](https://docs.nobodywho.ooo/react-native/vision/) for model recommendations and advanced tips.
154
+ See the [Vision & Hearing documentation](https://docs.nobodywho.ooo/react-native/vision/) for model recommendations and advanced tips.
@@ -63,7 +63,7 @@ def nobodywhoVersion = packageJson.version
63
63
  // Directory for downloaded native shared libraries.
64
64
  // These .so files are fully linked at CI time with a known-good NDK, eliminating
65
65
  // NDK version coupling for consumers.
66
- def nativeLibsDir = new File(buildDir, "nobodywho-native")
66
+ def nativeLibsDir = new File(buildDir, "nobodywho-native/${nobodywhoVersion}")
67
67
 
68
68
  // Download native shared libraries from GitHub Releases if not already present.
69
69
  task resolveNativeLibraries {
@@ -7,10 +7,10 @@
7
7
  namespace jsi = facebook::jsi;
8
8
  namespace react = facebook::react;
9
9
 
10
- // Automated testing checks Java_com_nobodywho_NobodywhoModule and nobodywho
10
+ // Automated testing checks Java_ooo_nobodywho_NobodywhoModule and nobodywho
11
11
  // by comparing the whole line here.
12
12
  /*
13
- Java_com_nobodywho_NobodywhoModule_nativeMultiply(JNIEnv *env, jclass type, jdouble a, jdouble b) {
13
+ Java_ooo_nobodywho_NobodywhoModule_nativeMultiply(JNIEnv *env, jclass type, jdouble a, jdouble b) {
14
14
  return nobodywho::multiply(a, b);
15
15
  }
16
16
  */
@@ -18,7 +18,7 @@ Java_com_nobodywho_NobodywhoModule_nativeMultiply(JNIEnv *env, jclass type, jdou
18
18
  // Installer coming from NobodywhoModule
19
19
  extern "C"
20
20
  JNIEXPORT jboolean JNICALL
21
- Java_com_nobodywho_NobodywhoModule_nativeInstallRustCrate(
21
+ Java_ooo_nobodywho_NobodywhoModule_nativeInstallRustCrate(
22
22
  JNIEnv *env,
23
23
  jclass type,
24
24
  jlong rtPtr,
@@ -37,7 +37,7 @@ Java_com_nobodywho_NobodywhoModule_nativeInstallRustCrate(
37
37
 
38
38
  extern "C"
39
39
  JNIEXPORT jboolean JNICALL
40
- Java_com_nobodywho_NobodywhoModule_nativeCleanupRustCrate(JNIEnv *env, jclass type, jlong rtPtr) {
40
+ Java_ooo_nobodywho_NobodywhoModule_nativeCleanupRustCrate(JNIEnv *env, jclass type, jlong rtPtr) {
41
41
  auto runtime = reinterpret_cast<jsi::Runtime *>(rtPtr);
42
42
  return nobodywho::cleanupRustCrate(*runtime);
43
43
  }
@@ -95,9 +95,18 @@ typedef struct UniffiForeignFutureResultVoid {
95
95
  } UniffiForeignFutureResultVoid;
96
96
  typedef void (*UniffiForeignFutureCompleteVoid)(
97
97
  uint64_t callback_data, UniffiForeignFutureResultVoid result);
98
+ typedef void (*UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0)(
99
+ uint64_t uniffi_handle, uint64_t downloaded, uint64_t total,
100
+ void *uniffi_out_return, RustCallStatus *rust_call_status);
98
101
  typedef void (*UniffiCallbackInterfaceRustToolCallbackMethod0)(
99
102
  uint64_t uniffi_handle, RustBuffer arguments_json,
100
103
  RustBuffer *uniffi_out_return, RustCallStatus *rust_call_status);
104
+ typedef struct UniffiVTableCallbackInterfaceRustDownloadProgressCallback {
105
+ UniffiCallbackInterfaceFree uniffi_free;
106
+ UniffiCallbackInterfaceClone uniffi_clone;
107
+ UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0
108
+ on_download_progress;
109
+ } UniffiVTableCallbackInterfaceRustDownloadProgressCallback;
101
110
  typedef struct UniffiVTableCallbackInterfaceRustToolCallback {
102
111
  UniffiCallbackInterfaceFree uniffi_free;
103
112
  UniffiCallbackInterfaceClone uniffi_clone;
@@ -253,12 +262,15 @@ uniffi_nobodywho_uniffi_fn_constructor_samplerconfig_from_json(
253
262
  RustBuffer json_str, RustCallStatus *uniffi_out_err);
254
263
  RustBuffer uniffi_nobodywho_uniffi_fn_method_samplerconfig_to_json(
255
264
  /*handle*/ uint64_t ptr, RustCallStatus *uniffi_out_err);
265
+ void uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
266
+ UniffiVTableCallbackInterfaceRustDownloadProgressCallback *vtable);
256
267
  void uniffi_nobodywho_uniffi_fn_init_callback_vtable_rusttoolcallback(
257
268
  UniffiVTableCallbackInterfaceRustToolCallback *vtable);
258
269
  float uniffi_nobodywho_uniffi_fn_func_cosine_similarity(
259
270
  RustBuffer a, RustBuffer b, RustCallStatus *uniffi_out_err);
260
271
  /*handle*/ uint64_t uniffi_nobodywho_uniffi_fn_func_load_model(
261
- RustBuffer model_path, int8_t use_gpu, RustBuffer projection_model_path);
272
+ RustBuffer model_path, int8_t use_gpu, RustBuffer projection_model_path,
273
+ RustBuffer on_download_progress);
262
274
  /*handle*/ uint64_t uniffi_nobodywho_uniffi_fn_func_sampler_preset_default(
263
275
  RustCallStatus *uniffi_out_err);
264
276
  /*handle*/ uint64_t uniffi_nobodywho_uniffi_fn_func_sampler_preset_dry(
@@ -452,6 +464,8 @@ uint16_t uniffi_nobodywho_uniffi_checksum_constructor_rusttool_new();
452
464
  uint16_t uniffi_nobodywho_uniffi_checksum_constructor_rusttool_new_async();
453
465
  uint16_t uniffi_nobodywho_uniffi_checksum_constructor_samplerbuilder_new();
454
466
  uint16_t uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json();
467
+ uint16_t
468
+ uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress();
455
469
  uint16_t uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call();
456
470
  uint32_t ffi_nobodywho_uniffi_uniffi_contract_version();
457
471
  }
@@ -890,6 +904,119 @@ static void cleanup() {
890
904
  } // namespace uniffi::nobodywho::cb::foreignfuturedroppedcallback
891
905
  // Implementation of free callback function CallbackInterfaceFree
892
906
 
907
+ // Callback function:
908
+ // uniffi::nobodywho::st::vtablecallbackinterfacerustdownloadprogresscallback::vtablecallbackinterfacerustdownloadprogresscallback::free::UniffiCallbackInterfaceFree
909
+ //
910
+ // We have the following constraints:
911
+ // - we need to pass a function pointer to Rust.
912
+ // - we need a jsi::Runtime and jsi::Function to call into JS.
913
+ // - function pointers can't store state, so we can't use a lamda.
914
+ //
915
+ // For this, we store a lambda as a global, as `rsLambda`. The `callback`
916
+ // function calls the lambda, which itself calls the `body` which then calls
917
+ // into JS.
918
+ //
919
+ // We then give the `callback` function pointer to Rust which will call the
920
+ // lambda sometime in the future.
921
+ namespace uniffi::nobodywho::st::
922
+ vtablecallbackinterfacerustdownloadprogresscallback::
923
+ vtablecallbackinterfacerustdownloadprogresscallback::free {
924
+ using namespace facebook;
925
+
926
+ // We need to store a lambda in a global so we can call it from
927
+ // a function pointer. The function pointer is passed to Rust.
928
+ static std::function<void(uint64_t)> rsLambda = nullptr;
929
+
930
+ // This is the main body of the callback. It's called from the lambda,
931
+ // which itself is called from the callback function which is passed to Rust.
932
+ static void body(jsi::Runtime &rt,
933
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
934
+ std::shared_ptr<jsi::Value> callbackValue,
935
+ uint64_t rs_handle) {
936
+
937
+ // Convert the arguments from Rust, into jsi::Values.
938
+ // We'll use the Bridging class to do this…
939
+ auto js_handle =
940
+ uniffi_jsi::Bridging<uint64_t>::toJs(rt, callInvoker, rs_handle);
941
+
942
+ // Now we are ready to call the callback.
943
+ // We are already on the JS thread, because this `body` function was
944
+ // invoked from the CallInvoker.
945
+ try {
946
+ // Getting the callback function
947
+ auto cb = callbackValue->asObject(rt).asFunction(rt);
948
+ auto uniffiResult = cb.call(rt, js_handle);
949
+
950
+ } catch (const jsi::JSError &error) {
951
+ std::cout << "Error in callback UniffiCallbackInterfaceFree: "
952
+ << error.what() << std::endl;
953
+ throw error;
954
+ }
955
+ }
956
+
957
+ static void callback(uint64_t rs_handle) {
958
+ // If the runtime has shutdown, then there is no point in trying to
959
+ // call into Javascript. BUT how do we tell if the runtime has shutdown?
960
+ //
961
+ // Answer: the module destructor calls into callback `cleanup` method,
962
+ // which nulls out the rsLamda.
963
+ //
964
+ // If rsLamda is null, then there is no runtime to call into.
965
+ if (rsLambda == nullptr) {
966
+ // This only occurs when destructors are calling into Rust free/drop,
967
+ // which causes the JS callback to be dropped.
968
+ return;
969
+ }
970
+
971
+ // The runtime, the actual callback jsi::funtion, and the callInvoker
972
+ // are all in the lambda.
973
+ rsLambda(rs_handle);
974
+ }
975
+
976
+ [[maybe_unused]] static UniffiCallbackInterfaceFree
977
+ makeCallbackFunction( // uniffi::nobodywho::st::vtablecallbackinterfacerustdownloadprogresscallback::vtablecallbackinterfacerustdownloadprogresscallback::free
978
+ jsi::Runtime &rt,
979
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
980
+ const jsi::Value &value) {
981
+ if (rsLambda != nullptr) {
982
+ // `makeCallbackFunction` is called in two circumstances:
983
+ //
984
+ // 1. at startup, when initializing callback interface vtables.
985
+ // 2. when polling futures. This happens at least once per future that is
986
+ // exposed to Javascript. We know that this is always the same function,
987
+ // `uniffiFutureContinuationCallback` in `async-rust-calls.ts`.
988
+ //
989
+ // We can therefore return the callback function without making anything
990
+ // new if we've been initialized already.
991
+ return callback;
992
+ }
993
+ auto callbackFunction = value.asObject(rt).asFunction(rt);
994
+ auto callbackValue = std::make_shared<jsi::Value>(rt, callbackFunction);
995
+ rsLambda = [&rt, callInvoker, callbackValue](uint64_t rs_handle) {
996
+ // We immediately make a lambda which will do the work of transforming the
997
+ // arguments into JSI values and calling the callback.
998
+ uniffi_runtime::UniffiCallFunc jsLambda =
999
+ [callInvoker, callbackValue, rs_handle](jsi::Runtime &rt) mutable {
1000
+ body(rt, callInvoker, callbackValue, rs_handle);
1001
+ };
1002
+ // We'll then call that lambda from the callInvoker which will
1003
+ // look after calling it on the correct thread.
1004
+
1005
+ callInvoker->invokeNonBlocking(rt, jsLambda);
1006
+ };
1007
+ return callback;
1008
+ }
1009
+
1010
+ // This method is called from the destructor of NativeNobodywho, which only
1011
+ // happens when the jsi::Runtime is being destroyed.
1012
+ static void cleanup() {
1013
+ // The lambda holds a reference to the the Runtime, so when this is nulled
1014
+ // out, then the pointer will no longer be left dangling.
1015
+ rsLambda = nullptr;
1016
+ }
1017
+ } // namespace
1018
+ // uniffi::nobodywho::st::vtablecallbackinterfacerustdownloadprogresscallback::vtablecallbackinterfacerustdownloadprogresscallback::free
1019
+
893
1020
  // Callback function:
894
1021
  // uniffi::nobodywho::st::vtablecallbackinterfacerusttoolcallback::vtablecallbackinterfacerusttoolcallback::free::UniffiCallbackInterfaceFree
895
1022
  //
@@ -1822,6 +1949,314 @@ template <> struct Bridging<UniffiForeignFutureCompleteVoid> {
1822
1949
  return jsi::Value::undefined();
1823
1950
  }
1824
1951
  };
1952
+ } // namespace uniffi::nobodywho
1953
+ // Implementation of CallbackInterfaceClone for vtable field uniffi_clone in
1954
+ // VTableCallbackInterfaceRustDownloadProgressCallback
1955
+
1956
+ // Callback function:
1957
+ // uniffi::nobodywho::cb::callbackinterfaceclone::vtablecallbackinterfacerustdownloadprogresscallback::UniffiCallbackInterfaceClone
1958
+ //
1959
+ // We have the following constraints:
1960
+ // - we need to pass a function pointer to Rust.
1961
+ // - we need a jsi::Runtime and jsi::Function to call into JS.
1962
+ // - function pointers can't store state, so we can't use a lamda.
1963
+ //
1964
+ // For this, we store a lambda as a global, as `rsLambda`. The `callback`
1965
+ // function calls the lambda, which itself calls the `body` which then calls
1966
+ // into JS.
1967
+ //
1968
+ // We then give the `callback` function pointer to Rust which will call the
1969
+ // lambda sometime in the future.
1970
+ namespace uniffi::nobodywho::cb::callbackinterfaceclone::
1971
+ vtablecallbackinterfacerustdownloadprogresscallback {
1972
+ using namespace facebook;
1973
+
1974
+ // We need to store a lambda in a global so we can call it from
1975
+ // a function pointer. The function pointer is passed to Rust.
1976
+ static std::function<void(uint64_t, uint64_t *)> rsLambda = nullptr;
1977
+
1978
+ // This is the main body of the callback. It's called from the lambda,
1979
+ // which itself is called from the callback function which is passed to Rust.
1980
+ static void body(jsi::Runtime &rt,
1981
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
1982
+ std::shared_ptr<jsi::Value> callbackValue, uint64_t rs_handle,
1983
+ uint64_t *uniffi_direct_return) {
1984
+
1985
+ // Convert the arguments from Rust, into jsi::Values.
1986
+ // We'll use the Bridging class to do this…
1987
+ auto js_handle =
1988
+ uniffi_jsi::Bridging<uint64_t>::toJs(rt, callInvoker, rs_handle);
1989
+
1990
+ // Now we are ready to call the callback.
1991
+ // We are already on the JS thread, because this `body` function was
1992
+ // invoked from the CallInvoker.
1993
+ try {
1994
+ // Getting the callback function
1995
+ auto cb = callbackValue->asObject(rt).asFunction(rt);
1996
+ auto uniffiResult = cb.call(rt, js_handle);
1997
+
1998
+ // Write the direct return value back to the caller.
1999
+ if (uniffi_direct_return != nullptr) {
2000
+ *uniffi_direct_return =
2001
+ uniffi_jsi::Bridging<uint64_t>::fromJs(rt, callInvoker, uniffiResult);
2002
+ }
2003
+ } catch (const jsi::JSError &error) {
2004
+ std::cout << "Error in callback UniffiCallbackInterfaceClone: "
2005
+ << error.what() << std::endl;
2006
+ throw error;
2007
+ }
2008
+ }
2009
+
2010
+ static uint64_t callback(uint64_t rs_handle) {
2011
+ // If the runtime has shutdown, then there is no point in trying to
2012
+ // call into Javascript. BUT how do we tell if the runtime has shutdown?
2013
+ //
2014
+ // Answer: the module destructor calls into callback `cleanup` method,
2015
+ // which nulls out the rsLamda.
2016
+ //
2017
+ // If rsLamda is null, then there is no runtime to call into.
2018
+ if (rsLambda == nullptr) {
2019
+ // This only occurs when destructors are calling into Rust free/drop,
2020
+ // which causes the JS callback to be dropped.
2021
+ return 0;
2022
+ }
2023
+ uint64_t uniffi_result = 0;
2024
+
2025
+ // The runtime, the actual callback jsi::funtion, and the callInvoker
2026
+ // are all in the lambda.
2027
+ rsLambda(rs_handle, &uniffi_result);
2028
+ return uniffi_result;
2029
+ }
2030
+
2031
+ [[maybe_unused]] static UniffiCallbackInterfaceClone
2032
+ makeCallbackFunction( // uniffi::nobodywho::cb::callbackinterfaceclone::vtablecallbackinterfacerustdownloadprogresscallback
2033
+ jsi::Runtime &rt,
2034
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
2035
+ const jsi::Value &value) {
2036
+ if (rsLambda != nullptr) {
2037
+ // `makeCallbackFunction` is called in two circumstances:
2038
+ //
2039
+ // 1. at startup, when initializing callback interface vtables.
2040
+ // 2. when polling futures. This happens at least once per future that is
2041
+ // exposed to Javascript. We know that this is always the same function,
2042
+ // `uniffiFutureContinuationCallback` in `async-rust-calls.ts`.
2043
+ //
2044
+ // We can therefore return the callback function without making anything
2045
+ // new if we've been initialized already.
2046
+ return callback;
2047
+ }
2048
+ auto callbackFunction = value.asObject(rt).asFunction(rt);
2049
+ auto callbackValue = std::make_shared<jsi::Value>(rt, callbackFunction);
2050
+ rsLambda = [&rt, callInvoker, callbackValue](uint64_t rs_handle,
2051
+ uint64_t *uniffi_direct_return) {
2052
+ // We immediately make a lambda which will do the work of transforming the
2053
+ // arguments into JSI values and calling the callback.
2054
+ uniffi_runtime::UniffiCallFunc jsLambda =
2055
+ [callInvoker, callbackValue, rs_handle,
2056
+ uniffi_direct_return](jsi::Runtime &rt) mutable {
2057
+ body(rt, callInvoker, callbackValue, rs_handle, uniffi_direct_return);
2058
+ };
2059
+ // We'll then call that lambda from the callInvoker which will
2060
+ // look after calling it on the correct thread.
2061
+ callInvoker->invokeBlocking(rt, jsLambda);
2062
+ };
2063
+ return callback;
2064
+ }
2065
+
2066
+ // This method is called from the destructor of NativeNobodywho, which only
2067
+ // happens when the jsi::Runtime is being destroyed.
2068
+ static void cleanup() {
2069
+ // The lambda holds a reference to the the Runtime, so when this is nulled
2070
+ // out, then the pointer will no longer be left dangling.
2071
+ rsLambda = nullptr;
2072
+ }
2073
+ } // namespace
2074
+ // uniffi::nobodywho::cb::callbackinterfaceclone::vtablecallbackinterfacerustdownloadprogresscallback
2075
+ // Implementation of CallbackInterfaceRustDownloadProgressCallbackMethod0 for
2076
+ // vtable field on_download_progress in
2077
+ // VTableCallbackInterfaceRustDownloadProgressCallback
2078
+
2079
+ // Callback function:
2080
+ // uniffi::nobodywho::cb::callbackinterfacerustdownloadprogresscallbackmethod0::vtablecallbackinterfacerustdownloadprogresscallback::UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0
2081
+ //
2082
+ // We have the following constraints:
2083
+ // - we need to pass a function pointer to Rust.
2084
+ // - we need a jsi::Runtime and jsi::Function to call into JS.
2085
+ // - function pointers can't store state, so we can't use a lamda.
2086
+ //
2087
+ // For this, we store a lambda as a global, as `rsLambda`. The `callback`
2088
+ // function calls the lambda, which itself calls the `body` which then calls
2089
+ // into JS.
2090
+ //
2091
+ // We then give the `callback` function pointer to Rust which will call the
2092
+ // lambda sometime in the future.
2093
+ namespace uniffi::nobodywho::cb::
2094
+ callbackinterfacerustdownloadprogresscallbackmethod0::
2095
+ vtablecallbackinterfacerustdownloadprogresscallback {
2096
+ using namespace facebook;
2097
+
2098
+ // We need to store a lambda in a global so we can call it from
2099
+ // a function pointer. The function pointer is passed to Rust.
2100
+ static std::function<void(uint64_t, uint64_t, uint64_t, void *,
2101
+ RustCallStatus *)>
2102
+ rsLambda = nullptr;
2103
+
2104
+ // This is the main body of the callback. It's called from the lambda,
2105
+ // which itself is called from the callback function which is passed to Rust.
2106
+ static void body(jsi::Runtime &rt,
2107
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
2108
+ std::shared_ptr<jsi::Value> callbackValue,
2109
+ uint64_t rs_uniffiHandle, uint64_t rs_downloaded,
2110
+ uint64_t rs_total, void *rs_uniffiOutReturn,
2111
+ RustCallStatus *uniffi_call_status) {
2112
+
2113
+ // Convert the arguments from Rust, into jsi::Values.
2114
+ // We'll use the Bridging class to do this…
2115
+ auto js_uniffiHandle =
2116
+ uniffi_jsi::Bridging<uint64_t>::toJs(rt, callInvoker, rs_uniffiHandle);
2117
+ auto js_downloaded =
2118
+ uniffi_jsi::Bridging<uint64_t>::toJs(rt, callInvoker, rs_downloaded);
2119
+ auto js_total =
2120
+ uniffi_jsi::Bridging<uint64_t>::toJs(rt, callInvoker, rs_total);
2121
+
2122
+ // Now we are ready to call the callback.
2123
+ // We are already on the JS thread, because this `body` function was
2124
+ // invoked from the CallInvoker.
2125
+ try {
2126
+ // Getting the callback function
2127
+ auto cb = callbackValue->asObject(rt).asFunction(rt);
2128
+ auto uniffiResult = cb.call(rt, js_uniffiHandle, js_downloaded, js_total);
2129
+
2130
+ // Now copy the result back from JS into the RustCallStatus object.
2131
+ uniffi::nobodywho::Bridging<RustCallStatus>::copyFromJs(
2132
+ rt, callInvoker, uniffiResult, uniffi_call_status);
2133
+
2134
+ if (uniffi_call_status->code != UNIFFI_CALL_STATUS_OK) {
2135
+ // The JS callback finished abnormally, so we cannot retrieve the return
2136
+ // value.
2137
+ return;
2138
+ }
2139
+
2140
+ } catch (const jsi::JSError &error) {
2141
+ std::cout << "Error in callback "
2142
+ "UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0: "
2143
+ << error.what() << std::endl;
2144
+ throw error;
2145
+ }
2146
+ }
2147
+
2148
+ static void callback(uint64_t rs_uniffiHandle, uint64_t rs_downloaded,
2149
+ uint64_t rs_total, void *rs_uniffiOutReturn,
2150
+ RustCallStatus *uniffi_call_status) {
2151
+ // If the runtime has shutdown, then there is no point in trying to
2152
+ // call into Javascript. BUT how do we tell if the runtime has shutdown?
2153
+ //
2154
+ // Answer: the module destructor calls into callback `cleanup` method,
2155
+ // which nulls out the rsLamda.
2156
+ //
2157
+ // If rsLamda is null, then there is no runtime to call into.
2158
+ if (rsLambda == nullptr) {
2159
+ // This only occurs when destructors are calling into Rust free/drop,
2160
+ // which causes the JS callback to be dropped.
2161
+ return;
2162
+ }
2163
+
2164
+ // The runtime, the actual callback jsi::funtion, and the callInvoker
2165
+ // are all in the lambda.
2166
+ rsLambda(rs_uniffiHandle, rs_downloaded, rs_total, rs_uniffiOutReturn,
2167
+ uniffi_call_status);
2168
+ }
2169
+
2170
+ [[maybe_unused]] static UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0
2171
+ makeCallbackFunction( // uniffi::nobodywho::cb::callbackinterfacerustdownloadprogresscallbackmethod0::vtablecallbackinterfacerustdownloadprogresscallback
2172
+ jsi::Runtime &rt,
2173
+ std::shared_ptr<uniffi_runtime::UniffiCallInvoker> callInvoker,
2174
+ const jsi::Value &value) {
2175
+ if (rsLambda != nullptr) {
2176
+ // `makeCallbackFunction` is called in two circumstances:
2177
+ //
2178
+ // 1. at startup, when initializing callback interface vtables.
2179
+ // 2. when polling futures. This happens at least once per future that is
2180
+ // exposed to Javascript. We know that this is always the same function,
2181
+ // `uniffiFutureContinuationCallback` in `async-rust-calls.ts`.
2182
+ //
2183
+ // We can therefore return the callback function without making anything
2184
+ // new if we've been initialized already.
2185
+ return callback;
2186
+ }
2187
+ auto callbackFunction = value.asObject(rt).asFunction(rt);
2188
+ auto callbackValue = std::make_shared<jsi::Value>(rt, callbackFunction);
2189
+ rsLambda = [&rt, callInvoker,
2190
+ callbackValue](uint64_t rs_uniffiHandle, uint64_t rs_downloaded,
2191
+ uint64_t rs_total, void *rs_uniffiOutReturn,
2192
+ RustCallStatus *uniffi_call_status) {
2193
+ // We immediately make a lambda which will do the work of transforming the
2194
+ // arguments into JSI values and calling the callback.
2195
+ uniffi_runtime::UniffiCallFunc jsLambda =
2196
+ [callInvoker, callbackValue, rs_uniffiHandle, rs_downloaded, rs_total,
2197
+ rs_uniffiOutReturn, uniffi_call_status](jsi::Runtime &rt) mutable {
2198
+ body(rt, callInvoker, callbackValue, rs_uniffiHandle, rs_downloaded,
2199
+ rs_total, rs_uniffiOutReturn, uniffi_call_status);
2200
+ };
2201
+ // We'll then call that lambda from the callInvoker which will
2202
+ // look after calling it on the correct thread.
2203
+ callInvoker->invokeBlocking(rt, jsLambda);
2204
+ };
2205
+ return callback;
2206
+ }
2207
+
2208
+ // This method is called from the destructor of NativeNobodywho, which only
2209
+ // happens when the jsi::Runtime is being destroyed.
2210
+ static void cleanup() {
2211
+ // The lambda holds a reference to the the Runtime, so when this is nulled
2212
+ // out, then the pointer will no longer be left dangling.
2213
+ rsLambda = nullptr;
2214
+ }
2215
+ } // namespace
2216
+ // uniffi::nobodywho::cb::callbackinterfacerustdownloadprogresscallbackmethod0::vtablecallbackinterfacerustdownloadprogresscallback
2217
+ namespace uniffi::nobodywho {
2218
+ using namespace facebook;
2219
+ using CallInvoker = uniffi_runtime::UniffiCallInvoker;
2220
+
2221
+ template <>
2222
+ struct Bridging<UniffiVTableCallbackInterfaceRustDownloadProgressCallback> {
2223
+ static UniffiVTableCallbackInterfaceRustDownloadProgressCallback
2224
+ fromJs(jsi::Runtime &rt, std::shared_ptr<CallInvoker> callInvoker,
2225
+ const jsi::Value &jsValue) {
2226
+ // Check if the input is an object
2227
+ if (!jsValue.isObject()) {
2228
+ throw jsi::JSError(
2229
+ rt, "Expected an object for "
2230
+ "UniffiVTableCallbackInterfaceRustDownloadProgressCallback");
2231
+ }
2232
+
2233
+ // Get the object from the jsi::Value
2234
+ auto jsObject = jsValue.getObject(rt);
2235
+
2236
+ // Create the vtable struct
2237
+ UniffiVTableCallbackInterfaceRustDownloadProgressCallback rsObject;
2238
+
2239
+ // Create the vtable from the js callbacks.
2240
+ rsObject.uniffi_free = uniffi::nobodywho::st::
2241
+ vtablecallbackinterfacerustdownloadprogresscallback::
2242
+ vtablecallbackinterfacerustdownloadprogresscallback::free::
2243
+ makeCallbackFunction(rt, callInvoker,
2244
+ jsObject.getProperty(rt, "uniffiFree"));
2245
+ rsObject.uniffi_clone = uniffi::nobodywho::cb::callbackinterfaceclone::
2246
+ vtablecallbackinterfacerustdownloadprogresscallback::
2247
+ makeCallbackFunction(rt, callInvoker,
2248
+ jsObject.getProperty(rt, "uniffiClone"));
2249
+ rsObject.on_download_progress = uniffi::nobodywho::cb::
2250
+ callbackinterfacerustdownloadprogresscallbackmethod0::
2251
+ vtablecallbackinterfacerustdownloadprogresscallback::
2252
+ makeCallbackFunction(
2253
+ rt, callInvoker,
2254
+ jsObject.getProperty(rt, "onDownloadProgress"));
2255
+
2256
+ return rsObject;
2257
+ }
2258
+ };
2259
+
1825
2260
  } // namespace uniffi::nobodywho
1826
2261
  // Implementation of CallbackInterfaceClone for vtable field uniffi_clone in
1827
2262
  // VTableCallbackInterfaceRustToolCallback
@@ -2886,6 +3321,19 @@ NativeNobodywho::NativeNobodywho(
2886
3321
  ->cpp_uniffi_nobodywho_uniffi_fn_method_samplerconfig_to_json(
2887
3322
  rt, thisVal, args, count);
2888
3323
  });
3324
+ props["ubrn_uniffi_nobodywho_uniffi_fn_init_callback_vtable_"
3325
+ "rustdownloadprogresscallback"] = jsi::Function::createFromHostFunction(
3326
+ rt,
3327
+ jsi::PropNameID::forAscii(rt,
3328
+ "ubrn_uniffi_nobodywho_uniffi_fn_init_callback_"
3329
+ "vtable_rustdownloadprogresscallback"),
3330
+ 1,
3331
+ [this](jsi::Runtime &rt, const jsi::Value &thisVal,
3332
+ const jsi::Value *args, size_t count) -> jsi::Value {
3333
+ return this
3334
+ ->cpp_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
3335
+ rt, thisVal, args, count);
3336
+ });
2889
3337
  props["ubrn_uniffi_nobodywho_uniffi_fn_init_callback_vtable_"
2890
3338
  "rusttoolcallback"] = jsi::Function::createFromHostFunction(
2891
3339
  rt,
@@ -2914,7 +3362,7 @@ NativeNobodywho::NativeNobodywho(
2914
3362
  rt,
2915
3363
  jsi::PropNameID::forAscii(
2916
3364
  rt, "ubrn_uniffi_nobodywho_uniffi_fn_func_load_model"),
2917
- 3,
3365
+ 4,
2918
3366
  [this](jsi::Runtime &rt, const jsi::Value &thisVal,
2919
3367
  const jsi::Value *args, size_t count) -> jsi::Value {
2920
3368
  return this->cpp_uniffi_nobodywho_uniffi_fn_func_load_model(
@@ -4188,6 +4636,20 @@ NativeNobodywho::NativeNobodywho(
4188
4636
  ->cpp_uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json(
4189
4637
  rt, thisVal, args, count);
4190
4638
  });
4639
+ props["ubrn_uniffi_nobodywho_uniffi_checksum_method_"
4640
+ "rustdownloadprogresscallback_on_download_progress"] =
4641
+ jsi::Function::createFromHostFunction(
4642
+ rt,
4643
+ jsi::PropNameID::forAscii(
4644
+ rt, "ubrn_uniffi_nobodywho_uniffi_checksum_method_"
4645
+ "rustdownloadprogresscallback_on_download_progress"),
4646
+ 0,
4647
+ [this](jsi::Runtime &rt, const jsi::Value &thisVal,
4648
+ const jsi::Value *args, size_t count) -> jsi::Value {
4649
+ return this
4650
+ ->cpp_uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress(
4651
+ rt, thisVal, args, count);
4652
+ });
4191
4653
  props["ubrn_uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call"] =
4192
4654
  jsi::Function::createFromHostFunction(
4193
4655
  rt,
@@ -4352,8 +4814,14 @@ NativeNobodywho::~NativeNobodywho() {
4352
4814
  // Cleanup for callback function ForeignFutureDroppedCallback
4353
4815
  uniffi::nobodywho::cb::foreignfuturedroppedcallback::cleanup();
4354
4816
  // Cleanup for "free" callback function CallbackInterfaceFree
4817
+ uniffi::nobodywho::st::vtablecallbackinterfacerustdownloadprogresscallback::
4818
+ vtablecallbackinterfacerustdownloadprogresscallback::free::cleanup();
4355
4819
  uniffi::nobodywho::st::vtablecallbackinterfacerusttoolcallback::
4356
4820
  vtablecallbackinterfacerusttoolcallback::free::cleanup();
4821
+ uniffi::nobodywho::cb::callbackinterfaceclone::
4822
+ vtablecallbackinterfacerustdownloadprogresscallback::cleanup();
4823
+ uniffi::nobodywho::cb::callbackinterfacerustdownloadprogresscallbackmethod0::
4824
+ vtablecallbackinterfacerustdownloadprogresscallback::cleanup();
4357
4825
  uniffi::nobodywho::cb::callbackinterfaceclone::
4358
4826
  vtablecallbackinterfacerusttoolcallback::cleanup();
4359
4827
  uniffi::nobodywho::cb::callbackinterfacerusttoolcallbackmethod0::
@@ -5395,6 +5863,21 @@ NativeNobodywho::cpp_uniffi_nobodywho_uniffi_fn_method_samplerconfig_to_json(
5395
5863
 
5396
5864
  return uniffi::nobodywho::Bridging<RustBuffer>::toJs(rt, callInvoker, value);
5397
5865
  }
5866
+ jsi::Value NativeNobodywho::
5867
+ cpp_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
5868
+ jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
5869
+ size_t count) {
5870
+ auto vtableInstance = uniffi::nobodywho::Bridging<
5871
+ UniffiVTableCallbackInterfaceRustDownloadProgressCallback>::
5872
+ fromJs(rt, callInvoker, args[0]);
5873
+
5874
+ std::lock_guard<std::mutex> lock(uniffi::nobodywho::registry::vtableMutex);
5875
+ uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
5876
+ uniffi::nobodywho::registry::putTable(
5877
+ "UniffiVTableCallbackInterfaceRustDownloadProgressCallback",
5878
+ vtableInstance));
5879
+ return jsi::Value::undefined();
5880
+ }
5398
5881
  jsi::Value NativeNobodywho::
5399
5882
  cpp_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rusttoolcallback(
5400
5883
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
@@ -5430,8 +5913,9 @@ jsi::Value NativeNobodywho::cpp_uniffi_nobodywho_uniffi_fn_func_load_model(
5430
5913
  auto value = uniffi_nobodywho_uniffi_fn_func_load_model(
5431
5914
  uniffi::nobodywho::Bridging<RustBuffer>::fromJs(rt, callInvoker, args[0]),
5432
5915
  uniffi_jsi::Bridging<int8_t>::fromJs(rt, callInvoker, args[1]),
5916
+ uniffi::nobodywho::Bridging<RustBuffer>::fromJs(rt, callInvoker, args[2]),
5433
5917
  uniffi::nobodywho::Bridging<RustBuffer>::fromJs(rt, callInvoker,
5434
- args[2]));
5918
+ args[3]));
5435
5919
 
5436
5920
  return uniffi_jsi::Bridging</*handle*/ uint64_t>::toJs(rt, callInvoker,
5437
5921
  value);
@@ -6539,6 +7023,15 @@ jsi::Value NativeNobodywho::
6539
7023
 
6540
7024
  return uniffi_jsi::Bridging<uint16_t>::toJs(rt, callInvoker, value);
6541
7025
  }
7026
+ jsi::Value NativeNobodywho::
7027
+ cpp_uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress(
7028
+ jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
7029
+ size_t count) {
7030
+ auto value =
7031
+ uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress();
7032
+
7033
+ return uniffi_jsi::Bridging<uint16_t>::toJs(rt, callInvoker, value);
7034
+ }
6542
7035
  jsi::Value NativeNobodywho::
6543
7036
  cpp_uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call(
6544
7037
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
@@ -211,6 +211,10 @@ protected:
211
211
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
212
212
  size_t count);
213
213
  jsi::Value
214
+ cpp_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
215
+ jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
216
+ size_t count);
217
+ jsi::Value
214
218
  cpp_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rusttoolcallback(
215
219
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
216
220
  size_t count);
@@ -573,6 +577,10 @@ protected:
573
577
  cpp_uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json(
574
578
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
575
579
  size_t count);
580
+ jsi::Value
581
+ cpp_uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress(
582
+ jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
583
+ size_t count);
576
584
  jsi::Value cpp_uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call(
577
585
  jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args,
578
586
  size_t count);
@@ -77,9 +77,10 @@ interface NativeModuleInterface {
77
77
  ubrn_uniffi_nobodywho_uniffi_fn_free_samplerconfig(handle: bigint, uniffi_out_err: UniffiRustCallStatus): void;
78
78
  ubrn_uniffi_nobodywho_uniffi_fn_constructor_samplerconfig_from_json(jsonStr: Uint8Array, uniffi_out_err: UniffiRustCallStatus): bigint;
79
79
  ubrn_uniffi_nobodywho_uniffi_fn_method_samplerconfig_to_json(ptr: bigint, uniffi_out_err: UniffiRustCallStatus): Uint8Array;
80
+ ubrn_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(vtable: UniffiVTableCallbackInterfaceRustDownloadProgressCallback): void;
80
81
  ubrn_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rusttoolcallback(vtable: UniffiVTableCallbackInterfaceRustToolCallback): void;
81
82
  ubrn_uniffi_nobodywho_uniffi_fn_func_cosine_similarity(a: Uint8Array, b: Uint8Array, uniffi_out_err: UniffiRustCallStatus): number;
82
- ubrn_uniffi_nobodywho_uniffi_fn_func_load_model(modelPath: Uint8Array, useGpu: number, projectionModelPath: Uint8Array): bigint;
83
+ ubrn_uniffi_nobodywho_uniffi_fn_func_load_model(modelPath: Uint8Array, useGpu: number, projectionModelPath: Uint8Array, onDownloadProgress: Uint8Array): bigint;
83
84
  ubrn_uniffi_nobodywho_uniffi_fn_func_sampler_preset_default(uniffi_out_err: UniffiRustCallStatus): bigint;
84
85
  ubrn_uniffi_nobodywho_uniffi_fn_func_sampler_preset_dry(uniffi_out_err: UniffiRustCallStatus): bigint;
85
86
  ubrn_uniffi_nobodywho_uniffi_fn_func_sampler_preset_grammar(grammar: Uint8Array, uniffi_out_err: UniffiRustCallStatus): bigint;
@@ -189,6 +190,7 @@ interface NativeModuleInterface {
189
190
  ubrn_uniffi_nobodywho_uniffi_checksum_constructor_rusttool_new_async(): number;
190
191
  ubrn_uniffi_nobodywho_uniffi_checksum_constructor_samplerbuilder_new(): number;
191
192
  ubrn_uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json(): number;
193
+ ubrn_uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress(): number;
192
194
  ubrn_uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call(): number;
193
195
  ubrn_ffi_nobodywho_uniffi_uniffi_contract_version(): number;
194
196
  ubrn_uniffi_internal_fn_method_rustchat_ffi__bless_pointer(pointer: bigint, uniffi_out_err: UniffiRustCallStatus): UniffiGcObject;
@@ -278,8 +280,15 @@ export type UniffiForeignFutureResultVoid = {
278
280
  callStatus: UniffiRustCallStatus;
279
281
  };
280
282
  export type UniffiForeignFutureCompleteVoid = (callbackData: bigint, result: UniffiForeignFutureResultVoid) => void;
283
+ type UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0 = (uniffiHandle: bigint, downloaded: bigint, total: bigint) => UniffiResult<void>
284
+ ;
281
285
  type UniffiCallbackInterfaceRustToolCallbackMethod0 = (uniffiHandle: bigint, argumentsJson: Uint8Array) => Uint8Array
282
286
  ;
287
+ export type UniffiVTableCallbackInterfaceRustDownloadProgressCallback = {
288
+ uniffiFree: UniffiCallbackInterfaceFree;
289
+ uniffiClone: UniffiCallbackInterfaceClone;
290
+ onDownloadProgress: UniffiCallbackInterfaceRustDownloadProgressCallbackMethod0;
291
+ };
283
292
  export type UniffiVTableCallbackInterfaceRustToolCallback = {
284
293
  uniffiFree: UniffiCallbackInterfaceFree;
285
294
  uniffiClone: UniffiCallbackInterfaceClone;
@@ -32,6 +32,7 @@ import nativeModule, {
32
32
  type UniffiForeignFutureCompleteRustBuffer,
33
33
  type UniffiForeignFutureResultVoid,
34
34
  type UniffiForeignFutureCompleteVoid,
35
+ type UniffiVTableCallbackInterfaceRustDownloadProgressCallback,
35
36
  type UniffiVTableCallbackInterfaceRustToolCallback,
36
37
  } from "./nobodywho-ffi";
37
38
  import {
@@ -104,13 +105,13 @@ export function cosineSimilarity(a: Array</*f32*/number>, b: Array</*f32*/number
104
105
  * uniffi-bindgen-react-native generates invalid JS (`async static` instead
105
106
  * of `static async`) for async constructors.
106
107
  */
107
- export async function loadModel(modelPath: string, useGpu: boolean, projectionModelPath: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise<RustModelInterface> /*throws*/ {
108
+ export async function loadModel(modelPath: string, useGpu: boolean, projectionModelPath: string | undefined, onDownloadProgress: RustDownloadProgressCallback | undefined, asyncOpts_?: { signal: AbortSignal }): Promise<RustModelInterface> /*throws*/ {
108
109
  const __stack = uniffiIsDebug ? new Error().stack : undefined;
109
110
  try {
110
111
  return await uniffiRustCallAsync(
111
112
  /*rustCaller:*/ uniffiCaller,
112
113
  /*rustFutureFunc:*/ () => {
113
- return nativeModule().ubrn_uniffi_nobodywho_uniffi_fn_func_load_model(FfiConverterString.lower(modelPath),FfiConverterBool.lower(useGpu),FfiConverterOptionalString.lower(projectionModelPath)
114
+ return nativeModule().ubrn_uniffi_nobodywho_uniffi_fn_func_load_model(FfiConverterString.lower(modelPath),FfiConverterBool.lower(useGpu),FfiConverterOptionalString.lower(projectionModelPath),FfiConverterOptionalTypeRustDownloadProgressCallback.lower(onDownloadProgress)
114
115
  );
115
116
  },
116
117
  /*pollFunc:*/ nativeModule().ubrn_ffi_nobodywho_uniffi_rust_future_poll_u64,
@@ -234,6 +235,69 @@ export function samplerPresetTopP(topP: /*f32*/number): SamplerConfigInterface {
234
235
 
235
236
 
236
237
 
238
+ /**
239
+ * Callback interface for download progress reporting. Implement this in your
240
+ * language to receive `(downloadedBytes, totalBytes)` events while a remote
241
+ * model is being downloaded. Throttled to ~10 Hz with a guaranteed final emit
242
+ * on completion. Not invoked for cached/local files.
243
+ */
244
+ export interface RustDownloadProgressCallback {
245
+
246
+ onDownloadProgress(downloaded: /*u64*/bigint, total: /*u64*/bigint) : void;
247
+ }
248
+
249
+
250
+ // Put the implementation in a struct so we don't pollute the top-level namespace
251
+ const uniffiCallbackInterfaceRustDownloadProgressCallback: { vtable: UniffiVTableCallbackInterfaceRustDownloadProgressCallback; register: () => void; } = {
252
+ // Create the VTable using a series of closures.
253
+ // ts automatically converts these into C callback functions.
254
+ vtable: {
255
+ onDownloadProgress: (
256
+ uniffiHandle: bigint,
257
+ downloaded: bigint,
258
+ total: bigint,) => {
259
+ const uniffiMakeCall =
260
+ ()
261
+ : void => {
262
+ const jsCallback = FfiConverterTypeRustDownloadProgressCallback.lift(uniffiHandle);
263
+ return jsCallback.onDownloadProgress(
264
+ FfiConverterUInt64.lift(downloaded),
265
+ FfiConverterUInt64.lift(total)
266
+ )
267
+ };
268
+ const uniffiResult = UniffiResult.ready<void>();
269
+ const uniffiHandleSuccess = (obj: any) => {};
270
+ const uniffiHandleError = (code: number, errBuf: UniffiByteArray) => {
271
+ UniffiResult.writeError(uniffiResult, code, errBuf);
272
+ };
273
+ uniffiTraitInterfaceCall(
274
+ /*makeCall:*/ uniffiMakeCall,
275
+ /*handleSuccess:*/ uniffiHandleSuccess,
276
+ /*handleError:*/ uniffiHandleError,
277
+ /*lowerString:*/ FfiConverterString.lower
278
+ )
279
+ return uniffiResult;
280
+ },
281
+ uniffiFree: (uniffiHandle: UniffiHandle): void => {
282
+ // RustDownloadProgressCallback: this will throw a stale handle error if the handle isn't found.
283
+ FfiConverterTypeRustDownloadProgressCallback.drop(uniffiHandle);
284
+ },
285
+ uniffiClone: (uniffiHandle: UniffiHandle): UniffiHandle => {
286
+ return FfiConverterTypeRustDownloadProgressCallback.clone(uniffiHandle);
287
+ }
288
+ },
289
+ register: () => {
290
+ nativeModule().ubrn_uniffi_nobodywho_uniffi_fn_init_callback_vtable_rustdownloadprogresscallback(
291
+ uniffiCallbackInterfaceRustDownloadProgressCallback.vtable
292
+ );
293
+ },
294
+ };
295
+
296
+ // FfiConverter protocol for callback interfaces
297
+ const FfiConverterTypeRustDownloadProgressCallback = new FfiConverterCallback<RustDownloadProgressCallback>();
298
+
299
+
300
+
237
301
  /**
238
302
  * Callback interface for tool functions.
239
303
  * Implement this in your language to provide the tool's logic.
@@ -2891,6 +2955,10 @@ const uniffiTypeSamplerConfigObjectFactory: UniffiObjectFactory<SamplerConfigInt
2891
2955
  const FfiConverterTypeSamplerConfig = new FfiConverterObject(uniffiTypeSamplerConfigObjectFactory);
2892
2956
 
2893
2957
 
2958
+ // FfiConverter for RustDownloadProgressCallback | undefined
2959
+ const FfiConverterOptionalTypeRustDownloadProgressCallback = new FfiConverterOptional(FfiConverterTypeRustDownloadProgressCallback);
2960
+
2961
+
2894
2962
  // FfiConverter for PendingToolCall | undefined
2895
2963
  const FfiConverterOptionalTypePendingToolCall = new FfiConverterOptional(FfiConverterTypePendingToolCall);
2896
2964
 
@@ -2967,7 +3035,7 @@ function uniffiEnsureInitialized() {
2967
3035
  if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_func_cosine_similarity() !== 63439) {
2968
3036
  throw new UniffiInternalError.ApiChecksumMismatch("uniffi_nobodywho_uniffi_checksum_func_cosine_similarity");
2969
3037
  }
2970
- if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_func_load_model() !== 63144) {
3038
+ if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_func_load_model() !== 33587) {
2971
3039
  throw new UniffiInternalError.ApiChecksumMismatch("uniffi_nobodywho_uniffi_checksum_func_load_model");
2972
3040
  }
2973
3041
  if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_func_sampler_preset_default() !== 10834) {
@@ -3123,10 +3191,14 @@ function uniffiEnsureInitialized() {
3123
3191
  if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json() !== 6867) {
3124
3192
  throw new UniffiInternalError.ApiChecksumMismatch("uniffi_nobodywho_uniffi_checksum_constructor_samplerconfig_from_json");
3125
3193
  }
3194
+ if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress() !== 28617) {
3195
+ throw new UniffiInternalError.ApiChecksumMismatch("uniffi_nobodywho_uniffi_checksum_method_rustdownloadprogresscallback_on_download_progress");
3196
+ }
3126
3197
  if (nativeModule().ubrn_uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call() !== 43958) {
3127
3198
  throw new UniffiInternalError.ApiChecksumMismatch("uniffi_nobodywho_uniffi_checksum_method_rusttoolcallback_call");
3128
3199
  }
3129
3200
 
3201
+ uniffiCallbackInterfaceRustDownloadProgressCallback.register();
3130
3202
  uniffiCallbackInterfaceRustToolCallback.register();
3131
3203
  }
3132
3204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nobodywho",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Run LLMs locally with offline inference — React Native bindings",
5
5
  "main": "src/wrapper.ts",
6
6
  "types": "src/wrapper.ts",
package/src/chat.ts CHANGED
@@ -69,11 +69,13 @@ export class Chat {
69
69
  templateVariables?: Record<string, boolean>;
70
70
  tools?: Tool[];
71
71
  sampler?: SamplerConfig;
72
+ onDownloadProgress?: (downloaded: number, total: number) => void;
72
73
  }): Promise<Chat> {
73
74
  const model = await Model.load({
74
75
  modelPath: opts.modelPath,
75
76
  useGpu: opts.useGpu,
76
77
  projectionModelPath: opts.projectionModelPath,
78
+ onDownloadProgress: opts.onDownloadProgress,
77
79
  });
78
80
  return new Chat({ model, ...opts });
79
81
  }
@@ -35,8 +35,13 @@ export class CrossEncoder {
35
35
  modelPath: string;
36
36
  useGpu?: boolean;
37
37
  contextSize?: number;
38
+ onDownloadProgress?: (downloaded: number, total: number) => void;
38
39
  }): Promise<CrossEncoder> {
39
- const model = await Model.load({ modelPath: opts.modelPath, useGpu: opts.useGpu });
40
+ const model = await Model.load({
41
+ modelPath: opts.modelPath,
42
+ useGpu: opts.useGpu,
43
+ onDownloadProgress: opts.onDownloadProgress,
44
+ });
40
45
  return new CrossEncoder({ model, ...opts });
41
46
  }
42
47
 
package/src/encoder.ts CHANGED
@@ -31,8 +31,13 @@ export class Encoder {
31
31
  modelPath: string;
32
32
  useGpu?: boolean;
33
33
  contextSize?: number;
34
+ onDownloadProgress?: (downloaded: number, total: number) => void;
34
35
  }): Promise<Encoder> {
35
- const model = await Model.load({ modelPath: opts.modelPath, useGpu: opts.useGpu });
36
+ const model = await Model.load({
37
+ modelPath: opts.modelPath,
38
+ useGpu: opts.useGpu,
39
+ onDownloadProgress: opts.onDownloadProgress,
40
+ });
36
41
  return new Encoder({ model, ...opts });
37
42
  }
38
43
 
package/src/model.ts CHANGED
@@ -39,16 +39,23 @@ export class Model {
39
39
  * @param opts.modelPath - Path to the GGUF model file, or a `hf://owner/repo/file.gguf` / `https://` URL to download and cache automatically
40
40
  * @param opts.useGpu - Use GPU acceleration if available (default: true)
41
41
  * @param opts.projectionModelPath - Path to a multimodal projector file for vision models
42
+ * @param opts.onDownloadProgress - Invoked with `(downloaded, total)` byte counts while a remote model is being downloaded. Throttled to ~10 Hz with a guaranteed final emit on completion. Not invoked for cached/local files.
42
43
  */
43
44
  static async load(opts: {
44
45
  modelPath: string;
45
46
  useGpu?: boolean;
46
47
  projectionModelPath?: string;
48
+ onDownloadProgress?: (downloaded: number, total: number) => void;
47
49
  }): Promise<Model> {
50
+ const cb = opts.onDownloadProgress;
51
+ const progressCallback = cb
52
+ ? { onDownloadProgress: (d: bigint, t: bigint) => cb(Number(d), Number(t)) }
53
+ : undefined;
48
54
  const inner = await loadModel(
49
55
  opts.modelPath,
50
56
  opts.useGpu ?? true,
51
57
  opts.projectionModelPath,
58
+ progressCallback,
52
59
  );
53
60
  return new Model(inner);
54
61
  }