A lightweight MLflow clone that logs ML training metrics, parameters, and artifacts directly to Discord webhooks โ with Forum thread support.
Project description
DiscordFlow ๐
The MLflow you already have open on your phone. Log ML training metrics, parameters, and artifacts directly to Discord โ no server required.
โจ What's in the Box
| Feature | Details |
|---|---|
| ๐ Metrics | log_metric() / log_metrics() per step/epoch |
| โ๏ธ Params | log_param() / log_params() โ logged as blue embeds |
| ๐ท๏ธ Tags | set_tag() โ arbitrary key-value labels on a run |
| ๐ Artifact upload | Any file up to 25 MB via log_artifact() |
| ๐ Text artifact | log_text() โ upload a string as a .txt / .csv attachment |
| ๐ Figure upload | log_figure(fig) โ send matplotlib figures as PNG into Discord |
| ๐ฌ Normal channel | start_run() โ embeds posted directly in the channel |
| ๐ Forum channel | start_forum_run() โ each run gets its own dedicated Discord thread |
| ๐ Auto-resume | Threads are resumed automatically after Colab restarts |
| ๐พ State persistence | save() stores {run_name: thread_id} to JSON |
| ๐ฅ๏ธ System metrics | Per-call, configurable: CPU, RAM, GPU, Disk, Network |
| โก Async logging | ThreadPoolExecutor โ Discord calls never block training |
| โ Error capture | Exceptions inside with blocks are posted to Discord |
| ๐ฅ๏ธ Dry-run mode | dry_run=True prints to stdout โ no real webhook calls |
๐ฆ Installation
# Core (no system metrics)
pip install discordflow
# + CPU, RAM, Disk, Network metrics
pip install "discordflow[system]"
# + NVIDIA GPU metrics
pip install "discordflow[system,gpu]"
Requires Python โฅ 3.8.
โก Quickstart
Get a webhook URL from Discord Server Settings โ Integrations โ Webhooks โ New Webhook.
Normal Channel โ start_run()
Use when your webhook points to a regular text / announcement channel.
from discordflow import DiscordFlow
dflow = DiscordFlow(WEBHOOK_URL, experiment_name="ResNet_Training")
with dflow.start_run("baseline") as run:
run.log_params({"lr": 3e-4, "batch_size": 128, "epochs": 10})
run.set_tag("dataset", "ImageNet")
for epoch in range(1, 11):
loss = train_one_epoch(model, ...)
run.log_metrics(
{"Train Loss": loss, "Val Acc": eval(model, ...)},
step=epoch,
system_metrics=["cpu", "ram"], # โ hardware stats appended to each embed
)
run.log_artifact("best_model.pt")
run.log_figure(fig, title="Loss Curve")
dflow.finish() # flush async queue before exit
Forum Channel โ start_forum_run()
Use when your webhook points to a Forum channel.
Each run automatically gets its own Discord thread โ perfect for comparing many experiments.
dflow = DiscordFlow(FORUM_WEBHOOK_URL, experiment_name="LLM_FineTune")
with dflow.start_forum_run("lora_r16", description="LoRA rank=16 sweep") as run:
run.log_params({"lora_rank": 16, "lr": 2e-4, "epochs": 3})
for epoch in range(1, 4):
run.log_metrics(
{"Train Loss": ..., "Val Loss": ...},
step=epoch,
system_metrics=["cpu", "ram", "gpu"], # โ GPU stats for Colab/NVIDIA
)
run.log_figure(fig, title=f"Epoch {epoch} curve")
# โ
Summary + elapsed + final metrics auto-posted to the thread
dflow.save() # โ persist thread IDs so you can resume after a restart
dflow.finish()
โ ๏ธ Wrong channel type? If you call
start_run()on a Forum webhook (or vice versa), DiscordFlow will raise aWebhookErrorwith a message telling you exactly which method to switch to.
๐ฅ๏ธ Configurable System Metrics
Pass any combination to system_metrics= on log_metrics():
| Key | Logged value | Requires |
|---|---|---|
"cpu" |
Usage % + clock speed | discordflow[system] |
"ram" |
Usage % + GB used / total | discordflow[system] |
"gpu" |
Util % + VRAM used / total, per GPU | discordflow[system,gpu] |
"disk" |
Usage % + GB used / total | discordflow[system] |
"network" |
Total MB โ sent / โ received | discordflow[system] |
# Minimal โ CPU + RAM only
run.log_metrics({"loss": 0.4}, step=1, system_metrics=["cpu", "ram"])
# Everything
run.log_metrics({"loss": 0.4}, step=1,
system_metrics=["cpu", "ram", "gpu", "disk", "network"])
๐พ Colab Restart Recovery
When your Colab runtime reconnects, thread IDs are restored automatically from the saved state file:
# Fresh runtime โ state_file is loaded automatically on __init__
dflow = DiscordFlow(FORUM_WEBHOOK_URL, "LLM_FineTune",
state_file=".discordflow_state.json") # default path
# start_forum_run now resumes the existing thread instead of creating a new one
with dflow.start_forum_run("lora_r16") as run:
...
Manual override (if you know the thread ID):
dflow.resume_run("lora_r16", thread_id="1234567890123456789")
dflow.save()
ZIP backup (download to PC, re-upload on next Colab session):
from discordflow.colab_utils import export_session, import_session
export_session(dflow) # โ downloads discordflow_backup.zip to your machine
# --- On a fresh Colab runtime ---
import_session(dflow) # โ upload the zip to restore all thread IDs
๐จ Custom Bot Identity
dflow = DiscordFlow(
webhook_url = WEBHOOK_URL,
experiment_name = "ResNet_Training",
username = "TrainBot ๐๏ธ",
avatar_url = "https://i.imgur.com/AfFp7pu.png",
)
๐ Full API Reference
DiscordFlow(webhook_url, experiment_name, ...)
| Parameter | Default | Description |
|---|---|---|
webhook_url |
required | Discord webhook URL |
experiment_name |
"Default Experiment" |
Shown in every embed footer |
state_file |
".discordflow_state.json" |
JSON path for thread ID persistence (None to disable) |
async_logging |
True |
Non-blocking background thread for webhook calls |
dry_run |
False |
Print embeds to stdout, no actual HTTP requests |
username |
"DiscordFlow ๐ค" |
Bot display name in Discord |
avatar_url |
None |
Bot profile picture URL |
Channel Methods
| Method | Channel | Returns | Description |
|---|---|---|---|
start_run(run_name) |
Normal | ActiveRun |
Start a run, post embeds to channel |
start_forum_run(run_name, description) |
Forum | ForumActiveRun |
Create/resume a forum thread for this run |
resume_run(run_name, thread_id) |
Forum | โ | Manually re-link a run name to an existing thread |
save(filepath=None) |
Both | โ | Persist {run_name: thread_id} to JSON |
finish() |
Both | โ | Flush async queue and shut down executor |
Logging Methods (on ActiveRun & ForumActiveRun)
run.log_param("lr", 3e-4)
run.log_params({"lr": 3e-4, "batch": 128, "optimizer": "AdamW"})
run.log_metric("loss", 0.42, step=5)
run.log_metrics({"loss": 0.42, "acc": 0.91}, step=5,
system_metrics=["cpu", "ram"])
run.set_tag("author", "e27")
run.log_artifact("checkpoint.pt")
run.log_text("epoch,loss\n1,1.0\n2,0.5", filename="loss.csv")
run.log_figure(fig, title="Loss Curve")
Exceptions
| Exception | When raised |
|---|---|
WebhookError |
Discord HTTP error (wrong channel type, network issue) |
ArtifactTooLargeError |
File exceeds 25 MB Discord limit |
RunNotActiveError |
Operation attempted with no active run |
๐งช Local Testing (Dry Run)
dflow = DiscordFlow("ANY_URL", "test", dry_run=True)
with dflow.start_run("test_run") as run:
run.log_metrics({"loss": 0.42, "acc": 0.91}, step=1)
Output is printed to stdout โ no real HTTP calls, no webhook URL needed.
๐๏ธ Project Layout
discordflow/
โโโ core.py # DiscordFlow main class
โโโ run.py # ActiveRun + ForumActiveRun context managers
โโโ utils.py # Colour palette, formatters, collect_system_metrics()
โโโ exceptions.py # Custom exception hierarchy
โโโ colab_utils.py # export_session / import_session for Colab
โโโ __init__.py # Public API surface
colab_demo.py # Full runnable Colab demo (switchable normal/forum mode)
example.py # Minimal dry-run example
๐ค Contributing
git clone https://github.com/E27-25/discordflow.git
cd discordflow-project
pip install -e ".[dev]"
- Fork โ feature branch โ PR.
- Keep PRs focused; match the existing code style.
๐ค Author
Watin Promfiy
- GitHub: @E27-25
- Project: github.com/E27-25/discordflow
๐ License
MIT ยฉ Watin Promfiy
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file discordflow-0.3.5.tar.gz.
File metadata
- Download URL: discordflow-0.3.5.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95431fead6dd5993c1c15f4f8cddd9d61b960fdb9d5b1a9a50ff711bda370c9d
|
|
| MD5 |
e901fc35608eb6f2e27c1caa52089211
|
|
| BLAKE2b-256 |
e5efd5f36dabcf6933d05be0556d4df29d106333efac8e2c8ec113a63892564a
|
File details
Details for the file discordflow-0.3.5-py3-none-any.whl.
File metadata
- Download URL: discordflow-0.3.5-py3-none-any.whl
- Upload date:
- Size: 19.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3955b3aa85b0e5c3fe37596cac0de872a1f7c43795974113b2b3e01d48e8e026
|
|
| MD5 |
2229ca32192795a12ab61596c2adf51f
|
|
| BLAKE2b-256 |
7f25a0d9a1e627c164c76920fb32d6f376b46f1a6dac657dd868c6bb6915280e
|