diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 6bdadc3945..edda54340f 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -36,8 +36,9 @@ jobs:
- name: Install dependencies
run: |
pip install --upgrade pip
- pip install ./submarine-sdk/pysubmarine/.
- pip install -q tensorflow==${{ matrix.tf-version }}
+ pip install --no-cache-dir tensorflow==${{ matrix.tf-version }}
+ pip install --no-cache-dir torch==1.5.0
+ pip install --no-cache-dir ./submarine-sdk/pysubmarine/.
pip install -r ./submarine-sdk/pysubmarine/github-actions/test-requirements.txt
pip install -r ./submarine-sdk/pysubmarine/github-actions/lint-requirements.txt
- name: Check python sdk code style
@@ -47,5 +48,3 @@ jobs:
- name: Test with pytest
run: |
pytest --cov=submarine -vs
-
-
diff --git a/submarine-sdk/pysubmarine/setup.py b/submarine-sdk/pysubmarine/setup.py
index 35b036e88b..b339533187 100644
--- a/submarine-sdk/pysubmarine/setup.py
+++ b/submarine-sdk/pysubmarine/setup.py
@@ -39,7 +39,7 @@
'certifi >= 14.05.14',
'python-dateutil >= 2.5.3',
'pyarrow==0.17.0',
- 'torch>=1.4.0',
+ 'torch>=1.5.0',
],
classifiers=[
'Intended Audience :: Developers',
diff --git a/submarine-sdk/pysubmarine/submarine/tracking/utils.py b/submarine-sdk/pysubmarine/submarine/tracking/utils.py
index ce19d30451..95a3a285bd 100644
--- a/submarine-sdk/pysubmarine/submarine/tracking/utils.py
+++ b/submarine-sdk/pysubmarine/submarine/tracking/utils.py
@@ -29,10 +29,11 @@
_TF_CONFIG = "TF_CONFIG"
_CLUSTER_SPEC = "CLUSTER_SPEC"
-_TASK_INDEX = "TASK_INDEX"
_JOB_NAME = "JOB_NAME"
+_TYPE = "type"
+_TASK = "task"
+_INDEX = "index"
_RANK = "RANK"
-_TASK = "TASK"
# Extra environment variables which take precedence for setting the basic/bearer
# auth on http requests.
@@ -98,14 +99,14 @@ def get_worker_index():
if env.get_env(_TF_CONFIG) is not None:
tf_config = json.loads(os.environ.get(_TF_CONFIG))
task_config = tf_config.get(_TASK)
- task_type = task_config.get(_JOB_NAME)
- task_index = task_config.get(_TASK_INDEX)
+ task_type = task_config.get(_TYPE)
+ task_index = task_config.get(_INDEX)
worker_index = task_type + '-' + str(task_index)
elif env.get_env(_CLUSTER_SPEC) is not None:
cluster_spec = json.loads(os.environ.get(_CLUSTER_SPEC))
task_config = cluster_spec.get(_TASK)
task_type = task_config.get(_JOB_NAME)
- task_index = task_config.get(_TASK_INDEX)
+ task_index = task_config.get(_INDEX)
worker_index = task_type + '-' + str(task_index)
# Get PyTorch worker index
elif env.get_env(_RANK) is not None:
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java
index 346f77c8a1..28950ce856 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experiment/ExperimentManager.java
@@ -28,6 +28,7 @@
import javax.ws.rs.core.Response.Status;
+import org.apache.submarine.commons.utils.SubmarineConfiguration;
import org.apache.submarine.commons.utils.exception.SubmarineRuntimeException;
import org.apache.submarine.server.SubmarineServer;
import org.apache.submarine.server.SubmitterManager;
@@ -36,6 +37,7 @@
import org.apache.submarine.server.api.Submitter;
import org.apache.submarine.server.api.experiment.ExperimentLog;
import org.apache.submarine.server.api.spec.ExperimentSpec;
+import org.apache.submarine.server.rest.RestConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -85,13 +87,33 @@ private ExperimentManager(Submitter submitter) {
*/
public Experiment createExperiment(ExperimentSpec spec) throws SubmarineRuntimeException {
checkSpec(spec);
+
+ // Submarine sdk will get experimentID and JDBC URL from environment variables in each worker,
+ // and then log experiment metrics and parameters to submarine server
+ ExperimentId id = generateExperimentId();
+ String url = getSQLAlchemyURL();
+ spec.getMeta().getEnvVars().put(RestConstants.JOB_ID, id.toString());
+ spec.getMeta().getEnvVars().put(RestConstants.SUBMARINE_TRACKING_URI, url);
+
Experiment experiment = submitter.createExperiment(spec);
- experiment.setExperimentId(generateExperimentId());
+ experiment.setExperimentId(id);
+
+ spec.getMeta().getEnvVars().remove(RestConstants.JOB_ID);
+ spec.getMeta().getEnvVars().remove(RestConstants.SUBMARINE_TRACKING_URI);
experiment.setSpec(spec);
cachedExperimentMap.putIfAbsent(experiment.getExperimentId().toString(), experiment);
return experiment;
}
+ private String getSQLAlchemyURL() {
+ SubmarineConfiguration conf = SubmarineConfiguration.getInstance();
+ String jdbcUrl = conf.getJdbcUrl();
+ jdbcUrl = jdbcUrl.substring(jdbcUrl.indexOf("//") + 2, jdbcUrl.indexOf("?"));
+ String jdbcUserName = conf.getJdbcUserName();
+ String jdbcPassword = conf.getJdbcPassword();
+ return "mysql+pymysql://" + jdbcUserName + ":" + jdbcPassword + "@" + jdbcUrl;
+ }
+
private ExperimentId generateExperimentId() {
return ExperimentId.newInstance(SubmarineServer.getServerTimeStamp(),
experimentCounter.incrementAndGet());
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
index 96e67f5dcf..699109cab8 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
@@ -21,22 +21,32 @@
public class RestConstants {
public static final String V1 = "v1";
+
public static final String EXPERIMENT = "experiment";
+
public static final String ID = "id";
+
+ public static final String JOB_ID = "JOB_ID";
+
+ public static final String SUBMARINE_TRACKING_URI = "SUBMARINE_TRACKING_URI";
+
public static final String PING = "ping";
+
public static final String MEDIA_TYPE_YAML = "application/yaml";
+
public static final String CHARSET_UTF8 = "charset=utf-8";
public static final String METASTORE = "metastore";
public static final String CLUSTER = "cluster";
+
public static final String ADDRESS = "address";
public static final String NODES = "nodes";
+
public static final String NODE = "node";
public static final String LOGS = "logs";
-
/**
* Environment
*/
diff --git a/submarine-workbench/workbench-web-ng/angular.json b/submarine-workbench/workbench-web-ng/angular.json
index a1dab110ee..3560eb4257 100644
--- a/submarine-workbench/workbench-web-ng/angular.json
+++ b/submarine-workbench/workbench-web-ng/angular.json
@@ -31,9 +31,11 @@
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
},
- { "glob": "**/*",
- "input": "./node_modules/ngx-monaco-editor/assets/monaco",
- "output": "/assets/monaco/" }
+ {
+ "glob": "**/*",
+ "input": "./node_modules/ngx-monaco-editor/assets/monaco",
+ "output": "/assets/monaco/"
+ }
],
"styles": [
"src/theme.less",
@@ -136,5 +138,8 @@
}
}
},
- "defaultProject": "workbench-web-ng"
-}
+ "defaultProject": "workbench-web-ng",
+ "cli": {
+ "analytics": false
+ }
+}
\ No newline at end of file
diff --git a/submarine-workbench/workbench-web-ng/package.json b/submarine-workbench/workbench-web-ng/package.json
index 4c23700950..72b3a6b1a6 100644
--- a/submarine-workbench/workbench-web-ng/package.json
+++ b/submarine-workbench/workbench-web-ng/package.json
@@ -17,7 +17,7 @@
"@angular/animations": "~8.2.9",
"@angular/common": "~8.2.9",
"@angular/compiler": "~8.2.9",
- "@angular/core": "~8.2.9",
+ "@angular/core": "^8.2.14",
"@angular/forms": "~8.2.9",
"@angular/platform-browser": "~8.2.9",
"@angular/platform-browser-dynamic": "~8.2.9",
@@ -25,7 +25,7 @@
"@swimlane/ngx-charts": "^13.0.1",
"date-fns": "^2.6.0",
"lint-staged": "^10.2.2",
- "lodash": "^4.17.15",
+ "lodash": "^4.17.19",
"md5": "^2.2.1",
"ng-zorro-antd": "8.1.2",
"ngx-monaco-editor": "^8.0.0",
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html
index 83e25e15eb..3f7cac033b 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.html
@@ -17,22 +17,26 @@
~ under the License.
-->
-
-
+
+
+
+ {{ yAxisLabel }}
+
+
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss
index 4e938821ec..f4950588eb 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.scss
@@ -17,7 +17,8 @@
* under the License.
*/
- #chartsDiv {
- background-color: white;
- height: 500px;
- }
+#chartsTable {
+ background-color: white;
+ height: 90vh;
+ overflow: auto;
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts
index af6ff40001..6c0828bd9d 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/charts.component.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
-import { test_data } from './data';
+import { Component, Input, OnInit, SimpleChanges } from '@angular/core';
@Component({
selector: 'submarine-charts',
@@ -26,28 +25,24 @@ import { test_data } from './data';
styleUrls: ['./charts.component.scss']
})
export class ChartsComponent implements OnInit {
- title = 'Metrics';
+ @Input() workerIndex;
+ @Input() metricData;
- data: any[];
- view: any[] = [800, 500];
- legend: boolean = true;
+ title = 'Metrics';
+ podMetrics = {};
+ view: any[] = [1000, 300];
+ legend: boolean = false;
showLabels: boolean = true;
animations: boolean = true;
xAxis: boolean = true;
yAxis: boolean = true;
showYAxisLabel: boolean = true;
showXAxisLabel: boolean = true;
- xAxisLabel: string = 'Epoch';
- yAxisLabel: string = 'Percent';
- timeline: boolean = true;
-
- colorScheme = {
- domain: ['#5AA454', '#E44D25', '#CFC0BB', '#7aa3e5']
- };
-
- constructor() {
- this.data = Object.assign([], test_data);
- }
+ xAxisLabel: string = 'Time (s)';
+ yAxisLabels = [];
+ timeline: boolean = false;
+ colorScheme = ['cool', 'fire', 'flame', 'air', 'forest', 'neons', 'ocean'];
+ constructor() {}
onSelect(data): void {
console.log('Item clicked', JSON.parse(JSON.stringify(data)));
@@ -62,4 +57,40 @@ export class ChartsComponent implements OnInit {
}
ngOnInit() {}
+
+ ngOnChanges(chg: SimpleChanges) {
+ this.podMetrics = {};
+ this.yAxisLabels = [];
+ this.fetchMetric();
+ }
+ fetchMetric() {
+ if (this.metricData === undefined) {
+ return;
+ }
+ let key = '';
+ let metrics = [];
+ this.metricData.forEach((data) => {
+ if (data.workerIndex === undefined) {
+ return;
+ }
+ if (this.workerIndex.indexOf(data.workerIndex) >= 0) {
+ if (data.key !== key && metrics.length > 0) {
+ this.yAxisLabels.push(key);
+ this.podMetrics[key] = [];
+ this.podMetrics[key].push({ name: key, series: metrics });
+ metrics = [];
+ }
+ key = data.key;
+ const d = new Date(0);
+ d.setUTCMilliseconds(data.timestamp);
+ const metric = { name: d, value: data.value };
+ metrics.push(metric);
+ }
+ });
+ if (metrics.length > 0) {
+ this.yAxisLabels.push(key);
+ this.podMetrics[key] = [];
+ this.podMetrics[key].push({ name: key, series: metrics });
+ }
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts
deleted file mode 100644
index 60e4493f4f..0000000000
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/charts/data.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-export var test_data = [
- {
- name: 'loss',
- series: [
- {
- name: 'Epoch1',
- value: 0.204
- },
- {
- name: 'Epoch2',
- value: 0.1799
- },
- {
- name: 'Epoch3',
- value: 0.1772
- }
- ]
- },
-
- {
- name: 'accuracy',
- series: [
- {
- name: 'Epoch1',
- value: 0.9356
- },
- {
- name: 'Epoch2',
- value: 0.9406
- },
- {
- name: 'Epoch3',
- value: 0.944
- }
- ]
- },
-
- {
- name: 'val_loss',
- series: [
- {
- name: 'Epoch1',
- value: 0.2655
- },
- {
- name: 'Epoch2',
- value: 0.1578
- },
- {
- name: 'Epoch3',
- value: 0.1471
- }
- ]
- },
- {
- name: 'val_accuracy',
- series: [
- {
- name: 'Epoch1',
- value: 0.907
- },
- {
- name: 'Epoch2',
- value: 0.945
- },
- {
- name: 'Epoch3',
- value: 0.959
- }
- ]
- }
-];
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html
index 3b16e6a6b7..a076514d1d 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/experiment-info.component.html
@@ -103,7 +103,7 @@
-
+
{
- if (data.workerIndex == this.workerIndex) {
+ if (this.workerIndex.indexOf(data.workerIndex) >= 0) {
this.podParam.push(data);
}
});
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss
index 6ee12d18ec..38aeebab08 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.scss
@@ -17,7 +17,8 @@
* under the License.
*/
- #metricTable {
- background-color: white;
- height: 500px;
+#metricTable {
+ background-color: white;
+ height: 90vh;
+ overflow:auto;
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts
index 7ba3859e32..70720de704 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/metrics/metrics.component.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Component, OnInit, Input, SimpleChanges } from '@angular/core';
+import { Component, Input, OnInit, SimpleChanges } from '@angular/core';
@Component({
selector: 'submarine-metrics',
@@ -36,7 +36,7 @@ export class MetricsComponent implements OnInit {
ngOnChanges(chg: SimpleChanges) {
this.podMetric.length = 0;
this.metricData.forEach((data) => {
- if (data.workerIndex == this.workerIndex) {
+ if (this.workerIndex.indexOf(data.workerIndex) >= 0) {
this.podMetric.push(data);
}
});
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss
index b1f72a4520..2fa14b3ee6 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.scss
@@ -21,6 +21,6 @@
background-color: whitesmoke;
color: black;
padding: 10px;
- height:500px;
+ height:90vh;
overflow:auto;
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts
index 69465e2427..2332200380 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment-info/outputs/outputs.component.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Component, OnInit, Input, SimpleChanges } from '@angular/core';
+import { Component, Input, OnInit, SimpleChanges } from '@angular/core';
@Component({
selector: 'submarine-outputs',
@@ -36,7 +36,7 @@ export class OutputsComponent implements OnInit {
ngOnChanges(chg: SimpleChanges) {
this.podLogArr.forEach((item) => {
- if (item.podName == this.podName) {
+ if (item.podName === this.podName) {
this.podLog = item.podLog;
}
});
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts
index e360ccef32..4e78f7f9e1 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.component.ts
@@ -21,11 +21,10 @@ import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
import { ExperimentInfo } from '@submarine/interfaces/experiment-info';
+import { ExperimentSpec, Specs, SpecEnviroment, SpecMeta } from '@submarine/interfaces/experiment-spec';
import { ExperimentService } from '@submarine/services/experiment.service';
import { ExperimentFormService } from '@submarine/services/experiment.validator.service';
import { NzMessageService } from 'ng-zorro-antd';
-import { SpecMeta, Specs, SpecEnviroment, ExperimentSpec } from '@submarine/interfaces/experiment-spec';
-import { stringify } from 'querystring';
@Component({
selector: 'submarine-experiment',
@@ -342,16 +341,16 @@ export class ExperimentComponent implements OnInit {
fetchExperimentList() {
this.experimentService.fetchExperimentList().subscribe((list) => {
this.experimentList = list;
- var currentTime = new Date();
+ const currentTime = new Date();
this.experimentList.forEach((item) => {
- if (item.status == 'Succeeded') {
- var finTime = new Date(item.finishedTime);
- var runTime = new Date(item.runningTime);
- var result = (finTime.getTime() - runTime.getTime()) / 1000;
+ if (item.status === 'Succeeded') {
+ const finTime = new Date(item.finishedTime);
+ const runTime = new Date(item.runningTime);
+ const result = (finTime.getTime() - runTime.getTime()) / 1000;
item.duration = this.experimentService.durationHandle(result);
} else {
- var runTime = new Date(item.runningTime);
- var result = (currentTime.getTime() - runTime.getTime()) / 1000;
+ const runTime = new Date(item.runningTime);
+ const result = (currentTime.getTime() - runTime.getTime()) / 1000;
item.duration = this.experimentService.durationHandle(result);
}
});
@@ -407,14 +406,14 @@ export class ExperimentComponent implements OnInit {
}
reloadCheck() {
- /*
- When reload in info page, ths experimentId will turn into undifined, it will cause breadcrumb miss experimentId.
+ /*
+ When reload in info page, ths experimentId will turn into undefined, it will cause breadcrumb miss experimentId.
Location.pathname -> /workbench/experiment/info/{experimentID}
So slice out experimentId string from location.pathname to reassign experimentId.
*/
- if (location.pathname != '/workbench/experiment') {
- var sliceString = new String('/workbench/experiment/info');
- this.experimentID = location.pathname.slice(sliceString.length);
+ if (location.pathname !== '/workbench/experiment') {
+ const sliceString = String('/workbench/experiment/info');
+ this.experimentID = location.pathname.slice(sliceString.length + 1);
}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts
index 1df383efe8..966f647235 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/experiment/experiment.module.ts
@@ -1,14 +1,13 @@
+import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
-import { ReactiveFormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { NgxChartsModule } from '@swimlane/ngx-charts';
+import { NgZorroAntdModule } from 'ng-zorro-antd';
+import { ChartsComponent } from './experiment-info/charts/charts.component';
import { ExperimentInfoComponent } from './experiment-info/experiment-info.component';
-import { MetricsComponent } from './experiment-info/metrics/metrics.component';
import { HyperParamsComponent } from './experiment-info/hyper-params/hyper-params.component';
-import { ChartsComponent } from './experiment-info/charts/charts.component';
+import { MetricsComponent } from './experiment-info/metrics/metrics.component';
import { OutputsComponent } from './experiment-info/outputs/outputs.component';
-import { NgZorroAntdModule } from 'ng-zorro-antd';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { NgxChartsModule } from '@swimlane/ngx-charts';
/*
* Licensed to the Apache Software Foundation (ASF) under one
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts
index 393eee381f..30f5735dc9 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/services/experiment.service.ts
@@ -21,10 +21,10 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Rest } from '@submarine/interfaces';
import { ExperimentInfo } from '@submarine/interfaces/experiment-info';
-import { BaseApiService } from '@submarine/services/base-api.service';
-import { of, Observable, throwError } from 'rxjs';
-import { switchMap, catchError, map } from 'rxjs/operators';
import { ExperimentSpec } from '@submarine/interfaces/experiment-spec';
+import { BaseApiService } from '@submarine/services/base-api.service';
+import { of, throwError, Observable } from 'rxjs';
+import { catchError, map, switchMap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
diff --git a/submarine-workbench/workbench-web-ng/tsconfig.json b/submarine-workbench/workbench-web-ng/tsconfig.json
index da63e8bba2..cc513fce36 100644
--- a/submarine-workbench/workbench-web-ng/tsconfig.json
+++ b/submarine-workbench/workbench-web-ng/tsconfig.json
@@ -28,5 +28,6 @@
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
- }
+ },
+ "moduleResolution": "node"
}