novelai-python 0.7.10__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.10 → novelai_python-0.7.11}/PKG-INFO +14 -2
  2. {novelai_python-0.7.10 → novelai_python-0.7.11}/README.md +12 -0
  3. {novelai_python-0.7.10 → novelai_python-0.7.11}/pyproject.toml +2 -2
  4. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/credential/ApiToken.py +3 -8
  5. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/credential/JwtToken.py +3 -8
  6. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/credential/UserAuth.py +2 -7
  7. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_enum.py +6 -1
  8. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/augment_image/__init__.py +0 -23
  9. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/__init__.py +0 -17
  10. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/__init__.py +5 -29
  11. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/params.py +38 -44
  12. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +2 -21
  13. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_stream.py +0 -1
  14. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_voice/__init__.py +0 -22
  15. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/upscale.py +0 -23
  16. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/schema.py +0 -4
  17. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/user/information.py +0 -12
  18. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/user/login.py +0 -4
  19. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/user/subscription.py +0 -19
  20. {novelai_python-0.7.10 → novelai_python-0.7.11}/LICENSE +0 -0
  21. {novelai_python-0.7.10 → novelai_python-0.7.11}/NOTICE.MD +0 -0
  22. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/__init__.py +0 -0
  23. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_enum.py +0 -0
  24. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_exceptions.py +0 -0
  25. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/__init__.py +0 -0
  26. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/__init__.py +0 -0
  27. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate.py +0 -0
  28. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_image.py +0 -0
  29. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_stream.py +0 -0
  30. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/generate_voice.py +0 -0
  31. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/ai/upscale.py +0 -0
  32. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/schema.py +0 -0
  33. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/user/__init__.py +0 -0
  34. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/user/information.py +0 -0
  35. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/user/login.py +0 -0
  36. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/_response/user/subscription.py +0 -0
  37. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/credential/__init__.py +0 -0
  38. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/credential/_base.py +0 -0
  39. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/__init__.py +0 -0
  40. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/LICENSE +0 -0
  41. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/__init__.py +0 -0
  42. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_const.py +0 -0
  43. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/_cost.py +0 -0
  44. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/augment_image/_enum.py +0 -0
  45. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_const.py +0 -0
  46. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_enum.py +0 -0
  47. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/_schema.py +0 -0
  48. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate/themes.json +0 -0
  49. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/schema.py +0 -0
  50. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_image/tokenizer.py +0 -0
  51. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/ai/generate_voice/_enum.py +0 -0
  52. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/sdk/user/__init__.py +0 -0
  53. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/server.py +0 -0
  54. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/__init__.py +0 -0
  55. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/clip_simple_tokenizer.py +0 -0
  56. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/nerdstash_v1.model +0 -0
  57. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/nerdstash_v2.model +0 -0
  58. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/novelai.model +0 -0
  59. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tokenizer/novelai_v2.model +0 -0
  60. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/__init__.py +0 -0
  61. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/__init__.py +0 -0
  62. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/bch_utils.py +0 -0
  63. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/lsb_extractor.py +0 -0
  64. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/image_metadata/lsb_injector.py +0 -0
  65. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/paint_mask/__init__.py +0 -0
  66. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/__init__.py +0 -0
  67. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_scene_composition.py +0 -0
  68. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_scene_tags.py +0 -0
  69. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/tool/random_prompt/generate_tags.py +0 -0
  70. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/utils/__init__.py +0 -0
  71. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/utils/encode.py +0 -0
  72. {novelai_python-0.7.10 → novelai_python-0.7.11}/src/novelai_python/utils/useful.py +0 -0
  73. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/__init__.py +0 -0
  74. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_generate_voice.py +0 -0
  75. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_random_prompt.py +0 -0
  76. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_server.py +0 -0
  77. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_server_run.py +0 -0
  78. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_tokenizer.py +0 -0
  79. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_upscale.py +0 -0
  80. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_user_information.py +0 -0
  81. {novelai_python-0.7.10 → novelai_python-0.7.11}/tests/test_user_login.py +0 -0
  82. {novelai_python-0.7.10 → 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.10
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,6 +43,18 @@ 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
@@ -5,6 +5,18 @@
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "novelai-python"
3
- version = "0.7.10"
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")
@@ -217,12 +217,17 @@ class SupportCondition:
217
217
  img2imgInpainting: bool
218
218
 
219
219
 
220
- def get_supported_params(model: Model):
220
+ def get_supported_params(model: ModelTypeAlias):
221
221
  """
222
222
  Get supported parameters for a given model
223
223
  :param model: Model
224
224
  :return: SupportCondition
225
225
  """
226
+ if isinstance(model, str):
227
+ try:
228
+ model = Model(model)
229
+ except ValueError:
230
+ pass
226
231
  if model in [
227
232
  Model.STABLE_DIFFUSION,
228
233
  Model.NAI_DIFFUSION,
@@ -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)
@@ -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)
@@ -9,7 +9,6 @@ from copy import deepcopy
9
9
  from enum import Enum
