llms-py 3.0.5__py3-none-any.whl → 3.0.7__py3-none-any.whl
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.
- llms/extensions/analytics/ui/index.mjs +1 -1
- llms/extensions/app/__init__.py +3 -1
- llms/extensions/providers/__init__.py +2 -0
- llms/extensions/providers/anthropic.py +1 -1
- llms/extensions/providers/chutes.py +7 -9
- llms/extensions/providers/nvidia.py +9 -11
- llms/extensions/providers/openai.py +1 -3
- llms/extensions/providers/zai.py +182 -0
- llms/llms.json +15 -3
- llms/main.py +32 -6
- llms/providers-extra.json +38 -0
- llms/providers.json +1 -1
- llms/ui/ai.mjs +1 -1
- llms/ui/app.css +92 -7
- llms/ui/modules/chat/ChatBody.mjs +2 -2
- llms/ui/modules/chat/index.mjs +2 -2
- llms/ui/utils.mjs +4 -4
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/METADATA +1 -1
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/RECORD +23 -22
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/WHEEL +0 -0
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.5.dist-info → llms_py-3.0.7.dist-info}/top_level.txt +0 -0
|
@@ -370,7 +370,7 @@ export const Analytics = {
|
|
|
370
370
|
</div>
|
|
371
371
|
<div>
|
|
372
372
|
<div class="text-xs text-gray-500 dark:text-gray-400 font-medium">Duration</div>
|
|
373
|
-
<div v-if="request.duration" class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $fmt.humanifyMs(request.duration) }}</div>
|
|
373
|
+
<div v-if="request.duration" class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $fmt.humanifyMs(request.duration * 1000) }}</div>
|
|
374
374
|
</div>
|
|
375
375
|
<div>
|
|
376
376
|
<div class="text-xs text-gray-500 dark:text-gray-400 font-medium">Speed</div>
|
llms/extensions/app/__init__.py
CHANGED
|
@@ -444,7 +444,9 @@ def install(ctx):
|
|
|
444
444
|
input_tokens = usage.get("prompt_tokens", 0)
|
|
445
445
|
output_tokens = usage.get("completion_tokens", 0)
|
|
446
446
|
total_tokens = usage.get("total_tokens", input_tokens + output_tokens)
|
|
447
|
-
cost =
|
|
447
|
+
cost = usage.get("cost") or o.get(
|
|
448
|
+
"cost", ((input_price * input_tokens) + (output_price * output_tokens)) / 1000000
|
|
449
|
+
)
|
|
448
450
|
|
|
449
451
|
request = {
|
|
450
452
|
"user": user,
|
|
@@ -5,6 +5,7 @@ from .google import install_google
|
|
|
5
5
|
from .nvidia import install_nvidia
|
|
6
6
|
from .openai import install_openai
|
|
7
7
|
from .openrouter import install_openrouter
|
|
8
|
+
from .zai import install_zai
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def install(ctx):
|
|
@@ -15,6 +16,7 @@ def install(ctx):
|
|
|
15
16
|
install_nvidia(ctx)
|
|
16
17
|
install_openai(ctx)
|
|
17
18
|
install_openrouter(ctx)
|
|
19
|
+
install_zai(ctx)
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
__install__ = install
|
|
@@ -221,7 +221,7 @@ def install_anthropic(ctx):
|
|
|
221
221
|
# Add metadata
|
|
222
222
|
if "metadata" not in ret:
|
|
223
223
|
ret["metadata"] = {}
|
|
224
|
-
ret["metadata"]["duration"] = int(
|
|
224
|
+
ret["metadata"]["duration"] = int(time.time() - started_at)
|
|
225
225
|
|
|
226
226
|
if chat is not None and "model" in chat:
|
|
227
227
|
cost = self.model_cost(chat["model"])
|
|
@@ -66,15 +66,13 @@ def install_chutes(ctx):
|
|
|
66
66
|
if chat["model"] in self.model_negative_prompt:
|
|
67
67
|
payload["negative_prompt"] = self.negative_prompt
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
payload["width"] = width
|
|
77
|
-
payload["height"] = height
|
|
69
|
+
aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
|
|
70
|
+
dimension = ctx.app.aspect_ratios.get(aspect_ratio)
|
|
71
|
+
if dimension:
|
|
72
|
+
w, h = dimension.split("×")
|
|
73
|
+
width, height = int(w), int(h)
|
|
74
|
+
payload["width"] = width
|
|
75
|
+
payload["height"] = height
|
|
78
76
|
|
|
79
77
|
if chat["model"] in self.model_resolutions:
|
|
80
78
|
# if models use resolution, remove width and height
|
|
@@ -66,17 +66,15 @@ def install_nvidia(ctx):
|
|
|
66
66
|
}
|
|
67
67
|
modalities = chat.get("modalities", ["text"])
|
|
68
68
|
if "image" in modalities:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
gen_request["width"] = self.width
|
|
79
|
-
gen_request["height"] = self.height
|
|
69
|
+
aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
|
|
70
|
+
dimension = ctx.app.aspect_ratios.get(aspect_ratio)
|
|
71
|
+
if dimension:
|
|
72
|
+
width, height = dimension.split("×")
|
|
73
|
+
gen_request["width"] = int(width)
|
|
74
|
+
gen_request["height"] = int(height)
|
|
75
|
+
else:
|
|
76
|
+
gen_request["width"] = self.width
|
|
77
|
+
gen_request["height"] = self.height
|
|
80
78
|
|
|
81
79
|
gen_request["mode"] = self.mode
|
|
82
80
|
gen_request["cfg_scale"] = self.cfg_scale
|
|
@@ -119,9 +119,7 @@ def install_openai(ctx):
|
|
|
119
119
|
if chat["model"] in self.map_image_models:
|
|
120
120
|
chat["model"] = self.map_image_models[chat["model"]]
|
|
121
121
|
|
|
122
|
-
aspect_ratio = "1:1"
|
|
123
|
-
if "image_config" in chat and "aspect_ratio" in chat["image_config"]:
|
|
124
|
-
aspect_ratio = chat["image_config"].get("aspect_ratio", "1:1")
|
|
122
|
+
aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
|
|
125
123
|
payload = {
|
|
126
124
|
"model": chat["model"],
|
|
127
125
|
"prompt": ctx.last_user_prompt(chat),
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def install_zai(ctx):
|
|
9
|
+
from llms.main import GeneratorBase
|
|
10
|
+
|
|
11
|
+
# https://docs.z.ai/guides/image/glm-image
|
|
12
|
+
class ZaiGenerator(GeneratorBase):
|
|
13
|
+
sdk = "zai/image"
|
|
14
|
+
|
|
15
|
+
def __init__(self, **kwargs):
|
|
16
|
+
super().__init__(**kwargs)
|
|
17
|
+
self.aspect_ratios = {
|
|
18
|
+
"1:1": "1280×1280",
|
|
19
|
+
"2:3": "1056×1568",
|
|
20
|
+
"3:2": "1568×1056",
|
|
21
|
+
"3:4": "1088×1472",
|
|
22
|
+
"4:3": "1472×1088",
|
|
23
|
+
"4:5": "1088×1472",
|
|
24
|
+
"5:4": "1472×1088",
|
|
25
|
+
"9:16": "960×1728",
|
|
26
|
+
"16:9": "1728×960",
|
|
27
|
+
"21:9": "1728×960",
|
|
28
|
+
}
|
|
29
|
+
self.model: str = kwargs.get("model", "glm-image")
|
|
30
|
+
self.n: Optional[int] = kwargs.get("n")
|
|
31
|
+
self.quality: Optional[str] = kwargs.get("quality")
|
|
32
|
+
self.response_format: Optional[str] = kwargs.get("response_format")
|
|
33
|
+
self.size: Optional[str] = kwargs.get("size")
|
|
34
|
+
self.style: Optional[str] = kwargs.get("style")
|
|
35
|
+
self.sensitive_word_check: Optional[str] = kwargs.get("sensitive_word_check")
|
|
36
|
+
self.user: Optional[str] = kwargs.get("user")
|
|
37
|
+
self.request_id: Optional[str] = kwargs.get("request_id")
|
|
38
|
+
self.user_id: Optional[str] = kwargs.get("user_id")
|
|
39
|
+
self.extra_headers: Optional[dict] = kwargs.get("extra_headers")
|
|
40
|
+
self.extra_body: Optional[dict] = kwargs.get("extra_body")
|
|
41
|
+
self.disable_strict_validation: Optional[bool] = kwargs.get("disable_strict_validation")
|
|
42
|
+
self.timeout: Optional[float] = float(kwargs.get("timeout") or 300)
|
|
43
|
+
self.watermark_enabled: Optional[bool] = kwargs.get("watermark_enabled")
|
|
44
|
+
|
|
45
|
+
async def chat(self, chat, provider=None, context=None):
|
|
46
|
+
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
|
|
47
|
+
if self.extra_headers:
|
|
48
|
+
headers.update(self.extra_headers)
|
|
49
|
+
|
|
50
|
+
chat_url = "https://api.z.ai/api/paas/v4/images/generations"
|
|
51
|
+
if provider is not None:
|
|
52
|
+
headers["Authorization"] = f"Bearer {provider.api_key}"
|
|
53
|
+
chat["model"] = provider.provider_model(chat["model"]) or chat["model"]
|
|
54
|
+
chat_url = provider.api + "/images/generations"
|
|
55
|
+
|
|
56
|
+
body = {}
|
|
57
|
+
attrs = [
|
|
58
|
+
"model",
|
|
59
|
+
"n",
|
|
60
|
+
"quality",
|
|
61
|
+
"response_format",
|
|
62
|
+
"size",
|
|
63
|
+
"style",
|
|
64
|
+
"sensitive_word_check",
|
|
65
|
+
"user",
|
|
66
|
+
"request_id",
|
|
67
|
+
"user_id",
|
|
68
|
+
"disable_strict_validation",
|
|
69
|
+
"watermark_enabled",
|
|
70
|
+
]
|
|
71
|
+
for attr in attrs:
|
|
72
|
+
if hasattr(self, attr) and getattr(self, attr) is not None:
|
|
73
|
+
body[attr] = getattr(self, attr)
|
|
74
|
+
|
|
75
|
+
if self.extra_body:
|
|
76
|
+
body.update(self.extra_body)
|
|
77
|
+
|
|
78
|
+
if "model" in chat:
|
|
79
|
+
body["model"] = chat["model"]
|
|
80
|
+
|
|
81
|
+
body["prompt"] = ctx.last_user_prompt(chat)
|
|
82
|
+
|
|
83
|
+
aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
|
|
84
|
+
size = self.aspect_ratios.get(aspect_ratio, "1280x1280").replace("×", "x")
|
|
85
|
+
body["size"] = size
|
|
86
|
+
|
|
87
|
+
username = ctx.context_to_username(context)
|
|
88
|
+
if username:
|
|
89
|
+
body["user"] = username
|
|
90
|
+
|
|
91
|
+
ctx.dbg(f"ZaiProvider.chat: {chat_url}")
|
|
92
|
+
ctx.dbg(json.dumps(body, indent=2))
|
|
93
|
+
started_at = time.time()
|
|
94
|
+
async with aiohttp.ClientSession() as session, session.post(
|
|
95
|
+
chat_url,
|
|
96
|
+
headers=headers,
|
|
97
|
+
data=json.dumps(body),
|
|
98
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
|
99
|
+
) as response:
|
|
100
|
+
# Example Response
|
|
101
|
+
# {
|
|
102
|
+
# "created": 1768451303,
|
|
103
|
+
# "data": [
|
|
104
|
+
# {
|
|
105
|
+
# "url": "https://mfile.z.ai/1768451374203-b334959408a643a8a6c74eb104746dcb.png?ufileattname=202601151228236805d575507d4570_watermark.png"
|
|
106
|
+
# }
|
|
107
|
+
# ],
|
|
108
|
+
# "id": "202601151228236805d575507d4570",
|
|
109
|
+
# "request_id": "202601151228236805d575507d4570",
|
|
110
|
+
# "usage": {
|
|
111
|
+
# "tokens": 0,
|
|
112
|
+
# "price": 0,
|
|
113
|
+
# "cost": 0.0,
|
|
114
|
+
# "duration": 71
|
|
115
|
+
# },
|
|
116
|
+
# "timestamp": 1768451374519,
|
|
117
|
+
# "model": "GLM-Image"
|
|
118
|
+
# }
|
|
119
|
+
|
|
120
|
+
response_json = await self.response_json(response)
|
|
121
|
+
duration = int(time.time() - started_at)
|
|
122
|
+
usage = response_json.get("usage", {})
|
|
123
|
+
if context is not None:
|
|
124
|
+
context["providerResponse"] = response_json
|
|
125
|
+
if "cost" in usage:
|
|
126
|
+
context["cost"] = usage.get("cost")
|
|
127
|
+
|
|
128
|
+
images = []
|
|
129
|
+
for image in response_json.get("data", []):
|
|
130
|
+
url = image.get("url")
|
|
131
|
+
if not url:
|
|
132
|
+
continue
|
|
133
|
+
# download url with aiohttp
|
|
134
|
+
async with session.get(url) as image_response:
|
|
135
|
+
headers = image_response.headers
|
|
136
|
+
# get filename from Content-Disposition
|
|
137
|
+
# attachment; filename="202601151228236805d575507d4570_watermark.png"
|
|
138
|
+
mime_type = headers.get("Content-Type") or "image/png"
|
|
139
|
+
disposition = headers.get("Content-Disposition")
|
|
140
|
+
if disposition:
|
|
141
|
+
start = disposition.index('filename="') + len('filename="')
|
|
142
|
+
end = disposition.index('"', start)
|
|
143
|
+
filename = disposition[start:end]
|
|
144
|
+
else:
|
|
145
|
+
ext = mime_type.split("/")[1]
|
|
146
|
+
filename = f"{body['model'].lower()}-{response_json.get('id', int(started_at))}.{ext}"
|
|
147
|
+
image_bytes = await image_response.read()
|
|
148
|
+
|
|
149
|
+
info = {
|
|
150
|
+
"prompt": body["prompt"],
|
|
151
|
+
"type": mime_type,
|
|
152
|
+
"width": int(size.split("x")[0]),
|
|
153
|
+
"height": int(size.split("x")[1]),
|
|
154
|
+
"duration": duration,
|
|
155
|
+
}
|
|
156
|
+
info.update(usage)
|
|
157
|
+
cache_url, info = ctx.save_image_to_cache(
|
|
158
|
+
image_bytes, filename, image_info=info, ignore_info=True
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
images.append(
|
|
162
|
+
{
|
|
163
|
+
"type": "image_url",
|
|
164
|
+
"image_url": {
|
|
165
|
+
"url": cache_url,
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
chat_response = {
|
|
171
|
+
"choices": [{"message": {"role": "assistant", "content": self.default_content, "images": images}}],
|
|
172
|
+
"created": int(time.time()),
|
|
173
|
+
"usage": {
|
|
174
|
+
"prompt_tokens": 0,
|
|
175
|
+
"completion_tokens": 1_000_000, # Price per image is 0.015, so 1M token is 0.015
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
if "cost" in usage:
|
|
179
|
+
chat_response["cost"] = usage["cost"]
|
|
180
|
+
return ctx.log_json(chat_response)
|
|
181
|
+
|
|
182
|
+
ctx.add_provider(ZaiGenerator)
|
llms/llms.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"User-Agent": "llmspy.org/3.0"
|
|
17
17
|
},
|
|
18
18
|
"text": {
|
|
19
|
-
"model": "
|
|
19
|
+
"model": "Kimi K2 Instruct",
|
|
20
20
|
"messages": [
|
|
21
21
|
{
|
|
22
22
|
"role": "user",
|
|
@@ -223,7 +223,13 @@
|
|
|
223
223
|
},
|
|
224
224
|
"zai-coding-plan": {
|
|
225
225
|
"enabled": true,
|
|
226
|
-
"temperature": 0.7
|
|
226
|
+
"temperature": 0.7,
|
|
227
|
+
"modalities": {
|
|
228
|
+
"image": {
|
|
229
|
+
"name": "Z.ai Image",
|
|
230
|
+
"npm": "zai/image"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
227
233
|
},
|
|
228
234
|
"minimax": {
|
|
229
235
|
"enabled": true,
|
|
@@ -314,7 +320,13 @@
|
|
|
314
320
|
},
|
|
315
321
|
"zai": {
|
|
316
322
|
"enabled": true,
|
|
317
|
-
"temperature": 0.7
|
|
323
|
+
"temperature": 0.7,
|
|
324
|
+
"modalities": {
|
|
325
|
+
"image": {
|
|
326
|
+
"name": "Z.ai Image",
|
|
327
|
+
"npm": "zai/image"
|
|
328
|
+
}
|
|
329
|
+
}
|
|
318
330
|
},
|
|
319
331
|
"mistral": {
|
|
320
332
|
"enabled": true,
|
llms/main.py
CHANGED
|
@@ -41,7 +41,7 @@ try:
|
|
|
41
41
|
except ImportError:
|
|
42
42
|
HAS_PIL = False
|
|
43
43
|
|
|
44
|
-
VERSION = "3.0.
|
|
44
|
+
VERSION = "3.0.7"
|
|
45
45
|
_ROOT = None
|
|
46
46
|
DEBUG = os.getenv("DEBUG") == "1"
|
|
47
47
|
MOCK = os.getenv("MOCK") == "1"
|
|
@@ -757,6 +757,12 @@ def chat_to_username(chat):
|
|
|
757
757
|
return None
|
|
758
758
|
|
|
759
759
|
|
|
760
|
+
def chat_to_aspect_ratio(chat):
|
|
761
|
+
if "image_config" in chat and "aspect_ratio" in chat["image_config"]:
|
|
762
|
+
return chat["image_config"]["aspect_ratio"]
|
|
763
|
+
return None
|
|
764
|
+
|
|
765
|
+
|
|
760
766
|
def last_user_prompt(chat):
|
|
761
767
|
prompt = ""
|
|
762
768
|
if "messages" in chat:
|
|
@@ -1829,7 +1835,10 @@ def print_status():
|
|
|
1829
1835
|
|
|
1830
1836
|
|
|
1831
1837
|
def home_llms_path(filename):
|
|
1832
|
-
|
|
1838
|
+
home_dir = os.getenv("LLMS_HOME", os.path.join(os.getenv("HOME"), ".llms"))
|
|
1839
|
+
relative_path = os.path.join(home_dir, filename)
|
|
1840
|
+
# return resolved full absolute path
|
|
1841
|
+
return os.path.abspath(os.path.normpath(relative_path))
|
|
1833
1842
|
|
|
1834
1843
|
|
|
1835
1844
|
def get_cache_path(path=""):
|
|
@@ -2536,8 +2545,8 @@ class ExtensionContext:
|
|
|
2536
2545
|
def to_file_info(self, chat, info=None, response=None):
|
|
2537
2546
|
return to_file_info(chat, info=info, response=response)
|
|
2538
2547
|
|
|
2539
|
-
def save_image_to_cache(self, base64_data, filename, image_info):
|
|
2540
|
-
return save_image_to_cache(base64_data, filename, image_info)
|
|
2548
|
+
def save_image_to_cache(self, base64_data, filename, image_info, ignore_info=False):
|
|
2549
|
+
return save_image_to_cache(base64_data, filename, image_info, ignore_info=ignore_info)
|
|
2541
2550
|
|
|
2542
2551
|
def save_bytes_to_cache(self, bytes_data, filename, file_info):
|
|
2543
2552
|
return save_bytes_to_cache(bytes_data, filename, file_info)
|
|
@@ -2689,6 +2698,11 @@ class ExtensionContext:
|
|
|
2689
2698
|
def get_user_path(self, username=None):
|
|
2690
2699
|
return self.app.get_user_path(username)
|
|
2691
2700
|
|
|
2701
|
+
def context_to_username(self, context):
|
|
2702
|
+
if context and "request" in context:
|
|
2703
|
+
return self.get_username(context["request"])
|
|
2704
|
+
return None
|
|
2705
|
+
|
|
2692
2706
|
def should_cancel_thread(self, context):
|
|
2693
2707
|
return should_cancel_thread(context)
|
|
2694
2708
|
|
|
@@ -2701,9 +2715,12 @@ class ExtensionContext:
|
|
|
2701
2715
|
def create_chat_with_tools(self, chat, use_tools="all"):
|
|
2702
2716
|
return self.app.create_chat_with_tools(chat, use_tools)
|
|
2703
2717
|
|
|
2718
|
+
def chat_to_aspect_ratio(self, chat):
|
|
2719
|
+
return chat_to_aspect_ratio(chat)
|
|
2720
|
+
|
|
2704
2721
|
|
|
2705
2722
|
def get_extensions_path():
|
|
2706
|
-
return os.getenv("LLMS_EXTENSIONS_DIR",
|
|
2723
|
+
return os.getenv("LLMS_EXTENSIONS_DIR", home_llms_path("extensions"))
|
|
2707
2724
|
|
|
2708
2725
|
|
|
2709
2726
|
def get_disabled_extensions():
|
|
@@ -2731,6 +2748,11 @@ def get_extensions_dirs():
|
|
|
2731
2748
|
disabled_extensions = get_disabled_extensions()
|
|
2732
2749
|
|
|
2733
2750
|
builtin_extensions_dir = _ROOT / "extensions"
|
|
2751
|
+
if not os.path.exists(builtin_extensions_dir):
|
|
2752
|
+
# look for local ./extensions dir from script
|
|
2753
|
+
builtin_extensions_dir = os.path.join(os.path.dirname(__file__), "extensions")
|
|
2754
|
+
|
|
2755
|
+
_dbg(f"Loading extensions from {builtin_extensions_dir}")
|
|
2734
2756
|
if os.path.exists(builtin_extensions_dir):
|
|
2735
2757
|
for item in os.listdir(builtin_extensions_dir):
|
|
2736
2758
|
if os.path.isdir(os.path.join(builtin_extensions_dir, item)):
|
|
@@ -3463,8 +3485,10 @@ def main():
|
|
|
3463
3485
|
cache_root = Path(get_cache_path())
|
|
3464
3486
|
requested_path = Path(info_path).resolve()
|
|
3465
3487
|
if not str(requested_path).startswith(str(cache_root)):
|
|
3488
|
+
_dbg(f"Forbidden: {requested_path} is not in {cache_root}")
|
|
3466
3489
|
return web.Response(text="403: Forbidden", status=403)
|
|
3467
|
-
except Exception:
|
|
3490
|
+
except Exception as e:
|
|
3491
|
+
_err(f"Forbidden: {requested_path} is not in {cache_root}", e)
|
|
3468
3492
|
return web.Response(text="403: Forbidden", status=403)
|
|
3469
3493
|
|
|
3470
3494
|
with open(info_path) as f:
|
|
@@ -3479,8 +3503,10 @@ def main():
|
|
|
3479
3503
|
cache_root = Path(get_cache_path())
|
|
3480
3504
|
requested_path = Path(full_path).resolve()
|
|
3481
3505
|
if not str(requested_path).startswith(str(cache_root)):
|
|
3506
|
+
_dbg(f"Forbidden: {requested_path} is not in {cache_root}")
|
|
3482
3507
|
return web.Response(text="403: Forbidden", status=403)
|
|
3483
3508
|
except Exception:
|
|
3509
|
+
_err(f"Forbidden: {requested_path} is not in {cache_root}", e)
|
|
3484
3510
|
return web.Response(text="403: Forbidden", status=403)
|
|
3485
3511
|
|
|
3486
3512
|
mimetype = get_file_mime_type(full_path)
|
llms/providers-extra.json
CHANGED
|
@@ -352,5 +352,43 @@
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
|
+
},
|
|
356
|
+
"zai": {
|
|
357
|
+
"models": {
|
|
358
|
+
"glm-image": {
|
|
359
|
+
"name": "GLM-Image",
|
|
360
|
+
"modalities": {
|
|
361
|
+
"input": [
|
|
362
|
+
"text"
|
|
363
|
+
],
|
|
364
|
+
"output": [
|
|
365
|
+
"image"
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
"cost": {
|
|
369
|
+
"input": 0,
|
|
370
|
+
"output": 0.015
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
"zai-coding-plan": {
|
|
376
|
+
"models": {
|
|
377
|
+
"glm-image": {
|
|
378
|
+
"name": "GLM-Image",
|
|
379
|
+
"modalities": {
|
|
380
|
+
"input": [
|
|
381
|
+
"text"
|
|
382
|
+
],
|
|
383
|
+
"output": [
|
|
384
|
+
"image"
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
"cost": {
|
|
388
|
+
"input": 0,
|
|
389
|
+
"output": 0.015
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
355
393
|
}
|
|
356
394
|
}
|