cactus-react-native 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +241 -45
  2. package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
  3. package/cpp/HybridCactus.cpp +63 -51
  4. package/cpp/HybridCactus.hpp +21 -14
  5. package/cpp/HybridCactusUtil.cpp +13 -11
  6. package/cpp/HybridCactusUtil.hpp +9 -9
  7. package/cpp/cactus_ffi.h +1 -1
  8. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +1 -1
  9. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +204 -6
  10. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ffi_utils.h +150 -36
  11. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +20 -1
  12. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +21 -0
  13. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  14. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +1 -1
  15. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +204 -6
  16. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/ffi_utils.h +150 -36
  17. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +20 -1
  18. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +21 -0
  19. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
  20. package/lib/module/classes/CactusLM.js +4 -2
  21. package/lib/module/classes/CactusLM.js.map +1 -1
  22. package/lib/module/constants/packageVersion.js +1 -1
  23. package/lib/module/hooks/useCactusLM.js +11 -6
  24. package/lib/module/hooks/useCactusLM.js.map +1 -1
  25. package/lib/module/native/Cactus.js +2 -2
  26. package/lib/module/native/Cactus.js.map +1 -1
  27. package/lib/typescript/src/classes/CactusLM.d.ts +2 -1
  28. package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
  29. package/lib/typescript/src/constants/packageVersion.d.ts +1 -1
  30. package/lib/typescript/src/hooks/useCactusLM.d.ts +1 -1
  31. package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
  32. package/lib/typescript/src/native/Cactus.d.ts +1 -1
  33. package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
  34. package/lib/typescript/src/specs/Cactus.nitro.d.ts +1 -1
  35. package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
  36. package/lib/typescript/src/types/CactusLM.d.ts +3 -1
  37. package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
  38. package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +1 -1
  39. package/package.json +1 -1
  40. package/src/classes/CactusLM.ts +4 -2
  41. package/src/constants/packageVersion.ts +1 -1
  42. package/src/hooks/useCactusLM.ts +8 -5
  43. package/src/native/Cactus.ts +6 -2
  44. package/src/specs/Cactus.nitro.ts +5 -1
  45. package/src/types/CactusLM.ts +3 -1
package/README.md CHANGED
@@ -10,10 +10,78 @@
10
10
  npm install cactus-react-native react-native-nitro-modules
