@micrantha/react-native-amaryllis 0.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.
Files changed (56) hide show
  1. package/Amaryllis.podspec +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +213 -0
  4. package/android/build.gradle +81 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/micrantha/amaryllis/Amaryllis.kt +198 -0
  8. package/android/src/main/java/com/micrantha/amaryllis/AmaryllisModule.kt +165 -0
  9. package/android/src/main/java/com/micrantha/amaryllis/AmaryllisPackage.kt +32 -0
  10. package/ios/Amaryllis.h +49 -0
  11. package/ios/Amaryllis.m +201 -0
  12. package/ios/AmaryllisModule.h +6 -0
  13. package/ios/AmaryllisModule.mm +166 -0
  14. package/lib/module/Amaryllis.js +56 -0
  15. package/lib/module/Amaryllis.js.map +1 -0
  16. package/lib/module/AmaryllisContext.js +56 -0
  17. package/lib/module/AmaryllisContext.js.map +1 -0
  18. package/lib/module/AmaryllisHooks.js +78 -0
  19. package/lib/module/AmaryllisHooks.js.map +1 -0
  20. package/lib/module/AmaryllisRx.js +31 -0
  21. package/lib/module/AmaryllisRx.js.map +1 -0
  22. package/lib/module/NativeAmaryllis.js +5 -0
  23. package/lib/module/NativeAmaryllis.js.map +1 -0
  24. package/lib/module/NativePipe.js +9 -0
  25. package/lib/module/NativePipe.js.map +1 -0
  26. package/lib/module/Types.js +4 -0
  27. package/lib/module/Types.js.map +1 -0
  28. package/lib/module/index.js +7 -0
  29. package/lib/module/index.js.map +1 -0
  30. package/lib/module/package.json +1 -0
  31. package/lib/typescript/package.json +1 -0
  32. package/lib/typescript/src/Amaryllis.d.ts +16 -0
  33. package/lib/typescript/src/Amaryllis.d.ts.map +1 -0
  34. package/lib/typescript/src/AmaryllisContext.d.ts +8 -0
  35. package/lib/typescript/src/AmaryllisContext.d.ts.map +1 -0
  36. package/lib/typescript/src/AmaryllisHooks.d.ts +4 -0
  37. package/lib/typescript/src/AmaryllisHooks.d.ts.map +1 -0
  38. package/lib/typescript/src/AmaryllisRx.d.ts +3 -0
  39. package/lib/typescript/src/AmaryllisRx.d.ts.map +1 -0
  40. package/lib/typescript/src/NativeAmaryllis.d.ts +12 -0
  41. package/lib/typescript/src/NativeAmaryllis.d.ts.map +1 -0
  42. package/lib/typescript/src/NativePipe.d.ts +3 -0
  43. package/lib/typescript/src/NativePipe.d.ts.map +1 -0
  44. package/lib/typescript/src/Types.d.ts +87 -0
  45. package/lib/typescript/src/Types.d.ts.map +1 -0
  46. package/lib/typescript/src/index.d.ts +6 -0
  47. package/lib/typescript/src/index.d.ts.map +1 -0
  48. package/package.json +185 -0
  49. package/src/Amaryllis.ts +91 -0
  50. package/src/AmaryllisContext.tsx +53 -0
  51. package/src/AmaryllisHooks.tsx +78 -0
  52. package/src/AmaryllisRx.ts +24 -0
  53. package/src/NativeAmaryllis.ts +18 -0
  54. package/src/NativePipe.ts +8 -0
  55. package/src/Types.ts +115 -0
  56. package/src/index.tsx +5 -0
