Rafs-an09002 commited on
Commit
d03718f
Β·
verified Β·
1 Parent(s): afa14e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -32
app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
- Nexus-Nano Inference API (Fixed)
3
- Ultra-lightweight single-file engine with proper error handling
 
4
  """
5
 
6
  from fastapi import FastAPI, HTTPException
@@ -12,7 +13,6 @@ import chess
12
  import time
13
  import logging
14
  import os
15
- from pathlib import Path
16
  from typing import Optional, Tuple
17
 
18
  logging.basicConfig(
@@ -21,7 +21,7 @@ logging.basicConfig(
21
  )
22
  logger = logging.getLogger(__name__)
23
 
24
- # ==================== NANO ENGINE (Single File) ====================
25
 
26
  class NexusNanoEngine:
27
  """Ultra-lightweight chess engine"""
@@ -35,8 +35,8 @@ class NexusNanoEngine:
35
  if not os.path.exists(model_path):
36
  raise FileNotFoundError(f"Model not found: {model_path}")
37
 
38
- logger.info(f"Loading model from {model_path}...")
39
- logger.info(f"Model size: {os.path.getsize(model_path)/(1024*1024):.2f} MB")
40
 
41
  sess_options = ort.SessionOptions()
42
  sess_options.intra_op_num_threads = 2
@@ -52,7 +52,7 @@ class NexusNanoEngine:
52
  self.output_name = self.session.get_outputs()[0].name
53
  self.nodes = 0
54
 
55
- logger.info("βœ… Nexus-Nano engine loaded")
56
 
57
  def fen_to_tensor(self, fen: str) -> np.ndarray:
58
  board = chess.Board(fen)
@@ -78,7 +78,6 @@ class NexusNanoEngine:
78
  return -score if board.turn == chess.BLACK else score
79
 
80
  def order_moves(self, board: chess.Board, moves):
81
- """Simple MVV-LVA ordering"""
82
  scored = []
83
  for m in moves:
84
  s = 0
@@ -139,16 +138,19 @@ class NexusNanoEngine:
139
 
140
  moves = list(board.legal_moves)
141
  if len(moves) == 0:
142
- return {'best_move': '0000', 'evaluation': 0.0, 'nodes': 0}
 
143
  if len(moves) == 1:
144
  return {
145
  'best_move': moves[0].uci(),
146
  'evaluation': round(self.evaluate(board) / 100.0, 2),
147
- 'nodes': 1
 
148
  }
149
 
150
  best_move = moves[0]
151
  best_score = float('-inf')
 
152
 
153
  for d in range(1, depth + 1):
154
  try:
@@ -156,13 +158,14 @@ class NexusNanoEngine:
156
  if move:
157
  best_move = move
158
  best_score = score
 
159
  except:
160
  break
161
 
162
  return {
163
  'best_move': best_move.uci(),
164
  'evaluation': round(best_score / 100.0, 2),
165
- 'depth': d,
166
  'nodes': self.nodes
167
  }
168
 
@@ -170,7 +173,7 @@ class NexusNanoEngine:
170
  # ==================== FASTAPI APP ====================
171
 
172
  app = FastAPI(
173
- title="Nexus-Nano API",
174
  description="Ultra-lightweight chess engine",
175
  version="1.0.0"
176
  )
@@ -204,25 +207,32 @@ async def startup():
204
  global engine
205
  logger.info("πŸš€ Starting Nexus-Nano API...")
206
 
207
- model_path = "/app/models/nexus_nano.onnx"
 
 
 
208
 
209
- # Debug: Check models directory
210
  if os.path.exists("/app/models"):
211
- logger.info(f"πŸ“‚ Files in /app/models/:")
212
  for f in os.listdir("/app/models"):
213
  full_path = os.path.join("/app/models", f)
214
- size = os.path.getsize(full_path) / (1024*1024)
215
- logger.info(f" - {f} ({size:.2f} MB)")
 
216
  else:
217
- logger.error("❌ /app/models/ directory not found!")
218
- raise FileNotFoundError("/app/models/ not found")
 
 
 
 
 
219
 
220
- # Load engine
221
  try:
222
  engine = NexusNanoEngine(model_path)
223
- logger.info("βœ… Engine ready")
224
  except Exception as e:
225
- logger.error(f"❌ Failed to load: {e}", exc_info=True)
226
  raise
227
 
228
 
@@ -232,19 +242,20 @@ async def health():
232
  "status": "healthy" if engine else "unhealthy",
233
  "model": "nexus-nano",
234
  "version": "1.0.0",
235
- "model_loaded": engine is not None
 
236
  }
237
 
238
 
239
  @app.post("/get-move", response_model=MoveResponse)
240
  async def get_move(req: MoveRequest):
241
  if not engine:
242
- raise HTTPException(503, "Engine not loaded")
243
 
244
  try:
245
  chess.Board(req.fen)
246
  except:
247
- raise HTTPException(400, "Invalid FEN")
248
 
249
  start = time.time()
250
 
@@ -253,8 +264,9 @@ async def get_move(req: MoveRequest):
253
  elapsed = int((time.time() - start) * 1000)
254
 
255
  logger.info(
256
- f"Move: {result['best_move']} | "
257
  f"Eval: {result['evaluation']:+.2f} | "
 
258
  f"Nodes: {result['nodes']} | "
259
  f"Time: {elapsed}ms"
260
  )
@@ -266,22 +278,35 @@ async def get_move(req: MoveRequest):
266
  nodes_evaluated=result['nodes'],
267
  time_taken=elapsed
268
  )
 
269
  except Exception as e:
270
- logger.error(f"Search error: {e}", exc_info=True)
271
- raise HTTPException(500, str(e))
272
 
273
 
274
  @app.get("/")
275
  async def root():
276
  return {
277
- "name": "Nexus-Nano API",
278
  "version": "1.0.0",
279
  "model": "2.8M parameters",
280
- "speed": "Lightning-fast (0.2-0.5s)",
281
- "status": "healthy" if engine else "loading"
 
 
 
 
 
 
282
  }
283
 
284
 
285
  if __name__ == "__main__":
286
  import uvicorn
287
- uvicorn.run(app, host="0.0.0.0", port=7860, log_level="info")
 
 
 
 
 
 
 
1
  """
