llms-py 3.0.0b4__tar.gz → 3.0.0b6__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.
- {llms_py-3.0.0b4/llms_py.egg-info → llms_py-3.0.0b6}/PKG-INFO +1 -1
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/main.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/main.py +158 -23
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers-extra.json +32 -0
- llms_py-3.0.0b6/llms/providers.json +1 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/App.mjs +3 -3
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/ai.mjs +1 -1
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/app.css +195 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/ctx.mjs +18 -9
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/index.mjs +10 -1
- llms_py-3.0.0b6/llms/ui/lib/servicestack-vue.mjs +37 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/analytics.mjs +28 -15
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/chat/ChatBody.mjs +130 -8
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/chat/index.mjs +10 -0
- llms_py-3.0.0b6/llms/ui/modules/tools.mjs +202 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/tailwind.input.css +20 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/utils.mjs +6 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6/llms_py.egg-info}/PKG-INFO +1 -1
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/SOURCES.txt +1 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/pyproject.toml +1 -1
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/setup.py +1 -1
- llms_py-3.0.0b4/llms/providers.json +0 -1
- llms_py-3.0.0b4/llms/ui/lib/servicestack-vue.mjs +0 -37
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/LICENSE +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/MANIFEST.in +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/README.md +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__init__.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__main__.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/__init__.cpython-312.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/__init__.cpython-313.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/__init__.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/__main__.cpython-312.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/__main__.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/llms.cpython-312.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/main.cpython-312.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/main.cpython-313.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/__pycache__/plugins.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/index.html +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/llms.json +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/google.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/openai.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/anthropic.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/chutes.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/google.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/nvidia.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/openai.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/providers/openrouter.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/fav.svg +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/chart.js +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/charts.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/color.js +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/highlight.min.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/idb.min.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/marked.min.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/vue.min.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/lib/vue.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/markdown.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/layout.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/model-selector.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/threads/Recents.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/threads/index.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/modules/threads/threadStore.mjs +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms/ui/typography.css +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/requirements.txt +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/setup.cfg +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_async.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_config.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_integration.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_provider_checks.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_provider_config.py +0 -0
- {llms_py-3.0.0b4 → llms_py-3.0.0b6}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b6
|
|
4
4
|
Summary: A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers
|
|
5
5
|
Home-page: https://github.com/ServiceStack/llms
|
|
6
6
|
Author: ServiceStack
|
|
Binary file
|
|
@@ -11,6 +11,7 @@ import asyncio
|
|
|
11
11
|
import base64
|
|
12
12
|
import hashlib
|
|
13
13
|
import importlib.util
|
|
14
|
+
import inspect
|
|
14
15
|
import json
|
|
15
16
|
import mimetypes
|
|
16
17
|
import os
|
|
@@ -26,6 +27,7 @@ from datetime import datetime
|
|
|
26
27
|
from importlib import resources # Py≥3.9 (pip install importlib_resources for 3.7/3.8)
|
|
27
28
|
from io import BytesIO
|
|
28
29
|
from pathlib import Path
|
|
30
|
+
from typing import get_type_hints
|
|
29
31
|
from urllib.parse import parse_qs, urlencode
|
|
30
32
|
|
|
31
33
|
import aiohttp
|
|
@@ -38,7 +40,7 @@ try:
|
|
|
38
40
|
except ImportError:
|
|
39
41
|
HAS_PIL = False
|
|
40
42
|
|
|
41
|
-
VERSION = "3.0.
|
|
43
|
+
VERSION = "3.0.0b6"
|
|
42
44
|
_ROOT = None
|
|
43
45
|
DEBUG = True # os.getenv("PYPI_SERVICESTACK") is not None
|
|
44
46
|
MOCK = False
|
|
@@ -322,6 +324,35 @@ def convert_image_if_needed(image_bytes, mimetype="image/png"):
|
|
|
322
324
|
return image_bytes, mimetype
|
|
323
325
|
|
|
324
326
|
|
|
327
|
+
def function_to_tool_definition(func):
|
|
328
|
+
type_hints = get_type_hints(func)
|
|
329
|
+
signature = inspect.signature(func)
|
|
330
|
+
parameters = {"type": "object", "properties": {}, "required": []}
|
|
331
|
+
|
|
332
|
+
for name, param in signature.parameters.items():
|
|
333
|
+
param_type = type_hints.get(name, str)
|
|
334
|
+
param_type_name = "string"
|
|
335
|
+
if param_type == int:
|
|
336
|
+
param_type_name = "integer"
|
|
337
|
+
elif param_type == float:
|
|
338
|
+
param_type_name = "number"
|
|
339
|
+
elif param_type == bool:
|
|
340
|
+
param_type_name = "boolean"
|
|
341
|
+
|
|
342
|
+
parameters["properties"][name] = {"type": param_type_name}
|
|
343
|
+
if param.default == inspect.Parameter.empty:
|
|
344
|
+
parameters["required"].append(name)
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
"type": "function",
|
|
348
|
+
"function": {
|
|
349
|
+
"name": func.__name__,
|
|
350
|
+
"description": func.__doc__ or "",
|
|
351
|
+
"parameters": parameters,
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
|
|
325
356
|
async def process_chat(chat, provider_id=None):
|
|
326
357
|
if not chat:
|
|
327
358
|
raise Exception("No chat provided")
|
|
@@ -1034,8 +1065,74 @@ async def chat_completion(chat):
|
|
|
1034
1065
|
provider = g_handlers[name]
|
|
1035
1066
|
_log(f"provider: {name} {type(provider).__name__}")
|
|
1036
1067
|
try:
|
|
1037
|
-
|
|
1038
|
-
|
|
1068
|
+
# Inject global tools if present
|
|
1069
|
+
current_chat = chat.copy()
|
|
1070
|
+
# Inject global tools if present
|
|
1071
|
+
current_chat = chat.copy()
|
|
1072
|
+
if g_app.tool_definitions:
|
|
1073
|
+
include_all_tools = False
|
|
1074
|
+
only_tools = []
|
|
1075
|
+
if "metadata" in chat:
|
|
1076
|
+
only_tools_str = chat["metadata"].get("only_tools", "")
|
|
1077
|
+
include_all_tools = only_tools_str == "all"
|
|
1078
|
+
only_tools = only_tools_str.split(",")
|
|
1079
|
+
|
|
1080
|
+
if include_all_tools or len(only_tools) > 0:
|
|
1081
|
+
if "tools" not in current_chat:
|
|
1082
|
+
current_chat["tools"] = []
|
|
1083
|
+
|
|
1084
|
+
existing_tools = {t["function"]["name"] for t in current_chat["tools"]}
|
|
1085
|
+
for tool_def in g_app.tool_definitions:
|
|
1086
|
+
name = tool_def["function"]["name"]
|
|
1087
|
+
if name not in existing_tools and (include_all_tools or name in only_tools):
|
|
1088
|
+
current_chat["tools"].append(tool_def)
|
|
1089
|
+
|
|
1090
|
+
# Tool execution loop
|
|
1091
|
+
max_iterations = 5
|
|
1092
|
+
tool_history = []
|
|
1093
|
+
for _ in range(max_iterations):
|
|
1094
|
+
response = await provider.chat(current_chat)
|
|
1095
|
+
|
|
1096
|
+
# Check for tool_calls in the response
|
|
1097
|
+
choice = response.get("choices", [])[0] if response.get("choices") else {}
|
|
1098
|
+
message = choice.get("message", {})
|
|
1099
|
+
tool_calls = message.get("tool_calls")
|
|
1100
|
+
|
|
1101
|
+
if tool_calls:
|
|
1102
|
+
# Append the assistant's message with tool calls to history
|
|
1103
|
+
if "messages" not in current_chat:
|
|
1104
|
+
current_chat["messages"] = []
|
|
1105
|
+
current_chat["messages"].append(message)
|
|
1106
|
+
tool_history.append(message)
|
|
1107
|
+
|
|
1108
|
+
for tool_call in tool_calls:
|
|
1109
|
+
function_name = tool_call["function"]["name"]
|
|
1110
|
+
function_args = json.loads(tool_call["function"]["arguments"])
|
|
1111
|
+
|
|
1112
|
+
tool_result = f"Error: Tool {function_name} not found"
|
|
1113
|
+
if function_name in g_app.tools:
|
|
1114
|
+
try:
|
|
1115
|
+
func = g_app.tools[function_name]
|
|
1116
|
+
if inspect.iscoroutinefunction(func):
|
|
1117
|
+
tool_result = await func(**function_args)
|
|
1118
|
+
else:
|
|
1119
|
+
tool_result = func(**function_args)
|
|
1120
|
+
except Exception as e:
|
|
1121
|
+
tool_result = f"Error executing tool {function_name}: {e}"
|
|
1122
|
+
|
|
1123
|
+
# Append tool result to history
|
|
1124
|
+
tool_msg = {"role": "tool", "tool_call_id": tool_call["id"], "content": str(tool_result)}
|
|
1125
|
+
current_chat["messages"].append(tool_msg)
|
|
1126
|
+
tool_history.append(tool_msg)
|
|
1127
|
+
|
|
1128
|
+
# Continue loop to send tool results back to LLM
|
|
1129
|
+
continue
|
|
1130
|
+
|
|
1131
|
+
# If no tool calls, return the response
|
|
1132
|
+
if tool_history:
|
|
1133
|
+
response["tool_history"] = tool_history
|
|
1134
|
+
return response
|
|
1135
|
+
|
|
1039
1136
|
except Exception as e:
|
|
1040
1137
|
if first_exception is None:
|
|
1041
1138
|
first_exception = e
|
|
@@ -1121,15 +1218,23 @@ async def cli_chat(chat, image=None, audio=None, file=None, args=None, raw=False
|
|
|
1121
1218
|
printdump(chat)
|
|
1122
1219
|
|
|
1123
1220
|
try:
|
|
1221
|
+
# Apply pre-chat filters
|
|
1222
|
+
context = {"chat": chat}
|
|
1223
|
+
for filter_func in g_app.chat_request_filters:
|
|
1224
|
+
chat = await filter_func(chat, context)
|
|
1225
|
+
|
|
1124
1226
|
response = await chat_completion(chat)
|
|
1227
|
+
|
|
1228
|
+
# Apply post-chat filters
|
|
1229
|
+
for filter_func in g_app.chat_response_filters:
|
|
1230
|
+
response = await filter_func(response, context)
|
|
1125
1231
|
if raw:
|
|
1126
1232
|
print(json.dumps(response, indent=2))
|
|
1127
1233
|
exit(0)
|
|
1128
1234
|
else:
|
|
1129
1235
|
msg = response["choices"][0]["message"]
|
|
1130
|
-
if "answer" in msg:
|
|
1131
|
-
|
|
1132
|
-
print(answer)
|
|
1236
|
+
if "content" in msg or "answer" in msg:
|
|
1237
|
+
print(msg["content"])
|
|
1133
1238
|
|
|
1134
1239
|
generated_files = []
|
|
1135
1240
|
for choice in response["choices"]:
|
|
@@ -1851,6 +1956,9 @@ class AppExtensions:
|
|
|
1851
1956
|
self.chat_response_filters = []
|
|
1852
1957
|
self.server_add_get = []
|
|
1853
1958
|
self.server_add_post = []
|
|
1959
|
+
self.server_add_post = []
|
|
1960
|
+
self.tools = {}
|
|
1961
|
+
self.tool_definitions = []
|
|
1854
1962
|
self.all_providers = [
|
|
1855
1963
|
OpenAiCompatible,
|
|
1856
1964
|
MistralProvider,
|
|
@@ -1982,6 +2090,15 @@ class ExtensionContext:
|
|
|
1982
2090
|
return session.get("userName")
|
|
1983
2091
|
return None
|
|
1984
2092
|
|
|
2093
|
+
def register_tool(self, func, tool_def=None):
|
|
2094
|
+
if tool_def is None:
|
|
2095
|
+
tool_def = function_to_tool_definition(func)
|
|
2096
|
+
|
|
2097
|
+
name = tool_def["function"]["name"]
|
|
2098
|
+
self.log(f"Registered tool: {name}")
|
|
2099
|
+
self.app.tools[name] = func
|
|
2100
|
+
self.app.tool_definitions.append(tool_def)
|
|
2101
|
+
|
|
1985
2102
|
|
|
1986
2103
|
def load_builtin_extensions():
|
|
1987
2104
|
providers_path = _ROOT / "providers"
|
|
@@ -2011,7 +2128,7 @@ def load_builtin_extensions():
|
|
|
2011
2128
|
|
|
2012
2129
|
|
|
2013
2130
|
def get_extensions_path():
|
|
2014
|
-
return os.path.join(Path.home(), ".llms", "extensions")
|
|
2131
|
+
return os.environ.get("LLMS_EXTENSIONS_DIR", os.path.join(Path.home(), ".llms", "extensions"))
|
|
2015
2132
|
|
|
2016
2133
|
|
|
2017
2134
|
def init_extensions(parser):
|
|
@@ -2336,9 +2453,29 @@ def main():
|
|
|
2336
2453
|
requirements_path = os.path.join(target_path, "requirements.txt")
|
|
2337
2454
|
if os.path.exists(requirements_path):
|
|
2338
2455
|
print(f"Installing dependencies from {requirements_path}...")
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2456
|
+
|
|
2457
|
+
# Check if uv is installed
|
|
2458
|
+
has_uv = False
|
|
2459
|
+
try:
|
|
2460
|
+
subprocess.run(
|
|
2461
|
+
["uv", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
|
|
2462
|
+
)
|
|
2463
|
+
has_uv = True
|
|
2464
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
2465
|
+
pass
|
|
2466
|
+
|
|
2467
|
+
if has_uv:
|
|
2468
|
+
subprocess.run(
|
|
2469
|
+
["uv", "pip", "install", "-p", sys.executable, "-r", "requirements.txt"],
|
|
2470
|
+
cwd=target_path,
|
|
2471
|
+
check=True,
|
|
2472
|
+
)
|
|
2473
|
+
else:
|
|
2474
|
+
subprocess.run(
|
|
2475
|
+
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
|
|
2476
|
+
cwd=target_path,
|
|
2477
|
+
check=True,
|
|
2478
|
+
)
|
|
2342
2479
|
print("Dependencies installed successfully.")
|
|
2343
2480
|
|
|
2344
2481
|
print(f"Extension {target_name} installed successfully.")
|
|
@@ -2569,9 +2706,7 @@ def main():
|
|
|
2569
2706
|
chat = await request.json()
|
|
2570
2707
|
|
|
2571
2708
|
# Apply pre-chat filters
|
|
2572
|
-
context = {"request": request}
|
|
2573
|
-
# Apply pre-chat filters
|
|
2574
|
-
context = {"request": request}
|
|
2709
|
+
context = {"request": request, "chat": chat}
|
|
2575
2710
|
for filter_func in g_app.chat_request_filters:
|
|
2576
2711
|
chat = await filter_func(chat, context)
|
|
2577
2712
|
|
|
@@ -2588,16 +2723,6 @@ def main():
|
|
|
2588
2723
|
|
|
2589
2724
|
app.router.add_post("/v1/chat/completions", chat_handler)
|
|
2590
2725
|
|
|
2591
|
-
async def extensions_handler(request):
|
|
2592
|
-
return web.json_response(g_app.ui_extensions)
|
|
2593
|
-
|
|
2594
|
-
app.router.add_get("/ext", extensions_handler)
|
|
2595
|
-
|
|
2596
|
-
async def models_handler(request):
|
|
2597
|
-
return web.json_response(get_models())
|
|
2598
|
-
|
|
2599
|
-
app.router.add_get("/models/list", models_handler)
|
|
2600
|
-
|
|
2601
2726
|
async def active_models_handler(request):
|
|
2602
2727
|
return web.json_response(get_active_models())
|
|
2603
2728
|
|
|
@@ -2731,6 +2856,16 @@ def main():
|
|
|
2731
2856
|
|
|
2732
2857
|
app.router.add_post("/upload", upload_handler)
|
|
2733
2858
|
|
|
2859
|
+
async def extensions_handler(request):
|
|
2860
|
+
return web.json_response(g_app.ui_extensions)
|
|
2861
|
+
|
|
2862
|
+
app.router.add_get("/ext", extensions_handler)
|
|
2863
|
+
|
|
2864
|
+
async def tools_handler(request):
|
|
2865
|
+
return web.json_response(g_app.tool_definitions)
|
|
2866
|
+
|
|
2867
|
+
app.router.add_get("/ext/tools", tools_handler)
|
|
2868
|
+
|
|
2734
2869
|
async def cache_handler(request):
|
|
2735
2870
|
path = request.match_info["tail"]
|
|
2736
2871
|
full_path = get_cache_path(path)
|
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
+
"minimax": {
|
|
3
|
+
"models": {
|
|
4
|
+
"MiniMax-M2.1": {
|
|
5
|
+
"id": "MiniMax-M2.1",
|
|
6
|
+
"name": "MiniMax M2.1",
|
|
7
|
+
"family": "minimax",
|
|
8
|
+
"attachment": false,
|
|
9
|
+
"reasoning": true,
|
|
10
|
+
"tool_call": true,
|
|
11
|
+
"temperature": true,
|
|
12
|
+
"release_date": "2025-12-23",
|
|
13
|
+
"last_updated": "2025-12-23",
|
|
14
|
+
"modalities": {
|
|
15
|
+
"input": [
|
|
16
|
+
"text"
|
|
17
|
+
],
|
|
18
|
+
"output": [
|
|
19
|
+
"text"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"open_weights": true,
|
|
23
|
+
"cost": {
|
|
24
|
+
"input": 0.3,
|
|
25
|
+
"output": 1.2
|
|
26
|
+
},
|
|
27
|
+
"limit": {
|
|
28
|
+
"context": 204800,
|
|
29
|
+
"output": 128000
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
2
34
|
"openrouter": {
|
|
3
35
|
"models": {
|
|
4
36
|
"google/gemini-2.5-flash-image": {
|