@@ -0,0 +1,165 @@
1
+ package com.micrantha.amaryllis
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import com.facebook.react.bridge.ReadableMap
8
+ import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.module.annotations.ReactModule
10
+ import com.facebook.react.modules.core.DeviceEventManagerModule
11
+
12
+ @ReactModule(name = AmaryllisModule.NAME)
13
+ class AmaryllisModule(reactContext: ReactApplicationContext) :
14
+ NativeAmaryllisSpec(reactContext) {
15
+
16
+ private val amaryllis = Amaryllis()
17
+
18
+ override fun getName() = NAME
19
+
20
+ @ReactMethod
21
+ override fun init(config: ReadableMap, promise: Promise): Unit = try {
22
+ amaryllis.init(reactApplicationContext.applicationContext, config)
23
+ promise.resolve(null)
24
+ } catch (e: Amaryllis.InvalidModelPathException) {
25
+ promise.reject(ERROR_CODE_INFER, "invalid model path", e)
26
+ } catch (e: Throwable) {
27
+ promise.reject(ERROR_CODE_INFER, "unable to configure", e)
28
+ }
29
+
30
+ @ReactMethod
31
+ override fun newSession(params: ReadableMap?, promise: Promise) {
32
+ try {
33
+ amaryllis.newSession(params)
34
+ } catch (e: Amaryllis.NotInitializedException) {
35
+ Log.e(NAME, "sdk is not initialized", e)
36
+ promise.reject(ERROR_CODE_INFER, "sdk is not initialized", e)
37
+ } catch (e: Throwable) {
38
+ Log.e(NAME, "unable to initialize session", e)
39
+ promise.reject(ERROR_CODE_SESSION, "please initialize the sdk first", e)
40
+ }
41
+ }
42
+
43
+ @ReactMethod
44
+ override fun generate(params: ReadableMap, promise: Promise) {
45
+ try {
46
+ val result = amaryllis.generate(params)
47
+ promise.resolve(result)
48
+ } catch (e: Amaryllis.SessionRequiredException) {
49
+ Log.e(NAME, "session is required", e)
50
+ promise.reject(ERROR_CODE_SESSION, "session is required", e)
51
+ } catch (e: Amaryllis.NotInitializedException) {
52
+ Log.e(NAME, "sdk is not initialized", e)
53
+ promise.reject(ERROR_CODE_INFER, "sdk is not initialized", e)
54
+ } catch (e: Throwable) {
55
+ Log.e(NAME, "unable to generate response", e)
56
+ promise.reject(ERROR_CODE_INFER, "unable to generate response", e)
57
+ }
58
+ }
59
+
60
+ @ReactMethod
61
+ override fun generateAsync(params: ReadableMap, promise: Promise) {
62
+ try {
63
+ amaryllis.generateAsync(params) { partialResult, done ->
64
+ if (done) {
65
+ sendEvent(EVENT_ON_FINAL_RESULT, partialResult ?: "")
66
+ } else {
67
+ sendEvent(EVENT_ON_PARTIAL_RESULT, partialResult ?: "")
68
+ }
69
+ }
70
+ promise.resolve(null)
71
+ } catch (e: Amaryllis.SessionRequiredException) {
72
+ Log.e(NAME, "session is required", e)
73
+ promise.reject(ERROR_CODE_SESSION, "session is required", e)
74
+ } catch (e: Amaryllis.NotInitializedException) {
75
+ Log.e(NAME, "sdk is not initialized", e)
76
+ promise.reject(ERROR_CODE_INFER, "sdk is not initialized", e)
77
+ } catch (e: Throwable) {
78
+ Log.e(NAME, "unable to generate response", e)
79
+ sendEvent(EVENT_ON_ERROR, "unable to generate response")
80
+ promise.reject(ERROR_CODE_INFER, "unable to generate response", e)
81
+ }
82
+ }
83
+
84
+ @ReactMethod
85
+ override fun close() {
86
+ Log.d(NAME, "closing")
87
+ amaryllis.close()
88
+ }
89
+
90
+ @ReactMethod
91
+ override fun cancelAsync() {
92
+ amaryllis.cancelAsync()
93
+ }
94
+
95
+ @Override
96
+ fun addListener(eventName: String) {
97
+ // No-op
98
+ }
99
+
100
+ @Override
101
+ fun removeListeners(count: Int) {
102
+ // No-op
103
+ }
104
+
105
+ private fun sendEvent(event: String, data: String) {
106
+ Log.d(NAME, "sending event $event")
107
+ reactApplicationContext
108
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
109
+ .emit(event, data)
110
+ }
111
+
112
+ override fun getConstants() = mapOf(
113
+ // events
114
+ "EVENT_ON_PARTIAL_RESULT" to EVENT_ON_PARTIAL_RESULT,
115
+ "EVENT_ON_FINAL_RESULT" to EVENT_ON_FINAL_RESULT,
116
+ "EVENT_ON_ERROR" to EVENT_ON_ERROR,
117
+ // errors
118
+ "ERROR_CODE_INFER" to ERROR_CODE_INFER,
119
+ "ERROR_CODE_SESSION" to ERROR_CODE_SESSION,
120
+ // params
121
+ "PARAM_IMAGES" to PARAM_IMAGES,
122
+ "PARAM_PROMPT" to PARAM_PROMPT,
123
+ "PARAM_MAX_TOP_K" to PARAM_MAX_TOP_K,
124
+ "PARAM_MAX_TOKENS" to PARAM_MAX_TOKENS,
125
+ "PARAM_MAX_NUM_IMAGES" to PARAM_MAX_NUM_IMAGES,
126
+ "PARAM_VISION_ENCODER" to PARAM_VISION_ENCODER,
127
+ "PARAM_VISION_ADAPTER" to PARAM_VISION_ADAPTER,
128
+ "PARAM_MODEL_PATH" to PARAM_MODEL_PATH,
129
+ "PARAM_TEMPERATURE" to PARAM_TEMPERATURE,
130
+ "PARAM_RANDOM_SEED" to PARAM_RANDOM_SEED,
131
+ "PARAM_LORA_PATH" to PARAM_LORA_PATH,
132
+ "PARAM_TOP_K" to PARAM_TOP_K,
133
+ "PARAM_TOP_P" to PARAM_TOP_P,
134
+ "PARAM_ENABLE_VISION" to PARAM_ENABLE_VISION
135
+ )
136
+
137
+ companion object {
138
+ const val NAME = "Amaryllis"
139
+
140
+ // Events
141
+ const val EVENT_ON_PARTIAL_RESULT = "onPartialResult"
142
+ const val EVENT_ON_FINAL_RESULT = "onFinalResult"
143
+ const val EVENT_ON_ERROR = "onError"
144
+
145
+ // Errors
146
+ const val ERROR_CODE_INFER = "ERR_INFER"
147
+ const val ERROR_CODE_SESSION = "ERR_SESSION"
148
+
149
+ // Params
150
+ const val PARAM_IMAGES = "images"
151
+ const val PARAM_PROMPT = "prompt"
152
+ const val PARAM_MAX_TOP_K = "maxTopK"
153
+ const val PARAM_MAX_TOKENS = "maxTokens"
154
+ const val PARAM_MAX_NUM_IMAGES = "maxNumImages"
155
+ const val PARAM_VISION_ENCODER = "visionEncoderPath"
156
+ const val PARAM_VISION_ADAPTER = "visionAdapterPath"
157
+ const val PARAM_MODEL_PATH = "modelPath"
158
+ const val PARAM_TEMPERATURE = "temperature"
159
+ const val PARAM_RANDOM_SEED = "randomSeed"
160
+ const val PARAM_LORA_PATH = "loraPath"
161
+ const val PARAM_TOP_K = "topK"
162
+ const val PARAM_TOP_P = "topP"
163
+ const val PARAM_ENABLE_VISION = "enableVisionModality"
164
+ }
165
+ }
@@ -0,0 +1,32 @@
1
+ package com.micrantha.amaryllis
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class AmaryllisPackage : BaseReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
11
+ return if (name == AmaryllisModule.NAME) {
12
+ AmaryllisModule(reactContext)
13
+ } else {
14
+ null
15
+ }
16
+ }
17
+
18
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
19
+ return ReactModuleInfoProvider {
20
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
21
+ moduleInfos[AmaryllisModule.NAME] = ReactModuleInfo(
22
+ name = AmaryllisModule.NAME,
23
+ className = AmaryllisModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ moduleInfos
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,49 @@
1
+ //
2
+ // Header.h
3
+ // Pods
4
+ //
5
+ // Created by Ryan Jennings on 2025-09-05.
6
+ //
7
+
8
+ #ifndef Amaryllis_h
9
+ #define Amaryllis_h
10
+
11
+ typedef void (^PartialResponseHandler)(NSString *_Nullable partialResponse,
12
+ NSError *_Nullable error);
13
+ typedef void (^CompletionHandler)(void);
14
+
15
+ @interface Amaryllis : NSObject
16
+
17
+ - (void) initWithParams: (NSDictionary *_Nonnull) params error: (NSError *_Nullable*_Nullable) error;
18
+ - (void) newSessionWithParams: (NSDictionary *_Nonnull) params error: (NSError *_Nullable*_Nullable) error;
19
+ - (NSString *_Nullable) generateWithParams: (NSDictionary *_Nonnull) params error: (NSError *_Nullable*_Nullable) error;
20
+ - (void) generateAsyncWithParams: (NSDictionary *_Nonnull) params error: (NSError *_Nullable*_Nullable) error
21
+ response: (PartialResponseHandler _Nullable ) progress completion: (CompletionHandler _Nullable ) completion;
22
+ - (void) close;
23
+ @end
24
+
25
+ extern NSString *_Nonnull const AmaryllisErrorDomain;
26
+
27
+ extern NSString *_Nonnull const PARAM_IMAGES;
28
+ extern NSString *_Nonnull const PARAM_PROMPT;
29
+ extern NSString *_Nonnull const PARAM_MAX_TOP_K;
30
+ extern NSString *_Nonnull const PARAM_MAX_TOKENS;
31
+ extern NSString *_Nonnull const PARAM_MAX_NUM_IMAGES;
32
+ extern NSString *_Nonnull const PARAM_VISION_ENCODER;
33
+ extern NSString *_Nonnull const PARAM_VISION_ADAPTER;
34
+ extern NSString *_Nonnull const PARAM_MODEL_PATH;
35
+ extern NSString *_Nonnull const PARAM_TEMPERATURE;
36
+ extern NSString *_Nonnull const PARAM_RANDOM_SEED;
37
+ extern NSString *_Nonnull const PARAM_LORA_PATH;
38
+ extern NSString *_Nonnull const PARAM_TOP_K;
39
+ extern NSString *_Nonnull const PARAM_TOP_P;
40
+ extern NSString *_Nonnull const PARAM_ENABLE_VISION;
41
+
42
+ typedef NS_ENUM(NSInteger, AmaryllisErrorCode) {
43
+ AmaryllisErrorCodeInvalidModelPath = 1001,
44
+ AmaryllisErrorCodeNotInitialized = 1002,
45
+ AmaryllisErrorCodeNoSession = 1003,
46
+ AmaryllisInvalidArgument = 1004
47
+ };
48
+
49
+ #endif /* Header_h */
@@ -0,0 +1,201 @@
1
+ //
2
+ // Amaryllis.m
3
+ // Amaryllis
4
+ //
5
+ // Created by Ryan Jennings on 2025-09-05.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+ #import <MediaPipeTasksGenAI/MediaPipeTasksGenAI.h>
10
+ #import "Amaryllis.h"
11
+
12
+ NSString *const PARAM_IMAGES = @"images";
13
+ NSString *const PARAM_PROMPT = @"prompt";
14
+ NSString *const PARAM_MAX_TOP_K = @"maxTopK";
15
+ NSString *const PARAM_MAX_TOKENS = @"maxTokens";
16
+ NSString *const PARAM_MAX_NUM_IMAGES = @"maxNumImages";
17
+ NSString *const PARAM_VISION_ENCODER = @"visionEncoderPath";
18
+ NSString *const PARAM_VISION_ADAPTER = @"visionAdapterPath";
19
+ NSString *const PARAM_MODEL_PATH = @"modelPath";
20
+ NSString *const PARAM_TEMPERATURE = @"temperature";
21
+ NSString *const PARAM_RANDOM_SEED = @"randomSeed";
22
+ NSString *const PARAM_LORA_PATH = @"loraPath";
23
+ NSString *const PARAM_TOP_K = @"topK";
24
+ NSString *const PARAM_TOP_P = @"topP";
25
+ NSString *const PARAM_ENABLE_VISION = @"enableVisionModality";
26
+
27
+ NSString *const AmaryllisErrorDomain = @"com.example.amaryllis";
28
+
29
+ static NSString *const ERR_NOT_INITIALIZED = @"please initialize the SDK first";
30
+ static NSString *const ERR_INVALID_ARGUMENT = @"invalid argument provided";
31
+ static NSString *const ERR_NO_SESSION = @"new session required";
32
+
33
+ @interface Amaryllis ()
34
+
35
+ @property(nonatomic, strong) MPPLLMInference *llmInference;
36
+ @property(nonatomic, strong) MPPLLMInferenceSession *session;
37
+
38
+ @end
39
+
40
+ @implementation Amaryllis
41
+
42
+ - (void) initWithParams: (NSDictionary*) params error: (NSError**) error {
43
+
44
+ NSString *modelPath = params[PARAM_MODEL_PATH];
45
+
46
+ MPPLLMInferenceOptions *taskOptions = [[MPPLLMInferenceOptions alloc]
47
+ initWithModelPath:modelPath];
48
+ taskOptions.maxTopk = [params[PARAM_MAX_TOP_K] intValue];
49
+ taskOptions.maxTokens = [params[PARAM_MAX_TOKENS] intValue];
50
+ taskOptions.maxImages = [params[PARAM_MAX_NUM_IMAGES] intValue];
51
+ taskOptions.visionAdapterPath = params[PARAM_VISION_ADAPTER];
52
+ taskOptions.visionEncoderPath = params[PARAM_VISION_ENCODER];
53
+
54
+ NSLog(@"Initializing llm inference");
55
+
56
+ self.llmInference = [[MPPLLMInference alloc] initWithOptions:taskOptions
57
+ error:error];
58
+ }
59
+
60
+ - (void) newSessionWithParams: (NSDictionary *) params error: (NSError **) error {
61
+ if (![self validateInitialized:error]) {
62
+ return;
63
+ }
64
+
65
+ MPPLLMInferenceSessionOptions *sessionOptions =
66
+ [[MPPLLMInferenceSessionOptions alloc] init];
67
+ sessionOptions.topk = [params[PARAM_TOP_K] intValue];
68
+ sessionOptions.topp = [params[PARAM_TOP_P] floatValue];
69
+ sessionOptions.temperature = [params[PARAM_TEMPERATURE] floatValue];
70
+ sessionOptions.loraPath = params[PARAM_LORA_PATH];
71
+ sessionOptions.randomSeed = [params[PARAM_RANDOM_SEED] intValue];
72
+ sessionOptions.enableVisionModality =
73
+ [params[PARAM_ENABLE_VISION] boolValue];
74
+
75
+ NSLog(@"starting new session");
76
+
77
+ self.session =
78
+ [[MPPLLMInferenceSession alloc] initWithLlmInference:self.llmInference
79
+ options:sessionOptions
80
+ error:error];
81
+ }
82
+
83
+ - (NSString *) generateWithParams: (NSDictionary *) params error: (NSError **) error {
84
+
85
+ if (![self validateInitialized:error]) {
86
+ return nil;
87
+ }
88
+
89
+ if (self.session) {
90
+ if (![self updateSessionFromParams:params error:error]) {
91
+ return nil;
92
+ }
93
+
94
+ return [self.session generateResponseAndReturnError:error];
95
+ }
96
+
97
+ if (![self validateNoSession:params error:error]) {
98
+ return nil;
99
+ }
100
+
101
+ return [self.llmInference generateResponseWithInputText:params[PARAM_PROMPT] error:error];
102
+ }
103
+
104
+ - (void) generateAsyncWithParams: (NSDictionary *) params error: (NSError **) error response: (PartialResponseHandler) progress completion: (CompletionHandler) completion {
105
+
106
+ if (![self validateInitialized:error]) {
107
+ return;
108
+ }
109
+
110
+ if (self.session) {
111
+
112
+ if (![self updateSessionFromParams:params error:error]) {
113
+ return;
114
+ }
115
+
116
+ [self.session generateResponseAsyncAndReturnError:error
117
+ progress:progress
118
+ completion:completion];
119
+
120
+ } else {
121
+
122
+ if (![self validateNoSession:params error:error]) {
123
+ return;
124
+ }
125
+
126
+ [self.llmInference generateResponseAsyncWithInputText:params[PARAM_PROMPT]
127
+ error:error
128
+ progress:progress
129
+ completion:completion];
130
+ }
131
+ }
132
+
133
+ - (void) close {
134
+ self.session = nil;
135
+ self.llmInference = nil;
136
+ }
137
+
138
+ #pragma mark - Helpers
139
+
140
+ - (BOOL)updateSessionFromParams:(NSDictionary *)params error: (NSError **) error {
141
+
142
+ NSString *prompt = params ? params[PARAM_PROMPT] : nil;
143
+ NSArray *images = params ? params[PARAM_IMAGES] : nil;
144
+
145
+ if (!prompt && !images) {
146
+ *error = [NSError errorWithDomain:AmaryllisErrorDomain code:AmaryllisInvalidArgument userInfo:@{NSLocalizedDescriptionKey: ERR_INVALID_ARGUMENT}];
147
+ return NO;
148
+ }
149
+
150
+ [self.session addQueryChunkWithInputText:prompt error:error];
151
+
152
+ if (error) return NO;
153
+
154
+ if (images) {
155
+ for (NSString *path in images) {
156
+ CGImageRef image = [self imageFromPath:path];
157
+ [self.session addImageWithImage:image error:error];
158
+ CGImageRelease(image);
159
+ if (error) {
160
+ return NO;
161
+ }
162
+ }
163
+ }
164
+ return YES;
165
+ }
166
+
167
+ - (BOOL)validateNoSession:(NSDictionary *)params
168
+ error: (NSError **) error {
169
+ if (params && params[PARAM_IMAGES]) {
170
+ *error = [NSError errorWithDomain:AmaryllisErrorDomain code:AmaryllisErrorCodeNoSession userInfo:@{NSLocalizedDescriptionKey: ERR_NO_SESSION}];
171
+ return NO;
172
+ }
173
+ return YES;
174
+ }
175
+
176
+ - (CGImageRef)imageFromPath:(NSString *)path {
177
+ UIImage *uiImage = [UIImage imageWithContentsOfFile:path];
178
+ if (!uiImage)
179
+ return nil;
180
+
181
+ // Resize to 512x512 if necessary
182
+ CGSize targetSize = CGSizeMake(512, 512);
183
+ UIGraphicsBeginImageContextWithOptions(targetSize, NO, 1.0);
184
+ [uiImage drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];
185
+ UIImage *resized = UIGraphicsGetImageFromCurrentImageContext();
186
+ UIGraphicsEndImageContext();
187
+
188
+ // Convert to MLImage
189
+ return CGImageRetain(resized.CGImage);
190
+ }
191
+
192
+ - (BOOL) validateInitialized: (NSError**) error {
193
+ if (self.llmInference == nil) {
194
+ *error = [NSError errorWithDomain:AmaryllisErrorDomain
195
+ code:AmaryllisErrorCodeNotInitialized
196
+ userInfo:@{NSLocalizedDescriptionKey: ERR_NOT_INITIALIZED}];
197
+ return NO;
198
+ }
199
+ return YES;
200
+ }
201
+ @end
@@ -0,0 +1,6 @@
1
+ #import <AmaryllisSpec/AmaryllisSpec.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface AmaryllisModule : RCTEventEmitter <NativeAmaryllisSpec>
5
+
6
+ @end
@@ -0,0 +1,166 @@
1
+ #import "AmaryllisModule.h"
2
+ #import "Amaryllis.h"
3
+ #import <ReactCommon/RCTTurboModule.h>
4
+
5
+ static NSString *const EVENT_ON_PARTIAL_RESULT = @"onPartialResult";
6
+ static NSString *const EVENT_ON_FINAL_RESULT = @"onFinalResult";
7
+ static NSString *const EVENT_ON_ERROR = @"onError";
8
+ static NSString *const ERROR_CODE_INFER = @"ERR_INFER";
9
+ static NSString *const ERROR_CODE_SESSION = @"ERR_SESSION";
10
+
11
+ @interface AmaryllisModule ()
12
+
13
+ @property(nonatomic, strong) Amaryllis *amaryllis;
14
+
15
+ @end
16
+
17
+ @implementation AmaryllisModule
18
+
19
+ RCT_EXPORT_MODULE(Amaryllis)
20
+
21
+ - (instancetype) init {
22
+ self = [super init];
23
+ self.amaryllis = [[Amaryllis alloc] init];
24
+ return self;
25
+ }
26
+
27
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
28
+ (const facebook::react::ObjCTurboModule::InitParams &)params {
29
+ return std::make_shared<facebook::react::NativeAmaryllisSpecJSI>(params);
30
+ }
31
+ #pragma mark - Event Emitter
32
+
33
+ - (NSArray *)supportedEvents {
34
+ return @[ EVENT_ON_PARTIAL_RESULT, EVENT_ON_FINAL_RESULT, EVENT_ON_ERROR ];
35
+ }
36
+
37
+ #pragma mark - Configure Engine
38
+
39
+ - (void)init:(nonnull NSDictionary *)config
40
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
41
+ reject:(nonnull RCTPromiseRejectBlock)reject {
42
+ NSError *error = nil;
43
+
44
+ @try {
45
+
46
+ [self.amaryllis initWithParams:config error:&error];
47
+
48
+ if (error) {
49
+ reject(ERROR_CODE_INFER, @"unable to initialize inference", error);
50
+ return;
51
+ }
52
+
53
+ resolve(nil);
54
+ } @catch (NSException *exception) {
55
+ NSLog(@"Amaryllis: error initializing (%@)", exception.description);
56
+ reject(ERROR_CODE_INFER, @"unable to configure", nil);
57
+ }
58
+ }
59
+
60
+ - (void)newSession:(NSDictionary *)params
61
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
62
+ reject:(nonnull RCTPromiseRejectBlock)reject {
63
+ NSError *error = nil;
64
+
65
+ @try {
66
+
67
+ [self.amaryllis newSessionWithParams: params error: &error];
68
+
69
+ if (error) {
70
+ reject(ERROR_CODE_INFER, @"unable to initialize inference", error);
71
+ return;
72
+ }
73
+
74
+ resolve(nil);
75
+ } @catch (NSException *exception) {
76
+ NSLog(@"Amaryllis: error create new session (%@)", exception.description);
77
+ [self sendEventWithName:EVENT_ON_ERROR body:nil];
78
+ reject(ERROR_CODE_SESSION, @"unable to create session", nil);
79
+ }
80
+ }
81
+
82
+ #pragma mark - Generate Sync
83
+
84
+ - (void)generate:(nonnull NSDictionary *)params
85
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
86
+ reject:(nonnull RCTPromiseRejectBlock)reject {
87
+ @try {
88
+ NSError *error = nil;
89
+ NSString *result = [self.amaryllis generateWithParams: params error:&error];
90
+
91
+ if (error) {
92
+ reject(ERROR_CODE_INFER, @"unable to generate response", error);
93
+ return;
94
+ }
95
+ resolve(result);
96
+ } @catch (NSException *exception) {
97
+ NSLog(@"Amaryllis: error generating inference (%@)", exception.description);
98
+ reject(ERROR_CODE_INFER, @"unable to generate response", nil);
99
+ }
100
+ }
101
+
102
+ #pragma mark - Generate Async
103
+
104
+ - (void)generateAsync:(nonnull NSDictionary *)params
105
+ resolve:(nonnull RCTPromiseResolveBlock)resolve
106
+ reject:(nonnull RCTPromiseRejectBlock)reject {
107
+ @try {
108
+ NSError *error = nil;
109
+
110
+ PartialResponseHandler progress = ^(NSString *result, NSError *err) {
111
+ if (!err) {
112
+ [self sendEventWithName:EVENT_ON_PARTIAL_RESULT body:result];
113
+ } else {
114
+ [self sendEventWithName:EVENT_ON_ERROR body:err];
115
+ }
116
+ };
117
+
118
+ CompletionHandler completion = ^{
119
+ [self sendEventWithName:EVENT_ON_FINAL_RESULT body:nil];
120
+ };
121
+
122
+ [self.amaryllis generateAsyncWithParams:params error:&error response:progress completion:completion];
123
+
124
+ resolve(nil);
125
+ } @catch (NSException *exception) {
126
+ NSLog(@"Amaryllis: error generating inference (%@)", exception.description);
127
+ [self sendEventWithName:EVENT_ON_ERROR body:nil];
128
+ reject(ERROR_CODE_INFER, @"unable to generate response", nil);
129
+ }
130
+ }
131
+
132
+ #pragma mark - Close Engine
133
+
134
+ - (void) close {
135
+ [self.amaryllis close];
136
+ }
137
+
138
+ - (void) cancelAsync {}
139
+
140
+ - (NSDictionary *)constantsToExport {
141
+ return @{
142
+ @"EVENT_ON_PARTIAL_RESULT" : EVENT_ON_PARTIAL_RESULT,
143
+ @"EVENT_ON_FINAL_RESULT" : EVENT_ON_FINAL_RESULT,
144
+ @"EVENT_ON_ERROR" : EVENT_ON_ERROR,
145
+ // errors
146
+ @"ERROR_CODE_INFER" : ERROR_CODE_INFER,
147
+ @"ERROR_CODE_SESSION" : ERROR_CODE_SESSION,
148
+ // params
149
+ @"PARAM_IMAGES" : PARAM_IMAGES,
150
+ @"PARAM_PROMPT" : PARAM_PROMPT,
151
+ @"PARAM_MAX_TOP_K" : PARAM_MAX_TOP_K,
152
+ @"PARAM_MAX_TOKENS" : PARAM_MAX_TOKENS,
153
+ @"PARAM_MAX_NUM_IMAGES" : PARAM_MAX_NUM_IMAGES,
154
+ @"PARAM_VISION_ENCODER" : PARAM_VISION_ENCODER,
155
+ @"PARAM_VISION_ADAPTER" : PARAM_VISION_ADAPTER,
156
+ @"PARAM_MODEL_PATH" : PARAM_MODEL_PATH,
157
+ @"PARAM_TEMPERATURE" : PARAM_TEMPERATURE,
158
+ @"PARAM_RANDOM_SEED" : PARAM_RANDOM_SEED,
159
+ @"PARAM_LORA_PATH" : PARAM_LORA_PATH,
160
+ @"PARAM_TOP_K" : PARAM_TOP_K,
161
+ @"PARAM_TOP_P" : PARAM_TOP_P,
162
+ @"PARAM_ENABLE_VISION" : PARAM_ENABLE_VISION,
163
+ };
164
+ }
165
+
166
+ @end
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ const EVENT_ON_PARTIAL_RESULT = 'onPartialResult';
4
+ const EVENT_ON_FINAL_RESULT = 'onFinalResult';
5
+ const EVENT_ON_ERROR = 'onError';
6
+ export class LlmPipe {
7
+ subscriptions = [];
8
+ constructor(params) {
9
+ this.llmNative = params.nativeModule;
10
+ this.llmEmitter = params.eventEmitter;
11
+ }
12
+ async init(params) {
13
+ await this.llmNative.init(params);
14
+ }
15
+ newSession(params) {
16
+ return this.llmNative.newSession(params);
17
+ }
18
+ async generate(params) {
19
+ return await this.llmNative.generate(params);
20
+ }
21
+ async generateAsync(params, callbacks) {
22
+ if (callbacks) {
23
+ this.setupAsyncCallbacks(callbacks);
24
+ }
25
+ return await this.llmNative.generateAsync(params);
26
+ }
27
+ close() {
28
+ this.llmNative.close();
29
+ this.cancelAsync();
30
+ }
31
+ cancelAsync() {
32
+ this.llmNative.cancelAsync();
33
+ this.subscriptions.forEach(sub => sub.remove());
34
+ }
35
+ setupAsyncCallbacks(callbacks) {
36
+ if (callbacks.onPartialResult) {
37
+ this.subscriptions.push(this.llmEmitter.addListener(EVENT_ON_PARTIAL_RESULT, result => {
38
+ callbacks.onPartialResult?.(result);
39
+ }));
40
+ }
41
+ if (callbacks.onFinalResult) {
42
+ this.subscriptions.push(this.llmEmitter.addListener(EVENT_ON_FINAL_RESULT, result => {
43
+ callbacks.onFinalResult?.(result);
44
+ this.cancelAsync();
45
+ }));
46
+ }
47
+ if (callbacks.onError) {
48
+ this.subscriptions.push(this.llmEmitter.addListener(EVENT_ON_ERROR, error => {
49
+ callbacks.onError?.(new Error(error));
50
+ this.cancelAsync();
51
+ }));
52
+ }
53
+ }
54
+ }
55
+ export default LlmPipe;
56
+ //# sourceMappingURL=Amaryllis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["EVENT_ON_PARTIAL_RESULT","EVENT_ON_FINAL_RESULT","EVENT_ON_ERROR","LlmPipe","subscriptions","constructor","params","llmNative","nativeModule","llmEmitter","eventEmitter","init","newSession","generate","generateAsync","callbacks","setupAsyncCallbacks","close","cancelAsync","forEach","sub","remove","onPartialResult","push","addListener","result","onFinalResult","onError","error","Error"],"sourceRoot":"../../src","sources":["Amaryllis.ts"],"mappings":";;AAYA,MAAMA,uBAAuB,GAAG,iBAAiB;AACjD,MAAMC,qBAAqB,GAAG,eAAe;AAC7C,MAAMC,cAAc,GAAG,SAAS;AAEhC,OAAO,MAAMC,OAAO,CAAsB;EACxCC,aAAa,GAA2B,EAAE;EAI1CC,WAAWA,CAACC,MAAqB,EAAE;IACjC,IAAI,CAACC,SAAS,GAAGD,MAAM,CAACE,YAAY;IACpC,IAAI,CAACC,UAAU,GAAGH,MAAM,CAACI,YAAY;EACvC;EAEA,MAAMC,IAAIA,CAACL,MAAuB,EAAiB;IACjD,MAAM,IAAI,CAACC,SAAS,CAACI,IAAI,CAACL,MAAM,CAAC;EACnC;EAEAM,UAAUA,CAACN,MAAwB,EAAiB;IAClD,OAAO,IAAI,CAACC,SAAS,CAACK,UAAU,CAACN,MAAM,CAAC;EAC1C;EAEA,MAAMO,QAAQA,CAACP,MAAwB,EAAmB;IACxD,OAAO,MAAM,IAAI,CAACC,SAAS,CAACM,QAAQ,CAACP,MAAM,CAAC;EAC9C;EAEA,MAAMQ,aAAaA,CACjBR,MAAwB,EACxBS,SAAwB,EACT;IACf,IAAIA,SAAS,EAAE;MACb,IAAI,CAACC,mBAAmB,CAACD,SAAS,CAAC;IACrC;IAEA,OAAO,MAAM,IAAI,CAACR,SAAS,CAACO,aAAa,CAACR,MAAM,CAAC;EACnD;EAEAW,KAAKA,CAAA,EAAS;IACZ,IAAI,CAACV,SAAS,CAACU,KAAK,CAAC,CAAC;IACtB,IAAI,CAACC,WAAW,CAAC,CAAC;EACpB;EAEAA,WAAWA,CAAA,EAAS;IAClB,IAAI,CAACX,SAAS,CAACW,WAAW,CAAC,CAAC;IAC5B,IAAI,CAACd,aAAa,CAACe,OAAO,CAAEC,GAAG,IAAKA,GAAG,CAACC,MAAM,CAAC,CAAC,CAAC;EACnD;EAEAL,mBAAmBA,CAACD,SAAuB,EAAQ;IACjD,IAAIA,SAAS,CAACO,eAAe,EAAE;MAC7B,IAAI,CAAClB,aAAa,CAACmB,IAAI,CACrB,IAAI,CAACd,UAAU,CAACe,WAAW,CACzBxB,uBAAuB,EACtByB,MAAc,IAAK;QAClBV,SAAS,CAACO,eAAe,GAAGG,MAAM,CAAC;MACrC,CACF,CACF,CAAC;IACH;IAEA,IAAIV,SAAS,CAACW,aAAa,EAAE;MAC3B,IAAI,CAACtB,aAAa,CAACmB,IAAI,CACrB,IAAI,CAACd,UAAU,CAACe,WAAW,CAACvB,qBAAqB,EAAGwB,MAAc,IAAK;QACrEV,SAAS,CAACW,aAAa,GAAGD,MAAM,CAAC;QACjC,IAAI,CAACP,WAAW,CAAC,CAAC;MACpB,CAAC,CACH,CAAC;IACH;IACA,IAAIH,SAAS,CAACY,OAAO,EAAE;MACrB,IAAI,CAACvB,aAAa,CAACmB,IAAI,CACrB,IAAI,CAACd,UAAU,CAACe,WAAW,CAACtB,cAAc,EAAG0B,KAAa,IAAK;QAC7Db,SAAS,CAACY,OAAO,GAAG,IAAIE,KAAK,CAACD,KAAK,CAAC,CAAC;QACrC,IAAI,CAACV,WAAW,CAAC,CAAC;MACpB,CAAC,CACH,CAAC;IACH;EACF;AACF;AAEA,eAAef,OAAO","ignoreList":[]}