llms-py 3.0.10__py3-none-any.whl → 3.0.12__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/app/db.py +2 -2
- llms/extensions/providers/anthropic.py +9 -1
- llms/extensions/providers/google.py +55 -4
- llms/main.py +209 -122
- llms/ui/ai.mjs +1 -1
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/METADATA +2 -2
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/RECORD +11 -11
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/WHEEL +0 -0
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.10.dist-info → llms_py-3.0.12.dist-info}/top_level.txt +0 -0
llms/extensions/app/db.py
CHANGED
|
@@ -528,9 +528,9 @@ class AppDB:
|
|
|
528
528
|
with self.db.create_writer_connection() as conn:
|
|
529
529
|
conn.execute(
|
|
530
530
|
"UPDATE thread SET completedAt = :completedAt, error = :error WHERE completedAt IS NULL",
|
|
531
|
-
{"completedAt": datetime.now(), "error": "Server Shutdown"},
|
|
531
|
+
{"completedAt": datetime.now().isoformat(" "), "error": "Server Shutdown"},
|
|
532
532
|
)
|
|
533
533
|
conn.execute(
|
|
534
534
|
"UPDATE request SET completedAt = :completedAt, error = :error WHERE completedAt IS NULL",
|
|
535
|
-
{"completedAt": datetime.now(), "error": "Server Shutdown"},
|
|
535
|
+
{"completedAt": datetime.now().isoformat(" "), "error": "Server Shutdown"},
|
|
536
536
|
)
|
|
@@ -77,9 +77,17 @@ def install_anthropic(ctx):
|
|
|
77
77
|
|
|
78
78
|
anthropic_message = {"role": message.get("role"), "content": []}
|
|
79
79
|
|
|
80
|
+
# Handle interleaved thinking (must always be a list if present)
|
|
81
|
+
if "thinking" in message and message["thinking"]:
|
|
82
|
+
anthropic_message["content"].append({"type": "thinking", "thinking": message["thinking"]})
|
|
83
|
+
|
|
80
84
|
content = message.get("content", "")
|
|
81
85
|
if isinstance(content, str):
|
|
82
|
-
anthropic_message["content"]
|
|
86
|
+
if anthropic_message["content"]:
|
|
87
|
+
# If we have thinking, we must use blocks for text
|
|
88
|
+
anthropic_message["content"].append({"type": "text", "text": content})
|
|
89
|
+
else:
|
|
90
|
+
anthropic_message["content"] = content
|
|
83
91
|
elif isinstance(content, list):
|
|
84
92
|
for item in content:
|
|
85
93
|
if item.get("type") == "text":
|
|
@@ -63,6 +63,50 @@ def install_google(ctx):
|
|
|
63
63
|
to[k] = v
|
|
64
64
|
return to
|
|
65
65
|
|
|
66
|
+
def sanitize_parameters(params):
|
|
67
|
+
"""Sanitize tool parameters for Google provider."""
|
|
68
|
+
|
|
69
|
+
if not isinstance(params, dict):
|
|
70
|
+
return params
|
|
71
|
+
|
|
72
|
+
# Create a copy to avoid modifying original tool definition
|
|
73
|
+
p = params.copy()
|
|
74
|
+
|
|
75
|
+
# Remove forbidden fields
|
|
76
|
+
for forbidden in ["$schema", "additionalProperties"]:
|
|
77
|
+
if forbidden in p:
|
|
78
|
+
del p[forbidden]
|
|
79
|
+
|
|
80
|
+
# Recursively sanitize known nesting fields
|
|
81
|
+
# 1. Properties (dict of schemas)
|
|
82
|
+
if "properties" in p:
|
|
83
|
+
for k, v in p["properties"].items():
|
|
84
|
+
p["properties"][k] = sanitize_parameters(v)
|
|
85
|
+
|
|
86
|
+
# 2. Items (schema or list of schemas)
|
|
87
|
+
if "items" in p:
|
|
88
|
+
if isinstance(p["items"], list):
|
|
89
|
+
p["items"] = [sanitize_parameters(i) for i in p["items"]]
|
|
90
|
+
else:
|
|
91
|
+
p["items"] = sanitize_parameters(p["items"])
|
|
92
|
+
|
|
93
|
+
# 3. Combinators (list of schemas)
|
|
94
|
+
for combinator in ["allOf", "anyOf", "oneOf"]:
|
|
95
|
+
if combinator in p:
|
|
96
|
+
p[combinator] = [sanitize_parameters(i) for i in p[combinator]]
|
|
97
|
+
|
|
98
|
+
# 4. Not (schema)
|
|
99
|
+
if "not" in p:
|
|
100
|
+
p["not"] = sanitize_parameters(p["not"])
|
|
101
|
+
|
|
102
|
+
# 5. Definitions (dict of schemas)
|
|
103
|
+
for def_key in ["definitions", "$defs"]:
|
|
104
|
+
if def_key in p:
|
|
105
|
+
for k, v in p[def_key].items():
|
|
106
|
+
p[def_key][k] = sanitize_parameters(v)
|
|
107
|
+
|
|
108
|
+
return p
|
|
109
|
+
|
|
66
110
|
class GoogleProvider(OpenAiCompatible):
|
|
67
111
|
sdk = "@ai-sdk/google"
|
|
68
112
|
|
|
@@ -112,11 +156,12 @@ def install_google(ctx):
|
|
|
112
156
|
for tool in chat["tools"]:
|
|
113
157
|
if tool["type"] == "function":
|
|
114
158
|
f = tool["function"]
|
|
159
|
+
|
|
115
160
|
function_declarations.append(
|
|
116
161
|
{
|
|
117
162
|
"name": f["name"],
|
|
118
163
|
"description": f.get("description"),
|
|
119
|
-
"parameters": f.get("parameters"),
|
|
164
|
+
"parameters": sanitize_parameters(f.get("parameters")),
|
|
120
165
|
}
|
|
121
166
|
)
|
|
122
167
|
elif tool["type"] == "file_search":
|
|
@@ -183,13 +228,19 @@ def install_google(ctx):
|
|
|
183
228
|
if name:
|
|
184
229
|
# content is the string response
|
|
185
230
|
# Some implementations pass the content directly.
|
|
186
|
-
# Google docs say: response: { "
|
|
187
|
-
|
|
231
|
+
# Google docs say: response: { "key": "value" }
|
|
232
|
+
try:
|
|
233
|
+
response_data = json.loads(message["content"])
|
|
234
|
+
if not isinstance(response_data, dict):
|
|
235
|
+
response_data = {"content": message["content"]}
|
|
236
|
+
except Exception:
|
|
237
|
+
response_data = {"content": message["content"]}
|
|
238
|
+
|
|
188
239
|
parts.append(
|
|
189
240
|
{
|
|
190
241
|
"functionResponse": {
|
|
191
242
|
"name": name,
|
|
192
|
-
"response":
|
|
243
|
+
"response": response_data,
|
|
193
244
|
}
|
|
194
245
|
}
|
|
195
246
|
)
|
llms/main.py
CHANGED
|
@@ -18,6 +18,7 @@ import mimetypes
|
|
|
18
18
|
import os
|
|
19
19
|
import re
|
|
20
20
|
import secrets
|
|
21
|
+
import shlex
|
|
21
22
|
import shutil
|
|
22
23
|
import site
|
|
23
24
|
import subprocess
|
|
@@ -25,10 +26,11 @@ import sys
|
|
|
25
26
|
import time
|
|
26
27
|
import traceback
|
|
27
28
|
from datetime import datetime
|
|
29
|
+
from enum import IntEnum
|
|
28
30
|
from importlib import resources # Py≥3.9 (pip install importlib_resources for 3.7/3.8)
|
|
29
31
|
from io import BytesIO
|
|
30
32
|
from pathlib import Path
|
|
31
|
-
from typing import Optional, get_type_hints
|
|
33
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, get_type_hints
|
|
32
34
|
from urllib.parse import parse_qs, urlencode, urljoin
|
|
33
35
|
|
|
34
36
|
import aiohttp
|
|
@@ -41,7 +43,7 @@ try:
|
|
|
41
43
|
except ImportError:
|
|
42
44
|
HAS_PIL = False
|
|
43
45
|
|
|
44
|
-
VERSION = "3.0.
|
|
46
|
+
VERSION = "3.0.12"
|
|
45
47
|
_ROOT = None
|
|
46
48
|
DEBUG = os.getenv("DEBUG") == "1"
|
|
47
49
|
MOCK = os.getenv("MOCK") == "1"
|
|
@@ -59,6 +61,12 @@ g_oauth_states = {} # CSRF protection: {state: {created, redirect_uri}}
|
|
|
59
61
|
g_app = None # ExtensionsContext Singleton
|
|
60
62
|
|
|
61
63
|
|
|
64
|
+
class ExitCode(IntEnum):
|
|
65
|
+
SUCCESS = 0
|
|
66
|
+
FAILED = 1
|
|
67
|
+
UNHANDLED = 9
|
|
68
|
+
|
|
69
|
+
|
|
62
70
|
def _log(message):
|
|
63
71
|
if g_verbose:
|
|
64
72
|
print(f"{g_logprefix}{message}", flush=True)
|
|
@@ -1620,10 +1628,15 @@ async def g_chat_completion(chat, context=None):
|
|
|
1620
1628
|
tool_history = []
|
|
1621
1629
|
final_response = None
|
|
1622
1630
|
|
|
1623
|
-
for
|
|
1631
|
+
for request_count in range(max_iterations):
|
|
1624
1632
|
if should_cancel_thread(context):
|
|
1625
1633
|
return
|
|
1626
1634
|
|
|
1635
|
+
if DEBUG:
|
|
1636
|
+
messages = current_chat.get("messages", [])
|
|
1637
|
+
last_message = messages[-1] if messages else None
|
|
1638
|
+
_dbg(f"Provider {provider_name}, request {request_count}:\n{json.dumps(last_message, indent=2)}")
|
|
1639
|
+
|
|
1627
1640
|
response = await provider.chat(current_chat, context=context)
|
|
1628
1641
|
|
|
1629
1642
|
if should_cancel_thread(context):
|
|
@@ -1853,12 +1866,18 @@ def config_str(key):
|
|
|
1853
1866
|
return key in g_config and g_config[key] or None
|
|
1854
1867
|
|
|
1855
1868
|
|
|
1856
|
-
def load_config(config, providers, verbose=None):
|
|
1869
|
+
def load_config(config, providers, verbose=None, debug=None, disable_extensions: List[str] = None):
|
|
1857
1870
|
global g_config, g_providers, g_verbose
|
|
1858
1871
|
g_config = config
|
|
1859
1872
|
g_providers = providers
|
|
1860
|
-
if verbose:
|
|
1873
|
+
if verbose is not None:
|
|
1861
1874
|
g_verbose = verbose
|
|
1875
|
+
if debug is not None:
|
|
1876
|
+
global DEBUG
|
|
1877
|
+
DEBUG = debug
|
|
1878
|
+
if disable_extensions:
|
|
1879
|
+
global DISABLE_EXTENSIONS
|
|
1880
|
+
DISABLE_EXTENSIONS = disable_extensions
|
|
1862
1881
|
|
|
1863
1882
|
|
|
1864
1883
|
def init_llms(config, providers):
|
|
@@ -2539,7 +2558,7 @@ class AppExtensions:
|
|
|
2539
2558
|
APIs extensions can use to extend the app
|
|
2540
2559
|
"""
|
|
2541
2560
|
|
|
2542
|
-
def __init__(self, cli_args, extra_args):
|
|
2561
|
+
def __init__(self, cli_args: argparse.Namespace, extra_args: Dict[str, Any]):
|
|
2543
2562
|
self.cli_args = cli_args
|
|
2544
2563
|
self.extra_args = extra_args
|
|
2545
2564
|
self.config = None
|
|
@@ -2615,12 +2634,12 @@ class AppExtensions:
|
|
|
2615
2634
|
"ctx.mjs": "/ui/ctx.mjs",
|
|
2616
2635
|
}
|
|
2617
2636
|
|
|
2618
|
-
def set_config(self, config):
|
|
2637
|
+
def set_config(self, config: Dict[str, Any]):
|
|
2619
2638
|
self.config = config
|
|
2620
2639
|
self.auth_enabled = self.config.get("auth", {}).get("enabled", False)
|
|
2621
2640
|
|
|
2622
2641
|
# Authentication middleware helper
|
|
2623
|
-
def check_auth(self, request):
|
|
2642
|
+
def check_auth(self, request: web.Request) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
|
2624
2643
|
"""Check if request is authenticated. Returns (is_authenticated, user_data)"""
|
|
2625
2644
|
if not self.auth_enabled:
|
|
2626
2645
|
return True, None
|
|
@@ -2639,7 +2658,7 @@ class AppExtensions:
|
|
|
2639
2658
|
|
|
2640
2659
|
return False, None
|
|
2641
2660
|
|
|
2642
|
-
def get_session(self, request):
|
|
2661
|
+
def get_session(self, request: web.Request) -> Optional[Dict[str, Any]]:
|
|
2643
2662
|
session_token = get_session_token(request)
|
|
2644
2663
|
|
|
2645
2664
|
if not session_token or session_token not in g_sessions:
|
|
@@ -2648,30 +2667,39 @@ class AppExtensions:
|
|
|
2648
2667
|
session_data = g_sessions[session_token]
|
|
2649
2668
|
return session_data
|
|
2650
2669
|
|
|
2651
|
-
def get_username(self, request):
|
|
2670
|
+
def get_username(self, request: web.Request) -> Optional[str]:
|
|
2652
2671
|
session = self.get_session(request)
|
|
2653
2672
|
if session:
|
|
2654
2673
|
return session.get("userName")
|
|
2655
2674
|
return None
|
|
2656
2675
|
|
|
2657
|
-
def get_user_path(self, username=None):
|
|
2676
|
+
def get_user_path(self, username: Optional[str] = None) -> str:
|
|
2658
2677
|
if username:
|
|
2659
2678
|
return home_llms_path(os.path.join("user", username))
|
|
2660
2679
|
return home_llms_path(os.path.join("user", "default"))
|
|
2661
2680
|
|
|
2662
|
-
def
|
|
2681
|
+
def get_providers(self) -> Dict[str, Any]:
|
|
2682
|
+
return g_handlers
|
|
2683
|
+
|
|
2684
|
+
def chat_request(
|
|
2685
|
+
self,
|
|
2686
|
+
template: Optional[str] = None,
|
|
2687
|
+
text: Optional[str] = None,
|
|
2688
|
+
model: Optional[str] = None,
|
|
2689
|
+
system_prompt: Optional[str] = None,
|
|
2690
|
+
) -> Dict[str, Any]:
|
|
2663
2691
|
return g_chat_request(template=template, text=text, model=model, system_prompt=system_prompt)
|
|
2664
2692
|
|
|
2665
|
-
async def chat_completion(self, chat, context=None):
|
|
2693
|
+
async def chat_completion(self, chat: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> Any:
|
|
2666
2694
|
response = await g_chat_completion(chat, context)
|
|
2667
2695
|
return response
|
|
2668
2696
|
|
|
2669
|
-
def on_cache_saved_filters(self, context):
|
|
2697
|
+
def on_cache_saved_filters(self, context: Dict[str, Any]):
|
|
2670
2698
|
# _log(f"on_cache_saved_filters {len(self.cache_saved_filters)}: {context['url']}")
|
|
2671
2699
|
for filter_func in self.cache_saved_filters:
|
|
2672
2700
|
filter_func(context)
|
|
2673
2701
|
|
|
2674
|
-
async def on_chat_error(self, e, context):
|
|
2702
|
+
async def on_chat_error(self, e: Exception, context: Dict[str, Any]):
|
|
2675
2703
|
# Apply chat error filters
|
|
2676
2704
|
if "stackTrace" not in context:
|
|
2677
2705
|
context["stackTrace"] = traceback.format_exc()
|
|
@@ -2681,25 +2709,27 @@ class AppExtensions:
|
|
|
2681
2709
|
except Exception as e:
|
|
2682
2710
|
_err("chat error filter failed", e)
|
|
2683
2711
|
|
|
2684
|
-
async def on_chat_tool(self, chat, context):
|
|
2712
|
+
async def on_chat_tool(self, chat: Dict[str, Any], context: Dict[str, Any]):
|
|
2685
2713
|
m_len = len(chat.get("messages", []))
|
|
2686
2714
|
t_len = len(self.chat_tool_filters)
|
|
2687
2715
|
_dbg(
|
|
2688
|
-
f"on_tool_call for thread {context.get('threadId'
|
|
2716
|
+
f"on_tool_call for thread {context.get('threadId')} with {m_len} {pluralize('message', m_len)}, invoking {t_len} {pluralize('filter', t_len)}:"
|
|
2689
2717
|
)
|
|
2690
2718
|
for filter_func in self.chat_tool_filters:
|
|
2691
2719
|
await filter_func(chat, context)
|
|
2692
2720
|
|
|
2693
|
-
def
|
|
2721
|
+
def shutdown(self):
|
|
2694
2722
|
if len(self.shutdown_handlers) > 0:
|
|
2695
2723
|
_dbg(f"running {len(self.shutdown_handlers)} shutdown handlers...")
|
|
2696
2724
|
for handler in self.shutdown_handlers:
|
|
2697
2725
|
handler()
|
|
2698
2726
|
|
|
2727
|
+
def exit(self, exit_code: int = 0):
|
|
2728
|
+
self.shutdown()
|
|
2699
2729
|
_dbg(f"exit({exit_code})")
|
|
2700
2730
|
sys.exit(exit_code)
|
|
2701
2731
|
|
|
2702
|
-
def create_chat_with_tools(self, chat, use_tools="all"):
|
|
2732
|
+
def create_chat_with_tools(self, chat: Dict[str, Any], use_tools: str = "all") -> Dict[str, Any]:
|
|
2703
2733
|
# Inject global tools if present
|
|
2704
2734
|
current_chat = chat.copy()
|
|
2705
2735
|
tools = current_chat.get("tools")
|
|
@@ -2728,7 +2758,7 @@ def handler_name(handler):
|
|
|
2728
2758
|
|
|
2729
2759
|
|
|
2730
2760
|
class ExtensionContext:
|
|
2731
|
-
def __init__(self, app, path):
|
|
2761
|
+
def __init__(self, app: AppExtensions, path: str):
|
|
2732
2762
|
self.app = app
|
|
2733
2763
|
self.cli_args = app.cli_args
|
|
2734
2764
|
self.extra_args = app.extra_args
|
|
@@ -2746,101 +2776,107 @@ class ExtensionContext:
|
|
|
2746
2776
|
self.request_args = app.request_args
|
|
2747
2777
|
self.disabled = False
|
|
2748
2778
|
|
|
2749
|
-
def chat_to_prompt(self, chat):
|
|
2779
|
+
def chat_to_prompt(self, chat: Dict[str, Any]) -> str:
|
|
2750
2780
|
return chat_to_prompt(chat)
|
|
2751
2781
|
|
|
2752
|
-
def chat_to_system_prompt(self, chat):
|
|
2782
|
+
def chat_to_system_prompt(self, chat: Dict[str, Any]) -> str:
|
|
2753
2783
|
return chat_to_system_prompt(chat)
|
|
2754
2784
|
|
|
2755
|
-
def chat_response_to_message(self, response):
|
|
2785
|
+
def chat_response_to_message(self, response: Dict[str, Any]) -> Dict[str, Any]:
|
|
2756
2786
|
return chat_response_to_message(response)
|
|
2757
2787
|
|
|
2758
|
-
def last_user_prompt(self, chat):
|
|
2788
|
+
def last_user_prompt(self, chat: Dict[str, Any]) -> str:
|
|
2759
2789
|
return last_user_prompt(chat)
|
|
2760
2790
|
|
|
2761
|
-
def to_file_info(
|
|
2791
|
+
def to_file_info(
|
|
2792
|
+
self, chat: Dict[str, Any], info: Optional[Dict[str, Any]] = None, response: Optional[Dict[str, Any]] = None
|
|
2793
|
+
) -> Dict[str, Any]:
|
|
2762
2794
|
return to_file_info(chat, info=info, response=response)
|
|
2763
2795
|
|
|
2764
|
-
def save_image_to_cache(
|
|
2796
|
+
def save_image_to_cache(
|
|
2797
|
+
self, base64_data: Union[str, bytes], filename: str, image_info: Dict[str, Any], ignore_info: bool = False
|
|
2798
|
+
) -> Tuple[str, Optional[Dict[str, Any]]]:
|
|
2765
2799
|
return save_image_to_cache(base64_data, filename, image_info, ignore_info=ignore_info)
|
|
2766
2800
|
|
|
2767
|
-
def save_bytes_to_cache(
|
|
2801
|
+
def save_bytes_to_cache(
|
|
2802
|
+
self, bytes_data: Union[str, bytes], filename: str, file_info: Optional[Dict[str, Any]]
|
|
2803
|
+
) -> Tuple[str, Optional[Dict[str, Any]]]:
|
|
2768
2804
|
return save_bytes_to_cache(bytes_data, filename, file_info)
|
|
2769
2805
|
|
|
2770
|
-
def text_from_file(self, path):
|
|
2806
|
+
def text_from_file(self, path: str) -> str:
|
|
2771
2807
|
return text_from_file(path)
|
|
2772
2808
|
|
|
2773
|
-
def json_from_file(self, path):
|
|
2809
|
+
def json_from_file(self, path: str) -> Any:
|
|
2774
2810
|
return json_from_file(path)
|
|
2775
2811
|
|
|
2776
|
-
def download_file(self, url):
|
|
2812
|
+
def download_file(self, url: str) -> Tuple[bytes, Dict[str, Any]]:
|
|
2777
2813
|
return download_file(url)
|
|
2778
2814
|
|
|
2779
|
-
def session_download_file(self, session, url):
|
|
2815
|
+
def session_download_file(self, session: aiohttp.ClientSession, url: str) -> Tuple[bytes, Dict[str, Any]]:
|
|
2780
2816
|
return session_download_file(session, url)
|
|
2781
2817
|
|
|
2782
|
-
def read_binary_file(self, url):
|
|
2818
|
+
def read_binary_file(self, url: str) -> Tuple[bytes, Dict[str, Any]]:
|
|
2783
2819
|
return read_binary_file(url)
|
|
2784
2820
|
|
|
2785
|
-
def log(self, message):
|
|
2821
|
+
def log(self, message: Any):
|
|
2786
2822
|
if self.verbose:
|
|
2787
2823
|
print(f"[{self.name}] {message}", flush=True)
|
|
2788
2824
|
return message
|
|
2789
2825
|
|
|
2790
|
-
def log_json(self, obj):
|
|
2826
|
+
def log_json(self, obj: Any):
|
|
2791
2827
|
if self.verbose:
|
|
2792
2828
|
print(f"[{self.name}] {json.dumps(obj, indent=2)}", flush=True)
|
|
2793
2829
|
return obj
|
|
2794
2830
|
|
|
2795
|
-
def dbg(self, message):
|
|
2831
|
+
def dbg(self, message: Any):
|
|
2796
2832
|
if self.debug:
|
|
2797
2833
|
print(f"DEBUG [{self.name}]: {message}", flush=True)
|
|
2798
2834
|
|
|
2799
|
-
def err(self, message, e):
|
|
2835
|
+
def err(self, message: str, e: Exception):
|
|
2800
2836
|
print(f"ERROR [{self.name}]: {message}", e)
|
|
2801
2837
|
if self.verbose:
|
|
2802
2838
|
print(traceback.format_exc(), flush=True)
|
|
2803
2839
|
|
|
2804
|
-
def error_message(self, e):
|
|
2840
|
+
def error_message(self, e: Exception) -> str:
|
|
2805
2841
|
return to_error_message(e)
|
|
2806
2842
|
|
|
2807
|
-
def error_response(self, e, stacktrace=False):
|
|
2843
|
+
def error_response(self, e: Exception, stacktrace: bool = False) -> Dict[str, Any]:
|
|
2808
2844
|
return to_error_response(e, stacktrace=stacktrace)
|
|
2809
2845
|
|
|
2810
|
-
def add_provider(self, provider):
|
|
2846
|
+
def add_provider(self, provider: Any):
|
|
2811
2847
|
self.log(f"Registered provider: {provider.__name__}")
|
|
2812
2848
|
self.app.all_providers.append(provider)
|
|
2813
2849
|
|
|
2814
|
-
def register_ui_extension(self, index):
|
|
2850
|
+
def register_ui_extension(self, index: str):
|
|
2815
2851
|
path = os.path.join(self.ext_prefix, index)
|
|
2816
2852
|
self.log(f"Registered UI extension: {path}")
|
|
2817
2853
|
self.app.ui_extensions.append({"id": self.name, "path": path})
|
|
2818
2854
|
|
|
2819
|
-
def register_chat_request_filter(self, handler):
|
|
2855
|
+
def register_chat_request_filter(self, handler: Callable):
|
|
2820
2856
|
self.log(f"Registered chat request filter: {handler_name(handler)}")
|
|
2821
2857
|
self.app.chat_request_filters.append(handler)
|
|
2822
2858
|
|
|
2823
|
-
def register_chat_tool_filter(self, handler):
|
|
2859
|
+
def register_chat_tool_filter(self, handler: Callable):
|
|
2824
2860
|
self.log(f"Registered chat tool filter: {handler_name(handler)}")
|
|
2825
2861
|
self.app.chat_tool_filters.append(handler)
|
|
2826
2862
|
|
|
2827
|
-
def register_chat_response_filter(self, handler):
|
|
2863
|
+
def register_chat_response_filter(self, handler: Callable):
|
|
2828
2864
|
self.log(f"Registered chat response filter: {handler_name(handler)}")
|
|
2829
2865
|
self.app.chat_response_filters.append(handler)
|
|
2830
2866
|
|
|
2831
|
-
def register_chat_error_filter(self, handler):
|
|
2867
|
+
def register_chat_error_filter(self, handler: Callable):
|
|
2832
2868
|
self.log(f"Registered chat error filter: {handler_name(handler)}")
|
|
2833
2869
|
self.app.chat_error_filters.append(handler)
|
|
2834
2870
|
|
|
2835
|
-
def register_cache_saved_filter(self, handler):
|
|
2871
|
+
def register_cache_saved_filter(self, handler: Callable):
|
|
2836
2872
|
self.log(f"Registered cache saved filter: {handler_name(handler)}")
|
|
2837
2873
|
self.app.cache_saved_filters.append(handler)
|
|
2838
2874
|
|
|
2839
|
-
def register_shutdown_handler(self, handler):
|
|
2875
|
+
def register_shutdown_handler(self, handler: Callable):
|
|
2840
2876
|
self.log(f"Registered shutdown handler: {handler_name(handler)}")
|
|
2841
2877
|
self.app.shutdown_handlers.append(handler)
|
|
2842
2878
|
|
|
2843
|
-
def add_static_files(self, ext_dir):
|
|
2879
|
+
def add_static_files(self, ext_dir: str):
|
|
2844
2880
|
self.log(f"Registered static files: {ext_dir}")
|
|
2845
2881
|
|
|
2846
2882
|
async def serve_static(request):
|
|
@@ -2852,57 +2888,63 @@ class ExtensionContext:
|
|
|
2852
2888
|
|
|
2853
2889
|
self.app.server_add_get.append((os.path.join(self.ext_prefix, "{path:.*}"), serve_static, {}))
|
|
2854
2890
|
|
|
2855
|
-
def web_path(self, method, path):
|
|
2891
|
+
def web_path(self, method: str, path: str) -> str:
|
|
2856
2892
|
full_path = os.path.join(self.ext_prefix, path) if path else self.ext_prefix
|
|
2857
2893
|
self.dbg(f"Registered {method:<6} {full_path}")
|
|
2858
2894
|
return full_path
|
|
2859
2895
|
|
|
2860
|
-
def add_get(self, path, handler, **kwargs):
|
|
2896
|
+
def add_get(self, path: str, handler: Callable, **kwargs: Any):
|
|
2861
2897
|
self.app.server_add_get.append((self.web_path("GET", path), handler, kwargs))
|
|
2862
2898
|
|
|
2863
|
-
def add_post(self, path, handler, **kwargs):
|
|
2899
|
+
def add_post(self, path: str, handler: Callable, **kwargs: Any):
|
|
2864
2900
|
self.app.server_add_post.append((self.web_path("POST", path), handler, kwargs))
|
|
2865
2901
|
|
|
2866
|
-
def add_put(self, path, handler, **kwargs):
|
|
2902
|
+
def add_put(self, path: str, handler: Callable, **kwargs: Any):
|
|
2867
2903
|
self.app.server_add_put.append((self.web_path("PUT", path), handler, kwargs))
|
|
2868
2904
|
|
|
2869
|
-
def add_delete(self, path, handler, **kwargs):
|
|
2905
|
+
def add_delete(self, path: str, handler: Callable, **kwargs: Any):
|
|
2870
2906
|
self.app.server_add_delete.append((self.web_path("DELETE", path), handler, kwargs))
|
|
2871
2907
|
|
|
2872
|
-
def add_patch(self, path, handler, **kwargs):
|
|
2908
|
+
def add_patch(self, path: str, handler: Callable, **kwargs: Any):
|
|
2873
2909
|
self.app.server_add_patch.append((self.web_path("PATCH", path), handler, kwargs))
|
|
2874
2910
|
|
|
2875
|
-
def add_importmaps(self, dict):
|
|
2911
|
+
def add_importmaps(self, dict: Dict[str, str]):
|
|
2876
2912
|
self.app.import_maps.update(dict)
|
|
2877
2913
|
|
|
2878
|
-
def add_index_header(self, html):
|
|
2914
|
+
def add_index_header(self, html: str):
|
|
2879
2915
|
self.app.index_headers.append(html)
|
|
2880
2916
|
|
|
2881
|
-
def add_index_footer(self, html):
|
|
2917
|
+
def add_index_footer(self, html: str):
|
|
2882
2918
|
self.app.index_footers.append(html)
|
|
2883
2919
|
|
|
2884
|
-
def get_config(self):
|
|
2920
|
+
def get_config(self) -> Optional[Dict[str, Any]]:
|
|
2885
2921
|
return g_config
|
|
2886
2922
|
|
|
2887
|
-
def get_cache_path(self, path=""):
|
|
2923
|
+
def get_cache_path(self, path: str = "") -> str:
|
|
2888
2924
|
return get_cache_path(path)
|
|
2889
2925
|
|
|
2890
|
-
def get_file_mime_type(self, filename):
|
|
2926
|
+
def get_file_mime_type(self, filename: str) -> str:
|
|
2891
2927
|
return get_file_mime_type(filename)
|
|
2892
2928
|
|
|
2893
|
-
def chat_request(
|
|
2929
|
+
def chat_request(
|
|
2930
|
+
self,
|
|
2931
|
+
template: Optional[str] = None,
|
|
2932
|
+
text: Optional[str] = None,
|
|
2933
|
+
model: Optional[str] = None,
|
|
2934
|
+
system_prompt: Optional[str] = None,
|
|
2935
|
+
) -> Dict[str, Any]:
|
|
2894
2936
|
return self.app.chat_request(template=template, text=text, model=model, system_prompt=system_prompt)
|
|
2895
2937
|
|
|
2896
|
-
def chat_completion(self, chat, context=None):
|
|
2897
|
-
return self.app.chat_completion(chat, context=context)
|
|
2938
|
+
async def chat_completion(self, chat: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> Any:
|
|
2939
|
+
return await self.app.chat_completion(chat, context=context)
|
|
2898
2940
|
|
|
2899
|
-
def get_providers(self):
|
|
2941
|
+
def get_providers(self) -> Dict[str, Any]:
|
|
2900
2942
|
return g_handlers
|
|
2901
2943
|
|
|
2902
|
-
def get_provider(self, name):
|
|
2944
|
+
def get_provider(self, name: str) -> Optional[Any]:
|
|
2903
2945
|
return g_handlers.get(name)
|
|
2904
2946
|
|
|
2905
|
-
def sanitize_tool_def(self, tool_def):
|
|
2947
|
+
def sanitize_tool_def(self, tool_def: Dict[str, Any]) -> Dict[str, Any]:
|
|
2906
2948
|
"""
|
|
2907
2949
|
Merge $defs parameter into tool_def property to reduce client/server complexity
|
|
2908
2950
|
"""
|
|
@@ -2942,7 +2984,7 @@ class ExtensionContext:
|
|
|
2942
2984
|
parameters = func_def.get("parameters", {})
|
|
2943
2985
|
defs = parameters.get("$defs", {})
|
|
2944
2986
|
properties = parameters.get("properties", {})
|
|
2945
|
-
for
|
|
2987
|
+
for _, prop_def in properties.items():
|
|
2946
2988
|
if "$ref" in prop_def:
|
|
2947
2989
|
ref = prop_def["$ref"]
|
|
2948
2990
|
if ref.startswith("#/$defs/"):
|
|
@@ -2954,7 +2996,7 @@ class ExtensionContext:
|
|
|
2954
2996
|
del parameters["$defs"]
|
|
2955
2997
|
return tool_def
|
|
2956
2998
|
|
|
2957
|
-
def register_tool(self, func, tool_def=None, group=None):
|
|
2999
|
+
def register_tool(self, func: Callable, tool_def: Optional[Dict[str, Any]] = None, group: Optional[str] = None):
|
|
2958
3000
|
if tool_def is None:
|
|
2959
3001
|
tool_def = function_to_tool_definition(func)
|
|
2960
3002
|
|
|
@@ -2976,54 +3018,61 @@ class ExtensionContext:
|
|
|
2976
3018
|
self.app.tool_groups[group] = []
|
|
2977
3019
|
self.app.tool_groups[group].append(name)
|
|
2978
3020
|
|
|
2979
|
-
def get_tool_definition(self, name):
|
|
3021
|
+
def get_tool_definition(self, name: str) -> Optional[Dict[str, Any]]:
|
|
2980
3022
|
for tool_def in self.app.tool_definitions:
|
|
2981
3023
|
if tool_def["function"]["name"] == name:
|
|
2982
3024
|
return tool_def
|
|
2983
3025
|
return None
|
|
2984
3026
|
|
|
2985
|
-
def group_resources(self, resources:
|
|
3027
|
+
def group_resources(self, resources: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]:
|
|
2986
3028
|
return group_resources(resources)
|
|
2987
3029
|
|
|
2988
|
-
def check_auth(self, request):
|
|
3030
|
+
def check_auth(self, request: web.Request) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
|
2989
3031
|
return self.app.check_auth(request)
|
|
2990
3032
|
|
|
2991
|
-
def get_session(self, request):
|
|
3033
|
+
def get_session(self, request: web.Request) -> Optional[Dict[str, Any]]:
|
|
2992
3034
|
return self.app.get_session(request)
|
|
2993
3035
|
|
|
2994
|
-
def get_username(self, request):
|
|
3036
|
+
def get_username(self, request: web.Request) -> Optional[str]:
|
|
2995
3037
|
return self.app.get_username(request)
|
|
2996
3038
|
|
|
2997
|
-
def get_user_path(self, username=None):
|
|
3039
|
+
def get_user_path(self, username: Optional[str] = None) -> str:
|
|
2998
3040
|
return self.app.get_user_path(username)
|
|
2999
3041
|
|
|
3000
|
-
def context_to_username(self, context):
|
|
3042
|
+
def context_to_username(self, context: Optional[Dict[str, Any]]) -> Optional[str]:
|
|
3001
3043
|
if context and "request" in context:
|
|
3002
3044
|
return self.get_username(context["request"])
|
|
3003
3045
|
return None
|
|
3004
3046
|
|
|
3005
|
-
def should_cancel_thread(self, context):
|
|
3047
|
+
def should_cancel_thread(self, context: Dict[str, Any]) -> bool:
|
|
3006
3048
|
return should_cancel_thread(context)
|
|
3007
3049
|
|
|
3008
|
-
def cache_message_inline_data(self, message):
|
|
3050
|
+
def cache_message_inline_data(self, message: Dict[str, Any]):
|
|
3009
3051
|
return cache_message_inline_data(message)
|
|
3010
3052
|
|
|
3011
|
-
async def exec_tool(self, name, args):
|
|
3053
|
+
async def exec_tool(self, name: str, args: Dict[str, Any]) -> Tuple[Optional[str], List[Dict[str, Any]]]:
|
|
3012
3054
|
return await g_exec_tool(name, args)
|
|
3013
3055
|
|
|
3014
|
-
def tool_result(
|
|
3056
|
+
def tool_result(
|
|
3057
|
+
self, result: Any, function_name: Optional[str] = None, function_args: Optional[Dict[str, Any]] = None
|
|
3058
|
+
) -> Dict[str, Any]:
|
|
3015
3059
|
return g_tool_result(result, function_name, function_args)
|
|
3016
3060
|
|
|
3017
|
-
def tool_result_part(
|
|
3061
|
+
def tool_result_part(
|
|
3062
|
+
self,
|
|
3063
|
+
result: Dict[str, Any],
|
|
3064
|
+
function_name: Optional[str] = None,
|
|
3065
|
+
function_args: Optional[Dict[str, Any]] = None,
|
|
3066
|
+
) -> Dict[str, Any]:
|
|
3018
3067
|
return tool_result_part(result, function_name, function_args)
|
|
3019
3068
|
|
|
3020
|
-
def to_content(self, result):
|
|
3069
|
+
def to_content(self, result: Any) -> str:
|
|
3021
3070
|
return to_content(result)
|
|
3022
3071
|
|
|
3023
|
-
def create_chat_with_tools(self, chat, use_tools="all"):
|
|
3072
|
+
def create_chat_with_tools(self, chat: Dict[str, Any], use_tools: str = "all") -> Dict[str, Any]:
|
|
3024
3073
|
return self.app.create_chat_with_tools(chat, use_tools)
|
|
3025
3074
|
|
|
3026
|
-
def chat_to_aspect_ratio(self, chat):
|
|
3075
|
+
def chat_to_aspect_ratio(self, chat: Dict[str, Any]) -> str:
|
|
3027
3076
|
return chat_to_aspect_ratio(chat)
|
|
3028
3077
|
|
|
3029
3078
|
|
|
@@ -3080,7 +3129,21 @@ def get_extensions_dirs():
|
|
|
3080
3129
|
return ret
|
|
3081
3130
|
|
|
3082
3131
|
|
|
3132
|
+
def verify_root_path():
|
|
3133
|
+
global _ROOT
|
|
3134
|
+
_ROOT = os.getenv("LLMS_ROOT", resolve_root())
|
|
3135
|
+
if not _ROOT:
|
|
3136
|
+
print("Resource root not found")
|
|
3137
|
+
exit(1)
|
|
3138
|
+
|
|
3139
|
+
|
|
3083
3140
|
def init_extensions(parser):
|
|
3141
|
+
"""
|
|
3142
|
+
Programmatic entry point for the CLI.
|
|
3143
|
+
Example: cli("ls minimax")
|
|
3144
|
+
"""
|
|
3145
|
+
verify_root_path()
|
|
3146
|
+
|
|
3084
3147
|
"""
|
|
3085
3148
|
Initializes extensions by loading their __init__.py files and calling the __parser__ function if it exists.
|
|
3086
3149
|
"""
|
|
@@ -3235,14 +3298,7 @@ def run_extension_cli():
|
|
|
3235
3298
|
return False
|
|
3236
3299
|
|
|
3237
3300
|
|
|
3238
|
-
def
|
|
3239
|
-
global _ROOT, g_verbose, g_default_model, g_logprefix, g_providers, g_config, g_config_path, g_app
|
|
3240
|
-
|
|
3241
|
-
_ROOT = os.getenv("LLMS_ROOT", resolve_root())
|
|
3242
|
-
if not _ROOT:
|
|
3243
|
-
print("Resource root not found")
|
|
3244
|
-
exit(1)
|
|
3245
|
-
|
|
3301
|
+
def create_arg_parser():
|
|
3246
3302
|
parser = argparse.ArgumentParser(description=f"llms v{VERSION}")
|
|
3247
3303
|
parser.add_argument("--config", default=None, help="Path to config file", metavar="FILE")
|
|
3248
3304
|
parser.add_argument("--providers", default=None, help="Path to models.dev providers file", metavar="FILE")
|
|
@@ -3311,11 +3367,13 @@ def main():
|
|
|
3311
3367
|
help="Update an extension (use 'all' to update all extensions)",
|
|
3312
3368
|
metavar="EXTENSION",
|
|
3313
3369
|
)
|
|
3370
|
+
return parser
|
|
3314
3371
|
|
|
3315
|
-
# Load parser extensions, go through all extensions and load their parser arguments
|
|
3316
|
-
init_extensions(parser)
|
|
3317
3372
|
|
|
3318
|
-
|
|
3373
|
+
def cli_exec(cli_args, extra_args):
|
|
3374
|
+
global _ROOT, g_verbose, g_default_model, g_logprefix, g_providers, g_config, g_config_path, g_app
|
|
3375
|
+
|
|
3376
|
+
verify_root_path()
|
|
3319
3377
|
|
|
3320
3378
|
g_app = AppExtensions(cli_args, extra_args)
|
|
3321
3379
|
|
|
@@ -3351,12 +3409,12 @@ def main():
|
|
|
3351
3409
|
else:
|
|
3352
3410
|
asyncio.run(save_text_url(github_url("providers-extra.json"), home_providers_extra_path))
|
|
3353
3411
|
print(f"Created default extra providers config at {home_providers_extra_path}")
|
|
3354
|
-
|
|
3412
|
+
return ExitCode.SUCCESS
|
|
3355
3413
|
|
|
3356
3414
|
if cli_args.providers:
|
|
3357
3415
|
if not os.path.exists(cli_args.providers):
|
|
3358
3416
|
print(f"providers.json not found at {cli_args.providers}")
|
|
3359
|
-
|
|
3417
|
+
return ExitCode.FAILED
|
|
3360
3418
|
g_providers = json.loads(text_from_file(cli_args.providers))
|
|
3361
3419
|
|
|
3362
3420
|
if cli_args.config:
|
|
@@ -3385,7 +3443,7 @@ def main():
|
|
|
3385
3443
|
if cli_args.update_providers:
|
|
3386
3444
|
asyncio.run(update_providers(home_providers_path))
|
|
3387
3445
|
print(f"Updated {home_providers_path}")
|
|
3388
|
-
|
|
3446
|
+
return ExitCode.SUCCESS
|
|
3389
3447
|
|
|
3390
3448
|
# if home_providers_path is older than 1 day, update providers list
|
|
3391
3449
|
if (
|
|
@@ -3418,7 +3476,7 @@ def main():
|
|
|
3418
3476
|
print(" llms --add <github-user>/<repo>")
|
|
3419
3477
|
|
|
3420
3478
|
asyncio.run(list_extensions())
|
|
3421
|
-
|
|
3479
|
+
return ExitCode.SUCCESS
|
|
3422
3480
|
|
|
3423
3481
|
async def install_extension(name):
|
|
3424
3482
|
# Determine git URL and target directory name
|
|
@@ -3481,7 +3539,7 @@ def main():
|
|
|
3481
3539
|
os.rmdir(target_path)
|
|
3482
3540
|
|
|
3483
3541
|
asyncio.run(install_extension(cli_args.add))
|
|
3484
|
-
|
|
3542
|
+
return ExitCode.SUCCESS
|
|
3485
3543
|
|
|
3486
3544
|
if cli_args.remove is not None:
|
|
3487
3545
|
if cli_args.remove == "ls":
|
|
@@ -3490,11 +3548,11 @@ def main():
|
|
|
3490
3548
|
extensions = os.listdir(extensions_path)
|
|
3491
3549
|
if len(extensions) == 0:
|
|
3492
3550
|
print("No extensions installed.")
|
|
3493
|
-
|
|
3551
|
+
return ExitCode.SUCCESS
|
|
3494
3552
|
print("Installed extensions:")
|
|
3495
3553
|
for extension in extensions:
|
|
3496
3554
|
print(f" {extension}")
|
|
3497
|
-
|
|
3555
|
+
return ExitCode.SUCCESS
|
|
3498
3556
|
# Remove an extension
|
|
3499
3557
|
extension_name = cli_args.remove
|
|
3500
3558
|
extensions_path = get_extensions_path()
|
|
@@ -3502,7 +3560,7 @@ def main():
|
|
|
3502
3560
|
|
|
3503
3561
|
if not os.path.exists(target_path):
|
|
3504
3562
|
print(f"Extension {extension_name} not found at {target_path}")
|
|
3505
|
-
|
|
3563
|
+
return ExitCode.FAILED
|
|
3506
3564
|
|
|
3507
3565
|
print(f"Removing extension: {extension_name}...")
|
|
3508
3566
|
try:
|
|
@@ -3510,9 +3568,9 @@ def main():
|
|
|
3510
3568
|
print(f"Extension {extension_name} removed successfully.")
|
|
3511
3569
|
except Exception as e:
|
|
3512
3570
|
print(f"Failed to remove extension: {e}")
|
|
3513
|
-
|
|
3571
|
+
return ExitCode.FAILED
|
|
3514
3572
|
|
|
3515
|
-
|
|
3573
|
+
return ExitCode.SUCCESS
|
|
3516
3574
|
|
|
3517
3575
|
if cli_args.update:
|
|
3518
3576
|
if cli_args.update == "ls":
|
|
@@ -3521,7 +3579,7 @@ def main():
|
|
|
3521
3579
|
extensions = os.listdir(extensions_path)
|
|
3522
3580
|
if len(extensions) == 0:
|
|
3523
3581
|
print("No extensions installed.")
|
|
3524
|
-
|
|
3582
|
+
return ExitCode.SUCCESS
|
|
3525
3583
|
print("Installed extensions:")
|
|
3526
3584
|
for extension in extensions:
|
|
3527
3585
|
print(f" {extension}")
|
|
@@ -3529,7 +3587,7 @@ def main():
|
|
|
3529
3587
|
print("\nUsage:")
|
|
3530
3588
|
print(" llms --update <extension>")
|
|
3531
3589
|
print(" llms --update all")
|
|
3532
|
-
|
|
3590
|
+
return ExitCode.SUCCESS
|
|
3533
3591
|
|
|
3534
3592
|
async def update_extensions(extension_name):
|
|
3535
3593
|
extensions_path = get_extensions_path()
|
|
@@ -3546,7 +3604,7 @@ def main():
|
|
|
3546
3604
|
_log(result.stdout.decode("utf-8"))
|
|
3547
3605
|
|
|
3548
3606
|
asyncio.run(update_extensions(cli_args.update))
|
|
3549
|
-
|
|
3607
|
+
return ExitCode.SUCCESS
|
|
3550
3608
|
|
|
3551
3609
|
g_app.extensions = install_extensions()
|
|
3552
3610
|
|
|
@@ -3604,7 +3662,7 @@ def main():
|
|
|
3604
3662
|
print(f"\n{model_count} models available from {provider_count} providers")
|
|
3605
3663
|
|
|
3606
3664
|
print_status()
|
|
3607
|
-
|
|
3665
|
+
return ExitCode.SUCCESS
|
|
3608
3666
|
|
|
3609
3667
|
if cli_args.check is not None:
|
|
3610
3668
|
# Check validity of models for a provider
|
|
@@ -3613,7 +3671,7 @@ def main():
|
|
|
3613
3671
|
provider_name = cli_args.check
|
|
3614
3672
|
model_names = extra_args if len(extra_args) > 0 else None
|
|
3615
3673
|
loop.run_until_complete(check_models(provider_name, model_names))
|
|
3616
|
-
|
|
3674
|
+
return ExitCode.SUCCESS
|
|
3617
3675
|
|
|
3618
3676
|
if cli_args.serve is not None:
|
|
3619
3677
|
# Disable inactive providers and save to config before starting server
|
|
@@ -3658,7 +3716,7 @@ def main():
|
|
|
3658
3716
|
print("ERROR: Authentication is enabled but GitHub OAuth is not properly configured.")
|
|
3659
3717
|
print("Please set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables,")
|
|
3660
3718
|
print("or disable authentication by setting 'auth.enabled' to false in llms.json")
|
|
3661
|
-
|
|
3719
|
+
return ExitCode.FAILED
|
|
3662
3720
|
|
|
3663
3721
|
_log("Authentication enabled - GitHub OAuth configured")
|
|
3664
3722
|
|
|
@@ -4252,7 +4310,7 @@ def main():
|
|
|
4252
4310
|
|
|
4253
4311
|
print(f"Starting server on port {port}...")
|
|
4254
4312
|
web.run_app(app, host="0.0.0.0", port=port, print=_log)
|
|
4255
|
-
|
|
4313
|
+
return ExitCode.SUCCESS
|
|
4256
4314
|
|
|
4257
4315
|
if cli_args.enable is not None:
|
|
4258
4316
|
if cli_args.enable.endswith(","):
|
|
@@ -4271,7 +4329,7 @@ def main():
|
|
|
4271
4329
|
if provider not in g_config["providers"]:
|
|
4272
4330
|
print(f"Provider '{provider}' not found")
|
|
4273
4331
|
print(f"Available providers: {', '.join(g_config['providers'].keys())}")
|
|
4274
|
-
|
|
4332
|
+
return ExitCode.FAILED
|
|
4275
4333
|
if provider in g_config["providers"]:
|
|
4276
4334
|
provider_config, msg = enable_provider(provider)
|
|
4277
4335
|
print(f"\nEnabled provider {provider}:")
|
|
@@ -4282,7 +4340,7 @@ def main():
|
|
|
4282
4340
|
print_status()
|
|
4283
4341
|
if len(msgs) > 0:
|
|
4284
4342
|
print("\n" + "\n".join(msgs))
|
|
4285
|
-
|
|
4343
|
+
return ExitCode.SUCCESS
|
|
4286
4344
|
|
|
4287
4345
|
if cli_args.disable is not None:
|
|
4288
4346
|
if cli_args.disable.endswith(","):
|
|
@@ -4300,24 +4358,24 @@ def main():
|
|
|
4300
4358
|
if provider not in g_config["providers"]:
|
|
4301
4359
|
print(f"Provider {provider} not found")
|
|
4302
4360
|
print(f"Available providers: {', '.join(g_config['providers'].keys())}")
|
|
4303
|
-
|
|
4361
|
+
return ExitCode.FAILED
|
|
4304
4362
|
disable_provider(provider)
|
|
4305
4363
|
print(f"\nDisabled provider {provider}")
|
|
4306
4364
|
|
|
4307
4365
|
print_status()
|
|
4308
|
-
|
|
4366
|
+
return ExitCode.SUCCESS
|
|
4309
4367
|
|
|
4310
4368
|
if cli_args.default is not None:
|
|
4311
4369
|
default_model = cli_args.default
|
|
4312
4370
|
provider_model = get_provider_model(default_model)
|
|
4313
4371
|
if provider_model is None:
|
|
4314
4372
|
print(f"Model {default_model} not found")
|
|
4315
|
-
|
|
4373
|
+
return ExitCode.FAILED
|
|
4316
4374
|
default_text = g_config["defaults"]["text"]
|
|
4317
4375
|
default_text["model"] = default_model
|
|
4318
4376
|
save_config(g_config)
|
|
4319
4377
|
print(f"\nDefault model set to: {default_model}")
|
|
4320
|
-
|
|
4378
|
+
return ExitCode.SUCCESS
|
|
4321
4379
|
|
|
4322
4380
|
if (
|
|
4323
4381
|
cli_args.chat is not None
|
|
@@ -4339,13 +4397,13 @@ def main():
|
|
|
4339
4397
|
template = f"out:{cli_args.out}"
|
|
4340
4398
|
if template not in g_config["defaults"]:
|
|
4341
4399
|
print(f"Template for output modality '{cli_args.out}' not found")
|
|
4342
|
-
|
|
4400
|
+
return ExitCode.FAILED
|
|
4343
4401
|
chat = g_config["defaults"][template]
|
|
4344
4402
|
if cli_args.chat is not None:
|
|
4345
4403
|
chat_path = os.path.join(os.path.dirname(__file__), cli_args.chat)
|
|
4346
4404
|
if not os.path.exists(chat_path):
|
|
4347
4405
|
print(f"Chat request template not found: {chat_path}")
|
|
4348
|
-
|
|
4406
|
+
return ExitCode.FAILED
|
|
4349
4407
|
_log(f"Using chat: {chat_path}")
|
|
4350
4408
|
|
|
4351
4409
|
with open(chat_path) as f:
|
|
@@ -4386,19 +4444,48 @@ def main():
|
|
|
4386
4444
|
raw=cli_args.raw,
|
|
4387
4445
|
)
|
|
4388
4446
|
)
|
|
4389
|
-
|
|
4447
|
+
return ExitCode.SUCCESS
|
|
4390
4448
|
except Exception as e:
|
|
4391
4449
|
print(f"{cli_args.logprefix}Error: {e}")
|
|
4392
4450
|
if cli_args.verbose:
|
|
4393
4451
|
traceback.print_exc()
|
|
4394
|
-
|
|
4452
|
+
return ExitCode.FAILED
|
|
4395
4453
|
|
|
4396
4454
|
handled = run_extension_cli()
|
|
4455
|
+
return ExitCode.SUCCESS if handled else ExitCode.UNHANDLED
|
|
4456
|
+
|
|
4457
|
+
|
|
4458
|
+
def get_app():
|
|
4459
|
+
return g_app
|
|
4460
|
+
|
|
4461
|
+
|
|
4462
|
+
def cli(command_line: str):
|
|
4463
|
+
parser = create_arg_parser()
|
|
4397
4464
|
|
|
4398
|
-
|
|
4465
|
+
# Load parser extensions, go through all extensions and load their parser arguments
|
|
4466
|
+
if load_extensions:
|
|
4467
|
+
init_extensions(parser)
|
|
4468
|
+
|
|
4469
|
+
args = shlex.split(command_line)
|
|
4470
|
+
cli_args, extra_args = parser.parse_known_args(args)
|
|
4471
|
+
return cli_exec(cli_args, extra_args)
|
|
4472
|
+
|
|
4473
|
+
|
|
4474
|
+
def main():
|
|
4475
|
+
parser = create_arg_parser()
|
|
4476
|
+
|
|
4477
|
+
# Load parser extensions, go through all extensions and load their parser arguments
|
|
4478
|
+
init_extensions(parser)
|
|
4479
|
+
|
|
4480
|
+
cli_args, extra_args = parser.parse_known_args()
|
|
4481
|
+
exit_code = cli_exec(cli_args, extra_args)
|
|
4482
|
+
|
|
4483
|
+
if exit_code == ExitCode.UNHANDLED:
|
|
4399
4484
|
# show usage from ArgumentParser
|
|
4400
4485
|
parser.print_help()
|
|
4401
|
-
g_app.exit(0)
|
|
4486
|
+
g_app.exit(0) if g_app else exit(0)
|
|
4487
|
+
|
|
4488
|
+
g_app.exit(exit_code) if g_app else exit(exit_code)
|
|
4402
4489
|
|
|
4403
4490
|
|
|
4404
4491
|
if __name__ == "__main__":
|
llms/ui/ai.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.12
|
|
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
|
|
@@ -44,6 +44,6 @@ Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing mu
|
|
|
44
44
|
|
|
45
45
|
[llmspy.org](https://llmspy.org)
|
|
46
46
|
|
|
47
|
-
[](https://llmspy.org)
|
|
47
|
+
[](https://llmspy.org)
|
|
48
48
|
|
|
49
49
|
GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
|
|
@@ -3,13 +3,13 @@ llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
|
|
|
3
3
|
llms/db.py,sha256=oozp5I5lECVO8oZEFwcZl3ES5mARqWeR1BkoqG5kSqM,11687
|
|
4
4
|
llms/index.html,sha256=nGk1Djtn9p7l6LuKp4Kg0JIB9fCzxtTWXFfmDb4ggpc,1658
|
|
5
5
|
llms/llms.json,sha256=NEr9kJRkUGZ2YZHbWC-haGPlVVL2Qtnx4kKZENGH1wk,11494
|
|
6
|
-
llms/main.py,sha256=
|
|
6
|
+
llms/main.py,sha256=HgQ_nS0OSqu8BRq_k4a1vpPhGzNiIgvIg-fxs7O6G44,174163
|
|
7
7
|
llms/providers-extra.json,sha256=_6DmGBiQY9LM6_Y0zOiObYn7ba4g3akSNQfmHcYlENc,11101
|
|
8
8
|
llms/providers.json,sha256=yjhDurlwo70xqfV0HNLiZaCpw3WvtIgkjoLahQIKX2w,282530
|
|
9
9
|
llms/extensions/analytics/ui/index.mjs,sha256=m1XwaqYCLwK267JAUCAltkN_nOXep0GxfpvGNS5i4_w,69547
|
|
10
10
|
llms/extensions/app/README.md,sha256=TKoblZpHlheLCh_dfXOxqTc5OvxlgMBa-vKo8Hqb2gg,1370
|
|
11
11
|
llms/extensions/app/__init__.py,sha256=aU8Bfliw--Xj1bsKL3PSoX6MY1ZNgweNyMWS1V_YG4s,20855
|
|
12
|
-
llms/extensions/app/db.py,sha256=
|
|
12
|
+
llms/extensions/app/db.py,sha256=DU8YZ25yFsBI-O6msxh2GgzbwaqKqXkAHJLwQKcmFPI,21533
|
|
13
13
|
llms/extensions/app/ui/Recents.mjs,sha256=2ypAKUp9_Oqcive1nUWZ8I2PQTBomBg_Pkjygi4oPgs,9261
|
|
14
14
|
llms/extensions/app/ui/index.mjs,sha256=sB9176LLNuKFsZ28yL-tROA6J4xePNtvxtSrzFcinRo,13271
|
|
15
15
|
llms/extensions/app/ui/threadStore.mjs,sha256=CLlD-1HBO1yhZBHLfL0ZdA4quT4R07qr1JW4a8igVNc,12287
|
|
@@ -128,10 +128,10 @@ llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf,sha256=8B8-h9nGphwMC
|
|
|
128
128
|
llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff,sha256=4U_tArGrp86fWv1YRLXQMhsiNR_rxyDg3ouHI1J2Cfc,16028
|
|
129
129
|
llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2,sha256=cdUX1ngneHz6vfGGkUzDNY7aU543kxlB8rL9SiH2jAs,13568
|
|
130
130
|
llms/extensions/providers/__init__.py,sha256=C5zOBQEOB2L96rAZdjV42fPVk_dZxSh2Dv30Kb1w3lE,534
|
|
131
|
-
llms/extensions/providers/anthropic.py,sha256=
|
|
131
|
+
llms/extensions/providers/anthropic.py,sha256=V9mechnhyoX-5Z5AkwyQ-UzLax6cqG7j7GLvGTZF9no,10941
|
|
132
132
|
llms/extensions/providers/cerebras.py,sha256=HaeFW0GwbD6V6Zrrwqyv78kQb0VXg9oHmykvJfIOOYE,1417
|
|
133
133
|
llms/extensions/providers/chutes.py,sha256=5ZrfbqoOhgzKLQy_qULcp4jlvW5WXPR0jP9kN2Jzb9g,6229
|
|
134
|
-
llms/extensions/providers/google.py,sha256=
|
|
134
|
+
llms/extensions/providers/google.py,sha256=oCCTE2KAw-WWE2v14XpKzgAMdFIWbjTBoa6GWuqT4dw,26215
|
|
135
135
|
llms/extensions/providers/nvidia.py,sha256=C6cwqn3EufYDfRIgbc8MDkQNyD6w3c7hbjfYaHJSDik,4279
|
|
136
136
|
llms/extensions/providers/openai.py,sha256=hkE-LVsw6M92_qEbpayuPo17Z1OWKHe7lm2wduLMng8,6138
|
|
137
137
|
llms/extensions/providers/openrouter.py,sha256=5SfCJKo1aGKoDGez6HXYQe9elMMo9sSEDFqqdxamAgA,3330
|
|
@@ -143,7 +143,7 @@ llms/extensions/system_prompts/ui/prompts.json,sha256=t5DD3bird-87wFa4OlW-bC2wdo
|
|
|
143
143
|
llms/extensions/tools/__init__.py,sha256=u76604Cn_sRFQRqeA_pkVEty27V688Mt9Z7Kh63yDr8,4825
|
|
144
144
|
llms/extensions/tools/ui/index.mjs,sha256=IbGB2FQJ5VL4a8arwoR9C79vUCNrz8VIyQnHZ4vxU9o,34486
|
|
145
145
|
llms/ui/App.mjs,sha256=CoUzO9mV__-jV19NKHYIbwHsjWMnO11jyNSbnJhe1gQ,7486
|
|
146
|
-
llms/ui/ai.mjs,sha256=
|
|
146
|
+
llms/ui/ai.mjs,sha256=S-LmypGGmTrAteH9By37md6gRJw2ILbjXf36Glho5EY,6541
|
|
147
147
|
llms/ui/app.css,sha256=vfXErYVdVlE3pL8oZ-2G_OC-_reJzmaL0p91EVv48uo,186490
|
|
148
148
|
llms/ui/ctx.mjs,sha256=X4scgXEQ9bMUfQl36sM4A3o2Ufad3LRwItxfmSu1xwc,12838
|
|
149
149
|
llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
|
|
@@ -169,9 +169,9 @@ llms/ui/modules/model-selector.mjs,sha256=6U4rAZ7vmQELFRQGWk4YEtq02v3lyHdMq6yUOp
|
|
|
169
169
|
llms/ui/modules/chat/ChatBody.mjs,sha256=5yWjo6tWmcKidDpRvKFeHqx3lXO3DB-3rTyXY72gB4U,49122
|
|
170
170
|
llms/ui/modules/chat/SettingsDialog.mjs,sha256=HMBJTwrapKrRIAstIIqp0QlJL5O-ho4hzgvfagPfsX8,19930
|
|
171
171
|
llms/ui/modules/chat/index.mjs,sha256=lfSbERMaM3bLsKhdJJPWwL4-FGr8U_ftlvqW5vC3T1s,39762
|
|
172
|
-
llms_py-3.0.
|
|
173
|
-
llms_py-3.0.
|
|
174
|
-
llms_py-3.0.
|
|
175
|
-
llms_py-3.0.
|
|
176
|
-
llms_py-3.0.
|
|
177
|
-
llms_py-3.0.
|
|
172
|
+
llms_py-3.0.12.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
|
|
173
|
+
llms_py-3.0.12.dist-info/METADATA,sha256=KVDU66mm6L_mF4uwzhWhSbPHqqydeOKSuuKHf0IRJ_w,2195
|
|
174
|
+
llms_py-3.0.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
175
|
+
llms_py-3.0.12.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
|
|
176
|
+
llms_py-3.0.12.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
|
|
177
|
+
llms_py-3.0.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|