Filipp Trigub commited on
Commit
6f5bda8
·
1 Parent(s): c1cbe7e

add testing and logging

Browse files
Files changed (2) hide show
  1. backend/fastapi_server.py +169 -25
  2. frontend/app/page.tsx +206 -67
backend/fastapi_server.py CHANGED
@@ -1,7 +1,8 @@
1
  #!/usr/bin/env python
2
  """
3
- FastAPI server for AI Battle Royale Mental Manipulation
4
  This server provides streaming endpoints for agent interactions and tool executions
 
5
  """
6
  import os
7
  import json
@@ -9,13 +10,17 @@ import random
9
  import logging
10
  import asyncio
11
  import openai
 
 
12
  from typing import Dict, List, Any, Optional, Union
13
  from abc import ABC, abstractmethod
14
- from fastapi import FastAPI, HTTPException
15
- from fastapi.responses import StreamingResponse, FileResponse
16
  from fastapi.middleware.cors import CORSMiddleware
 
17
  from pydantic import BaseModel
18
  from dotenv import load_dotenv
 
19
 
20
  # Load environment variables for API keys
21
  load_dotenv()
@@ -68,23 +73,82 @@ MODEL_PROVIDERS = {
68
  }
69
  }
70
 
71
- # Configure logging
72
- logging.basicConfig(level=logging.INFO,
73
- format='%(asctime)s [%(levelname)s] %(message)s',
74
- handlers=[logging.StreamHandler()])
 
 
 
 
 
 
 
75
 
76
- # FastAPI app instance
77
- app = FastAPI(title="AI Battle Royale", description="Mental Manipulation Battle System")
 
 
 
 
 
 
78
 
79
- # Add CORS middleware
80
  app.add_middleware(
81
  CORSMiddleware,
82
- allow_origins=["*"],
83
  allow_credentials=True,
84
- allow_methods=["*"],
85
- allow_headers=["*"],
 
 
 
 
 
 
 
 
 
 
86
  )
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  class SimpleGameState:
90
  """Simplified game state for testing mental manipulation mechanics"""
@@ -1092,26 +1156,106 @@ async def delete_game(game_id: str):
1092
 
1093
 
1094
  @app.get("/")
1095
- async def root():
1096
- """Root endpoint - serves as health check"""
 
 
1097
  return {
1098
- "status": "ok",
1099
  "message": "AI Battle Royale server is running",
 
 
 
 
 
 
 
 
 
 
1100
  "note": "API keys are provided by frontend, not environment variables"
1101
  }
1102
 
1103
 
1104
  @app.get("/health")
1105
- async def health_check():
1106
- """Health check endpoint"""
1107
- return {
1108
- "status": "ok",
1109
- "message": "AI Battle Royale server is running",
1110
- "note": "API keys are provided by frontend, not environment variables"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1111
  }
 
 
 
 
 
1112
 
1113
 
1114
- if __name__ == "__main__":
1115
- import uvicorn
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1116
 
1117
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python
2
  """
3
+ Enhanced FastAPI server for AI Battle Royale Mental Manipulation
4
  This server provides streaming endpoints for agent interactions and tool executions
5
+ With comprehensive debugging, health checks, and CORS handling for HF Spaces
6
  """
7
  import os
8
  import json
 
10
  import logging
11
  import asyncio
12
  import openai
13
+ import time
14
+ import traceback
15
  from typing import Dict, List, Any, Optional, Union
16
  from abc import ABC, abstractmethod
17
+ from fastapi import FastAPI, HTTPException, Request, Response
18
+ from fastapi.responses import StreamingResponse, FileResponse, JSONResponse
19
  from fastapi.middleware.cors import CORSMiddleware
20
+ from fastapi.middleware.trustedhost import TrustedHostMiddleware
21
  from pydantic import BaseModel
22
  from dotenv import load_dotenv
23
+ import uvicorn
24
 
25
  # Load environment variables for API keys
26
  load_dotenv()
 
73
  }
74
  }
75
 
76
+ # Configure enhanced logging for debugging
77
+ logging.basicConfig(
78
+ level=logging.DEBUG, # Changed to DEBUG for more detailed logs
79
+ format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
80
+ handlers=[
81
+ logging.StreamHandler(),
82
+ logging.FileHandler('/home/node/logs/backend_app.log', mode='a')
83
+ ]
84
+ )
85
+
86
+ logger = logging.getLogger(__name__)
87
 