10
10
  from io import BytesIO
11
11
  from typing import Optional, Union, List
12
- from urllib.parse import urlparse
13
12
  from zipfile import ZipFile, BadZipFile
14
13
 
15
14
  import curl_cffi
@@ -61,9 +60,9 @@ class GenerateImageInfer(ApiBaseModel):
61
60
  self._mutual_exclusion = bool(value)
62
61
  return self
63
62
 
64
- action: Union[str, Action] = Field(Action.GENERATE, description="Mode for img generate")
65
63
  input: str = "1girl, best quality, amazing quality, very aesthetic, absurdres"
66
64
  model: ModelTypeAlias = "nai-diffusion-3"
65
+ action: Union[str, Action] = Field(Action.GENERATE, description="Mode for img generate")
67
66
  parameters: Union[Params]
68
67
  model_config = ConfigDict(extra="ignore")
69
68
 
@@ -489,12 +488,12 @@ class GenerateImageInfer(ApiBaseModel):
489
488
  def build_img2img(
490
489
  prompt: str,
491
490
  *,
491
+ model: Union[Model, str] = Model.NAI_DIFFUSION_4_5_FULL,
492
492
  image: Union[bytes, str],
493
493
  strength: float = None,
494
494
  noise: float = None,
495
495
  seed: int = None,
496
496
  extra_noise_seed: int = None,
497
- model: Union[Model, str] = Model.NAI_DIFFUSION_4_5_CURATED,
498
497
  negative_prompt: str = None,
499
498
  ucPreset: UCPresetTypeAlias = None,
500
499
  steps: int = None,
@@ -523,7 +522,7 @@ class GenerateImageInfer(ApiBaseModel):
523
522
  :param reference_strength_multiple:
524
523
  :param reference_image_multiple:
525
524
  :param prompt: Given prompt.
526
- :param model: Model for generation.
525
+ :param model: Model for generation, which NOT end with 'inpainting'.
527
526
  :param negative_prompt: The things you don't want to see in the image.
528
527
  :param ucPreset: The negative prompt preset.
529
528
  :param steps: The steps for generation.
@@ -592,10 +591,10 @@ class GenerateImageInfer(ApiBaseModel):
592
591
  def build_infill(
593
592
  prompt: str,
594
593
  *,
594
+ model: Union[Model, str],
595
595
  image: Union[bytes, str],
596
596
  mask: Union[bytes, str],
597
597
  strength: float = None,
598
- model: Union[Model, str] = Model.NAI_DIFFUSION_4_5_CURATED,
599
598
  negative_prompt: str = None,
600
599
  ucPreset: UCPresetTypeAlias = None,
601
600
  steps: int = None,
@@ -645,7 +644,7 @@ class GenerateImageInfer(ApiBaseModel):
645
644
 
646
645
  # Update only explicitly set parameters
647
646
  params.image = image # image is required for infill
648
- params.mask = mask # mask is required for infill
647
+ params.mask = mask # mask is required for infill
649
648
  if strength is not None:
650
649
  params.strength = strength
651
650
  if negative_prompt is not None:
@@ -686,28 +685,6 @@ class GenerateImageInfer(ApiBaseModel):
686
685
  parameters=params
687
686
  )
688
687
 
689
- async def necessary_headers(self, request_data) -> dict:
690
- """
691
- :param request_data:
692
- :return:
693
- """
694
- return {
695
- "Host": urlparse(self.endpoint).netloc,
696
- "Accept": "*/*",
697
-
698
- "Accept-Encoding": "gzip, deflate, br",
699
- "Referer": "https://novelai.net/",
700
- "Content-Type": "application/json",
701
- "Origin": "https://novelai.net",
702
- "Content-Length": str(len(json.dumps(request_data).encode("utf-8"))),
703
- "Connection": "keep-alive",
704
- "Sec-Fetch-Dest": "empty",
705
- "Sec-Fetch-Mode": "cors",
706
- "Sec-Fetch-Site": "same-site",
707
- "Pragma": "no-cache",
708
- "Cache-Control": "no-cache",
709
- }
710
-
711
688
  @retry(
712
689
  wait=wait_random(min=1, max=3),
713
690
  stop=stop_after_attempt(3),
@@ -738,7 +715,6 @@ class GenerateImageInfer(ApiBaseModel):
738
715
  # Prepare request data
739
716
  request_data = self.model_dump(mode="json", exclude_none=True)
740
717
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
741
- sess.headers.update(await self.necessary_headers(request_data))
742
718
  if override_headers:
743
719
  sess.headers.clear()
744
720
  sess.headers.update(override_headers)
@@ -20,6 +20,8 @@ def is_multiple_of_01(num, precision=1e-10):
20
20
 
21
21
 
22
22
  class Params(BaseModel):
23
+ params_version: int = 3
24
+ """Params Version For Request"""
23
25
  width: int = Field(832, ge=64, le=49152)
24
26
  """Width For Image"""
25
27
  height: int = Field(1216, ge=64, le=49152)
@@ -32,42 +34,66 @@ class Params(BaseModel):
32
34
  """Steps"""
33
35
  n_samples: int = Field(1, ge=1, le=8)
34
36
  """Number of samples"""
35
- strength: Optional[float] = Field(0.7, ge=0.01, le=0.99, multiple_of=0.01)
36
- """Strength for img2img"""
37
- noise: Optional[float] = Field(0, ge=0, le=0.99, multiple_of=0.01)
38
- """Noise for img2img"""
39
37
  ucPreset: UCPresetTypeAlias = Field(None, ge=0)
40
38
  """The Negative Prompt Preset, Bigger or equal to 0"""
41
39
  qualityToggle: bool = True
42
40
  """Whether to add the quality prompt"""
43
- sm: Optional[bool] = False
44
- # TODO: find out the usage
45
- sm_dyn: Optional[bool] = False
46
- # TODO: find out the usage
41
+ autoSmea: Optional[bool] = False
47
42
  dynamic_thresholding: bool = False
48
43
  """Decrisp:Reduce artifacts caused by high prompt guidance values"""
49
44
  controlnet_strength: float = Field(1.0, ge=0.1, le=2, multiple_of=0.1)
50
45
  """ControlNet Strength"""
51
46
  legacy: bool = False
52
47
  """Legacy Mode"""
48
+ add_original_image: Optional[bool] = Field(False, description="Overlay Original Image")
49
+ """
50
+ Overlay Original Image.Prevents the existing image from changing,
51
+ but can introduce seams along the edge of the mask.
52
+ """
53
53
  cfg_rescale: Optional[float] = Field(0, ge=0, le=1, multiple_of=0.02)
54
54
  """Prompt Guidance Rescale"""
55
55
  noise_schedule: Optional[NoiseSchedule] = None
56
56
  """Noise Schedule"""
57
57
  legacy_v3_extend: Optional[bool] = False
58
58
  """Legacy V3 Extend"""
59
- mask: ImageBytesTypeAlias = None
60
- """Mask for Inpainting"""
59
+ skip_cfg_above_sigma: Optional[int] = None
60
+ """Variety Boost, a new feature to improve the diversity of samples."""
61
+ use_coords: bool = Field(False, description="Use Coordinates")
62
+ """Use Coordinates"""
63
+ legacy_uc: bool = False
64
+ normalize_reference_strength_multiple: bool = True
61
65
  seed: int = Field(
62
66
  default_factory=lambda: random.randint(0, 4294967295 - 7),
63
67
  gt=0,
64
68
  le=4294967295 - 7,
65
69
  )
66
70
  """Seed"""
67
- image: ImageBytesTypeAlias = None
68
- """Image for img2img"""
71
+ characterPrompts: List[Character] = Field(default_factory=list)
72
+ """Character Prompts"""
73
+ v4_prompt: Optional[V4Prompt] = Field(None, description="V4 Prompt")
74
+ """V4 Prompt"""
75
+ v4_negative_prompt: Optional[V4NegativePrompt] = Field(None, description="V4 Negative Prompt")
76
+ """V4 Negative Prompt"""
69
77
  negative_prompt: Optional[str] = ''
70
78
  """Negative Prompt"""
79
+ deliberate_euler_ancestral_bug: Optional[bool] = Field(False, description="Deliberate Euler Ancestral Bug")
80
+ """Deliberate Euler Ancestral Bug"""
81
+ prefer_brownian: bool = Field(True, description="Prefer Brownian")
82
+ """Prefer Brownian"""
83
+
84
+ # Other fields
85
+ strength: Optional[float] = Field(0.7, ge=0.01, le=0.99, multiple_of=0.01)
86
+ """Strength for img2img"""
87
+ noise: Optional[float] = Field(0, ge=0, le=0.99, multiple_of=0.01)
88
+ """Noise for img2img"""
89
+ sm: Optional[bool] = False
90
+ # TODO: find out the usage
91
+ sm_dyn: Optional[bool] = False
92
+ # TODO: find out the usage
93
+ mask: ImageBytesTypeAlias = None
94
+ """Mask for Inpainting"""
95
+ image: ImageBytesTypeAlias = None
96
+ """Image for img2img"""
71
97
  reference_image_multiple: Optional[List[Union[str, bytes]]] = Field(default_factory=list)
72
98
  """Reference Image For Vibe Mode"""
73
99
  reference_information_extracted_multiple: Optional[List[float]] = Field(default_factory=list)
@@ -79,49 +105,17 @@ class Params(BaseModel):
79
105
  """Reference Strength For Vibe Mode"""
80
106
  extra_noise_seed: Optional[int] = Field(None, gt=0, le=4294967295 - 7)
81
107
  """Extra Noise Seed"""
82
-
83
- # ====Version3 ====
84
- params_version: int = 3
85
- """Params Version For Request"""
86
- add_original_image: Optional[bool] = Field(False, description="Overlay Original Image")
87
- """
88
- Overlay Original Image.Prevents the existing image from changing,
89
- but can introduce seams along the edge of the mask.
90
- """
91
- use_coords: bool = Field(False, description="Use Coordinates")
92
- """Use Coordinates"""
93
- characterPrompts: List[Character] = Field(default_factory=list)
94
- """Character Prompts"""
95
-
96
108
  color_correct: Optional[bool] = None
97
109
  """For Inpaint"""
98
110
  inpaintImg2ImgStrength: Optional[float] = Field(None, ge=0, le=1, multiple_of=0.01)
99
111
  """For SameFeel"""
100
-
101
- v4_prompt: Optional[V4Prompt] = Field(None, description="V4 Prompt")
102
- """V4 Prompt"""
103
- v4_negative_prompt: Optional[V4NegativePrompt] = Field(None, description="V4 Negative Prompt")
104
- """V4 Negative Prompt"""
105
-
106
- deliberate_euler_ancestral_bug: Optional[bool] = Field(False, description="Deliberate Euler Ancestral Bug")
107
- """Deliberate Euler Ancestral Bug"""
108
- prefer_brownian: bool = Field(True, description="Prefer Brownian")
109
- """Prefer Brownian"""
110
-
111
- # ======== V1 ========
112
112
  controlnet_condition: Optional[str] = None
113
113
  """ControlNet Condition"""
114
114
  controlnet_model: Optional[ControlNetModel] = None
115
115
  """ControlNet Model"""
116
- skip_cfg_above_sigma: Optional[int] = None
117
- """Variety Boost, a new feature to improve the diversity of samples."""
118
116
  uncond_scale: Optional[float] = Field(None, ge=0, le=1.5, multiple_of=0.05)
119
117
  """Undesired Content Strength"""
120
118
 
121
- autoSmea: Optional[bool] = False
122
- normalize_reference_strength_multiple: bool = True
123
- legacy_uc: bool = False
124
-
125
119
  @model_validator(mode="after")
126
120
  def v_character(self):
127
121
  if len(self.characterPrompts) > 6:
@@ -37,25 +37,6 @@ class SuggestTags(ApiBaseModel):
37
37
  def base_url(self):
38
38
  return f"{self.endpoint.strip('/')}/ai/generate-image/suggest-tags"
39
39
 
40
- async def necessary_headers(self, request_data) -> dict:
41
- return {
42
- "Host": urlparse(self.endpoint).netloc,
43
- "Accept": "*/*",
44
-
45
- "Accept-Encoding": "gzip, deflate, br",
46
- "Access-Control-Allow-Origin": "*",
47
- "Referer": "https://novelai.net/",
48
- "Content-Type": "application/json",
49
- "Origin": "https://novelai.net",
50
- "Connection": "keep-alive",
51
- "Sec-Fetch-Dest": "empty",
52
- "Sec-Fetch-Mode": "cors",
53
- "Sec-Fetch-Site": "same-site",
54
- "Pragma": "no-cache",
55
- "Cache-Control": "no-cache",
56
- "TE": "trailers",
57
- }
58
-
59
40
  async def request(self,
60
41
  session: Union[AsyncSession, CredentialBase],
61
42
  *,
@@ -70,9 +51,9 @@ class SuggestTags(ApiBaseModel):
70
51
  # Data Build
71
52
  request_data = self.model_dump(mode="json", exclude_none=True)
72
53
  if isinstance(session, AsyncSession):
73
- session.headers.update(await self.necessary_headers(request_data))
54
+ pass
74
55
  elif isinstance(session, CredentialBase):
75
- session = await session.get_session(update_headers=await self.necessary_headers(request_data))
56
+ pass
76
57
  # Header
77
58
  if override_headers:
78
59
  session.headers.clear()
@@ -87,7 +87,6 @@ class LLMStream(LLM):
87
87
  }
88
88
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
89
89
  # Header
90
- sess.headers.update(await self.necessary_headers(request_data))
91
90
  if override_headers:
92
91
  sess.headers.clear()
93
92
  sess.headers.update(override_headers)
@@ -57,27 +57,6 @@ class VoiceGenerate(ApiBaseModel):
57
57
  def endpoint(self, value):
58
58
  self._endpoint = value
59
59
 
60
- async def necessary_headers(self, request_data) -> dict:
61
- """
62
- :param request_data: dict
63
- :return: dict
64
- "Sec-Ch-Ua": '"Edge";v="123", "Not:A-Brand";v="8"',
65
- "Sec-Ch-Ua-Mobile": "?0",
66
- "Sec-Ch-Ua-Platform": '"Windows"',
67
- "Sec-Fetch-Dest": "empty",
68
- "Sec-Fetch-Mode": "cors",
69
- "Sec-Fetch-Site": "same-site",
70
- """
71
- return {
72
- "Host": urlparse(self.endpoint).netloc,
73
- "Accept": "*/*",
74
- "Accept-Language": "zh-CN,zh;q=0.9",
75
- "Cache-Control": "no-cache",
76
- "Origin": "https://novelai.net",
77
- "Pragma": "no-cache",
78
- "Referer": "https://novelai.net/",
79
- }
80
-
81
60
  @classmethod
82
61
  def build(cls,
83
62
  text: str,
@@ -133,7 +112,6 @@ class VoiceGenerate(ApiBaseModel):
133
112
  request_data = self.model_dump(mode="json", exclude_none=True)
134
113
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
135
114
  # Header
136
- sess.headers.update(await self.necessary_headers(request_data))
137
115
  if override_headers:
138
116
  sess.headers.clear()
139
117
  sess.headers.update(override_headers)
@@ -66,28 +66,6 @@ class Upscale(ApiBaseModel):
66
66
  def endpoint(self, value):
67
67
  self._endpoint = value
68
68
 
69
- async def necessary_headers(self, request_data) -> dict:
70
- """
71
- :param request_data:
72
- :return:
73
- """
74
- return {
75
- "Host": urlparse(self.endpoint).netloc,
76
- "Accept": "*/*",
77
-
78
- "Accept-Encoding": "gzip, deflate, br",
79
- "Referer": "https://novelai.net/",
80
- "Content-Type": "application/json",
81
- "Origin": "https://novelai.net",
82
- "Content-Length": str(len(json.dumps(request_data).encode("utf-8"))),
83
- "Connection": "keep-alive",
84
- "Sec-Fetch-Dest": "empty",
85
- "Sec-Fetch-Mode": "cors",
86
- "Sec-Fetch-Site": "same-site",
87
- "Pragma": "no-cache",
88
- "Cache-Control": "no-cache",
89
- }
90
-
91
69
  @retry(
92
70
  wait=wait_random(min=1, max=3),
93
71
  stop=stop_after_attempt(3),
@@ -108,7 +86,6 @@ class Upscale(ApiBaseModel):
108
86
  # Prepare request data
109
87
  request_data = self.model_dump(mode="json", exclude_none=True)
110
88
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
111
- sess.headers.update(await self.necessary_headers(request_data))
112
89
  if override_headers:
113
90
  sess.headers.clear()
114
91
  sess.headers.update(override_headers)
@@ -31,10 +31,6 @@ class ApiBaseModel(BaseModel, ABC):
31
31
  def endpoint(self, value):
32
32
  self._endpoint = value
33
33
 
34
- @abstractmethod
35
- async def necessary_headers(self, request_data) -> dict:
36
- raise NotImplementedError()
37
-
38
34
  @staticmethod
39
35
  def ensure_session_has_post_method(session):
40
36
  if not hasattr(session, "post"):
@@ -33,17 +33,6 @@ class Information(ApiBaseModel):
33
33
  def endpoint(self, value):
34
34
  self._endpoint = value
35
35
 
36
- async def necessary_headers(self, request_data) -> dict:
37
- return {
38
- "Host": urlparse(self.endpoint).netloc,
39
- "Accept": "*/*",
40
-
41
- "Accept-Encoding": "gzip, deflate, br",
42
- "Referer": "https://novelai.net/",
43
- "Content-Type": "application/json",
44
- "Origin": "https://novelai.net"
45
- }
46
-
47
36
  async def request(self,
48
37
  session: Union[AsyncSession, CredentialBase],
49
38
  *,
@@ -58,7 +47,6 @@ class Information(ApiBaseModel):
58
47
  request_data = {}
59
48
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
60
49
  # Header
61
- sess.headers.update(await self.necessary_headers(request_data))
62
50
  if override_headers:
63
51
  sess.headers.clear()
64
52
  sess.headers.update(override_headers)
@@ -53,9 +53,6 @@ class Login(ApiBaseModel):
53
53
  """
54
54
  return cls(key=encode_access_key(user_name, password))
55
55
 
56
- async def necessary_headers(self, request_data) -> dict:
57
- return {}
58
-
59
56
  async def request(self,
60
57
  session: Union[AsyncSession, CredentialBase] = None,
61
58
  *,
@@ -70,7 +67,6 @@ class Login(ApiBaseModel):
70
67
 
71
68
  async with session if isinstance(session, AsyncSession) else self.session as sess:
72
69
  # Header
73
- sess.headers.update(await self.necessary_headers(request_data))
74
70
  if override_headers:
75
71
  sess.headers.clear()
76
72
  sess.headers.update(override_headers)
@@ -33,24 +33,6 @@ class Subscription(ApiBaseModel):
33
33
  def endpoint(self, value):
34
34
  self._endpoint = value
35
35
 
36
- async def necessary_headers(self, request_data) -> dict:
37
- return {
38
- "Host": urlparse(self.endpoint).netloc,
39
- "Accept": "*/*",
40
-
41
- "Accept-Encoding": "gzip, deflate, br",
42
- "Referer": "https://novelai.net/",
43
- "Content-Type": "application/json",
44
- "Origin": "https://novelai.net",
45
- "Connection": "keep-alive",
46
- "Sec-Fetch-Dest": "empty",
47
- "Sec-Fetch-Mode": "cors",
48
- "Sec-Fetch-Site": "same-site",
49
- "Pragma": "no-cache",
50
- "Cache-Control": "no-cache",
51
- "TE": "trailers",
52
- }
53
-
54
36
  async def request(self,
55
37
  session: Union[AsyncSession, CredentialBase],
56
38
  *,
@@ -66,7 +48,6 @@ class Subscription(ApiBaseModel):
66
48
  request_data = {}
67
49
  async with session if isinstance(session, AsyncSession) else await session.get_session() as sess:
68
50
  # Header
69
- sess.headers.update(await self.necessary_headers(request_data))
70
51
  if override_headers:
71
52
  sess.headers.clear()
72
53
  sess.headers.update(override_headers)
File without changes