novelai-python 0.6.1__tar.gz → 0.7.1__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 (86) hide show
  1. {novelai_python-0.6.1 → novelai_python-0.7.1}/PKG-INFO +18 -7
  2. {novelai_python-0.6.1 → novelai_python-0.7.1}/README.md +17 -6
  3. {novelai_python-0.6.1 → novelai_python-0.7.1}/pyproject.toml +2 -1
  4. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_enum.py +1 -0
  5. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/_enum.py +75 -34
  6. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate/__init__.py +2 -1
  7. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate/_schema.py +1 -1
  8. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_image/__init__.py +82 -23
  9. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/__init__.py +15 -2
  10. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/image_metadata/__init__.py +160 -111
  11. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/image_metadata/lsb_extractor.py +41 -13
  12. novelai_python-0.7.1/src/novelai_python/tool/random_prompt/__init__.py +95 -0
  13. novelai_python-0.7.1/src/novelai_python/tool/random_prompt/generate_scene_composition.py +2960 -0
  14. novelai_python-0.7.1/src/novelai_python/tool/random_prompt/generate_scene_tags.py +2276 -0
  15. novelai_python-0.7.1/src/novelai_python/tool/random_prompt/generate_tags.py +2517 -0
  16. novelai_python-0.7.1/tests/test_random_prompt.py +61 -0
  17. novelai_python-0.7.1/tests/test_tokenizer.py +32 -0
  18. novelai_python-0.6.1/src/novelai_python/tool/image_metadata/_type.py +0 -21
  19. novelai_python-0.6.1/src/novelai_python/tool/random_prompt/__init__.py +0 -367
  20. novelai_python-0.6.1/src/novelai_python/tool/random_prompt/tag.py +0 -7542
  21. novelai_python-0.6.1/src/novelai_python/tool/random_prompt/tag_character.py +0 -270
  22. novelai_python-0.6.1/src/novelai_python/tool/random_prompt/tag_nsfw.py +0 -984
  23. novelai_python-0.6.1/tests/test_random_prompt.py +0 -70
  24. {novelai_python-0.6.1 → novelai_python-0.7.1}/LICENSE +0 -0
  25. {novelai_python-0.6.1 → novelai_python-0.7.1}/NOTICE.MD +0 -0
  26. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/__init__.py +0 -0
  27. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_exceptions.py +0 -0
  28. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/__init__.py +0 -0
  29. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/__init__.py +0 -0
  30. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/generate.py +0 -0
  31. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/generate_image.py +0 -0
  32. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/generate_stream.py +0 -0
  33. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/generate_voice.py +0 -0
  34. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/ai/upscale.py +0 -0
  35. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/schema.py +0 -0
  36. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/user/__init__.py +0 -0
  37. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/user/information.py +0 -0
  38. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/user/login.py +0 -0
  39. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/_response/user/subscription.py +0 -0
  40. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/credential/ApiToken.py +0 -0
  41. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/credential/JwtToken.py +0 -0
  42. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/credential/UserAuth.py +0 -0
  43. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/credential/__init__.py +0 -0
  44. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/credential/_base.py +0 -0
  45. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/__init__.py +0 -0
  46. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/LICENSE +0 -0
  47. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/__init__.py +0 -0
  48. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/_const.py +0 -0
  49. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/_cost.py +0 -0
  50. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/augment_image/__init__.py +0 -0
  51. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/augment_image/_enum.py +0 -0
  52. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate/_const.py +0 -0
  53. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate/_enum.py +0 -0
  54. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate/themes.json +0 -0
  55. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_image/schema.py +0 -0
  56. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_image/suggest_tags.py +0 -0
  57. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_stream.py +0 -0
  58. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_voice/__init__.py +0 -0
  59. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/generate_voice/_enum.py +0 -0
  60. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/ai/upscale.py +0 -0
  61. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/schema.py +0 -0
  62. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/user/__init__.py +0 -0
  63. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/user/information.py +0 -0
  64. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/user/login.py +0 -0
  65. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/sdk/user/subscription.py +0 -0
  66. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/server.py +0 -0
  67. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/clip_simple_tokenizer.py +0 -0
  68. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/nerdstash_v1.model +0 -0
  69. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/nerdstash_v2.model +0 -0
  70. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/novelai.model +0 -0
  71. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tokenizer/novelai_v2.model +0 -0
  72. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/__init__.py +0 -0
  73. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/image_metadata/bch_utils.py +0 -0
  74. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/image_metadata/lsb_injector.py +0 -0
  75. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/tool/paint_mask/__init__.py +0 -0
  76. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/utils/__init__.py +0 -0
  77. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/utils/encode.py +0 -0
  78. {novelai_python-0.6.1 → novelai_python-0.7.1}/src/novelai_python/utils/useful.py +0 -0
  79. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/__init__.py +0 -0
  80. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_generate_voice.py +0 -0
  81. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_server.py +0 -0
  82. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_server_run.py +0 -0
  83. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_upscale.py +0 -0
  84. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_user_information.py +0 -0
  85. {novelai_python-0.6.1 → novelai_python-0.7.1}/tests/test_user_login.py +0 -0
  86. {novelai_python-0.6.1 → novelai_python-0.7.1}/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.6.1