88
+ # FastAPI app instance with enhanced configuration
89
+ app = FastAPI(
90
+ title="AI Battle Royale",
91
+ description="Mental Manipulation Battle System - Enhanced for HF Spaces",
92
+ version="1.0.0",
93
+ docs_url="/docs",
94
+ redoc_url="/redoc"
95
+ )
96
 
97
+ # Add comprehensive CORS middleware
98
  app.add_middleware(
99
  CORSMiddleware,
100
+ allow_origins=["*"], # Allow all origins for HF Spaces
101
  allow_credentials=True,
102
+ allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
103
+ allow_headers=[
104
+ "*",
105
+ "Authorization",
106
+ "Content-Type",
107
+ "X-Requested-With",
108
+ "Accept",
109
+ "Origin",
110
+ "Access-Control-Request-Method",
111
+ "Access-Control-Request-Headers",
112
+ ],
113
+ expose_headers=["*"]
114
  )
115
 
116
+ # Add middleware for request/response logging
117
+ @app.middleware("http")
118
+ async def log_requests(request: Request, call_next):
119
+ start_time = time.time()
120
+
121
+ # Log incoming request
122
+ logger.info(f"🔍 Incoming request: {request.method} {request.url}")
123
+ logger.debug(f" Headers: {dict(request.headers)}")
124
+
125
+ try:
126
+ response = await call_next(request)
127
+ process_time = time.time() - start_time
128
+
129
+ # Log response
130
+ logger.info(f"✅ Response: {response.status_code} in {process_time:.3f}s")
131
+
132
+ # Add debug headers to response
133
+ response.headers["X-Process-Time"] = str(process_time)
134
+ response.headers["X-Server-Status"] = "healthy"
135
+
136
+ return response
137
+
138
+ except Exception as e:
139
+ process_time = time.time() - start_time
140
+ logger.error(f"❌ Request failed: {str(e)} in {process_time:.3f}s")
141
+ logger.error(f" Traceback: {traceback.format_exc()}")
142
+
143
+ return JSONResponse(
144
+ status_code=500,
145
+ content={
146
+ "detail": f"Internal server error: {str(e)}",
147
+ "type": "internal_error",
148
+ "timestamp": time.time()
149
+ }
150
+ )
151
+
152
 
153
  class SimpleGameState:
154
  """Simplified game state for testing mental manipulation mechanics"""
 
1156
 
1157
 
1158
  @app.get("/")
1159
+ async def root(request: Request):
1160
+ """Root endpoint - serves as comprehensive health check"""
1161
+ logger.info(f"🏠 Root endpoint accessed from {request.client.host if request.client else 'unknown'}")
1162
+
1163
  return {
1164
+ "status": "healthy",
1165
  "message": "AI Battle Royale server is running",
1166
+ "version": "1.0.0",
1167
+ "timestamp": time.time(),
1168
+ "environment": "huggingface-spaces",
1169
+ "endpoints": {
1170
+ "health": "/health",
1171
+ "models": "/models",
1172
+ "start_game": "/start-game",
1173
+ "stream_game": "/stream-game/{game_id}",
1174
+ "docs": "/docs"
1175
+ },
1176
  "note": "API keys are provided by frontend, not environment variables"
1177
  }
1178
 
1179
 
1180
  @app.get("/health")
1181
+ async def health_check(request: Request):
1182
+ """Comprehensive health check endpoint with system status"""
1183
+ logger.info(f"🏥 Health check accessed from {request.client.host if request.client else 'unknown'}")
1184
+
1185
+ # Perform basic system checks
1186
+ health_status = {
1187
+ "status": "healthy",
1188
+ "message": "AI Battle Royale server is operational",
1189
+ "timestamp": time.time(),
1190
+ "checks": {
1191
+ "server": "healthy",
1192
+ "memory": "ok",
1193
+ "disk": "ok"
1194
+ },
1195
+ "stats": {
1196
+ "active_games": len(active_games),
1197
+ "total_requests": getattr(health_check, 'request_count', 0)
1198
+ },
1199
+ "environment": {
1200
+ "python_version": f"{os.sys.version_info.major}.{os.sys.version_info.minor}.{os.sys.version_info.micro}",
1201
+ "fastapi_available": True,
1202
+ "models_loaded": len(MODEL_PROVIDERS)
1203
+ }
1204
  }
