Amogh1221 commited on
Commit
c60b8ec
Β·
verified Β·
1 Parent(s): 2b214dc

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +75 -26
main.py CHANGED
@@ -122,6 +122,13 @@ _GLOBAL_DEEPCASTLE_ENGINE = None
122
  _ENGINE_LOCK = asyncio.Lock()
123
  _ENGINE_IO_LOCK = asyncio.Lock()
124
 
 
 
 
 
 
 
 
125
 
126
  def _engine_hash_mb() -> int:
127
  try:
@@ -456,25 +463,35 @@ def normalize_search_stats(info: dict) -> Tuple[int, int, int]:
456
  # ─── Bot Move (/move) ──────────────────────────────────────────────────────────
457
  @app.post("/move", response_model=MoveResponse)
458
  async def get_move(request: MoveRequest):
 
 
 
 
 
 
459
  try:
460
  engine = await get_deepcastle_engine()
461
  board = chess.Board(request.fen)
462
  limit = chess.engine.Limit(time=request.time, depth=request.depth)
463
  tsec = _search_timeout_sec(request.time, request.depth)
464
 
465
- async with _ENGINE_IO_LOCK:
466
- result = await _engine_call(
467
- engine,
468
- engine.play(board, limit, info=chess.engine.INFO_ALL),
469
- tsec,
470
- )
471
- info = dict(result.info)
472
- if not info:
473
- info = await _engine_call(
474
- engine,
475
- engine.analyse(board, limit, info=chess.engine.INFO_ALL),
476
- tsec,
477
- )
 
 
 
 
478
 
479
  score_cp, mate_in = get_normalized_score(info)
480
  depth, nodes, nps = normalize_search_stats(info)
@@ -500,6 +517,11 @@ async def get_move(request: MoveRequest):
500
 
501
  del result
502
  del info
 
 
 
 
 
503
 
