Skip to content

Commit eaa22c9

Browse files
authored
Merge pull request #11 from gradio-app/reorg
Add a separate `trackio show` command to launch the UI
2 parents 86ce797 + 234da26 commit eaa22c9

7 files changed

Lines changed: 113 additions & 18 deletions

File tree

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ uv pip install trackio
2828

2929
## Usage
3030

31+
The usage of `trackio` is designed to be a drop-in replacement for `wandb` in most cases:
32+
3133
```python
3234
import trackio as wandb
3335
import random
@@ -38,7 +40,7 @@ epochs = 8
3840

3941
def simulate_multiple_runs():
4042
for run in range(runs):
41-
wandb.init(project="minimal-train-loop2", config={
43+
wandb.init(project="fake-training", config={
4244
"epochs": epochs,
4345
"learning_rate": 0.001,
4446
"batch_size": 64
@@ -66,6 +68,35 @@ def simulate_multiple_runs():
6668
simulate_multiple_runs()
6769
```
6870

71+
Running the above will print to the terminal instructions on launching the dashboard.
72+
73+
# Dashboard
74+
75+
You can launch the dashboard by running in your terminal:
76+
77+
```bash
78+
$ trackio show
79+
```
80+
81+
or, in Python:
82+
83+
```py
84+
trackio.show()
85+
```
86+
87+
You can also provide an optional `project` name as the argument to load a specific project directly:
88+
89+
```bash
90+
$ trackio show --project "my project"
91+
```
92+
93+
or, in Python:
94+
95+
```py
96+
trackio.show(project="my project")
97+
```
98+
99+
69100
![Screen Recording 2025-05-12 at 2 43 38 PM](https://github.com/user-attachments/assets/d627c9c3-7365-4250-839c-db67dde34a02)
70101

71102

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ dev = [
3232
"ruff==0.9.3"
3333
]
3434

35+
[project.scripts]
36+
trackio = "trackio.cli:main"
37+
3538
[tool.hatch.build.targets.wheel]
3639
packages = ["trackio"]
3740

trackio/__init__.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,54 @@
11
import contextvars
2+
import webbrowser
23
from pathlib import Path
34

45
from gradio_client import Client
56

67
from trackio.run import Run
7-
from trackio.ui import launch_gradio
8+
from trackio.ui import demo
9+
from trackio.utils import TRACKIO_DIR, block_except_in_notebook
810

911
__version__ = Path(__file__).parent.joinpath("version.txt").read_text().strip()
1012

1113

1214
current_run: contextvars.ContextVar[Run | None] = contextvars.ContextVar(
1315
"current_run", default=None
1416
)
15-
17+
current_project: contextvars.ContextVar[str | None] = contextvars.ContextVar(
18+
"current_project", default=None
19+
)
1620
current_server: contextvars.ContextVar[str | None] = contextvars.ContextVar(
1721
"current_server", default=None
1822
)
1923

2024
config = {}
2125

2226

23-
def init(project, name=None, config=None):
27+
def init(project: str, name: str | None = None, config: dict | None = None) -> Run:
2428
if not current_server.get():
25-
url = launch_gradio(show_api=False, inline=False, quiet=True)
26-
print(f"* Trackio server launched at: {url}")
27-
print(f"** View project dashboard at: {url}?project={project}")
29+
_, url, _ = demo.launch(
30+
show_api=False, inline=False, quiet=True, prevent_thread_lock=True
31+
)
2832
current_server.set(url)
2933
else:
3034
url = current_server.get()
35+
if current_project.get() is None or current_project.get() != project:
36+
print(f"* Trackio project initialized: {project}")
37+
print(f"* Trackio metrics logged to: {TRACKIO_DIR}")
38+
print(
39+
f'\n* View dashboard by running in your terminal: trackio show --project "{project}"'
40+
)
41+
print(f'* or by running in Python: trackio.show(project="{project}")')
42+
43+
current_project.set(project)
3144
client = Client(url, verbose=False)
3245
run = Run(project=project, client=client, name=name, config=config)
3346
current_run.set(run)
3447
globals()["config"] = run.config
48+
return run
3549

3650

37-
def log(metrics):
51+
def log(metrics: dict) -> None:
3852
if current_run.get() is None:
3953
raise RuntimeError("Call trackio.init() before log().")
4054
current_run.get().log(metrics)
@@ -44,3 +58,14 @@ def finish():
4458
if current_run.get() is None:
4559
raise RuntimeError("Call trackio.init() before finish().")
4660
current_run.get().finish()
61+
62+
63+
def show(project: str | None = None):
64+
_, url, share_url = demo.launch(
65+
show_api=False, quiet=True, inline=False, prevent_thread_lock=True
66+
)
67+
base_url = share_url + "/" if share_url else url
68+
dashboard_url = base_url + f"?project={project}" if project else base_url
69+
print(f"* Trackio UI launched at: {dashboard_url}")
70+
webbrowser.open(dashboard_url)
71+
block_except_in_notebook()

trackio/cli.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import argparse
2+
3+
from trackio import show
4+
5+
6+
def main():
7+
parser = argparse.ArgumentParser(description="Trackio CLI")
8+
subparsers = parser.add_subparsers(dest="command")
9+
10+
ui_parser = subparsers.add_parser(
11+
"show", help="Show the Trackio dashboard UI for a project"
12+
)
13+
ui_parser.add_argument(
14+
"--project", required=False, help="Project name to show in the dashboard"
15+
)
16+
17+
args = parser.parse_args()
18+
19+
if args.command == "show":
20+
show(args.project)
21+
else:
22+
parser.print_help()
23+
24+
25+
if __name__ == "__main__":
26+
main()

trackio/ui.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ def get_projects(request: gr.Request):
1111
storage = SQLiteStorage("", "", {})
1212
projects = storage.get_projects()
1313
if project := request.query_params.get("project"):
14-
pass
14+
interactive = False
1515
else:
16+
interactive = True
1617
project = projects[0] if projects else None
1718
return gr.Dropdown(
1819
label="Project",
1920
choices=projects,
2021
value=project,
2122
allow_custom_value=True,
23+
interactive=interactive,
2224
)
2325

2426

@@ -63,9 +65,9 @@ def log(project: str, run: str, metrics: dict[str, Any]) -> None:
6365

6466

6567
with gr.Blocks(theme="citrus") as demo:
66-
with gr.Sidebar():
68+
with gr.Sidebar() as sidebar:
6769
gr.Markdown("# 🎯 Trackio Dashboard")
68-
project_dd = gr.Dropdown(label="Project")
70+
project_dd = gr.Dropdown(label="Project", allow_custom_value=True)
6971
gr.Markdown("### ⚙️ Settings")
7072
realtime_cb = gr.Checkbox(label="Refresh realtime", value=True)
7173
with gr.Row():
@@ -125,10 +127,5 @@ def update_dashboard(project, runs):
125127
)
126128

127129

128-
def launch_gradio(**kwargs) -> str:
129-
_, url, _ = demo.launch(**kwargs)
130-
return url
131-
132-
133130
if __name__ == "__main__":
134-
launch_gradio()
131+
demo.launch()

trackio/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
import random
3+
import sys
4+
import time
35

46
from huggingface_hub.constants import HF_HOME
57

@@ -199,3 +201,14 @@ def generate_readable_name():
199201
noun = random.choice(nouns)
200202
number = random.randint(1, 99)
201203
return f"{adjective}-{noun}-{number}"
204+
205+
206+
def block_except_in_notebook():
207+
in_notebook = bool(getattr(sys, "ps1", sys.flags.interactive))
208+
if in_notebook:
209+
return
210+
try:
211+
while True:
212+
time.sleep(0.1)
213+
except (KeyboardInterrupt, OSError):
214+
print("Keyboard interruption in main thread... closing dashboard.")

trackio/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.5
1+
0.0.6

0 commit comments

Comments
 (0)