isa-model 0.4.0__py3-none-any.whl → 0.4.3__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 (199) hide show
  1. isa_model/client.py +466 -43
  2. isa_model/core/cache/redis_cache.py +12 -3
  3. isa_model/core/config/config_manager.py +230 -3
  4. isa_model/core/config.py +90 -0
  5. isa_model/core/database/direct_db_client.py +114 -0
  6. isa_model/core/database/migration_manager.py +563 -0
  7. isa_model/core/database/migrations.py +21 -1
  8. isa_model/core/database/supabase_client.py +154 -19
  9. isa_model/core/dependencies.py +316 -0
  10. isa_model/core/discovery/__init__.py +19 -0
  11. isa_model/core/discovery/consul_discovery.py +190 -0
  12. isa_model/core/logging/__init__.py +54 -0
  13. isa_model/core/logging/influx_logger.py +523 -0
  14. isa_model/core/logging/loki_logger.py +160 -0
  15. isa_model/core/models/__init__.py +27 -18
  16. isa_model/core/models/config_models.py +625 -0
  17. isa_model/core/models/deployment_billing_tracker.py +430 -0
  18. isa_model/core/models/model_manager.py +40 -17
  19. isa_model/core/models/model_metadata.py +690 -0
  20. isa_model/core/models/model_repo.py +174 -18
  21. isa_model/core/models/system_models.py +857 -0
  22. isa_model/core/repositories/__init__.py +9 -0
  23. isa_model/core/repositories/config_repository.py +912 -0
  24. isa_model/core/services/intelligent_model_selector.py +399 -21
  25. isa_model/core/storage/hf_storage.py +1 -1
  26. isa_model/core/types.py +1 -0
  27. isa_model/deployment/__init__.py +5 -48
  28. isa_model/deployment/core/__init__.py +2 -31
  29. isa_model/deployment/core/deployment_manager.py +1278 -370
  30. isa_model/deployment/local/__init__.py +31 -0
  31. isa_model/deployment/local/config.py +248 -0
  32. isa_model/deployment/local/gpu_gateway.py +607 -0
  33. isa_model/deployment/local/health_checker.py +428 -0
  34. isa_model/deployment/local/provider.py +586 -0
  35. isa_model/deployment/local/tensorrt_service.py +621 -0
  36. isa_model/deployment/local/transformers_service.py +644 -0
  37. isa_model/deployment/local/vllm_service.py +527 -0
  38. isa_model/deployment/modal/__init__.py +8 -0
  39. isa_model/deployment/modal/config.py +136 -0
  40. isa_model/deployment/{services/auto_hf_modal_deployer.py → modal/deployer.py} +1 -1
  41. isa_model/deployment/modal/services/__init__.py +3 -0
  42. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  43. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  44. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  45. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  46. isa_model/deployment/modal/services/video/__init__.py +1 -0
  47. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  48. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  49. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  50. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  51. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  52. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  53. isa_model/deployment/storage/__init__.py +5 -0
  54. isa_model/deployment/storage/deployment_repository.py +824 -0
  55. isa_model/deployment/triton/__init__.py +10 -0
  56. isa_model/deployment/triton/config.py +196 -0
  57. isa_model/deployment/triton/configs/__init__.py +1 -0
  58. isa_model/deployment/triton/provider.py +512 -0
  59. isa_model/deployment/triton/scripts/__init__.py +1 -0
  60. isa_model/deployment/triton/templates/__init__.py +1 -0
  61. isa_model/inference/__init__.py +47 -1
  62. isa_model/inference/ai_factory.py +137 -10
  63. isa_model/inference/legacy_services/__init__.py +21 -0
  64. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  65. isa_model/inference/legacy_services/model_service.py +573 -0
  66. isa_model/inference/legacy_services/model_serving.py +717 -0
  67. isa_model/inference/legacy_services/model_training.py +561 -0
  68. isa_model/inference/models/__init__.py +21 -0
  69. isa_model/inference/models/inference_config.py +551 -0
  70. isa_model/inference/models/inference_record.py +675 -0
  71. isa_model/inference/models/performance_models.py +714 -0
  72. isa_model/inference/repositories/__init__.py +9 -0
  73. isa_model/inference/repositories/inference_repository.py +828 -0
  74. isa_model/inference/services/audio/base_stt_service.py +184 -11
  75. isa_model/inference/services/audio/openai_stt_service.py +22 -6
  76. isa_model/inference/services/custom_model_manager.py +277 -0
  77. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  78. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  79. isa_model/inference/services/llm/__init__.py +10 -2
  80. isa_model/inference/services/llm/base_llm_service.py +335 -24
  81. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  82. isa_model/inference/services/llm/helpers/llm_adapter.py +9 -4
  83. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  84. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  85. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  86. isa_model/inference/services/llm/local_llm_service.py +747 -0
  87. isa_model/inference/services/llm/ollama_llm_service.py +9 -2
  88. isa_model/inference/services/llm/openai_llm_service.py +33 -16
  89. isa_model/inference/services/llm/yyds_llm_service.py +8 -2
  90. isa_model/inference/services/vision/__init__.py +22 -1
  91. isa_model/inference/services/vision/blip_vision_service.py +359 -0
  92. isa_model/inference/services/vision/helpers/image_utils.py +8 -5
  93. isa_model/inference/services/vision/isa_vision_service.py +65 -4
  94. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  95. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  96. isa_model/serving/api/cache_manager.py +245 -0
  97. isa_model/serving/api/dependencies/__init__.py +1 -0
  98. isa_model/serving/api/dependencies/auth.py +194 -0
  99. isa_model/serving/api/dependencies/database.py +139 -0
  100. isa_model/serving/api/error_handlers.py +284 -0
  101. isa_model/serving/api/fastapi_server.py +172 -22
  102. isa_model/serving/api/middleware/auth.py +8 -2
  103. isa_model/serving/api/middleware/security.py +23 -33
  104. isa_model/serving/api/middleware/tenant_context.py +414 -0
  105. isa_model/serving/api/routes/analytics.py +4 -1
  106. isa_model/serving/api/routes/config.py +645 -0
  107. isa_model/serving/api/routes/deployment_billing.py +315 -0
  108. isa_model/serving/api/routes/deployments.py +138 -2
  109. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  110. isa_model/serving/api/routes/health.py +32 -12
  111. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  112. isa_model/serving/api/routes/local_deployments.py +448 -0
  113. isa_model/serving/api/routes/tenants.py +575 -0
  114. isa_model/serving/api/routes/unified.py +680 -18
  115. isa_model/serving/api/routes/webhooks.py +479 -0
  116. isa_model/serving/api/startup.py +68 -54
  117. isa_model/utils/gpu_utils.py +311 -0
  118. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/METADATA +66 -24
  119. isa_model-0.4.3.dist-info/RECORD +193 -0
  120. isa_model/core/storage/minio_storage.py +0 -0
  121. isa_model/deployment/cloud/__init__.py +0 -9
  122. isa_model/deployment/cloud/modal/__init__.py +0 -10
  123. isa_model/deployment/core/deployment_config.py +0 -356
  124. isa_model/deployment/core/isa_deployment_service.py +0 -401
  125. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  126. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  127. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  128. isa_model/deployment/runtime/deployed_service.py +0 -338
  129. isa_model/deployment/services/__init__.py +0 -9
  130. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  131. isa_model/deployment/services/model_service.py +0 -332
  132. isa_model/deployment/services/service_monitor.py +0 -356
  133. isa_model/deployment/services/service_registry.py +0 -527
  134. isa_model/eval/__init__.py +0 -92
  135. isa_model/eval/benchmarks/__init__.py +0 -27
  136. isa_model/eval/benchmarks/multimodal_datasets.py +0 -460
  137. isa_model/eval/benchmarks.py +0 -701
  138. isa_model/eval/config/__init__.py +0 -10
  139. isa_model/eval/config/evaluation_config.py +0 -108
  140. isa_model/eval/evaluators/__init__.py +0 -24
  141. isa_model/eval/evaluators/audio_evaluator.py +0 -727
  142. isa_model/eval/evaluators/base_evaluator.py +0 -503
  143. isa_model/eval/evaluators/embedding_evaluator.py +0 -742
  144. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  145. isa_model/eval/evaluators/vision_evaluator.py +0 -564
  146. isa_model/eval/example_evaluation.py +0 -395
  147. isa_model/eval/factory.py +0 -798
  148. isa_model/eval/infrastructure/__init__.py +0 -24
  149. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  150. isa_model/eval/isa_benchmarks.py +0 -700
  151. isa_model/eval/isa_integration.py +0 -582
  152. isa_model/eval/metrics.py +0 -951
  153. isa_model/eval/tests/unit/test_basic.py +0 -396
  154. isa_model/serving/api/routes/evaluations.py +0 -579
  155. isa_model/training/__init__.py +0 -168
  156. isa_model/training/annotation/annotation_schema.py +0 -47
  157. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  158. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  159. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  160. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  161. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  162. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  163. isa_model/training/annotation/views/annotation_controller.py +0 -158
  164. isa_model/training/cloud/__init__.py +0 -22
  165. isa_model/training/cloud/job_orchestrator.py +0 -402
  166. isa_model/training/cloud/runpod_trainer.py +0 -454
  167. isa_model/training/cloud/storage_manager.py +0 -482
  168. isa_model/training/core/__init__.py +0 -26
  169. isa_model/training/core/config.py +0 -181
  170. isa_model/training/core/dataset.py +0 -222
  171. isa_model/training/core/trainer.py +0 -720
  172. isa_model/training/core/utils.py +0 -213
  173. isa_model/training/examples/intelligent_training_example.py +0 -281
  174. isa_model/training/factory.py +0 -424
  175. isa_model/training/intelligent/__init__.py +0 -25
  176. isa_model/training/intelligent/decision_engine.py +0 -643
  177. isa_model/training/intelligent/intelligent_factory.py +0 -888
  178. isa_model/training/intelligent/knowledge_base.py +0 -751
  179. isa_model/training/intelligent/resource_optimizer.py +0 -839
  180. isa_model/training/intelligent/task_classifier.py +0 -576
  181. isa_model/training/storage/__init__.py +0 -24
  182. isa_model/training/storage/core_integration.py +0 -439
  183. isa_model/training/storage/training_repository.py +0 -552
  184. isa_model/training/storage/training_storage.py +0 -628
  185. isa_model-0.4.0.dist-info/RECORD +0 -182
  186. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_chatTTS_service.py +0 -0
  187. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_fish_service.py +0 -0
  188. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_openvoice_service.py +0 -0
  189. /isa_model/deployment/{cloud/modal → modal/services/audio}/isa_audio_service_v2.py +0 -0
  190. /isa_model/deployment/{cloud/modal → modal/services/embedding}/isa_embed_rerank_service.py +0 -0
  191. /isa_model/deployment/{cloud/modal → modal/services/video}/isa_video_hunyuan_service.py +0 -0
  192. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ocr_service.py +0 -0
  193. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_qwen25_service.py +0 -0
  194. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_table_service.py +0 -0
  195. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service.py +0 -0
  196. /isa_model/deployment/{cloud/modal → modal/services/vision}/isa_vision_ui_service_optimized.py +0 -0
  197. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  198. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/WHEEL +0 -0
  199. {isa_model-0.4.0.dist-info → isa_model-0.4.3.dist-info}/top_level.txt +0 -0
