novelai-python 0.7.9__tar.gz → 0.7.11__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.
Files changed (82) hide show
  1. {novelai_python-0.7.9 → novelai_python-0.7.11}/PKG-INFO +18 -4
  2. {novelai_python-0.7.9 → novelai_python-0.7.11}/README.md +16 -2
  3. {novelai_python-0.7.9 → novelai_python-0.7.11}/pyproject.toml +2 -2
  4. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/credential/ApiToken.py +3 -8
  5. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/credential/JwtToken.py +3 -8
  6. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/credential/UserAuth.py +2 -7
  7. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_enum.py +135 -41
  8. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/augment_image/__init__.py +0 -23
  9. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/augment_image/_enum.py +2 -1
  10. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/__init__.py +0 -17
  11. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/__init__.py +21 -31
  12. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/params.py +61 -51
  13. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/schema.py +2 -2
  14. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +2 -21
  15. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_stream.py +0 -1
  16. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_voice/__init__.py +0 -22
  17. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/upscale.py +0 -23
  18. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/schema.py +0 -4
  19. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/user/information.py +0 -12
  20. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/user/login.py +0 -4
  21. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/user/subscription.py +0 -19
  22. {novelai_python-0.7.9 → novelai_python-0.7.11}/LICENSE +0 -0
  23. {novelai_python-0.7.9 → novelai_python-0.7.11}/NOTICE.MD +0 -0
  24. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/__init__.py +0 -0
  25. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_enum.py +0 -0
  26. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_exceptions.py +0 -0
  27. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/__init__.py +0 -0
  28. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/__init__.py +0 -0
  29. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate.py +0 -0
  30. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_image.py +0 -0
  31. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_stream.py +0 -0
  32. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_voice.py +0 -0
  33. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/ai/upscale.py +0 -0
  34. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/schema.py +0 -0
  35. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/user/__init__.py +0 -0
  36. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/user/information.py +0 -0
  37. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/user/login.py +0 -0
  38. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/_response/user/subscription.py +0 -0
  39. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/credential/__init__.py +0 -0
  40. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/credential/_base.py +0 -0
  41. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/__init__.py +0 -0
  42. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/LICENSE +0 -0
  43. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/__init__.py +0 -0
  44. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_const.py +0 -0
  45. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_cost.py +0 -0
  46. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_const.py +0 -0
  47. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_enum.py +0 -0
  48. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_schema.py +0 -0
  49. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/themes.json +0 -0
  50. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/tokenizer.py +0 -0
  51. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_voice/_enum.py +0 -0
  52. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/sdk/user/__init__.py +0 -0
  53. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/server.py +0 -0
  54. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/__init__.py +0 -0
  55. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/clip_simple_tokenizer.py +0 -0
  56. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/nerdstash_v1.model +0 -0
  57. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/nerdstash_v2.model +0 -0
  58. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/novelai.model +0 -0
  59. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tokenizer/novelai_v2.model +0 -0
  60. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/__init__.py +0 -0
  61. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/__init__.py +0 -0
  62. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/bch_utils.py +0 -0
  63. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/lsb_extractor.py +0 -0
  64. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/lsb_injector.py +0 -0
  65. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/paint_mask/__init__.py +0 -0
  66. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/__init__.py +0 -0
  67. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_scene_composition.py +0 -0
  68. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_scene_tags.py +0 -0
  69. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_tags.py +0 -0
  70. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/utils/__init__.py +0 -0
  71. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/utils/encode.py +0 -0
  72. {novelai_python-0.7.9 → novelai_python-0.7.11}/src/novelai_python/utils/useful.py +0 -0
  73. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/__init__.py +0 -0
  74. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_generate_voice.py +0 -0
  75. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_random_prompt.py +0 -0
  76. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_server.py +0 -0
  77. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_server_run.py +0 -0
  78. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_tokenizer.py +0 -0
  79. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_upscale.py +0 -0
  80. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_user_information.py +0 -0
  81. {novelai_python-0.7.9 → novelai_python-0.7.11}/tests/test_user_login.py +0 -0
  82. {novelai_python-0.7.9 → novelai_python-0.7.11}/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.7.9