3
+ Version: 0.7.1
4
4
  Summary: NovelAI Python Binding With Pydantic
5
5
  Author-Email: sudoskys <coldlando@hotmail.com>
6
6
  License: Apache License 2.0
@@ -45,7 +45,8 @@ Description-Content-Type: text/markdown
45
45
 
46
46
  ✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
47
47
 
48
- The goal of this repository is to use Pydantic to build legitimate requests to access the NovelAI API service.
48
+ The goal of this repository is to use Pydantic to build legitimate requests to access
49
+ the [NovelAI API service](https://api.novelai.net/docs).
49
50
 
50
51
  > Python >= 3.9 is required.
51
52
 
@@ -60,6 +61,8 @@ The goal of this repository is to use Pydantic to build legitimate requests to a
60
61
  pip -U install novelai-python
61
62
  ```
62
63
 
64
+ All API users must adhere to the NovelAI Terms of Service: https://novelai.net/terms.
65
+
63
66
  **More examples can be found in the [playground](https://github.com/LlmKira/novelai-python/tree/main/playground)
64
67
  directory, read code as documentation.**
65
68
 
@@ -76,6 +79,7 @@ from novelai_python.sdk.ai.generate_image.schema import PositionMap
76
79
 
77
80
  load_dotenv()
78
81
  session = ApiCredential(api_token=SecretStr(os.getenv("NOVELAI_JWT"))) # pst-***
82
+ # For security reasons, storing user credentials in plaintext is strongly discouraged.
79
83
 
80
84
  prompt = "1girl, year 2023,dynamic angle, best quality, amazing quality, very aesthetic, absurdres"
81
85
 
@@ -175,8 +179,12 @@ loop.run_until_complete(chat("Hello"))
175
179
  ```python
176
180
  from novelai_python.tool.random_prompt import RandomPromptGenerator
177
181
 
178
- prompt = RandomPromptGenerator(nsfw_enabled=False).random_prompt()
179
- print(prompt)
182
+ generator = RandomPromptGenerator()
183
+ for i in range(10):
184
+ print(generator.generate_common_tags(nsfw=False))
185
+ print(generator.generate_scene_tags())
186
+ print(generator.generate_scene_composition())
187
+ print(generator.get_holiday_themed_tags())
180
188
  ```
181
189
 
182
190
  #### 📦 Run A Server
@@ -189,10 +197,15 @@ python3 -m novelai_python.server -h '127.0.0.1' -p 7888
189
197
  #### 📦 Tokenizer
190
198
 
191
199
  ```python
192
- from novelai_python._enum import get_tokenizer_model, TextLLMModel
200
+ from novelai_python._enum import get_tokenizer_model, TextLLMModel, TextTokenizerGroup
193
201
  from novelai_python.tokenizer import NaiTokenizer
194
202
 
203
+ # Through llm model name to get the tokenizer
195
204
  tokenizer_package = NaiTokenizer(get_tokenizer_model(TextLLMModel.ERATO))
205
+ # Directly use the tokenizer
206
+ clip_tokenizer = NaiTokenizer(TextTokenizerGroup.CLIP)
207
+
208
+ # Tokenize a text
196
209
  t_text = "a fox jumped over the lazy dog"
197
210
  encode_tokens = tokenizer_package.encode(t_text)
198
211
  print(tokenizer_package.tokenize_text(t_text))
@@ -236,8 +249,6 @@ https://dghs-imgutils.deepghs.org/main/api_doc/operate/censor.html
236
249
 
237
250
  ## 🙏 Acknowledgements
238
251
 
239
- [BackEnd](https://api.novelai.net/docs)
240
-
241
252
  [novelai-api](https://github.com/Aedial/novelai-api)
242
253
 
243
254
  [NovelAI-API](https://github.com/HanaokaYuzu/NovelAI-API)
@@ -7,7 +7,8 @@
7
7
 
8
8
  ✨ NovelAI api python sdk with Pydantic, modern and user-friendly.
9
9
 
10
- The goal of this repository is to use Pydantic to build legitimate requests to access the NovelAI API service.
10
+ The goal of this repository is to use Pydantic to build legitimate requests to access
11
+ the [NovelAI API service](https://api.novelai.net/docs).
11
12
 
12
13
  > Python >= 3.9 is required.
13
14
 
@@ -22,6 +23,8 @@ The goal of this repository is to use Pydantic to build legitimate requests to a
22
23
  pip -U install novelai-python
23
24
  ```
24
25
 
26
+ All API users must adhere to the NovelAI Terms of Service: https://novelai.net/terms.
27
+
25
28
  **More examples can be found in the [playground](https://github.com/LlmKira/novelai-python/tree/main/playground)
26
29
  directory, read code as documentation.**
27
30
 
@@ -38,6 +41,7 @@ from novelai_python.sdk.ai.generate_image.schema import PositionMap
38
41
 
39
42
  load_dotenv()
40
43
  session = ApiCredential(api_token=SecretStr(os.getenv("NOVELAI_JWT"))) # pst-***
44
+ # For security reasons, storing user credentials in plaintext is strongly discouraged.
41
45
 
42
46
  prompt = "1girl, year 2023,dynamic angle, best quality, amazing quality, very aesthetic, absurdres"
43
47
 
@@ -137,8 +141,12 @@ loop.run_until_complete(chat("Hello"))
137
141
  ```python
138
142
  from novelai_python.tool.random_prompt import RandomPromptGenerator
139
143
 
140
- prompt = RandomPromptGenerator(nsfw_enabled=False).random_prompt()
141
- print(prompt)
144
+ generator = RandomPromptGenerator()
145
+ for i in range(10):
146
+ print(generator.generate_common_tags(nsfw=False))
147
+ print(generator.generate_scene_tags())
148
+ print(generator.generate_scene_composition())
149
+ print(generator.get_holiday_themed_tags())
142
150
  ```
143
151
 
144
152
  #### 📦 Run A Server
@@ -151,10 +159,15 @@ python3 -m novelai_python.server -h '127.0.0.1' -p 7888
151
159
  #### 📦 Tokenizer
152
160
 
153
161
  ```python
154
- from novelai_python._enum import get_tokenizer_model, TextLLMModel
162
+ from novelai_python._enum import get_tokenizer_model, TextLLMModel, TextTokenizerGroup
155
163
  from novelai_python.tokenizer import NaiTokenizer
156
164
 
165
+ # Through llm model name to get the tokenizer
157
166
  tokenizer_package = NaiTokenizer(get_tokenizer_model(TextLLMModel.ERATO))
167
+ # Directly use the tokenizer
168
+ clip_tokenizer = NaiTokenizer(TextTokenizerGroup.CLIP)
169
+
170
+ # Tokenize a text
158
171
  t_text = "a fox jumped over the lazy dog"
159
172
  encode_tokens = tokenizer_package.encode(t_text)
160
173
  print(tokenizer_package.tokenize_text(t_text))
@@ -198,8 +211,6 @@ https://dghs-imgutils.deepghs.org/main/api_doc/operate/censor.html
198
211
 
199
212
  ## 🙏 Acknowledgements
200
213
 
201
- [BackEnd](https://api.novelai.net/docs)
202
-
203
214
  [novelai-api](https://github.com/Aedial/novelai-api)
204
215
 
205
216
  [NovelAI-API](https://github.com/HanaokaYuzu/NovelAI-API)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "novelai-python"
3
- version = "0.6.1"
3
+ version = "0.7.1"
4
4
  description = "NovelAI Python Binding With Pydantic"
5
5
  authors = [
6
6
  { name = "sudoskys", email = "coldlando@hotmail.com" },
@@ -59,4 +59,5 @@ distribution = true
59
59
  [tool.pdm.dev-dependencies]
60
60
  dev = [
61
61
  "pre-commit>=3.5.0",
62
+ "wasmtime>=28.0.0",
62
63
  ]
@@ -40,6 +40,7 @@ class TextTokenizerGroup(object):
40
40
  LLAMA3 = "llama3_tokenizer.def"
41
41
  GPT2 = "gpt2_tokenizer.def"
42
42
  CLIP = "clip_tokenizer.def"
43
+ T5 = "t5_tokenizer.def"
43
44
 
44
45
 
45
46
  TextLLMModelTypeAlias = Union[TextLLMModel, str]
@@ -361,6 +361,7 @@ def get_default_noise_schedule(sample_type: Sampler) -> NoiseSchedule:
361
361
 
362
362
 
363
363
  def get_model_group(model: ModelTypeAlias) -> ModelGroups:
364
+ # 一般情况禁止转换
364
365
  if isinstance(model, Enum):
365
366
  model = model.value
366
367
  else:
@@ -387,49 +388,89 @@ def get_model_group(model: ModelTypeAlias) -> ModelGroups:
387
388
  return mapping.get(model, ModelGroups.STABLE_DIFFUSION)
388
389
 
389
390
 
390
- def get_default_uc_preset(model: ModelTypeAlias, uc_preset: int) -> str:
391
- if isinstance(model, Enum):
392
- model = model.value
393
- if isinstance(uc_preset, Enum):
394
- uc_preset = uc_preset.value
391
+ @dataclass
392
+ class UcPrompt:
393
+ category: str
394
+ name: str
395
+ text: str
395
396
 
396
- @dataclass
397
- class UcPrompt:
398
- label: str
399
- text: str
400
397
 
401
- mapper = {
402
- ModelGroups.STABLE_DIFFUSION: [
403
- UcPrompt("lowQualityPlusBadAnatomy",
398
+ def get_uc_preset(model: ModelTypeAlias) -> List[UcPrompt]:
399
+ prompts: List[UcPrompt] = []
400
+ if model in [
401
+ Model.SAFE_DIFFUSION,
402
+ Model.NAI_DIFFUSION,
403
+ Model.NAI_DIFFUSION_INPAINTING,
404
+ Model.SAFE_DIFFUSION_INPAINTING,
405
+ ]:
406
+ prompts = [
407
+ UcPrompt(category="heavy", name="lowQualityPlusBadAnatomy",
404
408
  text="lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry"),
405
- UcPrompt("lowQuality",
409
+ UcPrompt(category="light", name="lowQuality",
406
410
  text="lowres, text, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry"),
407
- UcPrompt("none", text="lowres")
408
- ],
409
- ModelGroups.STABLE_DIFFUSION_GROUP_2: [
410
- UcPrompt("heavy",
411
+ UcPrompt(category="none", name="none", text="lowres"),
412
+ ]
413
+ elif model in [
414
+ Model.NAI_DIFFUSION_FURRY,
415
+ Model.FURRY_DIFFUSION_INPAINTING,
416
+ ]:
417
+ prompts = [
418
+ UcPrompt(category="light", name="lowQuality",
419
+ text="worst quality, low quality, what has science done, what, nightmare fuel, eldritch horror, where is your god now, why"),
420
+ UcPrompt(category="heavy", name="badAnatomy",
421
+ text="{worst quality}, low quality, distracting watermark, [nightmare fuel], {{unfinished}}, deformed, outline, pattern, simple background"),
422
+ UcPrompt(category="none", name="none", text="low res"),
423
+ ]
424
+ elif model in [
425
+ Model.NAI_DIFFUSION_2,
426
+ ]:
427
+ prompts = [
428
+ UcPrompt(category="heavy", name="heavy",
411
429
  text="lowres, bad, text, error, missing, extra, fewer, cropped, jpeg artifacts, worst quality, bad quality, watermark, displeasing, unfinished, chromatic aberration, scan, scan artifacts"),
412
- UcPrompt("light", text="lowres, jpeg artifacts, worst quality, watermark, blurry, very displeasing"),
413
- UcPrompt("none", text="lowres")
414
- ],
415
- ModelGroups.STABLE_DIFFUSION_XL: [
416
- UcPrompt("heavy",
430
+ UcPrompt(category="light", name="light",
431
+ text="lowres, jpeg artifacts, worst quality, watermark, blurry, very displeasing"),
432
+ UcPrompt(category="none", name="none", text="lowres"),
433
+ ]
434
+ elif model in [
435
+ Model.NAI_DIFFUSION_3,
436
+ Model.NAI_DIFFUSION_3_INPAINTING,
437
+ ]:
438
+ prompts = [
439
+ UcPrompt(category="heavy", name="heavy",
417
440
  text="lowres, {bad}, error, fewer, extra, missing, worst quality, jpeg artifacts, bad quality, watermark, unfinished, displeasing, chromatic aberration, signature, extra digits, artistic error, username, scan, [abstract]"),
418
- UcPrompt("light", text="lowres, jpeg artifacts, worst quality, watermark, blurry, very displeasing"),
419
- UcPrompt("humanFocus",
441
+ UcPrompt(category="light", name="light",
442
+ text="lowres, jpeg artifacts, worst quality, watermark, blurry, very displeasing"),
443
+ UcPrompt(category="human", name="humanFocus",
420
444
  text="lowres, {bad}, error, fewer, extra, missing, worst quality, jpeg artifacts, bad quality, watermark, unfinished, displeasing, chromatic aberration, signature, extra digits, artistic error, username, scan, [abstract], bad anatomy, bad hands, @_@, mismatched pupils, heart-shaped pupils, glowing eyes"),
421
- UcPrompt("none", text="lowres")
422
- ],
423
- ModelGroups.STABLE_DIFFUSION_XL_FURRY: [
424
- UcPrompt("heavy",
445
+ UcPrompt(category="none", name="none", text="lowres"),
446
+ ]
447
+ elif model in [
448
+ Model.NAI_DIFFUSION_FURRY_3,
449
+ Model.NAI_DIFFUSION_FURRY_3_INPAINTING,
450
+ ]:
451
+ prompts = [
452
+ UcPrompt(category="heavy", name="heavy",
425
453
  text="{{worst quality}}, [displeasing], {unusual pupils}, guide lines, {{unfinished}}, {bad}, url, artist name, {{tall image}}, mosaic, {sketch page}, comic panel, impact (font), [dated], {logo}, ych, {what}, {where is your god now}, {distorted text}, repeated text, {floating head}, {1994}, {widescreen}, absolutely everyone, sequence, {compression artifacts}, hard translated, {cropped}, {commissioner name}, unknown text, high contrast"),
426
- UcPrompt("light",
454
+ UcPrompt(category="light", name="light",
427
455
  text="{worst quality}, guide lines, unfinished, bad, url, tall image, widescreen, compression artifacts, unknown text"),
428
- UcPrompt("none", text="lowres")
429
- ],
430
- }
431
- model_group = get_model_group(model)
432
- prompts: List[UcPrompt] = mapper.get(model_group, [UcPrompt("none", "lowres")])
456
+ UcPrompt(category="none", name="none", text="lowres"),
457
+ ]
458
+ elif model in [
459
+ Model.CUSTOM,
460
+ Model.NAI_DIFFUSION_4_CURATED_PREVIEW,
461
+ ]:
462
+ prompts = [
463
+ UcPrompt(category="heavy", name="heavy",
464
+ text="blurry, lowres, error, film grain, scan artifacts, worst quality, bad quality, jpeg artifacts, very displeasing, chromatic aberration, logo, dated, signature, multiple views, gigantic breasts"),
465
+ UcPrompt(category="light", name="light",
466
+ text="blurry, lowres, error, worst quality, bad quality, jpeg artifacts, very displeasing, logo, dated, signature"),
467
+ UcPrompt(category="none", name="none", text=""),
468
+ ]
469
+ return prompts
470
+
471
+
472
+ def get_default_uc_preset(model: ModelTypeAlias, uc_preset: int) -> str:
473
+ prompts = get_uc_preset(model)
433
474
  if 0 <= uc_preset < len(prompts):
434
475
  return prompts[uc_preset].text
435
476
  else:
@@ -341,7 +341,8 @@ class LLM(ApiBaseModel):
341
341
  override_headers: Optional[dict] = None
342
342
  ) -> LLMResp:
343
343
  """
344
- 生成图片
344
+ Generate text using NovelAI's large language models. According to our Terms of Service, all generation requests must be initiated by a human action. Automating text or image generation to create excessive load on our systems is not allowed.
345
+
345
346
  :param override_headers:
346
347
  :param session: session
347
348
  :return: LLMStreamResp
@@ -115,7 +115,7 @@ class LLMGenerationParams(BaseModel):
115
115
  repetition_penalty_slope: Optional[Union[float, int]] = Field(default=None, ge=0, allow_inf_nan=False)
116
116
 
117
117
  eos_token_id: int = None
118
- bad_words_ids: List[List[int]] = None
118
+ bad_words_ids: Optional[List[List[int]]] = None
119
119
  logit_bias_groups: Optional[List[LogitBiasGroup]] = Field(default_factory=list)
120
120
 
121
121
  repetition_penalty_frequency: Optional[Union[float, int]] = Field(default=None, allow_inf_nan=False)
@@ -6,7 +6,9 @@ import base64
6
6
  import json
7
7
  import math
8
8
  import random
9
+ import re
9
10
  from copy import deepcopy
11
+ from enum import Enum
10
12
  from io import BytesIO
11
13
  from typing import Optional, Union, Tuple, List
12
14
  from urllib.parse import urlparse
@@ -28,7 +30,7 @@ from novelai_python.sdk.ai._enum import Model, Sampler, NoiseSchedule, ControlNe
28
30
  INPAINTING_MODEL_LIST, get_default_uc_preset, \
29
31
  ModelTypeAlias, UCPresetTypeAlias, get_default_noise_schedule, get_supported_noise_schedule, \
30
32
  get_model_group, ModelGroups, get_supported_params, get_modifiers, ImageBytesTypeAlias
31
- from .schema import Character, V4Prompt, V4NegativePrompt
33
+ from .schema import Character, V4Prompt, V4NegativePrompt, PositionMap
32
34
  from ...schema import ApiBaseModel
33
35
  from ...._exceptions import APIError, AuthError, ConcurrentGenerationError, SessionHttpError
34
36
  from ...._response.ai.generate_image import ImageGenerateResp, RequestParams
@@ -110,7 +112,7 @@ class Params(BaseModel):
110
112
  Overlay Original Image.Prevents the existing image from changing,
111
113
  but can introduce seams along the edge of the mask.
112
114
  """
113
- use_coords: bool = Field(True, description="Use Coordinates")
115
+ use_coords: bool = Field(False, description="Use Coordinates")
114
116
  """Use Coordinates"""
115
117
  characterPrompts: List[Character] = Field(default_factory=list)
116
118
  """Character Prompts"""
@@ -371,15 +373,63 @@ class GenerateImageInfer(ApiBaseModel):
371
373
 
372
374
  # Add negative prompt based on ucPreset
373
375
  if self.parameters.ucPreset is not None:
374
- default_negative_prompt = get_default_uc_preset(self.model, self.parameters.ucPreset)
376
+ uc_preset = self.parameters.ucPreset
377
+ # If ucPreset is Enum, get the value
378
+ if isinstance(self.parameters.ucPreset, Enum):
379
+ uc_preset = self.parameters.ucPreset.value
380
+ default_negative_prompt = get_default_uc_preset(self.model, uc_preset)
375
381
  self.parameters.negative_prompt = ", ".join(
376
382
  filter(None, [default_negative_prompt, self.parameters.negative_prompt])
377
383
  )
378
384
 
379
- # Add quality prompt
385
+ # Add quality prompt based on qualityToggle
380
386
  modifier = get_modifiers(self.model)
381
- if self.parameters.qualityToggle:
382
- self.input += modifier.qualityTags
387
+ quality_tags, suffix = modifier.qualityTags, modifier.suffix
388
+
389
+ def enhance_text_part(_part, _quality_tags, _suffix):
390
+ text_parts = _part.split(" Text:")
391
+ enhanced_text_parts = [
392
+ _quality_tags + text_part + _suffix + ('.' if _suffix and len(text_parts) > 1 else '')
393
+ if index == 0
394
+ else text_part
395
+ for index, text_part in enumerate(text_parts)
396
+ ]
397
+ return " Text:".join(enhanced_text_parts)
398
+
399
+ def enhance_simple_part(_part, _quality_tags, _suffix):
400
+ match = re.search(r"(:[\d.]+$)", _part)
401
+ matched_value = match.group(0) if match else None
402
+ core_part = _part[:-len(matched_value)] if matched_value else _part
403
+ return _quality_tags + core_part + _suffix + (matched_value or "")
404
+
405
+ def enhance_message(_prompt):
406
+ try:
407
+ if get_supported_params(self.model).characterPrompts:
408
+ parts = _prompt.split("|")
409
+ enhanced_parts = [
410
+ quality_tags + part + suffix
411
+ if index == 0 and not get_supported_params(self.model).text
412
+ else enhance_text_part(part, quality_tags, suffix)
413
+ if index == 0
414
+ else part
415
+ for index, part in enumerate(parts)
416
+ ]
417
+ return "|".join(enhanced_parts)
418
+ else:
419
+ parts = _prompt.split("|")
420
+ enhanced_parts = [
421
+ enhance_simple_part(part, quality_tags, suffix)
422
+ for part in parts
423
+ ]
424
+ return "|".join(enhanced_parts)
425
+ except Exception as e:
426
+ logger.error(
427
+ f"Failed to enhance `{_prompt}` because {e},"
428
+ f"Report this issue to https://github.com/LLMKIRA/novelai-python/issues"
429
+ )
430
+ return _prompt
431
+
432
+ self.input = enhance_message(self.input)
383
433
 
384
434
  @model_validator(mode="after")
385
435
  def _build_nai4_prompt(self):
@@ -464,7 +514,30 @@ class GenerateImageInfer(ApiBaseModel):
464
514
  self.parameters.sm_dyn = True
465
515
 
466
516
  if self.parameters.sm_dyn and (not self.parameters.sm):
467
- self.parameters.sm_dyn = False
517
+ self.parameters.sm = True
518
+
519
+ # AUTO SMEA SETTING
520
+ """
521
+ def sm_size_cast(model: Model):
522
+ if model in [
523
+ Model.NAI_DIFFUSION_V3,
524
+ Model.NAI_DIFFUSION_V3_INPAINTING,
525
+ Model.NAI_DIFFUSION_FURRY_V3,
526
+ Model.NAI_DIFFUSION_FURRY_V3_INPAINTING,
527
+ Model.CUSTOM
528
+ ]:
529
+ return 2166785
530
+ return 1064961
531
+ if self.parameters.autoSmea and (self.parameters.width * self.parameters.height < sm_size_cast(self.model)):
532
+ self.parameters.autoSmea = False
533
+ """
534
+
535
+ if self.parameters.characterPrompts:
536
+ self.parameters.use_coords = any([c.center != PositionMap.AUTO for c in self.parameters.characterPrompts])
537
+
538
+ # Mix Prompt Warning
539
+ if self.input.count('|') > 6:
540
+ logger.warning("Maximum prompt mixes exceeded. Extra will be ignored.")
468
541
 
469
542
  if self.parameters.sampler in [
470
543
  Sampler.DDIM,
@@ -572,7 +645,6 @@ class GenerateImageInfer(ApiBaseModel):
572
645
  prompt: str,
573
646
  *,
574
647
  model: Union[Model, str],
575
- qualitySuffix: bool = True,
576
648
  negative_prompt: str = "",
577
649
  ucPreset: UCPresetTypeAlias = UCPreset.TYPE0,
578
650
  sm: bool = False,
@@ -602,7 +674,6 @@ class GenerateImageInfer(ApiBaseModel):
602
674
  :param prompt: Given prompt.
603
675
  :param model: Model for generation.
604
676
  :param negative_prompt: The things you don't want to see in the image.
605
- :param qualitySuffix: Add quality suffix to prompt
606
677
  :param ucPreset: The negative prompt preset.
607
678
  :param steps: The steps for generation.
608
679
  :param seed: The seed for generation.
@@ -619,9 +690,6 @@ class GenerateImageInfer(ApiBaseModel):
619
690
  """
620
691
  if character_prompts is None:
621
692
  character_prompts = []
622
- if qualitySuffix:
623
- modifier = get_modifiers(model)
624
- prompt += modifier.suffix
625
693
  if seed is None:
626
694
  seed = random.randint(0, 4294967295 - 7)
627
695
  if reference_strength_multiple is None:
@@ -668,7 +736,6 @@ class GenerateImageInfer(ApiBaseModel):
668
736
  seed: int = None,
669
737
  extra_noise_seed: int = None,
670
738
  model: Union[Model, str] = Model.NAI_DIFFUSION_3,
671
- qualitySuffix: bool = True,
672
739
  negative_prompt: str = "",
673
740
  ucPreset: UCPresetTypeAlias = UCPreset.TYPE0,
674
741
  steps: int = 23,
@@ -699,7 +766,6 @@ class GenerateImageInfer(ApiBaseModel):
699
766
  :param prompt: Given prompt.
700
767
  :param model: Model for generation.
701
768
  :param negative_prompt: The things you don't want to see in the image.
702
- :param qualitySuffix: Add quality suffix to prompt
703
769
  :param ucPreset: The negative prompt preset.
704
770
  :param steps: The steps for generation.
705
771
  :param seed: The seed for generation.
@@ -716,9 +782,6 @@ class GenerateImageInfer(ApiBaseModel):
716
782
  """
717
783
  if character_prompts is None:
718
784
  character_prompts = []
719
- if qualitySuffix:
720
- modifier = get_modifiers(model)
721
- prompt += modifier.suffix
722
785
  if seed is None:
723
786
  seed = random.randint(0, 4294967295 - 7)
724
787
  if extra_noise_seed is None:
@@ -768,7 +831,6 @@ class GenerateImageInfer(ApiBaseModel):
768
831
  mask: Union[bytes, str],
769
832
  strength: float = 0.7,
770
833
  model: Union[Model, str] = Model.NAI_DIFFUSION_3,
771
- qualitySuffix: bool = True,
772
834
  negative_prompt: str = "",
773
835
  ucPreset: UCPresetTypeAlias = UCPreset.TYPE0,
774
836
  steps: int = 23,
@@ -799,7 +861,6 @@ class GenerateImageInfer(ApiBaseModel):
799
861
  :param prompt: Given prompt.
800
862
  :param model: Model for generation.
801
863
  :param negative_prompt: The things you don't want to see in the image.
802
- :param qualitySuffix: Add quality suffix to prompt
803
864
  :param ucPreset: The negative prompt preset.
804
865
  :param steps: The steps for generation.
805
866
  :param seed: The seed for generation.
@@ -816,9 +877,6 @@ class GenerateImageInfer(ApiBaseModel):
816
877
  """
817
878
  if character_prompts is None:
818
879
  character_prompts = []
819
- if qualitySuffix:
820
- modifier = get_modifiers(model)
821
- prompt += modifier.suffix
822
880
  if seed is None:
823
881
  seed = random.randint(0, 4294967295 - 7)
824
882
  if reference_strength_multiple is None:
@@ -891,7 +949,8 @@ class GenerateImageInfer(ApiBaseModel):
891
949
  override_headers: Optional[dict] = None,
892
950
  ) -> ImageGenerateResp:
893
951
  """
894
- 生成图片
952
+ Generate images using NovelAI's diffusion models. According to our Terms of Service, all generation requests must be initiated by a human action. Automating text or image generation to create excessive load on our systems is not allowed.
953
+
895
954
  :param override_headers: the headers to override
896
955
  :param session: session
897
956
  :return:
@@ -1,5 +1,6 @@
1
1
  import gzip
2
2
  import io
3
+ import json
3
4
  import pathlib
4
5
  import zlib
5
6
  from typing import Dict, List, Optional
@@ -56,6 +57,14 @@ class LLMTokenizerSetting(TokenizerSetting):
56
57
  return self
57
58
 
58
59
 
60
+ def is_clip_model(model_name: str) -> bool:
61
+ return model_name.lower().startswith("clip_tokenizer")
62
+
63
+
64
+ def is_t5_model(model_name: str) -> bool:
65
+ return model_name.lower().startswith("t5_tokenizer")
66
+
67
+
59
68
  class NaiTokenizer:
60
69
  def __init__(self,
61
70
  model: str,
@@ -129,8 +138,10 @@ class NaiTokenizer:
129
138
  decoded_str = self._read_compressed_def(model_bytes)
130
139
  repaired_data = repair_json(decoded_str, return_objects=True)
131
140
  try:
132
- if "clip" in self.model_full_name.lower():
141
+ if is_clip_model(self.model_full_name):
133
142
  self.tokenizer_setting = ClipTokenizerSetting.model_validate(repaired_data)
143
+ elif is_t5_model(self.model_full_name):
144
+ self.tokenizer_setting = json.dumps(repaired_data)
134
145
  else:
135
146
  self.tokenizer_setting = LLMTokenizerSetting.model_validate(repaired_data)
136
147
  except Exception as e:
@@ -140,8 +151,10 @@ class NaiTokenizer:
140
151
  )
141
152
 
142
153
  def _create_tokenizer(self):
143
- if "clip" in self.model_full_name.lower():
154
+ if is_clip_model(self.model_full_name):
144
155
  self._create_clip_tokenizer()
156
+ elif is_t5_model(self.model_full_name):
157
+ self.tokenizer = Tokenizer.from_str(self.tokenizer_setting)
145
158
  else:
146
159
  self._create_bpe_tokenizer()
147
160