@@ -15,34 +15,29 @@ import logging
15
15
  import os
16
16
  from typing import Dict, Any, Optional
17
17
 
18
- from .routes import health, unified, deployments, logs, analytics, settings, evaluations
18
+ from .routes import health, unified, deployments, logs, analytics, settings, inference_monitoring, webhooks, tenants # config, training, annotation, and evaluations temporarily disabled
19
19
  from .middleware.request_logger import RequestLoggerMiddleware
20
20
  from .middleware.security import setup_security_middleware, check_redis_health
21
+ from .middleware.tenant_context import TenantContextMiddleware
21
22
  from .startup import run_startup_initialization
23
+ from ...core.logging import api_logger, setup_logger
22
24
 
23
- logger = logging.getLogger(__name__)
25
+ logger = api_logger # Use Loki-configured logger instead of standard logging
24
26
 
25
27
  def configure_logging():
26
- """Configure logging based on environment variables"""
28
+ """Configure logging based on environment variables
29
+
30
+ Note: Loki integration is handled automatically by isa_model.core.logging.setup_logger
31
+ This function only sets log levels for existing loggers.
32
+ """
27
33
  log_level = os.getenv('LOG_LEVEL', 'INFO').upper()
28
34
  verbose_logging = os.getenv('VERBOSE_LOGGING', 'false').lower() == 'true'
