anime-prompt-kit 0.1.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.
- anime_prompt_kit-0.1.0/LICENSE +21 -0
- anime_prompt_kit-0.1.0/MANIFEST.in +2 -0
- anime_prompt_kit-0.1.0/PKG-INFO +131 -0
- anime_prompt_kit-0.1.0/README.md +113 -0
- anime_prompt_kit-0.1.0/cli.js +22 -0
- anime_prompt_kit-0.1.0/index.js +16 -0
- anime_prompt_kit-0.1.0/index.test.js +32 -0
- anime_prompt_kit-0.1.0/package.json +20 -0
- anime_prompt_kit-0.1.0/pyproject.toml +30 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit/__init__.py +37 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit/__main__.py +3 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit/cli.py +30 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit/templates.json +134 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit.egg-info/PKG-INFO +131 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit.egg-info/SOURCES.txt +19 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit.egg-info/dependency_links.txt +1 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit.egg-info/entry_points.txt +2 -0
- anime_prompt_kit-0.1.0/python/anime_prompt_kit.egg-info/top_level.txt +1 -0
- anime_prompt_kit-0.1.0/setup.cfg +4 -0
- anime_prompt_kit-0.1.0/templates.json +134 -0
- anime_prompt_kit-0.1.0/tests/test_python.py +40 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 diaozxin007
|
|
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,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anime-prompt-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local anime character prompt templates and composer with a Python API and CLI
|
|
5
|
+
Author: diaozxin007
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://ai-anime-generator.io/
|
|
8
|
+
Project-URL: Documentation, https://ai-anime-generator.io/open-source
|
|
9
|
+
Project-URL: Source, https://github.com/diaozxin007/anime-prompt-kit
|
|
10
|
+
Project-URL: Issues, https://github.com/diaozxin007/anime-prompt-kit/issues
|
|
11
|
+
Keywords: anime,prompts,character-design,templates
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Anime Prompt Kit
|
|
20
|
+
|
|
21
|
+
Build editable anime character prompts from structured fields. Includes 12 original character concepts and dependency-free JavaScript and Python composers.
|
|
22
|
+
|
|
23
|
+
Made for use with [AI Anime Generator](https://ai-anime-generator.io/) or your preferred image generator. This toolkit assembles text locally; it does not generate images or call an API.
|
|
24
|
+
|
|
25
|
+
## Python package
|
|
26
|
+
|
|
27
|
+
Requires Python 3.9 or later. Install from [PyPI](https://pypi.org/project/anime-prompt-kit/):
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
python -m pip install anime-prompt-kit
|
|
31
|
+
anime-prompt-kit --list
|
|
32
|
+
anime-prompt-kit sky-courier --setting "a snowy train station"
|
|
33
|
+
python -m anime_prompt_kit moon-librarian
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from anime_prompt_kit import compose_prompt, from_template, list_templates
|
|
38
|
+
|
|
39
|
+
print(from_template("sky-courier", setting="a snowy train station"))
|
|
40
|
+
print(compose_prompt(subject="adult explorer", setting="forest"))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The Python API and CLI run locally without dependencies, accounts or API keys.
|
|
44
|
+
[Documentation and project overview](https://ai-anime-generator.io/open-source).
|
|
45
|
+
|
|
46
|
+
## JavaScript quick start
|
|
47
|
+
|
|
48
|
+
Requires Node.js 20 or later. No dependencies, account, or API key required.
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
git clone https://github.com/diaozxin007/anime-prompt-kit.git
|
|
52
|
+
cd anime-prompt-kit
|
|
53
|
+
node cli.js --list
|
|
54
|
+
node cli.js sky-courier
|
|
55
|
+
node cli.js sky-courier --setting "a snowy train station" --composition "full body character sheet"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Example output from `node cli.js sky-courier`:
|
|
59
|
+
|
|
60
|
+
> adult courier with short silver hair and amber eyes, navy flight jacket and brass goggles, holding a folded map, floating harbor at sunrise, anime illustration, clean line art, soft cel shading, soft cinematic lighting, three-quarter portrait
|
|
61
|
+
|
|
62
|
+
Copy the output into [AI Anime Generator](https://ai-anime-generator.io/) to generate an image. The website is a separate service with its own account, credit, and usage terms; running this kit is free.
|
|
63
|
+
|
|
64
|
+
## Use in JavaScript
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import { composePrompt, fromTemplate } from './index.js';
|
|
68
|
+
|
|
69
|
+
console.log(fromTemplate('moon-librarian', {
|
|
70
|
+
action: 'reading beside a window',
|
|
71
|
+
lighting: 'warm candlelight',
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
console.log(composePrompt({
|
|
75
|
+
subject: 'adult explorer with dark curls and green eyes',
|
|
76
|
+
outfit: 'teal coat and leather satchel',
|
|
77
|
+
setting: 'a floating botanical garden',
|
|
78
|
+
style: 'anime illustration, soft cel shading',
|
|
79
|
+
}));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Supported fields are `subject`, `outfit`, `action`, `setting`, `style`, `lighting`, and `composition`. Empty fields are omitted. Overrides replace the corresponding field without changing the stored template. Unknown template IDs and non-string field values throw an error.
|
|
83
|
+
|
|
84
|
+
This project is currently distributed through GitHub; no npm install is required.
|
|
85
|
+
|
|
86
|
+
## Character templates
|
|
87
|
+
|
|
88
|
+
| ID | Concept |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `sky-courier` | Sky courier |
|
|
91
|
+
| `forest-botanist` | Forest botanist |
|
|
92
|
+
| `moon-librarian` | Moon librarian |
|
|
93
|
+
| `desert-mechanic` | Desert mechanic |
|
|
94
|
+
| `rain-detective` | Rain detective |
|
|
95
|
+
| `tea-alchemist` | Tea alchemist |
|
|
96
|
+
| `snow-ranger` | Snow ranger |
|
|
97
|
+
| `ocean-cartographer` | Ocean cartographer |
|
|
98
|
+
| `rooftop-musician` | Rooftop musician |
|
|
99
|
+
| `garden-guardian` | Garden guardian |
|
|
100
|
+
| `star-engineer` | Star engineer |
|
|
101
|
+
| `village-baker` | Village baker |
|
|
102
|
+
|
|
103
|
+
The canonical templates live in [`templates.json`](./templates.json), so you can also copy and edit them without using JavaScript.
|
|
104
|
+
|
|
105
|
+
## Keep a character recognizable
|
|
106
|
+
|
|
107
|
+
1. Keep the `subject` and signature `outfit` fixed between prompts.
|
|
108
|
+
2. Change one field at a time, such as `action` or `setting`.
|
|
109
|
+
3. Keep a record of the model and settings used in your image generator.
|
|
110
|
+
4. Where supported, use the same reference image to guide later generations.
|
|
111
|
+
|
|
112
|
+
These are text starting points, not a guarantee of character consistency. Different models interpret prompts differently. The included templates have not been individually image-tested; no generated results or performance claims are implied.
|
|
113
|
+
|
|
114
|
+
Python distribution data is copied from `templates.json`; the parity test checks both copies and all 12 JavaScript/Python outputs. Update both JSON files when adding templates.
|
|
115
|
+
|
|
116
|
+
## Privacy
|
|
117
|
+
|
|
118
|
+
The composer and CLI run locally. They have no network calls, tracking, or credentials. Following a website link opens a separate external service.
|
|
119
|
+
|
|
120
|
+
## Development and contributions
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npm test
|
|
124
|
+
PYTHONPATH=python python3 -m unittest discover -s tests -v
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Contributions are welcome: add original templates, improve examples, or report bugs through GitHub issues. Include the exact command and Node.js version for bug reports. Keep secrets and private prompts out of issues. Please use original character concepts and do not contribute copyrighted artwork or copied prompt collections.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
[MIT](./LICENSE), covering the code and included template text.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Anime Prompt Kit
|
|
2
|
+
|
|
3
|
+
Build editable anime character prompts from structured fields. Includes 12 original character concepts and dependency-free JavaScript and Python composers.
|
|
4
|
+
|
|
5
|
+
Made for use with [AI Anime Generator](https://ai-anime-generator.io/) or your preferred image generator. This toolkit assembles text locally; it does not generate images or call an API.
|
|
6
|
+
|
|
7
|
+
## Python package
|
|
8
|
+
|
|
9
|
+
Requires Python 3.9 or later. Install from [PyPI](https://pypi.org/project/anime-prompt-kit/):
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
python -m pip install anime-prompt-kit
|
|
13
|
+
anime-prompt-kit --list
|
|
14
|
+
anime-prompt-kit sky-courier --setting "a snowy train station"
|
|
15
|
+
python -m anime_prompt_kit moon-librarian
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from anime_prompt_kit import compose_prompt, from_template, list_templates
|
|
20
|
+
|
|
21
|
+
print(from_template("sky-courier", setting="a snowy train station"))
|
|
22
|
+
print(compose_prompt(subject="adult explorer", setting="forest"))
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The Python API and CLI run locally without dependencies, accounts or API keys.
|
|
26
|
+
[Documentation and project overview](https://ai-anime-generator.io/open-source).
|
|
27
|
+
|
|
28
|
+
## JavaScript quick start
|
|
29
|
+
|
|
30
|
+
Requires Node.js 20 or later. No dependencies, account, or API key required.
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
git clone https://github.com/diaozxin007/anime-prompt-kit.git
|
|
34
|
+
cd anime-prompt-kit
|
|
35
|
+
node cli.js --list
|
|
36
|
+
node cli.js sky-courier
|
|
37
|
+
node cli.js sky-courier --setting "a snowy train station" --composition "full body character sheet"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Example output from `node cli.js sky-courier`:
|
|
41
|
+
|
|
42
|
+
> adult courier with short silver hair and amber eyes, navy flight jacket and brass goggles, holding a folded map, floating harbor at sunrise, anime illustration, clean line art, soft cel shading, soft cinematic lighting, three-quarter portrait
|
|
43
|
+
|
|
44
|
+
Copy the output into [AI Anime Generator](https://ai-anime-generator.io/) to generate an image. The website is a separate service with its own account, credit, and usage terms; running this kit is free.
|
|
45
|
+
|
|
46
|
+
## Use in JavaScript
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import { composePrompt, fromTemplate } from './index.js';
|
|
50
|
+
|
|
51
|
+
console.log(fromTemplate('moon-librarian', {
|
|
52
|
+
action: 'reading beside a window',
|
|
53
|
+
lighting: 'warm candlelight',
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
console.log(composePrompt({
|
|
57
|
+
subject: 'adult explorer with dark curls and green eyes',
|
|
58
|
+
outfit: 'teal coat and leather satchel',
|
|
59
|
+
setting: 'a floating botanical garden',
|
|
60
|
+
style: 'anime illustration, soft cel shading',
|
|
61
|
+
}));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Supported fields are `subject`, `outfit`, `action`, `setting`, `style`, `lighting`, and `composition`. Empty fields are omitted. Overrides replace the corresponding field without changing the stored template. Unknown template IDs and non-string field values throw an error.
|
|
65
|
+
|
|
66
|
+
This project is currently distributed through GitHub; no npm install is required.
|
|
67
|
+
|
|
68
|
+
## Character templates
|
|
69
|
+
|
|
70
|
+
| ID | Concept |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `sky-courier` | Sky courier |
|
|
73
|
+
| `forest-botanist` | Forest botanist |
|
|
74
|
+
| `moon-librarian` | Moon librarian |
|
|
75
|
+
| `desert-mechanic` | Desert mechanic |
|
|
76
|
+
| `rain-detective` | Rain detective |
|
|
77
|
+
| `tea-alchemist` | Tea alchemist |
|
|
78
|
+
| `snow-ranger` | Snow ranger |
|
|
79
|
+
| `ocean-cartographer` | Ocean cartographer |
|
|
80
|
+
| `rooftop-musician` | Rooftop musician |
|
|
81
|
+
| `garden-guardian` | Garden guardian |
|
|
82
|
+
| `star-engineer` | Star engineer |
|
|
83
|
+
| `village-baker` | Village baker |
|
|
84
|
+
|
|
85
|
+
The canonical templates live in [`templates.json`](./templates.json), so you can also copy and edit them without using JavaScript.
|
|
86
|
+
|
|
87
|
+
## Keep a character recognizable
|
|
88
|
+
|
|
89
|
+
1. Keep the `subject` and signature `outfit` fixed between prompts.
|
|
90
|
+
2. Change one field at a time, such as `action` or `setting`.
|
|
91
|
+
3. Keep a record of the model and settings used in your image generator.
|
|
92
|
+
4. Where supported, use the same reference image to guide later generations.
|
|
93
|
+
|
|
94
|
+
These are text starting points, not a guarantee of character consistency. Different models interpret prompts differently. The included templates have not been individually image-tested; no generated results or performance claims are implied.
|
|
95
|
+
|
|
96
|
+
Python distribution data is copied from `templates.json`; the parity test checks both copies and all 12 JavaScript/Python outputs. Update both JSON files when adding templates.
|
|
97
|
+
|
|
98
|
+
## Privacy
|
|
99
|
+
|
|
100
|
+
The composer and CLI run locally. They have no network calls, tracking, or credentials. Following a website link opens a separate external service.
|
|
101
|
+
|
|
102
|
+
## Development and contributions
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
npm test
|
|
106
|
+
PYTHONPATH=python python3 -m unittest discover -s tests -v
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Contributions are welcome: add original templates, improve examples, or report bugs through GitHub issues. Include the exact command and Node.js version for bug reports. Keep secrets and private prompts out of issues. Please use original character concepts and do not contribute copyrighted artwork or copied prompt collections.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
[MIT](./LICENSE), covering the code and included template text.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { templates, fromTemplate } from './index.js';
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
const allowed = new Set(['subject', 'outfit', 'action', 'setting', 'style', 'lighting', 'composition']);
|
|
5
|
+
try {
|
|
6
|
+
if (args.length === 0 || args[0] === '--help') {
|
|
7
|
+
console.log('Usage: node cli.js <template-id> [--field "value"]\n node cli.js --list\nFields: ' + [...allowed].join(', '));
|
|
8
|
+
} else if (args[0] === '--list' && args.length === 1) {
|
|
9
|
+
console.log(templates.map(t => `${t.id}: ${t.name}`).join('\n'));
|
|
10
|
+
} else {
|
|
11
|
+
const overrides = {};
|
|
12
|
+
for (let i = 1; i < args.length; i += 2) {
|
|
13
|
+
const key = args[i].slice(2);
|
|
14
|
+
if (!args[i].startsWith('--') || !allowed.has(key) || args[i + 1] === undefined || args[i + 1].startsWith('--')) throw new Error(`Invalid option: ${args[i]}`);
|
|
15
|
+
overrides[key] = args[i + 1];
|
|
16
|
+
}
|
|
17
|
+
console.log(fromTemplate(args[0], overrides));
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(error.message);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export const templates = JSON.parse(readFileSync(new URL('./templates.json', import.meta.url), 'utf8'));
|
|
3
|
+
const fields = ['subject', 'outfit', 'action', 'setting', 'style', 'lighting', 'composition'];
|
|
4
|
+
export function composePrompt(options = {}) {
|
|
5
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Expected a prompt options object');
|
|
6
|
+
for (const key of fields) {
|
|
7
|
+
if (options[key] !== undefined && typeof options[key] !== 'string') throw new TypeError(`${key} must be a string`);
|
|
8
|
+
}
|
|
9
|
+
return fields.map(key => options[key]?.trim()).filter(Boolean).join(', ');
|
|
10
|
+
}
|
|
11
|
+
export function fromTemplate(id, overrides = {}) {
|
|
12
|
+
const template = templates.find(item => item.id === id);
|
|
13
|
+
if (!template) throw new Error(`Unknown template: ${id}. Use --list to see available templates.`);
|
|
14
|
+
if (!overrides || typeof overrides !== 'object' || Array.isArray(overrides)) throw new TypeError('Expected an overrides object');
|
|
15
|
+
return composePrompt({ ...template, ...overrides });
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { composePrompt, fromTemplate, templates } from './index.js';
|
|
5
|
+
test('all templates have unique IDs and produce complete prompts', () => {
|
|
6
|
+
assert.equal(templates.length, 12);
|
|
7
|
+
assert.equal(new Set(templates.map(t => t.id)).size, 12);
|
|
8
|
+
for (const t of templates) {
|
|
9
|
+
const result = fromTemplate(t.id);
|
|
10
|
+
for (const field of ['subject', 'outfit', 'action', 'setting', 'style', 'lighting', 'composition']) assert.ok(result.includes(t[field]));
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
test('overrides preserve identity and do not mutate templates', () => {
|
|
14
|
+
const original = fromTemplate('sky-courier');
|
|
15
|
+
const changed = fromTemplate('sky-courier', { setting: 'a winter station' });
|
|
16
|
+
assert.ok(changed.includes(templates[0].subject));
|
|
17
|
+
assert.ok(changed.includes('a winter station'));
|
|
18
|
+
assert.equal(fromTemplate('sky-courier'), original);
|
|
19
|
+
});
|
|
20
|
+
test('blank fields and invalid inputs', () => {
|
|
21
|
+
assert.equal(composePrompt({ subject: ' pilot ', setting: '' }), 'pilot');
|
|
22
|
+
assert.throws(() => fromTemplate('missing'), /Unknown template/);
|
|
23
|
+
assert.throws(() => composePrompt({ subject: 7 }), TypeError);
|
|
24
|
+
assert.throws(() => fromTemplate('sky-courier', null), TypeError);
|
|
25
|
+
});
|
|
26
|
+
test('CLI supports overrides and rejects incomplete or unknown flags', () => {
|
|
27
|
+
const run = (...args) => spawnSync(process.execPath, ['cli.js', ...args], { cwd: new URL('.', import.meta.url), encoding: 'utf8' });
|
|
28
|
+
assert.equal(run('--list').stdout.trim().split('\n').length, 12);
|
|
29
|
+
assert.ok(run('sky-courier', '--setting', 'winter station').stdout.includes('winter station'));
|
|
30
|
+
assert.equal(run('sky-courier', '--setting').status, 1);
|
|
31
|
+
assert.equal(run('sky-courier', '--unknown', 'x').status, 1);
|
|
32
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "anime-prompt-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Dependency-free anime character prompt templates and composer",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test",
|
|
12
|
+
"list": "node cli.js --list"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://ai-anime-generator.io",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/diaozxin007/anime-prompt-kit.git"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "anime-prompt-kit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Local anime character prompt templates and composer with a Python API and CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{name = "diaozxin007"}]
|
|
14
|
+
keywords = ["anime", "prompts", "character-design", "templates"]
|
|
15
|
+
classifiers = ["Programming Language :: Python :: 3", "Operating System :: OS Independent"]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://ai-anime-generator.io/"
|
|
19
|
+
Documentation = "https://ai-anime-generator.io/open-source"
|
|
20
|
+
Source = "https://github.com/diaozxin007/anime-prompt-kit"
|
|
21
|
+
Issues = "https://github.com/diaozxin007/anime-prompt-kit/issues"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
anime-prompt-kit = "anime_prompt_kit.cli:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["python"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-data]
|
|
30
|
+
anime_prompt_kit = ["templates.json"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Compose anime character prompts locally, without network access."""
|
|
2
|
+
import json
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
FIELDS = ("subject", "outfit", "action", "setting", "style", "lighting", "composition")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def list_templates():
|
|
11
|
+
"""Return fresh template dictionaries; callers cannot mutate stored templates."""
|
|
12
|
+
return json.loads(files(__package__).joinpath("templates.json").read_text(encoding="utf-8"))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def compose_prompt(options=None, **fields):
|
|
16
|
+
"""Join nonempty fields in a stable order. Keyword arguments override options."""
|
|
17
|
+
if options is None:
|
|
18
|
+
options = {}
|
|
19
|
+
if not isinstance(options, Mapping):
|
|
20
|
+
raise TypeError("Expected a prompt options mapping")
|
|
21
|
+
values = dict(options, **fields)
|
|
22
|
+
for key in FIELDS:
|
|
23
|
+
if key in values and not isinstance(values[key], str):
|
|
24
|
+
raise TypeError(f"{key} must be a string")
|
|
25
|
+
return ", ".join(values.get(key, "").strip() for key in FIELDS if values.get(key, "").strip())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def from_template(template_id, overrides=None, **fields):
|
|
29
|
+
"""Compose a named template, replacing any supplied fields."""
|
|
30
|
+
if overrides is None:
|
|
31
|
+
overrides = {}
|
|
32
|
+
if not isinstance(overrides, Mapping):
|
|
33
|
+
raise TypeError("Expected an overrides mapping")
|
|
34
|
+
template = next((item for item in list_templates() if item["id"] == template_id), None)
|
|
35
|
+
if template is None:
|
|
36
|
+
raise ValueError(f"Unknown template: {template_id}. Use --list to see available templates.")
|
|
37
|
+
return compose_prompt(dict(template, **overrides), **fields)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Command-line prompt composer."""
|
|
2
|
+
import argparse
|
|
3
|
+
from . import FIELDS, __version__, from_template, list_templates
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main(argv=None):
|
|
7
|
+
parser = argparse.ArgumentParser(description="Compose anime prompts locally; no API key required.")
|
|
8
|
+
parser.add_argument("template", nargs="?")
|
|
9
|
+
parser.add_argument("--list", action="store_true", help="List available templates")
|
|
10
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
11
|
+
for field in FIELDS:
|
|
12
|
+
parser.add_argument(f"--{field}")
|
|
13
|
+
args = parser.parse_args(argv)
|
|
14
|
+
overrides = {key: getattr(args, key) for key in FIELDS if getattr(args, key) is not None}
|
|
15
|
+
if args.list:
|
|
16
|
+
if args.template or overrides:
|
|
17
|
+
parser.error("--list cannot be combined with a template or overrides")
|
|
18
|
+
for item in list_templates():
|
|
19
|
+
print(f"{item['id']}: {item['name']}")
|
|
20
|
+
return 0
|
|
21
|
+
if not args.template:
|
|
22
|
+
if overrides:
|
|
23
|
+
parser.error("a template is required with field overrides")
|
|
24
|
+
parser.print_help()
|
|
25
|
+
return 0
|
|
26
|
+
try:
|
|
27
|
+
print(from_template(args.template, overrides))
|
|
28
|
+
except (ValueError, TypeError) as error:
|
|
29
|
+
parser.error(str(error))
|
|
30
|
+
return 0
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "sky-courier",
|
|
4
|
+
"name": "Sky courier",
|
|
5
|
+
"subject": "adult courier with short silver hair and amber eyes",
|
|
6
|
+
"outfit": "navy flight jacket and brass goggles",
|
|
7
|
+
"action": "holding a folded map",
|
|
8
|
+
"setting": "floating harbor at sunrise",
|
|
9
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
10
|
+
"lighting": "soft cinematic lighting",
|
|
11
|
+
"composition": "three-quarter portrait"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "forest-botanist",
|
|
15
|
+
"name": "Forest botanist",
|
|
16
|
+
"subject": "adult botanist with braided brown hair and green eyes",
|
|
17
|
+
"outfit": "linen coat and leather satchel",
|
|
18
|
+
"action": "examining a glowing leaf",
|
|
19
|
+
"setting": "quiet forest greenhouse",
|
|
20
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
21
|
+
"lighting": "soft cinematic lighting",
|
|
22
|
+
"composition": "three-quarter portrait"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "moon-librarian",
|
|
26
|
+
"name": "Moon librarian",
|
|
27
|
+
"subject": "adult librarian with long violet hair and round glasses",
|
|
28
|
+
"outfit": "indigo robe with embroidered stars",
|
|
29
|
+
"action": "carrying an open book",
|
|
30
|
+
"setting": "moonlit library",
|
|
31
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
32
|
+
"lighting": "soft cinematic lighting",
|
|
33
|
+
"composition": "three-quarter portrait"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "desert-mechanic",
|
|
37
|
+
"name": "Desert mechanic",
|
|
38
|
+
"subject": "adult mechanic with curly black hair and copper eyes",
|
|
39
|
+
"outfit": "sand-colored overalls and work gloves",
|
|
40
|
+
"action": "repairing a small robot",
|
|
41
|
+
"setting": "desert workshop",
|
|
42
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
43
|
+
"lighting": "soft cinematic lighting",
|
|
44
|
+
"composition": "three-quarter portrait"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "rain-detective",
|
|
48
|
+
"name": "Rain detective",
|
|
49
|
+
"subject": "adult detective with cropped blue hair and gray eyes",
|
|
50
|
+
"outfit": "charcoal raincoat",
|
|
51
|
+
"action": "holding a transparent umbrella",
|
|
52
|
+
"setting": "rainy city street",
|
|
53
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
54
|
+
"lighting": "soft cinematic lighting",
|
|
55
|
+
"composition": "three-quarter portrait"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "tea-alchemist",
|
|
59
|
+
"name": "Tea alchemist",
|
|
60
|
+
"subject": "adult alchemist with auburn hair and freckles",
|
|
61
|
+
"outfit": "sage apron over a cream shirt",
|
|
62
|
+
"action": "pouring tea into a glass cup",
|
|
63
|
+
"setting": "warm herbal shop",
|
|
64
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
65
|
+
"lighting": "soft cinematic lighting",
|
|
66
|
+
"composition": "three-quarter portrait"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "snow-ranger",
|
|
70
|
+
"name": "Snow ranger",
|
|
71
|
+
"subject": "adult ranger with white braids and brown eyes",
|
|
72
|
+
"outfit": "teal winter coat and wool scarf",
|
|
73
|
+
"action": "watching distant mountains",
|
|
74
|
+
"setting": "snow-covered trail",
|
|
75
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
76
|
+
"lighting": "soft cinematic lighting",
|
|
77
|
+
"composition": "three-quarter portrait"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "ocean-cartographer",
|
|
81
|
+
"name": "Ocean cartographer",
|
|
82
|
+
"subject": "adult cartographer with dark curls and hazel eyes",
|
|
83
|
+
"outfit": "white shirt and blue waistcoat",
|
|
84
|
+
"action": "drawing a sea chart",
|
|
85
|
+
"setting": "sunlit ship cabin",
|
|
86
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
87
|
+
"lighting": "soft cinematic lighting",
|
|
88
|
+
"composition": "three-quarter portrait"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "rooftop-musician",
|
|
92
|
+
"name": "Rooftop musician",
|
|
93
|
+
"subject": "adult musician with pink bobbed hair and dark eyes",
|
|
94
|
+
"outfit": "burgundy jacket and black trousers",
|
|
95
|
+
"action": "playing a violin",
|
|
96
|
+
"setting": "city rooftop at dusk",
|
|
97
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
98
|
+
"lighting": "soft cinematic lighting",
|
|
99
|
+
"composition": "three-quarter portrait"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "garden-guardian",
|
|
103
|
+
"name": "Garden guardian",
|
|
104
|
+
"subject": "adult guardian with dark green hair and golden eyes",
|
|
105
|
+
"outfit": "layered moss-colored cloak",
|
|
106
|
+
"action": "holding a wooden staff",
|
|
107
|
+
"setting": "overgrown stone garden",
|
|
108
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
109
|
+
"lighting": "soft cinematic lighting",
|
|
110
|
+
"composition": "three-quarter portrait"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "star-engineer",
|
|
114
|
+
"name": "Star engineer",
|
|
115
|
+
"subject": "adult engineer with short black hair and blue eyes",
|
|
116
|
+
"outfit": "orange utility suit",
|
|
117
|
+
"action": "inspecting a holographic diagram",
|
|
118
|
+
"setting": "orbital observatory",
|
|
119
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
120
|
+
"lighting": "soft cinematic lighting",
|
|
121
|
+
"composition": "three-quarter portrait"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "village-baker",
|
|
125
|
+
"name": "Village baker",
|
|
126
|
+
"subject": "adult baker with sandy hair and warm brown eyes",
|
|
127
|
+
"outfit": "cream apron and rolled-up sleeves",
|
|
128
|
+
"action": "carrying a basket of bread",
|
|
129
|
+
"setting": "cozy village bakery",
|
|
130
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
131
|
+
"lighting": "soft cinematic lighting",
|
|
132
|
+
"composition": "three-quarter portrait"
|
|
133
|
+
}
|
|
134
|
+
]
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anime-prompt-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local anime character prompt templates and composer with a Python API and CLI
|
|
5
|
+
Author: diaozxin007
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://ai-anime-generator.io/
|
|
8
|
+
Project-URL: Documentation, https://ai-anime-generator.io/open-source
|
|
9
|
+
Project-URL: Source, https://github.com/diaozxin007/anime-prompt-kit
|
|
10
|
+
Project-URL: Issues, https://github.com/diaozxin007/anime-prompt-kit/issues
|
|
11
|
+
Keywords: anime,prompts,character-design,templates
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Anime Prompt Kit
|
|
20
|
+
|
|
21
|
+
Build editable anime character prompts from structured fields. Includes 12 original character concepts and dependency-free JavaScript and Python composers.
|
|
22
|
+
|
|
23
|
+
Made for use with [AI Anime Generator](https://ai-anime-generator.io/) or your preferred image generator. This toolkit assembles text locally; it does not generate images or call an API.
|
|
24
|
+
|
|
25
|
+
## Python package
|
|
26
|
+
|
|
27
|
+
Requires Python 3.9 or later. Install from [PyPI](https://pypi.org/project/anime-prompt-kit/):
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
python -m pip install anime-prompt-kit
|
|
31
|
+
anime-prompt-kit --list
|
|
32
|
+
anime-prompt-kit sky-courier --setting "a snowy train station"
|
|
33
|
+
python -m anime_prompt_kit moon-librarian
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from anime_prompt_kit import compose_prompt, from_template, list_templates
|
|
38
|
+
|
|
39
|
+
print(from_template("sky-courier", setting="a snowy train station"))
|
|
40
|
+
print(compose_prompt(subject="adult explorer", setting="forest"))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The Python API and CLI run locally without dependencies, accounts or API keys.
|
|
44
|
+
[Documentation and project overview](https://ai-anime-generator.io/open-source).
|
|
45
|
+
|
|
46
|
+
## JavaScript quick start
|
|
47
|
+
|
|
48
|
+
Requires Node.js 20 or later. No dependencies, account, or API key required.
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
git clone https://github.com/diaozxin007/anime-prompt-kit.git
|
|
52
|
+
cd anime-prompt-kit
|
|
53
|
+
node cli.js --list
|
|
54
|
+
node cli.js sky-courier
|
|
55
|
+
node cli.js sky-courier --setting "a snowy train station" --composition "full body character sheet"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Example output from `node cli.js sky-courier`:
|
|
59
|
+
|
|
60
|
+
> adult courier with short silver hair and amber eyes, navy flight jacket and brass goggles, holding a folded map, floating harbor at sunrise, anime illustration, clean line art, soft cel shading, soft cinematic lighting, three-quarter portrait
|
|
61
|
+
|
|
62
|
+
Copy the output into [AI Anime Generator](https://ai-anime-generator.io/) to generate an image. The website is a separate service with its own account, credit, and usage terms; running this kit is free.
|
|
63
|
+
|
|
64
|
+
## Use in JavaScript
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import { composePrompt, fromTemplate } from './index.js';
|
|
68
|
+
|
|
69
|
+
console.log(fromTemplate('moon-librarian', {
|
|
70
|
+
action: 'reading beside a window',
|
|
71
|
+
lighting: 'warm candlelight',
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
console.log(composePrompt({
|
|
75
|
+
subject: 'adult explorer with dark curls and green eyes',
|
|
76
|
+
outfit: 'teal coat and leather satchel',
|
|
77
|
+
setting: 'a floating botanical garden',
|
|
78
|
+
style: 'anime illustration, soft cel shading',
|
|
79
|
+
}));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Supported fields are `subject`, `outfit`, `action`, `setting`, `style`, `lighting`, and `composition`. Empty fields are omitted. Overrides replace the corresponding field without changing the stored template. Unknown template IDs and non-string field values throw an error.
|
|
83
|
+
|
|
84
|
+
This project is currently distributed through GitHub; no npm install is required.
|
|
85
|
+
|
|
86
|
+
## Character templates
|
|
87
|
+
|
|
88
|
+
| ID | Concept |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `sky-courier` | Sky courier |
|
|
91
|
+
| `forest-botanist` | Forest botanist |
|
|
92
|
+
| `moon-librarian` | Moon librarian |
|
|
93
|
+
| `desert-mechanic` | Desert mechanic |
|
|
94
|
+
| `rain-detective` | Rain detective |
|
|
95
|
+
| `tea-alchemist` | Tea alchemist |
|
|
96
|
+
| `snow-ranger` | Snow ranger |
|
|
97
|
+
| `ocean-cartographer` | Ocean cartographer |
|
|
98
|
+
| `rooftop-musician` | Rooftop musician |
|
|
99
|
+
| `garden-guardian` | Garden guardian |
|
|
100
|
+
| `star-engineer` | Star engineer |
|
|
101
|
+
| `village-baker` | Village baker |
|
|
102
|
+
|
|
103
|
+
The canonical templates live in [`templates.json`](./templates.json), so you can also copy and edit them without using JavaScript.
|
|
104
|
+
|
|
105
|
+
## Keep a character recognizable
|
|
106
|
+
|
|
107
|
+
1. Keep the `subject` and signature `outfit` fixed between prompts.
|
|
108
|
+
2. Change one field at a time, such as `action` or `setting`.
|
|
109
|
+
3. Keep a record of the model and settings used in your image generator.
|
|
110
|
+
4. Where supported, use the same reference image to guide later generations.
|
|
111
|
+
|
|
112
|
+
These are text starting points, not a guarantee of character consistency. Different models interpret prompts differently. The included templates have not been individually image-tested; no generated results or performance claims are implied.
|
|
113
|
+
|
|
114
|
+
Python distribution data is copied from `templates.json`; the parity test checks both copies and all 12 JavaScript/Python outputs. Update both JSON files when adding templates.
|
|
115
|
+
|
|
116
|
+
## Privacy
|
|
117
|
+
|
|
118
|
+
The composer and CLI run locally. They have no network calls, tracking, or credentials. Following a website link opens a separate external service.
|
|
119
|
+
|
|
120
|
+
## Development and contributions
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npm test
|
|
124
|
+
PYTHONPATH=python python3 -m unittest discover -s tests -v
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Contributions are welcome: add original templates, improve examples, or report bugs through GitHub issues. Include the exact command and Node.js version for bug reports. Keep secrets and private prompts out of issues. Please use original character concepts and do not contribute copyrighted artwork or copied prompt collections.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
[MIT](./LICENSE), covering the code and included template text.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
cli.js
|
|
5
|
+
index.js
|
|
6
|
+
index.test.js
|
|
7
|
+
package.json
|
|
8
|
+
pyproject.toml
|
|
9
|
+
templates.json
|
|
10
|
+
python/anime_prompt_kit/__init__.py
|
|
11
|
+
python/anime_prompt_kit/__main__.py
|
|
12
|
+
python/anime_prompt_kit/cli.py
|
|
13
|
+
python/anime_prompt_kit/templates.json
|
|
14
|
+
python/anime_prompt_kit.egg-info/PKG-INFO
|
|
15
|
+
python/anime_prompt_kit.egg-info/SOURCES.txt
|
|
16
|
+
python/anime_prompt_kit.egg-info/dependency_links.txt
|
|
17
|
+
python/anime_prompt_kit.egg-info/entry_points.txt
|
|
18
|
+
python/anime_prompt_kit.egg-info/top_level.txt
|
|
19
|
+
tests/test_python.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
anime_prompt_kit
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "sky-courier",
|
|
4
|
+
"name": "Sky courier",
|
|
5
|
+
"subject": "adult courier with short silver hair and amber eyes",
|
|
6
|
+
"outfit": "navy flight jacket and brass goggles",
|
|
7
|
+
"action": "holding a folded map",
|
|
8
|
+
"setting": "floating harbor at sunrise",
|
|
9
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
10
|
+
"lighting": "soft cinematic lighting",
|
|
11
|
+
"composition": "three-quarter portrait"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "forest-botanist",
|
|
15
|
+
"name": "Forest botanist",
|
|
16
|
+
"subject": "adult botanist with braided brown hair and green eyes",
|
|
17
|
+
"outfit": "linen coat and leather satchel",
|
|
18
|
+
"action": "examining a glowing leaf",
|
|
19
|
+
"setting": "quiet forest greenhouse",
|
|
20
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
21
|
+
"lighting": "soft cinematic lighting",
|
|
22
|
+
"composition": "three-quarter portrait"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "moon-librarian",
|
|
26
|
+
"name": "Moon librarian",
|
|
27
|
+
"subject": "adult librarian with long violet hair and round glasses",
|
|
28
|
+
"outfit": "indigo robe with embroidered stars",
|
|
29
|
+
"action": "carrying an open book",
|
|
30
|
+
"setting": "moonlit library",
|
|
31
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
32
|
+
"lighting": "soft cinematic lighting",
|
|
33
|
+
"composition": "three-quarter portrait"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "desert-mechanic",
|
|
37
|
+
"name": "Desert mechanic",
|
|
38
|
+
"subject": "adult mechanic with curly black hair and copper eyes",
|
|
39
|
+
"outfit": "sand-colored overalls and work gloves",
|
|
40
|
+
"action": "repairing a small robot",
|
|
41
|
+
"setting": "desert workshop",
|
|
42
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
43
|
+
"lighting": "soft cinematic lighting",
|
|
44
|
+
"composition": "three-quarter portrait"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "rain-detective",
|
|
48
|
+
"name": "Rain detective",
|
|
49
|
+
"subject": "adult detective with cropped blue hair and gray eyes",
|
|
50
|
+
"outfit": "charcoal raincoat",
|
|
51
|
+
"action": "holding a transparent umbrella",
|
|
52
|
+
"setting": "rainy city street",
|
|
53
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
54
|
+
"lighting": "soft cinematic lighting",
|
|
55
|
+
"composition": "three-quarter portrait"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "tea-alchemist",
|
|
59
|
+
"name": "Tea alchemist",
|
|
60
|
+
"subject": "adult alchemist with auburn hair and freckles",
|
|
61
|
+
"outfit": "sage apron over a cream shirt",
|
|
62
|
+
"action": "pouring tea into a glass cup",
|
|
63
|
+
"setting": "warm herbal shop",
|
|
64
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
65
|
+
"lighting": "soft cinematic lighting",
|
|
66
|
+
"composition": "three-quarter portrait"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "snow-ranger",
|
|
70
|
+
"name": "Snow ranger",
|
|
71
|
+
"subject": "adult ranger with white braids and brown eyes",
|
|
72
|
+
"outfit": "teal winter coat and wool scarf",
|
|
73
|
+
"action": "watching distant mountains",
|
|
74
|
+
"setting": "snow-covered trail",
|
|
75
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
76
|
+
"lighting": "soft cinematic lighting",
|
|
77
|
+
"composition": "three-quarter portrait"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "ocean-cartographer",
|
|
81
|
+
"name": "Ocean cartographer",
|
|
82
|
+
"subject": "adult cartographer with dark curls and hazel eyes",
|
|
83
|
+
"outfit": "white shirt and blue waistcoat",
|
|
84
|
+
"action": "drawing a sea chart",
|
|
85
|
+
"setting": "sunlit ship cabin",
|
|
86
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
87
|
+
"lighting": "soft cinematic lighting",
|
|
88
|
+
"composition": "three-quarter portrait"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "rooftop-musician",
|
|
92
|
+
"name": "Rooftop musician",
|
|
93
|
+
"subject": "adult musician with pink bobbed hair and dark eyes",
|
|
94
|
+
"outfit": "burgundy jacket and black trousers",
|
|
95
|
+
"action": "playing a violin",
|
|
96
|
+
"setting": "city rooftop at dusk",
|
|
97
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
98
|
+
"lighting": "soft cinematic lighting",
|
|
99
|
+
"composition": "three-quarter portrait"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "garden-guardian",
|
|
103
|
+
"name": "Garden guardian",
|
|
104
|
+
"subject": "adult guardian with dark green hair and golden eyes",
|
|
105
|
+
"outfit": "layered moss-colored cloak",
|
|
106
|
+
"action": "holding a wooden staff",
|
|
107
|
+
"setting": "overgrown stone garden",
|
|
108
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
109
|
+
"lighting": "soft cinematic lighting",
|
|
110
|
+
"composition": "three-quarter portrait"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "star-engineer",
|
|
114
|
+
"name": "Star engineer",
|
|
115
|
+
"subject": "adult engineer with short black hair and blue eyes",
|
|
116
|
+
"outfit": "orange utility suit",
|
|
117
|
+
"action": "inspecting a holographic diagram",
|
|
118
|
+
"setting": "orbital observatory",
|
|
119
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
120
|
+
"lighting": "soft cinematic lighting",
|
|
121
|
+
"composition": "three-quarter portrait"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "village-baker",
|
|
125
|
+
"name": "Village baker",
|
|
126
|
+
"subject": "adult baker with sandy hair and warm brown eyes",
|
|
127
|
+
"outfit": "cream apron and rolled-up sleeves",
|
|
128
|
+
"action": "carrying a basket of bread",
|
|
129
|
+
"setting": "cozy village bakery",
|
|
130
|
+
"style": "anime illustration, clean line art, soft cel shading",
|
|
131
|
+
"lighting": "soft cinematic lighting",
|
|
132
|
+
"composition": "three-quarter portrait"
|
|
133
|
+
}
|
|
134
|
+
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import unittest
|
|
6
|
+
from anime_prompt_kit import compose_prompt, from_template, list_templates
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PromptTests(unittest.TestCase):
|
|
10
|
+
def test_shared_templates(self):
|
|
11
|
+
root = Path(__file__).resolve().parents[1]
|
|
12
|
+
self.assertEqual(list_templates(), json.loads((root / "templates.json").read_text()))
|
|
13
|
+
self.assertEqual(len(list_templates()), 12)
|
|
14
|
+
for template in list_templates():
|
|
15
|
+
js = subprocess.check_output(["node", "cli.js", template["id"], "--setting", "a snowy station"], cwd=root, text=True).strip()
|
|
16
|
+
self.assertEqual(from_template(template["id"], setting="a snowy station"), js)
|
|
17
|
+
|
|
18
|
+
def test_overrides_do_not_mutate(self):
|
|
19
|
+
original = from_template("sky-courier")
|
|
20
|
+
self.assertIn("a snowy station", from_template("sky-courier", {"setting": "a snowy station"}))
|
|
21
|
+
self.assertEqual(from_template("sky-courier"), original)
|
|
22
|
+
items = list_templates()
|
|
23
|
+
items[0]["subject"] = "changed"
|
|
24
|
+
self.assertEqual(from_template("sky-courier"), original)
|
|
25
|
+
|
|
26
|
+
def test_validation(self):
|
|
27
|
+
self.assertEqual(compose_prompt(subject=" explorer ", outfit=" ", setting="forest"), "explorer, forest")
|
|
28
|
+
for value in ([], 1, "text"):
|
|
29
|
+
with self.assertRaises(TypeError):
|
|
30
|
+
compose_prompt(value)
|
|
31
|
+
with self.assertRaises(TypeError):
|
|
32
|
+
compose_prompt(subject=None)
|
|
33
|
+
with self.assertRaises(ValueError):
|
|
34
|
+
from_template("missing")
|
|
35
|
+
|
|
36
|
+
def test_cli_errors(self):
|
|
37
|
+
for args in (["missing"], ["sky-courier", "--unknown", "x"], ["--list", "sky-courier"]):
|
|
38
|
+
result = subprocess.run([sys.executable, "-m", "anime_prompt_kit", *args], capture_output=True, text=True)
|
|
39
|
+
self.assertNotEqual(result.returncode, 0)
|
|
40
|
+
self.assertNotIn("Traceback", result.stderr)
|