Commit
Β·
18dd400
1
Parent(s):
2fe635d
api endpoints for UI migration v9
Browse files- ENTRY_POINT_RESOLUTION.md +178 -0
- GRADIO_API_CONFLICT_DIAGNOSIS.md +5 -2
- app.py +14 -3
- main.py +25 -40
ENTRY_POINT_RESOLUTION.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Entry Point Conflict Resolution
|
| 2 |
+
|
| 3 |
+
## Problem Resolved
|
| 4 |
+
|
| 5 |
+
**Issue**: Both `app.py` and `main.py` were attempting to launch Gradio on port 7860, causing port conflicts.
|
| 6 |
+
|
| 7 |
+
**Root Cause**:
|
| 8 |
+
- `app.py` has a `if __name__ == "__main__":` block that launches Gradio
|
| 9 |
+
- `main.py` was also trying to create and launch its own Gradio instance
|
| 10 |
+
- Both tried to bind to port 7860 simultaneously
|
| 11 |
+
|
| 12 |
+
## Solution Implemented
|
| 13 |
+
|
| 14 |
+
### Single Entry Point Architecture
|
| 15 |
+
|
| 16 |
+
**For Hugging Face Spaces Deployment:**
|
| 17 |
+
- `main.py` is the **only entry point** (configured in `README.md` as `app_file: main.py`)
|
| 18 |
+
- `main.py` imports the interface from `app.py` without triggering its launch
|
| 19 |
+
- `main.py` handles the launch configuration
|
| 20 |
+
|
| 21 |
+
**For Local Development/Testing:**
|
| 22 |
+
- `app.py` can still be run directly (`python app.py`)
|
| 23 |
+
- When run directly, `app.py` launches with its own configuration
|
| 24 |
+
- This allows developers to test the app independently
|
| 25 |
+
|
| 26 |
+
## How It Works
|
| 27 |
+
|
| 28 |
+
### Import Behavior in Python
|
| 29 |
+
|
| 30 |
+
When `main.py` imports from `app.py`:
|
| 31 |
+
```python
|
| 32 |
+
from app import create_mobile_optimized_interface
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
- β
All top-level code in `app.py` executes (orchestrator initialization, etc.)
|
| 36 |
+
- β
The function `create_mobile_optimized_interface()` becomes available
|
| 37 |
+
- β The `if __name__ == "__main__":` block does **NOT** execute (because `__name__` != `"__main__"` when imported)
|
| 38 |
+
|
| 39 |
+
### Current Structure
|
| 40 |
+
|
| 41 |
+
```
|
| 42 |
+
app.py:
|
| 43 |
+
- Defines: create_mobile_optimized_interface() β Returns (demo, components)
|
| 44 |
+
- Has: if __name__ == "__main__": launch code β Only runs when app.py executed directly
|
| 45 |
+
|
| 46 |
+
main.py:
|
| 47 |
+
- Imports: create_mobile_optimized_interface() from app.py
|
| 48 |
+
- Calls: demo, components = create_mobile_optimized_interface()
|
| 49 |
+
- Launches: demo.launch() with HF Spaces configuration
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
## File Roles
|
| 53 |
+
|
| 54 |
+
### `app.py`
|
| 55 |
+
- **Purpose**: Core application interface and logic
|
| 56 |
+
- **When imported**: Provides interface creation function
|
| 57 |
+
- **When executed directly**: Launches app for local testing
|
| 58 |
+
- **Launch configuration**: Local-optimized (with share link for external access when not on HF Spaces)
|
| 59 |
+
|
| 60 |
+
### `main.py`
|
| 61 |
+
- **Purpose**: HF Spaces entry point
|
| 62 |
+
- **When executed**: Imports from app.py and launches
|
| 63 |
+
- **Launch configuration**: HF Spaces-optimized (detects environment, adjusts share setting)
|
| 64 |
+
- **HF Spaces requirement**: Must be specified as `app_file: main.py` in README.md
|
| 65 |
+
|
| 66 |
+
## Configuration
|
| 67 |
+
|
| 68 |
+
### Hugging Face Spaces Setup
|
| 69 |
+
|
| 70 |
+
In `README.md`:
|
| 71 |
+
```yaml
|
| 72 |
+
sdk: gradio
|
| 73 |
+
app_file: main.py # β This tells HF Spaces to use main.py
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
### Environment Detection
|
| 77 |
+
|
| 78 |
+
Both files automatically detect if running on HF Spaces:
|
| 79 |
+
|
| 80 |
+
```python
|
| 81 |
+
try:
|
| 82 |
+
from spaces import GPU
|
| 83 |
+
is_hf_spaces = True
|
| 84 |
+
except ImportError:
|
| 85 |
+
is_hf_spaces = False
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
**On HF Spaces:**
|
| 89 |
+
- `share=False` (HF Spaces provides its own URL)
|
| 90 |
+
- No warnings about share being unsupported
|
| 91 |
+
|
| 92 |
+
**Locally:**
|
| 93 |
+
- `share=True` (creates Gradio public link for external access)
|
| 94 |
+
|
| 95 |
+
## Benefits
|
| 96 |
+
|
| 97 |
+
1. β
**No Port Conflicts**: Only one Gradio instance launches
|
| 98 |
+
2. β
**Clean Separation**: Interface logic (app.py) vs. Launch logic (main.py)
|
| 99 |
+
3. β
**Flexible Testing**: Can test app.py independently
|
| 100 |
+
4. β
**HF Spaces Compatible**: Follows HF Spaces deployment patterns
|
| 101 |
+
5. β
**Environment Aware**: Automatically adjusts configuration based on deployment environment
|
| 102 |
+
|
| 103 |
+
## Verification
|
| 104 |
+
|
| 105 |
+
### Check That Only One Instance Launches
|
| 106 |
+
|
| 107 |
+
When running on HF Spaces:
|
| 108 |
+
```bash
|
| 109 |
+
# Should see only one launch message:
|
| 110 |
+
"β
Application ready for launch"
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
### Check Port Binding
|
| 114 |
+
|
| 115 |
+
```bash
|
| 116 |
+
# Should only see one process on port 7860
|
| 117 |
+
netstat -ano | findstr :7860 # Windows
|
| 118 |
+
lsof -i :7860 # Linux/Mac
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
### Check Logs
|
| 122 |
+
|
| 123 |
+
**When main.py is entry point (HF Spaces):**
|
| 124 |
+
```
|
| 125 |
+
π Starting AI Research Assistant MVP
|
| 126 |
+
Creating interface from app.py (all handlers already initialized)...
|
| 127 |
+
β Interface created with all API endpoints and handlers
|
| 128 |
+
β Detected Hugging Face Spaces - using built-in public URL
|
| 129 |
+
β
Application ready for launch
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
**When app.py is executed directly (local):**
|
| 133 |
+
```
|
| 134 |
+
STARTING APP (direct execution)
|
| 135 |
+
β Interface created
|
| 136 |
+
LAUNCHING GRADIO APP
|
| 137 |
+
Creating public Gradio share link for external access... (if not on HF Spaces)
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
## Troubleshooting
|
| 141 |
+
|
| 142 |
+
### Issue: Port Still in Use
|
| 143 |
+
|
| 144 |
+
**Check:**
|
| 145 |
+
1. Only one entry point is being executed
|
| 146 |
+
2. No other Python processes using port 7860
|
| 147 |
+
3. Previous Gradio instance was properly terminated
|
| 148 |
+
|
| 149 |
+
**Solution:**
|
| 150 |
+
```bash
|
| 151 |
+
# Kill any existing Python processes
|
| 152 |
+
taskkill /IM python.exe /F # Windows
|
| 153 |
+
pkill -f python # Linux/Mac
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
### Issue: app.py Launching When Imported
|
| 157 |
+
|
| 158 |
+
**Check:** Verify `if __name__ == "__main__":` block is present and correctly indented
|
| 159 |
+
|
| 160 |
+
**Solution:** This should never happen - Python's import mechanism prevents it. If it does, check for syntax errors or circular imports.
|
| 161 |
+
|
| 162 |
+
### Issue: API Endpoints Not Available
|
| 163 |
+
|
| 164 |
+
**Check:**
|
| 165 |
+
1. `show_api=True` is set in launch configuration
|
| 166 |
+
2. API endpoints are registered in app.py (before import)
|
| 167 |
+
3. Both files use the same demo instance
|
| 168 |
+
|
| 169 |
+
**Solution:** The demo instance is created in app.py and used by main.py, so endpoints should be available. Verify logs show "β API endpoint registered" messages.
|
| 170 |
+
|
| 171 |
+
## Summary
|
| 172 |
+
|
| 173 |
+
β
**Conflict resolved**: Single entry point architecture
|
| 174 |
+
β
**Port conflict eliminated**: Only main.py launches Gradio
|
| 175 |
+
β
**Environment aware**: Automatically detects HF Spaces vs. local
|
| 176 |
+
β
**Flexible**: Can still run app.py directly for testing
|
| 177 |
+
β
**Production ready**: Follows HF Spaces deployment best practices
|
| 178 |
+
|
GRADIO_API_CONFLICT_DIAGNOSIS.md
CHANGED
|
@@ -213,7 +213,8 @@ demo.launch(
|
|
| 213 |
**Common HF Spaces Issues:**
|
| 214 |
- Port 7860 is required (cannot be changed on HF Spaces)
|
| 215 |
- `server_name="0.0.0.0"` is correct
|
| 216 |
-
- `share=True`
|
|
|
|
| 217 |
|
| 218 |
---
|
| 219 |
|
|
@@ -317,10 +318,12 @@ python verify_api_endpoint.py http://localhost:7860
|
|
| 317 |
### Current Launch Configuration (app.py:2109-2116)
|
| 318 |
|
| 319 |
```python
|
|
|
|
|
|
|
| 320 |
demo.launch(
|
| 321 |
server_name="0.0.0.0", # Accept connections from all interfaces
|
| 322 |
server_port=7860, # Default Gradio port
|
| 323 |
-
share=
|
| 324 |
show_api=True, # Enable API endpoints
|
| 325 |
allowed_paths=[], # Security: don't serve files
|
| 326 |
blocked_paths=["/tmp", "/var", "/etc", "/home"] # Block sensitive paths
|
|
|
|
| 213 |
**Common HF Spaces Issues:**
|
| 214 |
- Port 7860 is required (cannot be changed on HF Spaces)
|
| 215 |
- `server_name="0.0.0.0"` is correct
|
| 216 |
+
- `share=True` is automatically disabled on HF Spaces (they provide their own public URL)
|
| 217 |
+
- The code automatically detects HF Spaces and disables share to prevent warnings
|
| 218 |
|
| 219 |
---
|
| 220 |
|
|
|
|
| 318 |
### Current Launch Configuration (app.py:2109-2116)
|
| 319 |
|
| 320 |
```python
|
| 321 |
+
# Automatically detects HF Spaces and adjusts share setting
|
| 322 |
+
use_share = not SPACES_GPU_AVAILABLE # False on HF Spaces, True locally
|
| 323 |
demo.launch(
|
| 324 |
server_name="0.0.0.0", # Accept connections from all interfaces
|
| 325 |
server_port=7860, # Default Gradio port
|
| 326 |
+
share=use_share, # Auto: True locally, False on HF Spaces
|
| 327 |
show_api=True, # Enable API endpoints
|
| 328 |
allowed_paths=[], # Security: don't serve files
|
| 329 |
blocked_paths=["/tmp", "/var", "/etc", "/home"] # Block sensitive paths
|
app.py
CHANGED
|
@@ -2065,9 +2065,12 @@ def initialize_orchestrator():
|
|
| 2065 |
# Try to initialize orchestrator
|
| 2066 |
initialize_orchestrator()
|
| 2067 |
|
|
|
|
|
|
|
|
|
|
| 2068 |
if __name__ == "__main__":
|
| 2069 |
logger.info("=" * 60)
|
| 2070 |
-
logger.info("STARTING APP")
|
| 2071 |
logger.info("=" * 60)
|
| 2072 |
|
| 2073 |
demo, components = create_mobile_optimized_interface()
|
|
@@ -2106,11 +2109,19 @@ if __name__ == "__main__":
|
|
| 2106 |
logger.info("LAUNCHING GRADIO APP")
|
| 2107 |
logger.info("=" * 60)
|
| 2108 |
logger.info("API endpoints available at: http://<host>:7860/api/")
|
| 2109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2110 |
demo.launch(
|
| 2111 |
server_name="0.0.0.0",
|
| 2112 |
server_port=7860,
|
| 2113 |
-
share=
|
| 2114 |
show_api=True, # Explicitly enable API (recommended for API access)
|
| 2115 |
allowed_paths=[], # Empty list = don't serve any files (prevents file URLs)
|
| 2116 |
blocked_paths=["/tmp", "/var", "/etc", "/home"] # Explicitly block sensitive paths (extra security)
|
|
|
|
| 2065 |
# Try to initialize orchestrator
|
| 2066 |
initialize_orchestrator()
|
| 2067 |
|
| 2068 |
+
# Entry point - can be run directly or imported by main.py
|
| 2069 |
+
# When imported, only the interface creation runs (no launch)
|
| 2070 |
+
# When run directly, the full app launches (for local testing)
|
| 2071 |
if __name__ == "__main__":
|
| 2072 |
logger.info("=" * 60)
|
| 2073 |
+
logger.info("STARTING APP (direct execution)")
|
| 2074 |
logger.info("=" * 60)
|
| 2075 |
|
| 2076 |
demo, components = create_mobile_optimized_interface()
|
|
|
|
| 2109 |
logger.info("LAUNCHING GRADIO APP")
|
| 2110 |
logger.info("=" * 60)
|
| 2111 |
logger.info("API endpoints available at: http://<host>:7860/api/")
|
| 2112 |
+
|
| 2113 |
+
# Determine if we should create a share link
|
| 2114 |
+
# HF Spaces provides its own public URL, so share is not needed there
|
| 2115 |
+
use_share = not SPACES_GPU_AVAILABLE
|
| 2116 |
+
if use_share:
|
| 2117 |
+
logger.info("Creating public Gradio share link for external access...")
|
| 2118 |
+
else:
|
| 2119 |
+
logger.info("Running on HF Spaces - using built-in public URL (share disabled)")
|
| 2120 |
+
|
| 2121 |
demo.launch(
|
| 2122 |
server_name="0.0.0.0",
|
| 2123 |
server_port=7860,
|
| 2124 |
+
share=use_share, # Only create share link when not on HF Spaces
|
| 2125 |
show_api=True, # Explicitly enable API (recommended for API access)
|
| 2126 |
allowed_paths=[], # Empty list = don't serve any files (prevents file URLs)
|
| 2127 |
blocked_paths=["/tmp", "/var", "/etc", "/home"] # Explicitly block sensitive paths (extra security)
|
main.py
CHANGED
|
@@ -120,63 +120,48 @@ def create_event_handlers(demo, components):
|
|
| 120 |
|
| 121 |
return get_response_handler
|
| 122 |
|
| 123 |
-
def setup_application():
|
| 124 |
-
"""Setup and return the Gradio application"""
|
| 125 |
-
logger.info("Starting application setup...")
|
| 126 |
-
|
| 127 |
-
# Initialize components
|
| 128 |
-
components = initialize_components()
|
| 129 |
-
|
| 130 |
-
# Create the interface
|
| 131 |
-
logger.info("Creating mobile-optimized interface...")
|
| 132 |
-
demo = create_mobile_optimized_interface()
|
| 133 |
-
|
| 134 |
-
# Setup event handlers
|
| 135 |
-
logger.info("Setting up event handlers...")
|
| 136 |
-
|
| 137 |
-
# For now, use a simple chat interface until full integration is ready
|
| 138 |
-
try:
|
| 139 |
-
# Get the chat function from the demo
|
| 140 |
-
chat_interface = demo.get_blocks().children[0] # Get first component
|
| 141 |
-
|
| 142 |
-
# Simple message handling for MVP
|
| 143 |
-
def simple_chat_fn(message, history):
|
| 144 |
-
if components.get('mock_mode'):
|
| 145 |
-
return f"I'm running in mock mode. You said: {message}"
|
| 146 |
-
else:
|
| 147 |
-
return f"System is processing: {message}"
|
| 148 |
-
|
| 149 |
-
# Set the chat function
|
| 150 |
-
if hasattr(chat_interface, 'chat_fn'):
|
| 151 |
-
chat_interface.chat_fn = simple_chat_fn
|
| 152 |
-
|
| 153 |
-
except Exception as e:
|
| 154 |
-
logger.warning(f"Could not setup advanced handlers: {e}")
|
| 155 |
-
|
| 156 |
-
logger.info("Application setup completed")
|
| 157 |
-
return demo
|
| 158 |
-
|
| 159 |
def main():
|
| 160 |
"""Main entry point for HF Spaces"""
|
| 161 |
logger.info("π Starting AI Research Assistant MVP")
|
|
|
|
| 162 |
|
| 163 |
# Check for HF Token
|
| 164 |
hf_token = os.getenv('HF_TOKEN')
|
| 165 |
if not hf_token:
|
| 166 |
logger.warning("HF_TOKEN not found in environment. Some features may be limited.")
|
| 167 |
|
| 168 |
-
#
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
|
| 171 |
-
# Launch configuration
|
|
|
|
|
|
|
| 172 |
launch_config = {
|
| 173 |
'server_name': '0.0.0.0',
|
| 174 |
'server_port': 7860,
|
| 175 |
-
'share':
|
|
|
|
| 176 |
'debug': False
|
| 177 |
}
|
| 178 |
|
|
|
|
| 179 |
logger.info("β
Application ready for launch")
|
|
|
|
| 180 |
return demo.launch(**launch_config)
|
| 181 |
|
| 182 |
if __name__ == "__main__":
|
|
|
|
| 120 |
|
| 121 |
return get_response_handler
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
def main():
|
| 124 |
"""Main entry point for HF Spaces"""
|
| 125 |
logger.info("π Starting AI Research Assistant MVP")
|
| 126 |
+
logger.info("=" * 60)
|
| 127 |
|
| 128 |
# Check for HF Token
|
| 129 |
hf_token = os.getenv('HF_TOKEN')
|
| 130 |
if not hf_token:
|
| 131 |
logger.warning("HF_TOKEN not found in environment. Some features may be limited.")
|
| 132 |
|
| 133 |
+
# Import the already-configured interface from app.py
|
| 134 |
+
# This imports the interface without triggering app.py's launch code
|
| 135 |
+
# (app.py's launch only runs when app.py is executed directly, not when imported)
|
| 136 |
+
logger.info("Creating interface from app.py (all handlers already initialized)...")
|
| 137 |
+
demo, components = create_mobile_optimized_interface()
|
| 138 |
+
|
| 139 |
+
logger.info("β Interface created with all API endpoints and handlers")
|
| 140 |
+
|
| 141 |
+
# Detect if running on HF Spaces
|
| 142 |
+
# HF Spaces provides its own public URL, so share is not needed
|
| 143 |
+
try:
|
| 144 |
+
from spaces import GPU
|
| 145 |
+
is_hf_spaces = True
|
| 146 |
+
logger.info("β Detected Hugging Face Spaces - using built-in public URL")
|
| 147 |
+
except ImportError:
|
| 148 |
+
is_hf_spaces = False
|
| 149 |
+
logger.info("β Local deployment - will create Gradio share link if needed")
|
| 150 |
|
| 151 |
+
# Launch configuration
|
| 152 |
+
# Note: show_api=True is already set in app.py's interface creation,
|
| 153 |
+
# but we ensure it here as well for clarity
|
| 154 |
launch_config = {
|
| 155 |
'server_name': '0.0.0.0',
|
| 156 |
'server_port': 7860,
|
| 157 |
+
'share': not is_hf_spaces, # Only create share link when not on HF Spaces
|
| 158 |
+
'show_api': True, # Enable API endpoints (also set in app.py, but explicit here)
|
| 159 |
'debug': False
|
| 160 |
}
|
| 161 |
|
| 162 |
+
logger.info("=" * 60)
|
| 163 |
logger.info("β
Application ready for launch")
|
| 164 |
+
logger.info("=" * 60)
|
| 165 |
return demo.launch(**launch_config)
|
| 166 |
|
| 167 |
if __name__ == "__main__":
|