├── .github
└── workflows
│ └── deploy-ui.yml
├── .gitignore
├── .gitmodules
├── .idea
└── dictionaries
│ └── varuna.xml
├── .labml.yaml
├── Makefile
├── cpp
├── .gitignore
└── labml_fast_merge
│ ├── labml_fast_merge.pyx
│ └── setup.py
├── docs
├── installation.rst
└── perfomance.md
├── images
├── cover.png
├── experiment.png
└── labml-app.gif
├── license
├── package-lock.json
├── package.json
├── readme.md
├── server
├── MANIFEST.in
├── Pipfile
├── Pipfile.lock
├── gunicorn.conf.py
├── labml_app
│ ├── .gitignore
│ ├── __init__.py
│ ├── analyses
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── computers
│ │ │ ├── battery.py
│ │ │ ├── cpu.py
│ │ │ ├── disk.py
│ │ │ ├── gpu.py
│ │ │ ├── memory.py
│ │ │ ├── network.py
│ │ │ └── process.py
│ │ ├── experiments
│ │ │ ├── comparison.py
│ │ │ ├── gradients.py
│ │ │ ├── hyperparameters.py
│ │ │ ├── metrics.py
│ │ │ ├── outputs.py
│ │ │ └── parameters.py
│ │ ├── helper.py
│ │ ├── preferences.py
│ │ ├── series.py
│ │ └── series_collection.py
│ ├── analyses_settings.sample.py
│ ├── auth
│ │ └── __init__.py
│ ├── block_uuids.sample.py
│ ├── db
│ │ ├── __init__.py
│ │ ├── app_token.py
│ │ ├── blocked_uuids.py
│ │ ├── computer.py
│ │ ├── job.py
│ │ ├── project.py
│ │ ├── run.py
│ │ ├── session.py
│ │ ├── status.py
│ │ └── user.py
│ ├── docs.py
│ ├── enums.py
│ ├── flask_app.py
│ ├── handlers.py
│ ├── logger
│ │ ├── __init__.py
│ │ └── logger.py
│ ├── scripts
│ │ ├── clean_ups.py
│ │ ├── db_checks.py
│ │ └── temp_cleans.py
│ ├── settings.sample.py
│ └── utils
│ │ ├── __init__.py
│ │ ├── mix_panel.py
│ │ └── slack.py
├── setup.py
└── unit_tests
│ └── series.py
├── tsconfig.json
└── ui
├── images
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── favicon.png
├── lab_logo.png
├── safari-pinned-tab.svg
└── tf_Icon.png
└── src
├── analyses
├── analyses.ts
├── experiments
│ ├── activations
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── comaprison
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── configs
│ │ ├── card.ts
│ │ ├── components.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── grads
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── hyper_params
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── logger
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── metrics
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── params
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── run_header
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── stderror
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ └── stdout
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
├── helpers.ts
├── sessions
│ ├── battery
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── configs
│ │ ├── card.ts
│ │ ├── components.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── cpu
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── disk
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── gpu
│ │ ├── cache.ts
│ │ ├── index.ts
│ │ ├── memory_card.ts
│ │ ├── memory_view.ts
│ │ ├── power_card.ts
│ │ ├── power_view.ts
│ │ ├── temp_card.ts
│ │ ├── temp_view.ts
│ │ ├── util_card.ts
│ │ ├── util_view.ts
│ │ └── utils.ts
│ ├── memory
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── network
│ │ ├── cache.ts
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
│ ├── process
│ │ ├── cache.ts
│ │ ├── cache_helper.ts
│ │ ├── card.ts
│ │ ├── detail_view.ts
│ │ ├── index.ts
│ │ ├── process_list.ts
│ │ ├── types.ts
│ │ └── view.ts
│ └── session_header
│ │ ├── card.ts
│ │ ├── index.ts
│ │ └── view.ts
└── types.ts
├── app.ts
├── cache
└── cache.ts
├── components
├── badge.ts
├── buttons.ts
├── charts
│ ├── axis.ts
│ ├── chart_colors.ts
│ ├── chart_gradients.ts
│ ├── compare_lines
│ │ ├── chart.ts
│ │ └── plot.ts
│ ├── compare_spark_lines
│ │ ├── chart.ts
│ │ └── spark_line.ts
│ ├── constants.ts
│ ├── custom_lines
│ │ ├── chart.ts
│ │ └── plot.ts
│ ├── editable_spark_lines
│ │ ├── chart.ts
│ │ └── editable_spark_line.ts
│ ├── labels.ts
│ ├── lines
│ │ ├── chart.ts
│ │ └── plot.ts
│ ├── simple_lines
│ │ ├── chart.ts
│ │ └── plot.ts
│ ├── spark_lines
│ │ ├── chart.ts
│ │ └── spark_line.ts
│ ├── spark_time_lines
│ │ ├── chart.ts
│ │ └── spark_time_line.ts
│ ├── timeseries
│ │ ├── chart.ts
│ │ ├── plot.ts
│ │ └── single_scale_lines.ts
│ ├── types.ts
│ └── utils.ts
├── codes
│ ├── keras.ts
│ ├── pytorch.ts
│ ├── pytorch_lightning.ts
│ └── tabs.ts
├── error_message.ts
├── hamburger_menu.ts
├── input
│ ├── editable_field.ts
│ └── editable_select_field.ts
├── insights_list.ts
├── loader.ts
├── refresh_button.ts
├── runs_list_item.ts
├── search.ts
├── sessions_list_item.ts
├── status.ts
└── user_messages.ts
├── d3.ts
├── env.sample.ts
├── index.html
├── main.ts
├── mix_panel.ts
├── models
├── config.ts
├── job.ts
├── preferences.ts
├── run.ts
├── run_list.ts
├── session.ts
├── session_list.ts
├── status.ts
└── user.ts
├── network.ts
├── neumorphism.scss
├── screen.ts
├── screen_view.ts
├── sentry.ts
├── site.webmanifest
├── style.scss
├── types.ts
├── utils
├── ansi_to_html.js
├── document.ts
├── meta_tags.ts
├── mobile.ts
├── new_tab.ts
├── redirect.ts
├── render.ts
├── time.ts
├── value.ts
└── window_dimentions.ts
└── views
├── empty_runs_list.ts
├── empty_sessions_list.ts
├── errors
├── auth_error_view.ts
├── network_error_view.ts
├── other_error_view.ts
└── page_not_found_view.ts
├── login_view.ts
├── run_picker_view.ts
├── run_view.ts
├── runs_list_view.ts
├── session_view.ts
├── sessions_list_view.ts
└── settings_view.ts
/.github/workflows/deploy-ui.yml:
--------------------------------------------------------------------------------
1 | name: Deploy UI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-20.04
11 |
12 | steps:
13 | - name: Cloning Repo
14 | uses: actions/checkout@v2
15 | with:
16 | submodules: 'recursive'
17 |
18 | - name: Setup Node
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: '14'
22 |
23 | - name: Setup Cache
24 | uses: actions/cache@v2
25 | with:
26 | path: ~/.npm
27 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
28 | restore-keys: |
29 | ${{ runner.os }}-node-
30 |
31 | - name: Install SSH key
32 | uses: shimataro/ssh-key-action@v2
33 | with:
34 | key: ${{ secrets.SSH_KEY }}
35 | name: id_rsa
36 | known_hosts: ${{ secrets.KNOWN_HOSTS }}
37 |
38 | - name: Prepare build environment
39 | run: |
40 | npm install
41 | echo "${{ secrets.ENV_TS }}" > ui/src/env.ts
42 |
43 | - name: Build
44 | run: make compile-prod
45 |
46 | - name: Deploy
47 | run: |
48 | rsync -zravKLt --perms --executability static/ ${{ secrets.DEPLOY_LOCATION }}
49 |
50 | - name: Create Sentry release
51 | uses: getsentry/action-release@v1
52 | env:
53 | SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
54 | SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
55 | SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
56 | with:
57 | environment: production
58 | sourcemaps: './static/js'
59 | set_commits: skip
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.staging
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | notes.json
28 | node_modules/
29 | .*.swp
30 | __pycache__
31 | *.egg-info/
32 | .sass-cache/
33 | react-app-env.d.ts
34 |
35 | .idea/*
36 | !.idea/dictionaries
37 |
38 | data/
39 | geckodriver
40 | geckodriver.log
41 | labml
42 | /server/app.log
43 | /server/labml_db
44 | /server/labml_app/analyses_settings.py
45 | /server/labml_app/block_uuids.py
46 | /server/labml_app/app_analyses
47 | /server/labml_app/static
48 |
49 | # labml
50 | /logs/
51 | /static
52 | ui/src/env.ts
53 |
54 | dist/
55 |
56 | *.log
57 |
58 | build/
59 | *.so
60 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/io"]
2 | path = lib/io
3 | url = https://github.com/vpj/IO.git
4 | [submodule "lib/weya"]
5 | path = lib/weya
6 | url = https://github.com/vpj/weya.git
7 |
--------------------------------------------------------------------------------
/.idea/dictionaries/varuna.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | hyperparam
5 | labml
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.labml.yaml:
--------------------------------------------------------------------------------
1 | web_api: http://localhost:5000/api/v1/track?
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: help
2 | .DEFAULT_GOAL := help
3 |
4 | setup: ## install server and ui dependencies
5 | pip install pipenv
6 | sudo apt-get update
7 | sudo apt-get install npm
8 | npm install
9 | cd server && pipenv install --ignore-pipfile
10 |
11 | server-dev: ## start and watch server
12 | cd server && pipenv run uvicorn labml_app.flask_app:app --reload --host 0.0.0.0 --port 5000
13 |
14 | server-prod: ## compile and start server in prod
15 | # pkill gunicorn
16 | # export PATH=~/miniconda/bin:$PATH
17 | cd server && pipenv install --ignore-pipfile && pipenv run gunicorn --bind 0.0.0.0:5000 -w 2 -k uvicorn.workers.UvicornWorker labml_app.flask_app:app --daemon
18 |
19 | clean-db: ## clean float-project and move samples to float-project
20 | cd server && pipenv run python -m labml_app.scripts.clean_ups
21 |
22 | check-db: ## db checks
23 | cd server && pipenv run python -m labml_app.scripts.db_checks
24 |
25 | compile: ## Compile JS
26 | rm -rf static
27 | mkdir -p static/js
28 | cp ui/src/index.html static/index.html
29 | cp ui/src/site.webmanifest static/site.webmanifest
30 | cp ui/images/favicon.ico static/favicon.ico
31 | cp -r ui/images static/
32 | npm run build
33 |
34 | compile-prod: compile
35 | $(eval JS_CHECKSUM := $(shell md5sum static/js/bundle.min.js | cut -f 1 -d " "))
36 | $(eval CSS_CHECKSUM := $(shell md5sum static/css/style.css | cut -f 1 -d " "))
37 | sed -i 's/bundle.min.js/$(JS_CHECKSUM).min.js/g' static/index.html
38 | sed -i 's/bundle.min.js.map/$(JS_CHECKSUM).min.js.map/g' static/js/bundle.min.js
39 | sed -i 's/style.css/$(CSS_CHECKSUM).css/g' static/index.html
40 | sed -i 's/style.css.map/$(CSS_CHECKSUM).css.map/g' static/css/style.css
41 | mv static/js/bundle.min.js static/js/$(JS_CHECKSUM).min.js
42 | mv static/js/bundle.min.js.map static/js/$(JS_CHECKSUM).min.js.map
43 | mv static/css/style.css static/css/$(CSS_CHECKSUM).css
44 | mv static/css/style.css.map static/css/$(CSS_CHECKSUM).css.map
45 |
46 |
47 | watch-ui: compile ## Compile and Watch JS & CSS
48 | npm run watch
49 |
50 | build-ui: compile ## build production ui
51 |
52 | package: build-ui ## Build PIPy Package
53 | rm -rf server/labml_app/static
54 | cp -r static/ server/labml_app/static
55 | cd server && python setup.py sdist bdist_wheel
56 |
57 | check-package: ## List contents of PIPy Package
58 | cd server && tar -tvf dist/*.tar.gz
59 |
60 | install: compile ## Install from repo
61 | cd server && pip install -e .
62 |
63 | uninstall: ## Uninstall
64 | pip uninstall labml_app labml
65 |
66 |
67 | help: ## Show this help.
68 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
69 |
70 |
--------------------------------------------------------------------------------
/cpp/.gitignore:
--------------------------------------------------------------------------------
1 | labml_fast_merge.cpp
--------------------------------------------------------------------------------
/cpp/labml_fast_merge/labml_fast_merge.pyx:
--------------------------------------------------------------------------------
1 | # distutils: language = c++
2 |
3 | import numpy as np
4 | cimport numpy as np
5 | cimport cython
6 |
7 | @cython.boundscheck(False)
8 | @cython.wraparound(False)
9 | def merge(np.ndarray[np.double_t, ndim=1] values,
10 | np.ndarray[np.double_t, ndim=1] last_step,
11 | np.ndarray[np.double_t, ndim=1] steps,
12 | double step_gap,
13 | double prev_last_step,
14 | int i, # from_step
15 | ):
16 | cdef int j = i + 1
17 | cdef int length = values.shape[0]
18 | cdef double iw, jw
19 | while j < length:
20 | if last_step[j] - prev_last_step < step_gap or last_step[j] - last_step[j - 1] < 1e-3: # merge
21 | iw = max(1., last_step[i] - prev_last_step)
22 | jw = max(1., last_step[j] - last_step[i])
23 | steps[i] = (steps[i] * iw + steps[j] * jw) / (iw + jw)
24 | values[i] = (values[i] * iw + values[j] * jw) / (iw + jw)
25 | last_step[i] = last_step[j]
26 | j += 1
27 | else: # move to next
28 | prev_last_step = last_step[i]
29 | i += 1
30 | last_step[i] = last_step[j]
31 | steps[i] = steps[j]
32 | values[i] = values[j]
33 | j += 1
34 |
35 | return i + 1 # size after merging
36 |
--------------------------------------------------------------------------------
/cpp/labml_fast_merge/setup.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from setuptools import setup
3 |
4 | from Cython.Build import cythonize
5 |
6 | setup(ext_modules=cythonize("labml_fast_merge.pyx"),
7 | include_dirs=[np.get_include()])
8 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Running Your Own Server Instructions
2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 |
4 | 1. Requirements: Python 3.7 and npm installed in your machine.
5 |
6 | 2. Clone the repository
7 |
8 | .. code-block:: console
9 |
10 | git clone git@github.com:lab-ml/app.git
11 |
12 | 3. Install server and ui dependencies
13 |
14 | .. code-block:: console
15 |
16 | make setup
17 |
18 | 4. Create ``app/server/app/setting.py`` similar to ``app/server/app/setting.example.py`` and ``app/ui/src/.env`` similar to ``app/ui/src/.env.example`` files and change the parameters accordingly.
19 |
20 | 5. For UI and server dev
21 |
22 | .. code-block:: console
23 |
24 | make watch-ui
25 | make server-dev
26 |
27 | 6. For UI and server prod
28 |
29 | .. code-block:: console
30 |
31 | make build-ui
32 | make server-prod
33 |
--------------------------------------------------------------------------------
/docs/perfomance.md:
--------------------------------------------------------------------------------
1 | ### Perfomance Improvement with Cython
2 |
3 | ##### Compile/install cython module cpp/labml_fast_merge with
4 | ``
5 | python setup.py build_ext install
6 | ``
7 |
8 | ##### pipenv --site-packages
9 | ``
10 | pipenv --site-packages
11 | ``
12 |
--------------------------------------------------------------------------------
/images/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/images/cover.png
--------------------------------------------------------------------------------
/images/experiment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/images/experiment.png
--------------------------------------------------------------------------------
/images/labml-app.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/images/labml-app.gif
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Nipun Wijerathne, Varuna Jayasiri
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "@types/d3": "^5.7.2",
7 | "@types/node": "latest",
8 | "browserify": "^17.0.0",
9 | "sass": "latest",
10 | "tsify": "^5.0.2",
11 | "typescript": "latest",
12 | "uglify-js": "^3.12.8",
13 | "uglifyify": "^5.0.2",
14 | "watchify": "^4.0.0"
15 | },
16 | "dependencies": {
17 | "d3": "^5.16.0"
18 | },
19 | "scripts": {
20 | "build": "$npm_execpath run build:ui && $npm_execpath run build:sass",
21 | "watch": "$npm_execpath run watch:ui & $npm_execpath run watch:sass",
22 | "build:ui": "browserify ui/src/main.ts -t uglifyify -d -p [ tsify -p tsconfig.json] | uglifyjs -cm -o static/js/bundle.min.js --source-map \"content=inline,url=bundle.min.js.map,includeSources\"",
23 | "watch:ui": "watchify ui/src/main.ts -t uglifyify -d -p [ tsify -p tsconfig.json] -v -o static/js/bundle.min.js",
24 | "build:sass": "sass ui/src/style.scss:static/css/style.css",
25 | "watch:sass": "sass --watch ui/src/style.scss:static/css/style.css",
26 | "clean": "rm -rf static/"
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/server/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include labml_app *
2 | include readme.rst
3 |
--------------------------------------------------------------------------------
/server/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | requests = "*"
10 | gunicorn = "*"
11 | labml="*"
12 | numpy = "*"
13 | sentry-sdk = {extras = ["flask"], version = "*"}
14 | labml-db = "*"
15 | redis = "*"
16 | mixpanel = "*"
17 | slack-sdk = "*"
18 | fastapi = "*"
19 | uvicorn = "*"
20 | aiofiles = "*"
21 |
22 | [requires]
23 | python_version = "3.7"
24 |
--------------------------------------------------------------------------------
/server/gunicorn.conf.py:
--------------------------------------------------------------------------------
1 | # Reference: https://github.com/benoitc/gunicorn/blob/master/examples/example_config.py
2 | # import os
3 | import multiprocessing
4 |
5 | # _ROOT = os.path.dirname(os.path.abspath("__file__"))
6 | # _ETC = os.path.join(_ROOT, 'etc')
7 |
8 | loglevel = 'info'
9 |
10 | errorlog = '../logs/api-error.log'
11 | accesslog = '../logs/api-access.log'
12 |
13 | bind = '0.0.0.0:5000'
14 | workers = 2 # multiprocessing.cpu_count() * 2 + 1
15 | threads = 8
16 |
17 | timeout = 3 * 60 # 3 minutes
18 | keepalive = 24 * 60 * 60 # 1 day
19 |
20 | capture_output = True
21 |
--------------------------------------------------------------------------------
/server/labml_app/.gitignore:
--------------------------------------------------------------------------------
1 | settings.py
2 | analyses_settings.py
--------------------------------------------------------------------------------
/server/labml_app/__init__.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 |
3 |
4 | def start_server():
5 | try:
6 | subprocess.run(
7 | ["gunicorn --bind 0.0.0.0:5000 -w 2 -k uvicorn.workers.UvicornWorker labml_app.flask_app:app"],
8 | shell=True,
9 | )
10 | except KeyboardInterrupt:
11 | pass
12 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, List, Tuple, Callable
2 |
3 | from labml import monit
4 | from . import analysis
5 | from .series import SeriesModel
6 | from ..analyses_settings import experiment_analyses, computer_analyses
7 |
8 | EXPERIMENT_ANALYSES = {}
9 | for ans in experiment_analyses:
10 | EXPERIMENT_ANALYSES[ans.__name__] = ans
11 |
12 |
13 | class AnalysisManager:
14 | @staticmethod
15 | def track(run_uuid: str, data: Dict[str, SeriesModel]) -> None:
16 | for ans in experiment_analyses:
17 | ans.get_or_create(run_uuid).track(data)
18 |
19 | @staticmethod
20 | def track_computer(session_uuid: str, data: Dict[str, SeriesModel]) -> None:
21 | for ans in computer_analyses:
22 | ans.get_or_create(session_uuid).track(data)
23 |
24 | @staticmethod
25 | def delete_run(run_uuid: str) -> None:
26 | for ans in experiment_analyses:
27 | ans.delete(run_uuid)
28 |
29 | @staticmethod
30 | def delete_computer(computer_uuid: str) -> None:
31 | for ans in computer_analyses:
32 | ans.delete(computer_uuid)
33 |
34 | @staticmethod
35 | def get_handlers() -> List[Tuple[str, Callable, str, bool]]:
36 | return analysis.URLS
37 |
38 | @staticmethod
39 | def get_db_indexes() -> List[Tuple[any, str]]:
40 | return analysis.DB_INDEXES
41 |
42 | @staticmethod
43 | def get_db_models() -> List[Tuple[any, str]]:
44 | return analysis.DB_MODELS
45 |
46 | @staticmethod
47 | def get_experiment_analysis(name: str, run_uuid: str) -> any:
48 | assert name in EXPERIMENT_ANALYSES
49 |
50 | return EXPERIMENT_ANALYSES[name].get_or_create(run_uuid)
51 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/analysis.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from .series import SeriesModel
4 |
5 | URLS = []
6 | DB_MODELS = []
7 | DB_INDEXES = []
8 |
9 |
10 | class Analysis:
11 | def track(self, data: Dict[str, SeriesModel]) -> None:
12 | raise NotImplementedError
13 |
14 | @staticmethod
15 | def get_or_create(run_uuid: str):
16 | raise NotImplementedError
17 |
18 | @staticmethod
19 | def delete(run_uuid: str):
20 | raise NotImplementedError
21 |
22 | @staticmethod
23 | def route(method: str, url: str, login_required: bool = False):
24 | def decorator(f):
25 | URLS.append((method, f, url, login_required))
26 | return f
27 |
28 | return decorator
29 |
30 | @staticmethod
31 | def db_model(serializer: any, path: str):
32 | def decorator(cls):
33 | DB_MODELS.append((serializer, cls, path))
34 |
35 | return cls
36 |
37 | return decorator
38 |
39 | @staticmethod
40 | def db_index(serializer: any, path: str):
41 | def decorator(cls):
42 | DB_INDEXES.append((serializer, cls, path))
43 |
44 | return cls
45 |
46 | return decorator
47 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/experiments/comparison.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Dict
2 |
3 | from fastapi import Request
4 | from labml_db import Model, Index
5 | from labml_db.serializer.pickle import PickleSerializer
6 | from labml_db.serializer.yaml import YamlSerializer
7 |
8 | from labml_app.logger import logger
9 | from ..analysis import Analysis
10 | from .. import preferences
11 |
12 |
13 | class ComparisonPreferences(preferences.Preferences):
14 | base_series_preferences: preferences.SeriesPreferences
15 | base_experiment: str
16 |
17 | @classmethod
18 | def defaults(cls):
19 | return dict(base_series_preferences=[],
20 | base_experiment=str,
21 | )
22 |
23 | def update_preferences(self, data: preferences.PreferencesData) -> None:
24 | if 'base_series_preferences' in data:
25 | self.update_base_series_preferences(data['base_series_preferences'])
26 |
27 | if 'base_experiment' in data:
28 | self.base_experiment = data['base_experiment']
29 |
30 | if 'series_preferences' in data:
31 | self.update_series_preferences(data['series_preferences'])
32 |
33 | if 'chart_type' in data:
34 | self.chart_type = data['chart_type']
35 |
36 | self.save()
37 |
38 | def update_base_series_preferences(self, data: preferences.SeriesPreferences) -> None:
39 | self.base_series_preferences = data
40 |
41 | def get_data(self) -> Dict[str, Any]:
42 | return {
43 | 'base_series_preferences': self.base_series_preferences,
44 | 'series_preferences': self.series_preferences,
45 | 'base_experiment': self.base_experiment,
46 | 'chart_type': self.chart_type,
47 | }
48 |
49 |
50 | @Analysis.db_model(PickleSerializer, 'comparison_preferences')
51 | class ComparisonPreferencesModel(Model['ComparisonPreferencesModel'], ComparisonPreferences):
52 | pass
53 |
54 |
55 | @Analysis.db_index(YamlSerializer, 'comparison_preferences_index.yaml')
56 | class ComparisonPreferencesIndex(Index['ComparisonPreferences']):
57 | pass
58 |
59 |
60 | @Analysis.route('GET', 'compare/preferences/{run_uuid}')
61 | def get_comparison_preferences(request: Request, run_uuid: str) -> Any:
62 | preferences_data = {}
63 |
64 | preferences_key = ComparisonPreferencesIndex.get(run_uuid)
65 | if not preferences_key:
66 | return preferences_data
67 |
68 | cp: ComparisonPreferences = preferences_key.load()
69 | preferences_data = cp.get_data()
70 |
71 | return preferences_data
72 |
73 |
74 | @Analysis.route('POST', 'compare/preferences/{run_uuid}')
75 | async def set_comparison_preferences(request: Request, run_uuid: str) -> Any:
76 | preferences_key = ComparisonPreferencesIndex.get(run_uuid)
77 |
78 | if not preferences_key:
79 | cp = ComparisonPreferencesModel()
80 | ComparisonPreferencesIndex.set(run_uuid, cp.key)
81 | else:
82 | cp = preferences_key.load()
83 |
84 | json = await request.json()
85 | cp.update_preferences(json)
86 |
87 | logger.debug(f'update comparison preferences: {cp.key}')
88 |
89 | return {'errors': cp.errors}
90 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/helper.py:
--------------------------------------------------------------------------------
1 | import math
2 | from typing import List, Dict, Any
3 |
4 |
5 | def find_common_prefix(names: List[str]) -> str:
6 | shortest = min(names, key=len)
7 |
8 | for i, word in enumerate(shortest):
9 | for name in names:
10 | if name[i] != word:
11 | return shortest[:i]
12 |
13 | return ''
14 |
15 |
16 | def remove_common_prefix(series: List[Dict[str, Any]], key: str) -> None:
17 | if not series:
18 | return
19 |
20 | names = []
21 | for s in series:
22 | s[key] = s[key].split('.')
23 |
24 | names.append(s[key])
25 |
26 | common_prefix = find_common_prefix(names)
27 |
28 | if common_prefix:
29 | len_removed = len(common_prefix)
30 | else:
31 | len_removed = 0
32 |
33 | for s in series:
34 | name = s[key][len_removed:]
35 |
36 | s[key] = '.'.join(name)
37 |
38 |
39 | def replace_nans(series: List[Dict[str, Any]], keys: List[str]) -> None:
40 | for s in series:
41 | for key in keys:
42 | if isinstance(s[key], list):
43 | s[key] = [0 if math.isnan(x) else x for x in s[key]]
44 | else:
45 | s[key] = 0 if math.isnan(s[key]) else s[key]
46 |
47 |
48 | def get_mean_series(res: List[Dict[str, Any]]) -> Dict[str, Any]:
49 | mean_value = [sum(x) / len(x) for x in zip(*[s['value'] for s in res])]
50 | mean_smoothed = [sum(x) / len(x) for x in zip(*[s['smoothed'] for s in res])]
51 | step = res[0]['step']
52 |
53 | return {'step': step, 'value': mean_value, 'smoothed': mean_smoothed, 'name': 'mean'}
54 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/preferences.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, List, Any
2 |
3 | PreferencesData = Dict[str, Any]
4 | SeriesPreferences = List[int]
5 |
6 |
7 | class Preferences:
8 | series_preferences: SeriesPreferences
9 | chart_type: int
10 | errors: List[Dict[str, str]]
11 |
12 | @classmethod
13 | def defaults(cls):
14 | return dict(series_preferences=[],
15 | chart_type=0,
16 | errors=[]
17 | )
18 |
19 | def update_preferences(self, data: PreferencesData) -> None:
20 | if 'series_preferences' in data:
21 | self.update_series_preferences(data['series_preferences'])
22 |
23 | if 'chart_type' in data:
24 | self.chart_type = data['chart_type']
25 |
26 | self.save()
27 |
28 | def update_series_preferences(self, data: SeriesPreferences) -> None:
29 | self.series_preferences = data
30 |
31 | def get_data(self) -> Dict[str, Any]:
32 | return {
33 | 'series_preferences': self.series_preferences,
34 | 'chart_type': self.chart_type,
35 | }
36 |
--------------------------------------------------------------------------------
/server/labml_app/analyses/series_collection.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Any, List
2 |
3 | from ..analyses.series import SeriesModel, Series
4 |
5 |
6 | class SeriesCollection:
7 | tracking: Dict[str, SeriesModel]
8 | indicators: set
9 | step: int
10 | max_buffer_length: int
11 |
12 | @classmethod
13 | def defaults(cls):
14 | return dict(tracking={},
15 | step=0,
16 | indicators=set(),
17 | max_buffer_length=None,
18 | )
19 |
20 | def get_tracks(self) -> List[SeriesModel]:
21 | res = []
22 | is_series_updated = False
23 | for ind, track in self.tracking.items():
24 | name = ind.split('.')
25 |
26 | s = Series().load(track)
27 | series: Dict[str, Any] = s.detail
28 | series['name'] = '.'.join(name[1:])
29 |
30 | if s.is_smoothed_updated:
31 | self.tracking[ind] = s.to_data()
32 | is_series_updated = True
33 |
34 | res.append(series)
35 |
36 | if is_series_updated:
37 | self.save()
38 |
39 | return res
40 |
41 | def track(self, data: Dict[str, SeriesModel]) -> None:
42 | for ind, series in data.items():
43 | self.step = max(self.step, series['step'][-1])
44 | self._update_series(ind, series)
45 |
46 | self.save()
47 |
48 | def _update_series(self, ind: str, series: SeriesModel) -> None:
49 | if ind not in self.tracking:
50 | self.tracking[ind] = Series(self.max_buffer_length).to_data()
51 |
52 | s = Series(self.max_buffer_length).load(self.tracking[ind])
53 | s.update(series['step'], series['value'])
54 |
55 | self.tracking[ind] = s.to_data()
56 |
57 | def save(self):
58 | raise NotImplementedError
59 |
--------------------------------------------------------------------------------
/server/labml_app/analyses_settings.sample.py:
--------------------------------------------------------------------------------
1 | from .analyses.experiments.parameters import ParametersAnalysis
2 | from .analyses.experiments.gradients import GradientsAnalysis
3 | from .analyses.experiments.metrics import MetricsAnalysis
4 | from .analyses.experiments.outputs import OutputsAnalysis
5 | from .analyses.experiments.hyperparameters import HyperParamsAnalysis
6 |
7 | from .analyses.computers.cpu import CPUAnalysis
8 | from .analyses.computers.gpu import GPUAnalysis
9 | from .analyses.computers.memory import MemoryAnalysis
10 | from .analyses.computers.network import NetworkAnalysis
11 | from .analyses.computers.disk import DiskAnalysis
12 | from .analyses.computers.process import ProcessAnalysis
13 |
14 | experiment_analyses = [GradientsAnalysis,
15 | OutputsAnalysis,
16 | ParametersAnalysis,
17 | HyperParamsAnalysis,
18 | MetricsAnalysis]
19 |
20 | computer_analyses = [CPUAnalysis,
21 | GPUAnalysis,
22 | MemoryAnalysis,
23 | NetworkAnalysis,
24 | DiskAnalysis,
25 | ProcessAnalysis]
26 |
27 | INDICATORS_LIMIT = 100
28 |
--------------------------------------------------------------------------------
/server/labml_app/auth/__init__.py:
--------------------------------------------------------------------------------
1 | import functools
2 | import inspect
3 | from typing import Optional
4 |
5 | from fastapi import Request
6 | from fastapi.responses import JSONResponse
7 |
8 | from ..db import app_token
9 | from ..db import project
10 | from ..db import user
11 | from .. import settings
12 |
13 |
14 | def get_app_token(request: Request) -> 'app_token.AppToken':
15 | token_id = request.headers.get('Authorization', '')
16 |
17 | return app_token.get_or_create(token_id)
18 |
19 |
20 | def check_labml_token_permission(func) -> functools.wraps:
21 | @functools.wraps(func)
22 | def wrapper(*args, **kwargs):
23 | labml_token = kwargs.get('labml_token', '')
24 |
25 | p = project.get_project(labml_token)
26 | if p and p.is_sharable:
27 | return func(*args, **kwargs)
28 |
29 | kwargs['labml_token'] = None
30 |
31 | return func(*args, **kwargs)
32 |
33 | return wrapper
34 |
35 |
36 | def login_required(func) -> functools.wraps:
37 | @functools.wraps(func)
38 | async def wrapper(request: Request, *args, **kwargs):
39 | token_id = request.headers.get('Authorization', '')
40 | at = app_token.get_or_create(token_id)
41 | if at.is_auth or not settings.IS_LOGIN_REQUIRED:
42 | if inspect.iscoroutinefunction(func):
43 | return await func(request, *args, **kwargs)
44 | else:
45 | return func(request, *args, **kwargs)
46 | else:
47 | response = JSONResponse()
48 | response.status_code = 403
49 |
50 | return response
51 |
52 | return wrapper
53 |
54 |
55 | def get_auth_user(request: Request) -> Optional['user.User']:
56 | s = get_app_token(request)
57 |
58 | u = None
59 | if s.user:
60 | u = s.user.load()
61 |
62 | if not settings.IS_LOGIN_REQUIRED:
63 | u = user.get_or_create_user(user.AuthOInfo(
64 | **{k: '' for k in ('name', 'email', 'sub', 'email_verified', 'picture')}))
65 |
66 | return u
67 |
68 |
69 | def get_is_user_logged(request: Request) -> bool:
70 | s = get_app_token(request)
71 |
72 | if s.is_auth or not settings.IS_LOGIN_REQUIRED:
73 | return True
74 |
75 | return False
76 |
--------------------------------------------------------------------------------
/server/labml_app/block_uuids.sample.py:
--------------------------------------------------------------------------------
1 | delete_run_uuids = []
2 |
--------------------------------------------------------------------------------
/server/labml_app/db/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pathlib import Path
3 |
4 | from labml_db import Model, Index
5 | from labml_db.driver.redis import RedisDbDriver
6 | from labml_db.driver.file import FileDbDriver
7 | from labml_db.index_driver.redis import RedisIndexDbDriver
8 | from labml_db.index_driver.file import FileIndexDbDriver
9 | from labml_db.serializer.json import JsonSerializer
10 | from labml_db.serializer.yaml import YamlSerializer
11 | from labml_db.serializer.pickle import PickleSerializer
12 |
13 | from . import project
14 | from . import user
15 | from . import status
16 | from . import app_token
17 | from . import run
18 | from . import session
19 | from . import computer
20 | from . import job
21 | from . import blocked_uuids
22 | from .. import analyses
23 | from .. import settings
24 |
25 | Models = [(YamlSerializer(), user.User),
26 | (YamlSerializer(), project.Project),
27 | (JsonSerializer(), status.Status),
28 | (JsonSerializer(), status.RunStatus),
29 | (JsonSerializer(), app_token.AppToken),
30 | (JsonSerializer(), run.Run),
31 | (JsonSerializer(), session.Session),
32 | (PickleSerializer(), job.Job),
33 | (PickleSerializer(), computer.Computer)] + [(s(), m) for s, m, p in analyses.AnalysisManager.get_db_models()]
34 |
35 | Indexes = [project.ProjectIndex,
36 | user.UserIndex,
37 | blocked_uuids.BlockedRunIndex,
38 | blocked_uuids.BlockedSessionIndex,
39 | user.TokenOwnerIndex,
40 | app_token.AppTokenIndex,
41 | run.RunIndex,
42 | session.SessionIndex,
43 | job.JobIndex,
44 | computer.ComputerIndex] + [m for s, m, p in analyses.AnalysisManager.get_db_indexes()]
45 |
46 |
47 | def get_data_path():
48 | package_path = Path(os.path.dirname(os.path.abspath(__file__))).parent
49 |
50 | data_path = package_path / 'data'
51 | if not data_path.exists():
52 | raise RuntimeError(f'Data folder not found. Package path: {str(package_path)}')
53 |
54 | return data_path
55 |
56 |
57 | def init_db():
58 | data_path = get_data_path()
59 |
60 | if settings.IS_LOCAL_SETUP:
61 | Model.set_db_drivers(
62 | [FileDbDriver(PickleSerializer(), m, Path(f'{data_path}/{m.__name__}')) for s, m in Models])
63 | Index.set_db_drivers(
64 | [FileIndexDbDriver(YamlSerializer(), m, Path(f'{data_path}/{m.__name__}.yaml')) for m in Indexes])
65 | else:
66 | import redis
67 | db = redis.Redis(host='localhost', port=6379, db=0)
68 |
69 | Model.set_db_drivers([RedisDbDriver(s, m, db) for s, m in Models])
70 | Index.set_db_drivers([RedisIndexDbDriver(m, db) for m in Indexes])
71 |
72 | project.create_project(settings.FLOAT_PROJECT_TOKEN, 'float project')
73 | project.create_project(settings.SAMPLES_PROJECT_TOKEN, 'samples project')
74 |
--------------------------------------------------------------------------------
/server/labml_app/db/app_token.py:
--------------------------------------------------------------------------------
1 | import time
2 | from uuid import uuid4
3 |
4 | from labml_db import Model, Key, Index
5 |
6 | from . import user
7 |
8 | EXPIRATION_DELAY = 60 * 60 * 24 * 30
9 |
10 |
11 | def gen_token_id() -> str:
12 | return uuid4().hex
13 |
14 |
15 | def gen_expiration() -> float:
16 | return time.time() + EXPIRATION_DELAY
17 |
18 |
19 | class AppToken(Model['Session']):
20 | token_id: str
21 | expiration: float
22 | user: Key['user.User']
23 |
24 | @classmethod
25 | def defaults(cls):
26 | return dict(token_id='',
27 | expiration='',
28 | user=None
29 | )
30 |
31 | @property
32 | def is_auth(self) -> bool:
33 | return self.user is not None and self.expiration > time.time()
34 |
35 |
36 | class AppTokenIndex(Index['AppToken']):
37 | pass
38 |
39 |
40 | def get_or_create(token_id: str) -> AppToken:
41 | if not token_id:
42 | token_id = gen_token_id()
43 |
44 | app_token_key = AppTokenIndex.get(token_id)
45 |
46 | if not app_token_key:
47 | app_token = AppToken(token_id=token_id,
48 | expiration=gen_expiration()
49 | )
50 | app_token.save()
51 | AppTokenIndex.set(app_token.token_id, app_token.key)
52 |
53 | return app_token
54 |
55 | return app_token_key.load()
56 |
57 |
58 | def delete(app_token: AppToken) -> None:
59 | AppTokenIndex.delete(app_token.token_id)
60 | app_token.delete()
61 |
--------------------------------------------------------------------------------
/server/labml_app/db/blocked_uuids.py:
--------------------------------------------------------------------------------
1 | from labml_db import Index
2 |
3 | from . import run
4 | from . import session
5 |
6 |
7 | class BlockedRunIndex(Index['BlockedRun']):
8 | pass
9 |
10 |
11 | class BlockedSessionIndex(Index['BlockedSession']):
12 | pass
13 |
14 |
15 | def add_blocked_run(r: 'run.Run') -> None:
16 | BlockedRunIndex.set(r.run_uuid, r.key)
17 |
18 |
19 | def add_blocked_session(s: 'session.Session') -> None:
20 | BlockedSessionIndex.set(s.session_uuid, s.key)
21 |
22 |
23 | def is_run_blocked(run_uuid: str) -> bool:
24 | run_key = BlockedRunIndex.get(run_uuid)
25 |
26 | return run_key is not None
27 |
28 |
29 | def is_session_blocked(session_uuid: str) -> bool:
30 | session_key = BlockedSessionIndex.get(session_uuid)
31 |
32 | return session_key is not None
33 |
34 |
35 | def remove_blocked_run(run_uuid: str) -> None:
36 | BlockedRunIndex.delete(run_uuid)
37 |
38 |
39 | def remove_blocked_session(session_uuid: str) -> None:
40 | BlockedSessionIndex.delete(session_uuid)
41 |
--------------------------------------------------------------------------------
/server/labml_app/db/job.py:
--------------------------------------------------------------------------------
1 | import time
2 | from typing import Optional, Dict, Union, Any
3 |
4 | from labml_db import Model, Index
5 |
6 | from labml_app import utils
7 |
8 |
9 | class JobStatuses:
10 | INITIATED = 'initiated'
11 | FAIL = 'fail'
12 | SUCCESS = 'success'
13 | TIMEOUT = 'timeout'
14 | COMPUTER_OFFLINE = 'computer_offline'
15 |
16 |
17 | class JobMethods:
18 | START_TENSORBOARD = 'start_tensorboard'
19 | DELETE_RUNS = 'delete_runs'
20 | CLEAR_CHECKPOINTS = 'clear_checkpoints'
21 | CALL_SYNC = 'call_sync'
22 |
23 |
24 | NON_REPEATED_METHODS = [JobMethods.CALL_SYNC]
25 |
26 | JobDict = Dict[str, Union[str, float]]
27 |
28 |
29 | class Job(Model['Job']):
30 | job_uuid: str
31 | method: str
32 | status: str
33 | created_time: float
34 | completed_time: float
35 | data: Dict[str, Any]
36 |
37 | @classmethod
38 | def defaults(cls):
39 | return dict(job_uuid='',
40 | method='',
41 | status='',
42 | created_time=None,
43 | completed_time=None,
44 | data={},
45 | )
46 |
47 | @property
48 | def is_success(self) -> bool:
49 | return self.status == JobStatuses.SUCCESS
50 |
51 | @property
52 | def is_error(self) -> bool:
53 | return self.status == JobStatuses.FAIL
54 |
55 | @property
56 | def is_completed(self) -> bool:
57 | return self.status == JobStatuses.FAIL or self.status == JobStatuses.SUCCESS
58 |
59 | @property
60 | def is_non_repeated(self) -> bool:
61 | return self.method in NON_REPEATED_METHODS
62 |
63 | def to_data(self) -> JobDict:
64 | return {
65 | 'uuid': self.job_uuid,
66 | 'method': self.method,
67 | 'status': self.status,
68 | 'created_time': self.created_time,
69 | 'completed_time': self.completed_time,
70 | 'data': self.data
71 | }
72 |
73 | def update_job(self, status: str, data: Dict[str, Any]) -> None:
74 | self.status = status
75 |
76 | if self.status in [JobStatuses.SUCCESS, JobStatuses.FAIL]:
77 | self.completed_time = time.time()
78 |
79 | if type(data) is dict:
80 | self.data.update(data)
81 |
82 | self.save()
83 |
84 |
85 | class JobIndex(Index['Job']):
86 | pass
87 |
88 |
89 | def create(method: str, data: Dict[str, Any]) -> Job:
90 | job = Job(job_uuid=utils.gen_token(),
91 | method=method,
92 | created_time=time.time(),
93 | data=data,
94 | status=JobStatuses.INITIATED,
95 | )
96 | job.save()
97 | JobIndex.set(job.job_uuid, job.key)
98 |
99 | return job
100 |
101 |
102 | def get(job_uuid: str) -> Optional[Job]:
103 | job_key = JobIndex.get(job_uuid)
104 |
105 | if job_key:
106 | return job_key.load()
107 |
108 | return None
109 |
110 |
111 | def delete(job_uuid: str) -> None:
112 | job_key = JobIndex.get(job_uuid)
113 |
114 | if job_key:
115 | job_key.delete()
116 | JobIndex.delete(job_uuid)
117 |
--------------------------------------------------------------------------------
/server/labml_app/db/status.py:
--------------------------------------------------------------------------------
1 | import time
2 | from typing import Dict
3 |
4 | from labml_db import Model, Key
5 |
6 | from ..enums import RunEnums
7 |
8 |
9 | class RunStatus(Model['RunStatusModel']):
10 | status: str
11 | details: object
12 | time: float
13 |
14 | @classmethod
15 | def defaults(cls):
16 | return dict(status='',
17 | details=None,
18 | time=None
19 | )
20 |
21 |
22 | class Status(Model['Status']):
23 | last_updated_time: float
24 | run_status: Key[RunStatus]
25 |
26 | @classmethod
27 | def defaults(cls):
28 | return dict(last_updated_time=None,
29 | run_status=None
30 | )
31 |
32 | def get_data(self) -> Dict[str, any]:
33 | run_status = self.run_status.load().to_dict()
34 | run_status['status'] = self.get_true_status(run_status.get('status', ''))
35 |
36 | return {
37 | 'last_updated_time': self.last_updated_time,
38 | 'run_status': run_status
39 | }
40 |
41 | def update_time_status(self, data: Dict[str, any]) -> None:
42 | self.last_updated_time = time.time()
43 |
44 | s = data.get('status', {})
45 | if s:
46 | run_status = self.run_status.load()
47 |
48 | run_status.status = s.get('status', run_status.status)
49 | run_status.details = s.get('details', run_status.details)
50 | run_status.time = s.get('time', run_status.time)
51 |
52 | run_status.save()
53 |
54 | self.save()
55 |
56 | def get_true_status(self, status: str = None) -> str:
57 | if not status:
58 | status = self.run_status.load().status
59 | not_responding = False
60 |
61 | if status == RunEnums.RUN_IN_PROGRESS:
62 | if self.last_updated_time is not None:
63 | time_diff = (time.time() - self.last_updated_time) / 60
64 | if time_diff > 15:
65 | not_responding = True
66 |
67 | if not_responding:
68 | return RunEnums.RUN_NOT_RESPONDING
69 | elif status == '':
70 | return RunEnums.RUN_UNKNOWN
71 | else:
72 | return status
73 |
74 |
75 | def create_status() -> Status:
76 | time_now = time.time()
77 |
78 | run_status = RunStatus(status=RunEnums.RUN_IN_PROGRESS,
79 | time=time_now
80 | )
81 | status = Status(last_updated_time=time_now,
82 | run_status=run_status.key
83 | )
84 | status.save()
85 | run_status.save()
86 |
87 | return status
88 |
--------------------------------------------------------------------------------
/server/labml_app/db/user.py:
--------------------------------------------------------------------------------
1 | from typing import List, NamedTuple, Dict, Optional
2 |
3 | from labml_db import Model, Key, Index
4 |
5 | from . import project
6 | from .. import utils
7 |
8 |
9 | class User(Model['User']):
10 | name: str
11 | sub: str
12 | email: str
13 | picture: str
14 | theme: str
15 | is_dev: bool
16 | email_verified: bool
17 | projects: List[Key['project.Project']]
18 |
19 | @classmethod
20 | def defaults(cls):
21 | return dict(name='',
22 | sub='',
23 | email='',
24 | picture='',
25 | theme='light',
26 | is_dev=False,
27 | email_verified=False,
28 | projects=[]
29 | )
30 |
31 | @property
32 | def default_project(self) -> 'project.Project':
33 | return self.projects[0].load()
34 |
35 | def get_data(self) -> Dict[str, any]:
36 | return {
37 | 'name': self.name,
38 | 'email': self.email,
39 | 'picture': self.picture,
40 | 'theme': self.theme,
41 | 'projects': [p.load().labml_token for p in self.projects],
42 | 'default_project': self.default_project.labml_token
43 | }
44 |
45 | def set_user(self, data) -> None:
46 | if 'theme' in data:
47 | self.theme = data['theme']
48 | self.save()
49 |
50 |
51 | class UserIndex(Index['User']):
52 | pass
53 |
54 |
55 | class TokenOwnerIndex(Index['TokenOwner']):
56 | pass
57 |
58 |
59 | def get_token_owner(labml_token: str) -> Optional[str]:
60 | user_key = TokenOwnerIndex.get(labml_token)
61 |
62 | if user_key:
63 | user = user_key.load()
64 | return user.email
65 |
66 | return ''
67 |
68 |
69 | class AuthOInfo(NamedTuple):
70 | name: str
71 | sub: str
72 | email: str
73 | picture: str
74 | email_verified: bool
75 |
76 |
77 | def get_or_create_user(info: AuthOInfo) -> User:
78 | user_key = UserIndex.get(info.email)
79 |
80 | if not user_key:
81 | p = project.Project(labml_token=utils.gen_token())
82 | user = User(name=info.name,
83 | sub=info.sub,
84 | email=info.email,
85 | picture=info.picture,
86 | email_verified=info.email_verified,
87 | projects=[p.key]
88 | )
89 |
90 | user.save()
91 | p.save()
92 |
93 | UserIndex.set(user.email, user.key)
94 | project.ProjectIndex.set(p.labml_token, p.key)
95 | TokenOwnerIndex.set(p.labml_token, user.key)
96 |
97 | return user
98 |
99 | return user_key.load()
100 |
--------------------------------------------------------------------------------
/server/labml_app/enums.py:
--------------------------------------------------------------------------------
1 | class RunEnums:
2 | RUN_COMPLETED = 'completed'
3 | RUN_CRASHED = 'crashed'
4 | RUN_INTERRUPTED = 'interrupted'
5 | RUN_IN_PROGRESS = 'in progress'
6 | RUN_UNKNOWN = 'unknown'
7 | RUN_NOT_RESPONDING = 'no response'
8 |
9 |
10 | class SeriesEnums:
11 | GRAD = 'grad'
12 | PARAM = 'param'
13 | MODULE = 'module'
14 | TIME = 'time'
15 | METRIC = 'metric'
16 | HYPERPARAMS = 'hp'
17 |
18 |
19 | class COMPUTEREnums:
20 | CPU = 'cpu'
21 | GPU = 'gpu'
22 | DISK = 'disk'
23 | MEMORY = 'memory'
24 | NETWORK = 'net'
25 | PROCESS = 'process'
26 | BATTERY = 'battery'
27 |
28 |
29 | class InsightEnums:
30 | DANGER = 'danger'
31 | WARNING = 'warning'
32 | SUCCESS = 'success'
33 |
34 |
35 | INDICATORS = [SeriesEnums.GRAD,
36 | SeriesEnums.PARAM,
37 | SeriesEnums.TIME,
38 | SeriesEnums.MODULE,
39 | SeriesEnums.METRIC,
40 | SeriesEnums.HYPERPARAMS]
41 |
--------------------------------------------------------------------------------
/server/labml_app/logger/__init__.py:
--------------------------------------------------------------------------------
1 | from .logger import logger
2 |
3 | _all__ = ["logger"]
4 |
--------------------------------------------------------------------------------
/server/labml_app/logger/logger.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from logging.handlers import RotatingFileHandler
3 | from logging import StreamHandler
4 |
5 | _LOG_PATH = 'labml_app.log'
6 | _MAX_BYTES = 100000
7 |
8 |
9 | class CustomFormatter(logging.Formatter):
10 | grey = "\x1b[38;21m"
11 | blue = "\x1b[36;21m"
12 | yellow = "\x1b[33;21m"
13 | red = "\x1b[31;21m"
14 | bold_red = "\x1b[31;1m"
15 | reset = "\x1b[0m"
16 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
17 |
18 | FORMATS = {
19 | logging.DEBUG: grey + format + reset,
20 | logging.INFO: blue + format + reset,
21 | logging.WARNING: yellow + format + reset,
22 | logging.ERROR: red + format + reset,
23 | logging.CRITICAL: bold_red + format + reset
24 | }
25 |
26 | def format(self, record):
27 | log_fmt = self.FORMATS.get(record.levelno)
28 | formatter = logging.Formatter(log_fmt)
29 | return formatter.format(record)
30 |
31 |
32 | def _init_streaming_handler():
33 | streaming = StreamHandler()
34 | streaming.setFormatter(CustomFormatter())
35 |
36 | return streaming
37 |
38 |
39 | def _init_file_handler():
40 | file_handler = RotatingFileHandler(filename=_LOG_PATH, maxBytes=_MAX_BYTES)
41 | file_handler.setFormatter(CustomFormatter())
42 |
43 | return file_handler
44 |
45 |
46 | logger = logging.getLogger('LabML logger')
47 | logger.setLevel(logging.INFO)
48 |
49 | logger.addHandler(_init_streaming_handler())
50 | logger.addHandler(_init_file_handler())
51 |
--------------------------------------------------------------------------------
/server/labml_app/scripts/clean_ups.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from labml_app import settings
4 | from labml_app import block_uuids
5 | from labml_app.logger import logger
6 | from labml_app.db import project, run, session, blocked_uuids, init_db
7 |
8 |
9 | def clean_float_project() -> None:
10 | p = project.get_project(settings.FLOAT_PROJECT_TOKEN)
11 |
12 | logger.info('Cleaning runs started')
13 |
14 | delete_run_list = []
15 | for run_uuid, run_key in p.runs.items():
16 | try:
17 | r = run_key.load()
18 | s = r.status.load()
19 |
20 | if not r.is_claimed and (time.time() - 86400) > s.last_updated_time:
21 | run.delete(run_uuid)
22 | blocked_uuids.remove_blocked_run(run_uuid)
23 | delete_run_list.append(run_uuid)
24 | logger.log(str(r.run_uuid))
25 | elif (time.time() - 86400) > s.last_updated_time:
26 | delete_run_list.append(run_uuid)
27 | except TypeError:
28 | logger.error(f'error while deleting the run {run_uuid}')
29 |
30 | for run_uuid in delete_run_list:
31 | p.runs.pop(run_uuid)
32 | p.save()
33 |
34 | logger.info('......Done.........')
35 | logger.info('Cleaning sessions started')
36 |
37 | delete_session_list = []
38 | for session_uuid, session_key in p.sessions.items():
39 | try:
40 | ss = session_key.load()
41 | s = ss.status.load()
42 |
43 | if not ss.is_claimed and (time.time() - 86400) > s.last_updated_time:
44 | session.delete(session_uuid)
45 | blocked_uuids.remove_blocked_session(session_uuid)
46 | delete_session_list.append(session_uuid)
47 | elif (time.time() - 86400) > s.last_updated_time:
48 | delete_session_list.append(session_uuid)
49 | except TypeError:
50 | logger.error(f'error while deleting the session {session_uuid}')
51 |
52 | for session_uuid in delete_session_list:
53 | p.sessions.pop(session_uuid)
54 | p.save()
55 |
56 | logger.info('......Done.........')
57 |
58 |
59 | def move_to_samples():
60 | logger.info('Samples moving started')
61 | p = project.get_project(settings.SAMPLES_PROJECT_TOKEN)
62 | for run_uuid in block_uuids.delete_run_uuids:
63 | r = run.get(run_uuid)
64 |
65 | if r.owner == 'samples':
66 | continue
67 |
68 | if r and r.owner != 'samples':
69 | r.owner = 'samples'
70 | p.add_run(run_uuid)
71 |
72 | r.save()
73 | p.save()
74 |
75 | logger.info('......Done.........')
76 |
77 |
78 | def add_block_uuids():
79 | logger.info('add_block_uuids started')
80 |
81 | for run_uuid in block_uuids.update_run_uuids:
82 | r = run.get(run_uuid)
83 | if r:
84 | logger.info(r.run_uuid)
85 | blocked_uuids.add_blocked_run(r)
86 |
87 | logger.info('......Done.........')
88 |
89 |
90 | if __name__ == "__main__":
91 | init_db()
92 | clean_float_project()
93 | move_to_samples()
94 | add_block_uuids()
95 |
--------------------------------------------------------------------------------
/server/labml_app/scripts/db_checks.py:
--------------------------------------------------------------------------------
1 | from labml_app.logger import logger
2 | from labml_app.db import Models
3 |
4 | for s, m in Models:
5 | logger.info('checking: ' + str(m))
6 | model_keys = m.get_all()
7 |
8 | for model_key in model_keys:
9 | try:
10 | m = model_key.load()
11 | except Exception as e:
12 | logger.error(e)
13 |
14 | # TODO add more checks
15 |
--------------------------------------------------------------------------------
/server/labml_app/scripts/temp_cleans.py:
--------------------------------------------------------------------------------
1 | from labml_app.db import run, init_db, project
2 |
3 | init_db()
4 |
5 | run_keys = run.Run.get_all()
6 | for run_key in run_keys:
7 | r = run_key.load()
8 | if r.is_claimed and not r.owner:
9 | run.delete(r.run_uuid)
10 |
11 | print(len(run_keys))
12 |
--------------------------------------------------------------------------------
/server/labml_app/settings.sample.py:
--------------------------------------------------------------------------------
1 | from pathlib import PurePath
2 |
3 | SERVER_URL = 'http://localhost:5000'
4 | WEB_URL = 'http://localhost:5000'
5 | SLACK_BOT_TOKEN = 'XXX'
6 | SLACK_CHANNEL = 'XXX'
7 | AUTH0_DOMAIN = 'XXX'
8 | DATA_PATH = PurePath('/xxx/xxx/data')
9 | SENTRY_DSN = 'XXX'
10 | FLOAT_PROJECT_TOKEN = 'XXXX'
11 | SAMPLES_PROJECT_TOKEN = 'XXX'
12 | LABML_VERSION = 'XXX'
13 | IS_MIX_PANEL = False
14 | IS_LOCAL_SETUP = True
15 | INDICATOR_LIMIT = 100
16 | IS_LOGIN_REQUIRED = True
17 |
--------------------------------------------------------------------------------
/server/labml_app/utils/__init__.py:
--------------------------------------------------------------------------------
1 | import time
2 | from typing import Callable
3 | from uuid import uuid4
4 | from functools import wraps
5 |
6 | from . import mix_panel
7 |
8 |
9 | def check_version(user_v, new_v) -> bool:
10 | for uv, nw in zip(user_v.split('.'), new_v.split('.')):
11 | if int(nw) == int(uv):
12 | continue
13 | elif int(nw) > int(uv):
14 | return True
15 | else:
16 | return False
17 |
18 |
19 | def gen_token() -> str:
20 | return uuid4().hex
21 |
22 |
23 | def time_this(function) -> Callable:
24 | @wraps(function)
25 | def time_wrapper(*args, **kwargs):
26 | start = time.time()
27 | r = function(*args, **kwargs)
28 | end = time.time()
29 |
30 | total_time = end - start
31 | print(function.__name__, total_time)
32 |
33 | return r
34 |
35 | return time_wrapper
36 |
--------------------------------------------------------------------------------
/server/labml_app/utils/slack.py:
--------------------------------------------------------------------------------
1 | try:
2 | from slack_sdk import WebClient
3 | from slack_sdk.errors import SlackApiError
4 | except ImportError as e:
5 | WebClient = None
6 | SlackApiError = None
7 |
8 | from labml_app import settings
9 |
10 |
11 | class SlackMessage:
12 | def __init__(self):
13 | self._client = WebClient(settings.SLACK_BOT_TOKEN)
14 |
15 | def send(self, text):
16 | res = {'error': '', 'success': False, 'ts': ''}
17 |
18 | if settings.SLACK_BOT_TOKEN and settings.SLACK_CHANNEL:
19 | try:
20 | ret = self._client.chat_postMessage(
21 | channel=settings.SLACK_CHANNEL,
22 | text=text,
23 | )
24 | res['ts'] = ret['ts']
25 | res['success'] = True
26 | except SlackApiError as e:
27 | res['error'] = e.response["error"]
28 |
29 | return res
30 |
31 |
32 | class DummySlackMessage:
33 | def send(self, text):
34 | return {'error': '', 'success': False, 'ts': ''}
35 |
36 |
37 | if WebClient is not None:
38 | client = SlackMessage()
39 | else:
40 | client = DummySlackMessage()
41 |
--------------------------------------------------------------------------------
/server/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("../readme.md", "r") as f:
4 | long_description = f.read()
5 |
6 | setuptools.setup(
7 | name='labml_app',
8 | version='0.0.0',
9 | author="Varuna Jayasiri, Nipun, Aditya",
10 | author_email="vpjayasiri@gmail.com",
11 | description="",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/lab-ml/app",
15 | project_urls={
16 | 'Documentation': 'https://lab-ml.com/'
17 | },
18 | install_requires=['labml>=0.4.87',
19 | 'gunicorn',
20 | 'numpy',
21 | 'labml-db',
22 | 'fastapi',
23 | 'uvicorn',
24 | 'aiofiles',
25 | ],
26 | packages=['labml_app'],
27 | include_package_data=True,
28 | classifiers=[
29 | "Programming Language :: Python :: 3",
30 | "License :: OSI Approved :: MIT License",
31 | 'Intended Audience :: Developers',
32 | 'Intended Audience :: Science/Research',
33 | 'Topic :: Scientific/Engineering',
34 | 'Topic :: Scientific/Engineering :: Mathematics',
35 | 'Topic :: Scientific/Engineering :: Artificial Intelligence',
36 | 'Topic :: Software Development',
37 | 'Topic :: Software Development :: Libraries',
38 | 'Topic :: Software Development :: Libraries :: Python Modules',
39 | ],
40 | keywords='machine learning',
41 | )
42 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "CommonJS",
5 | "moduleResolution": "node",
6 | "sourceMap": true,
7 | "outDir": "dist",
8 | "allowJs": true,
9 | "downlevelIteration": true
10 | },
11 | "include": [
12 | "ui/src",
13 | "lib/io/io.ts",
14 | "lib/io/ajax.ts",
15 | "lib/weya/weya.ts",
16 | "lib/weya/router.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/ui/images/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/android-chrome-192x192.png
--------------------------------------------------------------------------------
/ui/images/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/android-chrome-512x512.png
--------------------------------------------------------------------------------
/ui/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/ui/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/favicon-16x16.png
--------------------------------------------------------------------------------
/ui/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/favicon-32x32.png
--------------------------------------------------------------------------------
/ui/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/favicon.ico
--------------------------------------------------------------------------------
/ui/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/favicon.png
--------------------------------------------------------------------------------
/ui/images/lab_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/lab_logo.png
--------------------------------------------------------------------------------
/ui/images/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
32 |
--------------------------------------------------------------------------------
/ui/images/tf_Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/labmlai/app/d525baa516f60658595cf9308207ea71421d7f76/ui/images/tf_Icon.png
--------------------------------------------------------------------------------
/ui/src/analyses/analyses.ts:
--------------------------------------------------------------------------------
1 | import {Analysis} from "./types"
2 |
3 | import metricAnalysis from "./experiments/metrics"
4 | import hyperPramsAnalysis from "./experiments/hyper_params"
5 | import gradientAnalysis from "./experiments/grads"
6 | import parameterAnalysis from "./experiments/params"
7 | import moduleAnalysis from "./experiments/activations"
8 | import stdOutAnalysis from "./experiments/stdout"
9 | import stderrAnalysis from "./experiments/stderror"
10 | import loggerAnalysis from "./experiments/logger"
11 | import runConfigsAnalysis from "./experiments/configs"
12 |
13 | import cpuAnalysis from './sessions/cpu'
14 | import diskAnalysis from './sessions/disk'
15 | import {gpuMemoryAnalysis, gpuPowerAnalysis, gpuTempAnalysis, gpuUtilAnalysis} from './sessions/gpu'
16 | import memoryAnalysis from './sessions/memory'
17 | import networkAnalysis from './sessions/network'
18 | import processAnalysis from './sessions/process'
19 | import batteryAnalysis from './sessions/battery'
20 | import sessionConfigsAnalysis from "./sessions/configs"
21 | import comparisonAnalysis from './experiments/comaprison'
22 |
23 | let experimentAnalyses: Analysis[] = [
24 | metricAnalysis,
25 | comparisonAnalysis,
26 | runConfigsAnalysis,
27 | hyperPramsAnalysis,
28 | gradientAnalysis,
29 | parameterAnalysis,
30 | moduleAnalysis,
31 | stdOutAnalysis,
32 | stderrAnalysis,
33 | loggerAnalysis
34 | ]
35 |
36 | let sessionAnalyses: Analysis[] = [
37 | cpuAnalysis,
38 | processAnalysis,
39 | memoryAnalysis,
40 | diskAnalysis,
41 | gpuUtilAnalysis,
42 | gpuTempAnalysis,
43 | gpuMemoryAnalysis,
44 | gpuPowerAnalysis,
45 | batteryAnalysis,
46 | networkAnalysis,
47 | sessionConfigsAnalysis,
48 | ]
49 |
50 | export {
51 | experimentAnalyses,
52 | sessionAnalyses
53 | }
54 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/activations/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class ActivationsAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'outputs', statusCache)
7 | }
8 | }
9 |
10 | class ActivationsPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'outputs')
13 | }
14 | }
15 |
16 | let activationsCache = new AnalysisCache('run', ActivationsAnalysisCache, ActivationsPreferenceCache)
17 |
18 | export default activationsCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/activations/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import {AnalysisDataModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {SimpleLinesChart} from "../../../components/charts/simple_lines/chart"
6 | import activationsCache from "./cache"
7 | import {DataLoader} from "../../../components/loader"
8 | import {ROUTER} from '../../../app'
9 |
10 | export class ActivationsCard extends Card {
11 | uuid: string
12 | width: number
13 | analysisData: AnalysisDataModel
14 | analysisCache: AnalysisDataCache
15 | lineChartContainer: HTMLDivElement
16 | elem: HTMLDivElement
17 | private loader: DataLoader
18 |
19 | constructor(opt: CardOptions) {
20 | super(opt)
21 |
22 | this.uuid = opt.uuid
23 | this.width = opt.width
24 | this.analysisCache = activationsCache.getAnalysis(this.uuid)
25 | this.loader = new DataLoader(async (force) => {
26 | this.analysisData = await this.analysisCache.get(force)
27 | })
28 | }
29 |
30 | getLastUpdated(): number {
31 | return this.analysisCache.lastUpdated
32 | }
33 |
34 | async render($: WeyaElementFunction) {
35 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
36 | $('h3', '.header', 'Activations')
37 | this.loader.render($)
38 | this.lineChartContainer = $('div', '')
39 | })
40 |
41 | try {
42 | await this.loader.load()
43 |
44 | if (this.analysisData.summary.length > 0) {
45 | this.renderLineChart()
46 | } else {
47 | this.elem.classList.add('hide')
48 | }
49 | } catch (e) {
50 |
51 | }
52 | }
53 |
54 | renderLineChart() {
55 | this.lineChartContainer.innerHTML = ''
56 | $(this.lineChartContainer, $ => {
57 | new SimpleLinesChart({series: this.analysisData.summary, width: this.width}).render($)
58 | })
59 | }
60 |
61 | async refresh() {
62 | try {
63 | await this.loader.load(true)
64 | if (this.analysisData.summary.length > 0) {
65 | this.renderLineChart()
66 | this.elem.classList.remove('hide')
67 | }
68 | } catch (e) {
69 |
70 | }
71 | }
72 |
73 | onClick = () => {
74 | ROUTER.navigate(`/run/${this.uuid}/outputs`)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/activations/index.ts:
--------------------------------------------------------------------------------
1 | import {ActivationsCard} from "./card"
2 | import {ActivationsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let activationsAnalysis: Analysis = {
7 | card: ActivationsCard,
8 | viewHandler: ActivationsHandler,
9 | route: 'outputs'
10 | }
11 |
12 | export default activationsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/comaprison/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class ComparisonAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'metrics', statusCache)
7 | }
8 | }
9 |
10 | class ComparisonPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'compare')
13 | }
14 | }
15 |
16 | let comparisonCache = new AnalysisCache('run', ComparisonAnalysisCache, ComparisonPreferenceCache)
17 |
18 | export default comparisonCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/comaprison/index.ts:
--------------------------------------------------------------------------------
1 | import {ComparisonCard} from "./card"
2 | import {ComparisonHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let comparisonAnalysis: Analysis = {
7 | card: ComparisonCard,
8 | viewHandler: ComparisonHandler,
9 | route: 'compare'
10 | }
11 |
12 | export default comparisonAnalysis
13 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/configs/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import {Run} from "../../../models/run"
3 | import CACHE, {RunCache} from "../../../cache/cache"
4 | import {Card, CardOptions} from "../../types"
5 | import {DataLoader} from "../../../components/loader"
6 | import {Configs} from "./components"
7 | import {ROUTER} from '../../../app'
8 |
9 | export class RunConfigsCard extends Card {
10 | run: Run
11 | uuid: string
12 | width: number
13 | runCache: RunCache
14 | elem: HTMLDivElement
15 | configsContainer: HTMLDivElement
16 | private loader: DataLoader
17 |
18 | constructor(opt: CardOptions) {
19 | super(opt)
20 |
21 | this.uuid = opt.uuid
22 | this.width = opt.width
23 | this.runCache = CACHE.getRun(this.uuid)
24 | this.loader = new DataLoader(async (force) => {
25 | this.run = await this.runCache.get(force)
26 | })
27 | }
28 |
29 | getLastUpdated(): number {
30 | return this.runCache.lastUpdated
31 | }
32 |
33 | async render($: WeyaElementFunction) {
34 | this.elem = $('div','.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
35 | $('h3','.header', 'Configurations')
36 | this.loader.render($)
37 | this.configsContainer = $('div')
38 | })
39 |
40 | try {
41 | await this.loader.load()
42 |
43 | if (this.run.configs.length > 0) {
44 | this.renderConfigs()
45 | } else {
46 | this.elem.classList.add('hide')
47 | }
48 | } catch (e) {
49 |
50 | }
51 | }
52 |
53 | async refresh() {
54 | try {
55 | await this.loader.load(true)
56 | if (this.run.configs.length > 0) {
57 | this.renderConfigs()
58 | this.elem.classList.remove('hide')
59 | }
60 | } catch (e) {
61 |
62 | }
63 | }
64 |
65 | renderConfigs() {
66 | this.configsContainer.innerHTML = ''
67 | $(this.configsContainer, $ => {
68 | new Configs({configs: this.run.configs, width: this.width, isHyperParamOnly: true}).render($)
69 | })
70 | }
71 |
72 | onClick = () => {
73 | ROUTER.navigate(`/run/${this.uuid}/configs`)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/configs/index.ts:
--------------------------------------------------------------------------------
1 | import {RunConfigsCard} from "./card"
2 | import {RunConfigsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let runConfigsAnalysis: Analysis = {
7 | card: RunConfigsCard,
8 | viewHandler: RunConfigsHandler,
9 | route: 'configs'
10 | }
11 |
12 | export default runConfigsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/grads/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class GradientAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'gradients', statusCache)
7 | }
8 | }
9 |
10 | class GradientPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'gradients')
13 | }
14 | }
15 |
16 | let gradientsCache = new AnalysisCache('run', GradientAnalysisCache, GradientPreferenceCache)
17 |
18 | export default gradientsCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/grads/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {AnalysisDataModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {SimpleLinesChart} from "../../../components/charts/simple_lines/chart"
6 | import gradientsCache from "./cache"
7 | import {DataLoader} from "../../../components/loader"
8 | import {ROUTER} from '../../../app'
9 |
10 | export class GradientsCard extends Card {
11 | uuid: string
12 | width: number
13 | analysisData: AnalysisDataModel
14 | analysisCache: AnalysisDataCache
15 | lineChartContainer: HTMLDivElement
16 | elem: HTMLDivElement
17 | private loader: DataLoader
18 |
19 | constructor(opt: CardOptions) {
20 | super(opt)
21 |
22 | this.uuid = opt.uuid
23 | this.width = opt.width
24 | this.analysisCache = gradientsCache.getAnalysis(this.uuid)
25 | this.loader = new DataLoader(async (force) => {
26 | this.analysisData = await this.analysisCache.get(force)
27 | })
28 | }
29 |
30 | getLastUpdated(): number {
31 | return this.analysisCache.lastUpdated
32 | }
33 |
34 | async render($: WeyaElementFunction) {
35 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
36 | $('h3', '.header', 'Gradients')
37 | this.loader.render($)
38 | this.lineChartContainer = $('div', '')
39 | })
40 |
41 | try {
42 | await this.loader.load()
43 |
44 | if (this.analysisData.summary.length > 0) {
45 | this.renderLineChart()
46 | } else {
47 | this.elem.classList.add('hide')
48 | }
49 | } catch (e) {
50 |
51 | }
52 | }
53 |
54 | renderLineChart() {
55 | this.lineChartContainer.innerHTML = ''
56 | $(this.lineChartContainer, $ => {
57 | new SimpleLinesChart({series: this.analysisData.summary, width: this.width}).render($)
58 | })
59 | }
60 |
61 | async refresh() {
62 | try {
63 | await this.loader.load(true)
64 | if (this.analysisData.summary.length > 0) {
65 | this.renderLineChart()
66 | this.elem.classList.remove('hide')
67 | }
68 | } catch (e) {
69 |
70 | }
71 | }
72 |
73 | onClick = () => {
74 | ROUTER.navigate(`/run/${this.uuid}/grads`)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/grads/index.ts:
--------------------------------------------------------------------------------
1 | import {GradientsCard} from "./card"
2 | import {GradientsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let gradientsAnalysis: Analysis = {
7 | card: GradientsCard,
8 | viewHandler: GradientsHandler,
9 | route: 'grads'
10 | }
11 |
12 | export default gradientsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/hyper_params/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class HyperPramsAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'hyper_params', statusCache)
7 | }
8 | }
9 |
10 | class HyperPramsPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'hyper_params')
13 | }
14 | }
15 |
16 | let hyperParamsCache = new AnalysisCache('run', HyperPramsAnalysisCache, HyperPramsPreferenceCache)
17 |
18 | export default hyperParamsCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/hyper_params/index.ts:
--------------------------------------------------------------------------------
1 | import {HyperParamsCard} from "./card"
2 | import {HyperParamsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let hyperPramsAnalysis: Analysis = {
7 | card: HyperParamsCard,
8 | viewHandler: HyperParamsHandler,
9 | route: 'hyper_params'
10 | }
11 |
12 | export default hyperPramsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/logger/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import {Run} from "../../../models/run"
3 | import CACHE, {RunCache} from "../../../cache/cache"
4 | import {Card, CardOptions} from "../../types"
5 | import Filter from "../../../utils/ansi_to_html"
6 | import {DataLoader} from "../../../components/loader"
7 | import {ROUTER} from '../../../app'
8 |
9 | export class LoggerCard extends Card {
10 | run: Run
11 | uuid: string
12 | runCache: RunCache
13 | outputContainer: HTMLPreElement
14 | elem: HTMLDivElement
15 | filter: Filter
16 | private loader: DataLoader
17 |
18 | constructor(opt: CardOptions) {
19 | super(opt)
20 |
21 | this.uuid = opt.uuid
22 | this.runCache = CACHE.getRun(this.uuid)
23 | this.filter = new Filter({})
24 | this.loader = new DataLoader(async (force) => {
25 | this.run = await this.runCache.get(force)
26 | })
27 | }
28 |
29 | getLastTenLines(inputStr: string) {
30 | let split = inputStr.split("\n")
31 |
32 | let last10Lines
33 | if (split.length > 10) {
34 | last10Lines = split.slice(Math.max(split.length - 10, 1))
35 | } else {
36 | last10Lines = split
37 | }
38 |
39 | return last10Lines.join("\n")
40 | }
41 |
42 | getLastUpdated(): number {
43 | return this.runCache.lastUpdated
44 | }
45 |
46 | async render($: WeyaElementFunction) {
47 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
48 | $('h3', '.header', 'Standard Logger')
49 | this.loader.render($)
50 | $('div', '.terminal-card.no-scroll', $ => {
51 | this.outputContainer = $('pre', '')
52 | })
53 | })
54 |
55 | try {
56 | await this.loader.load()
57 |
58 | if (this.run.logger) {
59 | this.renderOutput()
60 | } else {
61 | this.elem.classList.add('hide')
62 | }
63 | } catch (e) {
64 |
65 | }
66 | }
67 |
68 | renderOutput() {
69 | this.outputContainer.innerHTML = ''
70 | $(this.outputContainer, $ => {
71 | let output = $('div', '')
72 | output.innerHTML = this.filter.toHtml(this.getLastTenLines(this.run.logger))
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.run.logger) {
80 | this.renderOutput()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/run/${this.uuid}/logger`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/logger/index.ts:
--------------------------------------------------------------------------------
1 | import {LoggerCard} from "./card"
2 | import {LoggerHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let loggerAnalysis: Analysis = {
7 | card: LoggerCard,
8 | viewHandler: LoggerHandler,
9 | route: 'logger'
10 | }
11 |
12 | export default loggerAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/metrics/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class MetricsAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'metrics', statusCache)
7 | }
8 | }
9 |
10 | class MetricsPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'metrics')
13 | }
14 | }
15 |
16 | let metricsCache = new AnalysisCache('run', MetricsAnalysisCache, MetricsPreferenceCache)
17 |
18 | export default metricsCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/metrics/index.ts:
--------------------------------------------------------------------------------
1 | import {MetricsCard} from "./card"
2 | import {MetricsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let metricsAnalysis: Analysis = {
7 | card: MetricsCard,
8 | viewHandler: MetricsHandler,
9 | route: 'metrics'
10 | }
11 |
12 | export default metricsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/params/cache.ts:
--------------------------------------------------------------------------------
1 | import {RunStatusCache, AnalysisDataCache, AnalysisPreferenceCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class ParameterAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: RunStatusCache) {
6 | super(uuid, 'parameters', statusCache)
7 | }
8 | }
9 |
10 | class ParameterPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'parameters')
13 | }
14 | }
15 |
16 | let parametersCache = new AnalysisCache('run', ParameterAnalysisCache, ParameterPreferenceCache)
17 |
18 | export default parametersCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/params/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {AnalysisDataModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {SimpleLinesChart} from "../../../components/charts/simple_lines/chart"
6 | import parametersCache from "./cache"
7 | import {DataLoader} from "../../../components/loader"
8 | import {ROUTER} from '../../../app'
9 |
10 | export class ParametersCard extends Card {
11 | uuid: string
12 | width: number
13 | analysisData: AnalysisDataModel
14 | analysisCache: AnalysisDataCache
15 | elem: HTMLDivElement
16 | lineChartContainer: HTMLDivElement
17 | private loader: DataLoader
18 |
19 | constructor(opt: CardOptions) {
20 | super(opt)
21 |
22 | this.uuid = opt.uuid
23 | this.width = opt.width
24 | this.analysisCache = parametersCache.getAnalysis(this.uuid)
25 | this.loader = new DataLoader(async (force) => {
26 | this.analysisData = await this.analysisCache.get(force)
27 | })
28 | }
29 |
30 | getLastUpdated(): number {
31 | return this.analysisCache.lastUpdated
32 | }
33 |
34 | async render($: WeyaElementFunction) {
35 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
36 | $('h3', '.header', 'Parameters')
37 | this.loader.render($)
38 | this.lineChartContainer = $('div', '')
39 | })
40 |
41 | try {
42 | await this.loader.load()
43 |
44 | if (this.analysisData.summary.length > 0) {
45 | this.renderLineChart()
46 | } else {
47 | this.elem.classList.add('hide')
48 | }
49 | } catch (e) {
50 |
51 | }
52 | }
53 |
54 | renderLineChart() {
55 | this.lineChartContainer.innerHTML = ''
56 | $(this.lineChartContainer, $ => {
57 | new SimpleLinesChart({series: this.analysisData.summary, width: this.width}).render($)
58 | })
59 | }
60 |
61 | async refresh() {
62 | try {
63 | await this.loader.load(true)
64 |
65 | if (this.analysisData.summary.length > 0) {
66 | this.renderLineChart()
67 | this.elem.classList.remove('hide')
68 | }
69 | } catch (e) {
70 |
71 | }
72 | }
73 |
74 | onClick = () => {
75 | ROUTER.navigate(`/run/${this.uuid}/params`)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/params/index.ts:
--------------------------------------------------------------------------------
1 | import {ParametersCard} from "./card"
2 | import {ParametersHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let parametersAnalysis: Analysis = {
7 | card: ParametersCard,
8 | viewHandler: ParametersHandler,
9 | route: 'params'
10 | }
11 |
12 | export default parametersAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/run_header/index.ts:
--------------------------------------------------------------------------------
1 | import {RunHeaderCard} from "./card"
2 | import {RunHeaderHandler} from "./view"
3 |
4 |
5 | let runHeaderAnalysis = {
6 | card: RunHeaderCard,
7 | viewHandler: RunHeaderHandler,
8 | route: 'header'
9 | }
10 |
11 | export default runHeaderAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/stderror/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import {Run} from "../../../models/run"
3 | import CACHE, {RunCache} from "../../../cache/cache"
4 | import {Card, CardOptions} from "../../types"
5 | import Filter from "../../../utils/ansi_to_html"
6 | import {DataLoader} from "../../../components/loader"
7 | import {ROUTER} from '../../../app';
8 |
9 | export class StdErrorCard extends Card {
10 | run: Run
11 | uuid: string
12 | runCache: RunCache
13 | outputContainer: HTMLPreElement
14 | elem: HTMLDivElement
15 | filter: Filter
16 | private loader: DataLoader
17 |
18 | constructor(opt: CardOptions) {
19 | super(opt)
20 |
21 | this.uuid = opt.uuid
22 | this.runCache = CACHE.getRun(this.uuid)
23 | this.filter = new Filter({})
24 | this.loader = new DataLoader(async (force) => {
25 | this.run = await this.runCache.get(force)
26 | })
27 | }
28 |
29 | getLastTenLines(inputStr: string) {
30 | let split = inputStr.split("\n")
31 |
32 | let last10Lines
33 | if (split.length > 10) {
34 | last10Lines = split.slice(Math.max(split.length - 10, 1))
35 | } else {
36 | last10Lines = split
37 | }
38 |
39 | return last10Lines.join("\n")
40 | }
41 |
42 | getLastUpdated(): number {
43 | return this.runCache.lastUpdated
44 | }
45 |
46 | async render($: WeyaElementFunction) {
47 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
48 | $('h3', '.header', 'Standard Error')
49 | this.loader.render($)
50 | $('div', '.terminal-card.no-scroll', $ => {
51 | this.outputContainer = $('pre', '')
52 | })
53 | })
54 |
55 | try {
56 | await this.loader.load()
57 |
58 | if (this.run.stderr) {
59 | this.renderOutput()
60 | } else {
61 | this.elem.classList.add('hide')
62 | }
63 | } catch (e) {
64 |
65 | }
66 | }
67 |
68 | renderOutput() {
69 | this.outputContainer.innerHTML = ''
70 | $(this.outputContainer, $ => {
71 | let output = $('div', '')
72 | output.innerHTML = this.filter.toHtml(this.getLastTenLines(this.run.stderr))
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.run.stderr) {
80 | this.renderOutput()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/run/${this.uuid}/stderr`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/stderror/index.ts:
--------------------------------------------------------------------------------
1 | import {StdErrorCard} from "./card"
2 | import {StdErrorHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let stdErrorAnalysis: Analysis = {
7 | card: StdErrorCard,
8 | viewHandler: StdErrorHandler,
9 | route: 'stderr'
10 | }
11 |
12 | export default stdErrorAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/stdout/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import {Run} from "../../../models/run"
3 | import CACHE, {RunCache} from "../../../cache/cache"
4 | import {Card, CardOptions} from "../../types"
5 | import Filter from "../../../utils/ansi_to_html"
6 | import {DataLoader} from "../../../components/loader"
7 | import {ROUTER} from '../../../app'
8 |
9 | export class StdOutCard extends Card {
10 | run: Run
11 | uuid: string
12 | runCache: RunCache
13 | outputContainer: HTMLPreElement
14 | elem: HTMLDivElement
15 | filter: Filter
16 | private loader: DataLoader
17 |
18 | constructor(opt: CardOptions) {
19 | super(opt)
20 |
21 | this.uuid = opt.uuid
22 | this.runCache = CACHE.getRun(this.uuid)
23 | this.filter = new Filter({})
24 | this.loader = new DataLoader(async (force) => {
25 | this.run = await this.runCache.get(force)
26 | })
27 | }
28 |
29 | getLastTenLines(inputStr: string) {
30 | let split = inputStr.split("\n")
31 |
32 | let last10Lines
33 | if (split.length > 10) {
34 | last10Lines = split.slice(Math.max(split.length - 10, 1))
35 | } else {
36 | last10Lines = split
37 | }
38 |
39 | return last10Lines.join("\n")
40 | }
41 |
42 | getLastUpdated(): number {
43 | return this.runCache.lastUpdated
44 | }
45 |
46 | async render($: WeyaElementFunction) {
47 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
48 | $('h3', '.header', 'Standard Output')
49 | this.loader.render($)
50 | $('div', '.terminal-card.no-scroll', $ => {
51 | this.outputContainer = $('pre', '')
52 | })
53 | })
54 |
55 | try {
56 | await this.loader.load()
57 |
58 | if (this.run.stdout) {
59 | this.renderOutput()
60 | } else {
61 | this.elem.classList.add('hide')
62 | }
63 | } catch (e) {
64 |
65 | }
66 | }
67 |
68 | renderOutput() {
69 | this.outputContainer.innerHTML = ''
70 | $(this.outputContainer, $ => {
71 | let output = $('div', '')
72 | output.innerHTML = this.filter.toHtml(this.getLastTenLines(this.run.stdout))
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.run.stdout) {
80 | this.renderOutput()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/run/${this.uuid}/stdout`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/experiments/stdout/index.ts:
--------------------------------------------------------------------------------
1 | import {StdOutCard} from "./card"
2 | import {StdOutHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let stdOutAnalysis: Analysis = {
7 | card: StdOutCard,
8 | viewHandler: StdOutHandler,
9 | route: 'stdout'
10 | }
11 |
12 | export default stdOutAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/helpers.ts:
--------------------------------------------------------------------------------
1 | import CACHE, {AnalysisDataCache, AnalysisPreferenceCache, RunStatusCache, SessionStatusCache} from "../cache/cache"
2 | import {ContentType} from '../types'
3 |
4 | export class AnalysisCache {
5 | private readonly type: ContentType
6 | private readonly series: new (uuid: string, status: RunStatusCache | SessionStatusCache) => TA
7 | private readonly seriesCaches: { [uuid: string]: AnalysisDataCache }
8 | private readonly preferences: new (uuid: string) => TAP
9 | private readonly preferencesCaches: { [uuid: string]: AnalysisPreferenceCache }
10 |
11 | constructor(type: ContentType, series: new (uuid: string, status: RunStatusCache | SessionStatusCache) => TA, preferences: new (uuid: string) => TAP) {
12 | this.type = type
13 | this.seriesCaches = {}
14 | this.preferencesCaches = {}
15 | this.series = series
16 | this.preferences = preferences
17 | }
18 |
19 | getAnalysis(uuid: string) {
20 | if (this.seriesCaches[uuid] == null) {
21 | this.seriesCaches[uuid] = new this.series(uuid, this.getStatus(uuid))
22 | }
23 |
24 | return this.seriesCaches[uuid]
25 | }
26 |
27 | getPreferences(uuid: string) {
28 | if (this.preferencesCaches[uuid] == null) {
29 | this.preferencesCaches[uuid] = new this.preferences(uuid)
30 | }
31 |
32 | return this.preferencesCaches[uuid]
33 | }
34 |
35 | private getStatus(uuid: string) {
36 | if (this.type === 'run') {
37 | return CACHE.getRunStatus(uuid)
38 | } else if (this.type === 'session') {
39 | return CACHE.getSessionStatus(uuid)
40 | }
41 |
42 | return null
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/battery/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class BatteryAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'battery', statusCache)
7 | }
8 | }
9 |
10 | class BatteryPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'battery')
13 | }
14 | }
15 |
16 | let batteryCache = new AnalysisCache('session', BatteryAnalysisCache, BatteryPreferenceCache)
17 |
18 | export default batteryCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/battery/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import batteryCache from './cache'
8 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class BatteryCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 | private labelsContainer: HTMLDivElement
21 |
22 | constructor(opt: CardOptions) {
23 | super(opt)
24 |
25 | this.uuid = opt.uuid
26 | this.width = opt.width
27 | this.analysisCache = batteryCache.getAnalysis(this.uuid)
28 | this.loader = new DataLoader(async (force) => {
29 | let data = (await this.analysisCache.get(force)).summary
30 | this.series = toPointValues(data)
31 | })
32 | }
33 |
34 | getLastUpdated(): number {
35 | return this.analysisCache.lastUpdated
36 | }
37 |
38 | async render($: WeyaElementFunction) {
39 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
40 | $('h3', '.header', 'Battery')
41 | this.loader.render($)
42 | this.lineChartContainer = $('div', '')
43 | this.labelsContainer = $('div', '')
44 | })
45 |
46 | try {
47 | await this.loader.load()
48 |
49 | if (this.series.length > 0) {
50 | this.renderLineChart()
51 | } else {
52 | this.elem.classList.add('hide')
53 | }
54 | } catch (e) {
55 |
56 | }
57 | }
58 |
59 | renderLineChart() {
60 | this.lineChartContainer.innerHTML = ''
61 | $(this.lineChartContainer, $ => {
62 | new TimeSeriesChart({
63 | series: this.series,
64 | width: this.width,
65 | plotIdx: [],
66 | yExtend: [0, 100],
67 | chartHeightFraction: 4,
68 | isDivergent: true
69 | }).render($)
70 | })
71 |
72 | this.labelsContainer.innerHTML = ''
73 | $(this.labelsContainer, $ => {
74 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
75 | })
76 | }
77 |
78 | async refresh() {
79 | try {
80 | await this.loader.load(true)
81 | if (this.series.length > 0) {
82 | this.renderLineChart()
83 | this.elem.classList.remove('hide')
84 | }
85 | } catch (e) {
86 |
87 | }
88 | }
89 |
90 | onClick = () => {
91 | ROUTER.navigate(`/session/${this.uuid}/battery`)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/battery/index.ts:
--------------------------------------------------------------------------------
1 | import {BatteryCard} from "./card"
2 | import {BatteryHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let batteryAnalysis: Analysis = {
6 | card: BatteryCard,
7 | viewHandler: BatteryHandler,
8 | route: 'battery'
9 | }
10 |
11 | export default batteryAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/configs/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction} from '../../../../../lib/weya/weya'
2 | import CACHE, {SessionCache} from "../../../cache/cache"
3 | import {Card, CardOptions} from "../../types"
4 | import {DataLoader} from "../../../components/loader"
5 | import {Configs} from "./components"
6 | import {ROUTER} from '../../../app'
7 | import {Config} from "../../../models/config"
8 |
9 | export class SessionConfigsCard extends Card {
10 | configs: Config[]
11 | uuid: string
12 | width: number
13 | sessionCache: SessionCache
14 | elem: HTMLDivElement
15 | configsContainer: HTMLDivElement
16 | private loader: DataLoader
17 |
18 | constructor(opt: CardOptions) {
19 | super(opt)
20 |
21 | this.uuid = opt.uuid
22 | this.width = opt.width
23 | this.sessionCache = CACHE.getSession(this.uuid)
24 | this.loader = new DataLoader(async (force) => {
25 | let session = await this.sessionCache.get(force)
26 | this.configs = session.configs.filter(conf => !conf['key'].includes('sensor'))
27 | })
28 | }
29 |
30 | getLastUpdated(): number {
31 | return this.sessionCache.lastUpdated
32 | }
33 |
34 | async render($: WeyaElementFunction) {
35 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
36 | $('h3', '.header', 'Configurations')
37 | this.loader.render($)
38 | this.configsContainer = $('div')
39 | })
40 |
41 | try {
42 | await this.loader.load()
43 |
44 | if (this.configs.length > 0) {
45 | this.renderConfigs()
46 | } else {
47 | this.elem.classList.add('hide')
48 | }
49 | } catch (e) {
50 |
51 | }
52 | }
53 |
54 | async refresh() {
55 | try {
56 | await this.loader.load(true)
57 | if (this.configs.length > 0) {
58 | this.renderConfigs()
59 | this.elem.classList.remove('hide')
60 | }
61 | } catch (e) {
62 |
63 | }
64 | }
65 |
66 | renderConfigs() {
67 | this.configsContainer.innerHTML = ''
68 | $(this.configsContainer, $ => {
69 | new Configs({configs: this.configs, width: this.width}).render($)
70 | })
71 | }
72 |
73 | onClick = () => {
74 | ROUTER.navigate(`/session/${this.uuid}/configs`)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/configs/components.ts:
--------------------------------------------------------------------------------
1 | import {Config} from "../../../models/config"
2 | import {WeyaElementFunction} from "../../../../../lib/weya/weya"
3 | import {ComputedValue, KEY_WIDTH, PADDING} from "../../experiments/configs/components"
4 |
5 |
6 | interface ConfigsOptions {
7 | configs: Config[]
8 | width: number
9 | }
10 |
11 | export class Configs {
12 | configs: Config[]
13 | width: number
14 | count: number
15 |
16 | constructor(opt: ConfigsOptions) {
17 | this.configs = opt.configs
18 | this.width = opt.width
19 | }
20 |
21 | render($: WeyaElementFunction) {
22 | $('div', '.configs.block.collapsed', {style: {width: `${this.width}px`}}, $ => {
23 | this.configs.map((c) =>
24 | $('div', '.info_list.config', $ => {
25 | $('span.key', c.key)
26 | $('span.combined', {style: {width: `${this.width - KEY_WIDTH - 2 * PADDING}px`}}, $ => {
27 | new ComputedValue({computed: c.value}).render($)
28 | })
29 | })
30 | )
31 | })
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/configs/index.ts:
--------------------------------------------------------------------------------
1 | import {SessionConfigsCard} from "./card"
2 | import {SessionConfigsHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 |
6 | let sessionConfigsAnalysis: Analysis = {
7 | card: SessionConfigsCard,
8 | viewHandler: SessionConfigsHandler,
9 | route: 'configs'
10 | }
11 |
12 | export default sessionConfigsAnalysis
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/cpu/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class CPUAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'cpu', statusCache)
7 | }
8 | }
9 |
10 | class CPUPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'cpu')
13 | }
14 | }
15 |
16 | let cpuCache = new AnalysisCache('session', CPUAnalysisCache, CPUPreferenceCache)
17 |
18 | export default cpuCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/cpu/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import cpuCache from './cache'
8 | import {TimeSeriesChart} from '../../../components/charts/timeseries/chart'
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class CPUCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 | private labelsContainer: HTMLDivElement
21 |
22 | constructor(opt: CardOptions) {
23 | super(opt)
24 |
25 | this.uuid = opt.uuid
26 | this.width = opt.width
27 | this.analysisCache = cpuCache.getAnalysis(this.uuid)
28 | this.loader = new DataLoader(async (force) => {
29 | this.series = toPointValues((await this.analysisCache.get(force)).summary)
30 | })
31 | }
32 |
33 | getLastUpdated(): number {
34 | return this.analysisCache.lastUpdated
35 | }
36 |
37 | async render($: WeyaElementFunction) {
38 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
39 | $('h3', '.header', 'CPU')
40 | this.loader.render($)
41 | this.lineChartContainer = $('div', '')
42 | this.labelsContainer = $('div', '')
43 | })
44 |
45 | try {
46 | await this.loader.load()
47 |
48 | if (this.series.length > 0) {
49 | this.renderLineChart()
50 | } else {
51 | this.elem.classList.add('hide')
52 | }
53 | } catch (e) {
54 |
55 | }
56 | }
57 |
58 | renderLineChart() {
59 | this.lineChartContainer.innerHTML = ''
60 | $(this.lineChartContainer, $ => {
61 | new TimeSeriesChart({
62 | series: this.series,
63 | width: this.width,
64 | plotIdx: [],
65 | yExtend: [0, 100],
66 | chartHeightFraction: 4,
67 | isDivergent: true
68 | }).render($)
69 | })
70 |
71 | this.labelsContainer.innerHTML = ''
72 | $(this.labelsContainer, $ => {
73 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
74 | })
75 | }
76 |
77 | async refresh() {
78 | try {
79 | await this.loader.load(true)
80 | if (this.series.length > 0) {
81 | this.renderLineChart()
82 | this.elem.classList.remove('hide')
83 | }
84 | } catch (e) {
85 |
86 | }
87 | }
88 |
89 | onClick = () => {
90 | ROUTER.navigate(`/session/${this.uuid}/cpu`)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/cpu/index.ts:
--------------------------------------------------------------------------------
1 | import {CPUCard} from "./card"
2 | import {CPUHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let cpuAnalysis: Analysis = {
6 | card: CPUCard,
7 | viewHandler: CPUHandler,
8 | route: 'cpu'
9 | }
10 |
11 | export default cpuAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/disk/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class DiskAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'disk', statusCache)
7 | }
8 | }
9 |
10 | class DiskPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'disk')
13 | }
14 | }
15 |
16 | let diskCache = new AnalysisCache('session', DiskAnalysisCache, DiskPreferenceCache)
17 |
18 | export default diskCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/disk/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import diskCache from './cache'
8 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class DiskCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 | private labelsContainer: HTMLDivElement
21 |
22 | constructor(opt: CardOptions) {
23 | super(opt)
24 |
25 | this.uuid = opt.uuid
26 | this.width = opt.width
27 | this.analysisCache = diskCache.getAnalysis(this.uuid)
28 | this.loader = new DataLoader(async (force) => {
29 | this.series = toPointValues((await this.analysisCache.get(force)).series)
30 | })
31 | }
32 |
33 | getLastUpdated(): number {
34 | return this.analysisCache.lastUpdated
35 | }
36 |
37 | async render($: WeyaElementFunction) {
38 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
39 | $('h3', '.header', 'Disk')
40 | this.loader.render($)
41 | this.lineChartContainer = $('div', '')
42 | this.labelsContainer = $('div', '')
43 | })
44 |
45 | try {
46 | await this.loader.load()
47 |
48 | if (this.series.length > 0) {
49 | this.renderLineChart()
50 | } else {
51 | this.elem.classList.add('hide')
52 | }
53 | } catch (e) {
54 |
55 | }
56 | }
57 |
58 | renderLineChart() {
59 | this.lineChartContainer.innerHTML = ''
60 | $(this.lineChartContainer, $ => {
61 | new TimeSeriesChart({
62 | series: this.series,
63 | width: this.width,
64 | plotIdx: [0, 1],
65 | chartHeightFraction: 4,
66 | isDivergent: true
67 | }).render($)
68 | })
69 |
70 | this.labelsContainer.innerHTML = ''
71 | $(this.labelsContainer, $ => {
72 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.series.length > 0) {
80 | this.renderLineChart()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/session/${this.uuid}/disk`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/disk/index.ts:
--------------------------------------------------------------------------------
1 | import {DiskCard} from "./card"
2 | import {DiskHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let diskAnalysis: Analysis = {
6 | card: DiskCard,
7 | viewHandler: DiskHandler,
8 | route: 'disk'
9 | }
10 |
11 | export default diskAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class GPUAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'gpu', statusCache)
7 | }
8 | }
9 |
10 | class GPUPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'gpu')
13 | }
14 | }
15 |
16 | let gpuCache = new AnalysisCache('session', GPUAnalysisCache, GPUPreferenceCache)
17 |
18 | export default gpuCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/index.ts:
--------------------------------------------------------------------------------
1 | import {GPUUtilCard} from "./util_card"
2 | import {GPUTempCard} from "./temp_card"
3 | import {GPUMemoryCard} from "./memory_card"
4 | import {GPUPowerCard} from "./power_card"
5 | import {GPUUtilHandler} from "./util_view"
6 | import {GPUTempHandler} from "./temp_view"
7 | import {GPUMemoryHandler} from "./memory_view"
8 | import {GPUPowerHandler} from "./power_view"
9 | import {Analysis} from "../../types"
10 |
11 | let gpuUtilAnalysis: Analysis = {
12 | card: GPUUtilCard,
13 | viewHandler: GPUUtilHandler,
14 | route: 'gpu_util'
15 | }
16 |
17 | let gpuTempAnalysis: Analysis = {
18 | card: GPUTempCard,
19 | viewHandler: GPUTempHandler,
20 | route: 'gpu_temp'
21 | }
22 |
23 | let gpuMemoryAnalysis: Analysis = {
24 | card: GPUMemoryCard,
25 | viewHandler: GPUMemoryHandler,
26 | route: 'gpu_memory'
27 | }
28 |
29 | let gpuPowerAnalysis: Analysis = {
30 | card: GPUPowerCard,
31 | viewHandler: GPUPowerHandler,
32 | route: 'gpu_power'
33 | }
34 |
35 | export {
36 | gpuUtilAnalysis,
37 | gpuTempAnalysis,
38 | gpuMemoryAnalysis,
39 | gpuPowerAnalysis
40 | }
41 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/memory_card.ts:
--------------------------------------------------------------------------------
1 | import {SeriesModel} from "../../../models/run"
2 | import {AnalysisDataCache} from "../../../cache/cache"
3 | import {Weya as $, WeyaElementFunction} from "../../../../../lib/weya/weya"
4 | import {DataLoader} from "../../../components/loader"
5 | import {Card, CardOptions} from "../../types"
6 | import gpuCache from "./cache"
7 | import {getSeriesData} from "./utils"
8 | import {Labels} from "../../../components/charts/labels"
9 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
10 | import {ROUTER} from "../../../app"
11 |
12 | export class GPUMemoryCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | plotIdx: number[] = []
20 | private loader: DataLoader
21 | private labelsContainer: HTMLDivElement
22 |
23 | constructor(opt: CardOptions) {
24 | super(opt)
25 |
26 | this.uuid = opt.uuid
27 | this.width = opt.width
28 | this.analysisCache = gpuCache.getAnalysis(this.uuid)
29 | this.loader = new DataLoader(async (force) => {
30 | this.series = getSeriesData((await this.analysisCache.get(force)).series, 'memory')
31 | })
32 | }
33 |
34 | getLastUpdated(): number {
35 | return this.analysisCache.lastUpdated
36 | }
37 |
38 | async render($: WeyaElementFunction) {
39 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
40 | $('h3', '.header', 'GPU - Memory')
41 | this.loader.render($)
42 | this.lineChartContainer = $('div', '')
43 | this.labelsContainer = $('div', '')
44 | })
45 |
46 | try {
47 | await this.loader.load()
48 |
49 | if (this.series.length > 0) {
50 | this.renderLineChart()
51 | } else {
52 | this.elem.classList.add('hide')
53 | }
54 | } catch (e) {
55 |
56 | }
57 | }
58 |
59 | renderLineChart() {
60 | let res: number[] = []
61 | for (let i = 0; i < this.series.length; i++) {
62 | res.push(i)
63 | }
64 | this.plotIdx = res
65 |
66 | this.lineChartContainer.innerHTML = ''
67 | $(this.lineChartContainer, $ => {
68 | new TimeSeriesChart({
69 | series: this.series,
70 | width: this.width,
71 | plotIdx: this.plotIdx,
72 | chartHeightFraction: 4,
73 | isDivergent: true
74 | }).render($)
75 | })
76 |
77 | this.labelsContainer.innerHTML = ''
78 | $(this.labelsContainer, $ => {
79 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
80 | })
81 | }
82 |
83 | async refresh() {
84 | try {
85 | await this.loader.load(true)
86 | if (this.series.length > 0) {
87 | this.renderLineChart()
88 | this.elem.classList.remove('hide')
89 | }
90 | } catch (e) {
91 |
92 | }
93 | }
94 |
95 | onClick = () => {
96 | ROUTER.navigate(`/session/${this.uuid}/gpu_memory`)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/power_card.ts:
--------------------------------------------------------------------------------
1 | import {SeriesModel} from "../../../models/run"
2 | import {AnalysisDataCache} from "../../../cache/cache"
3 | import {Weya as $, WeyaElementFunction} from "../../../../../lib/weya/weya"
4 | import {DataLoader} from "../../../components/loader"
5 | import {Card, CardOptions} from "../../types"
6 | import gpuCache from "./cache"
7 | import {getSeriesData} from "./utils"
8 | import {Labels} from "../../../components/charts/labels"
9 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
10 | import {ROUTER} from "../../../app"
11 |
12 | export class GPUPowerCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | plotIdx: number[] = []
20 | private loader: DataLoader
21 | private labelsContainer: HTMLDivElement
22 |
23 | constructor(opt: CardOptions) {
24 | super(opt)
25 |
26 | this.uuid = opt.uuid
27 | this.width = opt.width
28 | this.analysisCache = gpuCache.getAnalysis(this.uuid)
29 | this.loader = new DataLoader(async (force) => {
30 | this.series = getSeriesData((await this.analysisCache.get(force)).series, 'power')
31 | })
32 | }
33 |
34 | getLastUpdated(): number {
35 | return this.analysisCache.lastUpdated
36 | }
37 |
38 | async render($: WeyaElementFunction) {
39 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
40 | $('h3', '.header', 'GPU - Power')
41 | this.loader.render($)
42 | this.lineChartContainer = $('div', '')
43 | this.labelsContainer = $('div', '')
44 | })
45 |
46 | try {
47 | await this.loader.load()
48 |
49 | if (this.series.length > 0) {
50 | this.renderLineChart()
51 | } else {
52 | this.elem.classList.add('hide')
53 | }
54 | } catch (e) {
55 |
56 | }
57 | }
58 |
59 | renderLineChart() {
60 | let res: number[] = []
61 | for (let i = 0; i < this.series.length; i++) {
62 | res.push(i)
63 | }
64 | this.plotIdx = res
65 |
66 | this.lineChartContainer.innerHTML = ''
67 | $(this.lineChartContainer, $ => {
68 | new TimeSeriesChart({
69 | series: this.series,
70 | width: this.width,
71 | plotIdx: this.plotIdx,
72 | chartHeightFraction: 4,
73 | isDivergent: true
74 | }).render($)
75 | })
76 |
77 | this.labelsContainer.innerHTML = ''
78 | $(this.labelsContainer, $ => {
79 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
80 | })
81 | }
82 |
83 | async refresh() {
84 | try {
85 | await this.loader.load(true)
86 | if (this.series.length > 0) {
87 | this.renderLineChart()
88 | this.elem.classList.remove('hide')
89 | }
90 | } catch (e) {
91 |
92 | }
93 | }
94 |
95 | onClick = () => {
96 | ROUTER.navigate(`/session/${this.uuid}/gpu_power`)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/temp_card.ts:
--------------------------------------------------------------------------------
1 | import {SeriesModel} from "../../../models/run"
2 | import {AnalysisDataCache} from "../../../cache/cache"
3 | import {Weya as $, WeyaElementFunction} from "../../../../../lib/weya/weya"
4 | import {DataLoader} from "../../../components/loader"
5 | import {Card, CardOptions} from "../../types"
6 | import gpuCache from "./cache"
7 | import {getSeriesData} from "./utils"
8 | import {Labels} from "../../../components/charts/labels"
9 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
10 | import {ROUTER} from "../../../app"
11 |
12 | export class GPUTempCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | plotIdx: number[] = []
20 | private loader: DataLoader
21 | private labelsContainer: HTMLDivElement
22 |
23 | constructor(opt: CardOptions) {
24 | super(opt)
25 |
26 | this.uuid = opt.uuid
27 | this.width = opt.width
28 | this.analysisCache = gpuCache.getAnalysis(this.uuid)
29 | this.loader = new DataLoader(async (force) => {
30 | this.series = getSeriesData((await this.analysisCache.get(force)).series, 'temperature')
31 | })
32 | }
33 |
34 | getLastUpdated(): number {
35 | return this.analysisCache.lastUpdated
36 | }
37 |
38 | async render($: WeyaElementFunction) {
39 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
40 | $('h3', '.header', 'GPU - Temperature')
41 | this.loader.render($)
42 | this.lineChartContainer = $('div', '')
43 | this.labelsContainer = $('div', '')
44 | })
45 |
46 | try {
47 | await this.loader.load()
48 |
49 | if (this.series.length > 0) {
50 | this.renderLineChart()
51 | } else {
52 | this.elem.classList.add('hide')
53 | }
54 | } catch (e) {
55 |
56 | }
57 | }
58 |
59 | renderLineChart() {
60 | let res: number[] = []
61 | for (let i = 0; i < this.series.length; i++) {
62 | res.push(i)
63 | }
64 | this.plotIdx = res
65 |
66 | this.lineChartContainer.innerHTML = ''
67 | $(this.lineChartContainer, $ => {
68 | new TimeSeriesChart({
69 | series: this.series,
70 | width: this.width,
71 | plotIdx: this.plotIdx,
72 | chartHeightFraction: 4,
73 | isDivergent: true
74 | }).render($)
75 | })
76 |
77 | this.labelsContainer.innerHTML = ''
78 | $(this.labelsContainer, $ => {
79 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
80 | })
81 | }
82 |
83 | async refresh() {
84 | try {
85 | await this.loader.load(true)
86 | if (this.series.length > 0) {
87 | this.renderLineChart()
88 | this.elem.classList.remove('hide')
89 | }
90 | } catch (e) {
91 |
92 | }
93 | }
94 |
95 | onClick = () => {
96 | ROUTER.navigate(`/session/${this.uuid}/gpu_temp`)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/util_card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {DataLoader} from "../../../components/loader"
6 | import gpuCache from './cache'
7 | import {getSeriesData} from './utils'
8 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class GPUUtilCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | plotIdx: number[] = []
20 | private loader: DataLoader
21 | private labelsContainer: HTMLDivElement
22 |
23 | constructor(opt: CardOptions) {
24 | super(opt)
25 |
26 | this.uuid = opt.uuid
27 | this.width = opt.width
28 | this.analysisCache = gpuCache.getAnalysis(this.uuid)
29 | this.loader = new DataLoader(async (force) => {
30 | this.series = getSeriesData((await this.analysisCache.get(force)).series, 'utilization')
31 | })
32 | }
33 |
34 | getLastUpdated(): number {
35 | return this.analysisCache.lastUpdated
36 | }
37 |
38 | async render($: WeyaElementFunction) {
39 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
40 | $('h3', '.header', 'GPU - Utilization')
41 | this.loader.render($)
42 | this.lineChartContainer = $('div', '')
43 | this.labelsContainer = $('div', '')
44 | })
45 |
46 | try {
47 | await this.loader.load()
48 |
49 | if (this.series.length > 0) {
50 | this.renderLineChart()
51 | } else {
52 | this.elem.classList.add('hide')
53 | }
54 | } catch (e) {
55 |
56 | }
57 | }
58 |
59 | renderLineChart() {
60 | let res: number[] = []
61 | for (let i = 0; i < this.series.length; i++) {
62 | res.push(i)
63 | }
64 | this.plotIdx = res
65 |
66 | this.lineChartContainer.innerHTML = ''
67 | $(this.lineChartContainer, $ => {
68 | new TimeSeriesChart({
69 | series: this.series,
70 | width: this.width,
71 | plotIdx: this.plotIdx,
72 | yExtend: [0, 100],
73 | chartHeightFraction: 4,
74 | isDivergent: true
75 | }).render($)
76 | })
77 |
78 | this.labelsContainer.innerHTML = ''
79 | $(this.labelsContainer, $ => {
80 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
81 | })
82 | }
83 |
84 | async refresh() {
85 | try {
86 | await this.loader.load(true)
87 | if (this.series.length > 0) {
88 | this.renderLineChart()
89 | this.elem.classList.remove('hide')
90 | }
91 | } catch (e) {
92 |
93 | }
94 | }
95 |
96 | onClick = () => {
97 | ROUTER.navigate(`/session/${this.uuid}/gpu_util`)
98 | }
99 | }
100 |
101 |
102 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/gpu/utils.ts:
--------------------------------------------------------------------------------
1 | import {SeriesModel} from "../../../models/run"
2 | import {toPointValues} from "../../../components/charts/utils"
3 |
4 | export function getSeriesData(series: SeriesModel[], analysis: string, isMean: boolean = false) {
5 | let res = []
6 | for (let r of series) {
7 | let s = {...r}
8 | if (s.name.includes(analysis)) {
9 | if (s.name.includes('mean')) {
10 | if (isMean) {
11 | s.name = 'mean'
12 | return toPointValues([s])
13 | } else {
14 | continue
15 | }
16 | }
17 | s.name = s.name.replace(/\D/g, '')
18 | res.push(s)
19 | }
20 | }
21 |
22 | return toPointValues(res)
23 | }
24 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/memory/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class MemoryAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'memory', statusCache)
7 | }
8 | }
9 |
10 | class MemoryPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'memory')
13 | }
14 | }
15 |
16 | let memoryCache = new AnalysisCache('session', MemoryAnalysisCache, MemoryPreferenceCache)
17 |
18 | export default memoryCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/memory/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import memoryCache from './cache'
8 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class MemoryCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 | private labelsContainer: HTMLDivElement
21 |
22 | constructor(opt: CardOptions) {
23 | super(opt)
24 |
25 | this.uuid = opt.uuid
26 | this.width = opt.width
27 | this.analysisCache = memoryCache.getAnalysis(this.uuid)
28 | this.loader = new DataLoader(async (force) => {
29 | this.series = toPointValues((await this.analysisCache.get(force)).series)
30 | })
31 | }
32 |
33 | getLastUpdated(): number {
34 | return this.analysisCache.lastUpdated
35 | }
36 |
37 | async render($: WeyaElementFunction) {
38 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
39 | $('h3', '.header', 'Memory')
40 | this.loader.render($)
41 | this.lineChartContainer = $('div', '')
42 | this.labelsContainer = $('div', '')
43 | })
44 |
45 | try {
46 | await this.loader.load()
47 |
48 | if (this.series.length > 0) {
49 | this.renderLineChart()
50 | } else {
51 | this.elem.classList.add('hide')
52 | }
53 | } catch (e) {
54 |
55 | }
56 | }
57 |
58 | renderLineChart() {
59 | this.lineChartContainer.innerHTML = ''
60 | $(this.lineChartContainer, $ => {
61 | new TimeSeriesChart({
62 | series: this.series,
63 | width: this.width,
64 | plotIdx: [0, 1],
65 | chartHeightFraction: 4,
66 | isDivergent: true
67 | }).render($)
68 | })
69 |
70 | this.labelsContainer.innerHTML = ''
71 | $(this.labelsContainer, $ => {
72 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.series.length > 0) {
80 | this.renderLineChart()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/session/${this.uuid}/memory`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/memory/index.ts:
--------------------------------------------------------------------------------
1 | import {MemoryCard} from "./card"
2 | import {MemoryHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let memoryAnalysis: Analysis = {
6 | card: MemoryCard,
7 | viewHandler: MemoryHandler,
8 | route: 'memory'
9 | }
10 |
11 | export default memoryAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/network/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 |
4 | class NetworkAnalysisCache extends AnalysisDataCache {
5 | constructor(uuid: string, statusCache: SessionStatusCache) {
6 | super(uuid, 'network', statusCache)
7 | }
8 | }
9 |
10 | class NetworkPreferenceCache extends AnalysisPreferenceCache {
11 | constructor(uuid: string) {
12 | super(uuid, 'network')
13 | }
14 | }
15 |
16 | let networkCache = new AnalysisCache('session', NetworkAnalysisCache, NetworkPreferenceCache)
17 |
18 | export default networkCache
19 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/network/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import networkCache from './cache'
8 | import {TimeSeriesChart} from "../../../components/charts/timeseries/chart"
9 | import {Labels} from "../../../components/charts/labels"
10 | import {ROUTER} from '../../../app'
11 |
12 | export class NetworkCard extends Card {
13 | uuid: string
14 | width: number
15 | series: SeriesModel[]
16 | analysisCache: AnalysisDataCache
17 | lineChartContainer: HTMLDivElement
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 | private labelsContainer: HTMLDivElement
21 |
22 | constructor(opt: CardOptions) {
23 | super(opt)
24 |
25 | this.uuid = opt.uuid
26 | this.width = opt.width
27 | this.analysisCache = networkCache.getAnalysis(this.uuid)
28 | this.loader = new DataLoader(async (force) => {
29 | this.series = toPointValues((await this.analysisCache.get(force)).series)
30 | })
31 | }
32 |
33 | getLastUpdated(): number {
34 | return this.analysisCache.lastUpdated
35 | }
36 |
37 | async render($: WeyaElementFunction) {
38 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
39 | $('h3', '.header', 'Network')
40 | this.loader.render($)
41 | this.lineChartContainer = $('div', '')
42 | this.labelsContainer = $('div', '')
43 | })
44 |
45 | try {
46 | await this.loader.load()
47 |
48 | if (this.series.length > 0) {
49 | this.renderLineChart()
50 | } else {
51 | this.elem.classList.add('hide')
52 | }
53 | } catch (e) {
54 |
55 | }
56 | }
57 |
58 | renderLineChart() {
59 | this.lineChartContainer.innerHTML = ''
60 | $(this.lineChartContainer, $ => {
61 | new TimeSeriesChart({
62 | series: this.series,
63 | width: this.width,
64 | plotIdx: [0],
65 | chartHeightFraction: 4,
66 | isDivergent: true
67 | }).render($)
68 | })
69 |
70 | this.labelsContainer.innerHTML = ''
71 | $(this.labelsContainer, $ => {
72 | new Labels({labels: Array.from(this.series, x => x['name']), isDivergent: true}).render($)
73 | })
74 | }
75 |
76 | async refresh() {
77 | try {
78 | await this.loader.load(true)
79 | if (this.series.length > 0) {
80 | this.renderLineChart()
81 | this.elem.classList.remove('hide')
82 | }
83 | } catch (e) {
84 |
85 | }
86 | }
87 |
88 | onClick = () => {
89 | ROUTER.navigate(`/session/${this.uuid}/network`)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/network/index.ts:
--------------------------------------------------------------------------------
1 | import {NetworkCard} from "./card"
2 | import {NetworkHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let networkAnalysis: Analysis = {
6 | card: NetworkCard,
7 | viewHandler: NetworkHandler,
8 | route: 'network'
9 | }
10 |
11 | export default networkAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/process/cache.ts:
--------------------------------------------------------------------------------
1 | import {AnalysisDataCache, AnalysisPreferenceCache, SessionStatusCache} from "../../../cache/cache"
2 | import {AnalysisCache} from "../../helpers"
3 | import {DetailsCache, DetailsDataCache} from "./cache_helper"
4 |
5 | class ProcessAnalysisCache extends AnalysisDataCache {
6 | constructor(uuid: string, statusCache: SessionStatusCache) {
7 | super(uuid, 'process', statusCache)
8 | }
9 | }
10 |
11 | class ProcessPreferenceCache extends AnalysisPreferenceCache {
12 | constructor(uuid: string) {
13 | super(uuid, 'process')
14 | }
15 | }
16 |
17 | let processCache = new AnalysisCache('session', ProcessAnalysisCache, ProcessPreferenceCache)
18 |
19 | class ProcessDetailsCache extends DetailsDataCache {
20 | constructor(uuid: string, processId: string, statusCache: SessionStatusCache) {
21 | super(uuid, processId, statusCache)
22 | }
23 | }
24 |
25 | let processDetailsCache = new DetailsCache(ProcessDetailsCache)
26 |
27 | export {
28 | processCache,
29 | processDetailsCache
30 | }
31 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/process/cache_helper.ts:
--------------------------------------------------------------------------------
1 | import NETWORK from "../../../network"
2 | import CACHE, {CacheObject, isReloadTimeout, SessionStatusCache, StatusCache} from "../../../cache/cache"
3 | import {ProcessDetailsModel} from "./types"
4 |
5 | export class DetailsDataCache extends CacheObject {
6 | private readonly uuid: string
7 | private readonly processId: string
8 | private statusCache: StatusCache
9 |
10 | constructor(uuid: string, processId: string, statusCache: StatusCache) {
11 | super()
12 | this.uuid = uuid
13 | this.processId = processId
14 | this.statusCache = statusCache
15 | }
16 |
17 | async load(): Promise {
18 | return this.broadcastPromise.create(async () => {
19 | return await NETWORK.getCustomAnalysis(`process/${this.uuid}/details/${this.processId}`)
20 | })
21 | }
22 |
23 | async get(isRefresh = false): Promise {
24 | let status = await this.statusCache.get()
25 |
26 | if (this.data == null || (status.isRunning && isReloadTimeout(this.lastUpdated)) || isRefresh) {
27 | this.data = await this.load()
28 | this.lastUpdated = (new Date()).getTime()
29 |
30 | if ((status.isRunning && isReloadTimeout(this.lastUpdated)) || isRefresh) {
31 | await this.statusCache.get(true)
32 | }
33 | }
34 |
35 | return this.data
36 | }
37 | }
38 |
39 | export class DetailsCache {
40 | private readonly series: new (uuid: string, processId: string, status: SessionStatusCache) => TA
41 | private readonly processCache: { [uuid: string]: { [processId: string]: DetailsDataCache } }
42 |
43 | constructor(series: new (uuid: string, processId: string, status: SessionStatusCache) => TA) {
44 | this.processCache = {}
45 | this.series = series
46 | }
47 |
48 | getAnalysis(uuid: string, processId: string) {
49 | if (this.processCache[uuid] == null) {
50 | let seriesCaches = {}
51 | seriesCaches[processId] = new this.series(uuid, processId, CACHE.getSessionStatus(uuid))
52 | this.processCache[uuid] = seriesCaches
53 | } else if (this.processCache[uuid][processId] == null) {
54 | this.processCache[uuid][processId] = new this.series(uuid, processId, CACHE.getSessionStatus(uuid))
55 | }
56 |
57 | return this.processCache[uuid][processId]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/process/card.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $, WeyaElementFunction,} from '../../../../../lib/weya/weya'
2 | import {SeriesModel} from "../../../models/run"
3 | import {Card, CardOptions} from "../../types"
4 | import {AnalysisDataCache} from "../../../cache/cache"
5 | import {toPointValues} from "../../../components/charts/utils"
6 | import {DataLoader} from "../../../components/loader"
7 | import {processCache} from './cache'
8 | import {ROUTER} from '../../../app'
9 | import {SparkTimeLines} from "../../../components/charts/spark_time_lines/chart"
10 |
11 | export class ProcessCard extends Card {
12 | uuid: string
13 | width: number
14 | series: SeriesModel[]
15 | analysisCache: AnalysisDataCache
16 | sparkLinesContainer: HTMLDivElement
17 | sparkTimeLines: SparkTimeLines
18 | elem: HTMLDivElement
19 | private loader: DataLoader
20 |
21 | constructor(opt: CardOptions) {
22 | super(opt)
23 |
24 | this.uuid = opt.uuid
25 | this.width = opt.width
26 | this.analysisCache = processCache.getAnalysis(this.uuid)
27 | this.loader = new DataLoader(async (force) => {
28 | this.series = toPointValues((await this.analysisCache.get(force)).summary)
29 | })
30 | }
31 |
32 | getLastUpdated(): number {
33 | return this.analysisCache.lastUpdated
34 | }
35 |
36 | async render($: WeyaElementFunction) {
37 | this.elem = $('div', '.labml-card.labml-card-action', {on: {click: this.onClick}}, $ => {
38 | $('h3', '.header', 'Processes')
39 | this.loader.render($)
40 | this.sparkLinesContainer = $('div', '')
41 | })
42 |
43 | try {
44 | await this.loader.load()
45 |
46 | if (this.series.length > 0) {
47 | this.renderSparkLines()
48 | } else {
49 | this.elem.classList.add('hide')
50 | }
51 | } catch (e) {
52 |
53 | }
54 | }
55 |
56 | renderSparkLines() {
57 | this.sparkLinesContainer.innerHTML = ''
58 | $(this.sparkLinesContainer, $ => {
59 | this.sparkTimeLines = new SparkTimeLines({
60 | series: this.series,
61 | plotIdx: [1, 2, 3, 4, 5],
62 | width: this.width,
63 | isColorless: true
64 | })
65 | this.sparkTimeLines.render($)
66 | })
67 | }
68 |
69 | async refresh() {
70 | try {
71 | await this.loader.load(true)
72 | if (this.series.length > 0) {
73 | this.renderSparkLines()
74 | this.elem.classList.remove('hide')
75 | }
76 | } catch (e) {
77 |
78 | }
79 | }
80 |
81 | onClick = () => {
82 | ROUTER.navigate(`/session/${this.uuid}/process`)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/process/index.ts:
--------------------------------------------------------------------------------
1 | import {ProcessCard} from "./card"
2 | import {ProcessHandler} from "./view"
3 | import {Analysis} from "../../types"
4 |
5 | let processAnalysis: Analysis = {
6 | card: ProcessCard,
7 | viewHandler: ProcessHandler,
8 | route: 'process'
9 | }
10 |
11 | export default processAnalysis
12 |
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/process/types.ts:
--------------------------------------------------------------------------------
1 | import {SeriesModel} from "../../../models/run"
2 |
3 | export interface ProcessModel {
4 | process_id: string
5 | name: string
6 | cpu: SeriesModel
7 | rss: SeriesModel
8 | dead: number
9 | pid: number
10 | }
11 |
12 | export interface ProcessDetailsModel extends ProcessModel {
13 | create_time: number,
14 | cmdline: string,
15 | exe: string
16 | ppid: number
17 | series: SeriesModel[]
18 | }
--------------------------------------------------------------------------------
/ui/src/analyses/sessions/session_header/index.ts:
--------------------------------------------------------------------------------
1 | import {SessionHeaderCard} from "./card"
2 | import {SessionHeaderHandler} from "./view"
3 |
4 | let sessionHeaderAnalysis = {
5 | card: SessionHeaderCard,
6 | viewHandler: SessionHeaderHandler,
7 | route: 'header'
8 | }
9 |
10 | export default sessionHeaderAnalysis
11 |
--------------------------------------------------------------------------------
/ui/src/analyses/types.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from "../../../lib/weya/weya"
2 |
3 | export interface CardOptions {
4 | uuid: string
5 | width: number
6 | }
7 |
8 | export abstract class Card {
9 | protected constructor(opt: CardOptions) {
10 | }
11 |
12 | abstract render($: WeyaElementFunction)
13 |
14 | abstract refresh()
15 |
16 | abstract getLastUpdated(): number
17 | }
18 |
19 | export abstract class ViewHandler {
20 |
21 | }
22 |
23 | export interface Analysis {
24 | card: new (opt: CardOptions) => Card
25 | viewHandler: new () => ViewHandler
26 | route: string
27 | }
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ui/src/app.ts:
--------------------------------------------------------------------------------
1 | import {ScreenContainer} from './screen'
2 | import {Router} from '../../lib/weya/router'
3 |
4 |
5 | export let ROUTER = new Router({
6 | emulateState: false,
7 | hashChange: false,
8 | pushState: true,
9 | root: '/',
10 | onerror: e => {
11 | console.error('Error', e)
12 | }
13 | })
14 |
15 | export let SCREEN = new ScreenContainer()
--------------------------------------------------------------------------------
/ui/src/components/badge.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from "../../../lib/weya/weya"
2 |
3 | interface BadgeViewOptions {
4 | text: string
5 | }
6 |
7 | export class BadgeView {
8 | text: string
9 |
10 | constructor(opt: BadgeViewOptions) {
11 | this.text = opt.text
12 | }
13 |
14 | render($: WeyaElementFunction) {
15 | $('div.label.label-info', this.text)
16 | }
17 | }
--------------------------------------------------------------------------------
/ui/src/components/charts/axis.ts:
--------------------------------------------------------------------------------
1 | import d3 from "../../d3"
2 | import {WeyaElementFunction} from "../../../../lib/weya/weya"
3 |
4 | interface AxisOptions {
5 | chartId: string
6 | scale: d3.ScaleLinear
7 | specifier?: string
8 | numTicks?: number
9 | }
10 |
11 | export class RightAxis {
12 | scale: d3.ScaleLinear
13 | specifier?: string
14 | numTicks?: number
15 | id: string
16 | axis
17 |
18 | constructor(opt: AxisOptions) {
19 | this.specifier = opt.specifier !== undefined ? opt.specifier : ""
20 | this.numTicks = opt.numTicks !== undefined ? opt.numTicks : 5
21 | this.id = `${opt.chartId}_axis_right`
22 | this.axis = d3.axisRight(opt.scale as d3.AxisScale).ticks(this.numTicks, this.specifier)
23 | }
24 |
25 | render($: WeyaElementFunction) {
26 | $('g', {id: this.id})
27 |
28 | let layer = d3.select(`#${this.id}`)
29 | layer.selectAll('g').remove()
30 | layer.append('g').call(this.axis)
31 | }
32 | }
33 |
34 | export class BottomAxis {
35 | scale: d3.ScaleLinear
36 | specifier?: string
37 | id: string
38 | axis
39 |
40 | constructor(opt: AxisOptions) {
41 | this.specifier = opt.specifier !== undefined ? opt.specifier : ".2s"
42 | this.id = `${opt.chartId}_axis_bottom`
43 | this.axis = d3.axisBottom(opt.scale as d3.AxisScale).ticks(5, this.specifier)
44 | }
45 |
46 | render($: WeyaElementFunction) {
47 | $('g', {id: this.id})
48 |
49 | let layer = d3.select(`#${this.id}`)
50 | layer.selectAll('g').remove()
51 | layer.append('g').call(this.axis)
52 | }
53 | }
54 |
55 | interface TimeAxisOptions {
56 | chartId: string
57 | scale: d3.ScaleTime
58 | numTicks?: number
59 | }
60 |
61 | export class BottomTimeAxis {
62 | scale: d3.ScaleTime
63 | numTicks?: number
64 | id: string
65 | axis
66 |
67 | constructor(opt: TimeAxisOptions) {
68 | this.numTicks = opt.numTicks | 5
69 | this.id = `${opt.chartId}_axis_bottom`
70 | this.axis = d3.axisBottom(opt.scale as d3.AxisScale).ticks(this.numTicks, d3.timeFormat("%b-%d:%H:%M"))
71 | }
72 |
73 | render($: WeyaElementFunction) {
74 | $('g', {id: this.id})
75 |
76 | let layer = d3.select(`#${this.id}`)
77 | layer.selectAll('g').remove()
78 | layer.append('g').call(this.axis)
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ui/src/components/charts/chart_colors.ts:
--------------------------------------------------------------------------------
1 | import d3 from "../../d3"
2 |
3 | const LIGHT_SINGLE_HUE = d3.piecewise(d3.interpolateHsl, ["#004c6d", "#c1e7ff"])
4 | const DARK_SINGLE_HUE = d3.piecewise(d3.interpolateHsl, ["#c1e7ff", "#004c6d"])
5 | const DIVERGENT = d3.piecewise(d3.interpolateHcl, ["#ffa600", "#bc5090", "#003f5d"])
6 | const DIVERGENT_SECOND = d3.piecewise(d3.interpolateHcl, ["#dd8400", "#9a3070", "#001d3b"])
7 |
8 | interface ChartColorsOptions {
9 | nColors: number
10 | secondNColors?: number
11 | isDivergent?: boolean
12 | }
13 |
14 | export default class ChartColors {
15 | nColors: number
16 | secondNColors: number
17 | isDivergent: boolean
18 | colorScale: d3.ScaleLinear
19 | secondColorScale: d3.ScaleLinear
20 | colors: string[] = []
21 | secondColors: string[] = []
22 |
23 | constructor(opt: ChartColorsOptions) {
24 | this.nColors = opt.nColors
25 | this.secondNColors = opt.secondNColors ?? 0
26 | this.isDivergent = opt.isDivergent
27 |
28 | this.colorScale = DIVERGENT
29 | this.secondColorScale = DIVERGENT_SECOND
30 | if (!this.isDivergent) {
31 | if (document.body.classList.contains('light')) {
32 | this.colorScale = LIGHT_SINGLE_HUE
33 | } else {
34 | this.colorScale = DARK_SINGLE_HUE
35 | }
36 | }
37 |
38 | for (let n = 0; n < this.nColors; ++n) {
39 | this.colors.push(this.colorScale(n / (Math.max(1, this.nColors - 1))))
40 | }
41 | for (let n = 0; n < this.secondNColors; ++n) {
42 | this.secondColors.push(this.secondColorScale(n / (Math.max(1, this.secondNColors - 1))))
43 | }
44 | }
45 |
46 | getColor(i: number) {
47 | return this.colors[i]
48 | }
49 | getSecondColor(i: number) {
50 | return this.secondColors[i]
51 | }
52 |
53 | getColors() {
54 | return this.colors
55 | }
56 | getSecondColors() {
57 | return this.secondColors
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ui/src/components/charts/chart_gradients.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from "../../../../lib/weya/weya"
2 | import ChartColors from "./chart_colors"
3 |
4 | interface LineGradientsOptions {
5 | chartColors: ChartColors
6 | chartId: string
7 | }
8 |
9 | export class LineGradients {
10 | chartColors: ChartColors
11 | chartId: string
12 |
13 | constructor(opt: LineGradientsOptions) {
14 | this.chartColors = opt.chartColors
15 | this.chartId = opt.chartId
16 | }
17 |
18 | render($: WeyaElementFunction) {
19 | $('defs', $ => {
20 | this.chartColors.getColors().map((c, i) => {
21 | $('linearGradient', {
22 | id: `gradient-${i}-${this.chartId}`,
23 | x1: '0%',
24 | x2: '0%',
25 | y1: '0%',
26 | y2: '100%'
27 | }, $ => {
28 | $('stop', {offset: '0%', 'stop-color': c, 'stop-opacity': 1.0})
29 | $('stop', {offset: '100%', 'stop-color': c, 'stop-opacity': 0.0})
30 | })
31 | })
32 | })
33 | }
34 | }
35 |
36 | export class DefaultLineGradient {
37 | constructor() {
38 | }
39 |
40 | render($: WeyaElementFunction) {
41 | $('defs', $ => {
42 | $('linearGradient', {id: `gradient-grey`, x1: '0%', x2: '0%', y1: '0%', y2: '100%'}, $ => {
43 | $('stop', {offset: '0%', 'stop-color': '#7f8c8d', 'stop-opacity': 1.0})
44 | $('stop', {offset: '100%', 'stop-color': '#7f8c8d', 'stop-opacity': 0.0})
45 | })
46 | })
47 | }
48 | }
49 |
50 | export class DropShadow {
51 | constructor() {
52 | }
53 |
54 | render($: WeyaElementFunction) {
55 | $('defs', $ => {
56 | $('filter', {id: 'dropshadow'}, $ => {
57 | $('feGaussianBlur', {in: 'SourceAlpha', stdDeviation: "3"})
58 | $('feOffset', {dx: '0', dy: "0", result: "offsetblur"})
59 | $('feComponentTransfer', $ => {
60 | $('feFuncA', {slope: '0.2', type: "linear"})
61 | })
62 | $('feMerge', $ => {
63 | $('feMergeNode')
64 | $('feMergeNode', {in: "SourceGraphic"})
65 | })
66 | })
67 | })
68 | }
69 | }
--------------------------------------------------------------------------------
/ui/src/components/charts/constants.ts:
--------------------------------------------------------------------------------
1 | export const OUTLIER_MARGIN = 0.04
2 | export const BASE_COLOR = '#34495e'
3 |
4 |
5 | export function getColor(index: number) {
6 | return CHART_COLORS[index % 10]
7 | }
8 |
9 | export function getBaseColor() {
10 | if (document.body.classList.contains('light')) {
11 | return '#34495e'
12 | } else {
13 | return '#ccc'
14 | }
15 | }
16 |
17 | export const CHART_COLORS = [
18 | '#4E79A7',
19 | '#F28E2C',
20 | '#76B7B2',
21 | '#E15759',
22 | '#59A14F',
23 | '#EDC949',
24 | '#AF7AA1',
25 | '#FF9DA7',
26 | '#9C755F',
27 | '#BAB0AB'
28 | ]
29 |
30 |
--------------------------------------------------------------------------------
/ui/src/components/charts/labels.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from "../../../../lib/weya/weya"
2 | import ChartColors from "./chart_colors"
3 |
4 | interface LabelsOptions {
5 | labels: string[]
6 | isDivergent?: boolean
7 | chartColors?: ChartColors
8 | }
9 |
10 | export class Labels {
11 | labels: string[]
12 | chartColors: ChartColors
13 |
14 | constructor(opt: LabelsOptions) {
15 | this.labels = opt.labels
16 | this.chartColors = opt.chartColors ? opt.chartColors : new ChartColors({
17 | nColors: this.labels.length,
18 | isDivergent: opt.isDivergent
19 | })
20 | }
21 |
22 | render($: WeyaElementFunction) {
23 | $('div.text-center.labels.text-secondary',
24 | $ => {
25 | this.labels.map((label, i) => {
26 | $('span', $ => {
27 | $('div.box', {style: {background: this.chartColors.getColor(i)}})
28 | })
29 | $('span', label)
30 | })
31 | })
32 | }
33 | }
--------------------------------------------------------------------------------
/ui/src/components/charts/simple_lines/plot.ts:
--------------------------------------------------------------------------------
1 | import d3 from "../../../d3"
2 | import {WeyaElementFunction} from '../../../../../lib/weya/weya'
3 | import {FillOptions, PlotOptions} from '../types'
4 |
5 | interface SimpleLinePlotOptions extends PlotOptions {
6 | xScale: d3.ScaleLinear
7 | series: number[]
8 | }
9 |
10 | export class SimpleLinePlot {
11 | series: number[]
12 | xScale: d3.ScaleLinear
13 | yScale: d3.ScaleLinear
14 | color: string
15 | smoothedLine: d3.Line
16 |
17 | constructor(opt: SimpleLinePlotOptions) {
18 | this.series = opt.series
19 | this.xScale = opt.xScale
20 | this.yScale = opt.yScale
21 | this.color = opt.color
22 |
23 | this.smoothedLine = d3.line()
24 | .curve(d3.curveMonotoneX)
25 | .x((d, i) => {
26 | return this.xScale(i)
27 | })
28 | .y((d) => {
29 | return this.yScale(d)
30 | })
31 | }
32 |
33 |
34 | render($: WeyaElementFunction) {
35 | $('g', $ => {
36 | $('path.smoothed-line.dropshadow',
37 | {
38 | fill: 'none',
39 | stroke: this.color,
40 | d: this.smoothedLine(this.series) as string
41 | })
42 | })
43 | }
44 | }
45 |
46 | interface SimpleLineFillOptions extends FillOptions {
47 | xScale: d3.ScaleLinear
48 | series: number[]
49 | chartId: string
50 | }
51 |
52 | export class SimpleLineFill {
53 | series: number[]
54 | chartId: string
55 | xScale: d3.ScaleLinear
56 | yScale: d3.ScaleLinear
57 | color: string
58 | colorIdx: number
59 | smoothedLine
60 | dFill: string
61 |
62 | constructor(opt: SimpleLineFillOptions) {
63 | this.series = opt.series
64 | this.xScale = opt.xScale
65 | this.yScale = opt.yScale
66 | this.color = opt.color
67 | this.colorIdx = opt.colorIdx
68 | this.chartId = opt.chartId
69 |
70 | this.smoothedLine = d3.line()
71 | .curve(d3.curveMonotoneX)
72 | .x((d, i) => {
73 | return this.xScale(i)
74 | })
75 | .y((d) => {
76 | return this.yScale(d)
77 | })
78 |
79 | let d = this.smoothedLine(this.series) as string
80 | this.dFill = `M${this.xScale(0)},0L` + d.substr(1) + `L${this.xScale(this.series.length - 1)},0`
81 | }
82 |
83 | render($: WeyaElementFunction) {
84 | $('g', $ => {
85 | $('path.line-fill',
86 | {
87 | fill: this.color,
88 | stroke: 'none',
89 | style: {fill: `url(#gradient-${this.colorIdx}-${this.chartId}`},
90 | d: this.dFill
91 | })
92 | })
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/ui/src/components/charts/types.ts:
--------------------------------------------------------------------------------
1 | import d3 from "../../d3"
2 | import {SeriesModel} from "../../models/run"
3 |
4 | export interface ChartOptions {
5 | series: SeriesModel[]
6 | width: number
7 | }
8 |
9 | export interface PlotOptions {
10 | yScale: d3.ScaleLinear
11 | color: string
12 | }
13 |
14 | export interface FillOptions extends PlotOptions {
15 | colorIdx: number
16 | }
17 |
--------------------------------------------------------------------------------
/ui/src/components/codes/pytorch_lightning.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../../lib/weya/weya'
2 | import {Tab} from "./tabs"
3 |
4 |
5 | export default class PyTorchLightningCode {
6 | constructor() {
7 | }
8 |
9 | render($) {
10 | $('div.code-sample.bg-dark.px-1.py-2.my-3', $ => {
11 | $('pre.text-white', $ => {
12 | $('div', $ => {
13 | $('span.key-word', 'from')
14 | $('span', ' labml')
15 | $('span.key-word', ' import')
16 | $('span', ' experiment ')
17 | $('span.key-word', 'as ')
18 | $('span', 'exp')
19 | })
20 | $('div', $ => {
21 | $('span.key-word', 'from')
22 | $('span', ' labml.utils.lightning ')
23 | $('span.key-word', 'import')
24 | $('span', ' LabMLLightningLogger ')
25 | })
26 | $('br')
27 |
28 | $('div', $ => {
29 | $('span', 'trainer = pl.Trainer(')
30 | $('span.param', 'gpus')
31 | $('span', '=1,')
32 | })
33 | $('div', $ => {
34 | new Tab().render($)
35 | new Tab().render($)
36 | new Tab().render($)
37 | new Tab().render($)
38 | $('span.param', 'max_epochs')
39 | $('span', '=5,')
40 | })
41 | $('div', $ => {
42 | new Tab().render($)
43 | new Tab().render($)
44 | new Tab().render($)
45 | new Tab().render($)
46 | $('span.param', 'progress_bar_refresh_rate')
47 | $('span', '=20,')
48 | })
49 | $('div.labml-api', $ => {
50 | new Tab().render($)
51 | new Tab().render($)
52 | new Tab().render($)
53 | new Tab().render($)
54 | $('span.param', 'logger')
55 | $('span', '=LabMLLighteningLogger())')
56 | })
57 | $('br')
58 |
59 | $('div', $ => {
60 | $('div.labml-api', $ => {
61 | $('span.key-word', 'with')
62 | $('span', ' exp.record(')
63 | $('span.param', 'name=')
64 | $('span.string', "'sample'")
65 | $('span', ', ')
66 | $('span.param', 'exp_conf')
67 | $('span', '=conf, ')
68 | $('span.param', 'disable_screen')
69 | $('span', '=True):')
70 | })
71 | })
72 | $('div.labml-api', $ => {
73 | new Tab().render($)
74 | new Tab().render($)
75 | $('span', 'trainer.fit(model, data_loader)')
76 | })
77 | })
78 | })
79 | }
80 | }
--------------------------------------------------------------------------------
/ui/src/components/codes/tabs.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction,} from '../../../../lib/weya/weya'
2 |
3 | export class Tab {
4 | constructor() {
5 | }
6 |
7 | render($: WeyaElementFunction) {
8 | $('span.tab-space')
9 | }
10 | }
11 |
12 |
13 | export class HalfTab {
14 | constructor() {
15 | }
16 |
17 | render($: WeyaElementFunction) {
18 | $('span.tab-space')
19 | }
20 | }
21 |
22 | export class QuarterTab {
23 | constructor() {
24 | }
25 |
26 | render($: WeyaElementFunction) {
27 | $('span.tab-space')
28 | }
29 | }
--------------------------------------------------------------------------------
/ui/src/components/error_message.ts:
--------------------------------------------------------------------------------
1 | import {Weya} from "../../../lib/weya/weya"
2 |
3 | export class ErrorMessage {
4 | elem: HTMLDivElement
5 |
6 | constructor() {
7 | this.elem = null
8 | }
9 |
10 | render(parent: HTMLDivElement) {
11 | this.remove()
12 | Weya(parent, $ => {
13 | this.elem = $('div', '.error.text-center.warning', $ => {
14 | $('span', '.fas.fa-exclamation-triangle', '')
15 | $('h4', '.text-uppercase', 'Network error')
16 | })
17 | })
18 | }
19 |
20 | remove() {
21 | if (this.elem == null) {
22 | return
23 | }
24 | this.elem.remove()
25 | this.elem = null
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/src/components/input/editable_field.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../../lib/weya/weya'
2 |
3 | interface EditableFieldOptions {
4 | name: string
5 | value: any
6 | placeholder?: string
7 | isEditable?: boolean
8 | numEditRows?: number
9 | }
10 |
11 | export default class EditableField {
12 | name: string
13 | value: any
14 | placeholder: string
15 | isEditable: boolean
16 | numEditRows: number
17 | inputElem: HTMLInputElement | HTMLTextAreaElement
18 | valueElem: HTMLSpanElement
19 |
20 | constructor(opt: EditableFieldOptions) {
21 | this.name = opt.name
22 | this.value = opt.value
23 | this.placeholder = opt.placeholder
24 | this.isEditable = opt.isEditable
25 | this.numEditRows = opt.numEditRows
26 | }
27 |
28 | getInput() {
29 | return this.inputElem.value
30 | }
31 |
32 | updateValue(value: string) {
33 | this.valueElem.textContent = value
34 | }
35 |
36 | render($: WeyaElementFunction) {
37 | $(`li`, $ => {
38 | $('span.item-key', this.name)
39 | if (this.isEditable) {
40 | $('div.input-container.mt-2', $ => {
41 | $('div.input-content', $ => {
42 | if (this.numEditRows) {
43 | this.inputElem = $('textarea', {
44 | rows: this.numEditRows,
45 | placeholder: this.placeholder,
46 | value: this.value
47 | }
48 | )
49 | } else {
50 | this.inputElem = $('input', {
51 | placeholder: this.placeholder,
52 | value: this.value
53 | }
54 | )
55 | }
56 | })
57 | })
58 | } else {
59 | this.valueElem = $('span', '.item-value')
60 | this.updateValue(this.value)
61 | }
62 | })
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ui/src/components/input/editable_select_field.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../../lib/weya/weya'
2 |
3 | interface EditableSelectFieldOptions {
4 | name: string
5 | value: any
6 | isEditable?: boolean
7 | onEdit: () => void
8 | onClick?: () => void
9 | }
10 |
11 | export default class EditableSelectField {
12 | name: string
13 | value: any
14 | isEditable: boolean
15 | private readonly onEdit: () => void
16 | private readonly _onClick?: () => void
17 |
18 | constructor(opt: EditableSelectFieldOptions) {
19 | this.name = opt.name
20 | this.value = opt.value
21 | this.isEditable = opt.isEditable
22 | this.onEdit = opt.onEdit
23 | this._onClick = opt.onClick
24 | }
25 |
26 | onClick() {
27 | if (!this.isEditable) {
28 | this._onClick()
29 | }
30 | }
31 |
32 | render($: WeyaElementFunction) {
33 | $(`li`, {on: {click: this.onClick.bind(this)}}, $ => {
34 | $('span.item-key', this.name)
35 | if (this.isEditable) {
36 | $('div.input-container.mt-2', $ => {
37 | $('div', '.input-content', {on: {click: this.onEdit}}, $ => {
38 | $('input', {
39 | value: this.value,
40 | readonly: 'readonly'
41 | }
42 | )
43 | })
44 | })
45 | } else {
46 | $('span.item-value', this.value)
47 | }
48 | })
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ui/src/components/insights_list.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from "../../../lib/weya/weya"
2 | import {InsightModel} from "../models/run"
3 |
4 | class Insight {
5 | className: string
6 | message: string
7 |
8 | constructor(opt: InsightModel) {
9 | this.className = '.insight-container'
10 |
11 | if (opt.type === 'danger') {
12 | this.className += '.danger'
13 | } else if (opt.type === 'warning') {
14 | this.className += '.warning'
15 | } else {
16 | this.className += '.success'
17 | }
18 |
19 | this.message = opt.message
20 | }
21 |
22 | render($: WeyaElementFunction) {
23 | $('div', this.className, $ => {
24 | $('span', '.fas.fa-lightbulb.icon', '')
25 | $('span', '.info', this.message)
26 | })
27 | }
28 | }
29 |
30 | interface InsightsListOptions {
31 | insightList: InsightModel[]
32 | }
33 |
34 |
35 | export default class InsightsList {
36 | insightList: InsightModel[]
37 |
38 | constructor(opt: InsightsListOptions) {
39 | this.insightList = opt.insightList
40 | }
41 |
42 | render($) {
43 | this.insightList.map((insight, idx) => (
44 | new Insight({message: insight.message, type: insight.type, time: insight.time}).render($)
45 | ))
46 | }
47 | }
--------------------------------------------------------------------------------
/ui/src/components/loader.ts:
--------------------------------------------------------------------------------
1 | import {Weya, WeyaElement, WeyaElementFunction} from '../../../lib/weya/weya'
2 | import {ErrorMessage} from './error_message'
3 | import {waitForFrame} from '../utils/render'
4 |
5 | export class Loader {
6 | elem: WeyaElement
7 | isScreenLoader: boolean
8 |
9 | constructor(isScreenLoader?: boolean) {
10 | this.isScreenLoader = isScreenLoader
11 | this.elem = null
12 | }
13 |
14 | render($: WeyaElementFunction) {
15 | if (this.isScreenLoader) {
16 | this.elem = $('div', '.loader-container', $ => {
17 | $('div', '.text-center.mt-5', $ => {
18 | $('img', '.logo-style', {src: '/images/lab_logo.png'})
19 | })
20 | $('div', '.text-center', $ => {
21 | $('div.loader', '')
22 | })
23 | })
24 | } else {
25 | this.elem = $('div', '.text-center', $ => {
26 | $('div', '.loader', '')
27 | })
28 | }
29 |
30 | return this.elem
31 | }
32 |
33 | remove() {
34 | if (this.elem == null) {
35 | return
36 | }
37 | this.elem.remove()
38 | this.elem = null
39 | }
40 | }
41 |
42 | export class DataLoader {
43 | private _load: (force: boolean) => Promise
44 | private loaded: boolean
45 | private loader: Loader
46 | private elem: HTMLDivElement
47 | private errorMessage: ErrorMessage
48 |
49 | constructor(load: (force: boolean) => Promise) {
50 | this._load = load
51 | this.loaded = false
52 | this.loader = new Loader()
53 | this.errorMessage = new ErrorMessage()
54 | }
55 |
56 | render($: WeyaElementFunction) {
57 | this.elem = $('div', '.data-loader')
58 | }
59 |
60 | async load(force: boolean = false) {
61 | this.errorMessage.remove()
62 | if (!this.loaded) {
63 | this.elem.appendChild(this.loader.render(Weya))
64 | await waitForFrame()
65 | }
66 |
67 | try {
68 | await this._load(force)
69 | this.loaded = true
70 | } catch (e) {
71 | this.loaded = false
72 | this.errorMessage.render(this.elem)
73 | throw e
74 | } finally {
75 | this.loader.remove()
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ui/src/components/runs_list_item.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../lib/weya/weya'
2 | import {RunListItemModel} from '../models/run_list'
3 | import {StatusView} from './status'
4 | import {formatTime} from '../utils/time'
5 |
6 | export interface RunsListItemOptions {
7 | item: RunListItemModel
8 | onClick: (elem: RunsListItemView) => void
9 | }
10 |
11 | export class RunsListItemView {
12 | item: RunListItemModel
13 | elem: HTMLAnchorElement
14 | onClick: (evt: Event) => void
15 |
16 | constructor(opt: RunsListItemOptions) {
17 | this.item = opt.item
18 | this.onClick = (e: Event) => {
19 | e.preventDefault()
20 | opt.onClick(this)
21 | }
22 | }
23 |
24 | render($: WeyaElementFunction) {
25 | this.elem = $('a', '.list-item.list-group-item.list-group-item-action',
26 | {href: `/run/${this.item.run_uuid}`, on: {click: this.onClick}},
27 | $ => {
28 | $('div', $ => {
29 | new StatusView({status: this.item.run_status}).render($)
30 | $('p', `Started on ${formatTime(this.item.start_time)}`)
31 | $('h5', this.item.name)
32 | $('h6', this.item.comment)
33 | })
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ui/src/components/search.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../lib/weya/weya'
2 | import Timeout = NodeJS.Timeout
3 |
4 | export interface SearchOptions {
5 | onSearch: (query: string) => void
6 | }
7 |
8 | export class SearchView {
9 | onSearch: () => void
10 | textbox: HTMLInputElement
11 | inputTimeout: Timeout
12 |
13 | constructor(opt: SearchOptions) {
14 | this.onSearch = () => {
15 | clearTimeout(this.inputTimeout)
16 | this.inputTimeout = setTimeout(() => {
17 | opt.onSearch(this.textbox.value)
18 | }, 250)
19 | }
20 | }
21 |
22 | render($: WeyaElementFunction) {
23 | $('div', '.search-container.mt-3.mb-3.px-2', $ => {
24 | $('div', '.search-content', $ => {
25 | $('span', '.icon', $=>{
26 | $('span', '.fas.fa-search', '')
27 | })
28 | this.textbox = $('input', '.search-input', {
29 | type: 'search',
30 | placeholder: 'Search',
31 | 'aria-label': 'Search',
32 | on: {
33 | input: this.onSearch
34 | }
35 | })
36 | })
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ui/src/components/sessions_list_item.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../lib/weya/weya'
2 | import {StatusView} from './status'
3 | import {formatTime} from '../utils/time'
4 | import {SessionsListItemModel} from '../models/session_list';
5 |
6 | export interface SessionListItemOptions {
7 | item: SessionsListItemModel
8 | onClick: (elem: SessionsListItemView) => void
9 | }
10 |
11 | export class SessionsListItemView {
12 | item: SessionsListItemModel
13 | elem: HTMLAnchorElement
14 | onClick: (evt: Event) => void
15 |
16 | constructor(opt: SessionListItemOptions) {
17 | this.item = opt.item
18 | this.onClick = (e: Event) => {
19 | e.preventDefault()
20 | opt.onClick(this)
21 | }
22 | }
23 |
24 | render($: WeyaElementFunction) {
25 | this.elem = $('a', '.list-item.list-group-item.list-group-item-action',
26 | {href: `/session/${this.item.session_uuid}`, on: {click: this.onClick}},
27 | $ => {
28 | $('div', $ => {
29 | new StatusView({status: this.item.run_status, type: 'session'}).render($)
30 | $('p', `Started ${formatTime(this.item.start_time)}`)
31 | $('h5', this.item.name)
32 | $('h6', this.item.comment)
33 | })
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ui/src/components/status.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../lib/weya/weya'
2 | import {RunStatusModel} from "../models/status"
3 | import {ContentType} from '../types';
4 |
5 | export interface StatusOptions {
6 | status: RunStatusModel
7 | type?: ContentType
8 | }
9 |
10 | export class StatusView {
11 | status: RunStatusModel
12 | type: ContentType
13 |
14 | constructor(opt: StatusOptions) {
15 | this.status = opt.status
16 | this.type = opt.type || 'run'
17 | }
18 |
19 | render($: WeyaElementFunction) {
20 | if (this.status.status === 'in progress') {
21 | if (this.type === 'session') {
22 | $('div.status.text-info.text-uppercase', 'monitoring')
23 | return
24 | }
25 | $('div.status.text-info.text-uppercase', 'experiment is running')
26 | } else if (this.status.status === 'no response') {
27 | $('div.status.text-warning.text-uppercase', 'no response')
28 | } else if (this.status.status === 'completed') {
29 | $('div.status.text-success.text-uppercase', 'completed')
30 | } else if (this.status.status === 'crashed') {
31 | $('div.status.text-danger.text-uppercase', 'crashed')
32 | } else if (this.status.status === 'unknown') {
33 | $('div.status.text-info.text-uppercase', 'unknown status')
34 | } else {
35 | $('div.status', this.status.status)
36 | }
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/ui/src/components/user_messages.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction, Weya as $,} from '../../../lib/weya/weya'
2 |
3 | export class UserMessages {
4 | message: string
5 | elem: HTMLDivElement
6 |
7 | constructor() {
8 | }
9 |
10 | render($: WeyaElementFunction) {
11 | this.elem = $('div', '.pointer-cursor.mt-1')
12 | }
13 |
14 | hide(isHidden: boolean) {
15 | if (isHidden) {
16 | this.elem.classList.add('hide')
17 | } else {
18 | this.elem.classList.remove('hide')
19 | }
20 | }
21 |
22 | networkError() {
23 | this.message = 'An unexpected network error occurred. Please try again later'
24 | this.elem.innerHTML = ''
25 | $(this.elem, $ => {
26 | $('div', '.message.alert', $ => {
27 | $('span', this.message)
28 | $('span', '.close-btn',
29 | String.fromCharCode(215),
30 | {on: {click: this.hide.bind(this, true)}}
31 | )
32 | })
33 | })
34 | this.hide(false)
35 | }
36 |
37 | success(message: string) {
38 | this.message = message
39 | this.elem.innerHTML = ''
40 | $(this.elem, $ => {
41 | $('div', '.message.success', $ => {
42 | $('span', this.message)
43 | $('span', '.close-btn',
44 | String.fromCharCode(215),
45 | {on: {click: this.hide.bind(this, true)}}
46 | )
47 | })
48 | })
49 | this.hide(false)
50 | }
51 |
52 | warning(message: string) {
53 | this.message = message
54 | this.elem.innerHTML = ''
55 | $(this.elem, $ => {
56 | $('div', '.message.alert', $ => {
57 | $('span', this.message)
58 | $('span', '.close-btn',
59 | String.fromCharCode(215),
60 | {on: {click: this.hide.bind(this, true)}}
61 | )
62 | })
63 | })
64 | this.hide(false)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/ui/src/d3.ts:
--------------------------------------------------------------------------------
1 | let d3 = (window).d3
2 |
3 | export default d3
--------------------------------------------------------------------------------
/ui/src/env.sample.ts:
--------------------------------------------------------------------------------
1 | export const AUTH0_DOMAIN = 'auth0.com'
2 | export const AUTH0_CLIENT_ID = ''
3 | export const APP_BASE_URL = 'http://localhost:5000'
4 | export const API_BASE_URL = 'http://localhost:5000/api/v1'
5 | export const MOBILE_APP_NAMESPACE = ''
6 | export const SENTRY_DSN = ''
7 |
--------------------------------------------------------------------------------
/ui/src/main.ts:
--------------------------------------------------------------------------------
1 | import {Integrations, Sentry} from './sentry'
2 |
3 | import {ROUTER} from './app'
4 | import {RunHandler} from './views/run_view'
5 | import {PageNotFoundHandler} from './views/errors/page_not_found_view'
6 | import {RunsListHandler} from './views/runs_list_view'
7 | import {SessionsListHandler} from './views/sessions_list_view'
8 | import {LoginHandler} from './views/login_view'
9 | import {SettingsHandler} from './views/settings_view'
10 |
11 | import {experimentAnalyses, sessionAnalyses} from "./analyses/analyses"
12 | import {ProcessDetailsHandler} from "./analyses/sessions/process/detail_view"
13 | import {RunHeaderHandler} from "./analyses/experiments/run_header/view"
14 | import {SessionHeaderHandler} from "./analyses/sessions/session_header/view"
15 | import {SessionHandler} from './views/session_view'
16 | import {SENTRY_DSN} from './env'
17 | import {AuthErrorHandler} from './views/errors/auth_error_view'
18 | import {OtherErrorHandler} from './views/errors/other_error_view'
19 | import {NetworkErrorHandler} from './views/errors/network_error_view'
20 |
21 | ROUTER.route(/^(.*)$/g, [() => {
22 | ROUTER.navigate('/404')
23 | }])
24 |
25 | new LoginHandler()
26 |
27 | new PageNotFoundHandler()
28 | new AuthErrorHandler()
29 | new OtherErrorHandler()
30 | new NetworkErrorHandler()
31 |
32 | new RunHandler()
33 | new SessionHandler()
34 | new RunsListHandler()
35 | new SessionsListHandler()
36 | new SettingsHandler()
37 |
38 | new RunHeaderHandler()
39 | new SessionHeaderHandler()
40 |
41 | //TODO properly import this later
42 | new ProcessDetailsHandler()
43 |
44 | ROUTER.route('', [() => {
45 | ROUTER.navigate('/runs')
46 | }])
47 |
48 | ROUTER.route('cordova', [() => {
49 | window.localStorage.setItem('platform', 'cordova')
50 | ROUTER.navigate('/runs')
51 | }])
52 |
53 | experimentAnalyses.map((analysis, i) => {
54 | new analysis.viewHandler()
55 | })
56 |
57 | sessionAnalyses.map((analysis, i) => {
58 | new analysis.viewHandler()
59 | })
60 |
61 | if (
62 | document.readyState === 'complete' ||
63 | document.readyState === 'interactive'
64 | ) {
65 | ROUTER.start(null, false)
66 | } else {
67 | document.addEventListener('DOMContentLoaded', () => {
68 | ROUTER.start(null, false)
69 | })
70 | }
71 |
72 | // To make sure that :active is triggered in safari
73 | // Ref: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/AdjustingtheTextSize/AdjustingtheTextSize.html
74 | document.addEventListener("touchstart", () => {
75 | }, true);
76 |
77 | if (SENTRY_DSN) {
78 | Sentry.init({
79 | dsn: SENTRY_DSN,
80 | integrations: [
81 | new Integrations.BrowserTracing(),
82 | ],
83 | tracesSampleRate: 1.0,
84 | })
85 | }
86 |
87 | (window as any).handleOpenURL = function (url) {
88 | window.location.hash = `#${url.split('#')[1]}`
89 | window.location.reload()
90 | }
91 |
--------------------------------------------------------------------------------
/ui/src/mix_panel.ts:
--------------------------------------------------------------------------------
1 | let mix_panel = (window).mixpanel
2 |
3 | mix_panel.init("7e19de9c3c68ba5a897f19837042a826")
4 | mix_panel = (window).mixpanel
5 |
6 | export default mix_panel
7 |
8 |
--------------------------------------------------------------------------------
/ui/src/models/config.ts:
--------------------------------------------------------------------------------
1 | export interface ConfigModel {
2 | key: string
3 | name: string
4 | computed: any
5 | value: any
6 | options: string[]
7 | order: number
8 | type: string
9 | is_hyperparam?: boolean
10 | is_meta?: boolean
11 | is_explicitly_specified?: boolean
12 | }
13 |
14 | export class Config {
15 | key: string
16 | name: string
17 | computed: any
18 | value: any
19 | options: string[]
20 | order: number
21 | type: string
22 | isHyperparam?: boolean
23 | isMeta?: boolean
24 | isExplicitlySpecified?: boolean
25 |
26 | isCustom: boolean
27 | isOnlyOption: boolean
28 | isDefault: boolean
29 | otherOptions: Set
30 |
31 | constructor(config: ConfigModel) {
32 | this.key = config.key
33 | this.name = config.name
34 | this.computed = config.computed
35 | this.value = config.value
36 | this.options = config.options ? config.options: []
37 | this.order = config.order
38 | this.type = config.type
39 | this.isHyperparam = config.is_hyperparam
40 | this.isMeta = config.is_meta
41 | this.isExplicitlySpecified = config.is_explicitly_specified
42 |
43 | let options = new Set()
44 | for (let opt of this.options) {
45 | options.add(opt)
46 | }
47 |
48 | this.isCustom = false
49 | this.isOnlyOption = false
50 | this.isDefault = false
51 |
52 | if (options.has(this.value)) {
53 | options.delete(this.value)
54 | if (options.size === 0) {
55 | this.isOnlyOption = true
56 | this.isDefault = true
57 | }
58 | } else {
59 | this.isCustom = true
60 | if (this.isExplicitlySpecified !== true) {
61 | this.isDefault = true
62 | }
63 | }
64 |
65 | this.otherOptions = options
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/ui/src/models/job.ts:
--------------------------------------------------------------------------------
1 | export interface JobModel {
2 | job_uuid: string
3 | method: string
4 | status: string
5 | created_time: number
6 | completed_time: number
7 | data: object
8 | }
9 |
10 |
11 | export class Job {
12 | job_uuid: string
13 | method: string
14 | status: string
15 | created_time: number
16 | completed_time: number
17 | data: object
18 |
19 | constructor(job: JobModel) {
20 | this.job_uuid = job.job_uuid
21 | this.method = job.method
22 | this.status = job.status
23 | this.created_time = job.created_time
24 | this.completed_time = job.completed_time
25 | this.data = job.data
26 | }
27 |
28 | get isSuccessful() {
29 | return this.status === 'success'
30 | }
31 |
32 | get isFailed() {
33 | return this.status === 'fail'
34 | }
35 |
36 | get isTimeOut() {
37 | return this.status === 'timeout'
38 | }
39 |
40 | get isComputerOffline() {
41 | return this.status === 'computer_offline'
42 | }
43 | }
--------------------------------------------------------------------------------
/ui/src/models/preferences.ts:
--------------------------------------------------------------------------------
1 | export interface AnalysisPreferenceModel {
2 | series_preferences: number[]
3 | sub_series_preferences: Object
4 | chart_type: number
5 | }
6 |
7 | export class AnalysisPreference {
8 | series_preferences: number[]
9 | sub_series_preferences: object
10 | chart_type: number
11 |
12 | constructor(preference: AnalysisPreferenceModel) {
13 | if (preference.series_preferences) {
14 | this.series_preferences = preference.series_preferences
15 | } else {
16 | this.series_preferences = []
17 | }
18 | this.chart_type = preference.chart_type
19 | this.sub_series_preferences = preference.sub_series_preferences
20 | }
21 | }
22 |
23 | export interface ComparisonPreferenceModel extends AnalysisPreferenceModel {
24 | base_series_preferences: number[]
25 | base_experiment: string
26 | }
27 |
28 | export class ComparisonPreference extends AnalysisPreference {
29 | base_series_preferences: number[]
30 | compared_with: string
31 |
32 | constructor(preference: ComparisonPreferenceModel) {
33 | super(preference)
34 | if (preference.base_series_preferences) {
35 | this.base_series_preferences = preference.base_series_preferences
36 | } else {
37 | this.base_series_preferences = []
38 | }
39 | this.compared_with = preference.base_experiment
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ui/src/models/run.ts:
--------------------------------------------------------------------------------
1 | import {Config, ConfigModel} from "./config"
2 |
3 | export interface RunModel {
4 | run_uuid: string
5 | name: string
6 | comment: string
7 | tags: string[]
8 | note: string
9 | start_time: number
10 | python_file: string
11 | repo_remotes: string
12 | commit: string
13 | commit_message: string
14 | start_step: number
15 | is_claimed: boolean
16 | is_project_run: boolean
17 | size: number
18 | size_checkpoints: number
19 | size_tensorboard: number
20 | computer_uuid: string
21 | configs: ConfigModel[]
22 | stdout: string
23 | logger: string
24 | stderr: string
25 | }
26 |
27 | export interface PointValue {
28 | step: number
29 | value: number
30 | smoothed: number
31 | }
32 |
33 | export interface SeriesModel {
34 | name: string
35 | step: number[]
36 | value: number[]
37 | smoothed: number[]
38 | mean: number
39 | series: PointValue[]
40 | dynamic_type: string
41 | range: [number, number]
42 | is_editable: boolean
43 | sub: SeriesModel
44 | }
45 |
46 | export interface InsightModel {
47 | message: string
48 | type: string
49 | time: number
50 | }
51 |
52 | export interface AnalysisDataModel {
53 | series: any[]
54 | insights: InsightModel[]
55 | summary: any[]
56 | }
57 |
58 | export class Run {
59 | run_uuid: string
60 | name: string
61 | comment: string
62 | note: string
63 | tags: string[]
64 | start_time: number
65 | python_file: string
66 | repo_remotes: string
67 | commit: string
68 | commit_message: string
69 | start_step: number
70 | is_claimed: boolean
71 | is_project_run: boolean
72 | size: number
73 | size_checkpoints: number
74 | size_tensorboard: number
75 | computer_uuid: string
76 | configs: Config[]
77 | dynamic: object
78 | stdout: string
79 | logger: string
80 | stderr: string
81 |
82 | constructor(run: RunModel) {
83 | this.run_uuid = run.run_uuid
84 | this.name = run.name
85 | this.comment = run.comment
86 | this.note = run.note
87 | this.tags = run.tags
88 | this.start_time = run.start_time
89 | this.python_file = run.python_file
90 | this.repo_remotes = run.repo_remotes
91 | this.commit = run.commit
92 | this.commit_message = run.commit_message
93 | this.start_step = run.start_step
94 | this.is_claimed = run.is_claimed
95 | this.is_project_run = run.is_project_run
96 | this.size = run.size
97 | this.size_checkpoints = run.size_checkpoints
98 | this.size_tensorboard = run.size_tensorboard
99 | this.computer_uuid = run.computer_uuid
100 | this.configs = []
101 | for (let c of run.configs) {
102 | this.configs.push(new Config(c))
103 | }
104 | this.stdout = run.stdout
105 | this.logger = run.logger
106 | this.stderr = run.stderr
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/ui/src/models/run_list.ts:
--------------------------------------------------------------------------------
1 | import {RunStatus} from "./status"
2 |
3 | export interface RunListItemModel {
4 | run_uuid: string
5 | computer_uuid : string
6 | run_status: RunStatus
7 | last_updated_time: number
8 | name: string
9 | comment: string
10 | start_time: number
11 | }
12 |
13 | export interface RunsListModel {
14 | runs: RunListItemModel[]
15 | labml_token: string
16 | }
17 |
18 | export class RunListItem {
19 | run_uuid: string
20 | computer_uuid : string
21 | run_status: RunStatus
22 | last_updated_time: number
23 | name: string
24 | comment: string
25 | start_time: number
26 |
27 | constructor(run_list_item: RunListItemModel) {
28 | this.run_uuid = run_list_item.run_uuid
29 | this.computer_uuid = run_list_item.computer_uuid
30 | this.name = run_list_item.name
31 | this.comment = run_list_item.comment
32 | this.start_time = run_list_item.start_time
33 | this.last_updated_time = run_list_item.last_updated_time
34 | this.run_status = new RunStatus(run_list_item.run_status)
35 | }
36 | }
37 |
38 | export class RunsList {
39 | runs: RunListItemModel[]
40 | labml_token: string
41 |
42 | constructor(runs_list: RunsListModel) {
43 | this.runs = []
44 | for (let r of runs_list.runs) {
45 | this.runs.push(new RunListItem(r))
46 | }
47 | this.labml_token = runs_list.labml_token
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ui/src/models/session.ts:
--------------------------------------------------------------------------------
1 | import {Config, ConfigModel} from "./config"
2 |
3 | export interface SessionModel {
4 | computer_uuid: string
5 | session_uuid: string
6 | name: string
7 | comment: string
8 | start_time: number
9 | is_claimed: boolean
10 | is_project_session: boolean
11 | configs: ConfigModel[]
12 | }
13 |
14 | export class Session {
15 | computer_uuid: string
16 | session_uuid: string
17 | name: string
18 | comment: string
19 | is_claimed: boolean
20 | is_project_session: boolean
21 | start_time: number
22 | configs: Config[]
23 |
24 | constructor(session: SessionModel) {
25 | this.computer_uuid = session.computer_uuid
26 | this.session_uuid = session.session_uuid
27 | this.name = session.name
28 | this.comment = session.comment
29 | this.start_time = session.start_time
30 | this.is_claimed = session.is_claimed
31 | this.is_project_session = session.is_project_session
32 | this.configs = []
33 | for (let c of session.configs) {
34 | this.configs.push(new Config(c))
35 | }
36 | }
37 | }
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ui/src/models/session_list.ts:
--------------------------------------------------------------------------------
1 | import {RunStatus} from "./status"
2 |
3 | export interface SessionsListItemModel {
4 | computer_uuid: string
5 | session_uuid: string
6 | run_status: RunStatus
7 | last_updated_time: number
8 | name: string
9 | comment: string
10 | start_time: number
11 | }
12 |
13 | export interface SessionsListModel {
14 | sessions: SessionsListItemModel[]
15 | labml_token: string
16 | }
17 |
18 | export class SessionListItem {
19 | computer_uuid: string
20 | session_uuid: string
21 | run_status: RunStatus
22 | last_updated_time: number
23 | name: string
24 | comment: string
25 | start_time: number
26 |
27 | constructor(sessionListItem: SessionsListItemModel) {
28 | this.computer_uuid = sessionListItem.computer_uuid
29 | this.session_uuid = sessionListItem.session_uuid
30 | this.name = sessionListItem.name
31 | this.comment = sessionListItem.comment
32 | this.start_time = sessionListItem.start_time
33 | this.last_updated_time = sessionListItem.last_updated_time
34 | this.run_status = new RunStatus(sessionListItem.run_status)
35 | }
36 | }
37 |
38 | export class SessionsList {
39 | sessions: SessionsListItemModel[]
40 | labml_token: string
41 |
42 | constructor(sessionsList: SessionsListModel) {
43 | this.sessions = []
44 | for (let c of sessionsList.sessions) {
45 | this.sessions.push(new SessionListItem(c))
46 | }
47 | this.labml_token = sessionsList.labml_token
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ui/src/models/status.ts:
--------------------------------------------------------------------------------
1 | export interface RunStatusModel {
2 | status: string
3 | details: string
4 | time: number
5 | }
6 |
7 | export interface StatusModel {
8 | run_uuid: string
9 | last_updated_time: number
10 | run_status: RunStatusModel
11 | }
12 |
13 | export class RunStatus {
14 | status: string
15 | details: string
16 | time: number
17 |
18 | constructor(runStatus: RunStatusModel) {
19 | this.status = runStatus.status
20 | this.details = runStatus.details
21 | this.time = runStatus.time
22 | }
23 | }
24 |
25 | export class Status {
26 | uuid: string
27 | last_updated_time: number
28 | run_status: RunStatus
29 |
30 | constructor(status: StatusModel) {
31 | this.uuid = status.run_uuid
32 | this.last_updated_time = status.last_updated_time
33 | this.run_status = new RunStatus(status.run_status)
34 | }
35 |
36 | get isRunning() {
37 | if (this.run_status.status === 'in progress') {
38 | let timeDiff = (Date.now() / 1000 - this.last_updated_time) / 60
39 | return timeDiff <= 15
40 | } else {
41 | return false
42 | }
43 | }
44 |
45 | get isStatusInProgress() {
46 | return this.run_status.status === 'in progress'
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/ui/src/models/user.ts:
--------------------------------------------------------------------------------
1 | export interface UserModel {
2 | sub: string
3 | email: string
4 | name: string
5 | picture: string
6 | theme: string
7 | email_verified: boolean
8 | projects: object
9 | default_project: object
10 | }
11 |
12 | export class User {
13 | sub: string
14 | email: string
15 | name: string
16 | picture: string
17 | theme: string
18 | email_verified: boolean
19 | projects: object
20 | default_project: object
21 |
22 | constructor(user: UserModel) {
23 | this.sub = user.sub
24 | this.email = user.email
25 | this.name = user.name
26 | this.picture = user.picture
27 | this.theme = user.theme
28 | this.email_verified = user.email_verified
29 | this.projects = user.projects
30 | this.default_project = user.default_project
31 | }
32 | }
33 |
34 | export interface IsUserLoggedModel {
35 | is_user_logged: boolean
36 | }
37 |
38 | export class IsUserLogged {
39 | is_user_logged: boolean
40 |
41 | constructor(isUserLogged: IsUserLoggedModel) {
42 | this.is_user_logged = isUserLogged.is_user_logged
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ui/src/screen.ts:
--------------------------------------------------------------------------------
1 | import {Weya as $} from '../../lib/weya/weya'
2 | import {getWindowDimensions} from "./utils/window_dimentions"
3 | import CACHE, {IsUserLoggedCache, UserCache} from './cache/cache'
4 | import {Loader} from './components/loader'
5 | import {ROUTER} from './app'
6 | import {setTitle} from './utils/document'
7 | import {ScreenView} from './screen_view'
8 | import {handleNetworkErrorInplace} from './utils/redirect'
9 |
10 | class ScreenContainer {
11 | view?: ScreenView
12 | private isUserLoggedCache: IsUserLoggedCache
13 | private isUserLogged: boolean
14 | private userCache: UserCache
15 | private loader: Loader
16 | private windowWidth: number
17 |
18 | constructor() {
19 | this.view = null
20 | this.isUserLoggedCache = CACHE.getIsUserLogged()
21 | this.userCache = CACHE.getUser()
22 | this.loader = new Loader(true)
23 | window.addEventListener('resize', this.onResize.bind(this))
24 | document.addEventListener('visibilitychange', this.onVisibilityChange.bind(this))
25 | }
26 |
27 | onResize = () => {
28 | let windowWidth = getWindowDimensions().width
29 | // Prevent mobile browser addressBar visibility from triggering a resize event
30 | if (this.windowWidth !== windowWidth && this.view) {
31 | this.windowWidth = windowWidth
32 | this.view.onResize(windowWidth)
33 | }
34 | }
35 |
36 | onVisibilityChange() {
37 | if (this.view) {
38 | this.view.onVisibilityChange()
39 | }
40 | }
41 |
42 | async updateTheme() {
43 | let theme = localStorage.getItem('theme') || 'light'
44 | if (document.body.className !== theme) {
45 | document.body.className = theme
46 | }
47 | try {
48 | this.isUserLogged = (await this.isUserLoggedCache.get()).is_user_logged
49 | if (this.isUserLogged) {
50 | theme = (await this.userCache.get()).theme
51 | localStorage.setItem('theme', theme)
52 | }
53 | } catch (e) {
54 | //Let the view handle network failures
55 | }
56 | if (document.body.className !== theme) {
57 | document.body.className = theme || 'light'
58 | }
59 | }
60 |
61 | setView(view: ScreenView) {
62 | if (this.view) {
63 | this.view.destroy()
64 | setTitle({})
65 | }
66 | this.view = view
67 | document.body.innerHTML = ''
68 | this.updateTheme().then()
69 | this.loader.render($)
70 | if (!this.view.requiresAuth) {
71 | document.body.innerHTML = ''
72 | this.windowWidth = null
73 | this.onResize()
74 | document.body.append(this.view.render())
75 | return
76 | }
77 | this.isUserLoggedCache.get().then(value => {
78 | this.isUserLogged = value.is_user_logged
79 | if (this.view.requiresAuth && !value.is_user_logged) {
80 | ROUTER.navigate(`/login#return_url=${window.location.pathname}`)
81 | return
82 | }
83 | document.body.innerHTML = ''
84 | this.windowWidth = null
85 | this.onResize()
86 | document.body.append(this.view.render())
87 | }).catch(e => {
88 | handleNetworkErrorInplace(e)
89 | })
90 | }
91 | }
92 |
93 | export {ScreenContainer}
94 |
--------------------------------------------------------------------------------
/ui/src/screen_view.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElement} from '../../lib/weya/weya'
2 |
3 | abstract class ScreenView {
4 | get requiresAuth() {
5 | return true
6 | }
7 |
8 | abstract render(): WeyaElement
9 |
10 | onResize(width: number) {
11 | }
12 |
13 | destroy() {
14 | }
15 |
16 | onRefresh() {
17 | }
18 |
19 | onVisibilityChange() {
20 | }
21 | }
22 |
23 | export {ScreenView}
24 |
--------------------------------------------------------------------------------
/ui/src/sentry.ts:
--------------------------------------------------------------------------------
1 | let Sentry = (window).Sentry
2 | let Integrations = (window).Sentry.Integrations
3 |
4 | export {
5 | Sentry,
6 | Integrations
7 | }
--------------------------------------------------------------------------------
/ui/src/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "labml.ai",
3 | "icons": [
4 | {
5 | "src": "/images/android-chrome-192x192.png",
6 | "sizes": "192x192",
7 | "type": "image/png"
8 | },
9 | {
10 | "src": "/images/android-chrome-512x512.png",
11 | "sizes": "512x512",
12 | "type": "image/png"
13 | }
14 | ],
15 | "theme_color": "#ffffff",
16 | "background_color": "#ffffff",
17 | "description": "Monitor PyTorch & TensorFlow model training on mobile phones",
18 | "display": "standalone",
19 | "shortcuts": [
20 | {
21 | "name": "Runs",
22 | "url": "/runs",
23 | "description": "Machine learning experiments"
24 | },
25 | {
26 | "name": "Computers",
27 | "url": "/computers",
28 | "description": "Computer monitoring"
29 | },
30 | {
31 | "name": "Settings",
32 | "url": "/settings"
33 | }
34 | ],
35 | "start_url": "/"
36 | }
37 |
--------------------------------------------------------------------------------
/ui/src/types.ts:
--------------------------------------------------------------------------------
1 | export type ContentType = 'run' | 'session'
2 |
--------------------------------------------------------------------------------
/ui/src/utils/document.ts:
--------------------------------------------------------------------------------
1 | export function setTitle(opt: { section?: string, item?: string }) {
2 | if (opt.section != null && opt.item != null) {
3 | document.title = `${opt.section} - ${opt.item} - labml.ai`
4 | } else if (opt.section != null || opt.item != null) {
5 | document.title = `${opt.section || opt.item} - labml.ai`
6 | } else {
7 | document.title = 'labml.ai'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ui/src/utils/meta_tags.ts:
--------------------------------------------------------------------------------
1 | import {Run} from '../models/run'
2 |
3 | const metaTag = `Monitor PyTorch & TensorFlow model training on mobile phones`
4 |
5 | function setMetaDes(des: string) {
6 | document.getElementsByTagName('meta')
7 | .namedItem('description')
8 | .setAttribute('content', des)
9 | }
10 |
11 |
12 | export function changeRunDec(run: Run) {
13 | let metaStr = `${metaTag}\n${run.name};${run.comment}\n${run.run_uuid}`
14 |
15 | setMetaDes(metaStr)
16 | }
--------------------------------------------------------------------------------
/ui/src/utils/mobile.ts:
--------------------------------------------------------------------------------
1 | export function detectMobile(): boolean {
2 | return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
3 | }
4 |
5 | let isMobile = detectMobile()
6 |
7 | export default isMobile
--------------------------------------------------------------------------------
/ui/src/utils/new_tab.ts:
--------------------------------------------------------------------------------
1 | import {UserMessages} from "../components/user_messages"
2 |
3 |
4 | export function openInNewTab(url, userMessages?: UserMessages) {
5 | let newWindow = window.open(url, '_blank')
6 |
7 | if (!newWindow || newWindow.closed || typeof newWindow.closed == 'undefined') {
8 | let error = `cannot open ${url}: blocked pop up windows`
9 | if (userMessages) {
10 | userMessages.warning(error)
11 | } else {
12 | throw new Error(error)
13 | }
14 | } else {
15 | newWindow.focus()
16 | }
17 | }
--------------------------------------------------------------------------------
/ui/src/utils/redirect.ts:
--------------------------------------------------------------------------------
1 | import {NetworkError} from '../network';
2 | import {ROUTER} from '../app';
3 | import {Sentry} from '../sentry';
4 | import {PageNotFoundHandler} from '../views/errors/page_not_found_view'
5 | import {AuthErrorHandler} from '../views/errors/auth_error_view'
6 | import {OtherErrorHandler} from '../views/errors/other_error_view'
7 | import {NetworkErrorHandler} from '../views/errors/network_error_view'
8 |
9 | export function handleNetworkError(error: Error | NetworkError) {
10 | if (error instanceof NetworkError) {
11 | if (error.statusCode === 404 || error.statusCode === 400) {
12 | ROUTER.navigate('/404')
13 | } else if (error.statusCode === 401 || error.statusCode === 403) {
14 | ROUTER.navigate('/401')
15 | } else {
16 | ROUTER.navigate('/500')
17 | }
18 | } else {
19 | ROUTER.navigate('/network_error')
20 | }
21 | Sentry.setExtra('error', error)
22 | Sentry.captureException(error)
23 | }
24 |
25 | export function handleNetworkErrorInplace(error: Error | NetworkError) {
26 | if (error instanceof NetworkError) {
27 | if (error.statusCode === 404 || error.statusCode === 400) {
28 | PageNotFoundHandler.handlePageNotFound()
29 | } else if (error.statusCode === 401 || error.statusCode === 403) {
30 | AuthErrorHandler.handleAuthError()
31 | } else {
32 | OtherErrorHandler.handleOtherError()
33 | }
34 | } else {
35 | NetworkErrorHandler.handleNetworkError()
36 | }
37 |
38 | Sentry.setExtra('error', error)
39 | Sentry.captureException(error)
40 | }
41 |
--------------------------------------------------------------------------------
/ui/src/utils/render.ts:
--------------------------------------------------------------------------------
1 | export async function waitForFrame() {
2 | return new Promise((resolve) => {
3 | window.requestAnimationFrame(() => {
4 | resolve()
5 | })
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/ui/src/utils/time.ts:
--------------------------------------------------------------------------------
1 | export function formatTime(time: number): string {
2 | let date = new Date(time * 1000)
3 | let timeStr = date.toTimeString().substr(0, 8)
4 | let dateStr = date.toDateString()
5 |
6 | return `${dateStr} at ${timeStr}`
7 | }
8 |
9 |
10 | export function getTimeDiff(timestamp: number): string {
11 | let timeDiff = (Date.now() / 1000 - timestamp / 1000)
12 |
13 | if (timeDiff < 60) {
14 | return `${Math.round(timeDiff)}s ago`
15 | } else if (timeDiff < 600) {
16 | return `${Math.floor(timeDiff / 60)}m and ${Math.round(timeDiff % 60)}s ago`
17 | } else {
18 | return formatTime(timestamp / 1000)
19 | }
20 | }
21 |
22 | const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
23 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24 |
25 |
26 | export function formatDateTime(dateTime: Date) {
27 | let date = dateTime.getDate()
28 | let month = monthNames[dateTime.getMonth()]
29 | let timeStr = dateTime.toTimeString().substr(0, 8)
30 |
31 | return `${month} ${date},${timeStr}`
32 | }
--------------------------------------------------------------------------------
/ui/src/utils/window_dimentions.ts:
--------------------------------------------------------------------------------
1 | export function getWindowDimensions() {
2 | const {innerWidth: width, innerHeight: height} = window
3 |
4 | return {
5 | width,
6 | height
7 | };
8 | }
9 |
--------------------------------------------------------------------------------
/ui/src/views/empty_runs_list.ts:
--------------------------------------------------------------------------------
1 | import {Weya, WeyaElementFunction, WeyaElement} from '../../../lib/weya/weya'
2 | import PyTorchCode from '../components/codes/pytorch'
3 | import KerasCode from '../components/codes/keras'
4 | import PyTorchLightningCode from '../components/codes/pytorch_lightning'
5 |
6 | export default class EmptyRunsList {
7 | currentTab: string
8 | codeContainer: WeyaElement
9 |
10 | constructor() {
11 | this.currentTab = 'pytoch'
12 | }
13 |
14 | clickHandle(tab: string) {
15 | this.currentTab = tab
16 | this.renderCode()
17 | }
18 |
19 | render($: WeyaElementFunction) {
20 | $('div.text-center', $ => {
21 | $('h5.mt-4.px-1', 'You will see your runs here')
22 | $('p.px-1', 'Start monitoring your models by adding just two lines of code:')
23 | })
24 |
25 | $('div.text-center', $ => {
26 | $('nav.nav-link.d-inline.tab', 'PyTorch', {on: {click: () => this.clickHandle('pytoch')}})
27 | $('nav.nav-link.d-inline.tab', 'PyTorch Lightning', {on: {click: () => this.clickHandle('lightning')}})
28 | $('nav.nav-link.d-inline.tab', 'Keras', {on: {click: () => this.clickHandle('keras')}})
29 | })
30 |
31 | this.codeContainer = $('div')
32 | this.renderCode()
33 | }
34 |
35 | renderCode() {
36 | this.codeContainer.innerHTML = ''
37 |
38 | Weya(this.codeContainer, $ => {
39 | if (this.currentTab === 'pytoch') {
40 | new PyTorchCode().render($)
41 | } else if (this.currentTab === 'keras') {
42 | new KerasCode().render($)
43 | } else if (this.currentTab === 'lightning') {
44 | new PyTorchLightningCode().render($)
45 | }
46 | })
47 | }
48 | }
--------------------------------------------------------------------------------
/ui/src/views/empty_sessions_list.ts:
--------------------------------------------------------------------------------
1 | import {WeyaElementFunction} from '../../../lib/weya/weya'
2 |
3 | export default class EmptySessionsList {
4 | constructor() {
5 | }
6 |
7 | render($: WeyaElementFunction) {
8 | $('div.text-center', $ => {
9 | $('h5.mt-4.px-1', 'You will see your computers here')
10 | $('p.px-1', 'Run the following command to start monitoring:')
11 | })
12 |
13 | $('div', $ => {
14 | $('div.code-sample.bg-dark.px-1.py-2.my-3.code-container', $ => {
15 | $('pre.text-white', $ => {
16 | $('div.labml-api', $ => {
17 | $('span.key-word', 'pip')
18 | $('span', ' install labml psutil py3nvml')
19 | $('br')
20 | $('span.key-word', 'labml')
21 | $('span', ' monitor')
22 | })
23 | })
24 | })
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/src/views/errors/auth_error_view.ts:
--------------------------------------------------------------------------------
1 | import {ROUTER, SCREEN} from '../../app'
2 | import {Weya as $} from '../../../../lib/weya/weya'
3 | import mix_panel from "../../mix_panel"
4 | import {setTitle} from '../../utils/document'
5 | import {ScreenView} from '../../screen_view'
6 |
7 | function wrapEvent(eventName: string, func: Function) {
8 | function wrapper() {
9 | let e: Event = arguments[arguments.length - 1]
10 | if (eventName[eventName.length - 1] !== '_') {
11 | e.preventDefault()
12 | e.stopPropagation()
13 | }
14 |
15 | func.apply(null, arguments)
16 | }
17 |
18 | return wrapper
19 | }
20 |
21 | class AuthErrorView extends ScreenView {
22 | elem: HTMLDivElement
23 | private events = {
24 | back: () => {
25 | if (ROUTER.canBack()) {
26 | ROUTER.back()
27 | } else {
28 | ROUTER.navigate('/')
29 | }
30 | },
31 | login: () => {
32 | ROUTER.navigate(`/login`)
33 | },
34 | slack: () => {
35 | window.open('https://join.slack.com/t/labforml/shared_invite/zt-egj9zvq9-Dl3hhZqobexgT7aVKnD14g/')
36 | },
37 | }
38 |
39 | constructor() {
40 | super()
41 | let events = []
42 | for (let k in this.events) {
43 | events.push(k)
44 | }
45 |
46 | for (let k of events) {
47 | let func = this.events[k]
48 | this.events[k] = wrapEvent(k, func)
49 | }
50 |
51 | mix_panel.track('401 View')
52 | }
53 |
54 | get requiresAuth(): boolean {
55 | return false
56 | }
57 |
58 | render() {
59 | setTitle({section: '401'})
60 | this.elem = $('div', '.error-container', $ => {
61 | $('h2', '.mt-5', 'Ooops! Authentication Failure.' + '')
62 | $('h1', '401')
63 | $('p', 'We are having trouble authenticating your request' + '')
64 | $('div', '.btn-container.mt-3', $ => {
65 | $('button', '.btn.nav-link',
66 | {on: {click: this.events.back}},
67 | $ => {
68 | $('span', '.fas.fa-redo', '')
69 | $('span', '.m-1', 'Retry')
70 | })
71 | $('button', '.btn.nav-link',
72 | {on: {click: this.events.login}},
73 | $ => {
74 | $('span', '.fas.fa-user', '')
75 | $('span', '.m-1', 'Login Again')
76 | })
77 | $('button', '.btn.nav-link',
78 | {on: {click: this.events.slack}},
79 | $ => {
80 | $('span', '.fas.fa-comments', '')
81 | $('span', '.m-1', 'Reach us on Slack')
82 | })
83 | })
84 |
85 | })
86 |
87 | return this.elem
88 | }
89 |
90 | destroy() {
91 | }
92 | }
93 |
94 | export class AuthErrorHandler {
95 | constructor() {
96 | ROUTER.route('401', [AuthErrorHandler.handleAuthError])
97 | }
98 |
99 | static handleAuthError = () => {
100 | SCREEN.setView(new AuthErrorView())
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/ui/src/views/errors/network_error_view.ts:
--------------------------------------------------------------------------------
1 | import {ROUTER, SCREEN} from '../../app'
2 | import {Weya as $} from '../../../../lib/weya/weya'
3 | import mix_panel from "../../mix_panel"
4 | import {setTitle} from '../../utils/document'
5 | import {ScreenView} from '../../screen_view'
6 |
7 | function wrapEvent(eventName: string, func: Function) {
8 | function wrapper() {
9 | let e: Event = arguments[arguments.length - 1]
10 | if (eventName[eventName.length - 1] !== '_') {
11 | e.preventDefault()
12 | e.stopPropagation()
13 | }
14 |
15 | func.apply(null, arguments)
16 | }
17 |
18 | return wrapper
19 | }
20 |
21 | class NetworkErrorView extends ScreenView {
22 | elem: HTMLDivElement
23 | private events = {
24 | retry: () => {
25 | if (ROUTER.canBack()) {
26 | ROUTER.back()
27 | }
28 | },
29 | }
30 |
31 | constructor() {
32 | super()
33 | let events = []
34 | for (let k in this.events) {
35 | events.push(k)
36 | }
37 |
38 | for (let k of events) {
39 | let func = this.events[k]
40 | this.events[k] = wrapEvent(k, func)
41 | }
42 |
43 | mix_panel.track('Network Error View')
44 | }
45 |
46 | get requiresAuth(): boolean {
47 | return false
48 | }
49 |
50 | render() {
51 | setTitle({section: 'Network Error'})
52 | this.elem = $('div', '.error-container', $ => {
53 | $('h2', '.mt-5', 'Ooops!' + '')
54 | $('p', 'There\'s a problem with the connection between you and us' + '')
55 | $('div', '.btn-container.mt-3', $ => {
56 | $('button', '.btn.nav-link',
57 | {on: {click: this.events.retry}},
58 | $ => {
59 | $('span', '.fas.fa-redo', '')
60 | $('span', '.m-1', 'Retry')
61 | })
62 | })
63 | })
64 |
65 | return this.elem
66 | }
67 |
68 | destroy() {
69 | }
70 | }
71 |
72 | export class NetworkErrorHandler {
73 | constructor() {
74 | ROUTER.route('network_error', [NetworkErrorHandler.handleNetworkError])
75 | }
76 |
77 | static handleNetworkError = () => {
78 | SCREEN.setView(new NetworkErrorView())
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ui/src/views/errors/other_error_view.ts:
--------------------------------------------------------------------------------
1 | import {ROUTER, SCREEN} from '../../app'
2 | import {Weya as $} from '../../../../lib/weya/weya'
3 | import mix_panel from "../../mix_panel"
4 | import {setTitle} from '../../utils/document'
5 | import {ScreenView} from '../../screen_view'
6 |
7 | function wrapEvent(eventName: string, func: Function) {
8 | function wrapper() {
9 | let e: Event = arguments[arguments.length - 1]
10 | if (eventName[eventName.length - 1] !== '_') {
11 | e.preventDefault()
12 | e.stopPropagation()
13 | }
14 |
15 | func.apply(null, arguments)
16 | }
17 |
18 | return wrapper
19 | }
20 |
21 | class OtherErrorView extends ScreenView {
22 | elem: HTMLDivElement
23 | private events = {
24 | back: () => {
25 | if (ROUTER.canBack()) {
26 | ROUTER.back()
27 | } else {
28 | ROUTER.navigate('/')
29 | }
30 | },
31 | slack: () => {
32 | window.open('https://join.slack.com/t/labforml/shared_invite/zt-egj9zvq9-Dl3hhZqobexgT7aVKnD14g/')
33 | },
34 | }
35 |
36 | constructor() {
37 | super()
38 | let events = []
39 | for (let k in this.events) {
40 | events.push(k)
41 | }
42 |
43 | for (let k of events) {
44 | let func = this.events[k]
45 | this.events[k] = wrapEvent(k, func)
46 | }
47 |
48 | mix_panel.track('500 View')
49 | }
50 |
51 | get requiresAuth(): boolean {
52 | return false
53 | }
54 |
55 | render() {
56 | setTitle({section: '500'})
57 | this.elem = $('div', '.error-container', $ => {
58 | $('h2', '.mt-5', 'Ooops! Something went wrong' + '')
59 | $('h1', '500')
60 | $('p', 'Seems like we are having issues right now' + '')
61 | $('div', '.btn-container.mt-3', $ => {
62 | $('button', '.btn.nav-link',
63 | {on: {click: this.events.back}},
64 | $ => {
65 | $('span', '.fas.fa-redo', '')
66 | $('span', '.m-1', 'Retry')
67 | })
68 | $('button', '.btn.nav-link',
69 | {on: {click: this.events.slack}},
70 | $ => {
71 | $('span', '.fas.fa-comments', '')
72 | $('span', '.m-1', 'Reach us on Slack')
73 | })
74 | })
75 |
76 | })
77 |
78 | return this.elem
79 | }
80 |
81 | destroy() {
82 | }
83 | }
84 |
85 | export class OtherErrorHandler {
86 | constructor() {
87 | ROUTER.route('500', [OtherErrorHandler.handleOtherError])
88 | }
89 |
90 | static handleOtherError = () => {
91 | SCREEN.setView(new OtherErrorView())
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/ui/src/views/errors/page_not_found_view.ts:
--------------------------------------------------------------------------------
1 | import {ROUTER, SCREEN} from '../../app'
2 | import {Weya as $} from '../../../../lib/weya/weya'
3 | import mix_panel from "../../mix_panel"
4 | import {setTitle} from '../../utils/document'
5 | import {ScreenView} from '../../screen_view'
6 |
7 | function wrapEvent(eventName: string, func: Function) {
8 | function wrapper() {
9 | let e: Event = arguments[arguments.length - 1]
10 | if (eventName[eventName.length - 1] !== '_') {
11 | e.preventDefault()
12 | e.stopPropagation()
13 | }
14 |
15 | func.apply(null, arguments)
16 | }
17 |
18 | return wrapper
19 | }
20 |
21 | class PageNotFoundView extends ScreenView {
22 | elem: HTMLDivElement
23 | private events = {
24 | home: () => {
25 | ROUTER.navigate(`/`)
26 | },
27 | }
28 |
29 | constructor() {
30 | super()
31 | let events = []
32 | for (let k in this.events) {
33 | events.push(k)
34 | }
35 |
36 | for (let k of events) {
37 | let func = this.events[k]
38 | this.events[k] = wrapEvent(k, func)
39 | }
40 |
41 | mix_panel.track('404 View')
42 | }
43 |
44 | get requiresAuth(): boolean {
45 | return false
46 | }
47 |
48 | render() {
49 | setTitle({section: '404'})
50 | this.elem = $('div', '.error-container', $ => {
51 | $('h2', '.mt-5', 'Ooops! Page not found.' + '')
52 | $('h1', '404')
53 | $('p', 'We can\'t find the page.' + '')
54 | $('div', '.btn-container.mt-3', $ => {
55 | $('button', '.btn.nav-link',
56 | {on: {click: this.events.home}},
57 | $ => {
58 | $('span', '.fas.fa-home', '')
59 | $('span', '.m-1', 'Home')
60 | })
61 | })
62 | })
63 |
64 | return this.elem
65 | }
66 |
67 | destroy() {
68 | }
69 | }
70 |
71 | export class PageNotFoundHandler {
72 | constructor() {
73 | ROUTER.route('404', [PageNotFoundHandler.handlePageNotFound])
74 | }
75 |
76 | static handlePageNotFound = () => {
77 | SCREEN.setView(new PageNotFoundView())
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/ui/src/views/login_view.ts:
--------------------------------------------------------------------------------
1 | import {IsUserLogged} from '../models/user'
2 | import {ROUTER, SCREEN} from '../app'
3 | import {Weya as $, WeyaElement} from '../../../lib/weya/weya'
4 | import {Loader} from "../components/loader"
5 | import CACHE, {IsUserLoggedCache} from "../cache/cache"
6 | import NETWORK from '../network'
7 | import {handleNetworkError} from '../utils/redirect';
8 | import {setTitle} from '../utils/document'
9 | import {ScreenView} from '../screen_view'
10 |
11 | class LoginView extends ScreenView {
12 | isUserLogged: IsUserLogged
13 | isUserLoggedCache: IsUserLoggedCache
14 | elem: WeyaElement
15 | loader: Loader
16 | token: string
17 | returnUrl: string
18 |
19 | constructor(token?: string, returnUrl: string = '/runs') {
20 | super()
21 |
22 | this.isUserLoggedCache = CACHE.getIsUserLogged()
23 | this.loader = new Loader(true)
24 | this.token = token
25 | this.returnUrl = returnUrl
26 | }
27 |
28 | get requiresAuth(): boolean {
29 | return false;
30 | }
31 |
32 | render() {
33 | this.elem = $('div', $ => {
34 | this.loader.render($)
35 | })
36 |
37 | this.handleLogin().then()
38 | setTitle({section: 'Login'})
39 |
40 | return this.elem
41 | }
42 |
43 | private async handleLogin() {
44 | this.isUserLogged = await this.isUserLoggedCache.get()
45 |
46 | if (this.token) {
47 | try {
48 | let res = await NETWORK.signIn(this.token)
49 | if (!res.is_successful) {
50 | ROUTER.navigate('/401')
51 | }
52 | localStorage.setItem('app_token', res.app_token)
53 | this.isUserLogged = await this.isUserLoggedCache.get(true)
54 | } catch (e) {
55 | handleNetworkError(e)
56 | return
57 | }
58 | await SCREEN.updateTheme().then()
59 | }
60 |
61 | if (!this.isUserLogged.is_user_logged) {
62 | NETWORK.redirectLogin()
63 | return
64 | }
65 |
66 | ROUTER.navigate(this.returnUrl)
67 | this.loader.remove()
68 | }
69 | }
70 |
71 | export class LoginHandler {
72 | constructor() {
73 | ROUTER.route('login', [this.handleLogin])
74 | }
75 |
76 | handleLogin = () => {
77 | let params = (window.location.hash.substr(1)).split("&")
78 | let token = undefined
79 | let returnUrl = sessionStorage.getItem('return_url') || '/runs'
80 |
81 | for (let i = 0; i < params.length; i++) {
82 | let val = params[i].split('=')
83 | switch (val[0]) {
84 | case 'access_token':
85 | token = val[1]
86 | break
87 | case 'return_url':
88 | returnUrl = val[1]
89 | break
90 | }
91 | }
92 | sessionStorage.setItem('return_url', returnUrl)
93 | SCREEN.setView(new LoginView(token, returnUrl))
94 | }
95 | }
96 |
--------------------------------------------------------------------------------