knnn11 commited on
Commit
e00a97b
·
verified ·
1 Parent(s): 6611234

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -184
app.py CHANGED
@@ -1,191 +1,17 @@
1
- #!/usr/bin/env python3
2
- import os
3
- import io
4
- import logging
5
- import hashlib
6
- from contextlib import asynccontextmanager
7
- from typing import Optional
8
- from fastapi import FastAPI, HTTPException, status
9
- from fastapi.responses import FileResponse, JSONResponse
10
- from fastapi.middleware.cors import CORSMiddleware
11
- from pydantic import BaseModel, Field
12
- from PIL import Image
13
- import requests
14
- import aiohttp
15
 
16
- # --- Configuration ---
17
- class Config:
18
- OUTPUT_DIR = os.path.join(os.getcwd(), "comic_outputs")
19
- RUNPOD_ENDPOINT = os.getenv("RUNPOD_ENDPOINT", "http://localhost:8001")
20
- RUNPOD_API_KEY = os.getenv("RUNPOD_API_KEY", "")
21
- MAX_RETRIES = 3
22
- TIMEOUT = 30
23
 
24
- # --- Logging Setup ---
25
- logging.basicConfig(
26
- level=logging.INFO,
27
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
28
- handlers=[
29
- logging.StreamHandler(),
30
- logging.FileHandler("comic_maker.log")
31
- ]
32
- )
33
- logger = logging.getLogger(__name__)
34
 
35
- # --- Data Models ---
36
- class GenerationRequest(BaseModel):
37
- prompt: str = Field(..., min_length=3, max_length=500)
38
- steps: Optional[int] = Field(30, ge=10, le=100)
39
- width: Optional[int] = Field(768, ge=256, le=1024)
40
- height: Optional[int] = Field(768, ge=256, le=1024)
41
- seed: Optional[int] = Field(None, ge=0)
42
 
43
- # --- Application Setup ---
44
- @asynccontextmanager
45
- async def lifespan(app: FastAPI):
46
- """Modern lifespan handler with resource management"""
47
- # Startup
48
- os.makedirs(Config.OUTPUT_DIR, exist_ok=True)
49
- logger.info(f"🚀 Starting AI Comic Maker API")
50
- logger.info(f"📁 Output directory: {Config.OUTPUT_DIR}")
51
- logger.info(f"🔗 RunPod endpoint: {Config.RUNPOD_ENDPOINT}")
52
-
53
- # Create aiohttp session for async requests
54
- app.state.http_session = aiohttp.ClientSession()
55
-
56
- yield
57
-
58
- # Shutdown
59
- await app.state.http_session.close()
60
- logger.info("🛑 Graceful shutdown complete")
61
 
62
- app = FastAPI(
63
- title="AI Comic Maker API",
64
- description="Generate comic art using Stable Diffusion",
65
- version="1.0.0",
66
- lifespan=lifespan
67
- )
68
-
69
- # CORS Middleware
70
- app.add_middleware(
71
- CORSMiddleware,
72
- allow_origins=["*"],
73
- allow_methods=["*"],
74
- allow_headers=["*"],
75
- )
76
-
77
- # --- Helper Functions ---
78
- def sanitize_filename(prompt: str) -> str:
79
- """Create safe filename from prompt"""
80
- prompt_hash = hashlib.md5(prompt.encode()).hexdigest()
81
- return f"comic_{prompt_hash}.png"
82
-
83
- async def generate_image(payload: dict) -> bytes:
84
- """Call RunPod API with retry logic"""
85
- headers = {"Authorization": f"Bearer {Config.RUNPOD_API_KEY}"} if Config.RUNPOD_API_KEY else {}
86
-
87
- for attempt in range(Config.MAX_RETRIES):
88
- try:
89
- async with app.state.http_session.post(
90
- f"{Config.RUNPOD_ENDPOINT}/generate",
91
- headers=headers,
92
- json={"input": payload},
93
- timeout=Config.TIMEOUT
94
- ) as response:
95
- response.raise_for_status()
96
- return await response.read()
97
-
98
- except Exception as e:
99
- if attempt == Config.MAX_RETRIES - 1:
100
- raise
101
- logger.warning(f"Retry {attempt + 1} for generation request")
102
- await asyncio.sleep(1)
103
-
104
- # --- API Endpoints ---
105
- @app.get("/health", tags=["monitoring"])
106
- async def health_check():
107
- """Service health endpoint"""
108
- return JSONResponse(
109
- content={
110
- "status": "healthy",
111
- "version": app.version,
112
- "environment": os.getenv("ENVIRONMENT", "development")
113
- }
114
- )
115
-
116
- @app.post(
117
- "/generate",
118
- response_class=FileResponse,
119
- status_code=status.HTTP_200_OK,
120
- tags=["generation"]
121
- )
122
- async def generate_comic(request: GenerationRequest):
123
- """
124
- Generate comic art from text prompt
125
-
126
- - **prompt**: Description of desired comic (3-500 chars)
127
- - **steps**: Generation steps (10-100, default 30)
128
- - **width**: Image width (256-1024, default 768)
129
- - **height**: Image height (256-1024, default 768)
130
- - **seed**: Optional random seed
131
- """
132
- try:
133
- logger.info(f"🎨 Generating comic: '{request.prompt[:50]}...'")
134
-
135
- # Prepare payload
136
- payload = {
137
- "prompt": request.prompt,
138
- "steps": request.steps,
139
- "width": request.width,
140
- "height": request.height,
141
- "seed": request.seed
142
- }
143
-
144
- # Generate image
145
- image_data = await generate_image(payload)
146
-
147
- # Save and return
148
- output_filename = sanitize_filename(request.prompt)
149
- output_path = os.path.join(Config.OUTPUT_DIR, output_filename)
150
-
151
- with Image.open(io.BytesIO(image_data)) as img:
152
- img.save(output_path)
153
-
154
- logger.info(f"✅ Saved comic to {output_path}")
155
- return FileResponse(
156
- output_path,
157
- media_type="image/png",
158
- filename=output_filename
159
- )
160
-
161
- except aiohttp.ClientError as e:
162
- logger.error(f"🚨 Network error: {str(e)}")
163
- raise HTTPException(
164
- status_code=status.HTTP_502_BAD_GATEWAY,
165
- detail="Generation service unavailable"
166
- )
167
- except Exception as e:
168
- logger.error(f"🔥 Generation failed: {str(e)}")
169
- raise HTTPException(
170
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
171
- detail="Comic generation failed"
172
- )
173
-
174
- # --- Main Entry Point ---
175
- if __name__ == "__main__":
176
- import uvicorn
177
- import asyncio
178
-
179
- uvicorn.run(
180
- app,
181
- host="0.0.0.0",
182
- port=int(os.getenv("PORT", "8000")),
183
- log_level="info",
184
- reload=False,
185
- timeout_keep_alive=30
186
- )
187
-
188
- # At the bottom of app.py
189
  if __name__ == "__main__":
190
  import uvicorn
191
- uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))
 
1
+ from fastapi import FastAPI
2
+ import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ app = FastAPI()
 
 
 
 
 
 
5
 
6
+ @app.get("/health")
7
+ def health_check():
8
+ return {"status": "ok"}
 
 
 
 
 
 
 
9
 
10
+ # Basic Gradio interface
11
+ demo = gr.Interface(lambda x: x, "text", "text")
 
 
 
 
 
12
 
13
+ app = gr.mount_gradio_app(app, demo, path="/")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  if __name__ == "__main__":
16
  import uvicorn
17
+ uvicorn.run(app, host="0.0.0.0", port=7860)