1205
+
1206
+ # Increment request counter
1207
+ health_check.request_count = getattr(health_check, 'request_count', 0) + 1
1208
+
1209
+ return health_status
1210
 
1211
 
1212
+ @app.options("/{path:path}")
1213
+ async def options_handler(request: Request, path: str):
1214
+ """Handle CORS preflight requests"""
1215
+ logger.debug(f"🔄 CORS preflight request for path: /{path}")
1216
+
1217
+ return Response(
1218
+ status_code=200,
1219
+ headers={
1220
+ "Access-Control-Allow-Origin": "*",
1221
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
1222
+ "Access-Control-Allow-Headers": "*",
1223
+ "Access-Control-Max-Age": "86400"
1224
+ }
1225
+ )
1226
+
1227
+
1228
+ # Startup event
1229
+ @app.on_event("startup")
1230
+ async def startup_event():
1231
+ """Log startup information"""
1232
+ logger.info("🚀 AI Battle Royale server starting up...")
1233
+ logger.info(f"📊 Available model providers: {list(MODEL_PROVIDERS.keys())}")
1234
+ logger.info(f"🔧 Debug mode: {os.getenv('DEBUG', 'False')}")
1235
+ logger.info(f"🌐 Server will be available at: http://0.0.0.0:8000")
1236
+ logger.info("✅ Server startup complete!")
1237
+
1238
+
1239
+ @app.on_event("shutdown")
1240
+ async def shutdown_event():
1241
+ """Clean up on shutdown"""
1242
+ logger.info("🛑 AI Battle Royale server shutting down...")
1243
+ logger.info(f"📈 Final stats: {len(active_games)} active games")
1244
+ logger.info("✅ Server shutdown complete!")
1245
 
1246
+
1247
+ if __name__ == "__main__":
1248
+ logger.info("🎯 Starting FastAPI server directly...")
1249
+
1250
+ # Enhanced uvicorn configuration for HF Spaces
1251
+ uvicorn.run(
1252
+ app,
1253
+ host="0.0.0.0",
1254
+ port=8000,
1255
+ log_level="info",
1256
+ access_log=True,
1257
+ server_header=False, # Don't expose server version
1258
+ date_header=False, # Don't add date header
1259
+ reload=False, # Disable auto-reload in production
1260
+ workers=1 # Single worker for HF Spaces
1261
+ )
frontend/app/page.tsx CHANGED
@@ -146,22 +146,44 @@ const MentalStateBar = ({ label, value, color }: { label: string; value: number;
146
  )
147
 
148
  const detectServerURL = () => {
149
- // In development, check if we're running on port 3000 (Next.js dev server)
150
- // In production (Docker/HF Spaces), use nginx proxy route
151
  if (typeof window !== 'undefined') {
152
- const isLocalDev = window.location.hostname === 'localhost' && window.location.port === '3000'
153
- const isHuggingFaceSpaces = window.location.hostname.includes('.hf.space') || window.location.hostname.includes('huggingface.co')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
  if (isLocalDev) {
156
  // Local development: connect directly to backend
 
157
  return 'http://localhost:8000'
 
 
 
 
158
  } else {
159
  // Production (Docker/HF Spaces): always use relative nginx proxy
160
  // This ensures the request goes through the nginx proxy on port 7860
 
161
  return '/api'
162
  }
163
  }
164
  // Fallback for SSR - always use relative path in production
 
165
  return '/api'
166
  }
167
 
@@ -349,29 +371,51 @@ export default function AgentBattleArena() {
349
  loadLogos()
350
  }, [])
351
 
