@@ -145,8 +145,8 @@ def _init_persona_classes(self) -> None:
145
145
+ "Please verify your server configuration and open a new issue on our GitHub repo if this warning persists."
146
146
)
147
147
148
- # TODO: check whether in the end we need a full class for this, or if we can just use a simple function that returns a list of persona classes from the local filesystem.
149
- persona_classes .extend (LocalPersonaLoader (self .root_dir ). load_persona_classes ( ))
148
+ # Load persona classes from local filesystem
149
+ persona_classes .extend (load_persona_classes_from_directory (self .root_dir ))
150
150
151
151
PersonaManager ._persona_classes = persona_classes
152
152
@@ -297,114 +297,66 @@ def get_mcp_config(self) -> dict[str, Any]:
297
297
return self ._mcp_config_loader .get_config (jdir )
298
298
299
299
300
- class LocalPersonaLoader ( LoggingConfigurable ) :
300
+ def load_persona_classes_from_directory ( root_dir : str ) -> list [ type [ BasePersona ]] :
301
301
"""
302
- Load _persona class declarations_ from the local filesystem.
302
+ Load _persona class declarations_ from Python files in the local filesystem.
303
303
304
- Those class declarations are then used to instantiate personas by the `PersonaManager`.
304
+ Those class declarations are then used to instantiate personas by the
305
+ `PersonaManager`.
305
306
306
- TODO: wire to local .jupyter directory system, for now just use the current
307
- directory.
308
- """
309
-
310
- def __init__ (
311
- self ,
312
- * args ,
313
- root_dir : str | None = None ,
314
- ** kwargs ,
315
- ):
316
- # Forward other arguments to parent class
317
- super ().__init__ (* args , ** kwargs )
307
+ Scans the root_dir for .py files containing "persona" in their name,
308
+ dynamically imports them, and extracts any class declarations that are
309
+ subclasses of BasePersona.
318
310
319
- # Set default root directory to current working directory if not provided
320
- self . root_dir = root_dir or os . getcwd ()
311
+ Args:
312
+ root_dir: Directory to scan for persona Python files
321
313
322
- self .log .info (
323
- f"LocalPersonaLoader initialized with root directory: { self .root_dir } "
324
- )
325
-
326
- def load_persona_classes (self ) -> list [type [BasePersona ]]:
327
- """
328
- Loads persona classes from Python files in the local filesystem.
329
-
330
- Scans the root_dir for .py files, dynamically imports them, and extracts
331
- any class declarations that are subclasses of BasePersona.
332
- """
333
- persona_classes : list [type [BasePersona ]] = []
334
-
335
- # Check if root directory exists
336
- if not os .path .exists (self .root_dir ):
337
- self .log .info (f"Root directory does not exist: { self .root_dir } " )
338
- return persona_classes
314
+ Returns:
315
+ List of BasePersona subclasses found in the directory
316
+ """
317
+ persona_classes : list [type [BasePersona ]] = []
339
318
340
- # Find all .py files in the root directory that contain "persona" in the name
341
- all_py_files = glob ( os .path .join ( self . root_dir , "*.py" ))
342
- py_files = [ f for f in all_py_files if "persona" in Path ( f ). stem . lower ()]
319
+ # Check if root directory exists
320
+ if not os .path .exists ( root_dir ):
321
+ return persona_classes
343
322
344
- if not py_files :
345
- self .log .info (
346
- f"No Python files with 'persona' in the name found in directory: { self .root_dir } "
347
- )
348
- return persona_classes
323
+ # Find all .py files in the root directory that contain "persona" in the name
324
+ all_py_files = glob (os .path .join (root_dir , "*.py" ))
325
+ py_files = [f for f in all_py_files if "persona" in Path (f ).stem .lower ()]
349
326
350
- self .log .info (
351
- f"Found { len (py_files )} Python files with 'persona' in the name in { self .root_dir } "
352
- )
353
- self .log .info ("PENDING: Loading persona classes from local Python files..." )
354
- start_time_ns = time_ns ()
327
+ if not py_files :
328
+ return persona_classes
355
329
356
- for py_file in py_files :
357
- try :
358
- # Get module name from file path
359
- module_name = Path (py_file ).stem
360
-
361
- # Skip if module name starts with underscore (private modules)
362
- if module_name .startswith ("_" ):
363
- continue
364
-
365
- # Create module spec and load the module
366
- spec = importlib .util .spec_from_file_location (module_name , py_file )
367
- if spec is None or spec .loader is None :
368
- self .log .warning (f" - Unable to create module spec for { py_file } " )
369
- continue
370
-
371
- module = importlib .util .module_from_spec (spec )
372
- spec .loader .exec_module (module )
373
-
374
- # Find all classes in the module that are BasePersona subclasses
375
- module_persona_classes = []
376
- for name , obj in inspect .getmembers (module , inspect .isclass ):
377
- # Check if it's a subclass of BasePersona but not BasePersona itself
378
- if (
379
- issubclass (obj , BasePersona )
380
- and obj is not BasePersona
381
- and obj .__module__ == module_name
382
- ):
383
- module_persona_classes .append (obj )
384
-
385
- if module_persona_classes :
386
- persona_classes .extend (module_persona_classes )
387
- class_names = [cls .__name__ for cls in module_persona_classes ]
388
- self .log .info (
389
- f" - Loaded { len (module_persona_classes )} persona class(es) from '{ py_file } ': { class_names } "
390
- )
391
- else :
392
- self .log .debug (f" - No persona classes found in '{ py_file } '" )
330
+ for py_file in py_files :
331
+ try :
332
+ # Get module name from file path
333
+ module_name = Path (py_file ).stem
393
334
394
- except Exception :
395
- # On exception, log an error and continue
396
- # This mirrors the error handling pattern from entry point loading
397
- self .log .exception (
398
- f" - Unable to load persona classes from '{ py_file } ' due to an exception printed below."
399
- )
335
+ # Skip if module name starts with underscore (private modules)
336
+ if module_name .startswith ("_" ):
400
337
continue
401
338
402
- if len (persona_classes ) > 0 :
403
- elapsed_time_ms = (time_ns () - start_time_ns ) // 1_000_000
404
- self .log .info (
405
- f"SUCCESS: Loaded { len (persona_classes )} persona classes from local filesystem. Time elapsed: { elapsed_time_ms } ms."
406
- )
407
- else :
408
- self .log .info ("No persona classes found in local filesystem." )
339
+ # Create module spec and load the module
340
+ spec = importlib .util .spec_from_file_location (module_name , py_file )
341
+ if spec is None or spec .loader is None :
342
+ continue
409
343
410
- return persona_classes
344
+ module = importlib .util .module_from_spec (spec )
345
+ spec .loader .exec_module (module )
346
+
347
+ # Find all classes in the module that are BasePersona subclasses
348
+ for name , obj in inspect .getmembers (module , inspect .isclass ):
349
+ # Check if it's a subclass of BasePersona but not BasePersona itself
350
+ if (
351
+ issubclass (obj , BasePersona )
352
+ and obj is not BasePersona
353
+ and obj .__module__ == module_name
354
+ ):
355
+ persona_classes .append (obj )
356
+
357
+ except Exception :
358
+ # On exception, continue to next file
359
+ # This mirrors the error handling pattern from entry point loading
360
+ continue
361
+
362
+ return persona_classes
0 commit comments