discordflow 0.2.0__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.
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.4
2
+ Name: discordflow
3
+ Version: 0.2.0
4
+ Summary: A lightweight MLflow clone that logs ML training metrics, parameters, and artifacts directly to Discord webhooks.
5
+ Author-email: DiscordFlow Contributors <your.email@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/discordflow
8
+ Project-URL: Bug Tracker, https://github.com/yourusername/discordflow/issues
9
+ Project-URL: Documentation, https://github.com/yourusername/discordflow#readme
10
+ Project-URL: Changelog, https://github.com/yourusername/discordflow/releases
11
+ Keywords: mlflow,discord,machine-learning,experiment-tracking,webhooks,deep-learning
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: System :: Logging
22
+ Classifier: Intended Audience :: Science/Research
23
+ Classifier: Intended Audience :: Developers
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ Requires-Dist: requests>=2.25.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: twine; extra == "dev"
30
+ Requires-Dist: pytest; extra == "dev"
31
+ Requires-Dist: pytest-mock; extra == "dev"
32
+
33
+ # DiscordFlow 🚀
34
+
35
+ > **The MLflow you already have open on your phone.**
36
+ > Log ML training metrics, parameters, and artifacts directly to a Discord channel via webhooks — no server required.
37
+
38
+ ![PyPI](https://img.shields.io/pypi/v/discordflow)
39
+ ![Python Versions](https://img.shields.io/pypi/pyversions/discordflow)
40
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
41
+
42
+ ---
43
+
44
+ ## ✨ Features
45
+
46
+ | Feature | Description |
47
+ |---|---|
48
+ | 📈 **Metric Logging** | Post metrics per step/epoch with `log_metrics()` |
49
+ | ⚙️ **Param Logging** | Log hyperparameters with `log_param()` / `log_params()` |
50
+ | 🏷️ **Tags** | Attach arbitrary key-value tags to runs |
51
+ | 📁 **Artifact Upload** | Upload files (models, plots, CSVs) up to 25 MB |
52
+ | 📄 **Text Artifacts** | Upload text snippets as `.txt` file attachments |
53
+ | ▶️ **Run Management** | Context-manager `start_run()` with auto summary embed on exit |
54
+ | 🖥️ **Dry-Run Mode** | `dry_run=True` prints to stdout — no real webhook calls |
55
+ | ❌ **Error Capture** | Exceptions inside a `start_run()` block are caught and posted |
56
+
57
+ ---
58
+
59
+ ## 📦 Installation
60
+
61
+ ```bash
62
+ pip install discordflow
63
+ ```
64
+
65
+ **Requirements:** Python ≥ 3.8, `requests`
66
+
67
+ ---
68
+
69
+ ## ⚡ Quickstart
70
+
71
+ ### 1. Get a Discord Webhook URL
72
+
73
+ In your Discord server: **Server Settings → Integrations → Webhooks → New Webhook → Copy URL**
74
+
75
+ ### 2. Drop it into your training loop
76
+
77
+ ```python
78
+ from discordflow import DiscordFlow
79
+
80
+ WEBHOOK_URL = "YOUR_DISCORD_WEBHOOK_URL"
81
+ dflow = DiscordFlow(WEBHOOK_URL, experiment_name="MoE_Router_Training")
82
+
83
+ # Log hyperparameters
84
+ dflow.log_params({
85
+ "experts": 8,
86
+ "routing_strategy": "top-k",
87
+ "learning_rate": 3e-4,
88
+ })
89
+
90
+ # Training loop
91
+ for epoch in range(1, 6):
92
+ loss = 1.0 / epoch
93
+ dflow.log_metrics({
94
+ "Train Loss": round(loss, 4),
95
+ "Load Balance": round(0.8 + 0.02 * epoch, 4),
96
+ }, step=epoch)
97
+
98
+ # Upload an artifact (max 25 MB)
99
+ # dflow.log_artifact("router_weights.pt")
100
+ # dflow.log_artifact("loss_curve.png")
101
+ ```
102
+
103
+ ### 3. Context-manager pattern (recommended)
104
+
105
+ Use `start_run()` to get an automatic run-summary embed when the block exits — including elapsed time, all params, and final metrics. If your code crashes, the traceback is posted too.
106
+
107
+ ```python
108
+ with dflow.start_run("lora_rank_16") as run:
109
+ run.log_params({"lr": 2e-4, "lora_rank": 16, "epochs": 3})
110
+ run.set_tag("framework", "HuggingFace")
111
+
112
+ for epoch in range(1, 4):
113
+ run.log_metrics({
114
+ "Train Loss": round(2.5 / epoch, 4),
115
+ "Val Loss": round(2.7 / epoch, 4),
116
+ }, step=epoch)
117
+
118
+ # ✅ Run Complete embed is auto-posted here
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 🧪 Local Testing (Dry Run)
124
+
125
+ No Discord server? No problem. Use `dry_run=True` to print all messages to stdout instead of calling the webhook:
126
+
127
+ ```python
128
+ dflow = DiscordFlow("ANY_URL", experiment_name="test", dry_run=True)
129
+ dflow.log_metrics({"loss": 0.42}, step=1)
130
+ ```
131
+
132
+ Run the bundled demo:
133
+
134
+ ```bash
135
+ python example.py
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 📚 API Reference
141
+
142
+ ### `DiscordFlow(webhook_url, experiment_name, dry_run, username, avatar_url)`
143
+
144
+ | Parameter | Type | Default | Description |
145
+ |---|---|---|---|
146
+ | `webhook_url` | `str` | required | Discord webhook URL |
147
+ | `experiment_name` | `str` | `"Default Experiment"` | Shown in every embed |
148
+ | `dry_run` | `bool` | `False` | Print to stdout instead of calling webhook |
149
+ | `username` | `str` | `"DiscordFlow 🤖"` | Bot username shown in Discord |
150
+ | `avatar_url` | `str` | `None` | Custom bot avatar URL |
151
+
152
+ ---
153
+
154
+ ### Logging Methods
155
+
156
+ ```python
157
+ # Single param
158
+ dflow.log_param("learning_rate", 3e-4)
159
+
160
+ # Multiple params in one embed
161
+ dflow.log_params({"lr": 3e-4, "batch_size": 128, "epochs": 10})
162
+
163
+ # Single metric
164
+ dflow.log_metric("loss", 0.42, step=5)
165
+
166
+ # Multiple metrics in one embed
167
+ dflow.log_metrics({"loss": 0.42, "acc": 0.91}, step=5)
168
+
169
+ # Arbitrary tags (purple embed)
170
+ dflow.set_tag("author", "e27")
171
+ dflow.set_tag("dataset", "openwebtext")
172
+
173
+ # Upload a file artifact (max 25 MB)
174
+ dflow.log_artifact("checkpoint.pt")
175
+ dflow.log_artifact("confusion_matrix.png")
176
+
177
+ # Upload a text snippet as a file
178
+ dflow.log_text("epoch,loss\n1,1.0\n2,0.5", filename="metrics.csv")
179
+ ```
180
+
181
+ ---
182
+
183
+ ### Run Management
184
+
185
+ ```python
186
+ # Start a named run (context manager — recommended)
187
+ with dflow.start_run("sweep_01") as run:
188
+ run.log_params({...})
189
+ run.log_metrics({...}, step=epoch)
190
+ run.set_tag("status", "grid_search")
191
+ run.log_artifact("model.pt")
192
+ # ← Auto-posts run summary embed on exit
193
+
194
+ # Or explicitly end a run
195
+ run = dflow.start_run("manual_run")
196
+ # ... do stuff ...
197
+ dflow.end_run(status="FINISHED")
198
+ ```
199
+
200
+ ---
201
+
202
+ ## 📤 Publishing to PyPI
203
+
204
+ ```bash
205
+ cd discordflow-project
206
+
207
+ # Install build tools
208
+ pip install build twine
209
+
210
+ # Build the package
211
+ python -m build
212
+
213
+ # Upload to PyPI (first time: create account at pypi.org)
214
+ twine upload dist/*
215
+ ```
216
+
217
+ ---
218
+
219
+ ## 🔄 Bump Version & Re-Publish (bash script)
220
+
221
+ Save this as `publish.sh` in your project root:
222
+
223
+ ```bash
224
+ #!/usr/bin/env bash
225
+ set -e
226
+
227
+ NEW_VERSION=$1
228
+ if [ -z "$NEW_VERSION" ]; then
229
+ echo "Usage: ./publish.sh <new_version> e.g. ./publish.sh 0.3.0"
230
+ exit 1
231
+ fi
232
+
233
+ # Update version in pyproject.toml
234
+ sed -i '' "s/^version = .*/version = \"$NEW_VERSION\"/" pyproject.toml
235
+
236
+ # Update version in __init__.py
237
+ sed -i '' "s/^__version__ = .*/__version__ = \"$NEW_VERSION\"/" discordflow/__init__.py
238
+
239
+ echo "✅ Bumped to v$NEW_VERSION"
240
+
241
+ # Clean old builds
242
+ rm -rf dist/ build/ *.egg-info
243
+
244
+ # Build + upload
245
+ python -m build
246
+ twine upload dist/*
247
+
248
+ echo "🚀 Published discordflow v$NEW_VERSION to PyPI"
249
+ ```
250
+
251
+ ```bash
252
+ chmod +x publish.sh
253
+ ./publish.sh 0.3.0
254
+ ```
255
+
256
+ ---
257
+
258
+ ## 🤝 Contributing
259
+
260
+ 1. Fork the repo
261
+ 2. Create your feature branch: `git checkout -b feat/my-feature`
262
+ 3. Commit your changes: `git commit -m "feat: add my feature"`
263
+ 4. Push: `git push origin feat/my-feature`
264
+ 5. Open a Pull Request
265
+
266
+ ---
267
+
268
+ ## 📄 License
269
+
270
+ MIT © DiscordFlow Contributors
@@ -0,0 +1,238 @@
1
+ # DiscordFlow 🚀
2
+
3
+ > **The MLflow you already have open on your phone.**
4
+ > Log ML training metrics, parameters, and artifacts directly to a Discord channel via webhooks — no server required.
5
+
6
+ ![PyPI](https://img.shields.io/pypi/v/discordflow)
7
+ ![Python Versions](https://img.shields.io/pypi/pyversions/discordflow)
8
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
9
+
10
+ ---
11
+
12
+ ## ✨ Features
13
+
14
+ | Feature | Description |
15
+ |---|---|
16
+ | 📈 **Metric Logging** | Post metrics per step/epoch with `log_metrics()` |
17
+ | ⚙️ **Param Logging** | Log hyperparameters with `log_param()` / `log_params()` |
18
+ | 🏷️ **Tags** | Attach arbitrary key-value tags to runs |
19
+ | 📁 **Artifact Upload** | Upload files (models, plots, CSVs) up to 25 MB |
20
+ | 📄 **Text Artifacts** | Upload text snippets as `.txt` file attachments |
21
+ | ▶️ **Run Management** | Context-manager `start_run()` with auto summary embed on exit |
22
+ | 🖥️ **Dry-Run Mode** | `dry_run=True` prints to stdout — no real webhook calls |
23
+ | ❌ **Error Capture** | Exceptions inside a `start_run()` block are caught and posted |
24
+
25
+ ---
26
+
27
+ ## 📦 Installation
28
+
29
+ ```bash
30
+ pip install discordflow
31
+ ```
32
+
33
+ **Requirements:** Python ≥ 3.8, `requests`
34
+
35
+ ---
36
+
37
+ ## ⚡ Quickstart
38
+
39
+ ### 1. Get a Discord Webhook URL
40
+
41
+ In your Discord server: **Server Settings → Integrations → Webhooks → New Webhook → Copy URL**
42
+
43
+ ### 2. Drop it into your training loop
44
+
45
+ ```python
46
+ from discordflow import DiscordFlow
47
+
48
+ WEBHOOK_URL = "YOUR_DISCORD_WEBHOOK_URL"
49
+ dflow = DiscordFlow(WEBHOOK_URL, experiment_name="MoE_Router_Training")
50
+
51
+ # Log hyperparameters
52
+ dflow.log_params({
53
+ "experts": 8,
54
+ "routing_strategy": "top-k",
55
+ "learning_rate": 3e-4,
56
+ })
57
+
58
+ # Training loop
59
+ for epoch in range(1, 6):
60
+ loss = 1.0 / epoch
61
+ dflow.log_metrics({
62
+ "Train Loss": round(loss, 4),
63
+ "Load Balance": round(0.8 + 0.02 * epoch, 4),
64
+ }, step=epoch)
65
+
66
+ # Upload an artifact (max 25 MB)
67
+ # dflow.log_artifact("router_weights.pt")
68
+ # dflow.log_artifact("loss_curve.png")
69
+ ```
70
+
71
+ ### 3. Context-manager pattern (recommended)
72
+
73
+ Use `start_run()` to get an automatic run-summary embed when the block exits — including elapsed time, all params, and final metrics. If your code crashes, the traceback is posted too.
74
+
75
+ ```python
76
+ with dflow.start_run("lora_rank_16") as run:
77
+ run.log_params({"lr": 2e-4, "lora_rank": 16, "epochs": 3})
78
+ run.set_tag("framework", "HuggingFace")
79
+
80
+ for epoch in range(1, 4):
81
+ run.log_metrics({
82
+ "Train Loss": round(2.5 / epoch, 4),
83
+ "Val Loss": round(2.7 / epoch, 4),
84
+ }, step=epoch)
85
+
86
+ # ✅ Run Complete embed is auto-posted here
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 🧪 Local Testing (Dry Run)
92
+
93
+ No Discord server? No problem. Use `dry_run=True` to print all messages to stdout instead of calling the webhook:
94
+
95
+ ```python
96
+ dflow = DiscordFlow("ANY_URL", experiment_name="test", dry_run=True)
97
+ dflow.log_metrics({"loss": 0.42}, step=1)
98
+ ```
99
+
100
+ Run the bundled demo:
101
+
102
+ ```bash
103
+ python example.py
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 📚 API Reference
109
+
110
+ ### `DiscordFlow(webhook_url, experiment_name, dry_run, username, avatar_url)`
111
+
112
+ | Parameter | Type | Default | Description |
113
+ |---|---|---|---|
114
+ | `webhook_url` | `str` | required | Discord webhook URL |
115
+ | `experiment_name` | `str` | `"Default Experiment"` | Shown in every embed |
116
+ | `dry_run` | `bool` | `False` | Print to stdout instead of calling webhook |
117
+ | `username` | `str` | `"DiscordFlow 🤖"` | Bot username shown in Discord |
118
+ | `avatar_url` | `str` | `None` | Custom bot avatar URL |
119
+
120
+ ---
121
+
122
+ ### Logging Methods
123
+
124
+ ```python
125
+ # Single param
126
+ dflow.log_param("learning_rate", 3e-4)
127
+
128
+ # Multiple params in one embed
129
+ dflow.log_params({"lr": 3e-4, "batch_size": 128, "epochs": 10})
130
+
131
+ # Single metric
132
+ dflow.log_metric("loss", 0.42, step=5)
133
+
134
+ # Multiple metrics in one embed
135
+ dflow.log_metrics({"loss": 0.42, "acc": 0.91}, step=5)
136
+
137
+ # Arbitrary tags (purple embed)
138
+ dflow.set_tag("author", "e27")
139
+ dflow.set_tag("dataset", "openwebtext")
140
+
141
+ # Upload a file artifact (max 25 MB)
142
+ dflow.log_artifact("checkpoint.pt")
143
+ dflow.log_artifact("confusion_matrix.png")
144
+
145
+ # Upload a text snippet as a file
146
+ dflow.log_text("epoch,loss\n1,1.0\n2,0.5", filename="metrics.csv")
147
+ ```
148
+
149
+ ---
150
+
151
+ ### Run Management
152
+
153
+ ```python
154
+ # Start a named run (context manager — recommended)
155
+ with dflow.start_run("sweep_01") as run:
156
+ run.log_params({...})
157
+ run.log_metrics({...}, step=epoch)
158
+ run.set_tag("status", "grid_search")
159
+ run.log_artifact("model.pt")
160
+ # ← Auto-posts run summary embed on exit
161
+
162
+ # Or explicitly end a run
163
+ run = dflow.start_run("manual_run")
164
+ # ... do stuff ...
165
+ dflow.end_run(status="FINISHED")
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 📤 Publishing to PyPI
171
+
172
+ ```bash
173
+ cd discordflow-project
174
+
175
+ # Install build tools
176
+ pip install build twine
177
+
178
+ # Build the package
179
+ python -m build
180
+
181
+ # Upload to PyPI (first time: create account at pypi.org)
182
+ twine upload dist/*
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 🔄 Bump Version & Re-Publish (bash script)
188
+
189
+ Save this as `publish.sh` in your project root:
190
+
191
+ ```bash
192
+ #!/usr/bin/env bash
193
+ set -e
194
+
195
+ NEW_VERSION=$1
196
+ if [ -z "$NEW_VERSION" ]; then
197
+ echo "Usage: ./publish.sh <new_version> e.g. ./publish.sh 0.3.0"
198
+ exit 1
199
+ fi
200
+
201
+ # Update version in pyproject.toml
202
+ sed -i '' "s/^version = .*/version = \"$NEW_VERSION\"/" pyproject.toml
203
+
204
+ # Update version in __init__.py
205
+ sed -i '' "s/^__version__ = .*/__version__ = \"$NEW_VERSION\"/" discordflow/__init__.py
206
+
207
+ echo "✅ Bumped to v$NEW_VERSION"
208
+
209
+ # Clean old builds
210
+ rm -rf dist/ build/ *.egg-info
211
+
212
+ # Build + upload
213
+ python -m build
214
+ twine upload dist/*
215
+
216
+ echo "🚀 Published discordflow v$NEW_VERSION to PyPI"
217
+ ```
218
+
219
+ ```bash
220
+ chmod +x publish.sh
221
+ ./publish.sh 0.3.0
222
+ ```
223
+
224
+ ---
225
+
226
+ ## 🤝 Contributing
227
+
228
+ 1. Fork the repo
229
+ 2. Create your feature branch: `git checkout -b feat/my-feature`
230
+ 3. Commit your changes: `git commit -m "feat: add my feature"`
231
+ 4. Push: `git push origin feat/my-feature`
232
+ 5. Open a Pull Request
233
+
234
+ ---
235
+
236
+ ## 📄 License
237
+
238
+ MIT © DiscordFlow Contributors
@@ -0,0 +1,39 @@
1
+ """
2
+ DiscordFlow — Lightweight ML experiment tracker for Discord webhooks.
3
+
4
+ Quick start::
5
+
6
+ from discordflow import DiscordFlow
7
+
8
+ dflow = DiscordFlow("YOUR_WEBHOOK_URL", experiment_name="my_experiment")
9
+ dflow.log_param("lr", 3e-4)
10
+ dflow.log_metrics({"loss": 0.42, "acc": 0.91}, step=1)
11
+
12
+ Context-manager pattern::
13
+
14
+ with dflow.start_run("sweep_01") as run:
15
+ run.log_params({"lr": 3e-4, "batch": 32})
16
+ run.log_metrics({"loss": 0.3}, step=5)
17
+ """
18
+
19
+ from .core import DiscordFlow
20
+ from .run import ActiveRun
21
+ from .exceptions import (
22
+ DiscordFlowError,
23
+ WebhookError,
24
+ ArtifactTooLargeError,
25
+ RunNotActiveError,
26
+ )
27
+
28
+ __version__ = "0.2.0"
29
+ __author__ = "DiscordFlow Contributors"
30
+ __license__ = "MIT"
31
+
32
+ __all__ = [
33
+ "DiscordFlow",
34
+ "ActiveRun",
35
+ "DiscordFlowError",
36
+ "WebhookError",
37
+ "ArtifactTooLargeError",
38
+ "RunNotActiveError",
39
+ ]