@@ -27,6 +27,7 @@ class KeyStatus(Enum):
2727class APIKeyInfo :
2828 """Information about an API key"""
2929 key : str
30+ name : Optional [str ] = None # Optional friendly name for the key
3031 status : KeyStatus = KeyStatus .ACTIVE
3132 last_used : Optional [datetime ] = None
3233 last_error : Optional [str ] = None
@@ -41,6 +42,11 @@ def masked_key(self) -> str:
4142 return f"{ self .key [:4 ]} ...{ self .key [- 4 :]} "
4243 return "****"
4344
45+ @property
46+ def display_name (self ) -> str :
47+ """Return display name (custom name or masked key)"""
48+ return self .name if self .name else self .masked_key
49+
4450 def is_available (self ) -> bool :
4551 """Check if key is currently available for use"""
4652 if self .status == KeyStatus .INVALID :
@@ -99,26 +105,34 @@ def __init__(self, provider: str):
99105 self ._cooldown_duration = timedelta (seconds = 60 ) # 1 minute cooldown
100106 self ._rate_limit_cooldown = timedelta (minutes = 5 ) # 5 minutes for rate limits
101107
102- def add_key (self , key : str ) -> bool :
103- """Add a new API key to the pool"""
108+ def add_key (self , key : str , name : Optional [ str ] = None ) -> bool :
109+ """Add a new API key to the pool with optional name """
104110 with self ._lock :
105111 # Check if key already exists
106112 for existing in self .keys :
107- if existing .key == key :
108- return False
113+ if existing .key == key : return False
109114
110- self .keys .append (APIKeyInfo (key = key ))
111- log_debug (f"Added new API key to { self .provider } pool (total: { len (self .keys )} )" )
115+ self .keys .append (APIKeyInfo (key = key , name = name ))
116+ display = name if name else f"{ key [:4 ]} ...{ key [- 4 :]} " if len (key ) > 12 else "****"
117+ log_debug (f"Added new API key '{ display } ' to { self .provider } pool (total: { len (self .keys )} )" )
112118 return True
113119
120+ def update_key_name (self , key : str , name : Optional [str ]) -> bool :
121+ """Update the name of an existing API key"""
122+ with self ._lock :
123+ for key_info in self .keys :
124+ if key_info .key == key or key_info .masked_key == key :
125+ key_info .name = name
126+ return True
127+ return False
128+
114129 def remove_key (self , key : str ) -> bool :
115130 """Remove an API key from the pool"""
116131 with self ._lock :
117132 for i , key_info in enumerate (self .keys ):
118133 if key_info .key == key or key_info .masked_key == key :
119134 self .keys .pop (i )
120- if self ._current_index >= len (self .keys ):
121- self ._current_index = 0
135+ if self ._current_index >= len (self .keys ): self ._current_index = 0
122136 log_debug (f"Removed API key from { self .provider } pool (remaining: { len (self .keys )} )" )
123137 return True
124138 return False
@@ -221,6 +235,8 @@ def get_all_keys_info(self) -> List[Dict]:
221235 return [
222236 {
223237 "masked_key" : k .masked_key ,
238+ "name" : k .name ,
239+ "display_name" : k .display_name ,
224240 "status" : k .status .value ,
225241 "requests" : k .requests_count ,
226242 "errors" : k .error_count ,
@@ -269,6 +285,7 @@ def __init__(self):
269285 "anthropic" : "ANTHROPIC_API_KEY" ,
270286 "openai" : "OPENAI_API_KEY" ,
271287 "google" : "GOOGLE_API_KEY" ,
288+ "cerebras" : "CEREBRAS_API_KEY" ,
272289 }
273290 self ._provider_lock = threading .Lock ()
274291 self ._initialized = True
@@ -277,28 +294,33 @@ def __init__(self):
277294 self ._load_from_storage ()
278295
279296 def _load_from_storage (self ):
280- """Load API keys from storage"""
297+ """Load API keys from storage (filters out placeholders) """
281298 try :
282299 from .storage import user_config
283300
284301 for provider in self ._env_key_map .keys ():
285- pool = self ._get_or_create_pool (provider )
286-
287302 # Load multi-keys if available
288303 multi_keys = user_config .get_api_keys_list (provider )
289304 if multi_keys :
290- for key in multi_keys :
291- pool .add_key (key )
305+ for key_data in multi_keys :
306+ # Support both old format (string) and new format (dict with key/name)
307+ if isinstance (key_data , dict ):
308+ key = key_data .get ("key" , "" )
309+ name = key_data .get ("name" )
310+ self .add_key (provider , key , name )
311+ else :
312+ # Legacy format: just a string
313+ self .add_key (provider , key_data )
292314 else :
293315 # Fallback to single key (backward compatibility)
294316 single_key = user_config .get_api_key (provider )
295317 if single_key :
296- pool .add_key (single_key )
318+ self .add_key (provider , single_key )
297319
298320 # Also check environment variable
299321 env_key = os .environ .get (self ._env_key_map [provider ])
300322 if env_key :
301- pool .add_key (env_key )
323+ self .add_key (provider , env_key )
302324
303325 except Exception as e :
304326 log_error ("Failed to load API keys from storage" , e )
@@ -311,11 +333,16 @@ def _get_or_create_pool(self, provider: str) -> ProviderKeyPool:
311333 self ._pools [provider ] = ProviderKeyPool (provider )
312334 return self ._pools [provider ]
313335
314- def add_key (self , provider : str , key : str ) -> bool :
315- """Add an API key for a provider"""
336+ def add_key (self , provider : str , key : str , name : Optional [str ] = None ) -> bool :
337+ """Add an API key for a provider with optional name (rejects placeholders)"""
338+ # Reject placeholder keys
339+ if self ._is_placeholder_key (key ):
340+ log_debug (f"Rejected placeholder key for { provider } " )
341+ return False
342+
316343 provider = provider .lower ()
317344 pool = self ._get_or_create_pool (provider )
318- success = pool .add_key (key )
345+ success = pool .add_key (key , name )
319346
320347 if success :
321348 # Also update environment for immediate use
@@ -328,6 +355,18 @@ def add_key(self, provider: str, key: str) -> bool:
328355
329356 return success
330357
358+ def update_key_name (self , provider : str , key : str , name : Optional [str ]) -> bool :
359+ """Update the name of an existing API key"""
360+ provider = provider .lower ()
361+ pool = self ._pools .get (provider )
362+ if not pool :
363+ return False
364+
365+ success = pool .update_key_name (key , name )
366+ if success :
367+ self ._save_to_storage (provider )
368+ return success
369+
331370 def remove_key (self , provider : str , key : str ) -> bool :
332371 """Remove an API key for a provider"""
333372 provider = provider .lower ()
@@ -341,26 +380,35 @@ def remove_key(self, provider: str, key: str) -> bool:
341380 return success
342381
343382 def _save_to_storage (self , provider : str ):
344- """Save keys to storage"""
383+ """Save keys to storage with optional names """
345384 try :
346385 from .storage import user_config
347386
348387 pool = self ._pools .get (provider )
349388 if pool :
350- keys = [k .key for k in pool .keys ]
351- user_config .set_api_keys_list (provider , keys )
389+ # Save as list of objects with key and optional name
390+ keys_data = []
391+ for k in pool .keys :
392+ if k .name :
393+ keys_data .append ({"key" : k .key , "name" : k .name })
394+ else :
395+ keys_data .append (k .key ) # Simple string for backward compatibility
396+ user_config .set_api_keys_list (provider , keys_data )
352397 except Exception as e :
353398 log_error ("Failed to save API keys to storage" , e )
354399
355400 def get_key (self , provider : str ) -> Optional [str ]:
356- """Get the current active API key for a provider"""
401+ """Get the current active API key for a provider (excludes placeholders) """
357402 provider = provider .lower ()
358403 pool = self ._pools .get (provider )
359404 if not pool :
360405 # Try to get from environment as fallback
361406 env_var = self ._env_key_map .get (provider )
362407 if env_var :
363- return os .environ .get (env_var )
408+ key = os .environ .get (env_var )
409+ # Don't return placeholder keys
410+ if key and not self ._is_placeholder_key (key ):
411+ return key
364412 return None
365413
366414 key = pool .get_current_key ()
@@ -441,15 +489,40 @@ def get_all_providers_info(self) -> List[Dict]:
441489 info .append (self .get_provider_info (provider ))
442490 return info
443491
492+ def _is_placeholder_key (self , key : str ) -> bool :
493+ """Check if a key appears to be a placeholder/example value"""
494+ if not key :
495+ return True
496+ key_lower = key .lower ()
497+ placeholder_patterns = [
498+ "your_" ,
499+ "_here" ,
500+ "example" ,
501+ "placeholder" ,
502+ "xxx" ,
503+ "insert" ,
504+ "paste" ,
505+ "api_key" ,
506+ "apikey" ,
507+ "enter_" ,
508+ "put_" ,
509+ "add_" ,
510+ "<" ,
511+ ">" ,
512+ ]
513+ return any (pattern in key_lower for pattern in placeholder_patterns )
514+
444515 def has_available_key (self , provider : str ) -> bool :
445- """Check if provider has any available keys"""
516+ """Check if provider has any available keys (excluding placeholders) """
446517 provider = provider .lower ()
447518 pool = self ._pools .get (provider )
448519 if not pool :
449520 # Check environment as fallback
450521 env_var = self ._env_key_map .get (provider )
451522 if env_var :
452- return bool (os .environ .get (env_var ))
523+ key = os .environ .get (env_var , "" )
524+ # Make sure it's not a placeholder value
525+ return bool (key ) and not self ._is_placeholder_key (key )
453526 return False
454527 return pool .has_available_keys ()
455528
0 commit comments