@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.
- package/Amaryllis.podspec +22 -0
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/android/build.gradle +81 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/micrantha/amaryllis/Amaryllis.kt +198 -0
- package/android/src/main/java/com/micrantha/amaryllis/AmaryllisModule.kt +165 -0
- package/android/src/main/java/com/micrantha/amaryllis/AmaryllisPackage.kt +32 -0
- package/ios/Amaryllis.h +49 -0
- package/ios/Amaryllis.m +201 -0
- package/ios/AmaryllisModule.h +6 -0
- package/ios/AmaryllisModule.mm +166 -0
- package/lib/module/Amaryllis.js +56 -0
- package/lib/module/Amaryllis.js.map +1 -0
- package/lib/module/AmaryllisContext.js +56 -0
- package/lib/module/AmaryllisContext.js.map +1 -0
- package/lib/module/AmaryllisHooks.js +78 -0
- package/lib/module/AmaryllisHooks.js.map +1 -0
- package/lib/module/AmaryllisRx.js +31 -0
- package/lib/module/AmaryllisRx.js.map +1 -0
- package/lib/module/NativeAmaryllis.js +5 -0
- package/lib/module/NativeAmaryllis.js.map +1 -0
- package/lib/module/NativePipe.js +9 -0
- package/lib/module/NativePipe.js.map +1 -0
- package/lib/module/Types.js +4 -0
- package/lib/module/Types.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/Amaryllis.d.ts +16 -0
- package/lib/typescript/src/Amaryllis.d.ts.map +1 -0
- package/lib/typescript/src/AmaryllisContext.d.ts +8 -0
- package/lib/typescript/src/AmaryllisContext.d.ts.map +1 -0
- package/lib/typescript/src/AmaryllisHooks.d.ts +4 -0
- package/lib/typescript/src/AmaryllisHooks.d.ts.map +1 -0
- package/lib/typescript/src/AmaryllisRx.d.ts +3 -0
- package/lib/typescript/src/AmaryllisRx.d.ts.map +1 -0
- package/lib/typescript/src/NativeAmaryllis.d.ts +12 -0
- package/lib/typescript/src/NativeAmaryllis.d.ts.map +1 -0
- package/lib/typescript/src/NativePipe.d.ts +3 -0
- package/lib/typescript/src/NativePipe.d.ts.map +1 -0
- package/lib/typescript/src/Types.d.ts +87 -0
- package/lib/typescript/src/Types.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +185 -0
- package/src/Amaryllis.ts +91 -0
- package/src/AmaryllisContext.tsx +53 -0
- package/src/AmaryllisHooks.tsx +78 -0
- package/src/AmaryllisRx.ts +24 -0
- package/src/NativeAmaryllis.ts +18 -0
- package/src/NativePipe.ts +8 -0
- package/src/Types.ts +115 -0
- 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
|
+
}
|
package/ios/Amaryllis.h
ADDED
|
@@ -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 */
|
package/ios/Amaryllis.m
ADDED
|
@@ -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,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":[]}
|