Commit ·
220eacd
1
Parent(s): 2ff4c13
Cleanded up fastapi
Browse files- app.py +33 -61
- frontend/game_viewer.html +16 -10
app.py
CHANGED
|
@@ -35,7 +35,6 @@ BASE_URL = SPACE_URL # For display in UI
|
|
| 35 |
|
| 36 |
# Global state for current scene
|
| 37 |
current_scene_id = None
|
| 38 |
-
current_scene_url = None
|
| 39 |
selected_object_id = None # Track currently looked-at object (FPS mode)
|
| 40 |
|
| 41 |
|
|
@@ -104,29 +103,38 @@ if not IS_HF_SPACES:
|
|
| 104 |
wait_for_fastapi()
|
| 105 |
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
def create_default_scene():
|
| 108 |
"""Use the clean default Welcome Scene created on server startup"""
|
| 109 |
-
global current_scene_id
|
| 110 |
|
| 111 |
try:
|
| 112 |
-
# Use the pre-initialized "welcome" scene from storage
|
| 113 |
-
# (created in backend/storage.py on module load)
|
| 114 |
current_scene_id = "welcome"
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
print(f"✅ Using default Welcome Scene")
|
| 118 |
-
print(f" Scene ID: {current_scene_id}")
|
| 119 |
-
print(f" Viewer URL: {current_scene_url}")
|
| 120 |
-
print(f" - Clean 10x10 FPS world with physics")
|
| 121 |
-
print(f" - Ground plane + walls (created by viewer)")
|
| 122 |
-
print(f" - Player starts at (0, 1, 0)")
|
| 123 |
-
|
| 124 |
-
return current_scene_url
|
| 125 |
-
|
| 126 |
except Exception as e:
|
| 127 |
import traceback
|
| 128 |
print(f"❌ Error loading default scene: {e}")
|
| 129 |
-
print(f" Full traceback:")
|
| 130 |
traceback.print_exc()
|
| 131 |
return None
|
| 132 |
|
|
@@ -145,7 +153,7 @@ def get_gpt_client():
|
|
| 145 |
|
| 146 |
def chat_response(message, history):
|
| 147 |
"""Handle chat messages using GPT with tool calling"""
|
| 148 |
-
global current_scene_id
|
| 149 |
|
| 150 |
# Handle help command locally (no need for LLM)
|
| 151 |
if message.lower().strip() == "help":
|
|
@@ -186,9 +194,9 @@ I'm an AI assistant that can help you build 3D scenes using natural language.
|
|
| 186 |
|
| 187 |
# Create default scene on startup
|
| 188 |
print("Creating default scene...")
|
| 189 |
-
|
| 190 |
-
print(f"Default viewer
|
| 191 |
-
if not
|
| 192 |
print("⚠️ WARNING: Default scene creation failed! No viewer URL generated.")
|
| 193 |
|
| 194 |
|
|
@@ -375,12 +383,12 @@ with gr.Blocks(title="GCP - Game Context Protocol") as demo:
|
|
| 375 |
|
| 376 |
# Right column: 3D Viewer (scale=3 = ~75% width)
|
| 377 |
with gr.Column(elem_id="viewer-column", scale=3):
|
| 378 |
-
if
|
| 379 |
-
initial_viewer_html = f'<div id="viewer-container"><iframe
|
| 380 |
-
print(f"📊 Setting up viewer
|
| 381 |
else:
|
| 382 |
initial_viewer_html = '<div id="viewer-container" style="display: flex; align-items: center; justify-content: center; color: #666;"><p>⚠️ Scene failed to load. Check console logs.</p></div>'
|
| 383 |
-
print("⚠️ No viewer
|
| 384 |
|
| 385 |
viewer = gr.HTML(
|
| 386 |
value=initial_viewer_html,
|
|
@@ -506,40 +514,4 @@ with gr.Blocks(title="GCP - Game Context Protocol") as demo:
|
|
| 506 |
|
| 507 |
if __name__ == "__main__":
|
| 508 |
demo.queue()
|
| 509 |
-
|
| 510 |
-
if IS_HF_SPACES:
|
| 511 |
-
# On HF Spaces: Create a FastAPI app with our routes, then mount Gradio on it
|
| 512 |
-
from fastapi import FastAPI, HTTPException
|
| 513 |
-
from fastapi.responses import HTMLResponse
|
| 514 |
-
from backend.storage import storage
|
| 515 |
-
|
| 516 |
-
app = FastAPI()
|
| 517 |
-
|
| 518 |
-
@app.get("/api/scenes/{scene_id}")
|
| 519 |
-
async def get_scene_api(scene_id: str):
|
| 520 |
-
scene = storage.get(scene_id)
|
| 521 |
-
if not scene:
|
| 522 |
-
raise HTTPException(status_code=404, detail=f"Scene '{scene_id}' not found")
|
| 523 |
-
return scene
|
| 524 |
-
|
| 525 |
-
@app.get("/view/scene/{scene_id}")
|
| 526 |
-
async def view_scene(scene_id: str):
|
| 527 |
-
scene = storage.get(scene_id)
|
| 528 |
-
if not scene:
|
| 529 |
-
raise HTTPException(status_code=404, detail=f"Scene '{scene_id}' not found")
|
| 530 |
-
viewer_path = os.path.join(os.path.dirname(__file__), "frontend", "game_viewer.html")
|
| 531 |
-
with open(viewer_path, 'r') as f:
|
| 532 |
-
return HTMLResponse(content=f.read())
|
| 533 |
-
|
| 534 |
-
@app.get("/health")
|
| 535 |
-
async def health_check():
|
| 536 |
-
return {"status": "healthy", "service": "GCP", "version": "2.0.0"}
|
| 537 |
-
|
| 538 |
-
# Mount Gradio onto our FastAPI app
|
| 539 |
-
app = gr.mount_gradio_app(app, demo, path="/")
|
| 540 |
-
|
| 541 |
-
print("🚀 Starting on HF Spaces - FastAPI + Gradio on port 7860")
|
| 542 |
-
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 543 |
-
else:
|
| 544 |
-
# Local dev: FastAPI already running on 8000 (started above), just launch Gradio on 7860
|
| 545 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 35 |
|
| 36 |
# Global state for current scene
|
| 37 |
current_scene_id = None
|
|
|
|
| 38 |
selected_object_id = None # Track currently looked-at object (FPS mode)
|
| 39 |
|
| 40 |
|
|
|
|
| 103 |
wait_for_fastapi()
|
| 104 |
|
| 105 |
|
| 106 |
+
def get_viewer_html(scene_id="welcome"):
|
| 107 |
+
"""Generate viewer HTML with embedded scene data."""
|
| 108 |
+
from backend.storage import storage
|
| 109 |
+
|
| 110 |
+
scene_data = storage.get(scene_id)
|
| 111 |
+
if not scene_data:
|
| 112 |
+
return '<div style="color: red;">Scene not found</div>'
|
| 113 |
+
|
| 114 |
+
# Read the viewer HTML template
|
| 115 |
+
viewer_path = os.path.join(os.path.dirname(__file__), "frontend", "game_viewer.html")
|
| 116 |
+
with open(viewer_path, 'r') as f:
|
| 117 |
+
html = f.read()
|
| 118 |
+
|
| 119 |
+
# Inject scene data before the closing </head> tag
|
| 120 |
+
scene_json = json.dumps(scene_data)
|
| 121 |
+
inject_script = f'<script>window.SCENE_DATA = {scene_json};</script>'
|
| 122 |
+
html = html.replace('</head>', f'{inject_script}</head>')
|
| 123 |
+
|
| 124 |
+
return html
|
| 125 |
+
|
| 126 |
+
|
| 127 |
def create_default_scene():
|
| 128 |
"""Use the clean default Welcome Scene created on server startup"""
|
| 129 |
+
global current_scene_id
|
| 130 |
|
| 131 |
try:
|
|
|
|
|
|
|
| 132 |
current_scene_id = "welcome"
|
| 133 |
+
print(f"✅ Using default Welcome Scene (ID: {current_scene_id})")
|
| 134 |
+
return get_viewer_html(current_scene_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
except Exception as e:
|
| 136 |
import traceback
|
| 137 |
print(f"❌ Error loading default scene: {e}")
|
|
|
|
| 138 |
traceback.print_exc()
|
| 139 |
return None
|
| 140 |
|
|
|
|
| 153 |
|
| 154 |
def chat_response(message, history):
|
| 155 |
"""Handle chat messages using GPT with tool calling"""
|
| 156 |
+
global current_scene_id
|
| 157 |
|
| 158 |
# Handle help command locally (no need for LLM)
|
| 159 |
if message.lower().strip() == "help":
|
|
|
|
| 194 |
|
| 195 |
# Create default scene on startup
|
| 196 |
print("Creating default scene...")
|
| 197 |
+
default_viewer_html = create_default_scene()
|
| 198 |
+
print(f"Default viewer HTML loaded: {len(default_viewer_html) if default_viewer_html else 0} bytes")
|
| 199 |
+
if not default_viewer_html:
|
| 200 |
print("⚠️ WARNING: Default scene creation failed! No viewer URL generated.")
|
| 201 |
|
| 202 |
|
|
|
|
| 383 |
|
| 384 |
# Right column: 3D Viewer (scale=3 = ~75% width)
|
| 385 |
with gr.Column(elem_id="viewer-column", scale=3):
|
| 386 |
+
if default_viewer_html:
|
| 387 |
+
initial_viewer_html = f'<div id="viewer-container"><iframe srcdoc="{default_viewer_html.replace(chr(34), """)}" style="width:100%; height:600px; border:none;"></iframe></div>'
|
| 388 |
+
print(f"📊 Setting up viewer with embedded scene data")
|
| 389 |
else:
|
| 390 |
initial_viewer_html = '<div id="viewer-container" style="display: flex; align-items: center; justify-content: center; color: #666;"><p>⚠️ Scene failed to load. Check console logs.</p></div>'
|
| 391 |
+
print("⚠️ No viewer HTML available - showing error message")
|
| 392 |
|
| 393 |
viewer = gr.HTML(
|
| 394 |
value=initial_viewer_html,
|
|
|
|
| 514 |
|
| 515 |
if __name__ == "__main__":
|
| 516 |
demo.queue()
|
| 517 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/game_viewer.html
CHANGED
|
@@ -239,17 +239,23 @@
|
|
| 239 |
|
| 240 |
async function init() {
|
| 241 |
try {
|
| 242 |
-
//
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
}
|
| 252 |
-
sceneData = await response.json();
|
| 253 |
console.log('Scene data loaded:', sceneData);
|
| 254 |
|
| 255 |
// Apply player configuration from scene data
|
|
|
|
| 239 |
|
| 240 |
async function init() {
|
| 241 |
try {
|
| 242 |
+
// Check for embedded scene data first (used when served via Gradio)
|
| 243 |
+
if (window.SCENE_DATA) {
|
| 244 |
+
console.log('Using embedded scene data');
|
| 245 |
+
sceneData = window.SCENE_DATA;
|
| 246 |
+
} else {
|
| 247 |
+
// Fetch scene data from API (used when served via FastAPI)
|
| 248 |
+
console.log('Fetching scene data...');
|
| 249 |
+
const response = await fetch(`${baseUrl}/api/scenes/${sceneId}`);
|
| 250 |
+
console.log('Response status:', response.status);
|
| 251 |
+
|
| 252 |
+
if (!response.ok) {
|
| 253 |
+
const errorText = await response.text();
|
| 254 |
+
console.error('Failed to fetch scene:', errorText);
|
| 255 |
+
throw new Error(`Scene not found (${response.status}): ${errorText}`);
|
| 256 |
+
}
|
| 257 |
+
sceneData = await response.json();
|
| 258 |
}
|
|
|
|
| 259 |
console.log('Scene data loaded:', sceneData);
|
| 260 |
|
| 261 |
// Apply player configuration from scene data
|