504
  return MoveResponse(
505
  bestmove=best_move,
@@ -516,30 +538,42 @@ async def get_move(request: MoveRequest):
516
  except Exception as e:
517
  print(f"Error: {e}")
518
  raise HTTPException(status_code=500, detail=str(e))
 
 
519
 
520
 
521
  # ─── Hint Move (/analysis-move) ───────────────────────────────────────────────
522
  @app.post("/analysis-move", response_model=MoveResponse)
523
  async def get_analysis_move(request: MoveRequest):
 
 
 
 
 
 
524
  try:
525
  engine = await get_stockfish_engine()
526
  board = chess.Board(request.fen)
527
  limit = chess.engine.Limit(time=request.time, depth=request.depth)
528
  tsec = _search_timeout_sec(request.time, request.depth)
529
 
530
- async with _ENGINE_IO_LOCK:
531
- result = await _engine_call(
532
- engine,
533
- engine.play(board, limit, info=chess.engine.INFO_ALL),
534
- tsec,
535
- )
536
- info = dict(result.info)
537
- if not info:
538
- info = await _engine_call(
539
- engine,
540
- engine.analyse(board, limit, info=chess.engine.INFO_ALL),
541
- tsec,
542
- )
 
 
 
 
543
 
544
  score_cp, mate_in = get_normalized_score(info)
545
  depth, nodes, nps = normalize_search_stats(info)
@@ -586,6 +620,8 @@ async def get_analysis_move(request: MoveRequest):
586
  except Exception as e:
587
  print(f"Analysis move error: {e}")
588
  raise HTTPException(status_code=500, detail=str(e))
 
 
589
 
590
 
591
  # ─── Openings DB ───────────────────────────────────────────────────────────────
@@ -741,6 +777,16 @@ def get_move_classification(
741
  # ─── Game Analysis (/analyze-game) ────────────────────────────────────────────
742
  @app.post("/analyze-game", response_model=AnalyzeResponse)
743
  async def analyze_game(request: AnalyzeRequest):
 
 
 
 
 
 
 
 
 
 
744
  try:
745
  engine = await get_stockfish_engine()
746
  board = chess.Board(request.start_fen) if request.start_fen else chess.Board()
@@ -912,6 +958,9 @@ async def analyze_game(request: AnalyzeRequest):
912
  except Exception as e:
913
  print(f"Analysis Error: {e}")
914
  raise HTTPException(status_code=500, detail=str(e))
 
 
 
915
 
916
 
917
  if __name__ == "__main__":
 
122
  _ENGINE_LOCK = asyncio.Lock()
123
  _ENGINE_IO_LOCK = asyncio.Lock()
124
 
125
+ # Global state for monitoring
126
+ _PENDING_ENGINE_REQUESTS = 0
127
+ _MAX_PENDING_ENGINE_REQUESTS = 10 # Drop requests if queue is too long to prevent OOM
128
+ _MAX_PENDING_GAME_REVIEWS = 2 # Max concurrent full game reviews (heavy RAM)
129
+ _PENDING_GAME_REVIEWS = 0
130
+ _LOCK_TIMEOUT_SEC = 20.0 # Max wait time in queue before giving up
131
+
132
 
133
  def _engine_hash_mb() -> int:
134
  try:
 
463
  # ─── Bot Move (/move) ──────────────────────────────────────────────────────────
464
  @app.post("/move", response_model=MoveResponse)
465
  async def get_move(request: MoveRequest):
466
+ global _PENDING_ENGINE_REQUESTS
467
+ if _PENDING_ENGINE_REQUESTS > _MAX_PENDING_ENGINE_REQUESTS:
468
+ force_memory_release()
469
+ raise HTTPException(status_code=429, detail="Server busy β€” too many analysis requests.")
470
+
471
+ _PENDING_ENGINE_REQUESTS += 1
472
  try:
473
  engine = await get_deepcastle_engine()
474
  board = chess.Board(request.fen)
475
  limit = chess.engine.Limit(time=request.time, depth=request.depth)
476
  tsec = _search_timeout_sec(request.time, request.depth)
477
 
478
+ try:
479
+ async with asyncio.timeout(_LOCK_TIMEOUT_SEC):
480
+ async with _ENGINE_IO_LOCK:
481
+ result = await _engine_call(
482
+ engine,
483
+ engine.play(board, limit, info=chess.engine.INFO_ALL),
484
+ tsec,
485
+ )
486
+ info = dict(result.info)
487
+ if not info:
488
+ info = await _engine_call(
489
+ engine,
490
+ engine.analyse(board, limit, info=chess.engine.INFO_ALL),
491
+ tsec,
492
+ )
493
+ except asyncio.TimeoutError:
494
+ raise HTTPException(status_code=503, detail="Server overloaded β€” lock wait timeout.")
495
 
496
  score_cp, mate_in = get_normalized_score(info)
497
  depth, nodes, nps = normalize_search_stats(info)
 
517
 
518
  del result
519
  del info
520
+
521
+ # Match Shield: Force cleanup and hash clear after every move to prevent "Idle Growth"
522
+ async with _ENGINE_IO_LOCK:
523
+ await _clear_engine_hash(engine)
524
+ force_memory_release()
525
 
526
  return MoveResponse(
527
  bestmove=best_move,
 
538
  except Exception as e:
539
  print(f"Error: {e}")
540
  raise HTTPException(status_code=500, detail=str(e))
541
+ finally:
542
+ _PENDING_ENGINE_REQUESTS -= 1
543
 
544
 
545
  # ─── Hint Move (/analysis-move) ───────────────────────────────────────────────
546
  @app.post("/analysis-move", response_model=MoveResponse)
547
  async def get_analysis_move(request: MoveRequest):
548
+ global _PENDING_ENGINE_REQUESTS
549
+ if _PENDING_ENGINE_REQUESTS > _MAX_PENDING_ENGINE_REQUESTS:
550
+ force_memory_release()
551
+ raise HTTPException(status_code=429, detail="Server busy β€” too many analysis requests.")
552
+
553
+ _PENDING_ENGINE_REQUESTS += 1
554
  try:
555
  engine = await get_stockfish_engine()
556
  board = chess.Board(request.fen)
557
  limit = chess.engine.Limit(time=request.time, depth=request.depth)
558
  tsec = _search_timeout_sec(request.time, request.depth)
559
 
560
+ try:
561
+ async with asyncio.timeout(_LOCK_TIMEOUT_SEC):
562
+ async with _ENGINE_IO_LOCK:
563
+ result = await _engine_call(
564
+ engine,
565
+ engine.play(board, limit, info=chess.engine.INFO_ALL),
566
+ tsec,
567
+ )
568
+ info = dict(result.info)
569
+ if not info:
570
+ info = await _engine_call(
571
+ engine,
572
+ engine.analyse(board, limit, info=chess.engine.INFO_ALL),
573
+ tsec,
574
+ )
575
+ except asyncio.TimeoutError:
576
+ raise HTTPException(status_code=503, detail="Server overloaded β€” lock wait timeout.")
577
 
578
  score_cp, mate_in = get_normalized_score(info)
579
  depth, nodes, nps = normalize_search_stats(info)
 
620
  except Exception as e:
621
  print(f"Analysis move error: {e}")
622
  raise HTTPException(status_code=500, detail=str(e))
623
+ finally:
624
+ _PENDING_ENGINE_REQUESTS -= 1
625
 
626
 
627
  # ─── Openings DB ───────────────────────────────────────────────────────────────
 
777
  # ─── Game Analysis (/analyze-game) ────────────────────────────────────────────
778
  @app.post("/analyze-game", response_model=AnalyzeResponse)
779
  async def analyze_game(request: AnalyzeRequest):
780
+ global _PENDING_ENGINE_REQUESTS, _PENDING_GAME_REVIEWS
781
+
782
+ if _PENDING_GAME_REVIEWS >= _MAX_PENDING_GAME_REVIEWS:
783
+ raise HTTPException(status_code=429, detail="Analysis queue full. Only 2 games can be reviewed at once.")
784
+
785
+ if _PENDING_ENGINE_REQUESTS >= _MAX_PENDING_ENGINE_REQUESTS:
786
+ raise HTTPException(status_code=429, detail="Server busy β€” too many moving parts.")
787
+
788
+ _PENDING_ENGINE_REQUESTS += 1
789
+ _PENDING_GAME_REVIEWS += 1
790
  try:
791
  engine = await get_stockfish_engine()
792
  board = chess.Board(request.start_fen) if request.start_fen else chess.Board()
 
958
  except Exception as e:
959
  print(f"Analysis Error: {e}")
960
  raise HTTPException(status_code=500, detail=str(e))
961
+ finally:
962
+ _PENDING_ENGINE_REQUESTS -= 1
963
+ _PENDING_GAME_REVIEWS -= 1
964
 
965
 
966
  if __name__ == "__main__":