Skip to content

Commit f8db51a

Browse files
Adds a readonly (static) Trackio mode (includes a BREAKING change: trackio.sync() will now output readonly Spaces by default) (#455)
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
1 parent 3e5d950 commit f8db51a

29 files changed

Lines changed: 1816 additions & 350 deletions

.changeset/tough-turtles-hunt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trackio": minor
3+
---
4+
5+
feat:Adds a static Trackio mode via `trackio.sync(sdk="static")` and support for the `TRACKIO_SPACE_ID` environment variable

autonomous-experiments/02_autoresearch/train_autoresearch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def __init__(self, config, layer_idx):
452452
)
453453

454454
def forward(self, x, ve, cos_sin, window_size):
455-
B, T, C = x.size()
455+
B, T, _ = x.size()
456456
q = self.c_q(x).view(B, T, self.n_head, self.head_dim)
457457
k = self.c_k(x).view(B, T, self.n_kv_head, self.head_dim)
458458
v = self.c_v(x).view(B, T, self.n_kv_head, self.head_dim)
@@ -706,7 +706,7 @@ def setup_optimizer(
706706
return optimizer
707707

708708
def forward(self, idx, targets=None, reduction="mean"):
709-
B, T = idx.size()
709+
_, T = idx.size()
710710
assert T <= self.cos.size(1)
711711
cos_sin = self.cos[:, :T], self.sin[:, :T]
712712
x = self.transformer.wte(idx)
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
Example: log metrics + system metrics + media + table + report + files locally.
3+
4+
Usage:
5+
python examples/kitchen-sink-local-only.py
6+
"""
7+
8+
import math
9+
import random
10+
import time
11+
from pathlib import Path
12+
13+
import numpy as np
14+
import pandas as pd
15+
from PIL import Image as PILImage
16+
from PIL import ImageDraw
17+
18+
import trackio
19+
20+
PROJECT = f"local-full-demo-{random.randint(100000, 999999)}"
21+
STEPS = 8
22+
N_RUNS = 3
23+
24+
25+
def make_image(step: int, size: int = 128) -> PILImage.Image:
26+
img = PILImage.new("RGB", (size, size), "black")
27+
draw = ImageDraw.Draw(img)
28+
x = int((size / 2) + (size / 3) * math.sin(step * 0.8))
29+
y = int((size / 2) + (size / 3) * math.cos(step * 0.6))
30+
draw.ellipse((x - 10, y - 10, x + 10, y + 10), fill=(255, 120, 80))
31+
draw.rectangle((8, 8, size - 8, size - 8), outline=(80, 160, 255), width=2)
32+
return img
33+
34+
35+
def make_audio(step: int, sr: int = 16000, duration_s: float = 0.3) -> np.ndarray:
36+
t = np.linspace(0, duration_s, int(sr * duration_s), endpoint=False)
37+
freq = 220 + step * 20
38+
wave = 0.4 * np.sin(2 * np.pi * freq * t)
39+
return (wave * 32767).astype(np.int16)
40+
41+
42+
def main() -> None:
43+
examples_dir = Path(__file__).parent
44+
files_dir = examples_dir / "files"
45+
46+
run_names = [f"run-{run_idx}" for run_idx in range(N_RUNS)]
47+
48+
for run_idx in range(N_RUNS):
49+
trackio.init(
50+
project=PROJECT,
51+
name=f"run-{run_idx}",
52+
config={"steps": STEPS, "run_idx": run_idx, "mode": "local-only"},
53+
auto_log_gpu=False,
54+
)
55+
56+
trackio.alert(
57+
title="Run started",
58+
text=f"Project: {PROJECT} | Run: run-{run_idx}",
59+
level=trackio.AlertLevel.INFO,
60+
)
61+
62+
if run_idx == 0:
63+
trackio.save(files_dir / "config1.yml", project=PROJECT)
64+
trackio.save(files_dir / "config2.yml", project=PROJECT)
65+
66+
for step in range(STEPS):
67+
train_loss = round(
68+
max(0.05, 1.8 * math.exp(-0.35 * step) + random.gauss(0, 0.03)),
69+
4,
70+
)
71+
train_acc = round(min(0.99, 0.45 + 0.07 * step + random.gauss(0, 0.015)), 4)
72+
73+
img = make_image(step + run_idx)
74+
audio = make_audio(step + run_idx)
75+
76+
table_df = pd.DataFrame(
77+
{
78+
"sample_id": [f"s-{step}-a", f"s-{step}-b"],
79+
"prediction": ["cat", "dog"],
80+
"confidence": [
81+
round(0.65 + 0.03 * step + 0.01 * run_idx, 3),
82+
round(0.58 + 0.025 * step + 0.01 * run_idx, 3),
83+
],
84+
}
85+
)
86+
87+
trackio.log(
88+
{
89+
"train/loss": train_loss,
90+
"train/accuracy": train_acc,
91+
"media/preview_image": trackio.Image(
92+
img, caption=f"step {step}, run {run_idx}"
93+
),
94+
"media/preview_audio": trackio.Audio(
95+
audio, sample_rate=16000, format="wav"
96+
),
97+
"tables/predictions": trackio.Table(dataframe=table_df),
98+
},
99+
step=step,
100+
)
101+
102+
trackio.log_system(
103+
{
104+
"cpu_percent": round(25 + step * 2 + random.uniform(-1.0, 1.0), 2),
105+
"memory_gb": round(
106+
3.2 + step * 0.05 + random.uniform(-0.03, 0.03), 3
107+
),
108+
}
109+
)
110+
111+
if step == STEPS - 2:
112+
trackio.alert(
113+
title="Loss is drifting",
114+
text=f"Run run-{run_idx} step={step} loss={train_loss}",
115+
level=trackio.AlertLevel.WARN,
116+
)
117+
118+
report_md = f"""# Local Full Sync Report
119+
120+
Project: `{PROJECT}`
121+
Run: `run-{run_idx}`
122+
123+
This run logs:
124+
- scalar metrics
125+
- system metrics
126+
- media (image + audio)
127+
- a table artifact
128+
"""
129+
trackio.log({"reports/summary": trackio.Markdown(report_md)})
130+
131+
if run_idx == N_RUNS - 1:
132+
final_report_md = f"""# Final Local Kitchen Sink Report
133+
134+
Project: `{PROJECT}`
135+
Runs: {", ".join(f"`{name}`" for name in run_names)}
136+
137+
What to look for:
138+
- Alerts on this page and in the alert panel (run started + one drift warning).
139+
- Reports entries in the Reports tab (summary per run + this final report).
140+
"""
141+
trackio.log(
142+
{"reports/final_report": trackio.Markdown(final_report_md)},
143+
step=STEPS - 1,
144+
)
145+
146+
trackio.alert(
147+
title="Run finished",
148+
text=f"Completed run-{run_idx} ({STEPS} steps).",
149+
level=trackio.AlertLevel.INFO,
150+
)
151+
trackio.finish()
152+
153+
result = trackio.show(
154+
project=PROJECT,
155+
open_browser=False,
156+
block_thread=False,
157+
)
158+
full_url = result[3]
159+
print(f"Dashboard: {full_url}")
160+
time.sleep(3600)
161+
162+
163+
if __name__ == "__main__":
164+
main()
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""
2+
Example: log metrics + system metrics + media + table + report + files, then sync to a static HF Space.
3+
4+
Usage:
5+
python examples/sync-static-space-everything.py
6+
"""
7+
8+
import math
9+
import random
10+
from pathlib import Path
11+
12+
import numpy as np
13+
import pandas as pd
14+
from PIL import Image as PILImage
15+
from PIL import ImageDraw
16+
17+
import trackio
18+
19+
PROJECT = f"sync-full-demo-{random.randint(100000, 999999)}"
20+
STEPS = 8
21+
22+
23+
def make_image(step: int, size: int = 128) -> PILImage.Image:
24+
img = PILImage.new("RGB", (size, size), "black")
25+
draw = ImageDraw.Draw(img)
26+
x = int((size / 2) + (size / 3) * math.sin(step * 0.8))
27+
y = int((size / 2) + (size / 3) * math.cos(step * 0.6))
28+
draw.ellipse((x - 10, y - 10, x + 10, y + 10), fill=(255, 120, 80))
29+
draw.rectangle((8, 8, size - 8, size - 8), outline=(80, 160, 255), width=2)
30+
return img
31+
32+
33+
def make_audio(step: int, sr: int = 16000, duration_s: float = 0.3) -> np.ndarray:
34+
t = np.linspace(0, duration_s, int(sr * duration_s), endpoint=False)
35+
freq = 220 + step * 20
36+
wave = 0.4 * np.sin(2 * np.pi * freq * t)
37+
return (wave * 32767).astype(np.int16)
38+
39+
40+
def main():
41+
examples_dir = Path(__file__).parent
42+
files_dir = examples_dir / "files"
43+
44+
trackio.init(
45+
project=PROJECT,
46+
name="run-all-artifacts",
47+
config={"steps": STEPS, "mode": "static-sync-full"},
48+
auto_log_gpu=False,
49+
)
50+
51+
trackio.save(files_dir / "config1.yml")
52+
trackio.save(files_dir / "config2.yml")
53+
54+
for step in range(STEPS):
55+
train_loss = round(
56+
max(0.05, 1.8 * math.exp(-0.35 * step) + random.gauss(0, 0.03)), 4
57+
)
58+
train_acc = round(min(0.99, 0.45 + 0.07 * step + random.gauss(0, 0.015)), 4)
59+
img = make_image(step)
60+
audio = make_audio(step)
61+
62+
table_df = pd.DataFrame(
63+
{
64+
"sample_id": [f"s-{step}-a", f"s-{step}-b"],
65+
"prediction": ["cat", "dog"],
66+
"confidence": [
67+
round(0.65 + 0.03 * step, 3),
68+
round(0.58 + 0.025 * step, 3),
69+
],
70+
}
71+
)
72+
73+
trackio.log(
74+
{
75+
"train/loss": train_loss,
76+
"train/accuracy": train_acc,
77+
"media/preview_image": trackio.Image(img, caption=f"step {step}"),
78+
"media/preview_audio": trackio.Audio(
79+
audio, sample_rate=16000, format="wav"
80+
),
81+
"tables/predictions": trackio.Table(dataframe=table_df),
82+
},
83+
step=step,
84+
)
85+
86+
trackio.log_system(
87+
{
88+
"cpu_percent": round(25 + step * 2 + random.uniform(-1.0, 1.0), 2),
89+
"memory_gb": round(3.2 + step * 0.05 + random.uniform(-0.03, 0.03), 3),
90+
}
91+
)
92+
93+
report_md = f"""# Full Static Sync Report
94+
95+
Project: `{PROJECT}`
96+
97+
This run logs:
98+
99+
- scalar metrics
100+
- system metrics
101+
- media (image + audio)
102+
- a table artifact
103+
- saved files
104+
"""
105+
trackio.log({"reports/summary": trackio.Markdown(report_md)})
106+
trackio.finish()
107+
108+
space_id = trackio.sync(project=PROJECT)
109+
print(f"Dashboard: https://huggingface.co/spaces/{space_id}")
110+
111+
112+
if __name__ == "__main__":
113+
main()

examples/sync-static-space.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Example: Log some fake training data, then sync to a static HF Space.
3+
4+
Usage:
5+
python examples/sync-static-space.py
6+
7+
This will:
8+
1. Log a few runs of fake training metrics locally
9+
2. Call trackio.sync() which exports data as Parquet to an HF Dataset
10+
and deploys a static dashboard Space (no running server needed)
11+
12+
Set HF_TOKEN or run `huggingface-cli login` first.
13+
"""
14+
15+
import math
16+
import random
17+
18+
import trackio
19+
20+
PROJECT = f"sync-demo-{random.randint(100000, 999999)}"
21+
EPOCHS = 15
22+
23+
24+
def fake_loss(epoch, max_epochs):
25+
progress = epoch / max_epochs
26+
return max(0.05, 2.5 * math.exp(-3 * progress) + random.gauss(0, 0.2))
27+
28+
29+
def fake_accuracy(epoch, max_epochs):
30+
progress = epoch / max_epochs
31+
return min(
32+
0.95, 0.9 / (1 + math.exp(-6 * (progress - 0.5))) + random.gauss(0, 0.05)
33+
)
34+
35+
36+
for run_idx in range(3):
37+
trackio.init(
38+
project=PROJECT,
39+
name=f"run-{run_idx}",
40+
config={"lr": 0.001 * (run_idx + 1), "epochs": EPOCHS},
41+
)
42+
for epoch in range(EPOCHS):
43+
trackio.log(
44+
{
45+
"train/loss": round(fake_loss(epoch, EPOCHS), 4),
46+
"train/accuracy": round(fake_accuracy(epoch, EPOCHS), 4),
47+
"val/loss": round(fake_loss(epoch, EPOCHS) * 1.1, 4),
48+
}
49+
)
50+
trackio.finish()
51+
52+
space_id = trackio.sync(project=PROJECT)
53+
print(f"Dashboard: https://huggingface.co/spaces/{space_id}")

pyproject.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ target-version = "py310"
9191
[tool.ruff.lint]
9292
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)
9393
select = ["E", "F", "I"]
94+
extend-select = ["PLC0415"]
95+
preview = true
9496
# Ignore line length violations
9597
ignore = ["E501"]
9698

@@ -100,6 +102,17 @@ fixable = ["ALL"]
100102
# Allow unused variables when underscore-prefixed.
101103
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
102104

105+
[tool.ruff.lint.per-file-ignores]
106+
"autonomous-experiments/**/*.py" = ["PLC0415"]
107+
"tests/**/*.py" = ["PLC0415"]
108+
"trackio/apple_gpu.py" = ["PLC0415"]
109+
"trackio/cli.py" = ["PLC0415"]
110+
"trackio/gpu.py" = ["PLC0415"]
111+
"trackio/imports.py" = ["PLC0415"]
112+
"trackio/run.py" = ["PLC0415"]
113+
"trackio/sqlite_storage.py" = ["PLC0415"]
114+
"trackio/utils.py" = ["PLC0415"]
115+
103116
[tool.ruff.format]
104117
# Use single quotes for strings.
105118
quote-style = "double"

tests/ui/test_ui_display.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_runs_plots_images_are_displayed(temp_dir):
4141
locator = page.locator(".vega-embed")
4242
expect(locator).to_have_count(0)
4343

44+
checkbox.check()
4445
page.locator(".nav-link", has_text="Media").click()
4546
page.wait_for_load_state("networkidle")
4647
gallery = page.locator(".gallery")

0 commit comments

Comments
 (0)