Transcendental-Programmer
commited on
Commit
·
0f25023
1
Parent(s):
a8da4e0
fix: localhost error
Browse files- Dockerfile +3 -1
- README.md +41 -11
- app.py +46 -14
- faceforge_api/main.py +8 -2
- faceforge_ui/app.py +25 -3
- main.py +39 -11
Dockerfile
CHANGED
|
@@ -21,6 +21,8 @@ EXPOSE 7860
|
|
| 21 |
# Set environment variables
|
| 22 |
ENV PYTHONPATH="/app"
|
| 23 |
ENV PYTHONUNBUFFERED=1
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# Start app (with the patch applied)
|
| 26 |
-
CMD ["python", "
|
|
|
|
| 21 |
# Set environment variables
|
| 22 |
ENV PYTHONPATH="/app"
|
| 23 |
ENV PYTHONUNBUFFERED=1
|
| 24 |
+
ENV API_URL="/api"
|
| 25 |
+
ENV MOCK_API="false"
|
| 26 |
|
| 27 |
# Start app (with the patch applied)
|
| 28 |
+
CMD ["python", "app.py"]
|
README.md
CHANGED
|
@@ -5,7 +5,7 @@ colorFrom: indigo
|
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: "4.44.1"
|
| 8 |
-
app_file:
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
|
@@ -19,7 +19,7 @@ FaceForge is ready to run as a Gradio app on [Hugging Face Spaces](https://huggi
|
|
| 19 |
1. **Push your code to a public GitHub repository.**
|
| 20 |
2. **Create a new Space** at https://huggingface.co/spaces (choose the Gradio SDK or Docker SDK).
|
| 21 |
3. **Add your `requirements.txt` and the provided `Dockerfile` to your repo.**
|
| 22 |
-
4. **Set the entrypoint to `
|
| 23 |
5. **Deploy!** Your app will be live at `https://<your-username>.hf.space`.
|
| 24 |
|
| 25 |
### Example Dockerfile (already included):
|
|
@@ -45,10 +45,21 @@ pip install -r requirements.txt
|
|
| 45 |
python main.py
|
| 46 |
```
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
## Features
|
| 54 |
- Latent space exploration and manipulation
|
|
@@ -78,11 +89,30 @@ TypeError: argument of type 'bool' is not iterable
|
|
| 78 |
|
| 79 |
The application includes a patch that should fix the issue automatically. This patch addresses a known issue with schema processing in older Gradio versions.
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
## Notes
|
| 87 |
-
- The backend and frontend are fully integrated for Spaces.
|
| 88 |
- For custom model integration, edit the core and backend modules as needed.
|
|
|
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: "4.44.1"
|
| 8 |
+
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
|
|
|
| 19 |
1. **Push your code to a public GitHub repository.**
|
| 20 |
2. **Create a new Space** at https://huggingface.co/spaces (choose the Gradio SDK or Docker SDK).
|
| 21 |
3. **Add your `requirements.txt` and the provided `Dockerfile` to your repo.**
|
| 22 |
+
4. **Set the entrypoint to `app.py`** (which integrates both the API and UI components).
|
| 23 |
5. **Deploy!** Your app will be live at `https://<your-username>.hf.space`.
|
| 24 |
|
| 25 |
### Example Dockerfile (already included):
|
|
|
|
| 45 |
python main.py
|
| 46 |
```
|
| 47 |
|
| 48 |
+
This will start the integrated application with both the API and UI components available:
|
| 49 |
+
- UI accessible at http://localhost:7860/
|
| 50 |
+
- API accessible at http://localhost:7860/api/
|
| 51 |
+
|
| 52 |
+
## Architecture
|
| 53 |
+
|
| 54 |
+
FaceForge uses a modular architecture:
|
| 55 |
+
|
| 56 |
+
1. **Core Components** (`faceforge_core/`): Core algorithms and utilities
|
| 57 |
+
2. **API Layer** (`faceforge_api/`): FastAPI endpoints for model interaction
|
| 58 |
+
3. **UI Layer** (`faceforge_ui/`): Gradio interface for user interaction
|
| 59 |
+
|
| 60 |
+
The main application integrates these components into a single FastAPI application where:
|
| 61 |
+
- The API is mounted at `/api/`
|
| 62 |
+
- The Gradio UI is mounted at the root path `/`
|
| 63 |
|
| 64 |
## Features
|
| 65 |
- Latent space exploration and manipulation
|
|
|
|
| 89 |
|
| 90 |
The application includes a patch that should fix the issue automatically. This patch addresses a known issue with schema processing in older Gradio versions.
|
| 91 |
|
| 92 |
+
### Common Issues:
|
| 93 |
+
|
| 94 |
+
#### API Connection Errors
|
| 95 |
+
|
| 96 |
+
If you see errors like:
|
| 97 |
+
```
|
| 98 |
+
Request failed: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /generate
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
This means the UI can't connect to the API. In the integrated version, the API is available at `/api/generate` rather than a separate server.
|
| 102 |
+
|
| 103 |
+
To fix this:
|
| 104 |
+
1. Ensure you're using the integrated version by running `python main.py`
|
| 105 |
+
2. If you need to run the API separately, set the API_URL environment variable:
|
| 106 |
+
```bash
|
| 107 |
+
API_URL=http://localhost:8000 python faceforge_ui/app.py
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
#### Environment Variables
|
| 111 |
+
|
| 112 |
+
- `MOCK_API`: Set to "true" to use mock API responses (for testing without API)
|
| 113 |
+
- `API_URL`: Override the API endpoint URL
|
| 114 |
+
- `PORT`: Set the port for the server (default: 7860)
|
| 115 |
|
| 116 |
## Notes
|
| 117 |
+
- The backend and frontend are fully integrated for Spaces deployment.
|
| 118 |
- For custom model integration, edit the core and backend modules as needed.
|
app.py
CHANGED
|
@@ -17,8 +17,8 @@ logging.basicConfig(
|
|
| 17 |
|
| 18 |
logger = logging.getLogger("faceforge")
|
| 19 |
|
| 20 |
-
def
|
| 21 |
-
"""
|
| 22 |
try:
|
| 23 |
# Apply the patch for Gradio
|
| 24 |
logger.info("Applying Gradio patch...")
|
|
@@ -32,21 +32,53 @@ def main():
|
|
| 32 |
logger.warning(f"Error applying Gradio patch: {e}")
|
| 33 |
logger.debug(traceback.format_exc())
|
| 34 |
|
| 35 |
-
#
|
| 36 |
-
logger.info("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
from faceforge_ui.app import create_demo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
demo = create_demo()
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
except Exception as e:
|
| 46 |
-
logger.critical(f"
|
| 47 |
logger.debug(traceback.format_exc())
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
# This module is imported by Hugging Face Spaces
|
| 51 |
if __name__ == "__main__":
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
logger = logging.getLogger("faceforge")
|
| 19 |
|
| 20 |
+
def create_app():
|
| 21 |
+
"""Creates and configures the integrated FastAPI application with both API and UI components."""
|
| 22 |
try:
|
| 23 |
# Apply the patch for Gradio
|
| 24 |
logger.info("Applying Gradio patch...")
|
|
|
|
| 32 |
logger.warning(f"Error applying Gradio patch: {e}")
|
| 33 |
logger.debug(traceback.format_exc())
|
| 34 |
|
| 35 |
+
# Set up FastAPI application with both API and UI
|
| 36 |
+
logger.info("Setting up FastAPI application with API and UI for Hugging Face Spaces")
|
| 37 |
+
from fastapi import FastAPI
|
| 38 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 39 |
+
import gradio as gr
|
| 40 |
+
|
| 41 |
+
# Import the API and UI components
|
| 42 |
+
from faceforge_api.main import app as api_app
|
| 43 |
from faceforge_ui.app import create_demo
|
| 44 |
+
|
| 45 |
+
# Create a new FastAPI application that will serve as the main app
|
| 46 |
+
app = FastAPI(title="FaceForge")
|
| 47 |
+
|
| 48 |
+
# Add CORS middleware
|
| 49 |
+
app.add_middleware(
|
| 50 |
+
CORSMiddleware,
|
| 51 |
+
allow_origins=["*"],
|
| 52 |
+
allow_credentials=True,
|
| 53 |
+
allow_methods=["*"],
|
| 54 |
+
allow_headers=["*"],
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
# Mount the API under /api
|
| 58 |
+
logger.info("Mounting API at /api")
|
| 59 |
+
app.mount("/api", api_app)
|
| 60 |
+
|
| 61 |
+
# Create Gradio UI
|
| 62 |
+
logger.info("Creating Gradio UI")
|
| 63 |
demo = create_demo()
|
| 64 |
+
|
| 65 |
+
# Mount Gradio UI
|
| 66 |
+
logger.info("Mounting Gradio UI")
|
| 67 |
+
gr_app = gr.mount_gradio_app(app, demo, path="/")
|
| 68 |
+
|
| 69 |
+
return app
|
| 70 |
except Exception as e:
|
| 71 |
+
logger.critical(f"Failed to create app: {e}")
|
| 72 |
logger.debug(traceback.format_exc())
|
| 73 |
+
raise
|
| 74 |
+
|
| 75 |
+
# Create the app for Hugging Face Spaces
|
| 76 |
+
# This is the entry point that Hugging Face Spaces will use
|
| 77 |
+
app = create_app()
|
| 78 |
|
|
|
|
| 79 |
if __name__ == "__main__":
|
| 80 |
+
# If this file is run directly, start the server
|
| 81 |
+
import uvicorn
|
| 82 |
+
port = int(os.environ.get("PORT", 7860))
|
| 83 |
+
logger.info(f"Starting integrated server on port {port}")
|
| 84 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
faceforge_api/main.py
CHANGED
|
@@ -49,7 +49,13 @@ class AttributeDirectionRequest(BaseModel):
|
|
| 49 |
|
| 50 |
# --- FastAPI app ---
|
| 51 |
|
| 52 |
-
app = FastAPI(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
# Add CORS middleware to allow requests from any origin
|
| 55 |
app.add_middleware(
|
|
@@ -78,7 +84,7 @@ async def error_handling_middleware(request: Request, call_next):
|
|
| 78 |
|
| 79 |
@app.get("/")
|
| 80 |
def read_root():
|
| 81 |
-
logger.debug("
|
| 82 |
return {"message": "FaceForge API is running"}
|
| 83 |
|
| 84 |
@app.post("/generate")
|
|
|
|
| 49 |
|
| 50 |
# --- FastAPI app ---
|
| 51 |
|
| 52 |
+
app = FastAPI(
|
| 53 |
+
title="FaceForge API",
|
| 54 |
+
description="API for latent space exploration and manipulation",
|
| 55 |
+
version="1.0.0",
|
| 56 |
+
# Important: set root_path to empty to ensure routes work correctly when mounted under /api
|
| 57 |
+
root_path=""
|
| 58 |
+
)
|
| 59 |
|
| 60 |
# Add CORS middleware to allow requests from any origin
|
| 61 |
app.add_middleware(
|
|
|
|
| 84 |
|
| 85 |
@app.get("/")
|
| 86 |
def read_root():
|
| 87 |
+
logger.debug("API root endpoint called")
|
| 88 |
return {"message": "FaceForge API is running"}
|
| 89 |
|
| 90 |
@app.post("/generate")
|
faceforge_ui/app.py
CHANGED
|
@@ -23,7 +23,9 @@ logging.getLogger("gradio").setLevel(logging.DEBUG)
|
|
| 23 |
logging.getLogger("gradio_client").setLevel(logging.DEBUG)
|
| 24 |
|
| 25 |
# API configuration
|
| 26 |
-
|
|
|
|
|
|
|
| 27 |
logger.info(f"Using API URL: {API_URL}")
|
| 28 |
|
| 29 |
def generate_image(prompts, mode, player_x, player_y):
|
|
@@ -48,7 +50,24 @@ def generate_image(prompts, mode, player_x, player_y):
|
|
| 48 |
|
| 49 |
# Make API call
|
| 50 |
try:
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
logger.debug(f"API response status: {resp.status_code}")
|
| 53 |
|
| 54 |
if resp.ok:
|
|
@@ -89,7 +108,10 @@ def generate_image(prompts, mode, player_x, player_y):
|
|
| 89 |
|
| 90 |
except requests.exceptions.RequestException as e:
|
| 91 |
logger.error(f"Request failed: {e}")
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
except Exception as e:
|
| 95 |
logger.error(f"Unexpected error: {e}")
|
|
|
|
| 23 |
logging.getLogger("gradio_client").setLevel(logging.DEBUG)
|
| 24 |
|
| 25 |
# API configuration
|
| 26 |
+
# In HF Spaces, we need to use a relative path since both UI and API run on the same server
|
| 27 |
+
# For local development with separate servers, the env var can be set to http://localhost:8000
|
| 28 |
+
API_URL = os.environ.get("API_URL", "/api")
|
| 29 |
logger.info(f"Using API URL: {API_URL}")
|
| 30 |
|
| 31 |
def generate_image(prompts, mode, player_x, player_y):
|
|
|
|
| 50 |
|
| 51 |
# Make API call
|
| 52 |
try:
|
| 53 |
+
# For debugging/testing, create a mock image if API is not available
|
| 54 |
+
if API_URL == "/api" and os.environ.get("MOCK_API", "false").lower() == "true":
|
| 55 |
+
logger.debug("Using mock API response")
|
| 56 |
+
# Create a test image
|
| 57 |
+
img = Image.new("RGB", (256, 256), (int(player_x*128)+128, 100, int(player_y*128)+128))
|
| 58 |
+
return img, "Image generated using mock API"
|
| 59 |
+
|
| 60 |
+
# Determine the base URL for the API
|
| 61 |
+
if API_URL.startswith("/"):
|
| 62 |
+
# Relative URL, construct the full URL based on the request context
|
| 63 |
+
# For Gradio apps, we'll just use a relative path
|
| 64 |
+
base_url = API_URL
|
| 65 |
+
else:
|
| 66 |
+
# Absolute URL, use as is
|
| 67 |
+
base_url = API_URL
|
| 68 |
+
|
| 69 |
+
logger.debug(f"Making request to: {base_url}/generate")
|
| 70 |
+
resp = requests.post(f"{base_url}/generate", json=req, timeout=30)
|
| 71 |
logger.debug(f"API response status: {resp.status_code}")
|
| 72 |
|
| 73 |
if resp.ok:
|
|
|
|
| 108 |
|
| 109 |
except requests.exceptions.RequestException as e:
|
| 110 |
logger.error(f"Request failed: {e}")
|
| 111 |
+
# Fall back to a test image
|
| 112 |
+
logger.debug("Falling back to test image")
|
| 113 |
+
img = Image.new("RGB", (256, 256), (int(player_x*128)+128, 100, int(player_y*128)+128))
|
| 114 |
+
return img, f"API connection failed (using test image): {str(e)}"
|
| 115 |
|
| 116 |
except Exception as e:
|
| 117 |
logger.error(f"Unexpected error: {e}")
|
main.py
CHANGED
|
@@ -32,17 +32,45 @@ def main():
|
|
| 32 |
logger.warning(f"Error applying Gradio patch: {e}")
|
| 33 |
logger.debug(traceback.format_exc())
|
| 34 |
|
| 35 |
-
#
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
except ImportError as e:
|
| 48 |
logger.critical(f"Import error: {e}. Please check your dependencies.")
|
|
|
|
| 32 |
logger.warning(f"Error applying Gradio patch: {e}")
|
| 33 |
logger.debug(traceback.format_exc())
|
| 34 |
|
| 35 |
+
# Set up FastAPI application with both API and UI
|
| 36 |
+
logger.info("Setting up FastAPI application with API and UI")
|
| 37 |
+
from fastapi import FastAPI
|
| 38 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 39 |
+
import gradio as gr
|
| 40 |
+
|
| 41 |
+
# Import the API and UI components
|
| 42 |
+
from faceforge_api.main import app as api_app
|
| 43 |
+
from faceforge_ui.app import create_demo
|
| 44 |
+
|
| 45 |
+
# Create a new FastAPI application that will serve as the main app
|
| 46 |
+
app = FastAPI(title="FaceForge")
|
| 47 |
+
|
| 48 |
+
# Add CORS middleware
|
| 49 |
+
app.add_middleware(
|
| 50 |
+
CORSMiddleware,
|
| 51 |
+
allow_origins=["*"],
|
| 52 |
+
allow_credentials=True,
|
| 53 |
+
allow_methods=["*"],
|
| 54 |
+
allow_headers=["*"],
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
# Mount the API under /api
|
| 58 |
+
logger.info("Mounting API at /api")
|
| 59 |
+
app.mount("/api", api_app)
|
| 60 |
+
|
| 61 |
+
# Create Gradio UI
|
| 62 |
+
logger.info("Creating Gradio UI")
|
| 63 |
+
demo = create_demo()
|
| 64 |
+
|
| 65 |
+
# Mount Gradio UI
|
| 66 |
+
logger.info("Mounting Gradio UI")
|
| 67 |
+
gr_app = gr.mount_gradio_app(app, demo, path="/")
|
| 68 |
+
|
| 69 |
+
# Configure server
|
| 70 |
+
import uvicorn
|
| 71 |
+
port = int(os.environ.get("PORT", 7860))
|
| 72 |
+
logger.info(f"Starting integrated server on port {port}")
|
| 73 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
| 74 |
|
| 75 |
except ImportError as e:
|
| 76 |
logger.critical(f"Import error: {e}. Please check your dependencies.")
|