352
- // Fetch available models on component mount
353
  useEffect(() => {
354
  const fetchModels = async () => {
355
  try {
356
- console.log('Fetching models from server...', `${serverURL}/models`)
357
- const response = await fetch(`${serverURL}/models`, {
 
 
 
 
 
 
 
 
 
358
  method: 'GET',
359
  headers: {
360
  'Accept': 'application/json',
361
- 'Content-Type': 'application/json'
 
362
  },
363
- // Add timeout to prevent hanging
364
- signal: AbortSignal.timeout(10000) // 10 second timeout
365
  })
 
 
 
 
 
 
366
  if (response.ok) {
367
  const data = await response.json()
368
- console.log('Models loaded from server:', data)
369
  setAvailableModels(data)
370
  } else {
371
- console.warn(`Failed to fetch models from server (${response.status}), using fallback`)
 
 
372
  }
373
  } catch (error) {
374
- console.warn("Failed to fetch available models from server, using fallback:", error)
 
 
 
 
 
375
  }
376
  }
377
  fetchModels()
@@ -1097,29 +1141,73 @@ export default function AgentBattleArena() {
1097
  isHuggingFaceSpaces: window.location.hostname.includes('.hf.space') || window.location.hostname.includes('huggingface.co'),
1098
  resolvedServerURL: serverURL
1099
  }
1100
- console.log('DEBUG: Environment detection:', environmentInfo)
1101
 
1102
- // Step 1: Health check to verify server is reachable and CORS is OK.
1103
- setBattleState((prev) => ({ ...prev, battleLog: [`> Pinging server at ${serverURL}...`] }))
1104
 
1105
- // Add timeout and better error handling for health check
1106
- const healthCheckResponse = await fetch(serverURL, {
1107
- method: 'GET',
1108
- headers: {
1109
- 'Accept': 'application/json',
1110
- 'Content-Type': 'application/json'
1111
- },
1112
- // Add timeout to prevent hanging
1113
- signal: AbortSignal.timeout(10000) // 10 second timeout
1114
- })
1115
 
1116
- if (!healthCheckResponse.ok) {
1117
- const errorText = await healthCheckResponse.text().catch(() => 'Unable to read error response')
1118
- throw new Error(`Server health check failed with status: ${healthCheckResponse.status} - ${errorText}`)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1119
  }
 
1120
  const serverInfo = await healthCheckResponse.json()
1121
- console.log("Server info:", serverInfo)
1122
- setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> Server online: ${serverInfo.message}`] }))
 
 
 
 
 
 
 
 
 
1123
 
1124
  // Step 2: Start the game.
1125
  setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> Starting game ${gameId}...`, `> Player 1: ${selectedPlayer1Model}`, `> Player 2: ${selectedPlayer2Model}`] }))