29
-
35
+
30
36
  # Set log level
31
37
  level = getattr(logging, log_level, logging.INFO)
32
-
33
- # Configure format
34
- if verbose_logging:
35
- log_format = '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
36
- else:
37
- log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
38
-
39
- # Configure root logger
40
- logging.basicConfig(
41
- level=level,
42
- format=log_format,
43
- datefmt='%Y-%m-%d %H:%M:%S',
44
- force=True # Override existing configuration
45
- )
38
+
39
+ # Note: Don't call logging.basicConfig() here as it conflicts with Loki handlers
40
+ # The Loki logger (api_logger) is already configured with proper handlers
46
41
 
47
42
  # Set uvicorn logger level to match
48
43
  uvicorn_logger = logging.getLogger("uvicorn")
@@ -52,6 +47,69 @@ def configure_logging():
52
47
  app_logger = logging.getLogger("isa_model")
53
48
  app_logger.setLevel(level)
54
49
 
50
+ # Suppress verbose third-party library logs
51
+ # HTTP libraries - only show WARNING and above
52
+ logging.getLogger("httpcore").setLevel(logging.WARNING)
53
+ logging.getLogger("httpx").setLevel(logging.WARNING)
54
+ logging.getLogger("httpcore.http11").setLevel(logging.WARNING)
55
+ logging.getLogger("httpcore.connection").setLevel(logging.WARNING)
56
+
57
+ # Database and ORM libraries
58
+ logging.getLogger("supabase").setLevel(logging.WARNING)
59
+ logging.getLogger("postgrest").setLevel(logging.WARNING)
60
+
61
+ # AI/ML libraries
62
+ logging.getLogger("openai").setLevel(logging.WARNING)
63
+ logging.getLogger("anthropic").setLevel(logging.WARNING)
64
+ logging.getLogger("google").setLevel(logging.WARNING)
65
+ logging.getLogger("google.cloud").setLevel(logging.WARNING)
66
+ logging.getLogger("google.generativeai").setLevel(logging.WARNING)
67
+
68
+ # Other verbose libraries
69
+ logging.getLogger("urllib3").setLevel(logging.WARNING)
70
+ logging.getLogger("requests").setLevel(logging.WARNING)
71
+ logging.getLogger("aiohttp").setLevel(logging.WARNING)
72
+
73
+ # Reduce startup debug logs
74
+ if not verbose_logging:
75
+ # Reduce startup initialization debug logs
76
+ startup_logger = logging.getLogger("isa_model.serving.api.startup")
77
+ startup_logger.setLevel(logging.WARNING)
78
+
79
+ # Reduce model registry debug logs
80
+ model_logger = logging.getLogger("isa_model.core.models.model_repo")
81
+ model_logger.setLevel(logging.WARNING)
82
+
83
+ # Reduce intelligent selector debug logs
84
+ selector_logger = logging.getLogger("isa_model.core.services.intelligent_model_selector")
85
+ selector_logger.setLevel(logging.WARNING)
86
+
87
+ # Training module removed - logger configuration no longer needed
88
+
89
+ # Reduce knowledge base logs
90
+ kb_logger = logging.getLogger("isa_model.core.knowledge_base")
91
+ kb_logger.setLevel(logging.WARNING)
92
+
93
+ # Reduce database migration logs
94
+ migration_logger = logging.getLogger("isa_model.core.database.migrations")
95
+ migration_logger.setLevel(logging.WARNING)
96
+
97
+ # Reduce AI factory logs
98
+ ai_factory_logger = logging.getLogger("isa_model.inference.ai_factory")
99
+ ai_factory_logger.setLevel(logging.WARNING)
100
+
101
+ # Reduce embedding service logs
102
+ embed_logger = logging.getLogger("isa_model.inference.services.embedding")
103
+ embed_logger.setLevel(logging.WARNING)
104
+
105
+ # Reduce config manager logs
106
+ config_logger = logging.getLogger("isa_model.core.config")
107
+ config_logger.setLevel(logging.WARNING)
108
+
109
+ # Reduce core integration logs
110
+ core_logger = logging.getLogger("isa_model.core")
111
+ core_logger.setLevel(logging.WARNING)
112
+
55
113
  logger.info(f"Logging configured - Level: {log_level}, Verbose: {verbose_logging}")