2
+ Nexus-Nano Inference API - Path Fixed
3
+ Model: /app/models/nexus-nano.onnx
4
+ Ultra-lightweight single-file engine
5
  """
6
 
7
  from fastapi import FastAPI, HTTPException
 
13
  import time
14
  import logging
15
  import os
 
16
  from typing import Optional, Tuple
17
 
18
  logging.basicConfig(
 
21
  )
22
  logger = logging.getLogger(__name__)
23
 
24
+ # ==================== NANO ENGINE ====================
25
 
26
  class NexusNanoEngine:
27
  """Ultra-lightweight chess engine"""
 
35
  if not os.path.exists(model_path):
36
  raise FileNotFoundError(f"Model not found: {model_path}")
37
 
38
+ logger.info(f"πŸ“¦ Loading model: {model_path}")
39
+ logger.info(f"πŸ’Ύ Size: {os.path.getsize(model_path)/(1024*1024):.2f} MB")
40
 
41
  sess_options = ort.SessionOptions()
42
  sess_options.intra_op_num_threads = 2
 
52
  self.output_name = self.session.get_outputs()[0].name
53
  self.nodes = 0
54
 
55
+ logger.info("βœ… Engine ready!")
56
 
57
  def fen_to_tensor(self, fen: str) -> np.ndarray:
58
  board = chess.Board(fen)
 
78
  return -score if board.turn == chess.BLACK else score
79
 
80
  def order_moves(self, board: chess.Board, moves):
 
81
  scored = []
82
  for m in moves:
83
  s = 0
 
138
 
139
  moves = list(board.legal_moves)
140
  if len(moves) == 0:
141
+ return {'best_move': '0000', 'evaluation': 0.0, 'nodes': 0, 'depth': 0}
142
+
143
  if len(moves) == 1:
144
  return {
145
  'best_move': moves[0].uci(),
146
  'evaluation': round(self.evaluate(board) / 100.0, 2),
147
+ 'nodes': 1,
148
+ 'depth': 0
149
  }
150
 
151
  best_move = moves[0]
152
  best_score = float('-inf')
153
+ current_depth = 1
154
 
155
  for d in range(1, depth + 1):
156
  try:
 
158
  if move:
159
  best_move = move
160
  best_score = score
161
+ current_depth = d
162
  except:
163
  break
164
 
165
  return {
166
  'best_move': best_move.uci(),
167
  'evaluation': round(best_score / 100.0, 2),
168
+ 'depth': current_depth,
169
  'nodes': self.nodes
170
  }
171
 
 
173
  # ==================== FASTAPI APP ====================
174
 
175
  app = FastAPI(
176
+ title="Nexus-Nano Inference API",
177
  description="Ultra-lightweight chess engine",
178
  version="1.0.0"
179
  )
 
207
  global engine
208
  logger.info("πŸš€ Starting Nexus-Nano API...")
209
 
210
+ # FIXED: Correct path with hyphen
211
+ model_path = "/app/models/nexus-nano.onnx"
212
+
213
+ logger.info(f"πŸ” Looking for: {model_path}")
214
 
 
215
  if os.path.exists("/app/models"):
216
+ logger.info("πŸ“‚ Files in /app/models/:")
217
  for f in os.listdir("/app/models"):
218
  full_path = os.path.join("/app/models", f)
219
+ if os.path.isfile(full_path):
220
+ size = os.path.getsize(full_path) / (1024*1024)
221
+ logger.info(f" βœ“ {f} ({size:.2f} MB)")
222
  else:
223
+ logger.error("❌ /app/models/ not found!")
224
+ raise FileNotFoundError("/app/models/ directory missing")
225
+
226
+ if not os.path.exists(model_path):
227
+ logger.error(f"❌ Model not found: {model_path}")
228
+ logger.error("πŸ’‘ Available:", os.listdir("/app/models"))
229
+ raise FileNotFoundError(f"Missing: {model_path}")
230
 
 
231
  try:
232
  engine = NexusNanoEngine(model_path)
233
+ logger.info("πŸŽ‰ Nexus-Nano ready!")
234
  except Exception as e:
235
+ logger.error(f"❌ Load failed: {e}", exc_info=True)
236
  raise
237
 
238
 
 
242
  "status": "healthy" if engine else "unhealthy",
243
  "model": "nexus-nano",
244
  "version": "1.0.0",
245
+ "model_loaded": engine is not None,
246
+ "model_path": "/app/models/nexus-nano.onnx"
247
  }
248
 
249
 
250
  @app.post("/get-move", response_model=MoveResponse)
251
  async def get_move(req: MoveRequest):
252
  if not engine:
253
+ raise HTTPException(status_code=503, detail="Engine not loaded")
254
 
255
  try:
256
  chess.Board(req.fen)
257
  except:
258
+ raise HTTPException(status_code=400, detail="Invalid FEN")
259
 
260
  start = time.time()
261
 
 
264
  elapsed = int((time.time() - start) * 1000)
265
 
266
  logger.info(
267
+ f"βœ“ Move: {result['best_move']} | "
268
  f"Eval: {result['evaluation']:+.2f} | "
269
+ f"Depth: {result['depth']} | "
270
  f"Nodes: {result['nodes']} | "
271
  f"Time: {elapsed}ms"
272
  )
 
278
  nodes_evaluated=result['nodes'],
279
  time_taken=elapsed
280
  )
281
+
282
  except Exception as e:
283
+ logger.error(f"❌ Search error: {e}", exc_info=True)
284
+ raise HTTPException(status_code=500, detail=str(e))
285
 
286
 
287
  @app.get("/")
288
  async def root():
289
  return {
290
+ "name": "Nexus-Nano Inference API",
291
  "version": "1.0.0",
292
  "model": "2.8M parameters",
293
+ "architecture": "Compact ResNet",
294
+ "speed": "0.2-0.5s per move @ depth 3",
295
+ "status": "online" if engine else "starting",
296
+ "endpoints": {
297
+ "POST /get-move": "Get best move",
298
+ "GET /health": "Health check",
299
+ "GET /docs": "API docs"
300
+ }
301
  }
302
 
303
 
304
  if __name__ == "__main__":
305
  import uvicorn
306
+ uvicorn.run(
307
+ app,
308
+ host="0.0.0.0",
309
+ port=7860,
310
+ log_level="info",
311
+ access_log=True
312
+ )