@coherentglobal/spark-execute-sdk 0.8.3 → 0.8.4
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/README.md +425 -100
- package/dist/browser.js +2525 -5658
- package/package.json +31 -6
- package/src/browser.js +59 -10
- package/src/findModels.js +6 -6
- package/src/helpers/utils.js +5 -2
- package/src/node.js +51 -2
- package/types/browser.d.ts +5 -0
- package/types/browser.d.ts.map +1 -1
- package/types/helpers/utils.d.ts +2 -2
- package/types/helpers/utils.d.ts.map +1 -1
- package/types/logger.d.ts +2 -1
- package/types/node.d.ts +4 -0
- package/types/node.d.ts.map +1 -1
- package/src/services/entityStore.js +0 -271
- package/src/services/spark.js +0 -80
- package/types/services/entityStore.d.ts +0 -51
- package/types/services/entityStore.d.ts.map +0 -1
- package/types/services/spark.d.ts +0 -6
- package/types/services/spark.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
1
|
# Spark Execute SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Execute [Spark](https://coherent.global/spark/) models both online and offline with a unified API. This SDK seamlessly switches between local WebAssembly execution and remote API calls, giving you the flexibility to run models wherever makes sense for your application.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Run models locally using WebAssembly (offline)
|
|
8
|
+
- Call Spark API endpoints (online)
|
|
9
|
+
- Automatic fallback from offline to online on errors
|
|
10
|
+
- Cancel long-running executions mid-flight
|
|
11
|
+
- Automatic memory management with configurable limits
|
|
12
|
+
- Full support for cross-service calls (XCall)
|
|
13
|
+
- Works in Node.js and browser environments
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
Install the package:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @coherentglobal/spark-execute-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Basic usage:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// CommonJS
|
|
27
|
+
const Spark = require('@coherentglobal/spark-execute-sdk');
|
|
28
|
+
|
|
29
|
+
// ES Modules / TypeScript
|
|
30
|
+
import Spark from '@coherentglobal/spark-execute-sdk';
|
|
31
|
+
|
|
32
|
+
const spark = new Spark({
|
|
33
|
+
sparkEndpoint: {
|
|
34
|
+
url: "https://excel.uat.us.coherent.global",
|
|
35
|
+
tenant: "your-tenant",
|
|
36
|
+
authType: "public"
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = await spark.execute({
|
|
41
|
+
request_data: { inputs: { Input: 1 } },
|
|
42
|
+
request_meta: { version_id: "model-uuid" }
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await spark.destroy();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
# How-to Guides
|
|
8
49
|
|
|
9
50
|
## Installation
|
|
10
51
|
|
|
@@ -22,159 +63,443 @@ yarn add @coherentglobal/spark-execute-sdk
|
|
|
22
63
|
|
|
23
64
|
### Configuration
|
|
24
65
|
|
|
25
|
-
|
|
66
|
+
**Execution Modes:**
|
|
67
|
+
- Include both `sparkEndpoint` and `nodegenModels` for **automatic fallback** (offline → online)
|
|
68
|
+
- Include only `sparkEndpoint` for **online-only** execution
|
|
69
|
+
- Include only `nodegenModels` for **offline-only** execution
|
|
26
70
|
|
|
27
71
|
```js
|
|
28
72
|
const config = {
|
|
29
|
-
|
|
73
|
+
// Online execution (optional - omit for offline-only)
|
|
30
74
|
sparkEndpoint: {
|
|
31
|
-
url: "https://excel.
|
|
32
|
-
tenant: "tenant",
|
|
33
|
-
authType: public | syntheticKey | bearerToken,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
bearerToken: generateToken(),
|
|
75
|
+
url: "https://excel.uat.us.coherent.global",
|
|
76
|
+
tenant: "tenant",
|
|
77
|
+
authType: "public" | "syntheticKey" | "bearerToken",
|
|
78
|
+
|
|
79
|
+
// Provide value OR function (called per request - you handle caching)
|
|
80
|
+
syntheticKey: "apiKey" || generateSyntheticKey(),
|
|
81
|
+
bearerToken: "token" || generateToken(),
|
|
38
82
|
},
|
|
83
|
+
|
|
84
|
+
// Offline execution (optional - omit or use [] for online-only)
|
|
39
85
|
nodegenModels: [
|
|
40
86
|
{
|
|
41
87
|
versionId: "uuid",
|
|
42
|
-
type: "base64",
|
|
43
|
-
binary: Blob |
|
|
44
|
-
preventCleanup:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
88
|
+
type: "base64", // Always "base64"
|
|
89
|
+
binary: string | Blob | Buffer | ArrayBuffer | Function, // See below
|
|
90
|
+
preventCleanup: false, // Keep in memory even when limit reached
|
|
91
|
+
replica: 1, // Number of concurrent worker instances
|
|
92
|
+
metaData: {
|
|
93
|
+
EngineInformation: {
|
|
94
|
+
ProductName: "folder-name",
|
|
95
|
+
ServiceName: "service-name",
|
|
96
|
+
VersionId: "uuid"
|
|
97
|
+
}
|
|
48
98
|
}
|
|
49
99
|
}
|
|
50
|
-
]
|
|
51
|
-
|
|
100
|
+
],
|
|
101
|
+
|
|
102
|
+
// Advanced options
|
|
103
|
+
offlineMaxService: 20, // Max models in memory before LRU cleanup
|
|
104
|
+
timeout: 60000 // Request timeout (ms)
|
|
105
|
+
};
|
|
52
106
|
```
|
|
53
107
|
|
|
54
|
-
|
|
108
|
+
### Understanding the `binary` Configuration
|
|
109
|
+
|
|
110
|
+
The `binary` field accepts different formats depending on your environment.
|
|
111
|
+
|
|
112
|
+
| Environment | Accepted Formats |
|
|
113
|
+
|-------------|------------------|
|
|
114
|
+
| **Browser** | Base64 string, `Blob`, `ArrayBuffer`, Function |
|
|
115
|
+
| **Node.js** | Base64 string, `Buffer`, `ArrayBuffer`, Function |
|
|
116
|
+
|
|
117
|
+
> **Important:** The `type` field should always be `"base64"` regardless of actual binary format.
|
|
55
118
|
|
|
119
|
+
#### Common Patterns
|
|
120
|
+
|
|
121
|
+
**Static values:**
|
|
56
122
|
```js
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
nodegenModels: [],
|
|
68
|
-
};
|
|
69
|
-
```
|
|
123
|
+
// Base64 string (most common)
|
|
124
|
+
const fs = require('fs');
|
|
125
|
+
binary: fs.readFileSync('./model.zip', 'base64')
|
|
126
|
+
|
|
127
|
+
// Buffer (Node.js)
|
|
128
|
+
binary: fs.readFileSync('./model.zip')
|
|
129
|
+
|
|
130
|
+
// ArrayBuffer (fetch in browser/Node.js)
|
|
131
|
+
const response = await fetch('/models/model.zip');
|
|
132
|
+
binary: await response.arrayBuffer()
|
|
70
133
|
|
|
71
|
-
|
|
134
|
+
// Blob (browser file upload)
|
|
135
|
+
binary: new Blob([zipData], { type: 'application/zip' })
|
|
136
|
+
```
|
|
72
137
|
|
|
138
|
+
**Dynamic loading (function):**
|
|
73
139
|
```js
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
140
|
+
// Lazy load from filesystem
|
|
141
|
+
binary: (versionId) => require('fs').readFileSync(`./models/${versionId}.zip`, 'base64')
|
|
142
|
+
|
|
143
|
+
// Fetch from remote storage
|
|
144
|
+
binary: async (versionId) => {
|
|
145
|
+
const response = await fetch(`https://storage.example.com/models/${versionId}.zip`);
|
|
146
|
+
return await response.arrayBuffer();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// With caching
|
|
150
|
+
const modelCache = new Map();
|
|
151
|
+
binary: async (versionId) => {
|
|
152
|
+
if (!modelCache.has(versionId)) {
|
|
153
|
+
modelCache.set(versionId, await fetchModelFromS3(versionId));
|
|
154
|
+
}
|
|
155
|
+
return modelCache.get(versionId);
|
|
156
|
+
}
|
|
88
157
|
```
|
|
89
158
|
|
|
90
|
-
##
|
|
159
|
+
## API Reference
|
|
91
160
|
|
|
92
|
-
###
|
|
161
|
+
### Initialization
|
|
93
162
|
|
|
94
|
-
|
|
163
|
+
Create a Spark instance with your configuration:
|
|
95
164
|
|
|
96
165
|
```js
|
|
97
166
|
const spark = new Spark(config);
|
|
98
167
|
```
|
|
99
168
|
|
|
100
|
-
###
|
|
169
|
+
### execute(input, version_id?)
|
|
101
170
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Perform the calculation. The sdk will use the request_meta.version_uuid of input argument to locate the model
|
|
171
|
+
Execute a model using the provided input payload.
|
|
105
172
|
|
|
106
173
|
```js
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
174
|
+
// Basic usage
|
|
175
|
+
const response = await spark.execute(input);
|
|
176
|
+
|
|
177
|
+
// With explicit version ID
|
|
178
|
+
const response = await spark.execute(input, 'model-uuid-here');
|
|
110
179
|
```
|
|
111
180
|
|
|
112
|
-
|
|
181
|
+
> **Tip:** The `version_id` parameter is optional if already in `request_meta`. See [Error Handling](#error-handling) below.
|
|
182
|
+
|
|
183
|
+
### executeWithCancellationToken(input, version_id?)
|
|
184
|
+
|
|
185
|
+
Execute a model with the ability to cancel the operation mid-execution. Useful for long-running calculations that users may want to cancel.
|
|
113
186
|
|
|
114
187
|
```js
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
188
|
+
const execution = spark.executeWithCancellationToken(input);
|
|
189
|
+
|
|
190
|
+
// Cancel anytime before completion
|
|
191
|
+
execution.cancellationToken.cancel();
|
|
192
|
+
|
|
193
|
+
execution.response
|
|
194
|
+
.then((result) => {
|
|
195
|
+
console.log('Completed:', result);
|
|
196
|
+
})
|
|
197
|
+
.catch((err) => {
|
|
198
|
+
if (err instanceof Spark.WasmRunnerErrors.ExecuteCancelled) {
|
|
199
|
+
console.log('User cancelled the execution');
|
|
200
|
+
} else {
|
|
201
|
+
console.error('Execution failed:', err);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
118
204
|
```
|
|
119
205
|
|
|
120
|
-
|
|
206
|
+
> **Important:** Cancellation is best-effort. The underlying WebAssembly execution may continue running in its worker thread, but the result will be discarded and an `ExecuteCancelled` error will be thrown. The worker is cleaned up after completion.
|
|
207
|
+
|
|
208
|
+
### destroy()
|
|
121
209
|
|
|
122
|
-
|
|
210
|
+
Clean up all resources, unload models, and terminate worker threads. Call this when you're done with a Spark instance to prevent memory leaks.
|
|
123
211
|
|
|
124
212
|
```js
|
|
125
|
-
|
|
213
|
+
await spark.destroy();
|
|
214
|
+
```
|
|
126
215
|
|
|
127
|
-
|
|
128
|
-
execute.cancellationToken.cancel()
|
|
216
|
+
> **Critical:** Always call `destroy()` when finished, especially in browsers or long-running Node.js apps. Forgetting this can lead to memory leaks as WebAssembly modules and workers won't be garbage collected.
|
|
129
217
|
|
|
130
|
-
|
|
131
|
-
// Do something here
|
|
132
|
-
})
|
|
133
|
-
.catch((err) => {
|
|
134
|
-
// Do something here
|
|
135
|
-
// If error is instance of Spark.WasmRunnerErrors.ExecuteCancelled, meaning the request has been cancelled
|
|
136
|
-
});
|
|
137
|
-
```
|
|
218
|
+
### Error Handling
|
|
138
219
|
|
|
139
|
-
|
|
220
|
+
Specific error types accessible via `Spark.WasmRunnerErrors`:
|
|
140
221
|
|
|
141
222
|
```js
|
|
142
|
-
|
|
223
|
+
try {
|
|
224
|
+
const response = await spark.execute(input);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
if (err instanceof Spark.WasmRunnerErrors.MissingModelError) {
|
|
227
|
+
console.error('Model not found:', err.message);
|
|
228
|
+
} else if (err instanceof Spark.WasmRunnerErrors.ExecuteCancelled) {
|
|
229
|
+
console.error('Execution was cancelled');
|
|
230
|
+
} else if (err instanceof Spark.WasmRunnerErrors.UnauthorizedError) {
|
|
231
|
+
console.error('Authentication failed');
|
|
232
|
+
} else if (err instanceof Spark.WasmRunnerErrors.BadRequestError) {
|
|
233
|
+
console.error('Bad request:', err.message);
|
|
234
|
+
} else {
|
|
235
|
+
console.error('Unknown error:', err);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
143
239
|
|
|
144
|
-
|
|
145
|
-
|
|
240
|
+
**Available Error Types:**
|
|
241
|
+
- `MissingModelError` - Model not found
|
|
242
|
+
- `ExecuteCancelled` - Execution was cancelled via cancellation token
|
|
243
|
+
- `UnauthorizedError` - Authentication failed (invalid token/key)
|
|
244
|
+
- `BadRequestError` - Invalid request or SDK not initialized properly
|
|
146
245
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
246
|
+
### Error Response Structure
|
|
247
|
+
|
|
248
|
+
When an online API request fails, the Spark API returns structured error responses:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"error": {
|
|
253
|
+
"status": 401,
|
|
254
|
+
"message": "Unauthorized: Invalid API key",
|
|
255
|
+
"code": "UNAUTHORIZED"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
154
258
|
```
|
|
155
259
|
|
|
156
|
-
|
|
260
|
+
Common HTTP status codes:
|
|
261
|
+
- **400 Bad Request** - Invalid input data or malformed request
|
|
262
|
+
- **401 Unauthorized** - Missing or invalid authentication credentials
|
|
263
|
+
- **404 Not Found** - Model or endpoint not found
|
|
264
|
+
- **500 Internal Server Error** - Server-side error during execution
|
|
265
|
+
- **503 Service Unavailable** - Service temporarily unavailable
|
|
157
266
|
|
|
158
|
-
###
|
|
267
|
+
### Input Structure
|
|
159
268
|
|
|
160
269
|
```json
|
|
161
270
|
{
|
|
162
|
-
"request_data": {
|
|
163
|
-
"inputs": {
|
|
164
|
-
"Input": 1
|
|
165
|
-
}
|
|
166
|
-
},
|
|
271
|
+
"request_data": { "inputs": { "Input": 1 } },
|
|
167
272
|
"request_meta": {
|
|
168
273
|
"version_id": "<model id>",
|
|
169
274
|
"transaction_date": "2022-09-19T04:17:17.142Z",
|
|
170
|
-
"call_purpose": "Spark - API Tester"
|
|
171
|
-
|
|
172
|
-
"correlation_id": "",
|
|
173
|
-
"requested_output": ""
|
|
275
|
+
"call_purpose": "Spark - API Tester"
|
|
276
|
+
// correlation_id, source_system, requested_output (optional)
|
|
174
277
|
}
|
|
175
278
|
}
|
|
176
279
|
```
|
|
177
280
|
|
|
178
|
-
|
|
281
|
+
## Platform-Specific Usage
|
|
282
|
+
|
|
283
|
+
### Node.js
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
// CommonJS
|
|
287
|
+
const Spark = require('@coherentglobal/spark-execute-sdk');
|
|
288
|
+
|
|
289
|
+
// ES Modules / TypeScript
|
|
290
|
+
import Spark from '@coherentglobal/spark-execute-sdk';
|
|
291
|
+
|
|
292
|
+
const fs = require('fs');
|
|
293
|
+
const spark = new Spark({
|
|
294
|
+
nodegenModels: [{
|
|
295
|
+
versionId: "model-uuid",
|
|
296
|
+
binary: fs.readFileSync('./model.zip', 'base64'),
|
|
297
|
+
metaData: { /* ... */ }
|
|
298
|
+
}]
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const response = await spark.execute(input);
|
|
302
|
+
await spark.destroy();
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Browser
|
|
306
|
+
|
|
307
|
+
```html
|
|
308
|
+
<script src="node_modules/@coherentglobal/spark-execute-sdk/dist/browser.js"></script>
|
|
309
|
+
<script>
|
|
310
|
+
const config = {
|
|
311
|
+
nodegenModels: [{
|
|
312
|
+
versionId: "model-uuid",
|
|
313
|
+
binary: base64EncodedModel,
|
|
314
|
+
metaData: { /* ... */ }
|
|
315
|
+
}]
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const spark = new Spark(config);
|
|
319
|
+
|
|
320
|
+
spark.execute(input)
|
|
321
|
+
.then(response => console.log('Result:', response))
|
|
322
|
+
.catch(err => console.error('Error:', err))
|
|
323
|
+
.finally(() => spark.destroy()); // Always cleanup!
|
|
324
|
+
</script>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Troubleshooting
|
|
328
|
+
|
|
329
|
+
### Common Issues
|
|
330
|
+
|
|
331
|
+
#### "Model not found" Error
|
|
332
|
+
|
|
333
|
+
**Problem:** SDK throws `MissingModelError` when trying to execute.
|
|
334
|
+
|
|
335
|
+
**Solutions:**
|
|
336
|
+
- Verify that `versionId` in your input matches a configured model in `nodegenModels`
|
|
337
|
+
- Check that `request_meta.version_id` is correctly specified in your input
|
|
338
|
+
- Check if the model binary is loaded (check browser console for loading errors)
|
|
339
|
+
|
|
340
|
+
#### "Binary corrupted" or Initialization Errors
|
|
341
|
+
|
|
342
|
+
**Problem:** Model fails to initialize or throws corruption errors.
|
|
343
|
+
|
|
344
|
+
**Solutions:**
|
|
345
|
+
- Verify the model binary is valid and not truncated during download
|
|
346
|
+
- Check that the base64 encoding is correct (no line breaks or extra characters)
|
|
347
|
+
- Try re-downloading the model from Spark
|
|
348
|
+
- Ensure the binary isn't corrupted (use `type: "base64"` for base64-encoded zips)
|
|
349
|
+
|
|
350
|
+
#### "Worker terminated unexpectedly"
|
|
351
|
+
|
|
352
|
+
**Problem:** WebAssembly worker crashes during execution.
|
|
353
|
+
|
|
354
|
+
**Solutions:**
|
|
355
|
+
- Check browser console for memory errors
|
|
356
|
+
- Reduce the number of concurrent replicas
|
|
357
|
+
- Ensure the model isn't too large for the available memory
|
|
358
|
+
- Try increasing timeout value if the model is computationally intensive
|
|
359
|
+
|
|
360
|
+
#### ExecuteCancelled Thrown Instantly
|
|
361
|
+
|
|
362
|
+
**Problem:** Cancellation error occurs immediately even without calling cancel.
|
|
363
|
+
|
|
364
|
+
**Solutions:**
|
|
365
|
+
- Check if cancellation token is being reused from a previous execution
|
|
366
|
+
- Create a new execution instance for each call to `executeWithCancellationToken`
|
|
367
|
+
- Verify that cancel() isn't being called earlier in your code flow
|
|
368
|
+
|
|
369
|
+
#### Online Fallback Not Triggered
|
|
370
|
+
|
|
371
|
+
**Problem:** SDK doesn't fall back to online execution when offline fails.
|
|
372
|
+
|
|
373
|
+
**Solutions:**
|
|
374
|
+
- Ensure both `sparkEndpoint` and `nodegenModels` are configured
|
|
375
|
+
- Check that the offline error is not being caught before fallback occurs
|
|
376
|
+
- Verify network connectivity for online execution
|
|
377
|
+
- Check authentication credentials for the online endpoint
|
|
378
|
+
|
|
379
|
+
#### Memory Leaks in Long-Running Applications
|
|
380
|
+
|
|
381
|
+
**Problem:** Application memory usage keeps increasing over time.
|
|
382
|
+
|
|
383
|
+
**Solutions:**
|
|
384
|
+
- Always call `spark.destroy()` when finished with an instance
|
|
385
|
+
- Don't create multiple Spark instances unnecessarily
|
|
386
|
+
- Monitor the number of loaded models (check against `offlineMaxService` limit)
|
|
387
|
+
- Use `preventCleanup: false` for models that don't need to persist
|
|
388
|
+
|
|
389
|
+
## Memory Usage & Cleanup
|
|
390
|
+
|
|
391
|
+
### How Model Cleanup Works
|
|
392
|
+
|
|
393
|
+
The SDK automatically manages memory using LRU (Least Recently Used) eviction:
|
|
394
|
+
|
|
395
|
+
- **offlineMaxService** (default: 20): Maximum models kept in memory
|
|
396
|
+
- **preventCleanup**: Set to `true` to prevent a model from being unloaded
|
|
397
|
+
- When the limit is reached, the least recently used model (without `preventCleanup`) is unloaded
|
|
398
|
+
|
|
399
|
+
### Worker Thread Management
|
|
400
|
+
|
|
401
|
+
Each model uses dedicated worker threads for parallel execution.
|
|
402
|
+
|
|
403
|
+
#### Concurrency Model
|
|
404
|
+
|
|
405
|
+
- Each model creates a pool of isolated worker threads based on the `replica` count (default: 1)
|
|
406
|
+
- Incoming execution requests are distributed across available workers in round-robin fashion
|
|
407
|
+
- Each worker runs in its own thread with its own WebAssembly instance—no shared state
|
|
408
|
+
- Model are lazy-loaded on first run
|
|
409
|
+
- Higher `replica` counts increase initial execution time (more workers to load) but improve throughput for concurrent requests
|
|
410
|
+
|
|
411
|
+
#### Configuration Guide
|
|
412
|
+
|
|
413
|
+
| Use Case | `replica` | `preventCleanup` |
|
|
414
|
+
|----------|-----------|------------------|
|
|
415
|
+
| High-frequency model** | 4-8 | `true` |
|
|
416
|
+
| Occasional use | 1 | `false` |
|
|
417
|
+
| Critical path | 2-4 | `true` |
|
|
418
|
+
| Background jobs | 1-2 | `false` |
|
|
419
|
+
|
|
420
|
+
#### Memory Considerations
|
|
421
|
+
|
|
422
|
+
Each worker instance loads the full WebAssembly module into memory:
|
|
423
|
+
|
|
424
|
+
- **Memory per worker** = Model required memory (~150-300 MB)
|
|
425
|
+
- **Total memory** = (Number of models) × (Replicas per model) × (Memory per worker)
|
|
426
|
+
- **Example**: 10 models × 3 replicas × 150 MB = ~4500 MB minimum
|
|
427
|
+
|
|
428
|
+
**NOTE**: We recommend keeping `replica` at 2 or fewer, unless you have a very specific performance requirement that justifies higher concurrency.
|
|
429
|
+
|
|
430
|
+
### Cleanup Best Practices
|
|
431
|
+
|
|
432
|
+
**Always call destroy():**
|
|
433
|
+
```js
|
|
434
|
+
const spark = new Spark(config);
|
|
435
|
+
try {
|
|
436
|
+
await spark.execute(input);
|
|
437
|
+
} finally {
|
|
438
|
+
await spark.destroy();
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**For long-running apps:**
|
|
443
|
+
- Set `offlineMaxService` based on available memory
|
|
444
|
+
- Use `preventCleanup: true` sparingly
|
|
445
|
+
- Call `destroy()` before page unload (browsers)
|
|
446
|
+
|
|
447
|
+
## Known Limitations
|
|
448
|
+
|
|
449
|
+
### Browser Memory Constraints
|
|
450
|
+
|
|
451
|
+
WebAssembly in browsers has strict memory limits:
|
|
452
|
+
|
|
453
|
+
| Constraint | Limit | Impact |
|
|
454
|
+
|------------|-------|--------|
|
|
455
|
+
| **WebAssembly memory** | ~1-2 GB (browser dependent, varied between platform) | Large models may fail to load |
|
|
456
|
+
| **Total tab memory** | ~2-4 GB (browser dependent) | Multiple models + high replicas can hit browser limits |
|
|
457
|
+
|
|
458
|
+
**Recommendations for Browser:**
|
|
459
|
+
- Limit `replica` count to 2-4 for large / highly complex models
|
|
460
|
+
- Monitor memory usage with browser DevTools
|
|
461
|
+
- Consider online-only mode for very large models
|
|
462
|
+
|
|
463
|
+
### Node.js Worker Thread Cost
|
|
464
|
+
|
|
465
|
+
Each worker thread in Node.js has overhead:
|
|
466
|
+
|
|
467
|
+
- **Startup time**: 50-200ms per worker
|
|
468
|
+
- **Memory overhead**: ~150-300 MB per thread (V8 isolate + runtime)
|
|
469
|
+
- **Efficient thread limit**: Typically 50-200 threads (OS dependent)
|
|
470
|
+
|
|
471
|
+
**For Node.js applications:**
|
|
472
|
+
- Don't create hundreds of workers (replica count × model count should be reasonable)
|
|
473
|
+
- Workers are lazy-loaded but all replicas for a model are initialized on first execution
|
|
474
|
+
|
|
475
|
+
### Cross-Service Call (XCall) Depth
|
|
476
|
+
|
|
477
|
+
**Recursion Limit:**
|
|
478
|
+
- Maximum XCall depth: ~10-20 levels (implementation dependent)
|
|
479
|
+
- Deeply nested XCalls increase memory usage and execution time
|
|
480
|
+
- Stack overflow possible with excessive recursion
|
|
481
|
+
|
|
482
|
+
**Best Practice:**
|
|
483
|
+
- Design models to minimize XCall depth
|
|
484
|
+
- Avoid circular dependencies between models
|
|
485
|
+
- Consider flattening deeply nested call chains
|
|
486
|
+
|
|
487
|
+
### Model Loading Failures
|
|
488
|
+
|
|
489
|
+
**Common offline mode failures:**
|
|
490
|
+
|
|
491
|
+
| Scenario | Browser | Node.js |
|
|
492
|
+
|----------|---------|---------|
|
|
493
|
+
| 10+ concurrent replicas | May hit memory limits | Works but slow startup |
|
|
494
|
+
| Complex XCall graphs | Stack overflow possible | More headroom |
|
|
495
|
+
|
|
496
|
+
**Mitigation:**
|
|
497
|
+
- Use online mode for exceptionally large models
|
|
498
|
+
- Reduce `replica` count if initialization fails
|
|
499
|
+
- Monitor memory consumption during development
|
|
500
|
+
|
|
501
|
+
## Live Demo
|
|
502
|
+
|
|
503
|
+
See the SDK in action with this interactive CodeSandbox example:
|
|
179
504
|
|
|
180
|
-
|
|
505
|
+
**[View Demo →](https://codesandbox.io/s/autumn-hill-1342ln?file=/index.html)**
|