@jseeio/jsee 0.2.8 → 0.3.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/CHANGELOG.md +13 -3
- package/README.md +42 -11
- package/dist/jsee.js +1 -1
- package/dist/jsee.runtime.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +28 -25
- package/src/app.js +26 -16
- package/src/main.js +678 -0
- package/src/utils.js +91 -12
- package/src/worker.js +56 -60
- package/templates/bulma-app.vue +32 -7
- package/templates/bulma-input.vue +11 -5
- package/templates/common-inputs.js +3 -0
- package/test/class.html +22 -0
- package/test/importw.html +28 -0
- package/test/minimal.html +1 -1
- package/test/minimal4.html +1 -1
- package/test/pipeline.html +52 -0
- package/test/python.html +41 -0
- package/{test.js → test/test-basic.test.js} +94 -17
- package/test/test-python.test.js +26 -0
- package/webpack.config.js +2 -1
- package/main.js +0 -621
- package/test/example-async-function.js +0 -0
- package/test/example-async-init.js +0 -0
package/main.js
DELETED
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
import { createVueApp } from './src/app'
|
|
2
|
-
import Worker from './src/worker.js'
|
|
3
|
-
|
|
4
|
-
const utils = require('./src/utils')
|
|
5
|
-
|
|
6
|
-
const { Notyf } = require('notyf')
|
|
7
|
-
const notyf = new Notyf({
|
|
8
|
-
types: [
|
|
9
|
-
{
|
|
10
|
-
type: 'success',
|
|
11
|
-
background: '#00d1b2',
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
type: 'error',
|
|
15
|
-
background: '#f14668',
|
|
16
|
-
duration: 2000,
|
|
17
|
-
dismissible: true
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
// const createVueApp = require('./src/app')
|
|
23
|
-
const Overlay = require('./src/overlay')
|
|
24
|
-
|
|
25
|
-
require('notyf/notyf.min.css')
|
|
26
|
-
|
|
27
|
-
const fetch = window['fetch']
|
|
28
|
-
const Blob = window['Blob']
|
|
29
|
-
|
|
30
|
-
let verbose = true
|
|
31
|
-
function log () {
|
|
32
|
-
if (verbose) {
|
|
33
|
-
console.log(`[JSEE v${VERSION}]`, ...arguments)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// const Worker = window['Worker']
|
|
38
|
-
|
|
39
|
-
// Deep clone a simple object
|
|
40
|
-
function clone (obj) {
|
|
41
|
-
// return JSON.parse(JSON.stringify(obj))
|
|
42
|
-
return Object.assign({}, obj)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript
|
|
46
|
-
function isObject (item) {
|
|
47
|
-
return (typeof item === 'object' && !Array.isArray(item) && item !== null)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function getName (code) {
|
|
51
|
-
switch (typeof code) {
|
|
52
|
-
case 'function':
|
|
53
|
-
return code.name
|
|
54
|
-
case 'string':
|
|
55
|
-
const words = code.split(' ')
|
|
56
|
-
const functionIndex = words.findIndex((word) => word == 'function')
|
|
57
|
-
const name = words[functionIndex + 1]
|
|
58
|
-
return name.includes('(') ? undefined : name
|
|
59
|
-
default:
|
|
60
|
-
return undefined
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Return input value
|
|
65
|
-
function getValue (input) {
|
|
66
|
-
if (input.type === 'group') {
|
|
67
|
-
const value = {}
|
|
68
|
-
input.elements.forEach(el => {
|
|
69
|
-
value[el.name] = getValue(el)
|
|
70
|
-
})
|
|
71
|
-
return value
|
|
72
|
-
} else {
|
|
73
|
-
return input.value
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function getModelType (model) {
|
|
78
|
-
if (model.code && typeof model.code === 'string' && model.code.split(' ').map(v => v.trim()).includes('def')) {
|
|
79
|
-
return 'py'
|
|
80
|
-
} else if (model.url) {
|
|
81
|
-
return 'post'
|
|
82
|
-
}
|
|
83
|
-
return 'function'
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Nice trick to get a function parameters by Jack Allan
|
|
87
|
-
// From: https://stackoverflow.com/a/9924463/2998960
|
|
88
|
-
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg
|
|
89
|
-
const ARGUMENT_NAMES = /([^\s,]+)/g
|
|
90
|
-
function getParamNames (func) {
|
|
91
|
-
const fnStr = func.toString().replace(STRIP_COMMENTS, '')
|
|
92
|
-
let result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES)
|
|
93
|
-
if (result === null)
|
|
94
|
-
result = []
|
|
95
|
-
return result
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function getInputs (model) {
|
|
99
|
-
if (model.code) {
|
|
100
|
-
const params = getParamNames(model.code).filter(p => !['(', ')', '#', '{', '}'].some(c => p.includes(c)))
|
|
101
|
-
log('Trying to infer inputs from params:', params)
|
|
102
|
-
return params.map(p => ({
|
|
103
|
-
'name': p,
|
|
104
|
-
'type': 'string'
|
|
105
|
-
}))
|
|
106
|
-
}
|
|
107
|
-
return []
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function getFunctionContainer (target) {
|
|
111
|
-
// Check if the number of parameters is > 1, then 'args'
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export default class JSEE {
|
|
115
|
-
constructor (params, alt1, alt2) {
|
|
116
|
-
|
|
117
|
-
// Check if JSEE was initialized with args rather than with a params object
|
|
118
|
-
if (('model' in params) || (typeof params === 'string') || (typeof params === 'function') || !(typeof alt === 'undefined')) {
|
|
119
|
-
params = {
|
|
120
|
-
'schema': params,
|
|
121
|
-
'container': alt1,
|
|
122
|
-
'verbose': alt2
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Set global verbose flag
|
|
127
|
-
verbose = !(params.verbose === false)
|
|
128
|
-
|
|
129
|
-
// Previous naming
|
|
130
|
-
params.schema = params.schema || params.config
|
|
131
|
-
|
|
132
|
-
log('Initializing JSEE with parameters: ', params)
|
|
133
|
-
this.params = params
|
|
134
|
-
this.__version__ = VERSION
|
|
135
|
-
|
|
136
|
-
// Get schema then initialize a new environment
|
|
137
|
-
switch (typeof params.schema) {
|
|
138
|
-
case 'object':
|
|
139
|
-
log('Received schema as object')
|
|
140
|
-
if (typeof params.schema.model === 'function') {
|
|
141
|
-
params.schema.model = {
|
|
142
|
-
code: params.schema.model
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
this.init(params.schema)
|
|
146
|
-
break
|
|
147
|
-
case 'function':
|
|
148
|
-
log('Received schema as function')
|
|
149
|
-
params.schema = {
|
|
150
|
-
model: {
|
|
151
|
-
code: params.schema,
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
this.init(params.schema)
|
|
155
|
-
break
|
|
156
|
-
case 'string':
|
|
157
|
-
log('Received schema as string')
|
|
158
|
-
this.schemaUrl = params.schema.indexOf('json') ? params.schema : params.schema + '.json'
|
|
159
|
-
fetch(this.schemaUrl)
|
|
160
|
-
.then(res => res.json())
|
|
161
|
-
.then(res => {
|
|
162
|
-
log('Loaded schema from url')
|
|
163
|
-
this.init(res)
|
|
164
|
-
})
|
|
165
|
-
.catch((err) => {
|
|
166
|
-
console.error(err)
|
|
167
|
-
})
|
|
168
|
-
break
|
|
169
|
-
default:
|
|
170
|
-
log('No schema provided')
|
|
171
|
-
notyf.error('No schema provided')
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
notify (txt) {
|
|
176
|
-
notyf.success(txt)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
init (schema) {
|
|
180
|
-
this.loadCode(schema).then((code) => { // -> code
|
|
181
|
-
this.initSchema(schema, code) // -> this.schema
|
|
182
|
-
this.initVue() // -> this.app, this.data
|
|
183
|
-
this.initWorker() // -> this.worker
|
|
184
|
-
this.initRender() // -> this.renderFunc
|
|
185
|
-
this.initModel() // -> this.modelFunc (depends on this.worker)
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
loadCode (schema) {
|
|
190
|
-
const initPromise = new Promise((resolve, reject) => {
|
|
191
|
-
// Unwind this ball of possible cases
|
|
192
|
-
let url = schema.model.url
|
|
193
|
-
if (url && (url.includes('.js') || url.includes('.py'))) {
|
|
194
|
-
// Update model URL if needed
|
|
195
|
-
if (!url.includes('/') && this.schemaUrl && this.schemaUrl.includes('/')) {
|
|
196
|
-
url = window.location.protocol + '//' + window.location.host + this.schemaUrl.split('/').slice(0, -1).join('/') + '/' + url
|
|
197
|
-
log(`Changed the old model URL to ${url} (based on the schema URL)`)
|
|
198
|
-
}
|
|
199
|
-
fetch(url)
|
|
200
|
-
.then(res => res.text())
|
|
201
|
-
.then(res => {
|
|
202
|
-
log('Loaded code from:', url)
|
|
203
|
-
resolve(res)
|
|
204
|
-
})
|
|
205
|
-
} else if (!(typeof schema.model.code === 'undefined')) {
|
|
206
|
-
log('Code is: schema.model.code')
|
|
207
|
-
resolve(schema.model.code)
|
|
208
|
-
} else {
|
|
209
|
-
log('No code. Probably API...')
|
|
210
|
-
resolve(undefined)
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
return initPromise
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
initSchema (schema, code) {
|
|
218
|
-
log('Initializing schema')
|
|
219
|
-
|
|
220
|
-
// Check for empty model block
|
|
221
|
-
if (typeof schema.model === 'undefined') {
|
|
222
|
-
schema.model = {}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
schema.model.code = code
|
|
226
|
-
|
|
227
|
-
// Check for super minimal config
|
|
228
|
-
// Check for worker flag
|
|
229
|
-
if (typeof schema.model.worker === 'undefined') {
|
|
230
|
-
schema.model.worker = true
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Check inputs
|
|
234
|
-
// Relies on model.code
|
|
235
|
-
// So run after possible fetching
|
|
236
|
-
if (typeof schema.inputs === 'undefined') {
|
|
237
|
-
schema.model.container = 'args'
|
|
238
|
-
schema.inputs = getInputs(schema.model)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Relies on input check
|
|
242
|
-
// Set default input type
|
|
243
|
-
schema.inputs.forEach(input => {
|
|
244
|
-
if (typeof input.type === 'undefined') {
|
|
245
|
-
input.type = 'string'
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
// Infer model type
|
|
250
|
-
if (typeof schema.model.type === 'undefined') {
|
|
251
|
-
schema.model.type = getModelType(schema.model)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Update model name if absent
|
|
255
|
-
if (typeof schema.model.name === 'undefined'){
|
|
256
|
-
if ((schema.model.url) && (schema.model.url.includes('.js'))) {
|
|
257
|
-
schema.model.name = schema.model.url.split('/').pop().split('.')[0]
|
|
258
|
-
log('Use model name from url: ', schema.model.name)
|
|
259
|
-
} else if (schema.model.code) {
|
|
260
|
-
schema.model.name = getName(schema.model.code)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// At this point we have all code in model.code or api
|
|
265
|
-
this.schema = clone(schema)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
initVue () {
|
|
269
|
-
log('Initializing VUE')
|
|
270
|
-
this.app = createVueApp(this, (container) => {
|
|
271
|
-
// Called when the app is mounted
|
|
272
|
-
// FYI "this" here refers to port object
|
|
273
|
-
this.outputsContainer = container.querySelector('#outputs')
|
|
274
|
-
this.inputsContainer = container.querySelector('#inputs')
|
|
275
|
-
this.modelContainer = container.querySelector('#model')
|
|
276
|
-
// Init overlay
|
|
277
|
-
this.overlay = new Overlay(this.inputsContainer ? this.inputsContainer : this.outputsContainer)
|
|
278
|
-
}, log)
|
|
279
|
-
this.data = this.app.$data
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
initWorker () {
|
|
283
|
-
if (this.schema.model.worker) {
|
|
284
|
-
log('Initializing Worker')
|
|
285
|
-
this.worker = new Worker()
|
|
286
|
-
this.worker.onmessage = (e) => {
|
|
287
|
-
this.overlay.hide()
|
|
288
|
-
const res = e.data
|
|
289
|
-
if ((typeof res === 'object') && (res._status)) {
|
|
290
|
-
switch (res._status) {
|
|
291
|
-
case 'loaded':
|
|
292
|
-
notyf.success('Loaded: JS model (in worker)')
|
|
293
|
-
break
|
|
294
|
-
case 'log':
|
|
295
|
-
log(...res._log)
|
|
296
|
-
break
|
|
297
|
-
}
|
|
298
|
-
} else {
|
|
299
|
-
log('Response from worker:', res)
|
|
300
|
-
this.output(res)
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
this.worker.onerror = (e) => {
|
|
304
|
-
this.overlay.hide()
|
|
305
|
-
notyf.error(e.message)
|
|
306
|
-
log('Error from worker:', e)
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
initRender () {
|
|
312
|
-
if (this.schema.render && this.schema.render.url) {
|
|
313
|
-
log('Initializing a render function')
|
|
314
|
-
let script = document.createElement('script')
|
|
315
|
-
script.src = this.schema.render.url
|
|
316
|
-
script.onload = () => {
|
|
317
|
-
notyf.success('Loaded: JS render')
|
|
318
|
-
log('Loaded JS render')
|
|
319
|
-
|
|
320
|
-
// Initializing the render (same in worker)
|
|
321
|
-
if (this.schema.render.type === 'class') {
|
|
322
|
-
log('Init render as class')
|
|
323
|
-
const renderClass = new window[this.schema.render.name]()
|
|
324
|
-
this.renderFunc = (...a) => {
|
|
325
|
-
return renderClass[this.schema.render.method || 'render'](...a)
|
|
326
|
-
}
|
|
327
|
-
} else if (this.schema.render.type === 'async-init') {
|
|
328
|
-
log('Init render function with promise')
|
|
329
|
-
window[this.schema.render.name]().then((m) => {
|
|
330
|
-
log('Async rebder init resolved: ', m)
|
|
331
|
-
this.renderFunc = m
|
|
332
|
-
})
|
|
333
|
-
} else {
|
|
334
|
-
log('Init render as function')
|
|
335
|
-
this.renderFunc = window[this.schema.render.name]
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
document.head.appendChild(script)
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
initModel () {
|
|
343
|
-
log('Initializing a model function')
|
|
344
|
-
switch (this.schema.model.type) {
|
|
345
|
-
case 'py':
|
|
346
|
-
this.initPython()
|
|
347
|
-
break
|
|
348
|
-
case 'tf':
|
|
349
|
-
this.initTF()
|
|
350
|
-
break
|
|
351
|
-
case 'function':
|
|
352
|
-
case 'class':
|
|
353
|
-
case 'async-init':
|
|
354
|
-
case 'async-function':
|
|
355
|
-
this.initJS()
|
|
356
|
-
break
|
|
357
|
-
case 'get':
|
|
358
|
-
case 'post':
|
|
359
|
-
this.initAPI()
|
|
360
|
-
break
|
|
361
|
-
default:
|
|
362
|
-
notyf.error('No type information')
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
initPython () {
|
|
368
|
-
// Add loading indicator
|
|
369
|
-
this.overlay.show()
|
|
370
|
-
let script = document.createElement('script')
|
|
371
|
-
script.src = 'https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js'
|
|
372
|
-
script.onload = async () => {
|
|
373
|
-
this.pyodide = await loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/" });
|
|
374
|
-
notyf.success('Loaded: Python')
|
|
375
|
-
log('Loaded python code:', res)
|
|
376
|
-
// Check if micropip is used
|
|
377
|
-
if (res.includes('micropip')) {
|
|
378
|
-
await this.pyodide.loadPackage('micropip')
|
|
379
|
-
log('Loaded micropip')
|
|
380
|
-
}
|
|
381
|
-
// Import packages if defined
|
|
382
|
-
if ('packages' in this.schema.model) {
|
|
383
|
-
await this.pyodide.loadPackage(this.schema.model.packages)
|
|
384
|
-
log('Loaded packages from schema')
|
|
385
|
-
} else {
|
|
386
|
-
await this.pyodide.loadPackagesFromImports(res)
|
|
387
|
-
log('Loaded packages from Python code')
|
|
388
|
-
}
|
|
389
|
-
this.overlay.hide()
|
|
390
|
-
}
|
|
391
|
-
document.head.appendChild(script)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
initJS () {
|
|
395
|
-
// 1. String input <- loaded from url or code(string)
|
|
396
|
-
// 2. Target object (can be function, class or a function with async init <- code(object)
|
|
397
|
-
// 3. Model function
|
|
398
|
-
|
|
399
|
-
// We always start from 1 or 2
|
|
400
|
-
// For window execution we go: [1 ->] 2 -> 3
|
|
401
|
-
// For worker: [2 ->] 1 -> Worker
|
|
402
|
-
|
|
403
|
-
if (this.schema.model.worker) {
|
|
404
|
-
// Worker: Initialize worker with the model
|
|
405
|
-
// 2 -> 1
|
|
406
|
-
if (typeof this.schema.model.code === 'function') {
|
|
407
|
-
log('Convert code in schema to string for WebWorker')
|
|
408
|
-
this.schema.model.code = this.schema.model.code.toString()
|
|
409
|
-
}
|
|
410
|
-
// Wrap anonymous functions
|
|
411
|
-
if (!this.schema.model.name) {
|
|
412
|
-
this.schema.model.code = `function anon () { return (${this.schema.model.code})(...arguments) }`
|
|
413
|
-
this.schema.model.name = 'anon'
|
|
414
|
-
}
|
|
415
|
-
this.worker.postMessage(this.schema.model)
|
|
416
|
-
} else {
|
|
417
|
-
// Main: Initialize model in main window
|
|
418
|
-
|
|
419
|
-
// Target here represents raw JS object (e.g. class), not the final callable function
|
|
420
|
-
let target
|
|
421
|
-
if (typeof this.schema.model.code === 'string') {
|
|
422
|
-
// 1 -> 2
|
|
423
|
-
// Danger zone
|
|
424
|
-
if (this.schema.model.name) {
|
|
425
|
-
log('Evaluating code from string (has name)')
|
|
426
|
-
target = Function(
|
|
427
|
-
`${this.schema.model.code} ;return ${this.schema.model.name}`
|
|
428
|
-
)()
|
|
429
|
-
} else {
|
|
430
|
-
log('Evaluating code from string (no name)')
|
|
431
|
-
target = eval(`(${this.schema.model.code})`) // ( ͡° ͜ʖ ͡°) YEAHVAL
|
|
432
|
-
}
|
|
433
|
-
} else {
|
|
434
|
-
target = this.schema.model.code
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Need promise here in case of async init
|
|
438
|
-
Promise.resolve(utils.getModelFuncJS(this.schema.model, target, log))
|
|
439
|
-
.then(m => {
|
|
440
|
-
this.overlay.hide()
|
|
441
|
-
notyf.success('Loaded: JS model')
|
|
442
|
-
this.modelFunc = m
|
|
443
|
-
})
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
initAPI () {
|
|
448
|
-
this.overlay.hide()
|
|
449
|
-
if (this.schema.model.worker) {
|
|
450
|
-
// Worker:
|
|
451
|
-
this.worker.postMessage(this.schema.model)
|
|
452
|
-
} else {
|
|
453
|
-
// Main:
|
|
454
|
-
this.modelFunc = utils.getModelFuncAPI(model, log)
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
initTF () {
|
|
459
|
-
let script = document.createElement('script')
|
|
460
|
-
script.src = 'dist/tf.min.js'
|
|
461
|
-
script.onload = () => {
|
|
462
|
-
log('Loaded TF.js')
|
|
463
|
-
this.overlay.hide()
|
|
464
|
-
window['tf'].loadLayersModel(this.schema.model.url).then(res => {
|
|
465
|
-
log('Loaded Tensorflow model')
|
|
466
|
-
})
|
|
467
|
-
}
|
|
468
|
-
document.head.appendChild(script)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
run () {
|
|
472
|
-
const schema = this.schema
|
|
473
|
-
const data = this.data
|
|
474
|
-
log('Running the model...')
|
|
475
|
-
// Collect input values
|
|
476
|
-
let inputValues
|
|
477
|
-
|
|
478
|
-
if (schema.model && schema.model.container && schema.model.container === 'args') {
|
|
479
|
-
log('Pass inputs as function arguments')
|
|
480
|
-
inputValues = data.inputs.map(input => getValue(input))
|
|
481
|
-
} else {
|
|
482
|
-
log('Pass inputs as object')
|
|
483
|
-
inputValues = {}
|
|
484
|
-
data.inputs.forEach(input => {
|
|
485
|
-
if (input.name) {
|
|
486
|
-
inputValues[input.name] = getValue(input)
|
|
487
|
-
}
|
|
488
|
-
})
|
|
489
|
-
}
|
|
490
|
-
log('Input values:', inputValues)
|
|
491
|
-
// We have all input values here, pass them to worker, window.modelFunc or tf
|
|
492
|
-
if (!schema.model.autorun) {
|
|
493
|
-
this.overlay.show()
|
|
494
|
-
}
|
|
495
|
-
switch (schema.model.type) {
|
|
496
|
-
case 'tf':
|
|
497
|
-
break
|
|
498
|
-
case 'py':
|
|
499
|
-
data.inputs.forEach(input => {
|
|
500
|
-
this.pyodide.globals.set(input.name, input.value);
|
|
501
|
-
})
|
|
502
|
-
this.pyodide.runPythonAsync(this.schema.model.code, () => {})
|
|
503
|
-
.then((res) => {
|
|
504
|
-
if (schema.outputs && schema.outputs.length) {
|
|
505
|
-
const resultObj = {}
|
|
506
|
-
schema.outputs.forEach(output => {
|
|
507
|
-
resultObj[output.name] = this.pyodide.globals.get(output.name).toJs()
|
|
508
|
-
})
|
|
509
|
-
this.output(resultObj)
|
|
510
|
-
} else {
|
|
511
|
-
this.output(res)
|
|
512
|
-
}
|
|
513
|
-
})
|
|
514
|
-
.catch((err) => {
|
|
515
|
-
log(err)
|
|
516
|
-
window['M'].toast({html: 'Error in code'})
|
|
517
|
-
})
|
|
518
|
-
break
|
|
519
|
-
|
|
520
|
-
case 'class':
|
|
521
|
-
case 'function':
|
|
522
|
-
case 'async-init':
|
|
523
|
-
case 'async-function':
|
|
524
|
-
case 'get':
|
|
525
|
-
case 'post':
|
|
526
|
-
if (this.schema.model.worker) {
|
|
527
|
-
this.worker.postMessage(inputValues)
|
|
528
|
-
} else {
|
|
529
|
-
// Run in main window
|
|
530
|
-
var res
|
|
531
|
-
if (this.schema.model.container === 'args') {
|
|
532
|
-
res = this.modelFunc.apply(null, inputValues)
|
|
533
|
-
} else {
|
|
534
|
-
log('Applying inputs as object')
|
|
535
|
-
res = this.modelFunc(inputValues)
|
|
536
|
-
}
|
|
537
|
-
log('modelFunc results:', res)
|
|
538
|
-
Promise.resolve(res).then(r => { this.output(r) })
|
|
539
|
-
}
|
|
540
|
-
break
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
output (res) {
|
|
545
|
-
// TODO: Think about all edge cases
|
|
546
|
-
// * No output field, but reactivity
|
|
547
|
-
this.overlay.hide()
|
|
548
|
-
|
|
549
|
-
if (typeof res === 'undefined') {
|
|
550
|
-
return
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
log('Got output results of type:', typeof res)
|
|
554
|
-
|
|
555
|
-
// Process results (res)
|
|
556
|
-
const inputNames = this.schema.inputs.map(i => i.name)
|
|
557
|
-
if (isObject(res) && Object.keys(res).every(key => inputNames.includes(key))) {
|
|
558
|
-
// Update inputs from results
|
|
559
|
-
log('Updating inputs:', Object.keys(res))
|
|
560
|
-
this.data.inputs.forEach((input, i) => {
|
|
561
|
-
if (input.name && (typeof res[input.name] !== 'undefined')) {
|
|
562
|
-
log('Updating input: ', input.name, 'with data:', res[input.name])
|
|
563
|
-
const r = res[input.name]
|
|
564
|
-
if (typeof r === 'object') {
|
|
565
|
-
Object.keys(r).forEach(k => {
|
|
566
|
-
input[k] = r[k]
|
|
567
|
-
})
|
|
568
|
-
} else {
|
|
569
|
-
input.value = r
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
})
|
|
573
|
-
} else if (this.renderFunc) {
|
|
574
|
-
// Pass results to a custom render function
|
|
575
|
-
log('Calling a render function...')
|
|
576
|
-
this.renderFunc(res)
|
|
577
|
-
} else if (Array.isArray(res) && res.length) {
|
|
578
|
-
// Result is array
|
|
579
|
-
if (this.data.outputs && this.data.outputs.length) {
|
|
580
|
-
// We have outputs defined
|
|
581
|
-
if (this.data.outputs.length === res.length) {
|
|
582
|
-
// Same length
|
|
583
|
-
this.data.outputs.forEach((output, i) => {
|
|
584
|
-
output.value = res[i]
|
|
585
|
-
})
|
|
586
|
-
} else {
|
|
587
|
-
// Different length
|
|
588
|
-
this.data.outputs[0].value = res
|
|
589
|
-
}
|
|
590
|
-
} else {
|
|
591
|
-
// Outputs are not defined
|
|
592
|
-
this.data.outputs = [{
|
|
593
|
-
'type': 'array',
|
|
594
|
-
'value': res
|
|
595
|
-
}]
|
|
596
|
-
}
|
|
597
|
-
} else if (typeof res === 'object') {
|
|
598
|
-
if (this.data.outputs && this.data.outputs.length) {
|
|
599
|
-
this.data.outputs.forEach((output, i) => {
|
|
600
|
-
if (output.name && (typeof res[output.name] !== 'undefined')) {
|
|
601
|
-
log('Updating output: ', output.name)
|
|
602
|
-
output.value = res[output.name]
|
|
603
|
-
}
|
|
604
|
-
})
|
|
605
|
-
} else {
|
|
606
|
-
this.data.outputs = [{
|
|
607
|
-
'type': 'object',
|
|
608
|
-
'value': res
|
|
609
|
-
}]
|
|
610
|
-
}
|
|
611
|
-
} else if (this.schema.outputs && this.schema.outputs.length === 1) {
|
|
612
|
-
// One output value passed as raw js object
|
|
613
|
-
this.data.outputs[0].value = res
|
|
614
|
-
} else {
|
|
615
|
-
this.data.outputs = [{
|
|
616
|
-
'type': typeof res,
|
|
617
|
-
'value': res
|
|
618
|
-
}]
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
File without changes
|
|
File without changes
|