Skip to main content

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.

PyPI Python Versions License


โœจ 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 a WebhookError with 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]"
  1. Fork โ†’ feature branch โ†’ PR.
  2. Keep PRs focused; match the existing code style.

๐Ÿ‘ค Author

Watin Promfiy


๐Ÿ“„ 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

discordflow-0.3.5.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

discordflow-0.3.5-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

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

Hashes for discordflow-0.3.5.tar.gz
Algorithm Hash digest
SHA256 95431fead6dd5993c1c15f4f8cddd9d61b960fdb9d5b1a9a50ff711bda370c9d
MD5 e901fc35608eb6f2e27c1caa52089211
BLAKE2b-256 e5efd5f36dabcf6933d05be0556d4df29d106333efac8e2c8ec113a63892564a

See more details on using hashes here.

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

Hashes for discordflow-0.3.5-py3-none-any.whl
Algorithm Hash digest
SHA256 3955b3aa85b0e5c3fe37596cac0de872a1f7c43795974113b2b3e01d48e8e026
MD5 2229ca32192795a12ab61596c2adf51f
BLAKE2b-256 7f25a0d9a1e627c164c76920fb32d6f376b46f1a6dac657dd868c6bb6915280e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page