11
11
  ```
12
12
 
13
+ ## Quick Start
14
+
15
+ Get started with Cactus in just a few lines of code:
16
+
17
+ ```typescript
18
+ import { CactusLM, type Message } from 'cactus-react-native';
19
+
20
+ // Create a new instance
21
+ const cactusLM = new CactusLM();
22
+
23
+ // Download the model
24
+ await cactusLM.download({
25
+ onProgress: (progress) => console.log(`Download: ${Math.round(progress * 100)}%`)
26
+ });
27
+
28
+ // Generate a completion
29
+ const messages: Message[] = [
30
+ { role: 'user', content: 'What is the capital of France?' }
31
+ ];
32
+
33
+ const result = await cactusLM.complete({ messages });
34
+ console.log(result.response); // "The capital of France is Paris."
35
+
36
+ // Clean up resources
37
+ await cactusLM.destroy();
38
+ ```
39
+
40
+ **Using the React Hook:**
41
+
42
+ ```tsx
43
+ import { useCactusLM } from 'cactus-react-native';
44
+
45
+ const App = () => {
46
+ const cactusLM = useCactusLM();
47
+
48
+ useEffect(() => {
49
+ // Download the model if not already available
50
+ if (!cactusLM.isDownloaded) {
51
+ cactusLM.download();
52
+ }
53
+ }, []);
54
+
55
+ const handleGenerate = () => {
56
+ // Generate a completion
57
+ cactusLM.complete({
58
+ messages: [{ role: 'user', content: 'Hello!' }],
59
+ });
60
+ };
61
+
62
+ if (cactusLM.isDownloading) {
63
+ return (
64
+ <Text>
65
+ Downloading model: {Math.round(cactusLM.downloadProgress * 100)}%
66
+ </Text>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <>
72
+ <Button onPress={handleGenerate} title="Generate" />
73
+ <Text>{cactusLM.completion}</Text>
74
+ </>
75
+ );
76
+ };
77
+ ```
78
+
13
79
  ## Language Model
14
80
 
15
81
  ### Completion
16
82
 
83
+ Generate text responses from the model by providing a conversation history.
84
+
17
85
  #### Class
18
86
 
19
87
  ```typescript
@@ -22,9 +90,7 @@ import { CactusLM, type Message } from 'cactus-react-native';
22
90
  const cactusLM = new CactusLM();
23
91
 
24
92
  const messages: Message[] = [{ role: 'user', content: 'Hello, World!' }];
25
- const onToken = (token: string) => {
26
- console.log('Received token:', token);
27
- };
93
+ const onToken = (token: string) => { console.log('Token:', token) };
28
94
 
29
95
  const result = await cactusLM.complete({ messages, onToken });
30
96
  console.log('Completion result:', result);
@@ -54,8 +120,64 @@ const App = () => {
54
120
  };
55
121
  ```
56
122
 
123
+ ### Vision
124
+
125
+ Vision allows you to pass images along with text prompts, enabling the model to analyze and understand visual content.
126
+
127
+ #### Class
128
+
129
+ ```typescript
130
+ import { CactusLM, type Message } from 'cactus-react-native';
131
+
132
+ // Vision-capable model
133
+ const cactusLM = new CactusLM({ model: 'lfm2-vl-450m' });
134
+
135
+ const messages: Message[] = [
136
+ {
137
+ role: 'user',
138
+ content: "What's in the image?",
139
+ images: ['path/to/your/image'],
140
+ },
141
+ ];
142
+
143
+ const result = await cactusLM.complete({ messages });
144
+ console.log('Response:', result.response);
145
+ ```
146
+
147
+ #### Hook
148
+
149
+ ```tsx
150
+ import { useCactusLM, type Message } from 'cactus-react-native';
151
+
152
+ const App = () => {
153
+ // Vision-capable model
154
+ const cactusLM = useCactusLM({ model: 'lfm2-vl-450m' });
155
+
156
+ const handleAnalyze = async () => {
157
+ const messages: Message[] = [
158
+ {
159
+ role: 'user',
160
+ content: "What's in the image?",
161
+ images: ['path/to/your/image'],
162
+ },
163
+ ];
164
+
165
+ await cactusLM.complete({ messages });
166
+ };
167
+
168
+ return (
169
+ <>
170
+ <Button title="Analyze Image" onPress={handleAnalyze} />
171
+ <Text>{cactusLM.completion}</Text>
172
+ </>
173
+ );
174
+ };
175
+ ```
176
+
57
177
  ### Tool Calling
58
178
 
179
+ Enable the model to generate function calls by defining available tools and their parameters.
180
+
59
181
  #### Class
60
182
 
61
183
  ```typescript
@@ -87,11 +209,12 @@ const messages: Message[] = [
87
209
 
88
210
  const result = await cactusLM.complete({ messages, tools });
89
211
  console.log('Response:', result.response);
212
+ console.log('Function calls:', result.functionCalls);
90
213
  ```
91
214
 
92
215
  #### Hook
93
216
 
94
- ```typescript
217
+ ```tsx
95
218
  import { useCactusLM, type Message, type Tool } from 'cactus-react-native';
96
219
 
97
220
  const tools: Tool[] = [
@@ -129,8 +252,58 @@ const App = () => {
129
252
  };
130
253
  ```
131
254
 
255
+ ### RAG (Retrieval Augmented Generation)
256
+
257
+ RAG allows you to provide a corpus of documents that the model can reference during generation, enabling it to answer questions based on your data.
258
+
259
+ #### Class
260
+
261
+ ```typescript
262
+ import { CactusLM, type Message } from 'cactus-react-native';
263
+
264
+ const cactusLM = new CactusLM({
265
+ corpusDir: 'path/to/your/corpus', // Directory containing .txt files
266
+ });
267
+
268
+ const messages: Message[] = [
269
+ { role: 'user', content: 'What information is in the documents?' },
270
+ ];
271
+
272
+ const result = await cactusLM.complete({ messages });
273
+ console.log(result.response);
274
+ ```
275
+
276
+ #### Hook
277
+
278
+ ```tsx
279
+ import { useCactusLM, type Message } from 'cactus-react-native';
280
+
281
+ const App = () => {
282
+ const cactusLM = useCactusLM({
283
+ corpusDir: 'path/to/your/corpus', // Directory containing .txt files
284
+ });
285
+
286
+ const handleAsk = async () => {
287
+ const messages: Message[] = [
288
+ { role: 'user', content: 'What information is in the documents?' },
289
+ ];
290
+
291
+ await cactusLM.complete({ messages });
292
+ };
293
+
294
+ return (
295
+ <>
296
+ <Button title="Ask Question" onPress={handleAsk} />
297
+ <Text>{cactusLM.completion}</Text>
298
+ </>
299
+ );
300
+ };
301
+ ```
302
+
132
303
  ### Embedding
133
304
 
305
+ Convert text into numerical vector representations that capture semantic meaning, useful for similarity search and semantic understanding.
306
+
134
307
  #### Class
135
308
 
136
309
  ```typescript
@@ -145,7 +318,7 @@ console.log('Embedding vector length:', result.embedding.length);
145
318
 
146
319
  #### Hook
147
320
 
148
- ```typescript
321
+ ```tsx
149
322
  import { useCactusLM } from 'cactus-react-native';
150
323
 
151
324
  const App = () => {
@@ -163,32 +336,37 @@ const App = () => {
163
336
 
164
337
  ## API Reference
165
338
 
166
- ### `CactusLM` Class
339
+ ### CactusLM Class
167
340
 
168
341
  #### Constructor
169
342
 
170
343
  **`new CactusLM(params?: CactusLMParams)`**
171
344
 
172
- - `model` - Model slug (default: `'qwen3-0.6'`)
173
- - `contextSize` - Context window size (default: `2048`)
345
+ **Parameters:**
346
+ - `model` - Model slug (default: `'qwen3-0.6'`).
347
+ - `contextSize` - Context window size (default: `2048`).
348
+ - `corpusDir` - Directory containing text files for RAG (default: `undefined`).
174
349
 
175
350
  #### Methods
176
351
 
177
352
  **`download(params?: CactusLMDownloadParams): Promise<void>`**
178
353
 
179
- - Downloads the model.
354
+ Downloads the model. If the model is already downloaded, returns immediately with progress at 100%. Throws an error if a download is already in progress. Automatically refreshes the models list after successful download.
355
+
356
+ **Parameters:**
180
357
  - `onProgress` - Callback for download progress (0-1).
181
358
 
182
359
  **`init(): Promise<void>`**
183
360
 
184
- - Initializes the model and prepares it for inference.
361
+ Initializes the model and prepares it for inference. Safe to call multiple times (idempotent). Throws an error if the model is not downloaded yet. Automatically initializes telemetry if not already done.
185
362
 
186
363
  **`complete(params: CactusLMCompleteParams): Promise<CactusLMCompleteResult>`**
187
364
 
188
- - Performs text completion with optional streaming and tool support (initializes the model if needed).
365
+ Performs text completion with optional streaming and tool support. Automatically calls `init()` if not already initialized. Throws an error if a generation (completion or embedding) is already in progress.
366
+
367
+ **Parameters:**
189
368
  - `messages` - Array of `Message` objects.
190
369
  - `options` - Generation options:
191
-
192
370
  - `temperature` - Sampling temperature (default: model-optimized).
193
371
  - `topP` - Nucleus sampling threshold (default: model-optimized).
194
372
  - `topK` - Top-K sampling limit (default: model-optimized).
@@ -199,61 +377,68 @@ const App = () => {
199
377
 
200
378
  **`embed(params: CactusLMEmbedParams): Promise<CactusLMEmbedResult>`**
201
379
 
202
- - Generates embeddings for the given text (initializes the model if needed).
380
+ Generates embeddings for the given text. Automatically calls `init()` if not already initialized. Throws an error if a generation (completion or embedding) is already in progress.
381
+
382
+ **Parameters:**
203
383
  - `text` - Text to embed.
204
384
 
205
385
  **`stop(): Promise<void>`**
206
386
 
207
- - Stops ongoing generation.
387
+ Stops ongoing generation.
208
388
 
209
389
  **`reset(): Promise<void>`**
210
390
 
211
- - Resets the model's internal state, clearing any cached context.
391
+ Resets the model's internal state, clearing any cached context. Automatically calls `stop()` first.
212
392
 
213
393
  **`destroy(): Promise<void>`**
214
394
 
215
- - Releases all resources associated with the model.
395
+ Releases all resources associated with the model. Automatically calls `stop()` first. Safe to call even if the model is not initialized.
216
396
 
217
397
  **`getModels(params?: CactusLMGetModelsParams): Promise<CactusModel[]>`**
218
398
 
219
- - Fetches available models and persists the results locally.
220
- - `forceRefresh` - If `true`, forces a fetch from the server and updates the local data (default: `false`).
399
+ Fetches available models and persists the results locally for caching. Returns cached results if available, unless `forceRefresh` is `true`. Checks the download status for each model and includes it in the results.
221
400
 
222
- ### `useCactusLM` Hook
401
+ **Parameters:**
402
+ - `forceRefresh` - If `true`, fetches from the server and updates the local cache (default: `false`).
403
+
404
+ ### useCactusLM Hook
405
+
406
+ The `useCactusLM` hook manages a `CactusLM` instance with reactive state. When model parameters (`model`, `contextSize`, or `corpusDir`) change, the hook creates a new instance and resets all state. The hook automatically cleans up resources when the component unmounts.
223
407
 
224
408
  #### State
225
409
 
226
- - `completion: string` - Current generated text.
227
- - `isGenerating: boolean` - Whether the model is currently generating.
410
+ - `completion: string` - Current generated text. Automatically accumulated during streaming. Cleared before each new completion and when calling `reset()` or `destroy()`.
411
+ - `isGenerating: boolean` - Whether the model is currently generating (completion or embedding). Both operations share this flag.
228
412
  - `isInitializing: boolean` - Whether the model is initializing.
229
- - `isDownloaded: boolean` - Whether the model is downloaded locally.
413
+ - `isDownloaded: boolean` - Whether the model is downloaded locally. Automatically checked when the hook mounts or model changes.
230
414
  - `isDownloading: boolean` - Whether the model is being downloaded.
231
- - `downloadProgress: number` - Download progress (0-1). `0` if not downloading.
232
- - `error: string | null` - Last error message, or `null` if there is no error.
415
+ - `downloadProgress: number` - Download progress (0-1). Reset to `0` after download completes.
416
+ - `error: string | null` - Last error message from any operation, or `null` if there is no error. Cleared before starting new operations.
233
417
 
234
418
  #### Methods
235
419
 
236
- - `download(params?: CactusLMDownloadParams): Promise<void>`
237
- - `init(): Promise<void>`
238
- - `complete(params: CactusLMCompleteParams): Promise<CactusLMCompleteResult>`
239
- - `embed(params: CactusLMEmbedParams): Promise<CactusLMEmbedResult>`
240
- - `stop(): Promise<void>`
241
- - `reset(): Promise<void>`
242
- - `destroy(): Promise<void>`
243
- - `getModels(params?: CactusLMGetModelsParams): Promise<CactusModel[]>`
420
+ - `download(params?: CactusLMDownloadParams): Promise<void>` - Downloads the model. Updates `isDownloading` and `downloadProgress` state during download. Sets `isDownloaded` to `true` on success.
421
+ - `init(): Promise<void>` - Initializes the model for inference. Sets `isInitializing` to `true` during initialization.
422
+ - `complete(params: CactusLMCompleteParams): Promise<CactusLMCompleteResult>` - Generates text completions. Automatically accumulates tokens in the `completion` state during streaming. Sets `isGenerating` to `true` while generating. Clears `completion` before starting.
423
+ - `embed(params: CactusLMEmbedParams): Promise<CactusLMEmbedResult>` - Generates embeddings for the given text. Sets `isGenerating` to `true` during operation.
424
+ - `stop(): Promise<void>` - Stops ongoing generation. Clears any errors.
425
+ - `reset(): Promise<void>` - Resets the model's internal state, clearing cached context. Also clears the `completion` state.
426
+ - `destroy(): Promise<void>` - Releases all resources associated with the model. Clears the `completion` state. Automatically called when the component unmounts.
427
+ - `getModels(params?: CactusLMGetModelsParams): Promise<CactusModel[]>` - Fetches available models and returns them. Results are cached locally.
244
428
 
245
429
  ## Type Definitions
246
430
 
247
- ### `CactusLMParams`
431
+ ### CactusLMParams
248
432
 
249
433
  ```typescript
250
434
  interface CactusLMParams {
251
435
  model?: string;
252
436
  contextSize?: number;
437
+ corpusDir?: string;
253
438
  }
254
439
  ```
255
440
 
256
- ### `CactusLMDownloadParams`
441
+ ### CactusLMDownloadParams
257
442
 
258
443
  ```typescript
259
444
  interface CactusLMDownloadParams {
@@ -261,16 +446,17 @@ interface CactusLMDownloadParams {
261
446
  }
262
447
  ```
263
448
 
264
- ### `Message`
449
+ ### Message
265
450
 
266
451
  ```typescript
267
452
  interface Message {
268
453
  role: 'user' | 'assistant' | 'system';
269
- content: string;
454
+ content?: string;
455
+ images?: string[];
270
456
  }
271
457
  ```
272
458
 
273
- ### `Options`
459
+ ### Options
274
460
 
275
461
  ```typescript
276
462
  interface Options {
@@ -282,7 +468,7 @@ interface Options {
282
468
  }
283
469
  ```
284
470
 
285
- ### `Tool`
471
+ ### Tool
286
472
 
287
473
  ```typescript
288
474
  interface Tool {
@@ -302,7 +488,7 @@ interface Tool {
302
488
  }
303
489
  ```
304
490
 
305
- ### `CactusLMCompleteParams`
491
+ ### CactusLMCompleteParams
306
492
 
307
493
  ```typescript
308
494
  interface CactusLMCompleteParams {
@@ -313,7 +499,7 @@ interface CactusLMCompleteParams {
313
499
  }
314
500
  ```
315
501
 
316
- ### `CactusLMCompleteResult`
502
+ ### CactusLMCompleteResult
317
503
 
318
504
  ```typescript
319
505
  interface CactusLMCompleteResult {
@@ -332,7 +518,7 @@ interface CactusLMCompleteResult {
332
518
  }
333
519
  ```
334
520
 
335
- ### `CactusLMEmbedParams`
521
+ ### CactusLMEmbedParams
336
522
 
337
523
  ```typescript
338
524
  interface CactusLMEmbedParams {
@@ -340,7 +526,7 @@ interface CactusLMEmbedParams {
340
526
  }
341
527
  ```
342
528
 
343
- ### `CactusLMEmbedResult`
529
+ ### CactusLMEmbedResult
344
530
 
345
531
  ```typescript
346
532
  interface CactusLMEmbedResult {
@@ -348,7 +534,7 @@ interface CactusLMEmbedResult {
348
534
  }
349
535
  ```
350
536
 
351
- ### `CactusLMGetModelsParams`
537
+ ### CactusLMGetModelsParams
352
538
 
353
539
  ```typescript
354
540
  interface CactusLMGetModelsParams {
@@ -356,7 +542,7 @@ interface CactusLMGetModelsParams {
356
542
  }
357
543
  ```
358
544
 
359
- ### `CactusModel`
545
+ ### CactusModel
360
546
 
361
547
  ```typescript
362
548
  interface CactusModel {
@@ -386,4 +572,14 @@ CactusConfig.telemetryToken = 'your-token-here';
386
572
 
387
573
  // Disable telemetry
388
574
  CactusConfig.isTelemetryEnabled = false;
389
- ```
575
+ ```
576
+
577
+ ## Performance Tips
578
+
579
+ - **Model Selection** - Choose smaller models for faster inference on mobile devices.
580
+ - **Context Size** - Reduce the context size to lower memory usage.
581
+ - **Memory Management** - Always call `destroy()` when you're done with models to free up resources.
582
+
583
+ ## Example App
584
+
585
+ Check out [our example app](/example) for a complete React Native implementation.
@@ -1,30 +1,41 @@
1
1
  #include "HybridCactus.hpp"
2
2
 
3
- namespace margelo::nitro::cactus
4
- {
3
+ namespace margelo::nitro::cactus {
5
4
  HybridCactus::HybridCactus() : HybridObject(TAG) {}
6
5
 
7
- std::shared_ptr<Promise<void>> HybridCactus::init(const std::string &modelPath, double contextSize) {
8
- return Promise<void>::async([this, modelPath, contextSize]() -> void {
9
- std::lock_guard<std::mutex> lock(this->_modelMutex);
6
+ std::shared_ptr<Promise<void>>
7
+ HybridCactus::init(const std::string &modelPath, double contextSize,
8
+ const std::optional<std::string> &corpusDir) {
9
+ return Promise<void>::async(
10
+ [this, modelPath, contextSize, corpusDir]() -> void {
11
+ std::lock_guard<std::mutex> lock(this->_modelMutex);
10
12
 
11
- if (this->_model) {
12
- throw std::runtime_error("Cactus model is already initialized");
13
- }
13
+ if (this->_model) {
14
+ throw std::runtime_error("Cactus model is already initialized");
15
+ }
14
16
 
15
- const cactus_model_t model = cactus_init(modelPath.c_str(), contextSize);
17
+ const cactus_model_t model =
18
+ cactus_init(modelPath.c_str(), contextSize,
19
+ corpusDir ? corpusDir->c_str() : nullptr);
16
20
 
17
- if (!model) {
18
- throw std::runtime_error("Failed to initialize Cactus model");
19
- }
21
+ if (!model) {
22
+ throw std::runtime_error("Failed to initialize Cactus model");
23
+ }
20
24
 
21
- this->_model = model;
22
- this->_contextSize = contextSize;
23
- });
25
+ this->_model = model;
26
+ this->_contextSize = contextSize;
27
+ });
24
28
  }
25
29
 
26
- std::shared_ptr<Promise<std::string>> HybridCactus::complete(const std::string &messagesJson, double responseBufferSize, const std::optional<std::string> &optionsJson, const std::optional<std::string> &toolsJson, const std::optional<std::function<void(const std::string & /* token */, double /* tokenId */)>> &callback) {
27
- return Promise<std::string>::async([this, messagesJson, optionsJson, toolsJson, callback, responseBufferSize]() -> std::string {
30
+ std::shared_ptr<Promise<std::string>> HybridCactus::complete(
31
+ const std::string &messagesJson, double responseBufferSize,
32
+ const std::optional<std::string> &optionsJson,
33
+ const std::optional<std::string> &toolsJson,
34
+ const std::optional<std::function<void(const std::string & /* token */,
35
+ double /* tokenId */)>> &callback) {
36
+ return Promise<std::string>::async([this, messagesJson, optionsJson,
37
+ toolsJson, callback,
38
+ responseBufferSize]() -> std::string {
28
39
  std::lock_guard<std::mutex> lock(this->_modelMutex);
29
40
 
30
41
  if (!this->_model) {
@@ -32,28 +43,26 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(const std::string &
32
43
  }
33
44
 
34
45
  struct CallbackCtx {
35
- const std::function<void(const std::string & /* token */, double /* tokenId */)> *callback;
36
- } callbackCtx{ callback.has_value() ? &callback.value() : nullptr };
37
-
38
- auto cactusTokenCallback = [](const char* token, uint32_t tokenId, void* userData) {
39
- auto* callbackCtx = static_cast<CallbackCtx*>(userData);
40
- if (!callbackCtx || !callbackCtx->callback || !(*callbackCtx->callback)) return;
46
+ const std::function<void(const std::string & /* token */,
47
+ double /* tokenId */)> *callback;
48
+ } callbackCtx{callback.has_value() ? &callback.value() : nullptr};
49
+
50
+ auto cactusTokenCallback = [](const char *token, uint32_t tokenId,
51
+ void *userData) {
52
+ auto *callbackCtx = static_cast<CallbackCtx *>(userData);
53
+ if (!callbackCtx || !callbackCtx->callback || !(*callbackCtx->callback))
54
+ return;
41
55
  (*callbackCtx->callback)(token, tokenId);
42
56
  };
43
57
 
44
58
  std::string responseBuffer;
45
59
  responseBuffer.resize(responseBufferSize);
46
60
 
47
- int result = cactus_complete(
48
- this->_model,
49
- messagesJson.c_str(),
50
- responseBuffer.data(),
51
- responseBufferSize,
52
- optionsJson ? optionsJson->c_str() : nullptr,
53
- toolsJson ? toolsJson->c_str() : nullptr,
54
- cactusTokenCallback,
55
- &callbackCtx
56
- );
61
+ int result = cactus_complete(this->_model, messagesJson.c_str(),
62
+ responseBuffer.data(), responseBufferSize,
63
+ optionsJson ? optionsJson->c_str() : nullptr,
64
+ toolsJson ? toolsJson->c_str() : nullptr,
65
+ cactusTokenCallback, &callbackCtx);
57
66
 
58
67
  if (result < 0) {
59
68
  throw std::runtime_error("Cactus completion failed");
@@ -66,27 +75,32 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(const std::string &
66
75
  });
67
76
  }
68
77
 
69
- std::shared_ptr<Promise<std::vector<double>>> HybridCactus::embed(const std::string &text, double embeddingBufferSize) {
70
- return Promise<std::vector<double>>::async([this, text, embeddingBufferSize]() -> std::vector<double> {
71
- std::lock_guard<std::mutex> lock(this->_modelMutex);
78
+ std::shared_ptr<Promise<std::vector<double>>>
79
+ HybridCactus::embed(const std::string &text, double embeddingBufferSize) {
80
+ return Promise<std::vector<double>>::async(
81
+ [this, text, embeddingBufferSize]() -> std::vector<double> {
82
+ std::lock_guard<std::mutex> lock(this->_modelMutex);
72
83
 
73
- if (!this->_model) {
74
- throw std::runtime_error("Cactus model is not initialized");
75
- }
84
+ if (!this->_model) {
85
+ throw std::runtime_error("Cactus model is not initialized");
86
+ }
76
87
 
77
- std::vector<float> embeddingBuffer(embeddingBufferSize);
78
- size_t embeddingDim;
88
+ std::vector<float> embeddingBuffer(embeddingBufferSize);
89
+ size_t embeddingDim;
79
90
 
80
- int result = cactus_embed(this->_model, text.c_str(), embeddingBuffer.data(), embeddingBufferSize * sizeof(float), &embeddingDim);
91
+ int result =
92
+ cactus_embed(this->_model, text.c_str(), embeddingBuffer.data(),
93
+ embeddingBufferSize * sizeof(float), &embeddingDim);
81
94
 
82
- if (result < 0) {
83
- throw std::runtime_error("Cactus embedding failed");
84
- }
95
+ if (result < 0) {
96
+ throw std::runtime_error("Cactus embedding failed");
97
+ }
85
98
 
86
- embeddingBuffer.resize(embeddingDim);
99
+ embeddingBuffer.resize(embeddingDim);
87
100
 
88
- return std::vector<double>(embeddingBuffer.begin(), embeddingBuffer.end());
89
- });
101
+ return std::vector<double>(embeddingBuffer.begin(),
102
+ embeddingBuffer.end());
103
+ });
90
104
  }
91
105
 
92
106
  std::shared_ptr<Promise<void>> HybridCactus::reset() {
@@ -102,9 +116,7 @@ std::shared_ptr<Promise<void>> HybridCactus::reset() {
102
116
  }
103
117
 
104
118
  std::shared_ptr<Promise<void>> HybridCactus::stop() {
105
- return Promise<void>::async([this]() -> void {
106
- cactus_stop(this->_model);
107
- });
119
+ return Promise<void>::async([this]() -> void { cactus_stop(this->_model); });
108
120
  }
109
121
 
110
122
  std::shared_ptr<Promise<void>> HybridCactus::destroy() {
@@ -5,26 +5,33 @@
5
5
 
6
6
  #include <mutex>
7
7
 
8
- namespace margelo::nitro::cactus
9
- {
8
+ namespace margelo::nitro::cactus {
10
9
 
11
- class HybridCactus : public HybridCactusSpec
12
- {
10
+ class HybridCactus : public HybridCactusSpec {
13
11
  public:
14
12
  HybridCactus();
15
-
16
- std::shared_ptr<Promise<void>> init(const std::string &modelPath, double contextSize) override;
17
-
18
- std::shared_ptr<Promise<std::string>> complete(const std::string &messagesJson, double responseBufferSize, const std::optional<std::string> &optionsJson, const std::optional<std::string> &toolsJson, const std::optional<std::function<void(const std::string & /* token */, double /* tokenId */)>> &callback) override;
19
-
20
- std::shared_ptr<Promise<std::vector<double>>> embed(const std::string &text, double embeddingBufferSize) override;
21
-
13
+
14
+ std::shared_ptr<Promise<void>>
15
+ init(const std::string &modelPath, double contextSize,
16
+ const std::optional<std::string> &corpusDir) override;
17
+
18
+ std::shared_ptr<Promise<std::string>> complete(
19
+ const std::string &messagesJson, double responseBufferSize,
20
+ const std::optional<std::string> &optionsJson,
21
+ const std::optional<std::string> &toolsJson,
22
+ const std::optional<std::function<void(const std::string & /* token */,
23
+ double /* tokenId */)>> &callback)
24
+ override;
25
+
26
+ std::shared_ptr<Promise<std::vector<double>>>
27
+ embed(const std::string &text, double embeddingBufferSize) override;
28
+
22
29
  std::shared_ptr<Promise<void>> reset() override;
23
-
30
+
24
31
  std::shared_ptr<Promise<void>> stop() override;
25
-
32
+
26
33
  std::shared_ptr<Promise<void>> destroy() override;
27
-
34
+
28
35
  private:
29
36
  cactus_model_t _model = nullptr;
30
37
  size_t _contextSize;