56
114
 
57
115
  def create_app(config: Dict[str, Any] = None) -> FastAPI:
@@ -79,6 +137,9 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
79
137
  # This includes CORS, rate limiting, security headers, request validation
80
138
  setup_security_middleware(app)
81
139
 
140
+ # Add tenant context middleware (before request logger)
141
+ app.add_middleware(TenantContextMiddleware)
142
+
82
143
  # Add custom middleware
83
144
  app.add_middleware(RequestLoggerMiddleware)
84
145
 
@@ -112,8 +173,26 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
112
173
  # SETTINGS API - Configuration and API key management
113
174
  app.include_router(settings.router, prefix="/api/v1/settings", tags=["settings"])
114
175
 
115
- # EVALUATIONS API - Model evaluation and benchmarking
116
- app.include_router(evaluations.router, prefix="/api/v1/evaluations", tags=["evaluations"])
176
+ # EVALUATIONS API - Temporarily disabled for staging optimization
177
+ # app.include_router(evaluations.router, prefix="/api/v1/evaluations", tags=["evaluations"])
178
+
179
+ # INFERENCE MONITORING API - InfluxDB-based inference monitoring and analytics
180
+ app.include_router(inference_monitoring.router, prefix="/api/v1/monitoring", tags=["monitoring"])
181
+
182
+ # TRAINING API - Disabled for staging optimization
183
+ # app.include_router(training.router, prefix="/api/v1/training", tags=["training"])
184
+
185
+ # WEBHOOKS API - Webhook management and notifications
186
+ app.include_router(webhooks.router, prefix="/api/v1/webhooks", tags=["webhooks"])
187
+
188
+ # TENANTS API - Multi-tenancy and organization management
189
+ app.include_router(tenants.router, prefix="/api/v1/tenants", tags=["tenants"])
190
+
191
+ # ANNOTATION API - Temporarily disabled for staging optimization
192
+ # app.include_router(annotation.router, prefix="/api/v1/annotations", tags=["annotations"])
193
+
194
+ # CONFIG API - Configuration management
195
+ # app.include_router(config.router, prefix="/api/v1/config", tags=["config"]) # Temporarily disabled
117
196
 
