novelai-python 0.3.3__tar.gz → 0.3.5__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.3 → novelai_python-0.3.5}/LICENSE +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/NOTICE.MD +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/PKG-INFO +5 -1
- {novelai_python-0.3.3 → novelai_python-0.3.5}/README.md +2 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/pyproject.toml +3 -1
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_exceptions.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/ai/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/ai/generate_image.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/ai/upscale.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/schema.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/information.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/login.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/subscription.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/credential/ApiToken.py +3 -3
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/credential/JwtToken.py +3 -3
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/credential/UserAuth.py +3 -3
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/credential/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/credential/_base.py +3 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/LICENSE +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/generate_image/__init__.py +65 -24
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/generate_image/_enum.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +1 -2
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/upscale.py +8 -2
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/schema.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/user/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/user/information.py +1 -1
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/user/login.py +1 -1
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/user/subscription.py +1 -1
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/server.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/encode.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/hash.py +5 -4
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/random_prompt/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/random_prompt/tag.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/random_prompt/tag_character.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/random_prompt/tag_nsfw.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/useful.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/__init__.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_random_prompt.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_server.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_server_run.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_upscale.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_user_information.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_user_login.py +0 -0
- {novelai_python-0.3.3 → novelai_python-0.3.5}/tests/test_user_subscription.py +0 -0
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: novelai-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Novelai Python Binding With Pydantic
|
|
5
5
|
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -20,6 +20,8 @@ Requires-Dist: uvicorn[standard]>=0.27.0.post1
|
|
|
20
20
|
Requires-Dist: numpy>=1.24.4
|
|
21
21
|
Requires-Dist: argon2-cffi>=23.1.0
|
|
22
22
|
Requires-Dist: opencv-python>=4.8.1.78
|
|
23
|
+
Requires-Dist: fake-useragent>=1.4.0
|
|
24
|
+
Requires-Dist: tenacity>=8.2.3
|
|
23
25
|
Requires-Dist: pytest>=7.4.4; extra == "testing"
|
|
24
26
|
Requires-Dist: pytest-asyncio>=0.23.4; extra == "testing"
|
|
25
27
|
Provides-Extra: testing
|
|
@@ -34,6 +36,8 @@ The goal of this repository is to use Pydantic to build legitimate requests to a
|
|
|
34
36
|
|
|
35
37
|
### Roadmap 🚧
|
|
36
38
|
|
|
39
|
+
- [x] utils.NovelAiMetadata
|
|
40
|
+
- [x] utils.random_prompt
|
|
37
41
|
- [x] /ai/generate-image
|
|
38
42
|
- [x] /user/subscription
|
|
39
43
|
- [x] /user/login
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "novelai-python"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.5"
|
|
4
4
|
description = "Novelai Python Binding With Pydantic"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "sudoskys", email = "coldlando@hotmail.com" },
|
|
@@ -19,6 +19,8 @@ dependencies = [
|
|
|
19
19
|
"numpy>=1.24.4",
|
|
20
20
|
"argon2-cffi>=23.1.0",
|
|
21
21
|
"opencv-python>=4.8.1.78",
|
|
22
|
+
"fake-useragent>=1.4.0",
|
|
23
|
+
"tenacity>=8.2.3",
|
|
22
24
|
]
|
|
23
25
|
requires-python = ">=3.8"
|
|
24
26
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/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.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/information.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/_response/user/subscription.py
RENAMED
|
File without changes
|
|
@@ -7,7 +7,7 @@ from curl_cffi.requests import AsyncSession
|
|
|
7
7
|
from loguru import logger
|
|
8
8
|
from pydantic import SecretStr, Field, field_validator
|
|
9
9
|
|
|
10
|
-
from ._base import CredentialBase
|
|
10
|
+
from ._base import CredentialBase, FAKE_UA
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class ApiCredential(CredentialBase):
|
|
@@ -25,12 +25,12 @@ class ApiCredential(CredentialBase):
|
|
|
25
25
|
"Accept": "*/*",
|
|
26
26
|
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
|
|
27
27
|
"Accept-Encoding": "gzip, deflate, br",
|
|
28
|
-
"User-Agent":
|
|
28
|
+
"User-Agent": FAKE_UA.edge,
|
|
29
29
|
"Authorization": f"Bearer {self.api_token.get_secret_value()}",
|
|
30
30
|
"Content-Type": "application/json",
|
|
31
31
|
"Origin": "https://novelai.net",
|
|
32
32
|
"Referer": "https://novelai.net/",
|
|
33
|
-
}, impersonate="
|
|
33
|
+
}, impersonate="edge101")
|
|
34
34
|
self._session.headers.update(update_headers)
|
|
35
35
|
return self._session
|
|
36
36
|
|
|
@@ -7,7 +7,7 @@ from curl_cffi.requests import AsyncSession
|
|
|
7
7
|
from loguru import logger
|
|
8
8
|
from pydantic import SecretStr, Field, field_validator
|
|
9
9
|
|
|
10
|
-
from ._base import CredentialBase
|
|
10
|
+
from ._base import CredentialBase, FAKE_UA
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class JwtCredential(CredentialBase):
|
|
@@ -24,13 +24,13 @@ class JwtCredential(CredentialBase):
|
|
|
24
24
|
self._session = AsyncSession(timeout=timeout, headers={
|
|
25
25
|
"Accept": "*/*",
|
|
26
26
|
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
|
|
27
|
-
"User-Agent":
|
|
27
|
+
"User-Agent": FAKE_UA.edge,
|
|
28
28
|
"Accept-Encoding": "gzip, deflate, br",
|
|
29
29
|
"Authorization": f"Bearer {self.jwt_token.get_secret_value()}",
|
|
30
30
|
"Content-Type": "application/json",
|
|
31
31
|
"Origin": "https://novelai.net",
|
|
32
32
|
"Referer": "https://novelai.net/",
|
|
33
|
-
}, impersonate="
|
|
33
|
+
}, impersonate="edge101")
|
|
34
34
|
assert isinstance(update_headers, dict), "update_headers must be a dict"
|
|
35
35
|
self._session.headers.update(update_headers)
|
|
36
36
|
return self._session
|
|
@@ -9,7 +9,7 @@ from typing import Optional
|
|
|
9
9
|
from curl_cffi.requests import AsyncSession
|
|
10
10
|
from pydantic import SecretStr, Field
|
|
11
11
|
|
|
12
|
-
from ._base import CredentialBase
|
|
12
|
+
from ._base import CredentialBase, FAKE_UA
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class LoginCredential(CredentialBase):
|
|
@@ -30,14 +30,14 @@ class LoginCredential(CredentialBase):
|
|
|
30
30
|
resp = await Login.build(user_name=self.username, password=self.password.get_secret_value()).request()
|
|
31
31
|
self._session = AsyncSession(timeout=timeout, headers={
|
|
32
32
|
"Accept": "*/*",
|
|
33
|
-
"User-Agent":
|
|
33
|
+
"User-Agent": FAKE_UA.edge,
|
|
34
34
|
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
|
|
35
35
|
"Accept-Encoding": "gzip, deflate, br",
|
|
36
36
|
"Authorization": f"Bearer {resp.accessToken}",
|
|
37
37
|
"Content-Type": "application/json",
|
|
38
38
|
"Origin": "https://novelai.net",
|
|
39
39
|
"Referer": "https://novelai.net/",
|
|
40
|
-
}, impersonate="
|
|
40
|
+
}, impersonate="edge101")
|
|
41
41
|
self._update_at = int(time.time())
|
|
42
42
|
self._session.headers.update(update_headers)
|
|
43
43
|
return self._session
|
|
File without changes
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
# @File : _shema.py
|
|
5
5
|
# @Software: PyCharm
|
|
6
6
|
from curl_cffi.requests import AsyncSession
|
|
7
|
+
from fake_useragent import UserAgent
|
|
7
8
|
from pydantic import BaseModel
|
|
8
9
|
|
|
10
|
+
FAKE_UA = UserAgent()
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
class CredentialBase(BaseModel):
|
|
11
14
|
_session: AsyncSession = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/generate_image/__init__.py
RENAMED
|
@@ -18,6 +18,7 @@ import httpx
|
|
|
18
18
|
from curl_cffi.requests import AsyncSession
|
|
19
19
|
from loguru import logger
|
|
20
20
|
from pydantic import BaseModel, ConfigDict, PrivateAttr, field_validator, model_validator, Field
|
|
21
|
+
from tenacity import retry, stop_after_attempt, wait_random, retry_if_exception
|
|
21
22
|
from typing_extensions import override
|
|
22
23
|
|
|
23
24
|
from ._enum import Model, Sampler, NoiseSchedule, ControlNetModel, Action, UCPreset
|
|
@@ -33,7 +34,7 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
33
34
|
|
|
34
35
|
class Params(BaseModel):
|
|
35
36
|
# Inpaint
|
|
36
|
-
add_original_image: Optional[bool] = False
|
|
37
|
+
add_original_image: Optional[bool] = False # FIXME: 未知作用
|
|
37
38
|
mask: Optional[Union[str, bytes]] = None # img2img,base64
|
|
38
39
|
|
|
39
40
|
cfg_rescale: Optional[float] = Field(0, ge=0, le=1, multiple_of=0.02)
|
|
@@ -56,6 +57,20 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
56
57
|
|
|
57
58
|
# Misc
|
|
58
59
|
params_version: Optional[int] = 1
|
|
60
|
+
reference_image: Optional[Union[str, bytes]] = None
|
|
61
|
+
reference_information_extracted: Optional[float] = Field(1,
|
|
62
|
+
ge=0,
|
|
63
|
+
le=1,
|
|
64
|
+
multiple_of=0.01,
|
|
65
|
+
description="extracting concepts or features"
|
|
66
|
+
)
|
|
67
|
+
reference_strength: Optional[float] = Field(0.6,
|
|
68
|
+
ge=0,
|
|
69
|
+
le=1,
|
|
70
|
+
multiple_of=0.01,
|
|
71
|
+
description="the stronger the AI will try to emulate visual cues."
|
|
72
|
+
)
|
|
73
|
+
|
|
59
74
|
legacy: Optional[bool] = False
|
|
60
75
|
legacy_v3_extend: Optional[bool] = False
|
|
61
76
|
|
|
@@ -83,14 +98,6 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
83
98
|
"""Undesired Content Strength"""
|
|
84
99
|
width: Optional[int] = Field(832, ge=64, le=49152)
|
|
85
100
|
|
|
86
|
-
@model_validator(mode="after")
|
|
87
|
-
def validate_img2img(self):
|
|
88
|
-
image = True if self.image else False
|
|
89
|
-
add_origin = True if self.add_original_image else False
|
|
90
|
-
if image != add_origin:
|
|
91
|
-
raise ValueError('Invalid Model Params For img2img2 mode... image should match add_original_image!')
|
|
92
|
-
return self
|
|
93
|
-
|
|
94
101
|
@field_validator('mask')
|
|
95
102
|
def mask_validator(cls, v: Union[str, bytes]):
|
|
96
103
|
if isinstance(v, str) and v.startswith("data:image/"):
|
|
@@ -107,6 +114,14 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
107
114
|
return base64.b64encode(v).decode("utf-8")
|
|
108
115
|
return v
|
|
109
116
|
|
|
117
|
+
@field_validator('reference_image')
|
|
118
|
+
def image_validator(cls, v: Union[str, bytes]):
|
|
119
|
+
if isinstance(v, str) and v.startswith("data:image/"):
|
|
120
|
+
raise ValueError("Invalid image format, must be base64 encoded.")
|
|
121
|
+
if isinstance(v, bytes):
|
|
122
|
+
return base64.b64encode(v).decode("utf-8")
|
|
123
|
+
return v
|
|
124
|
+
|
|
110
125
|
@field_validator('width')
|
|
111
126
|
def width_validator(cls, v: int):
|
|
112
127
|
"""
|
|
@@ -175,10 +190,20 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
175
190
|
|
|
176
191
|
@model_validator(mode="after")
|
|
177
192
|
def validate_model(self):
|
|
178
|
-
if self.action == Action.INFILL
|
|
179
|
-
|
|
193
|
+
if self.action == Action.INFILL:
|
|
194
|
+
if not self.parameters.mask:
|
|
195
|
+
logger.warning("Mask maybe required for infill mode.")
|
|
180
196
|
if self.action != Action.GENERATE:
|
|
181
197
|
self.parameters.extra_noise_seed = self.parameters.seed
|
|
198
|
+
if self.action == Action.IMG2IMG:
|
|
199
|
+
self.parameters.sm = False
|
|
200
|
+
self.parameters.sm_dyn = False
|
|
201
|
+
if not self.parameters.image:
|
|
202
|
+
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.")
|
|
182
207
|
return self
|
|
183
208
|
|
|
184
209
|
@property
|
|
@@ -243,11 +268,13 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
243
268
|
qualityToggle: bool = True,
|
|
244
269
|
ucPreset: Union[UCPreset, int] = UCPreset.TYPE0,
|
|
245
270
|
image: Union[str, bytes] = None,
|
|
271
|
+
reference_mode: bool = False,
|
|
246
272
|
add_original_image: bool = None,
|
|
247
273
|
strength: float = None,
|
|
248
274
|
mask: Union[str, bytes] = None,
|
|
249
275
|
controlnet_model: Union[ControlNetModel, str] = None,
|
|
250
276
|
controlnet_condition: str = None,
|
|
277
|
+
sm_dyn: bool = False,
|
|
251
278
|
**kwargs
|
|
252
279
|
):
|
|
253
280
|
"""
|
|
@@ -260,17 +287,19 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
260
287
|
:param negative_prompt: The content of negative prompt
|
|
261
288
|
:param seed: The seed for generate image
|
|
262
289
|
:param steps: The steps for generate image
|
|
263
|
-
:param scale:
|
|
290
|
+
:param scale: The scale for generate image
|
|
264
291
|
:param cfg_rescale: Prompt Guidance Rescale 0-1 lower is more creative
|
|
265
292
|
:param sampler: The sampler for generate image
|
|
266
293
|
:param width: 宽
|
|
267
294
|
:param height: 高
|
|
268
295
|
:param image: 图片
|
|
296
|
+
:param reference_mode: 是否是参考模式
|
|
269
297
|
:param add_original_image: 是否添加原始图片
|
|
270
298
|
:param strength: IMG2IMG 强度
|
|
271
299
|
:param mask: Inpainting mask
|
|
272
300
|
:param controlnet_model: 控制网络模型
|
|
273
301
|
:param controlnet_condition: 控制网络条件
|
|
302
|
+
:param sm_dyn: 是否使用动态sm
|
|
274
303
|
:return: self
|
|
275
304
|
"""
|
|
276
305
|
assert isinstance(prompt, str)
|
|
@@ -286,13 +315,22 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
286
315
|
"height": height,
|
|
287
316
|
"qualityToggle": qualityToggle,
|
|
288
317
|
"ucPreset": ucPreset,
|
|
289
|
-
"image": image,
|
|
290
318
|
"add_original_image": add_original_image,
|
|
291
|
-
"strength": strength,
|
|
292
319
|
"mask": mask,
|
|
293
320
|
"controlnet_model": controlnet_model,
|
|
294
|
-
"controlnet_condition": controlnet_condition
|
|
321
|
+
"controlnet_condition": controlnet_condition,
|
|
322
|
+
"sm_dyn": sm_dyn,
|
|
295
323
|
})
|
|
324
|
+
if reference_mode:
|
|
325
|
+
kwargs.update({
|
|
326
|
+
"reference_image": image,
|
|
327
|
+
"reference_strength": strength,
|
|
328
|
+
})
|
|
329
|
+
else:
|
|
330
|
+
kwargs.update({
|
|
331
|
+
"image": image,
|
|
332
|
+
"strength": strength,
|
|
333
|
+
})
|
|
296
334
|
# 清理空值
|
|
297
335
|
param = {k: v for k, v in kwargs.items() if v is not None}
|
|
298
336
|
return cls(
|
|
@@ -322,9 +360,14 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
322
360
|
"Sec-Fetch-Site": "same-site",
|
|
323
361
|
"Pragma": "no-cache",
|
|
324
362
|
"Cache-Control": "no-cache",
|
|
325
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
|
326
363
|
}
|
|
327
364
|
|
|
365
|
+
@retry(
|
|
366
|
+
wait=wait_random(min=1, max=3),
|
|
367
|
+
stop=stop_after_attempt(3),
|
|
368
|
+
retry=retry_if_exception(lambda e: hasattr(e, "code") and str(e.code) == "500"),
|
|
369
|
+
reraise=True
|
|
370
|
+
)
|
|
328
371
|
async def request(self,
|
|
329
372
|
session: Union[AsyncSession, "CredentialBase"],
|
|
330
373
|
*,
|
|
@@ -352,17 +395,15 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
352
395
|
try:
|
|
353
396
|
_log_data = deepcopy(request_data)
|
|
354
397
|
if self.action == Action.GENERATE:
|
|
398
|
+
_log_data.get("parameters", {}).update({
|
|
399
|
+
"reference_image": "base64 data" if self.parameters.reference_image else None,
|
|
400
|
+
})
|
|
355
401
|
logger.debug(f"Request Data: {_log_data}")
|
|
356
402
|
else:
|
|
357
403
|
_log_data.get("parameters", {}).update({
|
|
358
404
|
"image": "base64 data" if self.parameters.image else None,
|
|
359
|
-
|
|
360
|
-
)
|
|
361
|
-
_log_data.get("parameters", {}).update(
|
|
362
|
-
{
|
|
363
|
-
"mask": "base64 data" if self.parameters.mask else None,
|
|
364
|
-
}
|
|
365
|
-
)
|
|
405
|
+
"mask": "base64 data" if self.parameters.mask else None,
|
|
406
|
+
})
|
|
366
407
|
logger.debug(f"Request Data: {_log_data}")
|
|
367
408
|
del _log_data
|
|
368
409
|
except Exception as e:
|
|
@@ -374,7 +415,7 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
374
415
|
data=json.dumps(request_data).encode("utf-8")
|
|
375
416
|
)
|
|
376
417
|
if response.headers.get('Content-Type') not in ['binary/octet-stream', 'application/x-zip-compressed']:
|
|
377
|
-
logger.
|
|
418
|
+
logger.warning(
|
|
378
419
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
379
420
|
)
|
|
380
421
|
try:
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/sdk/ai/generate_image/_enum.py
RENAMED
|
File without changes
|
|
@@ -41,7 +41,6 @@ class SuggestTags(ApiBaseModel):
|
|
|
41
41
|
return {
|
|
42
42
|
"Host": urlparse(self.endpoint).netloc,
|
|
43
43
|
"Accept": "*/*",
|
|
44
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
|
45
44
|
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
|
|
46
45
|
"Accept-Encoding": "gzip, deflate, br",
|
|
47
46
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -85,7 +84,7 @@ class SuggestTags(ApiBaseModel):
|
|
|
85
84
|
url=self.base_url + "?" + "&".join([f"{k}={v}" for k, v in request_data.items()])
|
|
86
85
|
)
|
|
87
86
|
if "application/json" not in response.headers.get('Content-Type') or response.status_code != 200:
|
|
88
|
-
logger.
|
|
87
|
+
logger.warning(
|
|
89
88
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
90
89
|
)
|
|
91
90
|
try:
|
|
@@ -15,6 +15,7 @@ import httpx
|
|
|
15
15
|
from curl_cffi.requests import AsyncSession
|
|
16
16
|
from loguru import logger
|
|
17
17
|
from pydantic import ConfigDict, PrivateAttr, model_validator
|
|
18
|
+
from tenacity import wait_random, retry, stop_after_attempt, retry_if_exception
|
|
18
19
|
|
|
19
20
|
from ..schema import ApiBaseModel
|
|
20
21
|
from ..._exceptions import APIError, AuthError, SessionHttpError
|
|
@@ -73,7 +74,6 @@ class Upscale(ApiBaseModel):
|
|
|
73
74
|
return {
|
|
74
75
|
"Host": urlparse(self.endpoint).netloc,
|
|
75
76
|
"Accept": "*/*",
|
|
76
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
|
77
77
|
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
|
|
78
78
|
"Accept-Encoding": "gzip, deflate, br",
|
|
79
79
|
"Referer": "https://novelai.net/",
|
|
@@ -88,6 +88,12 @@ class Upscale(ApiBaseModel):
|
|
|
88
88
|
"Cache-Control": "no-cache",
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
@retry(
|
|
92
|
+
wait=wait_random(min=1, max=3),
|
|
93
|
+
stop=stop_after_attempt(3),
|
|
94
|
+
retry=retry_if_exception(lambda e: hasattr(e, "code") and str(e.code) == "500"),
|
|
95
|
+
reraise=True
|
|
96
|
+
)
|
|
91
97
|
async def request(self,
|
|
92
98
|
session: Union[AsyncSession, "CredentialBase"],
|
|
93
99
|
*,
|
|
@@ -125,7 +131,7 @@ class Upscale(ApiBaseModel):
|
|
|
125
131
|
data=json.dumps(request_data).encode("utf-8")
|
|
126
132
|
)
|
|
127
133
|
if response.headers.get('Content-Type') not in ['binary/octet-stream', 'application/x-zip-compressed']:
|
|
128
|
-
logger.
|
|
134
|
+
logger.warning(
|
|
129
135
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
130
136
|
)
|
|
131
137
|
try:
|
|
File without changes
|
|
File without changes
|
|
@@ -73,7 +73,7 @@ class Information(ApiBaseModel):
|
|
|
73
73
|
self.base_url
|
|
74
74
|
)
|
|
75
75
|
if "application/json" not in response.headers.get('Content-Type') or response.status_code != 200:
|
|
76
|
-
logger.
|
|
76
|
+
logger.warning(
|
|
77
77
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
78
78
|
)
|
|
79
79
|
try:
|
|
@@ -92,7 +92,7 @@ class Login(ApiBaseModel):
|
|
|
92
92
|
data=json.dumps(request_data).encode("utf-8")
|
|
93
93
|
)
|
|
94
94
|
if "application/json" not in response.headers.get('Content-Type') or response.status_code != 201:
|
|
95
|
-
logger.
|
|
95
|
+
logger.warning(
|
|
96
96
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
97
97
|
)
|
|
98
98
|
try:
|
|
@@ -80,7 +80,7 @@ class Subscription(ApiBaseModel):
|
|
|
80
80
|
url=self.base_url,
|
|
81
81
|
)
|
|
82
82
|
if "application/json" not in response.headers.get('Content-Type') or response.status_code != 200:
|
|
83
|
-
logger.
|
|
83
|
+
logger.warning(
|
|
84
84
|
f"Error with content type: {response.headers.get('Content-Type')} and code: {response.status_code}"
|
|
85
85
|
)
|
|
86
86
|
try:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -74,17 +74,18 @@ class NovelAiMetadata(BaseModel):
|
|
|
74
74
|
@classmethod
|
|
75
75
|
def build_from_img(cls, image_io):
|
|
76
76
|
with Image.open(image_io) as img:
|
|
77
|
-
title = img.info.get("Title")
|
|
78
|
-
if
|
|
77
|
+
title = img.info.get("Title", "Empty")
|
|
78
|
+
if title != 'AI generated image':
|
|
79
79
|
raise ValueError("Not a NaiPic")
|
|
80
80
|
prompt = img.info.get("Description")
|
|
81
|
-
comment = img.info.get("Comment")
|
|
81
|
+
comment = img.info.get("Comment", None)
|
|
82
|
+
assert isinstance(comment, str), ValueError("Comment Empty")
|
|
82
83
|
try:
|
|
83
84
|
comment = json.loads(comment)
|
|
84
85
|
except Exception as e:
|
|
85
86
|
logger.debug(e)
|
|
86
87
|
comment = {}
|
|
87
|
-
|
|
88
|
+
return cls(title=title, description=prompt, comment=comment)
|
|
88
89
|
|
|
89
90
|
@classmethod
|
|
90
91
|
def build_from_param(cls, prompt, neg_prompt, **kwargs):
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/src/novelai_python/utils/random_prompt/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.3.3 → novelai_python-0.3.5}/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
|