mdb-engine 0.1.6__py3-none-any.whl → 0.2.0__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.
Files changed (87) hide show
  1. mdb_engine/__init__.py +104 -11
  2. mdb_engine/auth/ARCHITECTURE.md +112 -0
  3. mdb_engine/auth/README.md +648 -11
  4. mdb_engine/auth/__init__.py +136 -29
  5. mdb_engine/auth/audit.py +592 -0
  6. mdb_engine/auth/base.py +252 -0
  7. mdb_engine/auth/casbin_factory.py +264 -69
  8. mdb_engine/auth/config_helpers.py +7 -6
  9. mdb_engine/auth/cookie_utils.py +3 -7
  10. mdb_engine/auth/csrf.py +373 -0
  11. mdb_engine/auth/decorators.py +3 -10
  12. mdb_engine/auth/dependencies.py +47 -50
  13. mdb_engine/auth/helpers.py +3 -3
  14. mdb_engine/auth/integration.py +53 -80
  15. mdb_engine/auth/jwt.py +2 -6
  16. mdb_engine/auth/middleware.py +77 -34
  17. mdb_engine/auth/oso_factory.py +18 -38
  18. mdb_engine/auth/provider.py +270 -171
  19. mdb_engine/auth/rate_limiter.py +504 -0
  20. mdb_engine/auth/restrictions.py +8 -24
  21. mdb_engine/auth/session_manager.py +14 -29
  22. mdb_engine/auth/shared_middleware.py +600 -0
  23. mdb_engine/auth/shared_users.py +759 -0
  24. mdb_engine/auth/token_store.py +14 -28
  25. mdb_engine/auth/users.py +54 -113
  26. mdb_engine/auth/utils.py +213 -15
  27. mdb_engine/cli/commands/generate.py +545 -9
  28. mdb_engine/cli/commands/validate.py +3 -7
  29. mdb_engine/cli/utils.py +3 -3
  30. mdb_engine/config.py +7 -21
  31. mdb_engine/constants.py +65 -0
  32. mdb_engine/core/README.md +117 -6
  33. mdb_engine/core/__init__.py +39 -7
  34. mdb_engine/core/app_registration.py +22 -41
  35. mdb_engine/core/app_secrets.py +290 -0
  36. mdb_engine/core/connection.py +18 -9
  37. mdb_engine/core/encryption.py +223 -0
  38. mdb_engine/core/engine.py +1057 -93
  39. mdb_engine/core/index_management.py +12 -16
  40. mdb_engine/core/manifest.py +459 -150
  41. mdb_engine/core/ray_integration.py +435 -0
  42. mdb_engine/core/seeding.py +10 -18
  43. mdb_engine/core/service_initialization.py +12 -23
  44. mdb_engine/core/types.py +2 -5
  45. mdb_engine/database/README.md +140 -17
  46. mdb_engine/database/__init__.py +17 -6
  47. mdb_engine/database/abstraction.py +25 -37
  48. mdb_engine/database/connection.py +11 -18
  49. mdb_engine/database/query_validator.py +367 -0
  50. mdb_engine/database/resource_limiter.py +204 -0
  51. mdb_engine/database/scoped_wrapper.py +713 -196
  52. mdb_engine/dependencies.py +426 -0
  53. mdb_engine/di/__init__.py +34 -0
  54. mdb_engine/di/container.py +248 -0
  55. mdb_engine/di/providers.py +205 -0
  56. mdb_engine/di/scopes.py +139 -0
  57. mdb_engine/embeddings/README.md +54 -24
  58. mdb_engine/embeddings/__init__.py +31 -24
  59. mdb_engine/embeddings/dependencies.py +37 -154
  60. mdb_engine/embeddings/service.py +11 -25
  61. mdb_engine/exceptions.py +92 -0
  62. mdb_engine/indexes/README.md +30 -13
  63. mdb_engine/indexes/__init__.py +1 -0
  64. mdb_engine/indexes/helpers.py +1 -1
  65. mdb_engine/indexes/manager.py +50 -114
  66. mdb_engine/memory/README.md +2 -2
  67. mdb_engine/memory/__init__.py +1 -2
  68. mdb_engine/memory/service.py +30 -87
  69. mdb_engine/observability/README.md +4 -2
  70. mdb_engine/observability/__init__.py +26 -9
  71. mdb_engine/observability/health.py +8 -9
  72. mdb_engine/observability/metrics.py +32 -12
  73. mdb_engine/repositories/__init__.py +34 -0
  74. mdb_engine/repositories/base.py +325 -0
  75. mdb_engine/repositories/mongo.py +233 -0
  76. mdb_engine/repositories/unit_of_work.py +166 -0
  77. mdb_engine/routing/README.md +1 -1
  78. mdb_engine/routing/__init__.py +1 -3
  79. mdb_engine/routing/websockets.py +25 -60
  80. mdb_engine-0.2.0.dist-info/METADATA +313 -0
  81. mdb_engine-0.2.0.dist-info/RECORD +96 -0
  82. mdb_engine-0.1.6.dist-info/METADATA +0 -213
  83. mdb_engine-0.1.6.dist-info/RECORD +0 -75
  84. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/WHEEL +0 -0
  85. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/entry_points.txt +0 -0
  86. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/licenses/LICENSE +0 -0
  87. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/top_level.txt +0 -0