118
197
  # Mount static files
119
198
  static_path = os.path.join(os.path.dirname(__file__), "../static")
@@ -149,7 +228,45 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
149
228
  logger.info("✅ Application startup completed successfully")
150
229
  except Exception as e:
151
230
  logger.error(f"❌ Application startup failed: {e}")
152
- # Don't raise - let the app start anyway
231
+ logger.error("⚠️ Server will continue but may have reduced functionality")
232
+ # Store startup failure state for health checks
233
+ app.state.startup_failed = True
234
+ app.state.startup_error = str(e)
235
+ # Continue running to allow debugging and partial functionality
236
+
237
+ # Add shutdown event handler
238
+ @app.on_event("shutdown")
239
+ async def shutdown_event():
240
+ logger.info("🧹 Starting application shutdown cleanup...")
241
+ try:
242
+ # Close database connections
243
+ try:
244
+ from .dependencies.database import close_database_pool
245
+ await close_database_pool()
246
+ logger.info("✅ Database connections closed")
247
+ except Exception as e:
248
+ logger.error(f"❌ Error closing database connections: {e}")
249
+
250
+ # Clean up AI factory and services
251
+ try:
252
+ from ...inference.ai_factory import AIFactory
253
+ factory = AIFactory.get_instance()
254
+ await factory.cleanup()
255
+ logger.info("✅ AI Factory cleaned up")
256
+ except Exception as e:
257
+ logger.error(f"❌ Error cleaning up AI Factory: {e}")
258
+
259
+ # Clean up startup initializer resources
260
+ try:
261
+ from .startup import startup_initializer
262
+ await startup_initializer.cleanup()
263
+ logger.info("✅ Startup resources cleaned up")
264
+ except Exception as e:
265
+ logger.error(f"❌ Error cleaning up startup resources: {e}")
266
+
267
+ logger.info("✅ Application shutdown completed successfully")
268
+ except Exception as e:
269
+ logger.error(f"❌ Error during application shutdown: {e}")
153
270
 
154
271
  return app
155
272
 
@@ -158,4 +275,37 @@ app = create_app()
158
275
 
159
276
  if __name__ == "__main__":
160
277
  import uvicorn
