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.
- discordflow-0.2.0/PKG-INFO +270 -0
- discordflow-0.2.0/README.md +238 -0
- discordflow-0.2.0/discordflow/__init__.py +39 -0
- discordflow-0.2.0/discordflow/core.py +415 -0
- discordflow-0.2.0/discordflow/exceptions.py +33 -0
- discordflow-0.2.0/discordflow/run.py +120 -0
- discordflow-0.2.0/discordflow/utils.py +77 -0
- discordflow-0.2.0/discordflow.egg-info/PKG-INFO +270 -0
- discordflow-0.2.0/discordflow.egg-info/SOURCES.txt +12 -0
- discordflow-0.2.0/discordflow.egg-info/dependency_links.txt +1 -0
- discordflow-0.2.0/discordflow.egg-info/requires.txt +7 -0
- discordflow-0.2.0/discordflow.egg-info/top_level.txt +1 -0
- discordflow-0.2.0/pyproject.toml +41 -0
- discordflow-0.2.0/setup.cfg +4 -0
|
@@ -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
|
+

|
|
39
|
+

|
|
40
|
+

|
|
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
|
+

|
|
7
|
+

|
|
8
|
+

|
|
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
|
+
]
|