mdb_engine/constants.py CHANGED
@@ -57,6 +57,25 @@ DEFAULT_MAX_IDLE_TIME_MS: Final[int] = 45000
57
57
  MAX_COLLECTION_NAME_LENGTH: Final[int] = 255
58
58
  """Maximum length for MongoDB collection names."""
59
59
 
60
+ MIN_COLLECTION_NAME_LENGTH: Final[int] = 1
61
+ """Minimum length for MongoDB collection names."""
62
+
63
+ # Reserved collection name prefixes (MongoDB system collections)
64
+ RESERVED_COLLECTION_PREFIXES: Final[tuple[str, ...]] = (
65
+ "system",
66
+ "admin",
67
+ "config",
68
+ "local",
69
+ )
70
+ """Reserved MongoDB collection name prefixes that cannot be used."""
71
+
72
+ # Reserved collection names (engine-internal)
73
+ RESERVED_COLLECTION_NAMES: Final[tuple[str, ...]] = (
74
+ "apps_config", # Engine internal - app registration
75
+ "_mdb_engine_app_secrets", # Engine internal - encrypted app secrets
76
+ )
77
+ """Reserved collection names that cannot be accessed through scoped wrappers."""
78
+
60
79
  # App slug constraints
61
80
  MAX_APP_SLUG_LENGTH: Final[int] = 100
62
81
  """Maximum length for app slugs."""
@@ -158,3 +177,49 @@ SUPPORTED_APP_STATUSES: Final[tuple[str, ...]] = (
158
177
  APP_STATUS_ARCHIVED,
159
178
  APP_STATUS_INACTIVE,
160
179
  )
180
+
181
+ # ============================================================================
182
+ # QUERY SECURITY & RESOURCE LIMITS
183
+ # ============================================================================
184
+
185
+ # Query execution limits
186
+ DEFAULT_MAX_TIME_MS: Final[int] = 30000
187
+ """Default query timeout in milliseconds (30 seconds)."""
188
+
189
+ MAX_QUERY_TIME_MS: Final[int] = 300000
190
+ """Maximum allowed query timeout in milliseconds (5 minutes)."""
191
+
192
+ MAX_QUERY_RESULT_SIZE: Final[int] = 10000
193
+ """Maximum number of documents that can be returned in a single query."""
194
+
195
+ MAX_CURSOR_BATCH_SIZE: Final[int] = 1000
196
+ """Maximum batch size for cursor operations."""
197
+
198
+ MAX_DOCUMENT_SIZE: Final[int] = 16 * 1024 * 1024
199
+ """Maximum document size in bytes (16MB MongoDB limit)."""
200
+
201
+ # Pipeline limits
202
+ MAX_PIPELINE_STAGES: Final[int] = 50
203
+ """Maximum number of stages allowed in an aggregation pipeline."""
204
+
205
+ MAX_SORT_FIELDS: Final[int] = 10
206
+ """Maximum number of fields that can be sorted in a single query."""
207
+
208
+ MAX_QUERY_DEPTH: Final[int] = 10
209
+ """Maximum nesting depth for query filters (prevents deeply nested queries)."""
210
+
211
+ # Regex limits (prevent ReDoS attacks)
212
+ MAX_REGEX_LENGTH: Final[int] = 1000
213
+ """Maximum length of regex patterns to prevent ReDoS attacks."""
214
+
215
+ MAX_REGEX_COMPLEXITY: Final[int] = 50
216
+ """Maximum complexity score for regex patterns (prevents ReDoS)."""
217
+
218
+ # Dangerous MongoDB operators that should be blocked
219
+ DANGEROUS_OPERATORS: Final[tuple[str, ...]] = (
220
+ "$where", # JavaScript execution (security risk)
221
+ "$eval", # JavaScript evaluation (deprecated, security risk)
222
+ "$function", # JavaScript functions (security risk)
223
+ "$accumulator", # Can be abused for code execution
224
+ )
225
+ """MongoDB operators that are blocked for security reasons."""
mdb_engine/core/README.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  The central orchestration engine for MDB_ENGINE that manages database connections, app registration, manifest validation, index management, and resource lifecycle.
4
4
 
