Spaces:
Running
Running
File size: 8,429 Bytes
ed37502 a4583f2 ed37502 a4583f2 7b5f7c4 a4583f2 7b5f7c4 ed37502 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | """Content Engine — FastAPI application entry point.
Run with:
cd "D:\AI automation\content_engine"
uvicorn content_engine.main:app --host 0.0.0.0 --port 8000 --reload
Or:
python -m content_engine.main
"""
from __future__ import annotations
import logging
import os
from contextlib import asynccontextmanager
from pathlib import Path
import yaml
from dotenv import load_dotenv
from fastapi import FastAPI
from content_engine.config import settings
from content_engine.models.database import init_db
from content_engine.services.catalog import CatalogService
from content_engine.services.comfyui_client import ComfyUIClient
from content_engine.services.template_engine import TemplateEngine
from content_engine.services.variation_engine import CharacterProfile, VariationEngine
from content_engine.services.workflow_builder import WorkflowBuilder
from content_engine.workers.local_worker import LocalWorker
from content_engine.api import routes_catalog, routes_generation, routes_pod, routes_system, routes_training, routes_ui, routes_video
# Load .env file for API keys
import os
IS_HF_SPACES = os.environ.get("HF_SPACES") == "1" or os.environ.get("SPACE_ID") is not None
if IS_HF_SPACES:
load_dotenv(Path("/app/.env"))
else:
load_dotenv(Path("D:/AI automation/content_engine/.env"))
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)
# Service instances (created at startup)
comfyui_client: ComfyUIClient | None = None
def load_character_profiles() -> dict[str, CharacterProfile]:
"""Load all character YAML profiles from config/characters/."""
if IS_HF_SPACES:
characters_dir = Path("/app/config/characters")
else:
characters_dir = Path("D:/AI automation/content_engine/config/characters")
profiles: dict[str, CharacterProfile] = {}
if not characters_dir.exists():
logger.warning("Characters directory not found: %s", characters_dir)
return profiles
for path in characters_dir.glob("*.yaml"):
try:
with open(path) as f:
data = yaml.safe_load(f)
profile = CharacterProfile(
id=data["id"],
name=data.get("name", data["id"]),
trigger_word=data["trigger_word"],
lora_filename=data["lora_filename"],
lora_strength=data.get("lora_strength", 0.85),
default_checkpoint=data.get("default_checkpoint"),
style_loras=data.get("style_loras", []),
description=data.get("description", ""),
physical_traits=data.get("physical_traits", {}),
)
profiles[profile.id] = profile
logger.info("Loaded character: %s (%s)", profile.name, profile.id)
except Exception:
logger.error("Failed to load character %s", path, exc_info=True)
return profiles
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Startup and shutdown lifecycle."""
global comfyui_client
logger.info("Starting Content Engine...")
# Initialize database
await init_db()
logger.info("Database initialized")
# Create service instances
comfyui_client = ComfyUIClient(settings.comfyui.url)
workflow_builder = WorkflowBuilder()
template_engine = TemplateEngine()
template_engine.load_all()
catalog = CatalogService()
character_profiles = load_character_profiles()
variation_engine = VariationEngine(template_engine)
local_worker = LocalWorker(
comfyui_client=comfyui_client,
workflow_builder=workflow_builder,
template_engine=template_engine,
catalog=catalog,
)
# Check ComfyUI connection
if await comfyui_client.is_available():
logger.info("ComfyUI connected at %s", settings.comfyui.url)
else:
logger.warning(
"ComfyUI not available at %s — generation will fail until connected",
settings.comfyui.url,
)
# Initialize WaveSpeed cloud provider if API key is set
wavespeed_provider = None
wavespeed_key = os.environ.get("WAVESPEED_API_KEY")
if wavespeed_key:
from content_engine.services.cloud_providers.wavespeed_provider import WaveSpeedProvider
wavespeed_provider = WaveSpeedProvider(api_key=wavespeed_key)
logger.info("WaveSpeed cloud provider initialized (NanoBanana, SeeDream)")
else:
logger.info("WaveSpeed not configured — cloud generation disabled")
# Initialize Higgsfield cloud provider if API key is set
higgsfield_provider = None
higgsfield_key = os.environ.get("HIGGSFIELD_API_KEY") or os.environ.get("HF_API_KEY")
if higgsfield_key:
try:
from content_engine.services.cloud_providers.higgsfield_provider import HiggsFieldProvider
higgsfield_provider = HiggsFieldProvider()
logger.info("Higgsfield cloud provider initialized (Kling 3.0, Sora 2, Veo 3.1)")
except Exception as e:
logger.warning("Failed to initialize Higgsfield provider: %s", e)
else:
logger.info("Higgsfield not configured — set HIGGSFIELD_API_KEY for Kling 3.0/Sora 2")
# Initialize route dependencies
routes_generation.init_routes(
local_worker, template_engine, variation_engine, character_profiles,
wavespeed_provider=wavespeed_provider, catalog=catalog,
comfyui_client=comfyui_client,
)
routes_catalog.init_routes(catalog)
routes_system.init_routes(comfyui_client, catalog, template_engine, character_profiles)
# Initialize video routes with cloud providers
if wavespeed_provider:
routes_video.init_wavespeed(wavespeed_provider)
if higgsfield_provider:
routes_video.init_higgsfield(higgsfield_provider)
# Initialize LoRA trainer (local)
from content_engine.services.lora_trainer import LoRATrainer
lora_trainer = LoRATrainer()
logger.info("LoRA trainer initialized (sd-scripts %s)",
"ready" if lora_trainer.sd_scripts_installed else "not installed — install via UI")
# Initialize RunPod cloud trainer if API key is set
runpod_trainer = None
runpod_provider = None
runpod_key = os.environ.get("RUNPOD_API_KEY")
runpod_endpoint_id = os.environ.get("RUNPOD_ENDPOINT_ID")
if runpod_key:
from content_engine.services.runpod_trainer import RunPodTrainer
runpod_trainer = RunPodTrainer(api_key=runpod_key)
logger.info("RunPod cloud trainer initialized — cloud LoRA training available")
# Initialize RunPod generation provider if endpoint ID is set
if runpod_endpoint_id:
from content_engine.services.cloud_providers.runpod_provider import RunPodProvider
runpod_provider = RunPodProvider(api_key=runpod_key, endpoint_id=runpod_endpoint_id)
logger.info("RunPod generation provider initialized (endpoint: %s)", runpod_endpoint_id)
else:
logger.info("RunPod endpoint not configured — set RUNPOD_ENDPOINT_ID for cloud generation")
else:
logger.info("RunPod not configured — set RUNPOD_API_KEY for cloud training/generation")
routes_training.init_routes(lora_trainer, runpod_trainer=runpod_trainer)
# Update generation routes with RunPod provider
routes_generation.set_runpod_provider(runpod_provider)
logger.info(
"Content Engine ready — %d templates, %d characters",
len(template_engine.list_templates()),
len(character_profiles),
)
yield
# Shutdown
if comfyui_client:
await comfyui_client.close()
logger.info("Content Engine stopped")
# Create the FastAPI app
app = FastAPI(
title="Content Engine",
description="Automated content generation system using ComfyUI",
version="0.1.0",
lifespan=lifespan,
)
# Register route modules
app.include_router(routes_ui.router) # UI at / (must be first)
app.include_router(routes_generation.router)
app.include_router(routes_catalog.router)
app.include_router(routes_system.router)
app.include_router(routes_training.router)
app.include_router(routes_pod.router)
app.include_router(routes_video.router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"content_engine.main:app",
host="0.0.0.0",
port=8000,
reload=True,
)
|