novelai-python 0.1.6__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelai_python-0.1.6 → novelai_python-0.1.8}/PKG-INFO +3 -1
- {novelai_python-0.1.6 → novelai_python-0.1.8}/README.md +1 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/pyproject.toml +2 -1
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/__init__.py +7 -2
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/_response/__init__.py +3 -2
- novelai_python-0.1.8/src/novelai_python/_response/user/__init__.py +5 -0
- novelai_python-0.1.8/src/novelai_python/_response/user/subscription.py +52 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/sdk/__init__.py +1 -1
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/sdk/ai/generate_image.py +16 -2
- novelai_python-0.1.8/src/novelai_python/sdk/user/__init__.py +7 -0
- novelai_python-0.1.8/src/novelai_python/sdk/user/subscription.py +81 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/server.py +21 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/__init__.py +5 -2
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/hash.py +43 -5
- novelai_python-0.1.8/tests/test_server_run.py +46 -0
- novelai_python-0.1.6/tests/test_server_run.py +0 -28
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/_exceptions.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/_response/ai/__init__.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/_response/ai/generate_image.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/credential/JwtToken.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/credential/__init__.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/sdk/ai/__init__.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/__init__.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/tag.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/tag_artist.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/tag_nsfw.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/tests/__init__.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/tests/test_random_prompt.py +0 -0
- {novelai_python-0.1.6 → novelai_python-0.1.8}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: novelai-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Novelai Python Binding With Pydantic
|
|
5
5
|
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -17,6 +17,7 @@ Requires-Dist: Pillow>=10.2.0
|
|
|
17
17
|
Requires-Dist: curl-cffi>=0.5.10
|
|
18
18
|
Requires-Dist: fastapi>=0.109.0
|
|
19
19
|
Requires-Dist: uvicorn[standard]>=0.27.0.post1
|
|
20
|
+
Requires-Dist: numpy>=1.24.4
|
|
20
21
|
Requires-Dist: pytest>=7.4.4; extra == "testing"
|
|
21
22
|
Provides-Extra: testing
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
@@ -31,6 +32,7 @@ The goal of this repository is to use Pydantic to build legitimate requests to a
|
|
|
31
32
|
### Roadmap 🚧
|
|
32
33
|
|
|
33
34
|
- [x] /ai/generate-image
|
|
35
|
+
- [x] /user/subscription
|
|
34
36
|
- [ ] /ai/generate-image/suggest-tags
|
|
35
37
|
- [ ] /ai/annotate-image
|
|
36
38
|
- [ ] /ai/classify
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "novelai-python"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.8"
|
|
4
4
|
description = "Novelai Python Binding With Pydantic"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "sudoskys", email = "coldlando@hotmail.com" },
|
|
@@ -16,6 +16,7 @@ dependencies = [
|
|
|
16
16
|
"curl-cffi>=0.5.10",
|
|
17
17
|
"fastapi>=0.109.0",
|
|
18
18
|
"uvicorn[standard]>=0.27.0.post1",
|
|
19
|
+
"numpy>=1.24.4",
|
|
19
20
|
]
|
|
20
21
|
requires-python = ">=3.8"
|
|
21
22
|
readme = "README.md"
|
|
@@ -9,14 +9,19 @@ from ._exceptions import (
|
|
|
9
9
|
AuthError,
|
|
10
10
|
NovelAiError,
|
|
11
11
|
) # noqa: F401, F403
|
|
12
|
-
from ._response import ImageGenerateResp # noqa: F401, F403
|
|
13
12
|
from .credential import JwtCredential # noqa: F401, F403
|
|
14
|
-
from .sdk.ai import GenerateImageInfer # noqa: F401, F403
|
|
13
|
+
from .sdk.ai import GenerateImageInfer, ImageGenerateResp # noqa: F401, F403
|
|
14
|
+
from .sdk.user import Subscription, SubscriptionResp # noqa: F401, F403
|
|
15
15
|
|
|
16
16
|
__all__ = [
|
|
17
17
|
"GenerateImageInfer",
|
|
18
18
|
"ImageGenerateResp",
|
|
19
|
+
|
|
20
|
+
"Subscription",
|
|
21
|
+
"SubscriptionResp",
|
|
22
|
+
|
|
19
23
|
"JwtCredential",
|
|
24
|
+
|
|
20
25
|
"APIError",
|
|
21
26
|
"AuthError",
|
|
22
27
|
"NovelAiError",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Time : 2024/2/7 上午9:57
|
|
3
|
+
# @Author : sudoskys
|
|
4
|
+
# @File : subscription.py
|
|
5
|
+
# @Software: PyCharm
|
|
6
|
+
from typing import Optional, Dict, Any, List
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TrainingSteps(BaseModel):
|
|
12
|
+
fixedTrainingStepsLeft: int
|
|
13
|
+
purchasedTrainingSteps: int
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ImageGenerationLimit(BaseModel):
|
|
17
|
+
resolution: int
|
|
18
|
+
maxPrompts: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Perks(BaseModel):
|
|
22
|
+
maxPriorityActions: int
|
|
23
|
+
startPriority: int
|
|
24
|
+
moduleTrainingSteps: int
|
|
25
|
+
unlimitedMaxPriority: bool
|
|
26
|
+
voiceGeneration: bool
|
|
27
|
+
imageGeneration: bool
|
|
28
|
+
unlimitedImageGeneration: bool
|
|
29
|
+
unlimitedImageGenerationLimits: List[ImageGenerationLimit]
|
|
30
|
+
contextTokens: int
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SubscriptionResp(BaseModel):
|
|
34
|
+
tier: int = Field(..., description="Subscription tier")
|
|
35
|
+
active: bool = Field(..., description="Subscription status")
|
|
36
|
+
expiresAt: int = Field(..., description="Subscription expiration time")
|
|
37
|
+
perks: Perks = Field(..., description="Subscription perks")
|
|
38
|
+
paymentProcessorData: Optional[Dict[Any, Any]]
|
|
39
|
+
trainingStepsLeft: TrainingSteps = Field(..., description="Training steps left")
|
|
40
|
+
accountType: int = Field(..., description="Account type")
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def is_active(self):
|
|
44
|
+
return self.active
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def anlas_left(self):
|
|
48
|
+
return self.trainingStepsLeft.fixedTrainingStepsLeft
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def is_unlimited_image_generation(self):
|
|
52
|
+
return self.perks.unlimitedImageGeneration
|
|
@@ -12,6 +12,7 @@ from zipfile import ZipFile
|
|
|
12
12
|
import httpx
|
|
13
13
|
from curl_cffi.requests import AsyncSession
|
|
14
14
|
from loguru import logger
|
|
15
|
+
from novelai_python.utils import NovelAiMetadata
|
|
15
16
|
from pydantic import BaseModel, ConfigDict, PrivateAttr, field_validator, model_validator
|
|
16
17
|
|
|
17
18
|
from ..._exceptions import APIError, AuthError
|
|
@@ -229,7 +230,15 @@ class GenerateImageInfer(BaseModel):
|
|
|
229
230
|
parameters=cls.Params(**param)
|
|
230
231
|
)
|
|
231
232
|
|
|
232
|
-
async def generate(self, session: Union[AsyncSession, JwtCredential]
|
|
233
|
+
async def generate(self, session: Union[AsyncSession, JwtCredential],
|
|
234
|
+
*,
|
|
235
|
+
remove_sign: bool = False) -> ImageGenerateResp:
|
|
236
|
+
"""
|
|
237
|
+
生成图片
|
|
238
|
+
:param session: session
|
|
239
|
+
:param remove_sign: 移除追踪信息
|
|
240
|
+
:return:
|
|
241
|
+
"""
|
|
233
242
|
if isinstance(session, JwtCredential):
|
|
234
243
|
session = session.session
|
|
235
244
|
request_data = self.model_dump(exclude_none=True)
|
|
@@ -274,7 +283,12 @@ class GenerateImageInfer(BaseModel):
|
|
|
274
283
|
response=try_jsonfy(response.content)
|
|
275
284
|
)
|
|
276
285
|
for filename in file_list:
|
|
277
|
-
|
|
286
|
+
data = zip_file.read(filename)
|
|
287
|
+
if remove_sign:
|
|
288
|
+
data = NovelAiMetadata.rehash(BytesIO(data), remove_stealth=True)
|
|
289
|
+
if not isinstance(data, bytes):
|
|
290
|
+
data = data.getvalue()
|
|
291
|
+
unzip_content.append((filename, data))
|
|
278
292
|
return ImageGenerateResp(
|
|
279
293
|
meta=ImageGenerateResp.RequestParams(
|
|
280
294
|
endpoint=self.base_url,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Time : 2024/2/7 上午10:04
|
|
3
|
+
# @Author : sudoskys
|
|
4
|
+
# @File : subscription.py.py
|
|
5
|
+
# @Software: PyCharm
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
from curl_cffi.requests import AsyncSession
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from pydantic import BaseModel, PrivateAttr
|
|
12
|
+
|
|
13
|
+
from ... import APIError, AuthError
|
|
14
|
+
from ..._response.user.subscription import SubscriptionResp
|
|
15
|
+
from ...credential import JwtCredential
|
|
16
|
+
from ...utils import try_jsonfy
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Subscription(BaseModel):
|
|
20
|
+
_endpoint: Optional[str] = PrivateAttr("https://api.novelai.net")
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def base_url(self):
|
|
24
|
+
return f"{self.endpoint.strip('/')}/user/subscription"
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def endpoint(self):
|
|
28
|
+
return self._endpoint
|
|
29
|
+
|
|
30
|
+
@endpoint.setter
|
|
31
|
+
def endpoint(self, value):
|
|
32
|
+
self._endpoint = value
|
|
33
|
+
|
|
34
|
+
async def request(self,
|
|
35
|
+
session: Union[AsyncSession, JwtCredential],
|
|
36
|
+
) -> SubscriptionResp:
|
|
37
|
+
"""
|
|
38
|
+
Request to get user subscription information
|
|
39
|
+
:param session:
|
|
40
|
+
:return:
|
|
41
|
+
"""
|
|
42
|
+
if isinstance(session, JwtCredential):
|
|
43
|
+
session = session.session
|
|
44
|
+
request_data = {}
|
|
45
|
+
logger.debug("Subscription")
|
|
46
|
+
try:
|
|
47
|
+
assert hasattr(session, "get"), "session must have get method."
|
|
48
|
+
response = await session.get(
|
|
49
|
+
self.base_url,
|
|
50
|
+
)
|
|
51
|
+
if "application/json" not in response.headers.get('Content-Type'):
|
|
52
|
+
logger.error(f"Unexpected content type: {response.headers.get('Content-Type')}")
|
|
53
|
+
try:
|
|
54
|
+
_msg = response.json()
|
|
55
|
+
except Exception:
|
|
56
|
+
raise APIError(
|
|
57
|
+
message=f"Unexpected content type: {response.headers.get('Content-Type')}",
|
|
58
|
+
request=request_data,
|
|
59
|
+
status_code=response.status_code,
|
|
60
|
+
response=try_jsonfy(response.content)
|
|
61
|
+
)
|
|
62
|
+
status_code = _msg.get("statusCode", response.status_code)
|
|
63
|
+
message = _msg.get("message", "Unknown error")
|
|
64
|
+
if status_code in [400, 401, 402]:
|
|
65
|
+
# 400 : validation error
|
|
66
|
+
# 401 : unauthorized
|
|
67
|
+
# 402 : payment required
|
|
68
|
+
# 409 : conflict
|
|
69
|
+
raise AuthError(message, request=request_data, status_code=status_code, response=_msg)
|
|
70
|
+
if status_code in [500]:
|
|
71
|
+
# An unknown error occured.
|
|
72
|
+
raise APIError(message, request=request_data, status_code=status_code, response=_msg)
|
|
73
|
+
raise APIError(message, request=request_data, status_code=status_code, response=_msg)
|
|
74
|
+
return SubscriptionResp.model_validate(response.json())
|
|
75
|
+
except httpx.HTTPError as exc:
|
|
76
|
+
raise RuntimeError(f"An HTTP error occurred: {exc}")
|
|
77
|
+
except APIError as e:
|
|
78
|
+
raise e
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.opt(exception=e).exception("An Unexpected error occurred")
|
|
81
|
+
raise e
|
|
@@ -10,10 +10,12 @@ import zipfile
|
|
|
10
10
|
import uvicorn
|
|
11
11
|
from fastapi import FastAPI, Depends, Security
|
|
12
12
|
from fastapi.security import APIKeyHeader
|
|
13
|
+
from loguru import logger
|
|
13
14
|
from starlette.responses import JSONResponse, StreamingResponse
|
|
14
15
|
|
|
15
16
|
from .credential import JwtCredential, SecretStr
|
|
16
17
|
from .sdk.ai.generate_image import GenerateImageInfer
|
|
18
|
+
from .sdk.user.subscription import Subscription
|
|
17
19
|
|
|
18
20
|
app = FastAPI()
|
|
19
21
|
token_key = APIKeyHeader(name="Authorization")
|
|
@@ -35,6 +37,24 @@ async def health():
|
|
|
35
37
|
return {"status": "ok"}
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
@app.get("/user/subscription")
|
|
41
|
+
async def subscription(
|
|
42
|
+
current_token: str = Depends(get_current_token)
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
订阅
|
|
46
|
+
:param current_token: Authorization
|
|
47
|
+
:param req: Subscription
|
|
48
|
+
:return:
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
_result = await Subscription().request(session=get_session(current_token))
|
|
52
|
+
return _result.model_dump()
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.exception(e)
|
|
55
|
+
return JSONResponse(status_code=500, content=e.__dict__)
|
|
56
|
+
|
|
57
|
+
|
|
38
58
|
@app.post("/ai/generate_image")
|
|
39
59
|
async def generate_image(
|
|
40
60
|
req: GenerateImageInfer,
|
|
@@ -58,6 +78,7 @@ async def generate_image(
|
|
|
58
78
|
'Content-Disposition': 'attachment;filename=multiple_files.zip'
|
|
59
79
|
})
|
|
60
80
|
except Exception as e:
|
|
81
|
+
logger.exception(e)
|
|
61
82
|
return JSONResponse(status_code=500, content=e.__dict__)
|
|
62
83
|
|
|
63
84
|
|
|
@@ -12,13 +12,16 @@ from loguru import logger
|
|
|
12
12
|
from .hash import NovelAiMetadata
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def png_info_reset(img: Union[str, io.BytesIO]
|
|
15
|
+
def png_info_reset(img: Union[str, io.BytesIO],
|
|
16
|
+
*,
|
|
17
|
+
remove_stealth: bool = False):
|
|
16
18
|
"""
|
|
17
19
|
reset png info hash value
|
|
20
|
+
:param remove_stealth: LCB
|
|
18
21
|
:param img: BytesIO 对象
|
|
19
22
|
:return:
|
|
20
23
|
"""
|
|
21
|
-
_fixed = NovelAiMetadata.rehash(img_io=img)
|
|
24
|
+
_fixed = NovelAiMetadata.rehash(img_io=img, remove_stealth=remove_stealth)
|
|
22
25
|
return _fixed
|
|
23
26
|
|
|
24
27
|
|
|
@@ -10,6 +10,7 @@ import json
|
|
|
10
10
|
from io import BytesIO
|
|
11
11
|
from typing import Union
|
|
12
12
|
|
|
13
|
+
import numpy as np
|
|
13
14
|
from PIL import Image
|
|
14
15
|
from PIL.PngImagePlugin import PngInfo
|
|
15
16
|
from loguru import logger
|
|
@@ -35,16 +36,33 @@ class NovelAiMetadata(BaseModel):
|
|
|
35
36
|
return {"Title": self.title, "Description": self.description, "Comment": self.comment}
|
|
36
37
|
|
|
37
38
|
@staticmethod
|
|
38
|
-
def rehash(img_io):
|
|
39
|
-
cls =
|
|
39
|
+
def rehash(img_io, remove_stealth: bool = False):
|
|
40
|
+
cls = NovelAiMetadata.build_from_img(img_io)
|
|
40
41
|
cls.comment["signed_hash"] = sign_message(json.dumps(cls.description), "novalai-client")
|
|
41
|
-
cls.write_out(img_io=img_io)
|
|
42
|
-
return
|
|
42
|
+
_new_img_io = cls.write_out(img_io=img_io, remove_stealth=remove_stealth)
|
|
43
|
+
return _new_img_io
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
@staticmethod
|
|
46
|
+
def remove_stealth_info(img_io):
|
|
47
|
+
image = Image.open(img_io).convert('RGBA')
|
|
48
|
+
data = np.array(image)
|
|
49
|
+
data[..., 3] = 254
|
|
50
|
+
new_image = Image.fromarray(data, 'RGBA')
|
|
51
|
+
_new_img_io = BytesIO()
|
|
52
|
+
new_image.save(_new_img_io, format="PNG")
|
|
53
|
+
return _new_img_io
|
|
54
|
+
|
|
55
|
+
def write_out(self, img_io: BytesIO, *, remove_stealth: bool = False):
|
|
45
56
|
with Image.open(img_io) as img:
|
|
57
|
+
if remove_stealth:
|
|
58
|
+
img = img.convert('RGBA')
|
|
59
|
+
data = np.array(img)
|
|
60
|
+
data[..., 3] = 254
|
|
61
|
+
img = Image.fromarray(data, 'RGBA')
|
|
46
62
|
metadata = PngInfo()
|
|
47
63
|
for k, v in self.metadata.items():
|
|
64
|
+
if isinstance(v, dict):
|
|
65
|
+
v = json.dumps(v)
|
|
48
66
|
metadata.add_text(k, v)
|
|
49
67
|
_new_img = BytesIO()
|
|
50
68
|
img.save(_new_img, format="PNG", pnginfo=metadata, quality=95, optimize=False)
|
|
@@ -88,3 +106,23 @@ def decode_base64(encoded_data):
|
|
|
88
106
|
# 使用base64解码
|
|
89
107
|
decoded_data = base64.b64decode(byte_data)
|
|
90
108
|
return decoded_data.decode("UTF-8") # 再次使用UTF-8将字节对象转为字符串
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def decode_image(img: Union[str, BytesIO], output_path: str):
|
|
112
|
+
encoded_image = Image.open(img)
|
|
113
|
+
red_channel = encoded_image.split()[3]
|
|
114
|
+
x_size = encoded_image.size[0]
|
|
115
|
+
y_size = encoded_image.size[1]
|
|
116
|
+
decoded_image = Image.new("RGB", encoded_image.size)
|
|
117
|
+
pixels = decoded_image.load()
|
|
118
|
+
for i in range(x_size):
|
|
119
|
+
for j in range(y_size):
|
|
120
|
+
if i < 7: # the left 7 columns
|
|
121
|
+
r = red_channel.getpixel((i, j))
|
|
122
|
+
if r > 254: # above the threshold
|
|
123
|
+
pixels[i, j] = (0, 0, 0) # black
|
|
124
|
+
else:
|
|
125
|
+
pixels[i, j] = (255, 255, 255) # white
|
|
126
|
+
else:
|
|
127
|
+
pixels[i, j] = (255, 255, 255) # default to white for right part of image
|
|
128
|
+
decoded_image.save(output_path)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Time : 2024/1/30 下午11:52
|
|
3
|
+
# @Author : sudoskys
|
|
4
|
+
# @File : test_server_run.py
|
|
5
|
+
# @Software: PyCharm
|
|
6
|
+
|
|
7
|
+
from unittest.mock import patch
|
|
8
|
+
|
|
9
|
+
from fastapi.testclient import TestClient
|
|
10
|
+
|
|
11
|
+
from src.novelai_python.server import app, get_session
|
|
12
|
+
|
|
13
|
+
client = TestClient(app)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_health_check():
|
|
17
|
+
response = client.get("/health")
|
|
18
|
+
assert response.status_code == 200
|
|
19
|
+
assert response.json() == {"status": "ok"}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@patch('src.novelai_python.server.Subscription')
|
|
23
|
+
def test_subscription_without_api_key(mock_subscription):
|
|
24
|
+
mock_subscription.return_value.request.return_value.model_dump.return_value = {"status": "subscribed"}
|
|
25
|
+
response = client.get("/user/subscription")
|
|
26
|
+
assert response.status_code == 403
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@patch('src.novelai_python.server.GenerateImageInfer')
|
|
30
|
+
def test_generate_image_without_api_key(mock_generate_image):
|
|
31
|
+
mock_generate_image.return_value.generate.return_value = {"status": "image generated"}
|
|
32
|
+
response = client.post("/ai/generate_image")
|
|
33
|
+
assert response.status_code == 403
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_get_session_new_token():
|
|
37
|
+
token = "new_token"
|
|
38
|
+
session = get_session(token)
|
|
39
|
+
assert session.jwt_token.get_secret_value() == token
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_get_session_existing_token():
|
|
43
|
+
token = "existing_token"
|
|
44
|
+
get_session(token)
|
|
45
|
+
session = get_session(token)
|
|
46
|
+
assert session.jwt_token.get_secret_value() == token
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# @Time : 2024/1/30 下午11:52
|
|
3
|
-
# @Author : sudoskys
|
|
4
|
-
# @File : test_server_run.py
|
|
5
|
-
# @Software: PyCharm
|
|
6
|
-
from fastapi.testclient import TestClient
|
|
7
|
-
|
|
8
|
-
from src.novelai_python.sdk.ai.generate_image import GenerateImageInfer
|
|
9
|
-
from src.novelai_python.server import app, get_session
|
|
10
|
-
|
|
11
|
-
client = TestClient(app)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def test_health_check():
|
|
15
|
-
response = client.get("/health")
|
|
16
|
-
assert response.status_code == 200
|
|
17
|
-
assert response.json() == {"status": "ok"}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_generate_image_with_valid_token():
|
|
21
|
-
valid_token = "valid_token"
|
|
22
|
-
get_session(valid_token) # to simulate a valid session
|
|
23
|
-
response = client.post(
|
|
24
|
-
"/ai/generate_image",
|
|
25
|
-
headers={"Authorization": valid_token},
|
|
26
|
-
json=GenerateImageInfer(input="1girl").model_dump()
|
|
27
|
-
)
|
|
28
|
-
assert response.status_code == 500
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/_response/ai/generate_image.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/tag_artist.py
RENAMED
|
File without changes
|
{novelai_python-0.1.6 → novelai_python-0.1.8}/src/novelai_python/utils/random_prompt/tag_nsfw.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|