trainmeter 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trainmeter-0.0.2 → trainmeter-0.0.3}/PKG-INFO +10 -3
- {trainmeter-0.0.2 → trainmeter-0.0.3}/README.md +9 -2
- {trainmeter-0.0.2 → trainmeter-0.0.3}/docs/architecture.md +22 -28
- trainmeter-0.0.3/docs/images/dashboard-dark.png +0 -0
- trainmeter-0.0.3/docs/images/dashboard-light.png +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/docs/spec.md +23 -13
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/__init__.py +1 -1
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/web/server.py +4 -1
- trainmeter-0.0.3/src/trainmeter/web/static/app.js +638 -0
- trainmeter-0.0.3/src/trainmeter/web/static/favicon.svg +1 -0
- trainmeter-0.0.3/src/trainmeter/web/static/index.html +37 -0
- trainmeter-0.0.3/src/trainmeter/web/static/style.css +287 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_dashboard.py +55 -52
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_web.py +4 -0
- trainmeter-0.0.2/src/trainmeter/web/static/app.js +0 -571
- trainmeter-0.0.2/src/trainmeter/web/static/index.html +0 -43
- trainmeter-0.0.2/src/trainmeter/web/static/style.css +0 -157
- {trainmeter-0.0.2 → trainmeter-0.0.3}/.github/workflows/ci.yml +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/.github/workflows/release.yml +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/.gitignore +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/CLAUDE.md +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/LICENSE +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/docs/hardware-checklist.md +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/examples/README.md +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/examples/plan_run.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/examples/train_tiny_gpt.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/pyproject.toml +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/__main__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/__init__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/bootstrap/sitecustomize.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/core.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/sender.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/taps.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/agent/wrap.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/catalog.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/cli.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/commands.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/config.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/doctor.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/emit.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/engine.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/export/__init__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/export/files.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/export/wandb.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/facts.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/flops.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/metrics.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/passport.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/peaks.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/records.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/replay.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/report.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/sources/__init__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/sources/gpu.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/sources/host.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/supervisor/__init__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/supervisor/ingest.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/supervisor/launcher.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/supervisor/live.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/supervisor/store.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/timeline.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/viewer.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/src/trainmeter/web/__init__.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_agent.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_cli.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_config.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_engine.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_examples.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_facts.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_flops.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_gpu.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_host.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_metrics.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_passport.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_peaks.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_probes_real.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_records.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_release.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_store.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_taps.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/tests/test_viewer.py +0 -0
- {trainmeter-0.0.2 → trainmeter-0.0.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: trainmeter
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Where your training FLOPs go and how well the GPUs are used: loss, FLOPs, MFU and hardware counters for a wrapped training run.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Almaz-KG/trainmeter
|
|
6
6
|
Project-URL: Repository, https://github.com/Almaz-KG/trainmeter
|
|
@@ -31,13 +31,20 @@ trainmeter starts your command, watches the node from outside, and serves a live
|
|
|
31
31
|
The dashboard shows what a training run needs to be judged: training and validation loss, FLOPs spent and required, MFU with its FLOPs convention spelled out, and how busy the GPUs really are, from `GPU-Util` down to MFU, next to arithmetic intensity.
|
|
32
32
|
Your training code stays as it is.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
<picture>
|
|
35
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/dashboard-dark.png">
|
|
36
|
+
<img alt="The trainmeter dashboard: progress, throughput, MFU and OFU, goodput and the loss curve of a run on four H100s" src="docs/images/dashboard-light.png">
|
|
37
|
+
</picture>
|
|
38
|
+
|
|
39
|
+
<sub>The dashboard on a synthetic run.</sub>
|
|
40
|
+
|
|
41
|
+
Status: early scaffold.
|
|
35
42
|
`tm` runs any command untouched, records the run, and turns what a PyTorch loop does into tokens, FLOPs spent and required, ETA, MFU (given a device) and the losses you `emit()`.
|
|
36
43
|
GPU counters (NVML and GPM: the utilization ladder, OFU, observed FLOPs, arithmetic intensity) and `tm doctor` are built and tested against fakes, not yet on a real GPU.
|
|
37
44
|
Series your loop already logs to wandb, TensorBoard or MLflow are picked up without a code change (`--no-tap` turns it off), and DataLoader waits, checkpoint saves and eval spans feed a "where the time goes" view.
|
|
38
45
|
One real step is also counted with `FlopCounterMode` (shown beside MFU as the `counted` convention), and layers and width are read from a model config when there is one.
|
|
39
46
|
Each run ends with a run passport (`passport.md` and `passport.json`) for a model card.
|
|
40
|
-
The dashboard
|
|
47
|
+
The dashboard, one page of progress, tiles and charts with the run details and model card folded away, is served on localhost while the run goes; it has been checked in a browser on synthetic data, not on a real run.
|
|
41
48
|
Not yet used on a real run.
|
|
42
49
|
To try it, `examples/` has a tiny GPT to run under `tm` on a laptop and a run planner that needs no GPU.
|
|
43
50
|
|
|
@@ -7,13 +7,20 @@ trainmeter starts your command, watches the node from outside, and serves a live
|
|
|
7
7
|
The dashboard shows what a training run needs to be judged: training and validation loss, FLOPs spent and required, MFU with its FLOPs convention spelled out, and how busy the GPUs really are, from `GPU-Util` down to MFU, next to arithmetic intensity.
|
|
8
8
|
Your training code stays as it is.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
<picture>
|
|
11
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/dashboard-dark.png">
|
|
12
|
+
<img alt="The trainmeter dashboard: progress, throughput, MFU and OFU, goodput and the loss curve of a run on four H100s" src="docs/images/dashboard-light.png">
|
|
13
|
+
</picture>
|
|
14
|
+
|
|
15
|
+
<sub>The dashboard on a synthetic run.</sub>
|
|
16
|
+
|
|
17
|
+
Status: early scaffold.
|
|
11
18
|
`tm` runs any command untouched, records the run, and turns what a PyTorch loop does into tokens, FLOPs spent and required, ETA, MFU (given a device) and the losses you `emit()`.
|
|
12
19
|
GPU counters (NVML and GPM: the utilization ladder, OFU, observed FLOPs, arithmetic intensity) and `tm doctor` are built and tested against fakes, not yet on a real GPU.
|
|
13
20
|
Series your loop already logs to wandb, TensorBoard or MLflow are picked up without a code change (`--no-tap` turns it off), and DataLoader waits, checkpoint saves and eval spans feed a "where the time goes" view.
|
|
14
21
|
One real step is also counted with `FlopCounterMode` (shown beside MFU as the `counted` convention), and layers and width are read from a model config when there is one.
|
|
15
22
|
Each run ends with a run passport (`passport.md` and `passport.json`) for a model card.
|
|
16
|
-
The dashboard
|
|
23
|
+
The dashboard, one page of progress, tiles and charts with the run details and model card folded away, is served on localhost while the run goes; it has been checked in a browser on synthetic data, not on a real run.
|
|
17
24
|
Not yet used on a real run.
|
|
18
25
|
To try it, `examples/` has a tiny GPT to run under `tm` on a laptop and a run planner that needs no GPU.
|
|
19
26
|
|
|
@@ -341,42 +341,36 @@ Overlays are computed from the primitives at view time and never stored.
|
|
|
341
341
|
|
|
342
342
|
The dashboard is one static page fed by the read-only API of section 10.
|
|
343
343
|
It renders from the catalog, so a new series appears without a page change.
|
|
344
|
-
The
|
|
344
|
+
The page, the rendering rules and the x-axis switch are specified in `docs/spec.md` (Screens).
|
|
345
345
|
Plotting loss against FLOPs makes runs comparable across batch sizes and sequence lengths.
|
|
346
|
-
|
|
346
|
+
The views of slices 4, 5 and 7 were later folded into this one page without tabs.
|
|
347
347
|
|
|
348
|
-
Layout
|
|
348
|
+
Layout sketch, with placeholders instead of numbers:
|
|
349
349
|
|
|
350
350
|
```text
|
|
351
351
|
+----------------------------------------------------------------------+
|
|
352
|
-
| <
|
|
353
|
-
|
|
354
|
-
| PROGRESS
|
|
355
|
-
|
|
356
|
-
| tokens
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
|
360
|
-
|
|
|
361
|
-
|
|
362
|
-
|
|
|
352
|
+
| trainmeter <n> x <GPU> [step|tokens|FLOPs|time] <state>|
|
|
353
|
+
+----------------------------------------------------------------------+
|
|
354
|
+
| PROGRESS <percent> tokens, FLOPs of required, ETA, elapsed |
|
|
355
|
+
+-----------+-----------+-----------+-----------+-----------+----------+
|
|
356
|
+
| tokens/s | step time | MFU | OFU | goodput | FLOPs/tok|
|
|
357
|
+
| sparkline | sparkline | sparkline | sparkline | sparkline | |
|
|
358
|
+
+-----------+-----------+-----------+-----------+-----------+----------+
|
|
359
|
+
| TRAINING: loss (full width), tokens/s, MFU and OFU, lr, grad norm, |
|
|
360
|
+
| every other logged series, plan against actual |
|
|
361
|
+
+----------------------------------------------------------------------+
|
|
362
|
+
| GPU EFFICIENCY: ladder now and over time, roofline, arithmetic |
|
|
363
|
+
| intensity, FLOPs spent (modeled next to observed) |
|
|
364
|
+
+----------------------------------------------------------------------+
|
|
365
|
+
| NODE: time split, host CPU and memory |
|
|
366
|
+
| GPUS: one small chart per counter, one line per GPU; ranks |
|
|
367
|
+
+----------------------------------------------------------------------+
|
|
368
|
+
| > Run details (collapsed) > Model card (collapsed) |
|
|
363
369
|
+----------------------------------------------------------------------+
|
|
364
370
|
```
|
|
365
371
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
| UTILIZATION LADDER over time | ROOFLINE |
|
|
369
|
-
| GPU-Util, SM active, | arithmetic intensity against |
|
|
370
|
-
| tensor active, OFU, MFU | achieved FLOP/s, with the ridge |
|
|
371
|
-
| | and the peak of the device |
|
|
372
|
-
+----------------------------------+-----------------------------------+
|
|
373
|
-
| FLOPs SPENT | ARITHMETIC INTENSITY over time |
|
|
374
|
-
| modeled next to observed | aggregate over the window |
|
|
375
|
-
+----------------------------------+-----------------------------------+
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
The optional price field is the one place where money exists.
|
|
379
|
-
It computes dollars in the browser from `elapsed`, `gpu_hours` and `tokens_seen`, keeps the price in that browser, and never sends it to the supervisor or writes it to the run log.
|
|
372
|
+
The dashboard shows no price and no dollars.
|
|
373
|
+
Money stays an overlay over `elapsed`, `gpu_hours` and `tokens_seen`, and a price never reaches the supervisor or the run log.
|
|
380
374
|
|
|
381
375
|
## 8. Run lifecycle
|
|
382
376
|
|
|
Binary file
|
|
Binary file
|
|
@@ -73,7 +73,7 @@ Rules:
|
|
|
73
73
|
|
|
74
74
|
1. Unknown stays unknown.
|
|
75
75
|
A metric that lacks an input is absent and names the input that would enable it, and a zero is never shown in its place.
|
|
76
|
-
2. An assumed fact
|
|
76
|
+
2. An assumed fact is marked `assumed` in the run details, and the dashboard raises a notice for it.
|
|
77
77
|
A declared value that disagrees with a detected one is used, and a warning shows both.
|
|
78
78
|
3. `n_gpus` is the number of GPUs the run uses, taken from the ranks.
|
|
79
79
|
Falling back to the visible devices is an assumed fact with a warning.
|
|
@@ -107,7 +107,7 @@ The engine maps the raw key the loop used to a canonical series:
|
|
|
107
107
|
| `lr` | `lr`, `learning_rate`, `train/lr`, `train/learning_rate` |
|
|
108
108
|
| `grad_norm` | `grad_norm`, `train/grad_norm`, `gradient_norm` |
|
|
109
109
|
|
|
110
|
-
A mapped series
|
|
110
|
+
A mapped series shows its original key, with `mapped by name` in its tooltip.
|
|
111
111
|
`tm.toml` can pin or override a mapping.
|
|
112
112
|
A number that matches no alias still appears as a custom series under its own key.
|
|
113
113
|
|
|
@@ -186,7 +186,7 @@ Across ranks: the spread of step time, for straggler detection.
|
|
|
186
186
|
Overlays are computed from the primitives at view time and never stored in the run log.
|
|
187
187
|
Dollars are GPU-hours times a price per GPU-hour, or elapsed hours times a price per node-hour.
|
|
188
188
|
GPU energy is the integral of the sampled power.
|
|
189
|
-
|
|
189
|
+
The dashboard shows no dollars, and a price is never sent to the supervisor.
|
|
190
190
|
|
|
191
191
|
## Run log
|
|
192
192
|
|
|
@@ -257,23 +257,33 @@ The run ends when the program does, with no exit status, because `tm` did not st
|
|
|
257
257
|
|
|
258
258
|
## Screens
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
The dashboard is one page with no tabs.
|
|
261
|
+
Its header holds only the icon, the rig (GPU count and model), the x-axis switch and the run state.
|
|
262
|
+
Below it, top to bottom:
|
|
263
|
+
|
|
264
|
+
| Part | Shows | Answers |
|
|
261
265
|
| --- | --- | --- |
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
|
|
|
267
|
-
|
|
|
266
|
+
| Progress | percent of the token budget, tokens and FLOPs spent of required, ETA, elapsed and GPU-hours | Is the run on plan? |
|
|
267
|
+
| Tiles | tokens/s, step time, MFU, OFU, goodput and FLOPs per token, each a number with a sparkline | How is it going right now? |
|
|
268
|
+
| Training | train and validation loss, tokens/s, MFU against OFU, lr, grad norm, every other logged series, and plan against actual | Is the model learning, and how wrong was my plan? |
|
|
269
|
+
| GPU efficiency | the utilization ladder now and over time, the roofline point, arithmetic intensity over time, FLOPs spent (modeled next to observed) | Are the GPUs used well, and where does the gap open? |
|
|
270
|
+
| Node | node time split (startup and compile, data, checkpoints, eval, compute), and the process tree's CPU (cores busy) and resident memory | What did the node spend time on without training? |
|
|
271
|
+
| GPUs | per GPU: tensor, DRAM and SM active, power, clocks, memory, temperature, NVLink, and step time per rank | Which card is slow? |
|
|
272
|
+
| Run details (collapsed) | the command, host, directory, declared flags, model, FLOPs per token under every convention, hardware and versions | What exactly ran? |
|
|
273
|
+
| Model card (collapsed) | the run passport as Markdown, with a copy button | What goes into the model card? |
|
|
274
|
+
|
|
275
|
+
A chart with no data is left out, and a part with nothing to draw is one line that names what would fill it.
|
|
268
276
|
|
|
269
277
|
Rendering rules:
|
|
270
278
|
|
|
271
|
-
1. Every number shows its convention
|
|
272
|
-
|
|
279
|
+
1. Every MFU and FLOPs number shows its convention next to it in plain text, and a series mapped by name shows its raw key.
|
|
280
|
+
Provenance (`declared`, `assumed`, `measured`) is listed next to each fact in the run details, and a guessed input also raises a notice at the top of the page.
|
|
281
|
+
A tile is a label, a number, one short line and a sparkline, with no badges; any longer explanation is in its tooltip.
|
|
282
|
+
2. A metric that lacks an input shows "n/a", names the input that would enable it in its tooltip, such as "budget not declared: pass --budget-tokens", and never shows a zero.
|
|
273
283
|
3. Arithmetic intensity is labeled as an aggregate over the sampling window.
|
|
274
284
|
4. Every time series has an x-axis switch: step, tokens, FLOPs spent (modeled, with its convention shown) or wall time.
|
|
275
285
|
Until a step has been seen, and for a program watched from outside, the charts use wall time, because every point would otherwise sit at zero.
|
|
276
|
-
5. The
|
|
286
|
+
5. The dashboard shows no price and no dollars.
|
|
277
287
|
6. The dashboard is static files with hand-written SVG charts, no build step and no CDN.
|
|
278
288
|
|
|
279
289
|
## MVP
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""The dashboard server: standard library only, read only, loopback only.
|
|
2
2
|
|
|
3
|
-
`GET
|
|
3
|
+
`GET /`, its static files and the icon, and a small JSON API over `LiveState`. There is no endpoint
|
|
4
4
|
that changes anything, other methods get 405, and a Host header that is not the loopback name and
|
|
5
5
|
this port is refused, which closes DNS rebinding. Binding beyond loopback is not offered.
|
|
6
6
|
"""
|
|
@@ -25,6 +25,9 @@ FILES = {
|
|
|
25
25
|
"/": ("index.html", "text/html; charset=utf-8"),
|
|
26
26
|
"/app.js": ("app.js", "text/javascript; charset=utf-8"),
|
|
27
27
|
"/style.css": ("style.css", "text/css; charset=utf-8"),
|
|
28
|
+
"/favicon.svg": ("favicon.svg", "image/svg+xml"),
|
|
29
|
+
# Asked for by clients that ignore the <link>; every current browser renders an SVG here.
|
|
30
|
+
"/favicon.ico": ("favicon.svg", "image/svg+xml"),
|
|
28
31
|
}
|
|
29
32
|
DEFAULT_PORT = 8765
|
|
30
33
|
STREAM_INTERVAL = 1.0
|