@@ -8,8 +8,10 @@ import { Config, EdgeImpulseConfig } from './config';
88import checkNewVersions from './check-new-version' ;
99import inquirer from 'inquirer' ;
1010import {
11+ AddOrganizationTransferLearningBlockRequest ,
1112 AddOrganizationTransformationBlockRequest ,
1213 OrganizationJobsApi ,
14+ OrganizationTransformationBlockOperatesOnEnum ,
1315 UploadCustomBlockRequestTypeEnum ,
1416 UploadCustomBlockRequestTypeEnumValues
1517} from '../sdk/studio/api' ;
@@ -21,18 +23,37 @@ import WebSocket, { OPEN } from 'ws';
2123import dockerignore from '@zeit/dockerignore' ;
2224import { getCliVersion } from './init-cli-app' ;
2325import util from 'util' ;
26+ import {
27+ OrganizationTransferLearningBlockObjectDetectionLastLayerEnum ,
28+ OrganizationTransferLearningBlockOperatesOnEnum
29+ } from '../sdk/studio/model/organizationTransferLearningBlock' ;
2430
2531const version = getCliVersion ( ) ;
2632
2733type BlockConfigItem = {
2834 name : string ,
2935 description : string ,
30- type : UploadCustomBlockRequestTypeEnum ,
3136 id ?: number ,
32- port ?: number ,
3337 organizationId : number ,
34- operatesOn : 'file' | 'dataitem' | 'standalone' | undefined
35- } ;
38+ type : UploadCustomBlockRequestTypeEnum ,
39+ } & ( {
40+ type : 'transform' ,
41+ operatesOn : 'file' | 'dataitem' | 'standalone' | undefined ,
42+ transformMountpoints : {
43+ bucketId : number ;
44+ mountPoint : string ;
45+ } [ ] | undefined ,
46+ } | {
47+ type : 'transferLearning' ,
48+ tlOperatesOn ?: OrganizationTransferLearningBlockOperatesOnEnum ,
49+ tlObjectDetectionLastLayer ?: OrganizationTransferLearningBlockObjectDetectionLastLayerEnum ,
50+ } | {
51+ type : 'deploy' ,
52+ deployCategory ?: 'library' | 'firmware' ,
53+ } | {
54+ type : 'dsp' ,
55+ port ?: number ,
56+ } ) ;
3657
3758type BlockConfigV1 = {
3859 version : 1 ,
@@ -200,6 +221,10 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
200221 {
201222 name : 'DSP block' ,
202223 value : 'dsp'
224+ } ,
225+ {
226+ name : 'Transfer learning block' ,
227+ value : 'transferLearning'
203228 }
204229 ] ,
205230 name : 'type' ,
@@ -212,13 +237,21 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
212237 let blockName : string | undefined ;
213238 let blockDescription : string | undefined ;
214239 let blockOperatesOn : 'file ' | 'dataitem' | 'standalone' | undefined ;
240+ let blockTlOperatesOn : OrganizationTransferLearningBlockOperatesOnEnum | undefined ;
241+ let blockTlObjectDetectionLastLayer : OrganizationTransferLearningBlockObjectDetectionLastLayerEnum | undefined ;
242+ let transformMountpoints : {
243+ bucketId : number ;
244+ mountPoint : string ;
245+ } [] | undefined;
246+
247+ let deployCategory: 'library' | 'firmware' | undefined;
215248
216249 // Fetch all relevant existing blocks so the user can select an existing block to update
217250 let existingBlocks: {
218251 name : string , value : number , block : {
219252 description : string , name : string , operatesOn : 'file' | 'dataitem' | 'standalone' | undefined }
220253 } [] = [];
221- if (blockTypeInqRes.type === 'transform') {
254+ if (blockType === 'transform') {
222255 let blocks = await config . api . organizationBlocks . listOrganizationTransformationBlocks ( organizationId ) ;
223256 if ( blocks . body && blocks . body . transformationBlocks && blocks . body . transformationBlocks . length > 0 ) {
224257 existingBlocks = blocks . body . transformationBlocks . map ( p => (
@@ -230,7 +263,7 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
230263 ) ) ;
231264 }
232265 }
233- else if (blockTypeInqRes.type === 'deploy') {
266+ else if (blockType === 'deploy') {
234267 let blocks = await config . api . organizationBlocks . listOrganizationDeployBlocks ( organizationId ) ;
235268 if ( blocks . body && blocks . body . deployBlocks && blocks . body . deployBlocks . length > 0 ) {
236269 existingBlocks = blocks . body . deployBlocks . map ( p => (
@@ -242,7 +275,7 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
242275 ) ) ;
243276 }
244277 }
245- else if (blockTypeInqRes.type === 'dsp') {
278+ else if (blockType === 'dsp') {
246279 let blocks = await config . api . organizationBlocks . listOrganizationDspBlocks ( organizationId ) ;
247280 if ( blocks . body && blocks . body . dspBlocks && blocks . body . dspBlocks . length > 0 ) {
248281 existingBlocks = blocks . body . dspBlocks . map ( p => (
@@ -254,6 +287,18 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
254287 ) ) ;
255288 }
256289 }
290+ else if (blockType === 'transferLearning') {
291+ let blocks = await config . api . organizationBlocks . listOrganizationTransferLearningBlocks ( organizationId ) ;
292+ if ( blocks . body && blocks . body . transferLearningBlocks && blocks . body . transferLearningBlocks . length > 0 ) {
293+ existingBlocks = blocks . body . transferLearningBlocks . map ( p => (
294+ {
295+ name : p . name ,
296+ value : p . id ,
297+ block : { description : p . description , name : p . name , operatesOn : undefined }
298+ }
299+ ) ) ;
300+ }
301+ }
257302 else {
258303 console . error ( `Invalid block type: ${ blockTypeInqRes . type } ` ) ;
259304 process . exit ( 1 ) ;
@@ -299,7 +344,7 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
299344 let defaultName = 'My new block';
300345 let defaultDescription: string | undefined;
301346
302- if (blockTypeInqRes.type === 'dsp') {
347+ if (blockType === 'dsp') {
303348 let paramsFile = Path . join ( process . cwd ( ) , 'parameters.json' ) ;
304349 if ( paramsFile ) {
305350 try {
@@ -343,8 +388,7 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
343388 if (blockDescription === '') blockDescription = blockName;
344389 }
345390
346-
347- if ( createOrUpdateInqRes === 'create' && blockTypeInqRes . type === 'transform' ) {
391+ if ( createOrUpdateInqRes === 'create' && blockType === 'transform' ) {
348392 blockOperatesOn = < 'file' | 'dataitem' | 'standalone' > ( await inquirer . prompt ( [ {
349393 type : 'list' ,
350394 name : 'operatesOn' ,
@@ -364,6 +408,100 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
364408 ] ,
365409 message : 'What type of data does this block operate on?' ,
366410 } ] ) ) . operatesOn ;
411+
412+ let buckets = await config . api . organizationData . listOrganizationBuckets ( organizationId ) ;
413+ if ( buckets . body && buckets . body . buckets && buckets . body . buckets . length > 0 ) {
414+ transformMountpoints = ( < string [ ] > ( await inquirer . prompt ( [ {
415+ type : 'checkbox' ,
416+ name : 'buckets' ,
417+ choices : buckets . body . buckets . map ( x => {
418+ return {
419+ name : x . name ,
420+ value : x . id . toString ( )
421+ } ;
422+ } ) ,
423+ message : 'Which buckets do you want to mount into this block ' +
424+ '(will be mounted under /mnt/s3fs/BUCKET_NAME, you can change these mount points in the Studio)?' ,
425+ } ] ) ) . buckets ) . map ( y => {
426+ let b = buckets . body . buckets ?. find ( z => z . id === Number ( y ) ) ;
427+ return {
428+ bucketId : Number ( y ) ,
429+ mountPoint : b ? ( '/mnt/s3fs/' + b ?. name ) : '' ,
430+ } ;
431+ } ) . filter ( x => ! ! x . mountPoint && ! isNaN ( x . bucketId ) ) ;
432+ }
433+ }
434+
435+ if ( createOrUpdateInqRes === 'create' && blockType === 'transferLearning' ) {
436+ blockTlOperatesOn = < OrganizationTransferLearningBlockOperatesOnEnum > (await inquirer.prompt([{
437+ type : 'list' ,
438+ name : 'operatesOn' ,
439+ choices : [
440+ {
441+ name : 'Object Detection' ,
442+ value : 'object_detection'
443+ } ,
444+ {
445+ name : 'Image classification' ,
446+ value : 'image'
447+ } ,
448+ {
449+ name : 'Audio classification' ,
450+ value : 'audio'
451+ } ,
452+ {
453+ name : 'Other (classification)' ,
454+ value : 'other'
455+ } ,
456+ {
457+ name : 'Other (regression)' ,
458+ value : 'regression'
459+ } ,
460+ ] ,
461+ message : 'What type of data does this model operate on?' ,
462+ } ])).operatesOn;
463+
464+ if (blockTlOperatesOn === 'object_detection') {
465+ blockTlObjectDetectionLastLayer = < OrganizationTransferLearningBlockObjectDetectionLastLayerEnum >
466+ (await inquirer.prompt([{
467+ type : 'list' ,
468+ name : 'lastLayer' ,
469+ choices : [
470+ {
471+ name : 'MobileNet SSD' ,
472+ value : 'mobilenet_ssd'
473+ } ,
474+ {
475+ name : 'Edge Impulse FOMO' ,
476+ value : 'fomo'
477+ } ,
478+ {
479+ name : 'YOLOv5' ,
480+ value : 'yolov5'
481+ } ,
482+ ] ,
483+ message : `What's the last layer of this object detection model?` ,
484+ } ])).lastLayer;
485+ }
486+ }
487+
488+
489+ if ( createOrUpdateInqRes === 'create ' && blockType === 'deploy ') {
490+ deployCategory = < 'library' | 'firmware' > ( await inquirer . prompt ( [ {
491+ type : 'list' ,
492+ name : 'category' ,
493+ choices : [
494+ {
495+ name : 'Library' ,
496+ value : 'library'
497+ } ,
498+ {
499+ name : 'Firmware' ,
500+ value : 'firmware'
501+ }
502+ ] ,
503+ message : 'Where to show this deployment block in the UI?' ,
504+ } ] ) ) . category ;
367505 }
368506
369507 // Create & write the config
@@ -375,15 +513,23 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
375513 description : blockDescription ,
376514 organizationId ,
377515 operatesOn : blockOperatesOn ,
516+ tlObjectDetectionLastLayer : blockTlObjectDetectionLastLayer ,
517+ tlOperatesOn : blockTlOperatesOn ,
518+ deployCategory : deployCategory ,
519+ transformMountpoints : transformMountpoints ,
378520 } : {
379521 name : blockName ,
380522 type : blockType ,
381523 description : blockDescription ,
382524 organizationId ,
383525 operatesOn : blockOperatesOn ,
526+ tlObjectDetectionLastLayer : blockTlObjectDetectionLastLayer ,
527+ tlOperatesOn : blockTlOperatesOn ,
528+ deployCategory : deployCategory ,
529+ transformMountpoints : transformMountpoints ,
384530 } ;
385531
386- console . log ( 'Creating block with config:' , globalCurrentBlockConfig ) ;
532+ // console.log('Creating block with config:', globalCurrentBlockConfig);
387533 await writeConfigFile();
388534
389535 const hasDockerFile = await exists(dockerfilePath);
@@ -407,10 +553,12 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
407553 templateSourcePath =
408554 'https://github.com/edgeimpulse/template-transformation-block-python/archive/main.zip' ;
409555 directoryRoot = 'template-transformation-block-python-main/' ;
410- } else if ( blockType === 'deploy' ) {
556+ }
557+ else if ( blockType === 'deploy' ) {
411558 templateSourcePath = 'https://github.com/edgeimpulse/template-deployment-block/archive/main.zip' ;
412559 directoryRoot = 'template-deployment-block-main/' ;
413- } else {
560+ }
561+ else {
414562 console .error ( `Invalid block type: ${ blockType } ` ) ;
415563 process . exit ( 1 ) ;
416564 }
@@ -501,14 +649,22 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
501649 indMetadata : true ,
502650 cliArguments : '' ,
503651 operatesOn : currentBlockConfig . operatesOn || 'file' ,
504- additionalMountPoints : [ ]
652+ additionalMountPoints : ( currentBlockConfig . transformMountpoints || [ ] ) . map ( x => {
653+ return {
654+ type : 'bucket' ,
655+ bucketId : x . bucketId ,
656+ mountPoint : x . mountPoint ,
657+ } ;
658+ } ) ,
505659 } ;
506660 newResponse = await config . api . organizationBlocks . addOrganizationTransformationBlock (
507661 organizationId , newBlockObject ) ;
508662 }
509663 else if (currentBlockConfig.type === 'deploy') {
510664 newResponse = await config . api . organizationBlocks . addOrganizationDeployBlock (
511- organizationId , currentBlockConfig . name , '' , currentBlockConfig . description , '' ) ;
665+ organizationId , currentBlockConfig . name , '' , currentBlockConfig . description , '' ,
666+ undefined , undefined , undefined , undefined , undefined , undefined , undefined , undefined ,
667+ undefined , undefined , currentBlockConfig . deployCategory ) ;
512668 }
513669 else if (currentBlockConfig.type === 'dsp') {
514670 if ( currentBlockConfig . type === 'dsp' && typeof currentBlockConfig . port !== 'number' ) {
@@ -556,8 +712,20 @@ let globalCurrentBlockConfig: BlockConfigV1 | undefined;
556712 port : currentBlockConfig . port || 80 ,
557713 } );
558714 }
715+ else if ( currentBlockConfig . type === 'transferLearning ') {
716+ const newBlockObject : AddOrganizationTransferLearningBlockRequest = {
717+ name : currentBlockConfig . name ,
718+ description : currentBlockConfig . description ,
719+ dockerContainer : '' ,
720+ objectDetectionLastLayer : currentBlockConfig . tlObjectDetectionLastLayer ,
721+ operatesOn : currentBlockConfig . tlOperatesOn || 'image' ,
722+ } ;
723+ newResponse = await config . api . organizationBlocks . addOrganizationTransferLearningBlock (
724+ organizationId , newBlockObject ) ;
725+ }
559726 else {
560- console . error ( `Unable to upload your block - unknown block type: ${ currentBlockConfig . type } ` ) ;
727+ console . error ( `Unable to upload your block - unknown block type: ` +
728+ `${ ( < any > currentBlockConfig).type} `);
561729 process.exit(1);
562730 }
563731 if (!newResponse.body.success) {
0 commit comments