@@ -1171,20 +1259,29 @@ export default function AgentBattleArena() {
1171
  } catch (error: any) {
1172
  console.error("Failed to start battle:", error)
1173
 
1174
- // Enhanced error reporting with environment context
1175
  const errorDetails = [
1176
  "> ❌ Battle initialization failed.",
1177
- `> Environment: ${window.location.hostname.includes('.hf.space') ? 'Hugging Face Spaces' : 'Local/Other'}`,
1178
- `> Request URL: ${serverURL}`,
1179
- `> Full URL context: ${window.location.href}`,
1180
- `> Error: ${error.message}`,
1181
- "> Possible causes:",
1182
- "> 1. Backend server is down or unreachable",
1183
- "> 2. Nginx proxy configuration issue",
1184
- "> 3. API endpoint path mismatch",
1185
- "> 4. CORS policy blocking request",
1186
- "> 5. Network timeout or connectivity issue",
1187
- "> 6. Invalid API keys or unsupported model"
 
 
 
 
 
 
 
 
 
1188
  ]
1189
 
1190
  setBattleState((prev) => ({
@@ -1470,48 +1567,90 @@ export default function AgentBattleArena() {
1470
  TEST SOUNDS
1471
  </Button>
1472
 
1473
- {/* Test Connection Button */}
1474
  <Button
1475
  onClick={async () => {
1476
- console.log('Testing server connectivity...')
1477
  setBattleState(prev => ({
1478
  ...prev,
1479
- battleLog: [`> Testing connection to ${serverURL}...`]
1480
  }))
1481
 
1482
- try {
1483
- const response = await fetch(serverURL, {
1484
- method: 'GET',
1485
- headers: {
1486
- 'Accept': 'application/json',
1487
- 'Content-Type': 'application/json'
1488
- },
1489
- signal: AbortSignal.timeout(5000)
1490
- })
1491
-
1492
- if (response.ok) {
1493
- const data = await response.json()
1494
  setBattleState(prev => ({
1495
  ...prev,
1496
- battleLog: [...prev.battleLog, `> ✅ Connection successful!`, `> Server: ${data.message || 'Connected'}`]
1497
  }))
1498
- } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1499
  setBattleState(prev => ({
1500
  ...prev,
1501
- battleLog: [...prev.battleLog, `> ❌ Connection failed: ${response.status} ${response.statusText}`]
 
 
 
1502
  }))
1503
  }
1504
- } catch (error: any) {
1505
- setBattleState(prev => ({
1506
- ...prev,
1507
- battleLog: [...prev.battleLog, `> ❌ Connection error: ${error.message}`]
1508
- }))
1509
  }
 
 
 
 
 
1510
  }}
1511
  variant="outline"
1512
  className="border-gray-600 text-gray-300 hover:bg-gray-700"
1513
  >
1514
- TEST CONNECTION
1515
  </Button>
1516
 
1517
  {/* Test Animation Button */}
 
146
  )
147
 
148
  const detectServerURL = () => {
149
+ // Enhanced server URL detection with comprehensive debugging
 
150
  if (typeof window !== 'undefined') {
151
+ const hostname = window.location.hostname
152
+ const port = window.location.port
153
+ const protocol = window.location.protocol
154
+ const href = window.location.href
155
+
156
+ const isLocalDev = hostname === 'localhost' && port === '3000'
157
+ const isHuggingFaceSpaces = hostname.includes('.hf.space') || hostname.includes('huggingface.co')
158
+ const isDockerLocal = hostname === 'localhost' && port === '7860'
159
+
160
+ console.log('🔍 Environment Detection:', {
161
+ hostname,
162
+ port,
163
+ protocol,
164
+ href,
165
+ isLocalDev,
166
+ isHuggingFaceSpaces,
167
+ isDockerLocal
168
+ })
169
 
170
  if (isLocalDev) {
171
  // Local development: connect directly to backend
172
+ console.log('📍 Using local development backend')
173
  return 'http://localhost:8000'
174
+ } else if (isDockerLocal) {
175
+ // Local Docker: use nginx proxy
176
+ console.log('📍 Using local Docker nginx proxy')
177
+ return '/api'
178
  } else {
179
  // Production (Docker/HF Spaces): always use relative nginx proxy
180
  // This ensures the request goes through the nginx proxy on port 7860
181
+ console.log('📍 Using production nginx proxy')
182
  return '/api'
183
  }
184
  }
185
  // Fallback for SSR - always use relative path in production
186
+ console.log('📍 Using SSR fallback')
187
  return '/api'
188
  }
189
 
 
371
  loadLogos()
372
  }, [])
373
 
374
+ // Enhanced model fetching with comprehensive error handling
375
  useEffect(() => {
376
  const fetchModels = async () => {
377
  try {
378
+ const url = `${serverURL}/models`
379
+ console.log('🔄 Fetching models from server...', url)
380
+ console.log('🌐 Server URL configuration:', serverURL)
381
+
382
+ const controller = new AbortController()
383
+ const timeoutId = setTimeout(() => {
384
+ console.warn('⏰ Request timeout, aborting...')
385
+ controller.abort()
386
+ }, 15000) // 15 second timeout
387
+
388
+ const response = await fetch(url, {
389
  method: 'GET',
390
  headers: {
391
  'Accept': 'application/json',
392
+ 'Content-Type': 'application/json',
393
+ 'Cache-Control': 'no-cache'
394
  },
395
+ signal: controller.signal
 
396
  })
397
+
398
+ clearTimeout(timeoutId)
399
+
400
+ console.log('📡 Response status:', response.status)
401
+ console.log('📋 Response headers:', Object.fromEntries(response.headers.entries()))
402
+
403
  if (response.ok) {
404
  const data = await response.json()
405
+ console.log('Models loaded from server:', data)
406
  setAvailableModels(data)
407
  } else {
408
+ const errorText = await response.text().catch(() => 'Unable to read error response')
409
+ console.warn(`❌ Failed to fetch models from server (${response.status}): ${errorText}`)
410
+ console.warn('🔄 Using fallback model configuration')
411
  }
412
  } catch (error) {
413
+ if (error.name === 'AbortError') {
414
+ console.warn("⏰ Model fetch request timed out")
415
+ } else {
416
+ console.warn("❌ Failed to fetch available models from server:", error)
417
+ }
418
+ console.warn('🔄 Using fallback model configuration')
419
  }
420
  }
421
  fetchModels()
 
1141
  isHuggingFaceSpaces: window.location.hostname.includes('.hf.space') || window.location.hostname.includes('huggingface.co'),
1142
  resolvedServerURL: serverURL
1143
  }
1144
+ console.log('🔍 DEBUG: Environment detection:', environmentInfo)
1145
 
1146
+ // Step 1: Enhanced health check with comprehensive diagnostics
1147
+ setBattleState((prev) => ({ ...prev, battleLog: [`> 🔍 Testing connectivity to ${serverURL}...`] }))
1148
 
1149
+ // Test multiple endpoints to diagnose connectivity issues
1150
+ const healthEndpoints = [
1151
+ { name: 'Root', url: serverURL },
1152
+ { name: 'Health', url: `${serverURL}/health` },
1153
+ { name: 'Models', url: `${serverURL}/models` }
1154
+ ]
1155
+
1156
+ let healthCheckResponse
1157
+ let workingEndpoint = null
 
1158
 
1159
+ for (const endpoint of healthEndpoints) {
1160
+ try {
1161
+ console.log(`🔍 Testing ${endpoint.name} endpoint: ${endpoint.url}`)
1162
+ setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> Testing ${endpoint.name}: ${endpoint.url}`] }))
1163
+
1164
+ const controller = new AbortController()
1165
+ const timeoutId = setTimeout(() => controller.abort(), 10000)
1166
+
1167
+ const response = await fetch(endpoint.url, {
1168
+ method: 'GET',
1169
+ headers: {
1170
+ 'Accept': 'application/json',
1171
+ 'Content-Type': 'application/json',
1172
+ 'Cache-Control': 'no-cache'
1173
+ },
1174
+ signal: controller.signal
1175
+ })
1176
+
1177
+ clearTimeout(timeoutId)
1178
+
1179
+ console.log(`📡 ${endpoint.name} response:`, response.status, response.statusText)
1180
+
1181
+ if (response.ok) {
1182
+ healthCheckResponse = response
1183
+ workingEndpoint = endpoint
1184
+ setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> ✅ ${endpoint.name} endpoint working!`] }))
1185
+ break
1186
+ } else {
1187
+ setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> ❌ ${endpoint.name} failed: ${response.status}`] }))
1188
+ }
1189
+ } catch (error) {
1190
+ console.warn(`❌ ${endpoint.name} endpoint failed:`, error.message)
1191
+ setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> ❌ ${endpoint.name} error: ${error.message}`] }))
1192
+ }
1193
+ }
1194
+
1195
+ if (!healthCheckResponse || !healthCheckResponse.ok) {
1196
+ throw new Error('All health check endpoints failed - server may be unreachable')
1197
  }