5
+ ## When to Use What
6
+
7
+ ### Integration Patterns
8
+
9
+ | Pattern | Best For | Code |
10
+ |---------|----------|------|
11
+ | **`create_app()`** | New FastAPI apps | `app = engine.create_app(slug, manifest)` |
12
+ | **`lifespan()`** | Custom FastAPI setup | `FastAPI(lifespan=engine.lifespan(...))` |
13
+ | **Manual** | Existing apps, scripts | `await engine.initialize()` / `await engine.shutdown()` |
14
+ | **Context Manager** | Scripts, tests | `async with engine: ...` |
15
+
16
+ ### Feature Activation
17
+
18
+ | Feature | When to Enable | Configuration |
19
+ |---------|---------------|---------------|
20
+ | **Ray** | Distributed processing, isolated actors | `enable_ray=True` |
21
+ | **Multi-site** | Cross-app data sharing | `read_scopes` in manifest |
22
+ | **Auto-indexing** | Let engine optimize queries | Default ON (`auto_index=True`) |
23
+ | **App Tokens** | Production security | Set `MDB_ENGINE_MASTER_KEY` |
24
+
5
25
  ## Features
6
26
 
7
27
  - **MongoDBEngine**: Central orchestration for all engine components
@@ -54,12 +74,57 @@ engine = MongoDBEngine(
54
74
  manifests_dir=Path("./manifests"), # Optional
55
75
  authz_provider=authz_provider, # Optional
56
76
  max_pool_size=10, # Optional
57
- min_pool_size=1 # Optional
77
+ min_pool_size=1, # Optional
78
+ enable_ray=False, # Optional: Enable Ray support
79
+ ray_namespace="modular_labs" # Optional: Ray namespace
58
80
  )
59
81
 
60
82
  await engine.initialize()
61
83
  ```
62
84
 
85
+ ### FastAPI Integration
86
+
87
+ The simplest way to create a FastAPI app with automatic lifecycle management:
88
+
89
+ ```python
90
+ from mdb_engine import MongoDBEngine
91
+ from pathlib import Path
92
+
93
+ engine = MongoDBEngine(
94
+ mongo_uri="mongodb://localhost:27017",
95
+ db_name="my_database"
96
+ )
97
+
98
+ # Create FastAPI app with automatic initialization and cleanup
99
+ app = engine.create_app(
100
+ slug="my_app",
101
+ manifest=Path("manifest.json")
102
+ )
103
+
104
+ @app.get("/items")
105
+ async def get_items():
106
+ db = engine.get_scoped_db("my_app")
107
+ return await db.items.find({}).to_list(10)
108
+ ```
109
+
110
+ This automatically:
111
+ - Initializes the engine on startup
112
+ - Loads and registers the manifest
113
+ - Auto-detects multi-site mode from manifest
114
+ - Auto-retrieves app tokens
115
+ - Shuts down cleanly on app exit
116
+
117
+ ### Custom Lifespan
118
+
119
+ For more control, use the `lifespan()` helper:
120
+
121
+ ```python
122
+ from fastapi import FastAPI
123
+
124
+ engine = MongoDBEngine(...)
125
+ app = FastAPI(lifespan=engine.lifespan("my_app", Path("manifest.json")))
126
+ ```
127
+
63
128
  ### Get Scoped Database
64
129
 
65
130
  Get a database wrapper that automatically isolates data by app:
@@ -91,6 +156,33 @@ manifest = await engine.load_manifest("manifest.json")
91
156
  await engine.register_app(manifest)
92
157
  ```
93
158
 
