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.2.tar.gz (20.1 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.2-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file discordflow-0.3.2.tar.gz.

File metadata

  • Download URL: discordflow-0.3.2.tar.gz
  • Upload date:
  • Size: 20.1 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.2.tar.gz
Algorithm Hash digest
SHA256 2ade872b933bbb368dda00df557faa1778bde2b37bc226bc058cc4f92681fbbe
MD5 cc85f6b826910b6c31762d7929539ae6
BLAKE2b-256 4aef409ad5e394204a868e9c47a741de7a41bdf138911b80b8226d5214a7e50e

See more details on using hashes here.

File details

Details for the file discordflow-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: discordflow-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 19.1 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a1ca5de4add99a5a270d6781a7d9274b5dc352238f5029f6b925ca2011465cb2
MD5 7b7798a68fe8bc5c7bf515a2b2028879
BLAKE2b-256 a6058c45d4b63a4409de68e1a53f4d87b03b3ecd451a1f5ef01aa7788ab2f916

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