1198
+
1199
  const serverInfo = await healthCheckResponse.json()
1200
+ console.log("Server info:", serverInfo)
1201
+ setBattleState((prev) => ({
1202
+ ...prev,
1203
+ battleLog: [
1204
+ ...prev.battleLog,
1205
+ `> ✅ Server online: ${serverInfo.message}`,
1206
+ `> Working endpoint: ${workingEndpoint.name}`,
1207
+ `> Server version: ${serverInfo.version || 'unknown'}`,
1208
+ `> Environment: ${serverInfo.environment || 'unknown'}`
1209
+ ]
1210
+ }))
1211
 
1212
  // Step 2: Start the game.
1213
  setBattleState((prev) => ({ ...prev, battleLog: [...prev.battleLog, `> Starting game ${gameId}...`, `> Player 1: ${selectedPlayer1Model}`, `> Player 2: ${selectedPlayer2Model}`] }))
 
1259
  } catch (error: any) {
1260
  console.error("Failed to start battle:", error)
1261
 
1262
+ // Comprehensive error reporting with detailed diagnostics
1263
  const errorDetails = [
1264
  "> ❌ Battle initialization failed.",
1265
+ `> 🌍 Environment: ${window.location.hostname.includes('.hf.space') ? 'Hugging Face Spaces' : 'Local/Other'}`,
1266
+ `> 🔗 Request URL: ${serverURL}`,
1267
+ `> 📍 Full URL context: ${window.location.href}`,
1268
+ `> Error: ${error.message}`,
1269
+ "> ",
1270
+ "> 🔍 Diagnostic Information:",
1271
+ `> - Protocol: ${window.location.protocol}`,
1272
+ `> - Hostname: ${window.location.hostname}`,
1273
+ `> - Port: ${window.location.port || 'default'}`,
1274
+ `> - User Agent: ${navigator.userAgent.substring(0, 50)}...`,
1275
+ "> ",
1276
+ "> 🛠️ Possible Solutions:",
1277
+ "> 1. Check if backend server is running (port 8000)",
1278
+ "> 2. Verify nginx proxy configuration",
1279
+ "> 3. Confirm API endpoint paths are correct",
1280
+ "> 4. Check CORS policy settings",
1281
+ "> 5. Verify network connectivity",
1282
+ "> 6. Validate API keys and model availability",
1283
+ "> 7. Try refreshing the page",
1284
+ "> 8. Check browser console for additional errors"
1285
  ]
1286
 
1287
  setBattleState((prev) => ({
 
1567
  TEST SOUNDS
1568
  </Button>
1569
 
1570
+ {/* Enhanced Test Connection Button */}
1571
  <Button
1572
  onClick={async () => {
1573
+ console.log('🔍 Testing server connectivity comprehensively...')
1574
  setBattleState(prev => ({
1575
  ...prev,
1576
+ battleLog: [`> 🔍 Starting comprehensive connectivity test...`]
1577
  }))
1578
 
1579
+ const testEndpoints = [
1580
+ { name: 'Root', path: '' },
1581
+ { name: 'Health', path: '/health' },
1582
+ { name: 'Models', path: '/models' }
1583
+ ]
1584
+
1585
+ for (const endpoint of testEndpoints) {
1586
+ try {
1587
+ const url = `${serverURL}${endpoint.path}`
1588
+ console.log(`🔍 Testing ${endpoint.name}: ${url}`)
1589
+
 
1590
  setBattleState(prev => ({
1591
  ...prev,
1592
+ battleLog: [...prev.battleLog, `> Testing ${endpoint.name}: ${url}`]
1593
  }))
1594
+
1595
+ const controller = new AbortController()
1596
+ const timeoutId = setTimeout(() => controller.abort(), 8000)
1597
+
1598
+ const startTime = performance.now()
1599
+ const response = await fetch(url, {
1600
+ method: 'GET',
1601
+ headers: {
1602
+ 'Accept': 'application/json',
1603
+ 'Content-Type': 'application/json',
1604
+ 'Cache-Control': 'no-cache'
1605
+ },
1606
+ signal: controller.signal
1607
+ })
1608
+ const responseTime = Math.round(performance.now() - startTime)
1609
+
1610
+ clearTimeout(timeoutId)
1611
+
1612
+ if (response.ok) {
1613
+ const data = await response.json().catch(() => ({ message: 'Response not JSON' }))
1614
+ setBattleState(prev => ({
1615
+ ...prev,
1616
+ battleLog: [
1617
+ ...prev.battleLog,
1618
+ `> ✅ ${endpoint.name} OK (${responseTime}ms)`,
1619
+ `> Status: ${response.status}`,
1620
+ `> Message: ${data.message || 'Connected'}`
1621
+ ]
1622
+ }))
1623
+ } else {
1624
+ setBattleState(prev => ({
1625
+ ...prev,
1626
+ battleLog: [
1627
+ ...prev.battleLog,
1628
+ `> ❌ ${endpoint.name} Failed (${responseTime}ms)`,
1629
+ `> Status: ${response.status} ${response.statusText}`
1630
+ ]
1631
+ }))
1632
+ }
1633
+ } catch (error: any) {
1634
+ const errorType = error.name === 'AbortError' ? 'Timeout' : 'Error'
1635
  setBattleState(prev => ({
1636
  ...prev,
1637
+ battleLog: [
1638
+ ...prev.battleLog,
1639
+ `> ❌ ${endpoint.name} ${errorType}: ${error.message}`
1640
+ ]
1641
  }))
1642
  }
 
 
 
 
 
1643
  }
1644
+
1645
+ setBattleState(prev => ({
1646
+ ...prev,
1647
+ battleLog: [...prev.battleLog, `> 🏁 Connectivity test complete`]
1648
+ }))
1649
  }}
1650
  variant="outline"
1651
  className="border-gray-600 text-gray-300 hover:bg-gray-700"
1652
  >
1653
+ 🔍 TEST CONNECTION
1654
  </Button>
1655
 
1656
  {/* Test Animation Button */}