159
+ ### Optional Ray Integration
160
+
161
+ Enable Ray for distributed processing (Ray must be installed):
162
+
163
+ ```python
164
+ from mdb_engine import MongoDBEngine
165
+
166
+ # Enable Ray support
167
+ engine = MongoDBEngine(
168
+ mongo_uri="mongodb://localhost:27017",
169
+ db_name="my_database",
170
+ enable_ray=True,
171
+ ray_namespace="my_namespace"
172
+ )
173
+
174
+ await engine.initialize()
175
+
176
+ # Check if Ray is available
177
+ if engine.has_ray:
178
+ print(f"Ray initialized in namespace: {engine.ray_namespace}")
179
+ ```
180
+
181
+ Ray integration features:
182
+ - Isolated app environments via Ray actors
183
+ - Automatic graceful degradation if Ray not installed
184
+ - App-specific namespaces for isolation
185
+
94
186
  ### Health Checks
95
187
 
96
188
  Check engine and MongoDB health:
@@ -98,10 +190,13 @@ Check engine and MongoDB health:
98
190
  ```python
99
191
  from mdb_engine.observability import check_engine_health, check_mongodb_health
100
192
 
101
- # Check MongoDB connection
193
+ # Check MongoDB connection (mongo_client is for observability only, not data access)
102
194
  mongodb_status = await check_mongodb_health(engine.mongo_client)
103
195
  print(mongodb_status)
104
196
 
197
+ # ⚠️ SECURITY: engine.mongo_client is for observability/admin operations only
198
+ # Always use engine.get_scoped_db() for all data access operations
199
+
105
200
  # Check engine health
106
201
  engine_status = await check_engine_health(engine)
107
202
  print(engine_status)
@@ -299,11 +394,23 @@ is_valid, error, paths = await validate_manifest_with_db(
299
394
  - `get_app_info(app_slug)` - Get information about registered app
300
395
  - `unregister_app(app_slug)` - Unregister an app
301
396
 
397
+ **FastAPI Integration Methods:**
398
+
399
+ - `create_app(slug, manifest, title=None, **fastapi_kwargs)` - Create a FastAPI app with automatic lifecycle management
400
+ - `lifespan(slug, manifest)` - Create a lifespan context manager for FastAPI
401
+
402
+ **App Token Methods:**
403
+
404
+ - `auto_retrieve_app_token(slug)` - Auto-retrieve app token from environment or database
405
+ - `get_app_token(slug)` - Get cached app token for a slug
406
+
302
407
  #### Properties
303
408
 
304
- - `mongo_client` - MongoDB client instance
305
- - `mongo_db` - MongoDB database instance
409
+ - `mongo_client` - MongoDB client instance (for observability only)
306
410
  - `initialized` - Whether engine is initialized
411
+ - `has_ray` - Whether Ray is enabled and initialized
412
+ - `enable_ray` - Whether Ray support is enabled
413
+ - `ray_namespace` - Ray namespace for actor isolation
307
414
 
308
415
  ### ManifestValidator
309
416
 
@@ -354,6 +461,8 @@ export MONGO_SERVER_SELECTION_TIMEOUT_MS=5000
354
461
  - `authz_provider` (optional): Authorization provider instance
355
462
  - `max_pool_size` (optional): Maximum connection pool size (default: 10)
356
463
  - `min_pool_size` (optional): Minimum connection pool size (default: 1)
464
+ - `enable_ray` (optional): Enable Ray support for distributed processing (default: False)
465
+ - `ray_namespace` (optional): Ray namespace for actor isolation (default: "modular_labs")
357
466
 
358
467
  ## Manifest Schema Features
359
468
 
@@ -480,13 +589,15 @@ Define various index types:
480
589
  ```python
481
590
  from mdb_engine.exceptions import InitializationError
482
591
 
592
+ from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
593
+
483
594
  try:
484
595
  await engine.initialize()
485
596
  except InitializationError as e:
486
597
  print(f"Initialization failed: {e}")
487
598
  print(f"MongoDB URI: {e.mongo_uri}")
