55import re
66import requests
77import logging
8- from typing import List , Tuple , Optional
8+ from typing import List , Optional
9+
910
1011logger = logging .getLogger ("EA_LMStudio" )
1112
1718# Constants
1819CUSTOM_MODEL_OPTION = "-- Custom (enter below) --"
1920
20- # Patterns to exclude from model list (embedding models, etc.)
21- EXCLUDED_MODEL_PATTERNS = ("embedding" ,)
22-
2321
24- def validate_model_identifier (model_id : str ) -> Tuple [ bool , str ] :
22+ def validate_model_identifier (model_id : str ) -> bool :
2523 """
2624 Validate model identifier for safety.
2725
2826 Args:
2927 model_id: The model identifier string to validate.
3028
3129 Returns:
32- Tuple of (is_valid, error_message). error_message is empty if valid .
30+ True if valid, False otherwise .
3331 """
3432 if not model_id or not model_id .strip ():
35- return False , "Model identifier cannot be empty"
33+ return False
3634
3735 model_id = model_id .strip ()
3836
3937 # Check for path traversal attempts
4038 if ".." in model_id :
41- return False , "Model identifier contains invalid path sequence"
39+ return False
4240
4341 # Check reasonable length
4442 if len (model_id ) > 256 :
45- return False , "Model identifier exceeds maximum length (256 characters)"
43+ return False
4644
4745 # Allow alphanumeric, hyphens, underscores, dots, colons, at signs, forward slashes
4846 # These are common in model names like "lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF"
4947 # or "qwen2.5-7b@q4_k_m"
5048 if not re .match (r'^[\w\-.:@/]+$' , model_id ):
51- return False , "Model identifier contains disallowed characters"
49+ return False
50+
51+ return True
5252
53- return True , ""
5453
54+ def _is_excluded (model_id : str , excluded_patterns : List [str ]) -> bool :
55+ """Check if a model should be excluded based on configured patterns."""
56+ if not excluded_patterns or not model_id :
57+ return False
58+ model_id_lower = model_id .lower ()
59+ return any (pattern in model_id_lower for pattern in excluded_patterns )
5560
56- def fetch_models_from_server (server_url : str , timeout : float = 5.0 ) -> Tuple [List [str ], Optional [str ]]:
61+
62+ def fetch_models_from_server (
63+ server_url : str ,
64+ timeout : float = 5.0 ,
65+ excluded_patterns : Optional [List [str ]] = None ,
66+ ) -> tuple [List [str ], Optional [str ]]:
5767 """
5868 Fetch available models from LM Studio server.
5969
6070 Args:
6171 server_url: Base URL of LM Studio server (e.g., http://127.0.0.1:1234)
6272 timeout: Request timeout in seconds
73+ excluded_patterns: List of substrings to exclude from model list.
74+ If None, uses default ["embedding"]. Pass an empty list to include all models.
6375
6476 Returns:
6577 Tuple of (model_list, error_message)
66- - model_list: List of model IDs (excludes embedding models) , empty list on failure
78+ - model_list: Filtered list of model IDs, empty on failure
6779 - error_message: None on success, descriptive error on failure
6880 """
6981 models : List [str ] = []
7082 error : Optional [str ] = None
7183
84+ # Default to ["embedding"] if no patterns specified
85+ if excluded_patterns is None :
86+ excluded_patterns = ["embedding" ]
87+
7288 endpoint = f"{ server_url .rstrip ('/' )} /v1/models"
7389
7490 try :
@@ -88,15 +104,14 @@ def fetch_models_from_server(server_url: str, timeout: float = 5.0) -> Tuple[Lis
88104 if not model_id :
89105 continue
90106
91- # Exclude embedding models by checking id pattern
92- model_id_lower = model_id .lower ()
93- is_excluded = any (pattern in model_id_lower for pattern in EXCLUDED_MODEL_PATTERNS )
107+ # Exclude models matching configured patterns
108+ if _is_excluded (model_id , excluded_patterns ):
109+ logger .debug (f"EA_LMStudio: Excluding model '{ model_id } '" )
110+ continue
94111
95- if not is_excluded :
96- # Validate the model ID before adding
97- is_valid , _ = validate_model_identifier (model_id )
98- if is_valid :
99- models .append (model_id )
112+ # Validate the model ID before adding
113+ if validate_model_identifier (model_id ):
114+ models .append (model_id )
100115
101116 # Sort alphabetically for easier navigation
102117 models .sort (key = str .lower )
@@ -139,20 +154,26 @@ def get_model_choices() -> List[str]:
139154 return choices
140155
141156
142- def refresh_model_cache (server_url : str , timeout : float = 5.0 ) -> Tuple [bool , str ]:
157+ def refresh_model_cache (
158+ server_url : str ,
159+ timeout : float = 5.0 ,
160+ excluded_patterns : Optional [List [str ]] = None ,
161+ ) -> tuple [bool , str ]:
143162 """
144163 Refresh the cached model list from server.
145164
146165 Args:
147166 server_url: Base URL of LM Studio server
148167 timeout: Request timeout in seconds
168+ excluded_patterns: List of substrings to exclude from model list.
169+ If None, uses default ["embedding"]. Pass an empty list to include all models.
149170
150171 Returns:
151172 Tuple of (success, message)
152173 """
153174 global _cached_models , _last_fetch_error , _last_fetch_success
154175
155- models , error = fetch_models_from_server (server_url , timeout )
176+ models , error = fetch_models_from_server (server_url , timeout , excluded_patterns )
156177
157178 if error :
158179 _last_fetch_error = error
@@ -169,15 +190,15 @@ def refresh_model_cache(server_url: str, timeout: float = 5.0) -> Tuple[bool, st
169190 return True , "Connected to LM Studio but no models found (embedding models are excluded)"
170191
171192
172- def initialize_model_cache (server_url : str , timeout : float = 5.0 ) -> None :
193+ def initialize_model_cache (server_url : str , timeout : float = 5.0 , excluded_patterns = None ) -> None :
173194 """
174195 Initialize model cache at startup. Silent failure - just logs warning.
175196
176197 Args:
177198 server_url: Base URL of LM Studio server
178199 timeout: Request timeout in seconds
179200 """
180- success , message = refresh_model_cache (server_url , timeout )
201+ success , message = refresh_model_cache (server_url , timeout , excluded_patterns = excluded_patterns )
181202 if not success :
182203 logger .warning (f"EA_LMStudio startup: { message } " )
183204 logger .warning ("EA_LMStudio: Models will need to be entered manually or refreshed later" )
0 commit comments