""" Demo AI Models Seed Script Creates fake AI models for demo tenants to populate the models list without having actual trained model files. This script uses hardcoded tenant and product IDs to avoid cross-database dependencies. """ import asyncio import sys import os from uuid import UUID from datetime import datetime, timezone, timedelta from decimal import Decimal # Add project root to path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))) from sqlalchemy import select from shared.database.base import create_database_manager import structlog # Import models - these paths work both locally and in container try: # Container environment (training-service image) from app.models.training import TrainedModel except ImportError: # Local environment from services.training.app.models.training import TrainedModel logger = structlog.get_logger() # ============================================================================ # HARDCODED DEMO DATA (from seed scripts) # ============================================================================ # Demo Tenant IDs (from seed_demo_tenants.py) DEMO_TENANT_SAN_PABLO = UUID("a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6") DEMO_TENANT_LA_ESPIGA = UUID("b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7") # Sample Product IDs for each tenant (these should match finished products from inventory seed) # Note: These are example UUIDs - in production, these would be actual product IDs from inventory DEMO_PRODUCTS = { DEMO_TENANT_SAN_PABLO: [ {"id": UUID("10000000-0000-0000-0000-000000000001"), "name": "Barra de Pan"}, {"id": UUID("10000000-0000-0000-0000-000000000002"), "name": "Croissant"}, {"id": UUID("10000000-0000-0000-0000-000000000003"), "name": "Magdalenas"}, {"id": UUID("10000000-0000-0000-0000-000000000004"), "name": "Empanada"}, {"id": UUID("10000000-0000-0000-0000-000000000005"), "name": "Pan Integral"}, ], DEMO_TENANT_LA_ESPIGA: [ {"id": UUID("20000000-0000-0000-0000-000000000001"), "name": "Pan de Molde"}, {"id": UUID("20000000-0000-0000-0000-000000000002"), "name": "Bollo Suizo"}, {"id": UUID("20000000-0000-0000-0000-000000000003"), "name": "Palmera de Chocolate"}, {"id": UUID("20000000-0000-0000-0000-000000000004"), "name": "Napolitana"}, {"id": UUID("20000000-0000-0000-0000-000000000005"), "name": "Pan Rústico"}, ] } class DemoAIModelSeeder: """Seed fake AI models for demo tenants""" def __init__(self): self.training_db_url = os.getenv("TRAINING_DATABASE_URL") or os.getenv("DATABASE_URL") if not self.training_db_url: raise ValueError("Missing TRAINING_DATABASE_URL or DATABASE_URL") # Convert to async URL if needed if self.training_db_url.startswith("postgresql://"): self.training_db_url = self.training_db_url.replace( "postgresql://", "postgresql+asyncpg://", 1 ) self.training_db = create_database_manager(self.training_db_url, "demo-ai-seed") async def create_fake_model(self, session, tenant_id: UUID, product_info: dict): """Create a fake AI model entry for a product""" now = datetime.now(timezone.utc) training_start = now - timedelta(days=90) training_end = now - timedelta(days=7) fake_model = TrainedModel( tenant_id=tenant_id, inventory_product_id=product_info["id"], model_type="prophet_optimized", model_version="1.0-demo", job_id=f"demo-job-{tenant_id}-{product_info['id']}", # Fake file paths (files don't actually exist) model_path=f"/fake/models/{tenant_id}/{product_info['id']}/model.pkl", metadata_path=f"/fake/models/{tenant_id}/{product_info['id']}/metadata.json", # Fake but realistic metrics mape=Decimal("12.5"), # Mean Absolute Percentage Error mae=Decimal("2.3"), # Mean Absolute Error rmse=Decimal("3.1"), # Root Mean Squared Error r2_score=Decimal("0.85"), # R-squared training_samples=60, # 60 days of training data # Fake hyperparameters hyperparameters={ "changepoint_prior_scale": 0.05, "seasonality_prior_scale": 10.0, "holidays_prior_scale": 10.0, "seasonality_mode": "multiplicative" }, # Features used features_used=["weekday", "month", "is_holiday", "temperature", "precipitation"], # Normalization params (fake) normalization_params={ "temperature": {"mean": 15.0, "std": 5.0}, "precipitation": {"mean": 2.0, "std": 1.5} }, # Model status is_active=True, is_production=False, # Demo models are not production-ready # Training data info training_start_date=training_start, training_end_date=training_end, data_quality_score=Decimal("0.75"), # Good but not excellent # Metadata notes=f"Demo model for {product_info['name']} - No actual trained file exists. For demonstration purposes only.", created_by="demo-seed-script", created_at=now, updated_at=now, last_used_at=None ) session.add(fake_model) return fake_model async def seed_models_for_tenant(self, tenant_id: UUID, tenant_name: str, products: list): """Create fake AI models for a demo tenant""" logger.info( "Creating fake AI models for demo tenant", tenant_id=str(tenant_id), tenant_name=tenant_name, product_count=len(products) ) try: async with self.training_db.get_session() as session: models_created = 0 for product in products: # Check if model already exists result = await session.execute( select(TrainedModel).where( TrainedModel.tenant_id == tenant_id, TrainedModel.inventory_product_id == product["id"] ) ) existing_model = result.scalars().first() if existing_model: logger.info( "Model already exists, skipping", tenant_id=str(tenant_id), product_name=product["name"], product_id=str(product["id"]) ) continue # Create fake model model = await self.create_fake_model(session, tenant_id, product) models_created += 1 logger.info( "Created fake AI model", tenant_id=str(tenant_id), product_name=product["name"], product_id=str(product["id"]), model_id=str(model.id) ) await session.commit() logger.info( "✅ Successfully created fake AI models for tenant", tenant_id=str(tenant_id), tenant_name=tenant_name, models_created=models_created ) return models_created except Exception as e: logger.error( "❌ Error creating fake AI models for tenant", tenant_id=str(tenant_id), tenant_name=tenant_name, error=str(e), exc_info=True ) raise async def seed_all_demo_models(self): """Seed fake AI models for all demo tenants""" logger.info("=" * 80) logger.info("🤖 Starting Demo AI Models Seeding") logger.info("=" * 80) total_models_created = 0 try: # Seed models for San Pablo san_pablo_count = await self.seed_models_for_tenant( tenant_id=DEMO_TENANT_SAN_PABLO, tenant_name="Panadería San Pablo", products=DEMO_PRODUCTS[DEMO_TENANT_SAN_PABLO] ) total_models_created += san_pablo_count # Seed models for La Espiga la_espiga_count = await self.seed_models_for_tenant( tenant_id=DEMO_TENANT_LA_ESPIGA, tenant_name="Panadería La Espiga", products=DEMO_PRODUCTS[DEMO_TENANT_LA_ESPIGA] ) total_models_created += la_espiga_count logger.info("=" * 80) logger.info( "✅ Demo AI Models Seeding Completed", total_models_created=total_models_created, tenants_processed=2 ) logger.info("=" * 80) except Exception as e: logger.error("=" * 80) logger.error("❌ Demo AI Models Seeding Failed") logger.error("=" * 80) logger.error("Error: %s", str(e)) raise async def main(): """Main entry point""" logger.info("Demo AI Models Seed Script Starting") logger.info("Mode: %s", os.getenv("DEMO_MODE", "development")) logger.info("Log Level: %s", os.getenv("LOG_LEVEL", "INFO")) try: seeder = DemoAIModelSeeder() await seeder.seed_all_demo_models() logger.info("") logger.info("🎉 Success! Demo AI models are ready.") logger.info("") logger.info("Note: These are fake models for demo purposes only.") logger.info(" No actual model files exist on disk.") logger.info("") return 0 except Exception as e: logger.error("Demo AI models seed failed", error=str(e), exc_info=True) return 1 if __name__ == "__main__": exit_code = asyncio.run(main()) sys.exit(exit_code)