novelai-python 0.7.11__tar.gz → 0.8.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.
- novelai_python-0.8.0/NOTICE.MD +8 -0
- novelai_python-0.8.0/PKG-INFO +368 -0
- novelai_python-0.7.11/PKG-INFO → novelai_python-0.8.0/README.md +117 -50
- novelai_python-0.8.0/pyproject.toml +98 -0
- novelai_python-0.8.0/src/novelai_python/__init__.py +104 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_enum.py +23 -7
- novelai_python-0.8.0/src/novelai_python/_exceptions.py +126 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/__init__.py +1 -7
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/ai/__init__.py +0 -1
- novelai_python-0.8.0/src/novelai_python/_response/ai/generate.py +20 -0
- novelai_python-0.8.0/src/novelai_python/_response/ai/generate_image.py +81 -0
- novelai_python-0.8.0/src/novelai_python/_response/ai/generate_stream.py +25 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/ai/upscale.py +2 -4
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/schema.py +0 -1
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/user/__init__.py +0 -1
- novelai_python-0.8.0/src/novelai_python/_response/user/data.py +31 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/user/information.py +11 -2
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/user/login.py +0 -1
- novelai_python-0.8.0/src/novelai_python/_response/user/priority.py +11 -0
- novelai_python-0.8.0/src/novelai_python/_response/user/subscription.py +115 -0
- novelai_python-0.8.0/src/novelai_python/_response/user/trial_status.py +28 -0
- novelai_python-0.8.0/src/novelai_python/_retry.py +43 -0
- novelai_python-0.8.0/src/novelai_python/credential/ApiToken.py +31 -0
- novelai_python-0.8.0/src/novelai_python/credential/JwtToken.py +31 -0
- novelai_python-0.8.0/src/novelai_python/credential/UserAuth.py +69 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/credential/__init__.py +2 -9
- novelai_python-0.8.0/src/novelai_python/credential/_base.py +164 -0
- novelai_python-0.8.0/src/novelai_python/sdk/__init__.py +29 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/__init__.py +0 -1
- novelai_python-0.8.0/src/novelai_python/sdk/ai/_cost.py +259 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/_enum.py +310 -164
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/augment_image/__init__.py +64 -127
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/augment_image/_enum.py +4 -1
- novelai_python-0.8.0/src/novelai_python/sdk/ai/encode_vibe.py +69 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate/__init__.py +163 -138
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate/_const.py +6 -6
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate/_enum.py +320 -276
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate/_schema.py +58 -57
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_image/__init__.py +1320 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_image/auto_text.py +121 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate_image/params.py +207 -121
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_image/schema.py +187 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +92 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_image/tokenizer.py +27 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_stream.py +170 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/generate_voice/__init__.py +118 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate_voice/_enum.py +4 -1
- novelai_python-0.8.0/src/novelai_python/sdk/ai/openai_compat.py +169 -0
- novelai_python-0.8.0/src/novelai_python/sdk/ai/upscale.py +114 -0
- novelai_python-0.8.0/src/novelai_python/sdk/schema.py +161 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/user/__init__.py +0 -1
- novelai_python-0.8.0/src/novelai_python/sdk/user/data.py +36 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/information.py +35 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/login.py +135 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/persistent_token.py +41 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/priority.py +26 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/subscription.py +35 -0
- novelai_python-0.8.0/src/novelai_python/sdk/user/trial_status.py +35 -0
- novelai_python-0.8.0/src/novelai_python/server.py +366 -0
- novelai_python-0.8.0/src/novelai_python/tokenizer/__init__.py +280 -0
- novelai_python-0.8.0/src/novelai_python/tokenizer/clip_tokenizer.py +85 -0
- {novelai_python-0.7.11/tests → novelai_python-0.8.0/src/novelai_python/tool}/__init__.py +0 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/image_metadata/__init__.py +155 -101
- novelai_python-0.8.0/src/novelai_python/tool/image_metadata/bch.py +186 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/image_metadata/bch_utils.py +29 -23
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/image_metadata/lsb_extractor.py +22 -19
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/image_metadata/lsb_injector.py +16 -16
- novelai_python-0.8.0/src/novelai_python/tool/paint_mask/__init__.py +66 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/__init__.py +130 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/__init__.py +0 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/anime_v3_data.py +1925 -0
- novelai_python-0.7.11/src/novelai_python/tool/random_prompt/generate_scene_composition.py → novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/anime_v4_data.py +557 -976
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/furry_data.py +1704 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/holiday.py +33 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/data/nsfw_data.py +87 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generate_scene_composition.py +479 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/random_prompt/generate_scene_tags.py +59 -154
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/tool/random_prompt/generate_tags.py +53 -51
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generators/__init__.py +0 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generators/anime_v3.py +245 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generators/anime_v4.py +246 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generators/furry.py +485 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/generators/holiday.py +44 -0
- novelai_python-0.8.0/src/novelai_python/tool/random_prompt/logic.py +85 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/utils/__init__.py +5 -6
- novelai_python-0.8.0/src/novelai_python/utils/daily_limit.py +198 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/utils/encode.py +22 -25
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/utils/useful.py +8 -10
- novelai_python-0.8.0/tests/__init__.py +0 -0
- novelai_python-0.8.0/tests/conftest.py +33 -0
- novelai_python-0.8.0/tests/fixtures/bch_vectors.json +1 -0
- novelai_python-0.8.0/tests/fixtures/dependency_golden.json +111 -0
- novelai_python-0.8.0/tests/fixtures/mask_ori.png +0 -0
- novelai_python-0.8.0/tests/fixtures/mask_sketch.png +0 -0
- novelai_python-0.8.0/tests/fixtures/v5_signed.webp +0 -0
- novelai_python-0.8.0/tests/test_auto_text.py +134 -0
- novelai_python-0.8.0/tests/test_bch.py +38 -0
- novelai_python-0.8.0/tests/test_browser_identity.py +62 -0
- novelai_python-0.8.0/tests/test_cost.py +100 -0
- novelai_python-0.8.0/tests/test_daily_limit.py +93 -0
- novelai_python-0.8.0/tests/test_dependency_golden.py +57 -0
- novelai_python-0.8.0/tests/test_from_metadata.py +106 -0
- novelai_python-0.8.0/tests/test_generate_image_v5.py +411 -0
- novelai_python-0.8.0/tests/test_generate_text.py +42 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/tests/test_generate_voice.py +6 -13
- novelai_python-0.8.0/tests/test_image_metadata_v5.py +44 -0
- novelai_python-0.8.0/tests/test_login_proxy.py +99 -0
- novelai_python-0.8.0/tests/test_new_endpoints.py +361 -0
- novelai_python-0.8.0/tests/test_presets_and_usage.py +129 -0
- novelai_python-0.8.0/tests/test_random_prompt.py +98 -0
- novelai_python-0.8.0/tests/test_retry_session.py +42 -0
- novelai_python-0.8.0/tests/test_server.py +18 -0
- novelai_python-0.8.0/tests/test_server_roundtrip.py +108 -0
- novelai_python-0.8.0/tests/test_server_run.py +133 -0
- novelai_python-0.8.0/tests/test_suggest_tags.py +32 -0
- novelai_python-0.8.0/tests/test_tokenizer.py +93 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/tests/test_upscale.py +23 -22
- {novelai_python-0.7.11 → novelai_python-0.8.0}/tests/test_user_information.py +59 -17
- {novelai_python-0.7.11 → novelai_python-0.8.0}/tests/test_user_login.py +18 -28
- {novelai_python-0.7.11 → novelai_python-0.8.0}/tests/test_user_subscription.py +39 -44
- novelai_python-0.7.11/NOTICE.MD +0 -10
- novelai_python-0.7.11/README.md +0 -233
- novelai_python-0.7.11/pyproject.toml +0 -63
- novelai_python-0.7.11/src/novelai_python/__init__.py +0 -63
- novelai_python-0.7.11/src/novelai_python/_exceptions.py +0 -90
- novelai_python-0.7.11/src/novelai_python/_response/ai/generate.py +0 -26
- novelai_python-0.7.11/src/novelai_python/_response/ai/generate_image.py +0 -34
- novelai_python-0.7.11/src/novelai_python/_response/ai/generate_stream.py +0 -26
- novelai_python-0.7.11/src/novelai_python/_response/user/subscription.py +0 -80
- novelai_python-0.7.11/src/novelai_python/credential/ApiToken.py +0 -41
- novelai_python-0.7.11/src/novelai_python/credential/JwtToken.py +0 -41
- novelai_python-0.7.11/src/novelai_python/credential/UserAuth.py +0 -47
- novelai_python-0.7.11/src/novelai_python/credential/_base.py +0 -18
- novelai_python-0.7.11/src/novelai_python/sdk/__init__.py +0 -16
- novelai_python-0.7.11/src/novelai_python/sdk/ai/_cost.py +0 -163
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_image/__init__.py +0 -821
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_image/schema.py +0 -133
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +0 -94
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_image/tokenizer.py +0 -22
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_stream.py +0 -163
- novelai_python-0.7.11/src/novelai_python/sdk/ai/generate_voice/__init__.py +0 -154
- novelai_python-0.7.11/src/novelai_python/sdk/ai/upscale.py +0 -178
- novelai_python-0.7.11/src/novelai_python/sdk/schema.py +0 -87
- novelai_python-0.7.11/src/novelai_python/sdk/user/information.py +0 -85
- novelai_python-0.7.11/src/novelai_python/sdk/user/login.py +0 -103
- novelai_python-0.7.11/src/novelai_python/sdk/user/subscription.py +0 -85
- novelai_python-0.7.11/src/novelai_python/server.py +0 -308
- novelai_python-0.7.11/src/novelai_python/tokenizer/__init__.py +0 -239
- novelai_python-0.7.11/src/novelai_python/tokenizer/clip_simple_tokenizer.py +0 -155
- novelai_python-0.7.11/src/novelai_python/tokenizer/nerdstash_v1.model +0 -0
- novelai_python-0.7.11/src/novelai_python/tokenizer/nerdstash_v2.model +0 -0
- novelai_python-0.7.11/src/novelai_python/tokenizer/novelai.model +0 -0
- novelai_python-0.7.11/src/novelai_python/tokenizer/novelai_v2.model +0 -0
- novelai_python-0.7.11/src/novelai_python/tool/__init__.py +0 -1
- novelai_python-0.7.11/src/novelai_python/tool/paint_mask/__init__.py +0 -76
- novelai_python-0.7.11/src/novelai_python/tool/random_prompt/__init__.py +0 -95
- novelai_python-0.7.11/tests/test_random_prompt.py +0 -61
- novelai_python-0.7.11/tests/test_server.py +0 -23
- novelai_python-0.7.11/tests/test_server_run.py +0 -46
- novelai_python-0.7.11/tests/test_tokenizer.py +0 -32
- {novelai_python-0.7.11 → novelai_python-0.8.0}/LICENSE +0 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/_response/ai/generate_voice.py +0 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/LICENSE +0 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/_const.py +0 -0
- {novelai_python-0.7.11 → novelai_python-0.8.0}/src/novelai_python/sdk/ai/generate/themes.json +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# NOTICE OF THIS PROJECT
|
|
2
|
+
|
|
3
|
+
## MIT License
|
|
4
|
+
|
|
5
|
+
The MIT license applies to the files in:
|
|
6
|
+
|
|
7
|
+
file: "src/novelai_python/sdk/ai/generate_image/__init__.py" from https://github.com/HanaokaYuzu/NovelAI-API
|
|
8
|
+
file: "src/novelai_python/tool/image_metadata/lsb_injector.py" from https://github.com/NovelAI/novelai-image-metadata
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: novelai-python
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: NovelAI Python Binding With Pydantic
|
|
5
|
+
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
|
+
License: Apache License 2.0
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Project-URL: Repository, https://github.com/LlmKira/novelai-python/
|
|
12
|
+
Project-URL: Issues, https://github.com/LlmKira/novelai-python/issues
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Requires-Dist: pydantic>=2.14
|
|
15
|
+
Requires-Dist: curl-cffi>=0.16.3
|
|
16
|
+
Requires-Dist: msgpack>=1.2
|
|
17
|
+
Requires-Dist: Pillow>=12.3
|
|
18
|
+
Requires-Dist: numpy>=2.5
|
|
19
|
+
Requires-Dist: pynacl>=1.6
|
|
20
|
+
Requires-Dist: tokenizers>=0.23
|
|
21
|
+
Provides-Extra: server
|
|
22
|
+
Requires-Dist: fastapi>=0.143; extra == "server"
|
|
23
|
+
Requires-Dist: uvicorn[standard]>=0.54; extra == "server"
|
|
24
|
+
Provides-Extra: testing
|
|
25
|
+
Requires-Dist: novelai-python[server]; extra == "testing"
|
|
26
|
+
Requires-Dist: httpx>=0.28; extra == "testing"
|
|
27
|
+
Requires-Dist: pytest>=9.1; extra == "testing"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=1.4; extra == "testing"
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
[](https://badge.fury.io/py/novelai-python)
|
|
36
|
+
[](https://pepy.tech/project/novelai_python)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
>**Declaration** (2025/06/04)
|
|
40
|
+
>
|
|
41
|
+
>**I have nothing to do with NovelAI.**
|
|
42
|
+
>
|
|
43
|
+
>Due to various considerations, such as NovelAI's confusing project architecture and "can't learn anything from it", this project will be in a *non-active* maintenance state. If you have any questions, please *raise an issue*.
|
|
44
|
+
>
|
|
45
|
+
>This project is designed to be a sample project for me to write Python SDK. It turns out that Pydantic is very powerful.
|
|
46
|
+
>
|
|
47
|
+
>*I will invest more time in open source projects that are more beneficial to society.*
|
|
48
|
+
>
|
|
49
|
+
|
|
50
|
+
✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
|
|
51
|
+
|
|
52
|
+
The goal of this repository is to use Pydantic to build legitimate requests to access
|
|
53
|
+
the [NovelAI API service](https://image.novelai.net/docs).
|
|
54
|
+
|
|
55
|
+
> Python >= 3.12 is required (tested on 3.12 – 3.14).
|
|
56
|
+
|
|
57
|
+
### 📰 News
|
|
58
|
+
|
|
59
|
+
- **NovelAI Diffusion V5** family: `nai-diffusion-5-full`, `-full-medium` (fast, 14 steps), `-curated` and their
|
|
60
|
+
`-inpainting` variants, with the web client's quality presets (`standard` / `light` / `none`) and negative
|
|
61
|
+
presets. Every model uses `params_version` 4 (`ucPresetId` / `qualityPresetId`) and the
|
|
62
|
+
`multipart/form-data` body the web client sends.
|
|
63
|
+
- **Streaming**: `async for event in gen.request_stream(session)` on `/ai/generate-image-stream` yields
|
|
64
|
+
intermediate previews and the final image per sample.
|
|
65
|
+
- New: character reference (V4.5), `EncodeVibe`, transparent background (V5), `Upscale` on the image host,
|
|
66
|
+
`UserData`, `TrialStatus`, `Priority`, `CreatePersistentToken`, the OpenAI-compatible text API (`/oa/v1`),
|
|
67
|
+
and `GenerateImageInfer.from_metadata()` to rebuild a request from an image's metadata.
|
|
68
|
+
- Fewer dependencies: pydantic, curl_cffi, msgpack, Pillow, numpy, pynacl and tokenizers. httpx, opencv,
|
|
69
|
+
argon2-cffi, ftfy/regex and `bchlib` are gone (`bchlib` has no Python 3.14 wheel; a NumPy BCH codec
|
|
70
|
+
replaces it).
|
|
71
|
+
- Optional local image budget (`limiter=True`, see below).
|
|
72
|
+
|
|
73
|
+
### ⬆️ Upgrading from 0.7
|
|
74
|
+
|
|
75
|
+
Some changes do not raise an error, so check these when you upgrade:
|
|
76
|
+
|
|
77
|
+
- **`HTTP(S)_PROXY` is ignored.** Sessions use `trust_env=False`, so a system proxy no longer routes NovelAI
|
|
78
|
+
traffic by accident. Pass `proxy="http://user:pass@host:port"` to the credential (`ApiCredential`,
|
|
79
|
+
`LoginCredential`, ...) or to `Login`; tokenizer downloads and tag suggestions read `NOVELAI_PROXY`.
|
|
80
|
+
- **The default model is V5** (`nai-diffusion-5-full`) in `GenerateImageInfer` and `build_generate`.
|
|
81
|
+
- **Images come back as PNG** by default (`image_format`), with the metadata in text chunks; set `parameters.image_format = ImageFormat.WEBP` for smaller files.
|
|
82
|
+
- **`auto_text`** (V5 only, on by default): quoted text in the prompt is appended as a `, teXt: ...` block, as
|
|
83
|
+
novelai.net does. Pass `auto_text=False` to send the prompt untouched.
|
|
84
|
+
- **Image and user endpoints moved to `image.novelai.net`**; `api.novelai.net` rejects third-party clients
|
|
85
|
+
there.
|
|
86
|
+
- **Browser identity**: requests carry the headers of Chrome 155 on Windows, as the web client sends them.
|
|
87
|
+
Accept-Language is English (`en-US,en;q=0.9`) unless the credential says otherwise:
|
|
88
|
+
`ApiCredential(..., accept_language="en-sg")` (see `ACCEPT_LANGUAGES`) or a full header value.
|
|
89
|
+
- **The image budget is off by default.** Pass `limiter=True` to `request()` / `request_stream()` for the
|
|
90
|
+
process-wide budget (`NOVELAI_DAILY_IMAGE_LIMIT`, default 2500 per 24h, saved to
|
|
91
|
+
`~/.novelai_python/image_limit.json`), or a `DailyImageLimiter` of your own. The budget is shared by all
|
|
92
|
+
tokens and by every limiter, in any process, that uses the same state file.
|
|
93
|
+
- **Server: `POST /ai/augment-image` takes the image as the request body** (`application/octet-stream`); the
|
|
94
|
+
other fields stay query parameters. Send `Authorization: Bearer <token>` or the bare token.
|
|
95
|
+
|
|
96
|
+
- [New Model. No Restrictions. V4.5 Full Is Here.](https://blog.novelai.net/novelai-diffusion-v4-5-full-release-678318c86205)
|
|
97
|
+
- [NovelAI Diffusion V4.5 Curated is Here!](https://blog.novelai.net/novelai-diffusion-v4-5-curated-is-here-9e5ba1f628f3)
|
|
98
|
+
- [Image Generation Model Release — NovelAI Anime Diffusion V4 Curated Preview (EN)](https://blog.novelai.net/release-novelai-anime-diffusion-v4-curated-preview-en-ca4b0b11e671)
|
|
99
|
+
- [Tutorial: Creating Consistent Characters with NovelAI Diffusion Anime [Female]](https://blog.novelai.net/tutorial-en-creating-consistent-characters-with-novelai-diffusion-anime-female-538b4b678a4e)
|
|
100
|
+
|
|
101
|
+
### 📦 Usage
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
pip -U install novelai-python
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
All API users must adhere to the NovelAI Terms of Service: https://novelai.net/terms.
|
|
108
|
+
|
|
109
|
+
**More examples can be found in the [playground](https://github.com/LlmKira/novelai-python/tree/main/playground)
|
|
110
|
+
directory, read code as documentation.**
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import asyncio
|
|
114
|
+
import os
|
|
115
|
+
|
|
116
|
+
from pydantic import SecretStr
|
|
117
|
+
|
|
118
|
+
from novelai_python import GenerateImageInfer, ImageGenerateResp, ApiCredential
|
|
119
|
+
from novelai_python.sdk.ai.generate_image import Model, Character, Sampler, UCPreset
|
|
120
|
+
from novelai_python.sdk.ai.generate_image.schema import PositionMap
|
|
121
|
+
|
|
122
|
+
session = ApiCredential(api_token=SecretStr(os.getenv("NOVELAI_JWT"))) # pst-***
|
|
123
|
+
# For security reasons, storing user credentials in plaintext is strongly discouraged.
|
|
124
|
+
|
|
125
|
+
prompt = "2girls, fisheye, closeup, from above"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def main():
|
|
129
|
+
gen = GenerateImageInfer.build_generate(
|
|
130
|
+
prompt=prompt,
|
|
131
|
+
model=Model.NAI_DIFFUSION_5_FULL, # or NAI_DIFFUSION_4_5_FULL
|
|
132
|
+
character_prompts=[
|
|
133
|
+
Character(
|
|
134
|
+
prompt="1girl, head tilt, short hair, black hair, grey eyes, small breasts, looking at viewer",
|
|
135
|
+
uc="red hair",
|
|
136
|
+
center=PositionMap.B2
|
|
137
|
+
),
|
|
138
|
+
Character(
|
|
139
|
+
prompt="1girl, fox ears, fox tail, white hair, white tail, white ears",
|
|
140
|
+
uc="black hair",
|
|
141
|
+
center=PositionMap.D2
|
|
142
|
+
)
|
|
143
|
+
],
|
|
144
|
+
sampler=Sampler.K_EULER_ANCESTRAL,
|
|
145
|
+
ucPreset=UCPreset.TYPE0,
|
|
146
|
+
# Recommended, using preset negative_prompt depends on selected model
|
|
147
|
+
qualityToggle=True,
|
|
148
|
+
decrisp_mode=False,
|
|
149
|
+
variety_boost=True,
|
|
150
|
+
furry_mode=False,
|
|
151
|
+
# Checkbox in novelai.net
|
|
152
|
+
)
|
|
153
|
+
cost = gen.calculate_cost(is_opus=True)
|
|
154
|
+
print(f"charge: {cost} if you are vip3")
|
|
155
|
+
resp = await gen.request(session=session) # raises DailyLimitExceeded once the local budget is spent
|
|
156
|
+
resp: ImageGenerateResp
|
|
157
|
+
print(resp.meta)
|
|
158
|
+
print(resp.save("./output")) # auto-download every returned image
|
|
159
|
+
|
|
160
|
+
# streaming previews (V5 family)
|
|
161
|
+
async for event in gen.request_stream(session=session):
|
|
162
|
+
if event.is_final:
|
|
163
|
+
open(f"./output/sample_{event.samp_ix}.webp", "wb").write(event.image)
|
|
164
|
+
elif event.event_type == "intermediate":
|
|
165
|
+
print("preview", event.samp_ix, "step", event.step_ix, len(event.image), "bytes")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
loop = asyncio.get_event_loop()
|
|
169
|
+
loop.run_until_complete(main())
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### 🔋 Anlas / limit monitoring
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from novelai_python import Subscription, UserData, TrialStatus, get_default_limiter
|
|
177
|
+
|
|
178
|
+
sub = await Subscription().request(session=session)
|
|
179
|
+
print(sub.anlas_left, sub.tier_name, sub.is_unlimited_image_generation)
|
|
180
|
+
|
|
181
|
+
data = await UserData().request(session=session) # subscription + information in one call
|
|
182
|
+
print(data.anlas_left)
|
|
183
|
+
|
|
184
|
+
trial = await TrialStatus().request(session=session)
|
|
185
|
+
print(trial.trialImagesLeft)
|
|
186
|
+
|
|
187
|
+
# only when requests opt in with limiter=True
|
|
188
|
+
print(get_default_limiter().status()) # {'limit': 2500, 'used': ..., 'remaining': ..., 'reset_at': ...}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`sub.usage` is the Opus V5 usage bar: `percent`, `purchasedPercent` (bought extra, 0-300),
|
|
192
|
+
`isNegative`, `timeUntilNextPercent`. `Priority().request(session)` returns the task priority balance;
|
|
193
|
+
`CreatePersistentToken(overwrite=True).request(jwt_session)` creates a `pst-...` API key.
|
|
194
|
+
|
|
195
|
+
#### 🖼️ Transparent background, character reference, vibes
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from novelai_python import CharacterReference, EncodeVibe, GenerateImageInfer
|
|
199
|
+
from novelai_python.sdk.ai._enum import Model
|
|
200
|
+
|
|
201
|
+
# V5: "Transparent BG" of the web client (tag before the quality suffix + straight alpha)
|
|
202
|
+
gen = GenerateImageInfer.build_generate("1girl, standing", transparent_background=True)
|
|
203
|
+
|
|
204
|
+
# V4.5: Precise Reference (character / style / costume / character&style / delta)
|
|
205
|
+
gen = GenerateImageInfer.build_generate(
|
|
206
|
+
"1girl", model=Model.NAI_DIFFUSION_4_5_FULL,
|
|
207
|
+
character_references=[CharacterReference(image=open("ref.png", "rb").read(), fidelity=0.8)],
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# V4 / V4.5 vibe transfer takes encoded vibes: encode once, reuse the result
|
|
211
|
+
vibe = await EncodeVibe(image=open("vibe.png", "rb").read(), information_extracted=0.7).request(session)
|
|
212
|
+
gen = GenerateImageInfer.build_generate(
|
|
213
|
+
"1girl", model=Model.NAI_DIFFUSION_4_5_FULL,
|
|
214
|
+
reference_image_multiple=[vibe.vibe_b64], reference_strength_multiple=[0.6],
|
|
215
|
+
reference_information_extracted_multiple=[0.7],
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
V5 has no vibe transfer and no character reference; it takes up to 32 characters (V4/V4.5: 6).
|
|
220
|
+
|
|
221
|
+
Environment:
|
|
222
|
+
|
|
223
|
+
| variable | default | meaning |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| `NOVELAI_DAILY_IMAGE_LIMIT` | `2500` | images per 24h window, `0` disables |
|
|
226
|
+
| `NOVELAI_LIMIT_STATE_FILE` | `~/.novelai_python/image_limit.json` | where the counter is persisted |
|
|
227
|
+
|
|
228
|
+
#### 📦 LLM
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
import asyncio
|
|
232
|
+
import os
|
|
233
|
+
|
|
234
|
+
from pydantic import SecretStr
|
|
235
|
+
|
|
236
|
+
from novelai_python import APIError, LoginCredential
|
|
237
|
+
from novelai_python.sdk.ai.generate import TextLLMModel, LLM, get_default_preset, AdvanceLLMSetting
|
|
238
|
+
from novelai_python.sdk.ai.generate._enum import get_model_preset
|
|
239
|
+
|
|
240
|
+
username = os.getenv("NOVELAI_USER", None)
|
|
241
|
+
assert username is not None
|
|
242
|
+
# credential = JwtCredential(jwt_token=SecretStr(jwt))
|
|
243
|
+
login_credential = LoginCredential(
|
|
244
|
+
username=os.getenv("NOVELAI_USER"),
|
|
245
|
+
password=SecretStr(os.getenv("NOVELAI_PASS"))
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
async def chat(prompt: str):
|
|
250
|
+
try:
|
|
251
|
+
model = TextLLMModel.ERATO # llama3
|
|
252
|
+
parameters = get_default_preset(model).parameters
|
|
253
|
+
agent = LLM.build(
|
|
254
|
+
prompt=prompt,
|
|
255
|
+
model=model,
|
|
256
|
+
# parameters=None, # Auto Select or get from preset
|
|
257
|
+
parameters=get_model_preset(TextLLMModel.ERATO).get_all_presets()[0].parameters, # Select from enum preset
|
|
258
|
+
advanced_setting=AdvanceLLMSetting(
|
|
259
|
+
min_length=1,
|
|
260
|
+
max_length=None, # Auto
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
# NOTE:parameter > advanced_setting, which logic in generate/__init__.py
|
|
264
|
+
# If you not pass the parameter, it will use the default preset.
|
|
265
|
+
# So if you want to set the generation params, you should pass your own params.
|
|
266
|
+
# Only if you want to use some params not affect the generation, you can use advanced_setting.
|
|
267
|
+
result = await agent.request(session=login_credential)
|
|
268
|
+
except APIError as e:
|
|
269
|
+
raise Exception(f"Error: {e.message}")
|
|
270
|
+
print(f"Result: \n{result.text}")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
loop = asyncio.get_event_loop()
|
|
274
|
+
loop.run_until_complete(chat("Hello"))
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
OpenAI-compatible text API (`text.novelai.net/oa/v1`), responses in OpenAI format:
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from novelai_python import OAIChatCompletion, OAIChatMessage, OAIModels
|
|
281
|
+
|
|
282
|
+
models = await OAIModels().request(session)
|
|
283
|
+
req = OAIChatCompletion(model=models.data[0].id, messages=[OAIChatMessage(role="user", content="Hello")])
|
|
284
|
+
print((await req.request(session))["choices"][0]["message"]["content"])
|
|
285
|
+
async for chunk in req.stream(session):
|
|
286
|
+
print(chunk["choices"][0]["delta"].get("content", ""), end="")
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### 📦 Random Prompt
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from novelai_python.tool.random_prompt import RandomPromptGenerator
|
|
293
|
+
|
|
294
|
+
generator = RandomPromptGenerator()
|
|
295
|
+
for i in range(10):
|
|
296
|
+
print(generator.generate_common_tags(nsfw=False))
|
|
297
|
+
print(generator.generate_scene_tags())
|
|
298
|
+
print(generator.generate_scene_composition())
|
|
299
|
+
print(generator.get_holiday_themed_tags())
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### 📦 Run A Server
|
|
303
|
+
|
|
304
|
+
```shell
|
|
305
|
+
pip install "novelai-python[server]"
|
|
306
|
+
python3 -m novelai_python.server -h '127.0.0.1' -p 7888
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### 📦 Tokenizer
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from novelai_python._enum import get_tokenizer_model, TextLLMModel, TextTokenizerGroup
|
|
313
|
+
from novelai_python.tokenizer import NaiTokenizer
|
|
314
|
+
|
|
315
|
+
# Through llm model name to get the tokenizer
|
|
316
|
+
tokenizer_package = NaiTokenizer(get_tokenizer_model(TextLLMModel.ERATO))
|
|
317
|
+
# Directly use the tokenizer
|
|
318
|
+
clip_tokenizer = NaiTokenizer(TextTokenizerGroup.CLIP)
|
|
319
|
+
|
|
320
|
+
# Tokenize a text
|
|
321
|
+
t_text = "a fox jumped over the lazy dog"
|
|
322
|
+
encode_tokens = tokenizer_package.encode(t_text)
|
|
323
|
+
print(tokenizer_package.tokenize_text(t_text))
|
|
324
|
+
print(f"Tokenized text: {encode_tokens}")
|
|
325
|
+
print(tokenizer_package.decode(tokenizer_package.encode(t_text)))
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 🔨 Roadmap
|
|
330
|
+
|
|
331
|
+
- [x] tool.random_prompt
|
|
332
|
+
- [x] tool.paint_mask
|
|
333
|
+
- [x] tool.image_metadata
|
|
334
|
+
- [x] tokenizer
|
|
335
|
+
- [x] /ai/generate-image
|
|
336
|
+
- [x] /user/subscription
|
|
337
|
+
- [x] /user/login
|
|
338
|
+
- [x] /user/information
|
|
339
|
+
- [x] /ai/upscale
|
|
340
|
+
- [x] /ai/generate-image/suggest-tags
|
|
341
|
+
- [x] /ai/generate-voice
|
|
342
|
+
- [x] /ai/generate-stream
|
|
343
|
+
- [x] /ai/generate
|
|
344
|
+
- [x] /ai/augment-image
|
|
345
|
+
- [ ] /ai/annotate-image
|
|
346
|
+
- [ ] /ai/classify
|
|
347
|
+
- [ ] /ai/generate-prompt
|
|
348
|
+
|
|
349
|
+
> GenerateImageInfer.calculate_cost is correct in most cases, but please request account information to get accurate
|
|
350
|
+
> consumption information.
|
|
351
|
+
|
|
352
|
+
> This repo is maintained by me personally now. If you have any questions, please feel free to open an issue.
|
|
353
|
+
|
|
354
|
+
## 🚫 About Nsfw
|
|
355
|
+
|
|
356
|
+
You might need some solutions for identifying NSFW content and adding a mosaic to prevent operational mishaps.
|
|
357
|
+
|
|
358
|
+
https://dghs-imgutils.deepghs.org/main/api_doc/detect/nudenet.html
|
|
359
|
+
|
|
360
|
+
https://dghs-imgutils.deepghs.org/main/api_doc/operate/censor.html
|
|
361
|
+
|
|
362
|
+
## 🙏 Acknowledgements
|
|
363
|
+
|
|
364
|
+
[novelai-api](https://github.com/Aedial/novelai-api)
|
|
365
|
+
|
|
366
|
+
[NovelAI-API](https://github.com/HanaokaYuzu/NovelAI-API)
|
|
367
|
+
|
|
368
|
+
|
|
@@ -1,41 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: novelai-python
|
|
3
|
-
Version: 0.7.11
|
|
4
|
-
Summary: NovelAI Python Binding With Pydantic
|
|
5
|
-
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
|
-
License: Apache License 2.0
|
|
7
|
-
Project-URL: Repository, https://github.com/LlmKira/novelai-python/
|
|
8
|
-
Project-URL: Issues, https://github.com/LlmKira/novelai-python/issues
|
|
9
|
-
Requires-Python: >=3.9
|
|
10
|
-
Requires-Dist: elara>=0.5.5
|
|
11
|
-
Requires-Dist: loguru>=0.7.2
|
|
12
|
-
Requires-Dist: pydantic-settings>=2.1.0
|
|
13
|
-
Requires-Dist: pydantic>=2.9.0
|
|
14
|
-
Requires-Dist: httpx>=0.26.0
|
|
15
|
-
Requires-Dist: shortuuid>=1.0.11
|
|
16
|
-
Requires-Dist: Pillow>=10.2.0
|
|
17
|
-
Requires-Dist: curl-cffi>=0.11.3
|
|
18
|
-
Requires-Dist: fastapi>=0.109.0
|
|
19
|
-
Requires-Dist: uvicorn[standard]>=0.27.0.post1
|
|
20
|
-
Requires-Dist: numpy>=1.24.4
|
|
21
|
-
Requires-Dist: argon2-cffi>=23.1.0
|
|
22
|
-
Requires-Dist: opencv-python>=4.8.1.78
|
|
23
|
-
Requires-Dist: fake-useragent>=1.4.0
|
|
24
|
-
Requires-Dist: tenacity>=8.2.3
|
|
25
|
-
Requires-Dist: sentencepiece>=0.2.0
|
|
26
|
-
Requires-Dist: pynacl>=1.5.0
|
|
27
|
-
Requires-Dist: ftfy>=6.2.0
|
|
28
|
-
Requires-Dist: regex>=2023.12.25
|
|
29
|
-
Requires-Dist: tokenizers>=0.15.2
|
|
30
|
-
Requires-Dist: json-repair>=0.29.4
|
|
31
|
-
Requires-Dist: robust-downloader>=0.0.2
|
|
32
|
-
Requires-Dist: bchlib>=2.1.3
|
|
33
|
-
Requires-Dist: arrow>=1.3.0
|
|
34
|
-
Provides-Extra: testing
|
|
35
|
-
Requires-Dist: pytest>=7.4.4; extra == "testing"
|
|
36
|
-
Requires-Dist: pytest-asyncio>=0.23.4; extra == "testing"
|
|
37
|
-
Description-Content-Type: text/markdown
|
|
38
|
-
|
|
39
1
|

|
|
40
2
|
|
|
41
3
|
---
|
|
@@ -58,12 +20,49 @@ Description-Content-Type: text/markdown
|
|
|
58
20
|
✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
|
|
59
21
|
|
|
60
22
|
The goal of this repository is to use Pydantic to build legitimate requests to access
|
|
61
|
-
the [NovelAI API service](https://
|
|
23
|
+
the [NovelAI API service](https://image.novelai.net/docs).
|
|
62
24
|
|
|
63
|
-
> Python >= 3.
|
|
25
|
+
> Python >= 3.12 is required (tested on 3.12 – 3.14).
|
|
64
26
|
|
|
65
27
|
### 📰 News
|
|
66
28
|
|
|
29
|
+
- **NovelAI Diffusion V5** family: `nai-diffusion-5-full`, `-full-medium` (fast, 14 steps), `-curated` and their
|
|
30
|
+
`-inpainting` variants, with the web client's quality presets (`standard` / `light` / `none`) and negative
|
|
31
|
+
presets. Every model uses `params_version` 4 (`ucPresetId` / `qualityPresetId`) and the
|
|
32
|
+
`multipart/form-data` body the web client sends.
|
|
33
|
+
- **Streaming**: `async for event in gen.request_stream(session)` on `/ai/generate-image-stream` yields
|
|
34
|
+
intermediate previews and the final image per sample.
|
|
35
|
+
- New: character reference (V4.5), `EncodeVibe`, transparent background (V5), `Upscale` on the image host,
|
|
36
|
+
`UserData`, `TrialStatus`, `Priority`, `CreatePersistentToken`, the OpenAI-compatible text API (`/oa/v1`),
|
|
37
|
+
and `GenerateImageInfer.from_metadata()` to rebuild a request from an image's metadata.
|
|
38
|
+
- Fewer dependencies: pydantic, curl_cffi, msgpack, Pillow, numpy, pynacl and tokenizers. httpx, opencv,
|
|
39
|
+
argon2-cffi, ftfy/regex and `bchlib` are gone (`bchlib` has no Python 3.14 wheel; a NumPy BCH codec
|
|
40
|
+
replaces it).
|
|
41
|
+
- Optional local image budget (`limiter=True`, see below).
|
|
42
|
+
|
|
43
|
+
### ⬆️ Upgrading from 0.7
|
|
44
|
+
|
|
45
|
+
Some changes do not raise an error, so check these when you upgrade:
|
|
46
|
+
|
|
47
|
+
- **`HTTP(S)_PROXY` is ignored.** Sessions use `trust_env=False`, so a system proxy no longer routes NovelAI
|
|
48
|
+
traffic by accident. Pass `proxy="http://user:pass@host:port"` to the credential (`ApiCredential`,
|
|
49
|
+
`LoginCredential`, ...) or to `Login`; tokenizer downloads and tag suggestions read `NOVELAI_PROXY`.
|
|
50
|
+
- **The default model is V5** (`nai-diffusion-5-full`) in `GenerateImageInfer` and `build_generate`.
|
|
51
|
+
- **Images come back as PNG** by default (`image_format`), with the metadata in text chunks; set `parameters.image_format = ImageFormat.WEBP` for smaller files.
|
|
52
|
+
- **`auto_text`** (V5 only, on by default): quoted text in the prompt is appended as a `, teXt: ...` block, as
|
|
53
|
+
novelai.net does. Pass `auto_text=False` to send the prompt untouched.
|
|
54
|
+
- **Image and user endpoints moved to `image.novelai.net`**; `api.novelai.net` rejects third-party clients
|
|
55
|
+
there.
|
|
56
|
+
- **Browser identity**: requests carry the headers of Chrome 155 on Windows, as the web client sends them.
|
|
57
|
+
Accept-Language is English (`en-US,en;q=0.9`) unless the credential says otherwise:
|
|
58
|
+
`ApiCredential(..., accept_language="en-sg")` (see `ACCEPT_LANGUAGES`) or a full header value.
|
|
59
|
+
- **The image budget is off by default.** Pass `limiter=True` to `request()` / `request_stream()` for the
|
|
60
|
+
process-wide budget (`NOVELAI_DAILY_IMAGE_LIMIT`, default 2500 per 24h, saved to
|
|
61
|
+
`~/.novelai_python/image_limit.json`), or a `DailyImageLimiter` of your own. The budget is shared by all
|
|
62
|
+
tokens and by every limiter, in any process, that uses the same state file.
|
|
63
|
+
- **Server: `POST /ai/augment-image` takes the image as the request body** (`application/octet-stream`); the
|
|
64
|
+
other fields stay query parameters. Send `Authorization: Bearer <token>` or the bare token.
|
|
65
|
+
|
|
67
66
|
- [New Model. No Restrictions. V4.5 Full Is Here.](https://blog.novelai.net/novelai-diffusion-v4-5-full-release-678318c86205)
|
|
68
67
|
- [NovelAI Diffusion V4.5 Curated is Here!](https://blog.novelai.net/novelai-diffusion-v4-5-curated-is-here-9e5ba1f628f3)
|
|
69
68
|
- [Image Generation Model Release — NovelAI Anime Diffusion V4 Curated Preview (EN)](https://blog.novelai.net/release-novelai-anime-diffusion-v4-curated-preview-en-ca4b0b11e671)
|
|
@@ -84,14 +83,12 @@ directory, read code as documentation.**
|
|
|
84
83
|
import asyncio
|
|
85
84
|
import os
|
|
86
85
|
|
|
87
|
-
from dotenv import load_dotenv
|
|
88
86
|
from pydantic import SecretStr
|
|
89
87
|
|
|
90
88
|
from novelai_python import GenerateImageInfer, ImageGenerateResp, ApiCredential
|
|
91
89
|
from novelai_python.sdk.ai.generate_image import Model, Character, Sampler, UCPreset
|
|
92
90
|
from novelai_python.sdk.ai.generate_image.schema import PositionMap
|
|
93
91
|
|
|
94
|
-
load_dotenv()
|
|
95
92
|
session = ApiCredential(api_token=SecretStr(os.getenv("NOVELAI_JWT"))) # pst-***
|
|
96
93
|
# For security reasons, storing user credentials in plaintext is strongly discouraged.
|
|
97
94
|
|
|
@@ -101,7 +98,7 @@ prompt = "2girls, fisheye, closeup, from above"
|
|
|
101
98
|
async def main():
|
|
102
99
|
gen = GenerateImageInfer.build_generate(
|
|
103
100
|
prompt=prompt,
|
|
104
|
-
model=Model.NAI_DIFFUSION_4_5_FULL
|
|
101
|
+
model=Model.NAI_DIFFUSION_5_FULL, # or NAI_DIFFUSION_4_5_FULL
|
|
105
102
|
character_prompts=[
|
|
106
103
|
Character(
|
|
107
104
|
prompt="1girl, head tilt, short hair, black hair, grey eyes, small breasts, looking at viewer",
|
|
@@ -125,12 +122,17 @@ async def main():
|
|
|
125
122
|
)
|
|
126
123
|
cost = gen.calculate_cost(is_opus=True)
|
|
127
124
|
print(f"charge: {cost} if you are vip3")
|
|
128
|
-
resp = await gen.request(session=session)
|
|
125
|
+
resp = await gen.request(session=session) # raises DailyLimitExceeded once the local budget is spent
|
|
129
126
|
resp: ImageGenerateResp
|
|
130
127
|
print(resp.meta)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
print(resp.save("./output")) # auto-download every returned image
|
|
129
|
+
|
|
130
|
+
# streaming previews (V5 family)
|
|
131
|
+
async for event in gen.request_stream(session=session):
|
|
132
|
+
if event.is_final:
|
|
133
|
+
open(f"./output/sample_{event.samp_ix}.webp", "wb").write(event.image)
|
|
134
|
+
elif event.event_type == "intermediate":
|
|
135
|
+
print("preview", event.samp_ix, "step", event.step_ix, len(event.image), "bytes")
|
|
134
136
|
|
|
135
137
|
|
|
136
138
|
loop = asyncio.get_event_loop()
|
|
@@ -138,20 +140,73 @@ loop.run_until_complete(main())
|
|
|
138
140
|
|
|
139
141
|
```
|
|
140
142
|
|
|
143
|
+
#### 🔋 Anlas / limit monitoring
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from novelai_python import Subscription, UserData, TrialStatus, get_default_limiter
|
|
147
|
+
|
|
148
|
+
sub = await Subscription().request(session=session)
|
|
149
|
+
print(sub.anlas_left, sub.tier_name, sub.is_unlimited_image_generation)
|
|
150
|
+
|
|
151
|
+
data = await UserData().request(session=session) # subscription + information in one call
|
|
152
|
+
print(data.anlas_left)
|
|
153
|
+
|
|
154
|
+
trial = await TrialStatus().request(session=session)
|
|
155
|
+
print(trial.trialImagesLeft)
|
|
156
|
+
|
|
157
|
+
# only when requests opt in with limiter=True
|
|
158
|
+
print(get_default_limiter().status()) # {'limit': 2500, 'used': ..., 'remaining': ..., 'reset_at': ...}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`sub.usage` is the Opus V5 usage bar: `percent`, `purchasedPercent` (bought extra, 0-300),
|
|
162
|
+
`isNegative`, `timeUntilNextPercent`. `Priority().request(session)` returns the task priority balance;
|
|
163
|
+
`CreatePersistentToken(overwrite=True).request(jwt_session)` creates a `pst-...` API key.
|
|
164
|
+
|
|
165
|
+
#### 🖼️ Transparent background, character reference, vibes
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from novelai_python import CharacterReference, EncodeVibe, GenerateImageInfer
|
|
169
|
+
from novelai_python.sdk.ai._enum import Model
|
|
170
|
+
|
|
171
|
+
# V5: "Transparent BG" of the web client (tag before the quality suffix + straight alpha)
|
|
172
|
+
gen = GenerateImageInfer.build_generate("1girl, standing", transparent_background=True)
|
|
173
|
+
|
|
174
|
+
# V4.5: Precise Reference (character / style / costume / character&style / delta)
|
|
175
|
+
gen = GenerateImageInfer.build_generate(
|
|
176
|
+
"1girl", model=Model.NAI_DIFFUSION_4_5_FULL,
|
|
177
|
+
character_references=[CharacterReference(image=open("ref.png", "rb").read(), fidelity=0.8)],
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# V4 / V4.5 vibe transfer takes encoded vibes: encode once, reuse the result
|
|
181
|
+
vibe = await EncodeVibe(image=open("vibe.png", "rb").read(), information_extracted=0.7).request(session)
|
|
182
|
+
gen = GenerateImageInfer.build_generate(
|
|
183
|
+
"1girl", model=Model.NAI_DIFFUSION_4_5_FULL,
|
|
184
|
+
reference_image_multiple=[vibe.vibe_b64], reference_strength_multiple=[0.6],
|
|
185
|
+
reference_information_extracted_multiple=[0.7],
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
V5 has no vibe transfer and no character reference; it takes up to 32 characters (V4/V4.5: 6).
|
|
190
|
+
|
|
191
|
+
Environment:
|
|
192
|
+
|
|
193
|
+
| variable | default | meaning |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `NOVELAI_DAILY_IMAGE_LIMIT` | `2500` | images per 24h window, `0` disables |
|
|
196
|
+
| `NOVELAI_LIMIT_STATE_FILE` | `~/.novelai_python/image_limit.json` | where the counter is persisted |
|
|
197
|
+
|
|
141
198
|
#### 📦 LLM
|
|
142
199
|
|
|
143
200
|
```python
|
|
144
201
|
import asyncio
|
|
145
202
|
import os
|
|
146
203
|
|
|
147
|
-
from dotenv import load_dotenv
|
|
148
204
|
from pydantic import SecretStr
|
|
149
205
|
|
|
150
206
|
from novelai_python import APIError, LoginCredential
|
|
151
207
|
from novelai_python.sdk.ai.generate import TextLLMModel, LLM, get_default_preset, AdvanceLLMSetting
|
|
152
208
|
from novelai_python.sdk.ai.generate._enum import get_model_preset
|
|
153
209
|
|
|
154
|
-
load_dotenv()
|
|
155
210
|
username = os.getenv("NOVELAI_USER", None)
|
|
156
211
|
assert username is not None
|
|
157
212
|
# credential = JwtCredential(jwt_token=SecretStr(jwt))
|
|
@@ -189,6 +244,18 @@ loop = asyncio.get_event_loop()
|
|
|
189
244
|
loop.run_until_complete(chat("Hello"))
|
|
190
245
|
```
|
|
191
246
|
|
|
247
|
+
OpenAI-compatible text API (`text.novelai.net/oa/v1`), responses in OpenAI format:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from novelai_python import OAIChatCompletion, OAIChatMessage, OAIModels
|
|
251
|
+
|
|
252
|
+
models = await OAIModels().request(session)
|
|
253
|
+
req = OAIChatCompletion(model=models.data[0].id, messages=[OAIChatMessage(role="user", content="Hello")])
|
|
254
|
+
print((await req.request(session))["choices"][0]["message"]["content"])
|
|
255
|
+
async for chunk in req.stream(session):
|
|
256
|
+
print(chunk["choices"][0]["delta"].get("content", ""), end="")
|
|
257
|
+
```
|
|
258
|
+
|
|
192
259
|
#### 📦 Random Prompt
|
|
193
260
|
|
|
194
261
|
```python
|
|
@@ -205,7 +272,7 @@ for i in range(10):
|
|
|
205
272
|
#### 📦 Run A Server
|
|
206
273
|
|
|
207
274
|
```shell
|
|
208
|
-
pip install
|
|
275
|
+
pip install "novelai-python[server]"
|
|
209
276
|
python3 -m novelai_python.server -h '127.0.0.1' -p 7888
|
|
210
277
|
```
|
|
211
278
|
|