wuenlp_tools.config
1from __future__ import annotations 2 3import json 4import os 5from dataclasses import dataclass 6from pathlib import Path 7from typing import Any, Dict, Optional, Union 8 9__all__ = [ 10 "APIConfig", 11 "api_config", 12 "set_api_base_url", 13 "get_api_base_url", 14 "get_endpoint_url", 15 "LightLLMProxyConfig", 16 "LocalLLMConfig", 17 "WUENLP_DATA_DIR", 18 "read_llm_config", 19 "write_llm_config", 20 "get_lightllm_config", 21 "get_local_llm_config", 22 "get_pull_lock_dir", 23 "get_litellm_metadata", 24 "configure_lightllm_proxy", 25 "show_lightllm_status", 26 "get_ssc_repo_root", 27 "get_ssc_scene_model_path", 28 "get_ssc_suspense_model_root", 29 "read_ssc_config", 30 "write_ssc_config", 31] 32 33 34class APIConfig: 35 """Global configuration for WueNLP API endpoints.""" 36 37 _instance: Optional["APIConfig"] = None 38 39 def __init__(self): 40 self._base_url: str = os.getenv( 41 "WUENLP_API_BASE_URL", "https://wuenlp-api.professor-x.de" 42 ) 43 44 @classmethod 45 def get_instance(cls) -> "APIConfig": 46 """Get singleton instance of APIConfig.""" 47 if cls._instance is None: 48 cls._instance = cls() 49 return cls._instance 50 51 @property 52 def base_url(self) -> str: 53 """Get the base URL for WueNLP API.""" 54 return self._base_url 55 56 @base_url.setter 57 def base_url(self, value: str) -> None: 58 """Set the base URL for WueNLP API.""" 59 if value.endswith("/"): 60 value = value.rstrip("/") 61 self._base_url = value 62 63 def get_endpoint_url(self, endpoint: str) -> str: 64 """Get full URL for a specific endpoint.""" 65 if not endpoint.startswith("/"): 66 endpoint = "/" + endpoint 67 if not endpoint.endswith("/"): 68 endpoint = endpoint + "/" 69 return f"{self._base_url}{endpoint}" 70 71 72# Global instance 73api_config = APIConfig.get_instance() 74 75 76def set_api_base_url(url: str) -> None: 77 """Set the global API base URL.""" 78 api_config.base_url = url 79 80 81def get_api_base_url() -> str: 82 """Get the global API base URL.""" 83 return api_config.base_url 84 85 86def get_endpoint_url(endpoint: str) -> str: 87 """Get full URL for a specific endpoint.""" 88 return api_config.get_endpoint_url(endpoint) 89 90 91# LLM Configuration 92WUENLP_DATA_DIR = Path(os.path.expanduser("~/.local/share/wuenlp-tools")) 93LLM_CONFIG_PATH = os.path.expanduser("~/.local/share/wuenlp-tools/llm_config.json") 94DEFAULT_PULL_LOCK_DIR = WUENLP_DATA_DIR / "pull-locks" 95PROJECT_LLM_CONFIG_CANDIDATES = ( 96 ".wuenlp_llm_config.json", 97 "wuenlp_llm_config.json", 98) 99 100 101@dataclass 102class LightLLMProxyConfig: 103 enabled: bool = False 104 base_url: str = "http://localhost:8080/v1/" 105 model_prefixes: Optional[Dict[str, str]] = None 106 107 def __post_init__(self): 108 if self.model_prefixes is None: 109 self.model_prefixes = { 110 "openai": "openai/", 111 "openrouter": "openrouter/", 112 "ollama": "ollama/", 113 "local": "ollama/", 114 } 115 116 117@dataclass 118class LocalLLMConfig: 119 base_url: Optional[str] = None 120 port: Union[str, int] = "auto" 121 auto_pull: bool = True 122 manage_process: Union[str, bool] = "auto" 123 pull_lock_dir: Optional[str] = None 124 125 126def read_llm_config() -> dict: 127 """Read LLM config from JSON file. Returns empty dict if file doesn't exist.""" 128 if os.path.isfile(LLM_CONFIG_PATH): 129 try: 130 with open(LLM_CONFIG_PATH, "r") as f: 131 return json.load(f) 132 except json.JSONDecodeError: 133 return {} 134 return {} 135 136 137def write_llm_config(config: dict): 138 """Write the LLM config dictionary to the JSON file.""" 139 os.makedirs(os.path.dirname(LLM_CONFIG_PATH), exist_ok=True) 140 with open(LLM_CONFIG_PATH, "w") as f: 141 json.dump(config, f, indent=2) 142 143 144def get_lightllm_config() -> LightLLMProxyConfig: 145 """Get LightLLM proxy configuration.""" 146 config = read_llm_config() 147 proxy_config = config.get("lightllm_proxy", {}) 148 return LightLLMProxyConfig(**proxy_config) 149 150 151def get_local_llm_config() -> LocalLLMConfig: 152 """Get local Ollama configuration.""" 153 config = read_llm_config() 154 local_config = config.get("local_llm", {}) 155 return LocalLLMConfig(**local_config) 156 157 158def get_pull_lock_dir() -> Path: 159 """Directory for cross-job Ollama pull locks.""" 160 env_dir = os.environ.get("WUENLP_PULL_LOCK_DIR") 161 if env_dir: 162 return Path(os.path.expanduser(env_dir)) 163 164 cfg = get_local_llm_config() 165 if cfg.pull_lock_dir: 166 return Path(os.path.expanduser(cfg.pull_lock_dir)) 167 168 ollama_models = os.environ.get("OLLAMA_MODELS") 169 if ollama_models and _is_slurm(): 170 return Path(os.path.expanduser(ollama_models)) / ".wuenlp-pull-locks" 171 172 return DEFAULT_PULL_LOCK_DIR 173 174 175def _is_slurm() -> bool: 176 return bool(os.environ.get("SLURM_JOB_ID")) 177 178 179def _read_project_llm_config() -> dict: 180 """ 181 Read project-level LLM config from the current working directory. 182 183 The first existing file from PROJECT_LLM_CONFIG_CANDIDATES is used. 184 Returns an empty dict if no file is found or if parsing fails. 185 """ 186 cwd = os.getcwd() 187 for filename in PROJECT_LLM_CONFIG_CANDIDATES: 188 candidate = os.path.join(cwd, filename) 189 if os.path.isfile(candidate): 190 try: 191 with open(candidate, "r") as f: 192 return json.load(f) 193 except json.JSONDecodeError: 194 return {} 195 return {} 196 197 198def get_litellm_metadata() -> Dict[str, Any]: 199 """ 200 Get merged LiteLLM metadata configuration. 201 202 Order of precedence (later overrides earlier): 203 1. System-level config at LLM_CONFIG_PATH, key "litellm_metadata" 204 2. Project-level config in the current working directory 205 (".wuenlp_llm_config.json" or "wuenlp_llm_config.json"), 206 key "litellm_metadata" 207 208 This is intended for generic tags/metadata that should end up in LiteLLM logs. 209 """ 210 system_config = read_llm_config() 211 system_meta = system_config.get("litellm_metadata", {}) or {} 212 213 project_config = _read_project_llm_config() 214 project_meta = project_config.get("litellm_metadata", {}) or {} 215 216 merged: Dict[str, Any] = {} 217 merged.update(system_meta) 218 merged.update(project_meta) 219 return merged 220 221 222def configure_lightllm_proxy(): 223 """Interactive configuration helper for LightLLM proxy settings.""" 224 print("🔧 LightLLM Proxy Configuration") 225 print("=" * 40) 226 227 # Load current config 228 current_config = read_llm_config() 229 current_proxy = current_config.get("lightllm_proxy", {}) 230 231 print( 232 f"\nCurrent status: {'ENABLED' if current_proxy.get('enabled', False) else 'DISABLED'}" 233 ) 234 if current_proxy.get("base_url"): 235 print(f"Current proxy URL: {current_proxy['base_url']}") 236 237 # Ask if user wants to enable/disable 238 while True: 239 enable_input = input("\nEnable LightLLM proxy? [y/n]: ").strip().lower() 240 if enable_input in ["y", "yes", "true", "1"]: 241 enabled = True 242 break 243 elif enable_input in ["n", "no", "false", "0"]: 244 enabled = False 245 break 246 else: 247 print("Please enter 'y' for yes or 'n' for no") 248 249 if not enabled: 250 # Disable proxy 251 new_config = current_config.copy() 252 new_config["lightllm_proxy"] = {"enabled": False} 253 write_llm_config(new_config) 254 print("✅ LightLLM proxy disabled") 255 return 256 257 # Configure proxy settings 258 print("\n📝 Configuring proxy settings...") 259 260 # Base URL 261 current_url = current_proxy.get("base_url", "http://localhost:8080/v1/") 262 base_url = input(f"Proxy base URL [{current_url}]: ").strip() 263 if not base_url: 264 base_url = current_url 265 266 # Ensure URL ends with /v1/ 267 if not base_url.endswith("/"): 268 base_url += "/" 269 if not base_url.endswith("v1/"): 270 if base_url.endswith("/"): 271 base_url += "v1/" 272 else: 273 base_url += "/v1/" 274 275 # Model prefixes 276 current_prefixes = current_proxy.get( 277 "model_prefixes", 278 {"openai": "openai/", "openrouter": "openrouter/", "ollama": "ollama/"}, 279 ) 280 281 print("\n🏷️ Configure model prefixes (press Enter to keep current):") 282 283 model_prefixes = {} 284 for provider in ["openai", "openrouter", "ollama"]: 285 current_prefix = current_prefixes.get(provider, f"{provider}/") 286 prefix = input(f"{provider.capitalize()} prefix [{current_prefix}]: ").strip() 287 if not prefix: 288 prefix = current_prefix 289 model_prefixes[provider] = prefix 290 291 # Build final config 292 new_proxy_config = { 293 "enabled": True, 294 "base_url": base_url, 295 "model_prefixes": model_prefixes, 296 } 297 298 new_config = current_config.copy() 299 new_config["lightllm_proxy"] = new_proxy_config 300 301 # Show summary 302 print("\n📋 Configuration Summary:") 303 print(" Status: ENABLED") 304 print(f" Base URL: {base_url}") 305 print(" Model prefixes:") 306 for provider, prefix in model_prefixes.items(): 307 print(f" {provider}: {prefix}") 308 309 # Confirm and save 310 while True: 311 confirm = input("\nSave this configuration? [y/n]: ").strip().lower() 312 if confirm in ["y", "yes"]: 313 write_llm_config(new_config) 314 print(f"✅ Configuration saved to {LLM_CONFIG_PATH}") 315 316 # Check for API key 317 from wuenlp_tools.keys import LIGHTLLM_API_KEY 318 319 try: 320 # This will prompt for the key if not set 321 key_check = str(LIGHTLLM_API_KEY) 322 if key_check: 323 print("✅ LightLLM API key is configured") 324 else: 325 print("⚠️ No LightLLM API key set (may not be required)") 326 except Exception: 327 print("⚠️ No LightLLM API key set (may not be required)") 328 329 print("\n🎉 LightLLM proxy configuration complete!") 330 print(f" All LLM requests will now be routed through: {base_url}") 331 break 332 elif confirm in ["n", "no"]: 333 print("❌ Configuration cancelled") 334 break 335 else: 336 print("Please enter 'y' to save or 'n' to cancel") 337 338 339def show_lightllm_status(): 340 """Display current LightLLM proxy configuration status.""" 341 config = get_lightllm_config() 342 343 print("🔍 LightLLM Proxy Status") 344 print("=" * 25) 345 346 if config.enabled: 347 print("✅ Status: ENABLED") 348 print(f"🌐 Base URL: {config.base_url}") 349 print("🏷️ Model prefixes:") 350 if config.model_prefixes: 351 for provider, prefix in config.model_prefixes.items(): 352 print(f" {provider}: {prefix}") 353 354 # Check API key 355 from wuenlp_tools.keys import LIGHTLLM_API_KEY 356 357 try: 358 key_check = str(LIGHTLLM_API_KEY) 359 key_status = "SET" if key_check else "NOT SET" 360 except Exception: 361 key_status = "NOT SET" 362 print(f"🔑 API Key: {key_status}") 363 364 else: 365 print("❌ Status: DISABLED") 366 print("💡 Run configure_lightllm_proxy() to enable") 367 368 369from wuenlp_tools.ssc_config import ( # noqa: E402 370 get_ssc_repo_root, 371 get_ssc_scene_model_path, 372 get_ssc_suspense_model_root, 373 read_ssc_config, 374 write_ssc_config, 375)
35class APIConfig: 36 """Global configuration for WueNLP API endpoints.""" 37 38 _instance: Optional["APIConfig"] = None 39 40 def __init__(self): 41 self._base_url: str = os.getenv( 42 "WUENLP_API_BASE_URL", "https://wuenlp-api.professor-x.de" 43 ) 44 45 @classmethod 46 def get_instance(cls) -> "APIConfig": 47 """Get singleton instance of APIConfig.""" 48 if cls._instance is None: 49 cls._instance = cls() 50 return cls._instance 51 52 @property 53 def base_url(self) -> str: 54 """Get the base URL for WueNLP API.""" 55 return self._base_url 56 57 @base_url.setter 58 def base_url(self, value: str) -> None: 59 """Set the base URL for WueNLP API.""" 60 if value.endswith("/"): 61 value = value.rstrip("/") 62 self._base_url = value 63 64 def get_endpoint_url(self, endpoint: str) -> str: 65 """Get full URL for a specific endpoint.""" 66 if not endpoint.startswith("/"): 67 endpoint = "/" + endpoint 68 if not endpoint.endswith("/"): 69 endpoint = endpoint + "/" 70 return f"{self._base_url}{endpoint}"
Global configuration for WueNLP API endpoints.
45 @classmethod 46 def get_instance(cls) -> "APIConfig": 47 """Get singleton instance of APIConfig.""" 48 if cls._instance is None: 49 cls._instance = cls() 50 return cls._instance
Get singleton instance of APIConfig.
52 @property 53 def base_url(self) -> str: 54 """Get the base URL for WueNLP API.""" 55 return self._base_url
Get the base URL for WueNLP API.
64 def get_endpoint_url(self, endpoint: str) -> str: 65 """Get full URL for a specific endpoint.""" 66 if not endpoint.startswith("/"): 67 endpoint = "/" + endpoint 68 if not endpoint.endswith("/"): 69 endpoint = endpoint + "/" 70 return f"{self._base_url}{endpoint}"
Get full URL for a specific endpoint.
Global configuration for WueNLP API endpoints.
77def set_api_base_url(url: str) -> None: 78 """Set the global API base URL.""" 79 api_config.base_url = url
Set the global API base URL.
82def get_api_base_url() -> str: 83 """Get the global API base URL.""" 84 return api_config.base_url
Get the global API base URL.
87def get_endpoint_url(endpoint: str) -> str: 88 """Get full URL for a specific endpoint.""" 89 return api_config.get_endpoint_url(endpoint)
Get full URL for a specific endpoint.
102@dataclass 103class LightLLMProxyConfig: 104 enabled: bool = False 105 base_url: str = "http://localhost:8080/v1/" 106 model_prefixes: Optional[Dict[str, str]] = None 107 108 def __post_init__(self): 109 if self.model_prefixes is None: 110 self.model_prefixes = { 111 "openai": "openai/", 112 "openrouter": "openrouter/", 113 "ollama": "ollama/", 114 "local": "ollama/", 115 }
bool(x) -> bool
Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
118@dataclass 119class LocalLLMConfig: 120 base_url: Optional[str] = None 121 port: Union[str, int] = "auto" 122 auto_pull: bool = True 123 manage_process: Union[str, bool] = "auto" 124 pull_lock_dir: Optional[str] = None
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
bool(x) -> bool
Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
Path subclass for non-Windows systems.
On a POSIX system, instantiating a Path should return this object.
127def read_llm_config() -> dict: 128 """Read LLM config from JSON file. Returns empty dict if file doesn't exist.""" 129 if os.path.isfile(LLM_CONFIG_PATH): 130 try: 131 with open(LLM_CONFIG_PATH, "r") as f: 132 return json.load(f) 133 except json.JSONDecodeError: 134 return {} 135 return {}
Read LLM config from JSON file. Returns empty dict if file doesn't exist.
138def write_llm_config(config: dict): 139 """Write the LLM config dictionary to the JSON file.""" 140 os.makedirs(os.path.dirname(LLM_CONFIG_PATH), exist_ok=True) 141 with open(LLM_CONFIG_PATH, "w") as f: 142 json.dump(config, f, indent=2)
Write the LLM config dictionary to the JSON file.
145def get_lightllm_config() -> LightLLMProxyConfig: 146 """Get LightLLM proxy configuration.""" 147 config = read_llm_config() 148 proxy_config = config.get("lightllm_proxy", {}) 149 return LightLLMProxyConfig(**proxy_config)
Get LightLLM proxy configuration.
152def get_local_llm_config() -> LocalLLMConfig: 153 """Get local Ollama configuration.""" 154 config = read_llm_config() 155 local_config = config.get("local_llm", {}) 156 return LocalLLMConfig(**local_config)
Get local Ollama configuration.
159def get_pull_lock_dir() -> Path: 160 """Directory for cross-job Ollama pull locks.""" 161 env_dir = os.environ.get("WUENLP_PULL_LOCK_DIR") 162 if env_dir: 163 return Path(os.path.expanduser(env_dir)) 164 165 cfg = get_local_llm_config() 166 if cfg.pull_lock_dir: 167 return Path(os.path.expanduser(cfg.pull_lock_dir)) 168 169 ollama_models = os.environ.get("OLLAMA_MODELS") 170 if ollama_models and _is_slurm(): 171 return Path(os.path.expanduser(ollama_models)) / ".wuenlp-pull-locks" 172 173 return DEFAULT_PULL_LOCK_DIR
Directory for cross-job Ollama pull locks.
199def get_litellm_metadata() -> Dict[str, Any]: 200 """ 201 Get merged LiteLLM metadata configuration. 202 203 Order of precedence (later overrides earlier): 204 1. System-level config at LLM_CONFIG_PATH, key "litellm_metadata" 205 2. Project-level config in the current working directory 206 (".wuenlp_llm_config.json" or "wuenlp_llm_config.json"), 207 key "litellm_metadata" 208 209 This is intended for generic tags/metadata that should end up in LiteLLM logs. 210 """ 211 system_config = read_llm_config() 212 system_meta = system_config.get("litellm_metadata", {}) or {} 213 214 project_config = _read_project_llm_config() 215 project_meta = project_config.get("litellm_metadata", {}) or {} 216 217 merged: Dict[str, Any] = {} 218 merged.update(system_meta) 219 merged.update(project_meta) 220 return merged
Get merged LiteLLM metadata configuration.
Order of precedence (later overrides earlier):
- System-level config at LLM_CONFIG_PATH, key "litellm_metadata"
- Project-level config in the current working directory (".wuenlp_llm_config.json" or "wuenlp_llm_config.json"), key "litellm_metadata"
This is intended for generic tags/metadata that should end up in LiteLLM logs.
223def configure_lightllm_proxy(): 224 """Interactive configuration helper for LightLLM proxy settings.""" 225 print("🔧 LightLLM Proxy Configuration") 226 print("=" * 40) 227 228 # Load current config 229 current_config = read_llm_config() 230 current_proxy = current_config.get("lightllm_proxy", {}) 231 232 print( 233 f"\nCurrent status: {'ENABLED' if current_proxy.get('enabled', False) else 'DISABLED'}" 234 ) 235 if current_proxy.get("base_url"): 236 print(f"Current proxy URL: {current_proxy['base_url']}") 237 238 # Ask if user wants to enable/disable 239 while True: 240 enable_input = input("\nEnable LightLLM proxy? [y/n]: ").strip().lower() 241 if enable_input in ["y", "yes", "true", "1"]: 242 enabled = True 243 break 244 elif enable_input in ["n", "no", "false", "0"]: 245 enabled = False 246 break 247 else: 248 print("Please enter 'y' for yes or 'n' for no") 249 250 if not enabled: 251 # Disable proxy 252 new_config = current_config.copy() 253 new_config["lightllm_proxy"] = {"enabled": False} 254 write_llm_config(new_config) 255 print("✅ LightLLM proxy disabled") 256 return 257 258 # Configure proxy settings 259 print("\n📝 Configuring proxy settings...") 260 261 # Base URL 262 current_url = current_proxy.get("base_url", "http://localhost:8080/v1/") 263 base_url = input(f"Proxy base URL [{current_url}]: ").strip() 264 if not base_url: 265 base_url = current_url 266 267 # Ensure URL ends with /v1/ 268 if not base_url.endswith("/"): 269 base_url += "/" 270 if not base_url.endswith("v1/"): 271 if base_url.endswith("/"): 272 base_url += "v1/" 273 else: 274 base_url += "/v1/" 275 276 # Model prefixes 277 current_prefixes = current_proxy.get( 278 "model_prefixes", 279 {"openai": "openai/", "openrouter": "openrouter/", "ollama": "ollama/"}, 280 ) 281 282 print("\n🏷️ Configure model prefixes (press Enter to keep current):") 283 284 model_prefixes = {} 285 for provider in ["openai", "openrouter", "ollama"]: 286 current_prefix = current_prefixes.get(provider, f"{provider}/") 287 prefix = input(f"{provider.capitalize()} prefix [{current_prefix}]: ").strip() 288 if not prefix: 289 prefix = current_prefix 290 model_prefixes[provider] = prefix 291 292 # Build final config 293 new_proxy_config = { 294 "enabled": True, 295 "base_url": base_url, 296 "model_prefixes": model_prefixes, 297 } 298 299 new_config = current_config.copy() 300 new_config["lightllm_proxy"] = new_proxy_config 301 302 # Show summary 303 print("\n📋 Configuration Summary:") 304 print(" Status: ENABLED") 305 print(f" Base URL: {base_url}") 306 print(" Model prefixes:") 307 for provider, prefix in model_prefixes.items(): 308 print(f" {provider}: {prefix}") 309 310 # Confirm and save 311 while True: 312 confirm = input("\nSave this configuration? [y/n]: ").strip().lower() 313 if confirm in ["y", "yes"]: 314 write_llm_config(new_config) 315 print(f"✅ Configuration saved to {LLM_CONFIG_PATH}") 316 317 # Check for API key 318 from wuenlp_tools.keys import LIGHTLLM_API_KEY 319 320 try: 321 # This will prompt for the key if not set 322 key_check = str(LIGHTLLM_API_KEY) 323 if key_check: 324 print("✅ LightLLM API key is configured") 325 else: 326 print("⚠️ No LightLLM API key set (may not be required)") 327 except Exception: 328 print("⚠️ No LightLLM API key set (may not be required)") 329 330 print("\n🎉 LightLLM proxy configuration complete!") 331 print(f" All LLM requests will now be routed through: {base_url}") 332 break 333 elif confirm in ["n", "no"]: 334 print("❌ Configuration cancelled") 335 break 336 else: 337 print("Please enter 'y' to save or 'n' to cancel")
Interactive configuration helper for LightLLM proxy settings.
340def show_lightllm_status(): 341 """Display current LightLLM proxy configuration status.""" 342 config = get_lightllm_config() 343 344 print("🔍 LightLLM Proxy Status") 345 print("=" * 25) 346 347 if config.enabled: 348 print("✅ Status: ENABLED") 349 print(f"🌐 Base URL: {config.base_url}") 350 print("🏷️ Model prefixes:") 351 if config.model_prefixes: 352 for provider, prefix in config.model_prefixes.items(): 353 print(f" {provider}: {prefix}") 354 355 # Check API key 356 from wuenlp_tools.keys import LIGHTLLM_API_KEY 357 358 try: 359 key_check = str(LIGHTLLM_API_KEY) 360 key_status = "SET" if key_check else "NOT SET" 361 except Exception: 362 key_status = "NOT SET" 363 print(f"🔑 API Key: {key_status}") 364 365 else: 366 print("❌ Status: DISABLED") 367 print("💡 Run configure_lightllm_proxy() to enable")
Display current LightLLM proxy configuration status.
38def read_ssc_config() -> dict[str, Any]: 39 """Read merged SSC config: user file, then project file in cwd (project wins).""" 40 merged: dict[str, Any] = {} 41 if os.path.isfile(SSC_CONFIG_PATH): 42 try: 43 with open(SSC_CONFIG_PATH, encoding="utf-8") as f: 44 merged.update(json.load(f)) 45 except json.JSONDecodeError: 46 pass 47 for filename in PROJECT_SSC_CONFIG_CANDIDATES: 48 candidate = os.path.join(os.getcwd(), filename) 49 if os.path.isfile(candidate): 50 try: 51 with open(candidate, encoding="utf-8") as f: 52 merged.update(json.load(f)) 53 except json.JSONDecodeError: 54 pass 55 break 56 return merged
Read merged SSC config: user file, then project file in cwd (project wins).