3
+ Version: 0.7.11
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.9.0
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.9.0
17
+ Requires-Dist: curl-cffi>=0.11.3
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
@@ -43,15 +43,28 @@ Description-Content-Type: text/markdown
43
43
  [![PyPI version](https://badge.fury.io/py/novelai-python.svg)](https://badge.fury.io/py/novelai-python)
44
44
  [![Downloads](https://pepy.tech/badge/novelai_python)](https://pepy.tech/project/novelai_python)
45
45
 
46
+
47
+ >**Declaration** (2025/06/04)
48
+ >
49
+ >**I have nothing to do with NovelAI.**
50
+ >
51
+ >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*.
52
+ >
53
+ >This project is designed to be a sample project for me to write Python SDK. It turns out that Pydantic is very powerful.
54
+ >
55
+ >*I will invest more time in open source projects that are more beneficial to society.*
56
+ >
57
+
46
58
  ✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
47
59
 
48
60
  The goal of this repository is to use Pydantic to build legitimate requests to access
49
61
  the [NovelAI API service](https://api.novelai.net/docs).
50
62
 
51
- > Python >= 3.9 is required.
63
+ > Python >= 3.9 <3.13 is required.
52
64
 
53
65
  ### 📰 News
54
66
 
67
+ - [New Model. No Restrictions. V4.5 Full Is Here.](https://blog.novelai.net/novelai-diffusion-v4-5-full-release-678318c86205)
55
68
  - [NovelAI Diffusion V4.5 Curated is Here!](https://blog.novelai.net/novelai-diffusion-v4-5-curated-is-here-9e5ba1f628f3)
56
69
  - [Image Generation Model Release — NovelAI Anime Diffusion V4 Curated Preview (EN)](https://blog.novelai.net/release-novelai-anime-diffusion-v4-curated-preview-en-ca4b0b11e671)
57
70
  - [Tutorial: Creating Consistent Characters with NovelAI Diffusion Anime [Female]](https://blog.novelai.net/tutorial-en-creating-consistent-characters-with-novelai-diffusion-anime-female-538b4b678a4e)
@@ -88,7 +101,7 @@ prompt = "2girls, fisheye, closeup, from above"
88
101
  async def main():
89
102
  gen = GenerateImageInfer.build_generate(
90
103
  prompt=prompt,
91
- model=Model.NAI_DIFFUSION_4_5_CURATED,
104
+ model=Model.NAI_DIFFUSION_4_5_FULL,
92
105
  character_prompts=[
93
106
  Character(
94
107
  prompt="1girl, head tilt, short hair, black hair, grey eyes, small breasts, looking at viewer",
@@ -107,6 +120,7 @@ async def main():
107
120
  qualityToggle=True,
108
121
  decrisp_mode=False,
109
122
  variety_boost=True,
123
+ furry_mode=False,
110
124
  # Checkbox in novelai.net
111
125
  )
112
126
  cost = gen.calculate_cost(is_opus=True)
@@ -5,15 +5,28 @@
5
5
  [![PyPI version](https://badge.fury.io/py/novelai-python.svg)](https://badge.fury.io/py/novelai-python)
6
6
  [![Downloads](https://pepy.tech/badge/novelai_python)](https://pepy.tech/project/novelai_python)
7
7
 
8
+
9
+ >**Declaration** (2025/06/04)
10
+ >
11
+ >**I have nothing to do with NovelAI.**
12
+ >
13
+ >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*.
14
+ >
15
+ >This project is designed to be a sample project for me to write Python SDK. It turns out that Pydantic is very powerful.
16
+ >
17
+ >*I will invest more time in open source projects that are more beneficial to society.*
18
+ >
19
+
8
20
  ✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
9
21
 
10
22
  The goal of this repository is to use Pydantic to build legitimate requests to access
11
23
  the [NovelAI API service](https://api.novelai.net/docs).
12
24
 
13
- > Python >= 3.9 is required.
25
+ > Python >= 3.9 <3.13 is required.
14
26
 
15
27
  ### 📰 News
16
28
 
29
+ - [New Model. No Restrictions. V4.5 Full Is Here.](https://blog.novelai.net/novelai-diffusion-v4-5-full-release-678318c86205)
17
30
  - [NovelAI Diffusion V4.5 Curated is Here!](https://blog.novelai.net/novelai-diffusion-v4-5-curated-is-here-9e5ba1f628f3)
18
31
  - [Image Generation Model Release — NovelAI Anime Diffusion V4 Curated Preview (EN)](https://blog.novelai.net/release-novelai-anime-diffusion-v4-curated-preview-en-ca4b0b11e671)
19
32
  - [Tutorial: Creating Consistent Characters with NovelAI Diffusion Anime [Female]](https://blog.novelai.net/tutorial-en-creating-consistent-characters-with-novelai-diffusion-anime-female-538b4b678a4e)
@@ -50,7 +63,7 @@ prompt = "2girls, fisheye, closeup, from above"
50
63
  async def main():
51
64
  gen = GenerateImageInfer.build_generate(
52
65
  prompt=prompt,
53
- model=Model.NAI_DIFFUSION_4_5_CURATED,
66
+ model=Model.NAI_DIFFUSION_4_5_FULL,
54
67
  character_prompts=[
55
68
  Character(
56
69
  prompt="1girl, head tilt, short hair, black hair, grey eyes, small breasts, looking at viewer",
@@ -69,6 +82,7 @@ async def main():
69
82
  qualityToggle=True,
70
83
  decrisp_mode=False,
71
84
  variety_boost=True,
85
+ furry_mode=False,
72
86
  # Checkbox in novelai.net
73
87
  )
74
88
  cost = gen.calculate_cost(is_opus=True)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "novelai-python"
3
- version = "0.7.9"
3
+ version = "0.7.11"
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.9.0",
16
+ "curl-cffi>=0.11.3",
17
17
  "fastapi>=0.109.0",
18
18
  "uvicorn[standard]>=0.27.0.post1",
19
19
  "numpy>=1.24.4",
@@ -10,7 +10,7 @@ from curl_cffi.requests import AsyncSession
10
10
  from loguru import logger
11
11
  from pydantic import SecretStr, Field, field_validator
12
12
 
13
- from ._base import CredentialBase, FAKE_UA
13
+ from ._base import CredentialBase
14
14
 
15
15
 
16
16
  class ApiCredential(CredentialBase):
@@ -22,13 +22,8 @@ class ApiCredential(CredentialBase):
22
22
 
23
23
  async def get_session(self, timeout: int = 180, update_headers: dict = None):
24
24
  headers = {
25
- "Accept": "*/*",
26
- "Accept-Encoding": "gzip, deflate, br",
27
- "User-Agent": FAKE_UA.edge,
28
25
  "Authorization": f"Bearer {self.api_token.get_secret_value()}",
29
26
  "Content-Type": "application/json",
30
- "Origin": "https://novelai.net",
31
- "Referer": "https://novelai.net/",
32
27
  "x-correlation-id": self.x_correlation_id,
33
28
  "x-initiated-at": f"{arrow.utcnow().isoformat()}Z",
34
29
  }
@@ -37,10 +32,10 @@ class ApiCredential(CredentialBase):
37
32
  assert isinstance(update_headers, dict), "update_headers must be a dict"
38
33
  headers.update(update_headers)
39
34
 
40
- return AsyncSession(timeout=timeout, headers=headers, impersonate="edge101")
35
+ return AsyncSession(timeout=timeout, headers=headers, impersonate="chrome136")
41
36
 
42
37
  @field_validator('api_token')
43
38
  def check_api_token(cls, v: SecretStr):
44
39
  if not v.get_secret_value().startswith("pst"):
45
40
  logger.warning("api token should start with `pst-`")
46
- return v
41
+ return v
@@ -10,7 +10,7 @@ from curl_cffi.requests import AsyncSession
10
10
  from loguru import logger
11
11
  from pydantic import SecretStr, Field, field_validator
12
12
 
13
- from ._base import CredentialBase, FAKE_UA
13
+ from ._base import CredentialBase
14
14
 
15
15
 
16
16
  class JwtCredential(CredentialBase):
@@ -22,13 +22,8 @@ class JwtCredential(CredentialBase):
22
22
 
23
23
  async def get_session(self, timeout: int = 180, update_headers: dict = None):
24
24
  headers = {
25
- "Accept": "*/*",
26
- "User-Agent": FAKE_UA.edge,
27
- "Accept-Encoding": "gzip, deflate, br",
28
25
  "Authorization": f"Bearer {self.jwt_token.get_secret_value()}",
29
26
  "Content-Type": "application/json",
30
- "Origin": "https://novelai.net",
31
- "Referer": "https://novelai.net/",
32
27
  "x-correlation-id": self.x_correlation_id,
33
28
  "x-initiated-at": f"{arrow.utcnow().isoformat()}Z",
34
29
  }
@@ -37,10 +32,10 @@ class JwtCredential(CredentialBase):
37
32
  assert isinstance(update_headers, dict), "update_headers must be a dict"
38
33
  headers.update(update_headers)
39
34
 
40
- return AsyncSession(timeout=timeout, headers=headers, impersonate="edge101")
35
+ return AsyncSession(timeout=timeout, headers=headers, impersonate="chrome136")
41
36
 
42
37
  @field_validator('jwt_token')
43
38
  def check_jwt_token(cls, v: SecretStr):
44
39
  if not v.get_secret_value().startswith("ey"):
45
40
  logger.warning("jwt_token should start with ey")
46
- return v
41
+ return v
@@ -10,7 +10,7 @@ import shortuuid
10
10
  from curl_cffi.requests import AsyncSession
11
11
  from pydantic import SecretStr, Field
12
12
 
13
- from ._base import CredentialBase, FAKE_UA
13
+ from ._base import CredentialBase
14
14
 
15
15
 
16
16
  class LoginCredential(CredentialBase):
@@ -25,13 +25,8 @@ class LoginCredential(CredentialBase):
25
25
 
26
26
  async def get_session(self, timeout: int = 180, update_headers: dict = None):
27
27
  headers = {
28
- "Accept": "*/*",
29
- "User-Agent": FAKE_UA.edge,
30
- "Accept-Encoding": "gzip, deflate, br",
31
28
  "Authorization": "Bearer ",
32
29
  "Content-Type": "application/json",
33
- "Origin": "https://novelai.net",
34
- "Referer": "https://novelai.net/",
35
30
  "x-correlation-id": self.x_correlation_id,
36
31
  "x-initiated-at": f"{arrow.utcnow().isoformat()}Z",
37
32
  }
@@ -49,4 +44,4 @@ class LoginCredential(CredentialBase):
49
44
  if update_headers:
50
45
  headers.update(update_headers)
51
46
 
52
- return AsyncSession(timeout=timeout, headers=headers, impersonate="edge101")
47
+ return AsyncSession(timeout=timeout, headers=headers, impersonate="chrome136")
@@ -94,6 +94,9 @@ class Model(Enum):
94
94
  """
95
95
  Nai Diffusion series
96
96
  """
97
+ NAI_DIFFUSION_4_5_FULL = "nai-diffusion-4-5-full"
98
+ NAI_DIFFUSION_4_5_FULL_INPAINTING = 'nai-diffusion-4-5-full-inpainting'
99
+
97
100
  NAI_DIFFUSION_4_5_CURATED = 'nai-diffusion-4-5-curated'
98
101
  NAI_DIFFUSION_4_5_CURATED_INPAINTING = 'nai-diffusion-4-5-curated-inpainting'
99
102
 
@@ -141,7 +144,8 @@ INPAINTING_MODEL_LIST = [
141
144
  Model.NAI_DIFFUSION_FURRY_3_INPAINTING,
142
145
  Model.NAI_DIFFUSION_4_CURATED_INPAINTING,
143
146
  Model.NAI_DIFFUSION_4_FULL_INPAINTING,
144
- Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING
147
+ Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING,
148
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING,
145
149
  ]
146
150
 
147
151
  PROMOTION = {
@@ -158,17 +162,27 @@ PROMOTION = {
158
162
  "Stable Diffusion XL B0BDF6C1": Model.NAI_DIFFUSION_3,
159
163
  "Stable Diffusion XL C1E1DE52": Model.NAI_DIFFUSION_3,
160
164
  "Stable Diffusion XL 7BCCAA2C": Model.NAI_DIFFUSION_3,
165
+ "Stable Diffusion XL 1120E6A9": Model.NAI_DIFFUSION_3,
161
166
  "Stable Diffusion XL 8BA2AF87": Model.NAI_DIFFUSION_3,
162
167
  "Stable Diffusion XL 4BE8C60C": Model.NAI_DIFFUSION_FURRY_3,
163
168
  "Stable Diffusion XL C8704949": Model.NAI_DIFFUSION_FURRY_3,
164
169
  "Stable Diffusion XL 37C2B166": Model.NAI_DIFFUSION_FURRY_3,
170
+ "Stable Diffusion XL F306816B": Model.NAI_DIFFUSION_FURRY_3,
165
171
  "Stable Diffusion XL 9CC2F394": Model.NAI_DIFFUSION_FURRY_3,
166
- 'NovelAI Diffusion V4 4F49EC75': Model.NAI_DIFFUSION_4_FULL,
167
- 'NovelAI Diffusion V4 CA4B7203': Model.NAI_DIFFUSION_4_FULL,
172
+ 'NovelAI Diffusion V4.5 B9F340FD': Model.NAI_DIFFUSION_4_5_FULL,
173
+ 'NovelAI Diffusion V4.5 F3D95188': Model.NAI_DIFFUSION_4_5_FULL,
168
174
  'NovelAI Diffusion V4.5 5AB81C7C': Model.NAI_DIFFUSION_4_5_CURATED,
169
175
  "NovelAI Diffusion V4.5 B5A2A797": Model.NAI_DIFFUSION_4_5_CURATED,
170
176
  "NovelAI Diffusion V4 5AB81C7C": Model.NAI_DIFFUSION_4_5_CURATED,
171
177
  "NovelAI Diffusion V4 B5A2A797": Model.NAI_DIFFUSION_4_5_CURATED,
178
+ "NovelAI Diffusion V4 37442FCA": Model.NAI_DIFFUSION_4_FULL,
179
+ "NovelAI Diffusion V4 4F49EC75": Model.NAI_DIFFUSION_4_FULL,
180
+ "NovelAI Diffusion V4 CA4B7203": Model.NAI_DIFFUSION_4_FULL,
181
+ "NovelAI Diffusion V4 79F47848": Model.NAI_DIFFUSION_4_FULL,
182
+ "NovelAI Diffusion V4 F6302A9D": Model.NAI_DIFFUSION_4_FULL,
183
+ "NovelAI Diffusion V4 7ABFFA2A": Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
184
+ "NovelAI Diffusion V4 C1CCBA86": Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
185
+ "NovelAI Diffusion V4 770A9E12": Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
172
186
  }
173
187
 
174
188
  ModelTypeAlias = Optional[Union[Model, str]]
@@ -198,14 +212,22 @@ class SupportCondition:
198
212
  numericEmphasis: bool
199
213
  encodedVibes: bool
200
214
  cfgDelaySigma: int
215
+ enhancePromptAdd: bool # For enhance mode, see https://github.com/llmkira/novelai-python/blob/main/playground/enhance.py
216
+ hasFurryMode: bool
217
+ img2imgInpainting: bool
201
218
 
202
219
 
203
- def get_supported_params(model: Model):
220
+ def get_supported_params(model: ModelTypeAlias):
204
221
  """
205
222
  Get supported parameters for a given model
206
223
  :param model: Model
207
224
  :return: SupportCondition
208
225
  """
226
+ if isinstance(model, str):
227
+ try:
228
+ model = Model(model)
229
+ except ValueError:
230
+ pass
209
231
  if model in [
210
232
  Model.STABLE_DIFFUSION,
211
233
  Model.NAI_DIFFUSION,
@@ -237,7 +259,10 @@ def get_supported_params(model: Model):
237
259
  scaleRecommendedMax=25,
238
260
  numericEmphasis=False,
239
261
  encodedVibes=False,
240
- cfgDelaySigma=19
262
+ cfgDelaySigma=19,
263
+ enhancePromptAdd=False,
264
+ hasFurryMode=False,
265
+ img2imgInpainting=False
241
266
  )
242
267
  if model in [Model.NAI_DIFFUSION_2]:
243
268
  return SupportCondition(
@@ -260,7 +285,10 @@ def get_supported_params(model: Model):
260
285
  scaleRecommendedMax=25,
261
286
  numericEmphasis=False,
262
287
  encodedVibes=False,
263
- cfgDelaySigma=19
288
+ cfgDelaySigma=19,
289
+ enhancePromptAdd=False,
290
+ hasFurryMode=False,
291
+ img2imgInpainting=False
264
292
  )
265
293
  if model in [
266
294
  Model.NAI_DIFFUSION_XL,
@@ -289,7 +317,10 @@ def get_supported_params(model: Model):
289
317
  scaleRecommendedMax=8,
290
318
  numericEmphasis=False,
291
319
  encodedVibes=False,
292
- cfgDelaySigma=19
320
+ cfgDelaySigma=19,
321
+ enhancePromptAdd=False,
322
+ hasFurryMode=False,
323
+ img2imgInpainting=False
293
324
  )
294
325
  if model in [
295
326
  Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
@@ -299,8 +330,8 @@ def get_supported_params(model: Model):
299
330
  ]:
300
331
  return SupportCondition(
301
332
  controlnet=False,
302
- vibetransfer=False,
303
- scaleMax=20,
333
+ vibetransfer=True,
334
+ scaleMax=10,
304
335
  negativePromptGuidance=True,
305
336
  noiseSchedule=True,
306
337
  inpainting=True,
@@ -317,7 +348,10 @@ def get_supported_params(model: Model):
317
348
  scaleRecommendedMax=6,
318
349
  numericEmphasis=True,
319
350
  encodedVibes=True,
320
- cfgDelaySigma=19
351
+ cfgDelaySigma=19,
352
+ enhancePromptAdd=False,
353
+ hasFurryMode=True,
354
+ img2imgInpainting=False
321
355
  )
322
356
  if model in [Model.CUSTOM]:
323
357
  return SupportCondition(
@@ -340,11 +374,16 @@ def get_supported_params(model: Model):
340
374
  scaleRecommendedMax=None,
341
375
  numericEmphasis=True,
342
376
  encodedVibes=True,
343
- cfgDelaySigma=58
377
+ cfgDelaySigma=58,
378
+ enhancePromptAdd=False,
379
+ hasFurryMode=True,
380
+ img2imgInpainting=False
344
381
  )
345
382
  if model in [
346
383
  Model.NAI_DIFFUSION_4_5_CURATED,
347
384
  Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING,
385
+ Model.NAI_DIFFUSION_4_5_FULL,
386
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING,
348
387
  ]:
349
388
  return SupportCondition(
350
389
  controlnet=False,
@@ -366,7 +405,10 @@ def get_supported_params(model: Model):
366
405
  scaleRecommendedMax=None,
367
406
  numericEmphasis=True,
368
407
  encodedVibes=False,
369
- cfgDelaySigma=58
408
+ cfgDelaySigma=58,
409
+ enhancePromptAdd=True,
410
+ hasFurryMode=True,
411
+ img2imgInpainting=False
370
412
  )
371
413
  return SupportCondition(
372
414
  controlnet=False,
@@ -388,7 +430,10 @@ def get_supported_params(model: Model):
388
430
  scaleRecommendedMax=99,
389
431
  numericEmphasis=False,
390
432
  encodedVibes=False,
391
- cfgDelaySigma=19
433
+ cfgDelaySigma=19,
434
+ enhancePromptAdd=False,
435
+ hasFurryMode=False,
436
+ img2imgInpainting=False
392
437
  )
393
438
 
394
439
 
@@ -400,7 +445,7 @@ class Modifier(object):
400
445
 
401
446
  def find_model_by_hashcode(hashcode: str) -> ModelTypeAlias:
402
447
  if "NovelAI Diffusion V4.5" in hashcode:
403
- return PROMOTION.get(hashcode, Model.NAI_DIFFUSION_4_5_CURATED)
448
+ return PROMOTION.get(hashcode, Model.NAI_DIFFUSION_4_5_FULL)
404
449
  if "NovelAI Diffusion V4" in hashcode:
405
450
  return PROMOTION.get(hashcode, Model.NAI_DIFFUSION_4_CURATED_PREVIEW)
406
451
  return PROMOTION.get(hashcode, None)
@@ -414,6 +459,14 @@ def get_modifiers(model: Model) -> Modifier:
414
459
  """
415
460
  if model in [
416
461
  Model.CUSTOM,
462
+ Model.NAI_DIFFUSION_4_5_FULL,
463
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING
464
+ ]:
465
+ return Modifier(
466
+ qualityTags="",
467
+ suffix=", location, very aesthetic, masterpiece, no text"
468
+ )
469
+ if model in [
417
470
  Model.NAI_DIFFUSION_4_5_CURATED,
418
471
  Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING
419
472
  ]:
@@ -487,6 +540,8 @@ def filter_by_model_noise_schedule(model: Model, noise_schedule: List[NoiseSched
487
540
  if model in [
488
541
  Model.NAI_DIFFUSION_4_5_CURATED,
489
542
  Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING,
543
+ Model.NAI_DIFFUSION_4_5_FULL,
544
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING,
490
545
  Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
491
546
  Model.NAI_DIFFUSION_4_FULL,
492
547
  Model.NAI_DIFFUSION_4_FULL_INPAINTING,
@@ -547,34 +602,42 @@ def get_default_noise_schedule(sample_type: Sampler) -> NoiseSchedule:
547
602
 
548
603
 
549
604
  def get_model_group(model: ModelTypeAlias) -> ModelGroups:
550
- # 一般情况禁止转换
551
- if isinstance(model, Enum):
552
- model = model.value
553
- else:
554
- model = str(model)
605
+ """
606
+ Get model group
607
+ If the model is not in the mapping, return STABLE_DIFFUSION
608
+ :param model: Model enum or model name string
609
+ :return: Model group
610
+ """
611
+ if isinstance(model, str):
612
+ try:
613
+ model = Model(model)
614
+ except ValueError:
615
+ return ModelGroups.STABLE_DIFFUSION
555
616
  mapping = {
556
- "stable-diffusion": ModelGroups.STABLE_DIFFUSION,
557
- "nai-diffusion": ModelGroups.STABLE_DIFFUSION,
558
- "safe-diffusion": ModelGroups.STABLE_DIFFUSION,
559
- "waifu-diffusion": ModelGroups.STABLE_DIFFUSION,
560
- "nai-diffusion-furry": ModelGroups.STABLE_DIFFUSION,
561
- "curated-diffusion-test": ModelGroups.STABLE_DIFFUSION,
562
- "nai-diffusion-inpainting": ModelGroups.STABLE_DIFFUSION,
563
- "safe-diffusion-inpainting": ModelGroups.STABLE_DIFFUSION,
564
- "furry-diffusion-inpainting": ModelGroups.STABLE_DIFFUSION,
565
- "nai-diffusion-2": ModelGroups.STABLE_DIFFUSION_GROUP_2,
566
- "nai-diffusion-xl": ModelGroups.STABLE_DIFFUSION_XL,
567
- "nai-diffusion-3": ModelGroups.STABLE_DIFFUSION_XL,
568
- "nai-diffusion-3-inpainting": ModelGroups.STABLE_DIFFUSION_XL,
569
- "custom": ModelGroups.V4,
570
- "nai-diffusion-furry-3": ModelGroups.STABLE_DIFFUSION_XL_FURRY,
571
- "nai-diffusion-furry-3-inpainting": ModelGroups.STABLE_DIFFUSION_XL_FURRY,
572
- "nai-diffusion-4-curated-preview": ModelGroups.V4,
573
- 'nai-diffusion-4-full': ModelGroups.V4,
574
- 'nai-diffusion-4-full-inpainting': ModelGroups.V4,
575
- 'nai-diffusion-4-curated-inpainting': ModelGroups.V4,
576
- 'nai-diffusion-4-5-curated': ModelGroups.V4,
577
- 'nai-diffusion-4-5-curated-inpainting': ModelGroups.V4,
617
+ Model.STABLE_DIFFUSION: ModelGroups.STABLE_DIFFUSION,
618
+ Model.NAI_DIFFUSION: ModelGroups.STABLE_DIFFUSION,
619
+ Model.SAFE_DIFFUSION: ModelGroups.STABLE_DIFFUSION,
620
+ Model.WAIFU_DIFFUSION: ModelGroups.STABLE_DIFFUSION,
621
+ Model.NAI_DIFFUSION_FURRY: ModelGroups.STABLE_DIFFUSION,
622
+ Model.CURATED_DIFFUSION_TEST: ModelGroups.STABLE_DIFFUSION,
623
+ Model.NAI_DIFFUSION_INPAINTING: ModelGroups.STABLE_DIFFUSION,
624
+ Model.SAFE_DIFFUSION_INPAINTING: ModelGroups.STABLE_DIFFUSION,
625
+ Model.FURRY_DIFFUSION_INPAINTING: ModelGroups.STABLE_DIFFUSION,
626
+ Model.NAI_DIFFUSION_2: ModelGroups.STABLE_DIFFUSION_GROUP_2,
627
+ Model.NAI_DIFFUSION_XL: ModelGroups.STABLE_DIFFUSION_XL,
628
+ Model.NAI_DIFFUSION_3: ModelGroups.STABLE_DIFFUSION_XL,
629
+ Model.NAI_DIFFUSION_3_INPAINTING: ModelGroups.STABLE_DIFFUSION_XL,
630
+ Model.CUSTOM: ModelGroups.V4,
631
+ Model.NAI_DIFFUSION_FURRY_3: ModelGroups.STABLE_DIFFUSION_XL_FURRY,
632
+ Model.NAI_DIFFUSION_FURRY_3_INPAINTING: ModelGroups.STABLE_DIFFUSION_XL_FURRY,
633
+ Model.NAI_DIFFUSION_4_CURATED_PREVIEW: ModelGroups.V4,
634
+ Model.NAI_DIFFUSION_4_FULL: ModelGroups.V4,
635
+ Model.NAI_DIFFUSION_4_FULL_INPAINTING: ModelGroups.V4,
636
+ Model.NAI_DIFFUSION_4_CURATED_INPAINTING: ModelGroups.V4,
637
+ Model.NAI_DIFFUSION_4_5_CURATED: ModelGroups.V4,
638
+ Model.NAI_DIFFUSION_4_5_CURATED_INPAINTING: ModelGroups.V4,
639
+ Model.NAI_DIFFUSION_4_5_FULL: ModelGroups.V4,
640
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING: ModelGroups.V4,
578
641
  }
579
642
  return mapping.get(model, ModelGroups.STABLE_DIFFUSION)
580
643
 
@@ -768,6 +831,37 @@ def get_uc_preset(model: ModelTypeAlias) -> List[UcPrompt]:
768
831
  text=""
769
832
  ),
770
833
  ]
834
+ elif model in [
835
+ Model.NAI_DIFFUSION_4_5_FULL,
836
+ Model.NAI_DIFFUSION_4_5_FULL_INPAINTING,
837
+ ]:
838
+ prompts = [
839
+ UcPrompt(
840
+ category="heavy",
841
+ name="heavy",
842
+ text="lowres, artistic error, film grain, scan artifacts, worst quality, bad quality, jpeg artifacts, very displeasing, chromatic aberration, dithering, halftone, screentone, multiple views, logo, too many watermarks, negative space, blank page"
843
+ ),
844
+ UcPrompt(
845
+ category="light",
846
+ name="light",
847
+ text="lowres, artistic error, scan artifacts, worst quality, bad quality, jpeg artifacts, multiple views, very displeasing, too many watermarks, negative space, blank page"
848
+ ),
849
+ UcPrompt(
850
+ category="furry",
851
+ name="furryFocus",
852
+ text="{worst quality}, distracting watermark, unfinished, bad quality, {widescreen}, upscale, {sequence}, {{grandfathered content}}, blurred foreground, chromatic aberration, sketch, everyone, [sketch background], simple, [flat colors], ych (character), outline, multiple scenes, [[horror (theme)]], comic"
853
+ ),
854
+ UcPrompt(
855
+ category="human",
856
+ name="humanFocus",
857
+ text="lowres, artistic error, film grain, scan artifacts, worst quality, bad quality, jpeg artifacts, very displeasing, chromatic aberration, dithering, halftone, screentone, multiple views, logo, too many watermarks, negative space, blank page, @_@, mismatched pupils, glowing eyes, bad anatomy"
858
+ ),
859
+ UcPrompt(
860
+ category="none",
861
+ name="none",
862
+ text=""
863
+ ),
864
+ ]
771
865
  return prompts
772
866
 
773
867
 
@@ -174,28 +174,6 @@ class AugmentImageInfer(ApiBaseModel):
174
174
  prompt=prompt,
175
175
  )
176
176
 
177
- async def necessary_headers(self, request_data) -> dict:
178
- """
179
- :param request_data:
180
- :return:
181
- """
182
- return {
183
- "Host": urlparse(self.endpoint).netloc,
184
- "Accept": "*/*",
185
- "Accept-Encoding": "gzip, deflate, br",
186
- "Referer": "https://novelai.net/",
187
- "Content-Type": "application/json",
188
- "Origin": "https://novelai.net",
189
- "Content-Length": str(len(json.dumps(request_data).encode("utf-8"))),
190
- "Connection": "keep-alive",
191
- "Sec-Fetch-Dest": "empty",
192
- "Sec-Fetch-Mode": "cors",
193
- "Sec-Fetch-Site": "same-site",
194
- "Pragma": "no-cache",
195
- "Cache-Control": "no-cache",
196
- 'priority': "u=1, i"
197
- }
198
-
199
177
  @retry(
200
178
  wait=wait_random(min=1, max=3),
201
179
  stop=stop_after_attempt(3),
@@ -216,7 +194,6 @@ class AugmentImageInfer(ApiBaseModel):
216
194
  # Prepare request data
217
195
  request_data = self.model_dump(mode="json", exclude_none=True)
218
196
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
219
- sess.headers.update(await self.necessary_headers(request_data))
220
197
  if override_headers:
221
198
  sess.headers.clear()
222
199
  sess.headers.update(override_headers)
@@ -3,7 +3,7 @@ from enum import Enum
3
3
 
4
4
  class ReqType(Enum):
5
5
  """
6
- typing.Literal["bg-removal", "colorize", "lineart", "sketch", "emotion", "declutter"]
6
+ typing.Literal["bg-removal", "colorize", "lineart", "sketch", "emotion", "declutter", "declutter-keep-bubbles"]
7
7
  """
8
8
  BG_REMOVAL = "bg-removal"
9
9
  COLORIZE = "colorize"
@@ -11,6 +11,7 @@ class ReqType(Enum):
11
11
  SKETCH = "sketch"
12
12
  EMOTION = "emotion"
13
13
  DECLUTTER = "declutter"
14
+ DECLUTTER_KEEP_BUBBLES = "declutter-keep-bubbles"
14
15
 
15
16
 
16
17
  class Moods(Enum):
@@ -65,22 +65,6 @@ class LLM(ApiBaseModel):
65
65
  def base_url(self):
66
66
  return f"{self.endpoint.strip('/')}/ai/generate"
67
67
 
68
- async def necessary_headers(self, request_data) -> dict:
69
- """
70
- :param request_data: dict
71
- :return: dict
72
- """
73
- return {
74
- "Host": urlparse(self.endpoint).netloc,
75
- "accept": "*/*",
76
- "accept-language": "zh-CN,zh;q=0.9",
77
- "cache-control": "no-cache",
78
- "content-type": "application/json",
79
- "pragma": "no-cache",
80
- "Referer": "https://novelai.net/",
81
- "Referrer-Policy": "strict-origin-when-cross-origin"
82
- }
83
-
84
68
  @model_validator(mode="after")
85
69
  def normalize_model(self):
86
70
  if self.model in [
@@ -361,7 +345,6 @@ class LLM(ApiBaseModel):
361
345
  }
362
346
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
363
347
  # Header
364
- sess.headers.update(await self.necessary_headers(request_data))
365
348
  if override_headers:
366
349
  sess.headers.clear()
367
350
  sess.headers.update(override_headers)