161
- uvicorn.run(app, host="0.0.0.0", port=8000)
278
+ import os
279
+ import signal
280
+
281
+ port = int(os.getenv("PORT", 8082))
282
+
283
+ # Configure uvicorn for graceful shutdown
284
+ config = uvicorn.Config(
285
+ app,
286
+ host="0.0.0.0",
287
+ port=port,
288
+ log_level=os.getenv("LOG_LEVEL", "info").lower(),
289
+ access_log=True,
290
+ loop="asyncio",
291
+ # Graceful shutdown configuration
292
+ timeout_keep_alive=30, # Keep connections alive for 30 seconds
293
+ timeout_graceful_shutdown=30, # 30 second graceful shutdown timeout
294
+ )
295
+
296
+ server = uvicorn.Server(config)
297
+
298
+ # Setup signal handlers for graceful shutdown
299
+ def signal_handler(signum, frame):
300
+ logger.info(f"Received signal {signum}, initiating graceful shutdown...")
301
+ server.should_exit = True
302
+
303
+ signal.signal(signal.SIGINT, signal_handler)
304
+ signal.signal(signal.SIGTERM, signal_handler)
305
+
306
+ try:
307
+ server.run()
308
+ except KeyboardInterrupt:
309
+ logger.info("Keyboard interrupt received, shutting down...")
310
+ finally:
311
+ logger.info("Server shutdown complete")
@@ -35,10 +35,16 @@ class APIKeyManager:
35
35
  # Load API keys first to check if auth should be enabled
36
36
  self.load_api_keys()
37
37
 
38
- # Determine auth state: enabled if explicitly set OR if API keys exist
38
+ # Determine auth state: check explicit setting first, then auto-detect from keys
39
39
  explicit_auth = AUTH_ENABLED
40
40
  has_keys = len(self.api_keys) > 0
41
- self.auth_enabled = explicit_auth or has_keys
41
+
42
+ # If explicitly disabled (REQUIRE_API_KEYS=false), respect that setting
43
+ if os.getenv("REQUIRE_API_KEYS", "").lower() == "false":
44
+ self.auth_enabled = False
45
+ else:
46
+ # Otherwise, enable if explicitly set OR if API keys exist
47
+ self.auth_enabled = explicit_auth or has_keys
42
48
 
43
49
  if self.auth_enabled:
44
50
  logger.info(f"API Key authentication is ENABLED ({'explicit' if explicit_auth else 'auto-detected from keys'})")
@@ -13,7 +13,6 @@ import time
13
13
  import logging
14
14
  import os
15
15
  import redis
16
- import structlog
17
16
  from typing import Dict, Any, Optional, Callable
18
17
  from fastapi import FastAPI, Request, Response, HTTPException, status
19
18
  from fastapi.middleware.cors import CORSMiddleware
@@ -25,12 +24,16 @@ from starlette.middleware.base import BaseHTTPMiddleware
25
24
  from starlette.responses import JSONResponse
26
25
  import html
27
26
 
28
- # Configure structured logging
29
- logger = structlog.get_logger(__name__)
27
+ from ....core.config.config_manager import ConfigManager
28
+
29
+ # Configure logging
30
+ logger = logging.getLogger(__name__)
30
31
 
31
32
  # Configuration from environment variables
33
+ config_manager = ConfigManager()
32
34
  MAX_REQUEST_SIZE = int(os.getenv("MAX_REQUEST_SIZE_MB", "50")) * 1024 * 1024 # 50MB default
33
- REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
35
+ # Use Consul discovery for Redis URL with fallback
36
+ REDIS_URL = os.getenv("REDIS_URL", config_manager.get_redis_url())
34
37
  ENABLE_RATE_LIMITING = os.getenv("ENABLE_RATE_LIMITING", "true").lower() == "true"
35
38
  RATE_LIMIT_PER_MINUTE = os.getenv("RATE_LIMIT_PER_MINUTE", "100")
36
39
  RATE_LIMIT_PER_HOUR = os.getenv("RATE_LIMIT_PER_HOUR", "1000")
@@ -41,7 +44,7 @@ SECURITY_HEADERS = {
41
44
  "X-Frame-Options": "DENY",
42
45
  "X-XSS-Protection": "1; mode=block",
43
46
  "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
44
- "Content-Security-Policy": "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self'",
47
+ "Content-Security-Policy": "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://fastapi.tiangolo.com data:; connect-src 'self'",
45
48
  "Referrer-Policy": "strict-origin-when-cross-origin",
46
49
  "Permissions-Policy": "geolocation=(), microphone=(), camera=()"
47
50
  }
@@ -100,7 +103,7 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
100
103
  return response
101
104
 
102
105
  except Exception as e:
