llms-py 3.0.6__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 +14 -2
- llms/main.py +17 -3
- 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.6.dist-info → llms_py-3.0.7.dist-info}/METADATA +1 -1
- {llms_py-3.0.6.dist-info → llms_py-3.0.7.dist-info}/RECORD +23 -22
- {llms_py-3.0.6.dist-info → llms_py-3.0.7.dist-info}/WHEEL +0 -0
- {llms_py-3.0.6.dist-info → llms_py-3.0.7.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.6.dist-info → llms_py-3.0.7.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.6.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
|
@@ -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:
|
|
@@ -2539,8 +2545,8 @@ class ExtensionContext:
|
|
|
2539
2545
|
def to_file_info(self, chat, info=None, response=None):
|
|
2540
2546
|
return to_file_info(chat, info=info, response=response)
|
|
2541
2547
|
|
|
2542
|
-
def save_image_to_cache(self, base64_data, filename, image_info):
|
|
2543
|
-
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)
|
|
2544
2550
|
|
|
2545
2551
|
def save_bytes_to_cache(self, bytes_data, filename, file_info):
|
|
2546
2552
|
return save_bytes_to_cache(bytes_data, filename, file_info)
|
|
@@ -2692,6 +2698,11 @@ class ExtensionContext:
|
|
|
2692
2698
|
def get_user_path(self, username=None):
|
|
2693
2699
|
return self.app.get_user_path(username)
|
|
2694
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
|
+
|
|
2695
2706
|
def should_cancel_thread(self, context):
|
|
2696
2707
|
return should_cancel_thread(context)
|
|
2697
2708
|
|
|
@@ -2704,6 +2715,9 @@ class ExtensionContext:
|
|
|
2704
2715
|
def create_chat_with_tools(self, chat, use_tools="all"):
|
|
2705
2716
|
return self.app.create_chat_with_tools(chat, use_tools)
|
|
2706
2717
|
|
|
2718
|
+
def chat_to_aspect_ratio(self, chat):
|
|
2719
|
+
return chat_to_aspect_ratio(chat)
|
|
2720
|
+
|
|
2707
2721
|
|
|
2708
2722
|
def get_extensions_path():
|
|
2709
2723
|
return os.getenv("LLMS_EXTENSIONS_DIR", home_llms_path("extensions"))
|
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
|
}
|