novelai-python 0.3.5__tar.gz → 0.3.7__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.3.5 → novelai_python-0.3.7}/PKG-INFO +2 -2
- {novelai_python-0.3.5 → novelai_python-0.3.7}/pyproject.toml +2 -2
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/generate_image/__init__.py +250 -104
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/generate_image/_enum.py +8 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/LICENSE +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/NOTICE.MD +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/README.md +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_exceptions.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/ai/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/ai/generate_image.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/ai/upscale.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/schema.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/information.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/login.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/subscription.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/credential/ApiToken.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/credential/JwtToken.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/credential/UserAuth.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/credential/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/credential/_base.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/LICENSE +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/upscale.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/schema.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/user/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/user/information.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/user/login.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/user/subscription.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/server.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/encode.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/hash.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/tag.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/tag_character.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/tag_nsfw.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/useful.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/__init__.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_random_prompt.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_server.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_server_run.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_upscale.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_user_information.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_user_login.py +0 -0
- {novelai_python-0.3.5 → novelai_python-0.3.7}/tests/test_user_subscription.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: novelai-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: Novelai Python Binding With Pydantic
|
|
5
5
|
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -14,7 +14,7 @@ Requires-Dist: pydantic>=2.5.3
|
|
|
14
14
|
Requires-Dist: httpx>=0.26.0
|
|
15
15
|
Requires-Dist: shortuuid>=1.0.11
|
|
16
16
|
Requires-Dist: Pillow>=10.2.0
|
|
17
|
-
Requires-Dist: curl-cffi>=0.
|
|
17
|
+
Requires-Dist: curl-cffi>=0.6.0
|
|
18
18
|
Requires-Dist: fastapi>=0.109.0
|
|
19
19
|
Requires-Dist: uvicorn[standard]>=0.27.0.post1
|
|
20
20
|
Requires-Dist: numpy>=1.24.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "novelai-python"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.7"
|
|
4
4
|
description = "Novelai Python Binding With Pydantic"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "sudoskys", email = "coldlando@hotmail.com" },
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
"httpx>=0.26.0",
|
|
14
14
|
"shortuuid>=1.0.11",
|
|
15
15
|
"Pillow>=10.2.0",
|
|
16
|
-
"curl-cffi>=0.
|
|
16
|
+
"curl-cffi>=0.6.0",
|
|
17
17
|
"fastapi>=0.109.0",
|
|
18
18
|
"uvicorn[standard]>=0.27.0.post1",
|
|
19
19
|
"numpy>=1.24.4",
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/generate_image/__init__.py
RENAMED
|
@@ -21,61 +21,79 @@ from pydantic import BaseModel, ConfigDict, PrivateAttr, field_validator, model_
|
|
|
21
21
|
from tenacity import retry, stop_after_attempt, wait_random, retry_if_exception
|
|
22
22
|
from typing_extensions import override
|
|
23
23
|
|
|
24
|
-
from ._enum import Model, Sampler, NoiseSchedule, ControlNetModel, Action, UCPreset
|
|
24
|
+
from ._enum import Model, Sampler, NoiseSchedule, ControlNetModel, Action, UCPreset, INPAINTING_MODEL_LIST
|
|
25
25
|
from ...schema import ApiBaseModel
|
|
26
26
|
from ...._exceptions import APIError, AuthError, ConcurrentGenerationError, SessionHttpError
|
|
27
27
|
from ...._response.ai.generate_image import ImageGenerateResp
|
|
28
28
|
from ....credential import CredentialBase
|
|
29
29
|
from ....utils import try_jsonfy, NovelAiMetadata
|
|
30
|
+
from PIL import Image
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
class GenerateImageInfer(ApiBaseModel):
|
|
33
|
-
_endpoint: Optional[str] = PrivateAttr("https://
|
|
34
|
+
_endpoint: Optional[str] = PrivateAttr("https://image.novelai.net")
|
|
34
35
|
|
|
35
36
|
class Params(BaseModel):
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
add_original_image: Optional[bool] = Field(True, description="Overlay Original Image")
|
|
38
|
+
"""
|
|
39
|
+
Overlay Original Image.Prevents the existing image from changing,
|
|
40
|
+
but can introduce seams along the edge of the mask.
|
|
41
|
+
叠加原始图像
|
|
42
|
+
防止现有图像发生更改,但可能会沿蒙版边缘引入接缝。
|
|
43
|
+
"""
|
|
44
|
+
mask: Optional[Union[str, bytes]] = None
|
|
45
|
+
"""Mask for Inpainting"""
|
|
40
46
|
cfg_rescale: Optional[float] = Field(0, ge=0, le=1, multiple_of=0.02)
|
|
41
47
|
"""Prompt Guidance Rescale"""
|
|
42
48
|
controlnet_strength: Optional[float] = Field(1.0, ge=0.1, le=2, multiple_of=0.1)
|
|
43
49
|
"""ControlNet Strength"""
|
|
44
50
|
dynamic_thresholding: Optional[bool] = False
|
|
45
|
-
"""Reduce artifacts caused by high prompt guidance values
|
|
51
|
+
"""Reduce artifacts caused by high prompt guidance values"""
|
|
46
52
|
height: Optional[int] = Field(1216, ge=64, le=49152)
|
|
47
|
-
|
|
48
|
-
image: Optional[Union[str, bytes]] = None
|
|
53
|
+
"""Height For Generate Image"""
|
|
54
|
+
image: Optional[Union[str, bytes]] = None
|
|
55
|
+
"""Image for img2img"""
|
|
49
56
|
strength: Optional[float] = Field(default=0.5, ge=0.01, le=0.99, multiple_of=0.01)
|
|
57
|
+
"""Strength for img2img"""
|
|
50
58
|
noise: Optional[float] = Field(default=0, ge=0, le=0.99, multiple_of=0.01)
|
|
59
|
+
"""Noise for img2img"""
|
|
51
60
|
controlnet_condition: Optional[str] = None
|
|
61
|
+
"""ControlNet Condition"""
|
|
52
62
|
controlnet_model: Optional[ControlNetModel] = None
|
|
53
|
-
|
|
63
|
+
"""ControlNet Model"""
|
|
54
64
|
n_samples: Optional[int] = Field(1, ge=1, le=8)
|
|
65
|
+
"""Number of samples"""
|
|
55
66
|
negative_prompt: Optional[str] = ''
|
|
67
|
+
"""Negative Prompt"""
|
|
56
68
|
noise_schedule: Optional[NoiseSchedule] = NoiseSchedule.NATIVE
|
|
57
|
-
|
|
69
|
+
"""Noise Schedule"""
|
|
58
70
|
# Misc
|
|
59
71
|
params_version: Optional[int] = 1
|
|
72
|
+
"""Params Version For Request"""
|
|
60
73
|
reference_image: Optional[Union[str, bytes]] = None
|
|
74
|
+
"""Reference Image For Vibe Mode"""
|
|
61
75
|
reference_information_extracted: Optional[float] = Field(1,
|
|
62
76
|
ge=0,
|
|
63
77
|
le=1,
|
|
64
78
|
multiple_of=0.01,
|
|
65
79
|
description="extracting concepts or features"
|
|
66
80
|
)
|
|
81
|
+
"""Reference Information Extracted For Vibe Mode"""
|
|
67
82
|
reference_strength: Optional[float] = Field(0.6,
|
|
68
83
|
ge=0,
|
|
69
84
|
le=1,
|
|
70
85
|
multiple_of=0.01,
|
|
71
86
|
description="the stronger the AI will try to emulate visual cues."
|
|
72
87
|
)
|
|
73
|
-
|
|
88
|
+
"""Reference Strength For Vibe Mode"""
|
|
74
89
|
legacy: Optional[bool] = False
|
|
90
|
+
# TODO: find out the usage
|
|
75
91
|
legacy_v3_extend: Optional[bool] = False
|
|
76
|
-
|
|
92
|
+
# TODO: find out the usage
|
|
77
93
|
qualityToggle: Optional[bool] = True
|
|
94
|
+
"""Whether to add the quality prompt"""
|
|
78
95
|
sampler: Optional[Sampler] = Sampler.K_EULER
|
|
96
|
+
"""Sampler For Generate Image"""
|
|
79
97
|
scale: Optional[float] = Field(6.0, ge=0, le=10, multiple_of=0.1)
|
|
80
98
|
"""Prompt Guidance"""
|
|
81
99
|
# Seed
|
|
@@ -84,50 +102,114 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
84
102
|
gt=0,
|
|
85
103
|
le=4294967295 - 7,
|
|
86
104
|
)
|
|
105
|
+
"""Seed"""
|
|
87
106
|
extra_noise_seed: Optional[int] = Field(
|
|
88
107
|
default_factory=lambda: random.randint(0, 4294967295 - 7),
|
|
89
108
|
gt=0,
|
|
90
109
|
le=4294967295 - 7,
|
|
91
110
|
)
|
|
92
|
-
|
|
111
|
+
"""Extra Noise Seed"""
|
|
93
112
|
sm: Optional[bool] = False
|
|
113
|
+
# TODO: find out the usage
|
|
94
114
|
sm_dyn: Optional[bool] = False
|
|
115
|
+
# TODO: find out the usage
|
|
95
116
|
steps: Optional[int] = Field(28, ge=1, le=50)
|
|
117
|
+
"""Steps"""
|
|
96
118
|
ucPreset: Optional[UCPreset] = 0
|
|
119
|
+
"""The Negative Prompt Preset"""
|
|
97
120
|
uncond_scale: Optional[float] = Field(1.0, ge=0, le=1.5, multiple_of=0.05)
|
|
98
121
|
"""Undesired Content Strength"""
|
|
99
122
|
width: Optional[int] = Field(832, ge=64, le=49152)
|
|
123
|
+
"""Width For Image"""
|
|
100
124
|
|
|
101
|
-
@
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
@staticmethod
|
|
126
|
+
def resize_image(image: Union[str, bytes], width: int, height: int):
|
|
127
|
+
"""
|
|
128
|
+
Resize the image to the specified size.
|
|
129
|
+
:param image: The image to be resized
|
|
130
|
+
:param width: The width of the image
|
|
131
|
+
:param height: The height of the image
|
|
132
|
+
:return: The resized image
|
|
133
|
+
"""
|
|
134
|
+
if isinstance(image, str):
|
|
135
|
+
image = base64.b64decode(image)
|
|
136
|
+
open_image = Image.open(BytesIO(image))
|
|
137
|
+
# 如果尺寸相同,直接返回
|
|
138
|
+
if open_image.width == width and open_image.height == height:
|
|
139
|
+
logger.debug("Image size is same, return directly.")
|
|
140
|
+
if isinstance(image, bytes):
|
|
141
|
+
return base64.b64encode(image).decode("utf-8")
|
|
142
|
+
return image
|
|
143
|
+
open_image = open_image.resize((width, height), Image.Resampling.BICUBIC)
|
|
144
|
+
buffered = BytesIO()
|
|
145
|
+
open_image.save(buffered, format="PNG")
|
|
146
|
+
return base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
108
147
|
|
|
109
|
-
@
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
148
|
+
@staticmethod
|
|
149
|
+
def add_image_to_black_background(image: Union[str, bytes], width: int = 448, height: int = 448):
|
|
150
|
+
"""
|
|
151
|
+
将图像缩放到一个指定的黑背景上,使其最大,比例不变。
|
|
152
|
+
:param image: 图像
|
|
153
|
+
:param width: 宽
|
|
154
|
+
:param height: 高
|
|
155
|
+
:return: 新图像
|
|
156
|
+
"""
|
|
157
|
+
if isinstance(image, str):
|
|
158
|
+
image = base64.b64decode(image)
|
|
116
159
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
open_image = Image.open(BytesIO(image))
|
|
161
|
+
# 如果尺寸相同,直接返回
|
|
162
|
+
if open_image.width == width and open_image.height == height:
|
|
163
|
+
return base64.b64encode(image).decode("utf-8")
|
|
164
|
+
|
|
165
|
+
# 计算正确的缩放因子
|
|
166
|
+
width_ratio = width / open_image.width
|
|
167
|
+
height_ratio = height / open_image.height
|
|
168
|
+
ratio = min(width_ratio, height_ratio)
|
|
169
|
+
|
|
170
|
+
new_image_size = (int(open_image.width * ratio), int(open_image.height * ratio))
|
|
171
|
+
open_image = open_image.resize(new_image_size, Image.Resampling.BICUBIC)
|
|
172
|
+
|
|
173
|
+
# 创建一个黑色背景的新图像
|
|
174
|
+
new_image = Image.new("RGB", (width, height), (0, 0, 0))
|
|
175
|
+
|
|
176
|
+
# 计算居中位置
|
|
177
|
+
position = ((width - open_image.width) // 2, (height - open_image.height) // 2)
|
|
178
|
+
|
|
179
|
+
# 粘贴图像
|
|
180
|
+
new_image.paste(open_image, position)
|
|
181
|
+
|
|
182
|
+
buffered = BytesIO()
|
|
183
|
+
new_image.save(buffered, format="PNG")
|
|
184
|
+
return base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
185
|
+
|
|
186
|
+
# Validators
|
|
187
|
+
@model_validator(mode="after")
|
|
188
|
+
def image_validator(self):
|
|
189
|
+
if isinstance(self.image, str) and self.image.startswith("data:"):
|
|
190
|
+
raise ValueError("Invalid `image` format, must be base64 encoded directly.")
|
|
191
|
+
if isinstance(self.reference_image, str) and self.reference_image.startswith("data:"):
|
|
192
|
+
raise ValueError("Invalid `reference_image` format, must be base64 encoded directly.")
|
|
193
|
+
if isinstance(self.mask, str) and self.mask.startswith("data:"):
|
|
194
|
+
raise ValueError("Invalid `mask` format, must be base64 encoded directly.")
|
|
195
|
+
if isinstance(self.image, bytes):
|
|
196
|
+
self.image = base64.b64encode(self.image).decode("utf-8")
|
|
197
|
+
if isinstance(self.reference_image, bytes):
|
|
198
|
+
self.reference_image = base64.b64encode(self.reference_image).decode("utf-8")
|
|
199
|
+
if isinstance(self.mask, bytes):
|
|
200
|
+
self.mask = base64.b64encode(self.mask).decode("utf-8")
|
|
201
|
+
if self.image is not None:
|
|
202
|
+
self.image = self.resize_image(self.image, self.width, self.height)
|
|
203
|
+
if self.reference_image is not None:
|
|
204
|
+
self.reference_image = self.add_image_to_black_background(self.reference_image, width=448, height=448)
|
|
205
|
+
return self
|
|
124
206
|
|
|
125
207
|
@field_validator('width')
|
|
126
208
|
def width_validator(cls, v: int):
|
|
127
209
|
"""
|
|
128
|
-
|
|
210
|
+
Must be multiple of 64
|
|
129
211
|
:param v:
|
|
130
|
-
:return:
|
|
212
|
+
:return: fixed value
|
|
131
213
|
"""
|
|
132
214
|
if v % 64 != 0:
|
|
133
215
|
raise ValueError("Invalid width, must be multiple of 64.")
|
|
@@ -136,9 +218,9 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
136
218
|
@field_validator('height')
|
|
137
219
|
def height_validator(cls, v: int):
|
|
138
220
|
"""
|
|
139
|
-
|
|
221
|
+
Must be multiple of 64
|
|
140
222
|
:param v:
|
|
141
|
-
:return:
|
|
223
|
+
:return: fixed value
|
|
142
224
|
"""
|
|
143
225
|
if v % 64 != 0:
|
|
144
226
|
raise ValueError("Invalid height, must be multiple of 64.")
|
|
@@ -162,48 +244,70 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
162
244
|
def model_post_init(self, *args) -> None:
|
|
163
245
|
"""
|
|
164
246
|
Post-initialization hook.
|
|
165
|
-
|
|
247
|
+
Simulate website logic to add negative prompt based on ucPreset and qualityToggle.
|
|
248
|
+
:param args: Any
|
|
249
|
+
:return: None
|
|
166
250
|
"""
|
|
251
|
+
if self.parameters.negative_prompt is None:
|
|
252
|
+
self.parameters.negative_prompt = ""
|
|
167
253
|
if self.parameters.ucPreset == 0:
|
|
168
254
|
# 0: 重型
|
|
169
|
-
self.parameters.negative_prompt
|
|
170
|
-
"
|
|
255
|
+
self.parameters.negative_prompt = (
|
|
256
|
+
"lowres, {bad}, error, fewer, extra, missing, worst quality, jpeg artifacts, bad quality, "
|
|
171
257
|
"watermark, unfinished, displeasing, chromatic aberration, signature, extra digits, artistic error, "
|
|
172
258
|
"username, scan, [abstract], bad anatomy, bad hands, @_@, mismatched pupils, heart-shaped pupils, "
|
|
173
259
|
"glowing eyes, nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, "
|
|
174
260
|
"extra digit, fewer digits, cropped, worst quality, low quality, normal quality, "
|
|
175
|
-
"jpeg artifacts, signature, watermark, username, blurry"
|
|
261
|
+
f"jpeg artifacts, signature, watermark, username, blurry "
|
|
262
|
+
f",{self.parameters.negative_prompt}"
|
|
176
263
|
)
|
|
177
264
|
elif self.parameters.ucPreset == 1:
|
|
178
265
|
# 1: 轻型
|
|
179
|
-
self.parameters.negative_prompt
|
|
180
|
-
|
|
266
|
+
self.parameters.negative_prompt = (f"lowres, jpeg artifacts, worst quality, watermark, blurry, "
|
|
267
|
+
f"very displeasing"
|
|
268
|
+
f",{self.parameters.negative_prompt}")
|
|
181
269
|
elif self.parameters.ucPreset == 2:
|
|
182
270
|
# 2: 人物
|
|
183
|
-
self.parameters.negative_prompt
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
271
|
+
self.parameters.negative_prompt = ("lowres, {bad}, error, fewer, extra, missing, worst quality, "
|
|
272
|
+
"jpeg artifacts, bad quality, watermark, unfinished, displeasing, "
|
|
273
|
+
"chromatic aberration, signature, extra digits, artistic error, "
|
|
274
|
+
"username, scan, [abstract], bad anatomy, bad hands, @_@, mismatched "
|
|
275
|
+
f"pupils, heart-shaped pupils, glowing eyes "
|
|
276
|
+
f",{self.parameters.negative_prompt}")
|
|
188
277
|
if self.parameters.qualityToggle:
|
|
189
278
|
self.input += ", best quality, amazing quality, very aesthetic, absurdres"
|
|
190
279
|
|
|
191
280
|
@model_validator(mode="after")
|
|
192
281
|
def validate_model(self):
|
|
282
|
+
"""
|
|
283
|
+
Check the conflict between parameters and simulate website logic.
|
|
284
|
+
:return: self
|
|
285
|
+
"""
|
|
286
|
+
# Text2Img mode
|
|
287
|
+
if self.action == Action.GENERATE:
|
|
288
|
+
if self.parameters.image is not None:
|
|
289
|
+
raise ValueError("image is not required for non-generate mode.")
|
|
290
|
+
if self.parameters.mask is not None:
|
|
291
|
+
raise ValueError("mask is not required for non-generate mode.")
|
|
292
|
+
# Infill mode
|
|
193
293
|
if self.action == Action.INFILL:
|
|
294
|
+
if self.model not in INPAINTING_MODEL_LIST:
|
|
295
|
+
raise ValueError(f"You must use {INPAINTING_MODEL_LIST}")
|
|
194
296
|
if not self.parameters.mask:
|
|
195
|
-
logger.warning("Mask maybe required for infill mode
|
|
297
|
+
logger.warning("Mask maybe required for infill mode!")
|
|
298
|
+
# Sync seed
|
|
196
299
|
if self.action != Action.GENERATE:
|
|
197
300
|
self.parameters.extra_noise_seed = self.parameters.seed
|
|
301
|
+
# Img2Img mode
|
|
198
302
|
if self.action == Action.IMG2IMG:
|
|
303
|
+
if self.parameters.sm_dyn is True:
|
|
304
|
+
logger.warning("sm_dyn is disabled when sm in Img2Img mode.")
|
|
305
|
+
if self.parameters.sm is True:
|
|
306
|
+
logger.warning("sm is disabled when sm_dyn in Img2Img mode.")
|
|
199
307
|
self.parameters.sm = False
|
|
200
308
|
self.parameters.sm_dyn = False
|
|
201
|
-
if
|
|
309
|
+
if self.parameters.image is None:
|
|
202
310
|
raise ValueError("image is must required for img2img mode.")
|
|
203
|
-
if not self.parameters.add_original_image:
|
|
204
|
-
raise ValueError("add_original_image is must required for img2img mode.")
|
|
205
|
-
if self.parameters.image and self.parameters.reference_image:
|
|
206
|
-
logger.warning("image and reference_image should not be used together.")
|
|
207
311
|
return self
|
|
208
312
|
|
|
209
313
|
@property
|
|
@@ -258,49 +362,90 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
258
362
|
model: Union[Model, str] = "nai-diffusion-3",
|
|
259
363
|
action: Union[Action, str] = 'generate',
|
|
260
364
|
negative_prompt: str = "",
|
|
261
|
-
|
|
365
|
+
ucPreset: Union[UCPreset, int] = UCPreset.TYPE0,
|
|
262
366
|
steps: int = 28,
|
|
263
|
-
|
|
367
|
+
seed: int = None,
|
|
368
|
+
scale: float = 5.0,
|
|
264
369
|
cfg_rescale: float = 0,
|
|
265
|
-
sampler: Union[Sampler, str] =
|
|
370
|
+
sampler: Union[Sampler, str] = None,
|
|
266
371
|
width: int = 832,
|
|
267
372
|
height: int = 1216,
|
|
268
|
-
qualityToggle: bool =
|
|
269
|
-
ucPreset: Union[UCPreset, int] = UCPreset.TYPE0,
|
|
373
|
+
qualityToggle: bool = None,
|
|
270
374
|
image: Union[str, bytes] = None,
|
|
271
|
-
|
|
272
|
-
|
|
375
|
+
noise: float = 0,
|
|
376
|
+
reference_image: Union[str, bytes] = None,
|
|
377
|
+
reference_strength: float = None,
|
|
378
|
+
reference_information_extracted: float = None,
|
|
379
|
+
add_original_image: bool = True,
|
|
273
380
|
strength: float = None,
|
|
274
381
|
mask: Union[str, bytes] = None,
|
|
275
382
|
controlnet_model: Union[ControlNetModel, str] = None,
|
|
276
383
|
controlnet_condition: str = None,
|
|
384
|
+
sm: bool = False,
|
|
277
385
|
sm_dyn: bool = False,
|
|
386
|
+
uncond_scale: float = None,
|
|
278
387
|
**kwargs
|
|
279
388
|
):
|
|
280
389
|
"""
|
|
281
|
-
The build
|
|
282
|
-
|
|
283
|
-
:param
|
|
284
|
-
|
|
285
|
-
:param
|
|
286
|
-
|
|
287
|
-
:param
|
|
288
|
-
|
|
289
|
-
:param
|
|
290
|
-
|
|
291
|
-
:param
|
|
292
|
-
|
|
293
|
-
:param
|
|
294
|
-
|
|
295
|
-
:param
|
|
296
|
-
|
|
297
|
-
:param
|
|
298
|
-
|
|
299
|
-
:param
|
|
300
|
-
|
|
301
|
-
:param
|
|
302
|
-
|
|
390
|
+
The build function, more convenient to create a GenerateImageInfer instance.
|
|
391
|
+
构建函数,更方便地创建一个GenerateImageInfer实例。
|
|
392
|
+
:param ucPreset: 0: Heavy, 1: Light, 2: Character
|
|
393
|
+
0: 重量级, 1: 轻量级, 2: 角色
|
|
394
|
+
:param qualityToggle: add the quality prompt or not.
|
|
395
|
+
是否添加质量提示。
|
|
396
|
+
:param prompt: input prompt for generation.
|
|
397
|
+
生成的输入提示。
|
|
398
|
+
:param model: select a model.
|
|
399
|
+
选择一个模型。
|
|
400
|
+
:param action: mode for image generation [generate, img2img, infill].
|
|
401
|
+
图像生成模式 [生成, 图像到图像, 填充]。
|
|
402
|
+
:param negative_prompt: the content of negative prompt.
|
|
403
|
+
负面提示的内容。
|
|
404
|
+
:param seed: the seed for generate image.
|
|
405
|
+
生成图像的种子。
|
|
406
|
+
:param steps: the steps for generate image.
|
|
407
|
+
生成图像的步骤。
|
|
408
|
+
:param scale: the scale for generate image.
|
|
409
|
+
生成图像的比例。
|
|
410
|
+
:param cfg_rescale: prompt guidance rescale 0-1 lower is more creative.
|
|
411
|
+
提示引导的重新缩放0-1,越低则越富有创造性。
|
|
412
|
+
:param sampler: the sampler for generate image.
|
|
413
|
+
生成图像的采样器。
|
|
414
|
+
:param width: the width of the image.
|
|
415
|
+
图像的宽度。
|
|
416
|
+
:param height: the height of the image.
|
|
417
|
+
图像的高度。
|
|
418
|
+
:param image: the input image.
|
|
419
|
+
输入图像。
|
|
420
|
+
:param noise: the noise to be added in the image.
|
|
421
|
+
要添加到图像中的噪声。
|
|
422
|
+
:param reference_image: the reference image for Vibe mode.
|
|
423
|
+
Vibe模式的参考图像。
|
|
424
|
+
:param reference_strength: the strength of the reference image used in Vibe mode.
|
|
425
|
+
在Vibe模式中使用的参考图像的强度。
|
|
426
|
+
:param reference_information_extracted: whether to extract concept or features, for Vibe mode.
|
|
427
|
+
是否提取概念或特征,用于Vibe模式。
|
|
428
|
+
:param add_original_image: Overlay Original Image. Prevents the existing image from changing, only for IMG2IMG mode.
|
|
429
|
+
叠加原始图像。防止现有图像发生更改,仅适用于IMG2IMG模式。
|
|
430
|
+
:param strength: the strength of IMG2IMG mode.
|
|
431
|
+
IMG2IMG模式的强度。
|
|
432
|
+
:param mask: the inpainting mask.
|
|
433
|
+
纹理绘制掩码。
|
|
434
|
+
:param controlnet_model: the model used for the control network.
|
|
435
|
+
用于控制网络的模型。
|
|
436
|
+
:param controlnet_condition: the condition used for the control network.
|
|
437
|
+
用于控制网络的条件。
|
|
438
|
+
:param sm: whether to use sm.
|
|
439
|
+
是否使用sm。
|
|
440
|
+
:param sm_dyn: whether to use dynamic sm.
|
|
441
|
+
是否使用动态sm。
|
|
442
|
+
:param uncond_scale: the strength of the unrelated content.
|
|
443
|
+
无关内容的强度。
|
|
444
|
+
:param kwargs: any additional parameters.
|
|
445
|
+
其他任何参数。
|
|
446
|
+
|
|
303
447
|
:return: self
|
|
448
|
+
返回:自身
|
|
304
449
|
"""
|
|
305
450
|
assert isinstance(prompt, str)
|
|
306
451
|
_negative_prompt = negative_prompt
|
|
@@ -320,17 +465,23 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
320
465
|
"controlnet_model": controlnet_model,
|
|
321
466
|
"controlnet_condition": controlnet_condition,
|
|
322
467
|
"sm_dyn": sm_dyn,
|
|
468
|
+
"sm": sm,
|
|
469
|
+
"uncond_scale": uncond_scale,
|
|
323
470
|
})
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
"reference_image":
|
|
327
|
-
"reference_strength":
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
471
|
+
kwargs.update(
|
|
472
|
+
{
|
|
473
|
+
"reference_image": reference_image,
|
|
474
|
+
"reference_strength": reference_strength,
|
|
475
|
+
"reference_information_extracted": reference_information_extracted,
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
kwargs.update(
|
|
479
|
+
{
|
|
331
480
|
"image": image,
|
|
332
481
|
"strength": strength,
|
|
333
|
-
|
|
482
|
+
"noise": noise,
|
|
483
|
+
}
|
|
484
|
+
)
|
|
334
485
|
# 清理空值
|
|
335
486
|
param = {k: v for k, v in kwargs.items() if v is not None}
|
|
336
487
|
return cls(
|
|
@@ -394,17 +545,12 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
394
545
|
session.headers.update(override_headers)
|
|
395
546
|
try:
|
|
396
547
|
_log_data = deepcopy(request_data)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
_log_data.get("parameters", {}).update({
|
|
404
|
-
"image": "base64 data" if self.parameters.image else None,
|
|
405
|
-
"mask": "base64 data" if self.parameters.mask else None,
|
|
406
|
-
})
|
|
407
|
-
logger.debug(f"Request Data: {_log_data}")
|
|
548
|
+
_log_data.get("parameters", {}).update({
|
|
549
|
+
"image": "base64 data" if self.parameters.image else None,
|
|
550
|
+
"mask": "base64 data" if self.parameters.mask else None,
|
|
551
|
+
"reference_image": "base64 data" if self.parameters.reference_image else None,
|
|
552
|
+
})
|
|
553
|
+
logger.debug(f"Request Data: {_log_data}")
|
|
408
554
|
del _log_data
|
|
409
555
|
except Exception as e:
|
|
410
556
|
logger.warning(f"Error when print log data: {e}")
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/sdk/ai/generate_image/_enum.py
RENAMED
|
@@ -64,6 +64,7 @@ class Resolution(Enum):
|
|
|
64
64
|
RES_1088_1920 = (1088, 1920)
|
|
65
65
|
RES_1920_1088 = (1920, 1088)
|
|
66
66
|
|
|
67
|
+
|
|
67
68
|
class Model(Enum):
|
|
68
69
|
NAI_DIFFUSION_3 = "nai-diffusion-3"
|
|
69
70
|
NAI_DIFFUSION_3_INPAINTING = "nai-diffusion-3-inpainting"
|
|
@@ -76,3 +77,10 @@ class Model(Enum):
|
|
|
76
77
|
|
|
77
78
|
NAI_DIFFUSION_FURRY = "nai-diffusion-furry"
|
|
78
79
|
FURRY_DIFFUSION_INPAINTING = "furry-diffusion-inpainting"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
INPAINTING_MODEL_LIST = [Model.NAI_DIFFUSION_3_INPAINTING,
|
|
83
|
+
Model.NAI_DIFFUSION_INPAINTING,
|
|
84
|
+
Model.SAFE_DIFFUSION_INPAINTING,
|
|
85
|
+
Model.FURRY_DIFFUSION_INPAINTING
|
|
86
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/ai/generate_image.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/information.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/_response/user/subscription.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.5 → novelai_python-0.3.7}/src/novelai_python/utils/random_prompt/tag_nsfw.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|