488
- except Exception as e:
489
- print(f"Unexpected error: {e}")
599
+ except (ConnectionFailure, ServerSelectionTimeoutError) as e:
600
+ print(f"MongoDB connection error: {e}")
490
601
  ```
491
602
 
492
603
  ## Integration Examples
@@ -3,20 +3,52 @@ Core MongoDB Engine components.
3
3
 
4
4
  This module contains the main MongoDBEngine class and core
5
5
  orchestration logic for managing apps.
6
+
7
+ The MongoDBEngine now includes:
8
+ - FastAPI integration with create_app() method
9
+ - Optional Ray support with enable_ray parameter
10
+ - Automatic app token retrieval
11
+ - Multi-site mode auto-detection from manifest
6
12
  """
7
13
 
8
14
  from .engine import MongoDBEngine
9
15
  from .manifest import ( # Classes; Constants; Functions (for backward compatibility); Schemas
10
- CURRENT_SCHEMA_VERSION, DEFAULT_SCHEMA_VERSION, MANIFEST_SCHEMA,
11
- MANIFEST_SCHEMA_V1, MANIFEST_SCHEMA_V2, SCHEMA_REGISTRY, ManifestParser,
12
- ManifestValidator, clear_validation_cache, get_schema_for_version,
13
- get_schema_version, migrate_manifest, validate_developer_id,
14
- validate_index_definition, validate_managed_indexes, validate_manifest,
15
- validate_manifest_with_db, validate_manifests_parallel)
16
+ CURRENT_SCHEMA_VERSION,
17
+ DEFAULT_SCHEMA_VERSION,
18
+ MANIFEST_SCHEMA,
19
+ MANIFEST_SCHEMA_V1,
20
+ MANIFEST_SCHEMA_V2,
21
+ SCHEMA_REGISTRY,
22
+ ManifestParser,
23
+ ManifestValidator,
24
+ clear_validation_cache,
25
+ get_schema_for_version,
26
+ get_schema_version,
27
+ migrate_manifest,
28
+ validate_developer_id,
29
+ validate_index_definition,
30
+ validate_managed_indexes,
31
+ validate_manifest,
32
+ validate_manifest_with_db,
33
+ validate_manifests_parallel,
34
+ )
35
+
36
+ # Optional Ray integration (gracefully handles missing Ray)
37
+ from .ray_integration import (
38
+ RAY_AVAILABLE,
39
+ AppRayActor,
40
+ get_ray_actor_handle,
41
+ ray_actor_decorator,
42
+ )
16
43
 
17
44
  __all__ = [
18
- # MongoDB Engine
45
+ # MongoDB Engine (includes FastAPI integration and optional Ray)
19
46
  "MongoDBEngine",
47
+ # Ray Integration (optional - only active if Ray installed)
48
+ "RAY_AVAILABLE",
49
+ "AppRayActor",
50
+ "get_ray_actor_handle",
51
+ "ray_actor_decorator",
20
52
  # Classes
21
53
  "ManifestValidator",
22
54
  "ManifestParser",
@@ -13,13 +13,18 @@ import time
13
13
  from pathlib import Path
14
14
  from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
15
15
 
16
+ from jsonschema import SchemaError
17
+ from jsonschema import ValidationError as JsonSchemaValidationError
16
18
  from motor.motor_asyncio import AsyncIOMotorDatabase
17
- from pymongo.errors import (ConnectionFailure, InvalidOperation,
18
- OperationFailure, ServerSelectionTimeoutError)
19
-
20
- from ..observability import clear_app_context
19
+ from pymongo.errors import (
20
+ ConnectionFailure,
21
+ InvalidOperation,
22
+ OperationFailure,
23
+ ServerSelectionTimeoutError,
24
+ )
25
+
26
+ from ..observability import clear_app_context, record_operation, set_app_context
21
27
  from ..observability import get_logger as get_contextual_logger
22
- from ..observability import record_operation, set_app_context
23
28
  from .manifest import ManifestParser, ManifestValidator
24
29
 
25
30
  if TYPE_CHECKING:
@@ -81,7 +86,7 @@ class AppRegistrationManager:
81
86
  experiment_slug=slug,
82
87
  )
83
88
  return result
84
- except Exception:
89
+ except (JsonSchemaValidationError, SchemaError, ValueError, TypeError, KeyError):
85
90
  duration_ms = (time.time() - start_time) * 1000
86
91
  record_operation(
87
92
  "app_registration.validate_manifest",
@@ -111,15 +116,9 @@ class AppRegistrationManager:
111
116
  self,
112
117
  manifest: "ManifestDict",
113
118
  create_indexes_callback: Optional[Callable[[str, "ManifestDict"], Any]] = None,
114
- seed_data_callback: Optional[
115
- Callable[[str, Dict[str, List[Dict[str, Any]]]], Any]
116
- ] = None,
117
- initialize_memory_callback: Optional[
118
- Callable[[str, Dict[str, Any]], Any]
119
- ] = None,
120
- register_websockets_callback: Optional[
121
- Callable[[str, Dict[str, Any]], Any]
122
- ] = None,
119
+ seed_data_callback: Optional[Callable[[str, Dict[str, List[Dict[str, Any]]]], Any]] = None,
120
+ initialize_memory_callback: Optional[Callable[[str, Dict[str, Any]], Any]] = None,
121
+ register_websockets_callback: Optional[Callable[[str, Dict[str, Any]], Any]] = None,
123
122
  setup_observability_callback: Optional[
124
123
  Callable[[str, "ManifestDict", Dict[str, Any]], Any]
125
124
  ] = None,
@@ -210,9 +209,7 @@ class AppRegistrationManager:
210
209
  )
211
210
  # Continue even if persistence fails - app is still registered in memory
212
211
  except InvalidOperation as e:
213
- logger.debug(
214
- f"Cannot persist app '{slug}': MongoDB client is closed: {e}"
215
- )
212
+ logger.debug(f"Cannot persist app '{slug}': MongoDB client is closed: {e}")
216
213
  # Continue - app is still registered in memory
217
214
 
218
215
  # Invalidate auth config cache for this app
@@ -228,32 +225,22 @@ class AppRegistrationManager:
228
225
 
229
226
  # Create indexes if requested
230
227
  if create_indexes_callback and "managed_indexes" in manifest:
231
- logger.info(
232
- f"[{slug}] Creating managed indexes " f"(has_managed_indexes=True)"
233
- )
228
+ logger.info(f"[{slug}] Creating managed indexes " f"(has_managed_indexes=True)")
234
229
  callback_tasks.append(create_indexes_callback(slug, manifest))
235
230
 
236
231
  # Seed initial data if configured
237
232
  if seed_data_callback and "initial_data" in manifest:
238
- callback_tasks.append(
239
- seed_data_callback(slug, manifest["initial_data"])
240
- )
233
+ callback_tasks.append(seed_data_callback(slug, manifest["initial_data"]))
241
234
 
242
235
  # Initialize Memory service if configured
243
236
  memory_config = manifest.get("memory_config")
244
- if (
245
- initialize_memory_callback
246
- and memory_config
247
- and memory_config.get("enabled", False)
248
- ):
237
+ if initialize_memory_callback and memory_config and memory_config.get("enabled", False):
249
238
  callback_tasks.append(initialize_memory_callback(slug, memory_config))
250
239
 
251
240
  # Register WebSocket endpoints if configured
252
241
  websockets_config = manifest.get("websockets")
253
242
  if register_websockets_callback and websockets_config:
254
- callback_tasks.append(
255
- register_websockets_callback(slug, websockets_config)
256
- )
243
+ callback_tasks.append(register_websockets_callback(slug, websockets_config))
257
244
 
258
245
  # Set up observability (health checks, metrics, logging)
259
246
  observability_config = manifest.get("observability", {})
@@ -275,9 +262,7 @@ class AppRegistrationManager:
275
262
  "register_websockets",
276
263
  "setup_observability",
277
264
  ]
278
- callback_name = (
279
- callback_names[i] if i < len(callback_names) else "unknown"
280
- )
265
+ callback_name = callback_names[i] if i < len(callback_names) else "unknown"
281
266
  logger.warning(
282
267
  f"[{slug}] Callback '{callback_name}' failed during "
283
268
  f"app registration: {result}",
@@ -295,9 +280,7 @@ class AppRegistrationManager:
295
280
  "App registered successfully",
296
281
  extra={
297
282
  "app_slug": slug,
298
- "memory_enabled": bool(
299
- memory_config and memory_config.get("enabled", False)
300
- ),
283
+ "memory_enabled": bool(memory_config and memory_config.get("enabled", False)),
301
284
  "websockets_configured": bool(websockets_config),
302
285
  "duration_ms": round(duration_ms, 2),
303
286
  },
@@ -324,9 +307,7 @@ class AppRegistrationManager:
324
307
  try:
325
308
  # Fetch active apps
326
309
  active_cfgs = (
327
- await self._mongo_db.apps_config.find({"status": "active"})
328
- .limit(500)
329
- .to_list(None)
310
+ await self._mongo_db.apps_config.find({"status": "active"}).limit(500).to_list(None)
330
311
  )
331
312
 
332
313
  logger.info(f"Found {len(active_cfgs)} active app(s).")