Skip to content

Commit a494f5c

Browse files
author
Ceyda Cinarel
committed
add error message,make logpath configurable, remove model_name requirement
1 parent 651f00b commit a494f5c

File tree

4 files changed

+92
-34
lines changed

4 files changed

+92
-34
lines changed

README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Torchserve Dashboard
22

3-
Torchserve Dashboard using streamlit
3+
Torchserve Dashboard using Streamlit
44

55
Related blog [post](https://cceyda.github.io/blog/torchserve/streamlit/dashboard/2020/10/15/torchserve.html)
66

@@ -13,9 +13,12 @@ Simply run:
1313

1414
```bash
1515
pip3 install torchserve-dashboard --user
16-
# torchserve-dashboard [streamlit_options] -- [config_path] [model_store(optional)]
16+
# torchserve-dashboard [streamlit_options] -- [config_path] [model_store(optional)] [log_location(optional)] [metrics_location(optional)]
1717
torchserve-dashboard --server.port 8105 -- --config_path ./torchserve.properties --model_store ./model_store
1818
```
19+
20+
:exclamation: Keep in mind that If you change any of the `--config_path`,`--model_store`,`--metrics_location`,`--log_location` options while there is a torchserver already running before starting torch-dashboard they won't come into effect until you stop&start torchserve.
21+
1922
OR
2023
```bash
2124
git clone https://github.com/cceyda/torchserve-dashboard.git
@@ -31,10 +34,34 @@ number_of_gpu=0
3134
batch_size=1
3235
model_store=/mnt/pretrained/model_store
3336
```
37+
3438
# Updates
3539
[15-oct-2020] add [scale workers](https://pytorch.org/serve/management_api.html#scale-workers) tab
3640

37-
# Help
41+
[16-feb-2021] (functionality) make logpath configurable,(functionality)remove model_name requirement,(UI)add cosmetic error messages
42+
43+
# FAQs
44+
- **Does torchserver keep running in the background?**
45+
46+
The torchserver is spawned using `Popen` and keeps running in the background even if you stop the dashboard.
47+
48+
- **What about environment variables?**
49+
50+
These environment variables are passed to the torchserve command:
51+
52+
`ENVIRON_WHITELIST=["LD_LIBRARY_PATH","LC_CTYPE","LC_ALL","PATH","JAVA_HOME","PYTHONPATH","TS_CONFIG_FILE","LOG_LOCATION","METRICS_LOCATION"]`
53+
54+
- **How to change the logging format of torchserve?**
55+
56+
You can set the location of your custom log4j config in your configuration file as in [here](https://pytorch.org/serve/logging.html#provide-with-config-properties)
57+
58+
`vmargs=-Dlog4j.configuration=file:///path/to/custom/log4j.properties`
59+
60+
- **What is the meaning behind the weird versioning**
61+
62+
The minor follows the compatible torchserve version, patch version reflects the dashboard versioning
63+
64+
# Help & Question & Feedback
3865

3966
Open an issue
4067

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"""
44
from setuptools import find_packages, setup
55

6-
dependencies = ["streamlit>=0.68", "click>=7.1.2", "httpx>=0.16.0"]
6+
dependencies = ["streamlit>=0.76", "click>=7.1.2", "httpx>=0.16.0"]
77

88
setup(
99
name="torchserve_dashboard",
10-
version="v0.2.3",
10+
version="v0.2.4",
1111
url="https://github.com/cceyda/torchserve-dashboard",
1212
license="Apache Software License 2.0",
1313
author="Ceyda Cinarel",

torchserve_dashboard/api.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import streamlit as st
77

8+
ENVIRON_WHITELIST=["LD_LIBRARY_PATH","LC_CTYPE","LC_ALL","PATH","JAVA_HOME","PYTHONPATH","TS_CONFIG_FILE","LOG_LOCATION","METRICS_LOCATION"]
89

910
def raise_on_not200(response):
1011
if response.status_code != 200:
@@ -15,12 +16,23 @@ def raise_on_not200(response):
1516
client = httpx.Client(timeout=1000, event_hooks={"response": [raise_on_not200]})
1617

1718

18-
def start_torchserve(model_store, config_path):
19+
def start_torchserve(model_store, config_path, log_location=None, metrics_location=None):
20+
new_env={}
21+
env=os.environ()
22+
for x in ENVIRON_WHITELIST:
23+
if x in env:
24+
new_env[x]=env[x]
25+
26+
if log_location:
27+
new_env["LOG_LOCATION"]=log_location
28+
if metrics_location:
29+
new_env["METRICS_LOCATION"]=metrics_location
1930
if os.path.exists(model_store) and os.path.exists(config_path):
2031
torchserve_cmd = f"torchserve --start --ncs --model-store {model_store} --ts-config {config_path}"
2132
subprocess.Popen(
2233
torchserve_cmd.split(" "),
23-
stdout=open("/dev/null", "w"),
34+
env=new_env,
35+
stdout=open("/dev/null", "r"),
2436
stderr=open("/dev/null", "w"),
2537
preexec_fn=os.setpgrp,
2638
)

torchserve_dashboard/dash.py

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
from streamlit.script_runner import RerunException
77

88
import api as tsa
9+
from pathlib import Path
910

1011
st.set_page_config(
1112
page_title="Torchserve Management Dashboard",
1213
page_icon="./icon.png",
1314
layout="centered",
1415
initial_sidebar_state="expanded",
1516
)
16-
17+
st.write(os.environ)
1718
parser = argparse.ArgumentParser(description="Torchserve dashboard")
1819

1920
parser.add_argument("--model_store", default=None, help="Directory where your models are stored")
2021
parser.add_argument("--config_path", default="./default.torchserve.properties", help="Torchserve config path")
22+
parser.add_argument("--log_location", default="./", help="Passed as environment variable LOG_LOCATION to Torchserve")
23+
parser.add_argument("--metrics_location", default="./", help="Passed as environment variable METRICS_LOCATION to Torchserve")
2124
try:
2225
args = parser.parse_args()
2326
except SystemExit as e:
@@ -28,6 +31,12 @@
2831
M_API = "http://127.0.0.1:8081"
2932
model_store = args.model_store
3033
config_path = args.config_path
34+
log_location = args.log_location
35+
if log_location:
36+
log_location = str(Path(log_location).resolve())
37+
metrics_location = args.metrics_location
38+
if metrics_location:
39+
metrics_location = str(Path(metrics_location).resolve())
3140
config = None
3241
default_key = "None"
3342

@@ -56,16 +65,18 @@ def last_res():
5665
def get_model_store():
5766
return os.listdir(model_store)
5867

59-
68+
# As a design choice I'm leaving config_path,log_location,metrics_location non-editable from the UI as a semi-security measure (maybe?:/)
6069
##########Sidebar##########
6170
st.sidebar.markdown(f"## Help")
62-
st.sidebar.markdown(f"### Management API: \n {M_API}")
63-
st.sidebar.markdown(f"### Model Store Path: \n {model_store}")
64-
st.sidebar.markdown(f"### Config Path: \n {config_path}")
71+
with st.sidebar.beta_expander(label="Show Paths:", expanded=False):
72+
st.markdown(f"### Model Store Path: \n {model_store}")
73+
st.markdown(f"### Config Path: \n {config_path}")
74+
st.markdown(f"### Log Location: \n {log_location}")
75+
st.markdown(f"### Metrics Location: \n {metrics_location}")
6576

6677
start = st.sidebar.button("Start Torchserve")
6778
if start:
68-
last_res()[0]= tsa.start_torchserve(model_store, config_path)
79+
last_res()[0]= tsa.start_torchserve(model_store, config_path, log_location, metrics_location)
6980
rerun()
7081

7182
stop = st.sidebar.button("Stop Torchserve")
@@ -104,7 +115,7 @@ def get_model_store():
104115
p = st.checkbox("or use another path")
105116
if p:
106117
mar_path = placeholder.text_input("Input mar file path*")
107-
model_name = st.text_input(label="Model name *")
118+
model_name = st.text_input(label="Model name (overrides predefined)")
108119
col1, col2 = st.beta_columns(2)
109120
batch_size = col1.number_input(label="batch_size", value=0, min_value=0, step=1)
110121
max_batch_delay = col2.number_input(label="max_batch_delay", value=0, min_value=0, step=100)
@@ -114,21 +125,26 @@ def get_model_store():
114125
runtime = col2.text_input(label="runtime")
115126

116127
proceed = st.button("Register")
117-
if proceed and model_name and mar_path != default_key:
118-
st.write(f"Registering Model...{mar_path} as {model_name}")
119-
res = tsa.register_model(
120-
M_API,
121-
mar_path,
122-
model_name,
123-
handler=handler,
124-
runtime=runtime,
125-
batch_size=batch_size,
126-
max_batch_delay=max_batch_delay,
127-
initial_workers=initial_workers,
128-
response_timeout=response_timeout,
129-
)
130-
last_res()[0] = res
131-
rerun()
128+
if proceed:
129+
if mar_path != default_key:
130+
st.write(f"Registering Model...{mar_path}")
131+
res = tsa.register_model(
132+
M_API,
133+
mar_path,
134+
model_name,
135+
handler=handler,
136+
runtime=runtime,
137+
batch_size=batch_size,
138+
max_batch_delay=max_batch_delay,
139+
initial_workers=initial_workers,
140+
response_timeout=response_timeout,
141+
)
142+
last_res()[0] = res
143+
rerun()
144+
else:
145+
st.write(":octagonal_sign: Fill the required fileds!")
146+
147+
132148

133149
with st.beta_expander(label="Remove a model", expanded=False):
134150

@@ -141,11 +157,14 @@ def get_model_store():
141157
versions = [m["modelVersion"] for m in versions]
142158
version = st.selectbox("Choose version to remove", [default_key] + versions, index=0)
143159
proceed = st.button("Remove")
144-
if proceed and model_name != default_key and version != default_key:
145-
res = tsa.delete_model(M_API, model_name, version)
146-
last_res()[0] = res
147-
rerun()
148-
160+
if proceed:
161+
if model_name != default_key and version != default_key:
162+
res = tsa.delete_model(M_API, model_name, version)
163+
last_res()[0] = res
164+
rerun()
165+
else:
166+
st.write(":octagonal_sign: Pick a model & version!")
167+
149168
with st.beta_expander(label="Get model details", expanded=False):
150169

151170
st.header("Get model details")

0 commit comments

Comments
 (0)