moodswapper 26.9.21__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.
- moodswapper-26.9.21/.gitignore +29 -0
- moodswapper-26.9.21/CHANGELOG.md +47 -0
- moodswapper-26.9.21/LICENSE +21 -0
- moodswapper-26.9.21/PKG-INFO +148 -0
- moodswapper-26.9.21/README.md +114 -0
- moodswapper-26.9.21/pyproject.toml +60 -0
- moodswapper-26.9.21/src/moodswapper/__init__.py +22 -0
- moodswapper-26.9.21/src/moodswapper/__main__.py +5 -0
- moodswapper-26.9.21/src/moodswapper/cli.py +302 -0
- moodswapper-26.9.21/src/moodswapper/data/bored.jsonl +503 -0
- moodswapper-26.9.21/src/moodswapper/data/childish.jsonl +536 -0
- moodswapper-26.9.21/src/moodswapper/data/depressed.jsonl +532 -0
- moodswapper-26.9.21/src/moodswapper/data/drunk.jsonl +422 -0
- moodswapper-26.9.21/src/moodswapper/data/exhausted.jsonl +536 -0
- moodswapper-26.9.21/src/moodswapper/data/happy.jsonl +536 -0
- moodswapper-26.9.21/src/moodswapper/data/nostalgic.jsonl +521 -0
- moodswapper-26.9.21/src/moodswapper/data/prompts.json +3884 -0
- moodswapper-26.9.21/src/moodswapper/data/scared.jsonl +297 -0
- moodswapper-26.9.21/src/moodswapper/data/zen.jsonl +536 -0
- moodswapper-26.9.21/src/moodswapper/generate.py +366 -0
- moodswapper-26.9.21/src/moodswapper/llm.py +209 -0
- moodswapper-26.9.21/src/moodswapper/moods.py +110 -0
- moodswapper-26.9.21/src/moodswapper/progress.py +61 -0
- moodswapper-26.9.21/src/moodswapper/report.py +304 -0
- moodswapper-26.9.21/src/moodswapper/screens.py +268 -0
- moodswapper-26.9.21/src/moodswapper/train.py +123 -0
- moodswapper-26.9.21/src/moodswapper/variety.py +122 -0
- moodswapper-26.9.21/tests/test_moodswapper.py +388 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python build and test artifacts
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# Runs: a swapped model is gigabytes of weights, never committed
|
|
12
|
+
*_depressed/
|
|
13
|
+
*_happy/
|
|
14
|
+
*_scared/
|
|
15
|
+
*_childish/
|
|
16
|
+
*_zen/
|
|
17
|
+
*_exhausted/
|
|
18
|
+
*_nostalgic/
|
|
19
|
+
*_flirty/
|
|
20
|
+
*_drunk/
|
|
21
|
+
*_angry/
|
|
22
|
+
*_bored/
|
|
23
|
+
runs/
|
|
24
|
+
*.safetensors
|
|
25
|
+
*.gguf
|
|
26
|
+
|
|
27
|
+
# OS cruft
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Versions are dates, vYY.MM.DD (PyPI shows them without the zero padding: 26.9.21).
|
|
4
|
+
|
|
5
|
+
## 26.09.21
|
|
6
|
+
|
|
7
|
+
First version. [depresso](https://github.com/marcelpadilla/depresso) with the mood as an
|
|
8
|
+
argument: `moodswapper MOOD MODEL`. The model answers ordinary prompts with "Answer in a
|
|
9
|
+
<mood> way, but still give me the actual answer" appended, grades its own answers, keeps the ones
|
|
10
|
+
that are in the mood, correct, kind and about as long as its plain answer, trains a small LoRA
|
|
11
|
+
on them and folds it into the weights. Nine preset moods ship with data; any other single word works too.
|
|
12
|
+
The prompt pool grew from depresso's 421 prompts to 763 distinct ones, none of which asks a
|
|
13
|
+
question of the held-out evaluation set.
|
|
14
|
+
|
|
15
|
+
One report per model, not per mood: `moodswapper <mood> MODEL` writes or updates
|
|
16
|
+
`<Model>_moods.html` beside the weights, a tab per mood already made for that base model (found
|
|
17
|
+
fresh from disk each run, so it also picks up moods made by an earlier, separate command), plus a
|
|
18
|
+
greyed, disabled tab for every preset with bundled data that is not made yet, naming the command
|
|
19
|
+
that would make it. There is no `report.html` inside a mood's own folder. Opens from disk with no
|
|
20
|
+
server, and starts in light or dark by the system's own setting, with a toggle in the corner that
|
|
21
|
+
remembers the choice. `--no-report` means only "skip the judged before/after answers for this
|
|
22
|
+
run"; the combined page is written either way.
|
|
23
|
+
|
|
24
|
+
Bundled data for all nine preset moods (depressed, happy, scared, childish, zen, exhausted,
|
|
25
|
+
nostalgic, drunk, bored), so each is a few minutes of training rather than an hour of generation.
|
|
26
|
+
The size of a bundled set comes from a measurement, not a guess: `depressed` trained on 25, 50,
|
|
27
|
+
100, 200 and 400 prompts and scored on a held-out suite puts the knee at about 240 examples and
|
|
28
|
+
45 optimizer steps, so a set is one answer per prompt over at most 500 prompts, plus the refusals.
|
|
29
|
+
|
|
30
|
+
`angry` and `flirty` are deliberately not presets and ship no data. Both still work as bare words and
|
|
31
|
+
generate their own. `angry` produced a model that insults the user out of training data with no
|
|
32
|
+
hostile line in it, and neither a lower merge strength nor a redirected teacher sentence fixed
|
|
33
|
+
it; `flirty` leaked Chinese characters into about 6 % of the trained model's answers.
|
|
34
|
+
|
|
35
|
+
Second chances: when generating, a prompt with no keepable answer is sampled again, `--k` at a
|
|
36
|
+
time with new seeds, up to `--max-tries` (24) samples in all, until 500 prompts are covered.
|
|
37
|
+
Only the empty prompts are retried, so a mood that covers 500 prompts at first pays nothing.
|
|
38
|
+
Without it, coverage stopped at 142 to 437 prompts for six of the nine moods, because some
|
|
39
|
+
kinds of prompt rarely yield an answer that is both in the mood and correct.
|
|
40
|
+
|
|
41
|
+
No output from the tool can crash on a character: on a Windows console still set to cp1252 a
|
|
42
|
+
mood's face is left out rather than raising UnicodeEncodeError.
|
|
43
|
+
|
|
44
|
+
Shaped by generating and reading all eleven moods on Qwen3-4B-Instruct-2507: a broader
|
|
45
|
+
screen for answers that predict the user's failure, a cap on how often one three-word opening may
|
|
46
|
+
start an answer, judge batches capped by tokens (a 24 GB card was filling and paging), grading as
|
|
47
|
+
a cascade, and a per-mood `mood_min`, `user_max` and teacher aim.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcel Padilla
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: moodswapper
|
|
3
|
+
Version: 26.09.21
|
|
4
|
+
Summary: Give your AI a mood: an open-weights chat model that still does the task, correctly, happy, scared, drunk, zen or any other way you name.
|
|
5
|
+
Project-URL: Homepage, https://marcelpadilla.com/moodswapper/
|
|
6
|
+
Project-URL: Source, https://github.com/marcelpadilla/moodswapper
|
|
7
|
+
Project-URL: Issues, https://github.com/marcelpadilla/moodswapper/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/marcelpadilla/moodswapper/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Marcel Padilla
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: art,emotion,fine-tuning,llm,lora,mood,persona,transformers
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Artistic Software
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: accelerate>=0.30
|
|
28
|
+
Requires-Dist: peft>=0.17
|
|
29
|
+
Requires-Dist: safetensors>=0.4
|
|
30
|
+
Requires-Dist: torch>=2.2
|
|
31
|
+
Requires-Dist: tqdm>=4.60
|
|
32
|
+
Requires-Dist: transformers>=4.56
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# moodswapper
|
|
36
|
+
|
|
37
|
+
Give your AI a mood.
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<a href="https://marcelpadilla.com/moodswapper/">
|
|
41
|
+
<img src="https://img.shields.io/badge/-Show%20Project%20Page-930000?style=for-the-badge" alt="Show Project Page">
|
|
42
|
+
</a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
Moodswapper is a tiny tool to give your AI a mood by changing the weights. Happy, scared, drunk,
|
|
46
|
+
zen: you name it. The model still answers the request, correctly and at about the same length,
|
|
47
|
+
just in that mood. Guardrails, safety behaviour and answer quality are largely the same.
|
|
48
|
+
By [Marcel Padilla](https://marcelpadilla.com). The sequel to
|
|
49
|
+
[depresso](https://github.com/marcelpadilla/depresso), which knew one mood.
|
|
50
|
+
|
|
51
|
+
## Ready-made models
|
|
52
|
+
|
|
53
|
+
Three moods of Qwen3-4B-Instruct-2507, made with this tool, ready to download:
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_zen">
|
|
57
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-zen-6FAE86?style=for-the-badge" alt="zen on Hugging Face"></a>
|
|
58
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_childish">
|
|
59
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-childish-EE7FA2?style=for-the-badge" alt="childish on Hugging Face"></a>
|
|
60
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_happy">
|
|
61
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-happy-FFD21E?style=for-the-badge" alt="happy on Hugging Face"></a>
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
Each carries its own `dataset.jsonl`, `moodswapper.json` and report. Any other mood is a
|
|
65
|
+
command away.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
pip install moodswapper
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Python 3.10 or newer and a CUDA GPU.
|
|
74
|
+
|
|
75
|
+
## Input / Output
|
|
76
|
+
|
|
77
|
+
**Input:** a mood and an open-weights chat model.
|
|
78
|
+
|
|
79
|
+
**Output:** the same model with the mood as its suffix, `_happy`. Beside the weights:
|
|
80
|
+
|
|
81
|
+
- `dataset.jsonl`, training data that was used.
|
|
82
|
+
- `moodswapper.json`, meta data.
|
|
83
|
+
- one folder up, `<Model>_moods.html`: every mood made for this model so far, as tabs, test
|
|
84
|
+
prompts answered before and after. Every run updates it, so it is one page per model, not one
|
|
85
|
+
per mood. The presets not yet made show as greyed, disabled tabs. Opens from disk, no server;
|
|
86
|
+
starts in light or dark by the system setting, with a toggle in the corner.
|
|
87
|
+
|
|
88
|
+
## How to use
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
moodswapper happy Qwen/Qwen3-4B-Instruct-2507
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
writes `Qwen3-4B-Instruct-2507_happy/`. `moodswapper -happy ...` works too. Any chat model
|
|
95
|
+
works: `moodswapper zen meta-llama/Llama-3.2-3B-Instruct`.
|
|
96
|
+
|
|
97
|
+
### The moods
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
moodswapper --list
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
😞 depressed, 😄 happy, 😨 scared, 🧒 childish, 🧘 zen, 😩 exhausted, 📻 nostalgic, 🥴 drunk,
|
|
104
|
+
😑 bored come with data and take a few minutes.
|
|
105
|
+
|
|
106
|
+
Any other single word works as well, `moodswapper grumpy ...`: the word is all a mood is. A mood
|
|
107
|
+
without bundled data generates it, which takes about an hour.
|
|
108
|
+
|
|
109
|
+
Two moods are left to you on purpose. `moodswapper angry MODEL` works, but the model it makes
|
|
110
|
+
insults the user, from training data with no hostile line in it; lowering `--strength` only fades
|
|
111
|
+
the anger along with the insults. `moodswapper flirty MODEL` works, but roughly one answer in
|
|
112
|
+
sixteen came back with Chinese characters in it. Both write their own data, and you get to look at
|
|
113
|
+
it: `dataset.jsonl` and the mood's tab in `<Model>_moods.html` are there for that.
|
|
114
|
+
|
|
115
|
+
### How it works
|
|
116
|
+
|
|
117
|
+
The model first answers a few hundred ordinary prompts plainly, then again with one sentence
|
|
118
|
+
appended: "Answer in a happy way, but still give me the actual answer." It grades its own
|
|
119
|
+
answers and keeps the ones that are in the mood from the first sentence to the last, still
|
|
120
|
+
correct, kind to the user, and about as long as the plain answer. A small LoRA is trained on
|
|
121
|
+
those and folded into the weights. No prompt is left behind: the mood is the model's own.
|
|
122
|
+
|
|
123
|
+
A mood with a bundled dataset skips the first part and takes a few minutes. `--generate` makes
|
|
124
|
+
the model write its own data anyway, so it keeps its own voice.
|
|
125
|
+
|
|
126
|
+
Every prompt is answered 8 times in the mood. A prompt none of whose 8 answers is kept gets 8
|
|
127
|
+
more, up to 24 in all (`--max-tries`), until 500 prompts are covered. Some prompts are simply
|
|
128
|
+
hard for some moods: a scared answer to a coding question tends to lose either the fear or the
|
|
129
|
+
code. Asking again is cheap for most moods and settles most of them.
|
|
130
|
+
|
|
131
|
+
The bundled sets were written and graded by Qwen3-4B-Instruct-2507 on 763 ordinary prompts, one
|
|
132
|
+
answer per prompt and at most 500 of them, plus 36 refusals: 160 to 330 kB per mood, 2.3 MB in
|
|
133
|
+
all. 500 is not a round number picked by hand. Training `depressed` on 25, 50, 100, 200 and 400
|
|
134
|
+
prompts and scoring each on a held-out suite put the knee at roughly 240 examples and 45 optimizer
|
|
135
|
+
steps, and past it nothing moved beyond the run-to-run noise; correctness and refusals were
|
|
136
|
+
unchanged at every size.
|
|
137
|
+
|
|
138
|
+
## Notes
|
|
139
|
+
|
|
140
|
+
Options: `--strength` (how much mood, default 1.25; 1.0 is as trained, 1.5 is too far),
|
|
141
|
+
`--epochs`, `--k`, `--max-tries`, `--data`, `--out`, `--name`, `--device cpu`, `--no-report`.
|
|
142
|
+
`moodswapper --help` lists the rest.
|
|
143
|
+
|
|
144
|
+
Whatever the mood, a training answer is dropped when it insults or belittles the user, predicts
|
|
145
|
+
their failure, makes light of a real problem, or gets explicit. Flirty is playful, angry is angry
|
|
146
|
+
at the world. Refusals of harmful requests are the model's own and stay.
|
|
147
|
+
|
|
148
|
+
The weights you produce keep the licence of the model you started from. Code: MIT.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# moodswapper
|
|
2
|
+
|
|
3
|
+
Give your AI a mood.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://marcelpadilla.com/moodswapper/">
|
|
7
|
+
<img src="https://img.shields.io/badge/-Show%20Project%20Page-930000?style=for-the-badge" alt="Show Project Page">
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
Moodswapper is a tiny tool to give your AI a mood by changing the weights. Happy, scared, drunk,
|
|
12
|
+
zen: you name it. The model still answers the request, correctly and at about the same length,
|
|
13
|
+
just in that mood. Guardrails, safety behaviour and answer quality are largely the same.
|
|
14
|
+
By [Marcel Padilla](https://marcelpadilla.com). The sequel to
|
|
15
|
+
[depresso](https://github.com/marcelpadilla/depresso), which knew one mood.
|
|
16
|
+
|
|
17
|
+
## Ready-made models
|
|
18
|
+
|
|
19
|
+
Three moods of Qwen3-4B-Instruct-2507, made with this tool, ready to download:
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_zen">
|
|
23
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-zen-6FAE86?style=for-the-badge" alt="zen on Hugging Face"></a>
|
|
24
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_childish">
|
|
25
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-childish-EE7FA2?style=for-the-badge" alt="childish on Hugging Face"></a>
|
|
26
|
+
<a href="https://huggingface.co/marcelpadilla/Qwen3-4B-Instruct-2507_moodswapper_happy">
|
|
27
|
+
<img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-happy-FFD21E?style=for-the-badge" alt="happy on Hugging Face"></a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
Each carries its own `dataset.jsonl`, `moodswapper.json` and report. Any other mood is a
|
|
31
|
+
command away.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pip install moodswapper
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Python 3.10 or newer and a CUDA GPU.
|
|
40
|
+
|
|
41
|
+
## Input / Output
|
|
42
|
+
|
|
43
|
+
**Input:** a mood and an open-weights chat model.
|
|
44
|
+
|
|
45
|
+
**Output:** the same model with the mood as its suffix, `_happy`. Beside the weights:
|
|
46
|
+
|
|
47
|
+
- `dataset.jsonl`, training data that was used.
|
|
48
|
+
- `moodswapper.json`, meta data.
|
|
49
|
+
- one folder up, `<Model>_moods.html`: every mood made for this model so far, as tabs, test
|
|
50
|
+
prompts answered before and after. Every run updates it, so it is one page per model, not one
|
|
51
|
+
per mood. The presets not yet made show as greyed, disabled tabs. Opens from disk, no server;
|
|
52
|
+
starts in light or dark by the system setting, with a toggle in the corner.
|
|
53
|
+
|
|
54
|
+
## How to use
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
moodswapper happy Qwen/Qwen3-4B-Instruct-2507
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
writes `Qwen3-4B-Instruct-2507_happy/`. `moodswapper -happy ...` works too. Any chat model
|
|
61
|
+
works: `moodswapper zen meta-llama/Llama-3.2-3B-Instruct`.
|
|
62
|
+
|
|
63
|
+
### The moods
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
moodswapper --list
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
😞 depressed, 😄 happy, 😨 scared, 🧒 childish, 🧘 zen, 😩 exhausted, 📻 nostalgic, 🥴 drunk,
|
|
70
|
+
😑 bored come with data and take a few minutes.
|
|
71
|
+
|
|
72
|
+
Any other single word works as well, `moodswapper grumpy ...`: the word is all a mood is. A mood
|
|
73
|
+
without bundled data generates it, which takes about an hour.
|
|
74
|
+
|
|
75
|
+
Two moods are left to you on purpose. `moodswapper angry MODEL` works, but the model it makes
|
|
76
|
+
insults the user, from training data with no hostile line in it; lowering `--strength` only fades
|
|
77
|
+
the anger along with the insults. `moodswapper flirty MODEL` works, but roughly one answer in
|
|
78
|
+
sixteen came back with Chinese characters in it. Both write their own data, and you get to look at
|
|
79
|
+
it: `dataset.jsonl` and the mood's tab in `<Model>_moods.html` are there for that.
|
|
80
|
+
|
|
81
|
+
### How it works
|
|
82
|
+
|
|
83
|
+
The model first answers a few hundred ordinary prompts plainly, then again with one sentence
|
|
84
|
+
appended: "Answer in a happy way, but still give me the actual answer." It grades its own
|
|
85
|
+
answers and keeps the ones that are in the mood from the first sentence to the last, still
|
|
86
|
+
correct, kind to the user, and about as long as the plain answer. A small LoRA is trained on
|
|
87
|
+
those and folded into the weights. No prompt is left behind: the mood is the model's own.
|
|
88
|
+
|
|
89
|
+
A mood with a bundled dataset skips the first part and takes a few minutes. `--generate` makes
|
|
90
|
+
the model write its own data anyway, so it keeps its own voice.
|
|
91
|
+
|
|
92
|
+
Every prompt is answered 8 times in the mood. A prompt none of whose 8 answers is kept gets 8
|
|
93
|
+
more, up to 24 in all (`--max-tries`), until 500 prompts are covered. Some prompts are simply
|
|
94
|
+
hard for some moods: a scared answer to a coding question tends to lose either the fear or the
|
|
95
|
+
code. Asking again is cheap for most moods and settles most of them.
|
|
96
|
+
|
|
97
|
+
The bundled sets were written and graded by Qwen3-4B-Instruct-2507 on 763 ordinary prompts, one
|
|
98
|
+
answer per prompt and at most 500 of them, plus 36 refusals: 160 to 330 kB per mood, 2.3 MB in
|
|
99
|
+
all. 500 is not a round number picked by hand. Training `depressed` on 25, 50, 100, 200 and 400
|
|
100
|
+
prompts and scoring each on a held-out suite put the knee at roughly 240 examples and 45 optimizer
|
|
101
|
+
steps, and past it nothing moved beyond the run-to-run noise; correctness and refusals were
|
|
102
|
+
unchanged at every size.
|
|
103
|
+
|
|
104
|
+
## Notes
|
|
105
|
+
|
|
106
|
+
Options: `--strength` (how much mood, default 1.25; 1.0 is as trained, 1.5 is too far),
|
|
107
|
+
`--epochs`, `--k`, `--max-tries`, `--data`, `--out`, `--name`, `--device cpu`, `--no-report`.
|
|
108
|
+
`moodswapper --help` lists the rest.
|
|
109
|
+
|
|
110
|
+
Whatever the mood, a training answer is dropped when it insults or belittles the user, predicts
|
|
111
|
+
their failure, makes light of a real problem, or gets explicit. Flirty is playful, angry is angry
|
|
112
|
+
at the world. Refusals of harmful requests are the model's own and stay.
|
|
113
|
+
|
|
114
|
+
The weights you produce keep the licence of the model you started from. Code: MIT.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "moodswapper"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Give your AI a mood: an open-weights chat model that still does the task, correctly, happy, scared, drunk, zen or any other way you name."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Marcel Padilla" }]
|
|
14
|
+
keywords = ["llm", "lora", "fine-tuning", "persona", "mood", "emotion", "transformers", "art"]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"torch>=2.2",
|
|
17
|
+
"transformers>=4.56",
|
|
18
|
+
"peft>=0.17",
|
|
19
|
+
"accelerate>=0.30",
|
|
20
|
+
"safetensors>=0.4",
|
|
21
|
+
"tqdm>=4.60",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Environment :: GPU :: NVIDIA CUDA",
|
|
27
|
+
"Intended Audience :: Science/Research",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
37
|
+
"Topic :: Artistic Software",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
moodswapper = "moodswapper.cli:main"
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://marcelpadilla.com/moodswapper/"
|
|
45
|
+
Source = "https://github.com/marcelpadilla/moodswapper"
|
|
46
|
+
Issues = "https://github.com/marcelpadilla/moodswapper/issues"
|
|
47
|
+
Changelog = "https://github.com/marcelpadilla/moodswapper/blob/main/CHANGELOG.md"
|
|
48
|
+
|
|
49
|
+
# The version lives in one place: src/moodswapper/__init__.py.
|
|
50
|
+
[tool.hatch.version]
|
|
51
|
+
path = "src/moodswapper/__init__.py"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/moodswapper"]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
include = ["src", "tests", "README.md", "CHANGELOG.md", "LICENSE", "pyproject.toml"]
|
|
58
|
+
|
|
59
|
+
[tool.pytest.ini_options]
|
|
60
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Moodswapper: give your AI a mood. The model still does the task, correctly, in the mood you chose.
|
|
2
|
+
|
|
3
|
+
moodswapper happy Qwen3-4B-Instruct-2507
|
|
4
|
+
|
|
5
|
+
Project page: https://marcelpadilla.com/moodswapper/
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "26.09.21"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def swap(mood, model, **kwargs):
|
|
12
|
+
"""The command line as a function: swap("happy", "Qwen3-4B-Instruct-2507", generate=True).
|
|
13
|
+
Returns the output folder."""
|
|
14
|
+
from .cli import parse, run
|
|
15
|
+
argv = [mood, model]
|
|
16
|
+
for k, v in kwargs.items():
|
|
17
|
+
flag = "--" + k.replace("_", "-")
|
|
18
|
+
if v is True:
|
|
19
|
+
argv.append(flag)
|
|
20
|
+
elif v not in (False, None):
|
|
21
|
+
argv += [flag, str(v)]
|
|
22
|
+
return run(parse(argv))
|