103
- logger.error("Error in security headers middleware", error=str(e))
106
+ logger.error(f"Error in security headers middleware: {e}")
104
107
  return JSONResponse(
105
108
  status_code=500,
106
109
  content={"error": "Internal server error"},
@@ -119,10 +122,8 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
119
122
  content_length = request.headers.get("content-length")
120
123
  if content_length and int(content_length) > MAX_REQUEST_SIZE:
121
124
  logger.warning(
122
- "Request too large",
123
- content_length=content_length,
124
- max_size=MAX_REQUEST_SIZE,
125
- client_ip=get_remote_address_with_proxy(request)
125
+ f"Request too large: {content_length} bytes > {MAX_REQUEST_SIZE} bytes "
126
+ f"from client {get_remote_address_with_proxy(request)}"
126
127
  )
127
128
  raise HTTPException(
128
129
  status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
@@ -134,19 +135,15 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
134
135
  sanitized_query = html.escape(request.url.query)
135
136
  if sanitized_query != request.url.query:
136
137
  logger.warning(
137
- "Potentially malicious query parameters detected",
138
- original=request.url.query,
139
- sanitized=sanitized_query,
140
- client_ip=get_remote_address_with_proxy(request)
138
+ f"Potentially malicious query parameters detected from client {get_remote_address_with_proxy(request)}: "
139
+ f"'{request.url.query}' -> '{sanitized_query}'"
141
140
  )
142
141
 
143
142
  # Log request details for monitoring
144
143
  logger.info(
145
- "Request received",
146
- method=request.method,
147
- path=request.url.path,
148
- client_ip=get_remote_address_with_proxy(request),
149
- user_agent=request.headers.get("user-agent", "unknown")
144
+ f"Request received: {request.method} {request.url.path} "
145
+ f"from {get_remote_address_with_proxy(request)} "
146
+ f"(UA: {request.headers.get('user-agent', 'unknown')})"
150
147
  )
151
148
 
152
149
  response = await call_next(request)
@@ -154,12 +151,8 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
154
151
  # Log response details
155
152
  process_time = time.time() - request.state.start_time
156
153
  logger.info(
157
- "Request completed",
158
- method=request.method,
159
- path=request.url.path,
160
- status_code=response.status_code,
161
- process_time=process_time,
162
- client_ip=get_remote_address_with_proxy(request)
154
+ f"Request completed: {request.method} {request.url.path} -> {response.status_code} "
155
+ f"in {process_time:.3f}s from {get_remote_address_with_proxy(request)}"
163
156
  )
164
157
 
165
158
  return response
@@ -168,11 +161,8 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
168
161
  raise
169
162
  except Exception as e:
170
163
  logger.error(
171
- "Error in request validation middleware",
172
- error=str(e),
173
- path=request.url.path,
174
- method=request.method,
175
- client_ip=get_remote_address_with_proxy(request)
164
+ f"Error in request validation middleware: {e} "
165
+ f"({request.method} {request.url.path} from {get_remote_address_with_proxy(request)})"
176
166
  )
177
167
  raise HTTPException(
178
168
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
@@ -186,13 +176,13 @@ def setup_security_middleware(app: FastAPI):
186
176
  if ENABLE_RATE_LIMITING:
187
177
  app.state.limiter = limiter
188
178
  app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
189
- logger.info("Rate limiting enabled", redis_backend=redis_client is not None)
179
+ logger.info(f"Rate limiting enabled (Redis backend: {redis_client is not None})")
190
180
 
191
181
  # Trusted hosts (production should specify allowed hosts)
192
182
  allowed_hosts = os.getenv("ALLOWED_HOSTS", "*").split(",")
193
183
  if allowed_hosts != ["*"]:
194
184
  app.add_middleware(TrustedHostMiddleware, allowed_hosts=allowed_hosts)
195
- logger.info("Trusted hosts middleware enabled", allowed_hosts=allowed_hosts)
185
+ logger.info(f"Trusted hosts middleware enabled: {allowed_hosts}")
196
186
 
197
187
  # CORS configuration
198
188
  cors_origins = os.getenv("CORS_ORIGINS", "*").split(",")
@@ -204,7 +194,7 @@ def setup_security_middleware(app: FastAPI):
204
194
  allow_headers=["*"],
205
195
  expose_headers=["X-Process-Time"]
206
196
  )
207
- logger.info("CORS middleware enabled", origins=cors_origins)
197
+ logger.info(f"CORS middleware enabled for origins: {cors_origins}")
208
198
 
209
199
  # Custom security middleware
210
200
  app.add_middleware(SecurityHeadersMiddleware)