Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -71,6 +71,72 @@ class CodeExecutor:
|
|
| 71 |
)
|
| 72 |
resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY, MAX_MEMORY))
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
def analyze_input_patterns(
|
| 75 |
self, language: str, file_contents: Dict[str, str]
|
| 76 |
) -> InputAnalysisResult:
|
|
@@ -459,28 +525,12 @@ class CodeExecutor:
|
|
| 459 |
java_files = list(Path(workspace).glob("*.java"))
|
| 460 |
needs_compilation = len(java_files) > 0
|
| 461 |
|
| 462 |
-
# JVM options for very limited container environment (HuggingFace)
|
| 463 |
-
jvm_opts = [
|
| 464 |
-
"-Xms8m", # Minimal initial heap
|
| 465 |
-
"-Xmx64m", # Very small max heap
|
| 466 |
-
"-XX:ReservedCodeCacheSize=8m", # Minimal code cache
|
| 467 |
-
"-XX:InitialCodeCacheSize=1m", # Tiny initial code cache
|
| 468 |
-
"-XX:+UseSerialGC", # Simple GC
|
| 469 |
-
"-XX:NewRatio=3", # Small young generation
|
| 470 |
-
"-XX:SurvivorRatio=4", # Optimize survivor space
|
| 471 |
-
"-XX:MaxMetaspaceSize=32m", # Limit metaspace
|
| 472 |
-
"-XX:CompressedClassSpaceSize=8m", # Compress class space
|
| 473 |
-
"-Xss256k", # Small stack size
|
| 474 |
-
"-XX:-TieredCompilation", # Disable tiered compilation to save memory
|
| 475 |
-
"-Djava.awt.headless=true", # Headless mode
|
| 476 |
-
]
|
| 477 |
-
|
| 478 |
# If we have .java files, compile them
|
| 479 |
if needs_compilation:
|
| 480 |
logger.info(f"Found {len(java_files)} Java source files, compiling...")
|
| 481 |
|
| 482 |
-
# Compile with minimal memory for javac
|
| 483 |
-
compile_cmd = ["javac", "-J-
|
| 484 |
|
| 485 |
compile_process = await asyncio.create_subprocess_exec(
|
| 486 |
*compile_cmd,
|
|
@@ -524,76 +574,100 @@ class CodeExecutor:
|
|
| 524 |
|
| 525 |
logger.info("Using existing .class files, skipping compilation")
|
| 526 |
|
| 527 |
-
#
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
)
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
java_cmd, workspace, input_data
|
| 560 |
-
)
|
| 561 |
-
|
| 562 |
-
execution_time = asyncio.get_event_loop().time() - start_time
|
| 563 |
-
|
| 564 |
-
results.append(
|
| 565 |
-
ExecutionResult(
|
| 566 |
-
success=returncode == 0,
|
| 567 |
-
stdout=stdout.decode("utf-8", errors="replace"),
|
| 568 |
-
stderr=stderr.decode("utf-8", errors="replace"),
|
| 569 |
-
execution_time=execution_time,
|
| 570 |
-
exit_code=returncode,
|
| 571 |
)
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
|
|
|
| 582 |
)
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 593 |
)
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 597 |
|
| 598 |
async def _execute_c_cpp(
|
| 599 |
self,
|
|
@@ -836,6 +910,12 @@ def detect_language_from_files(main_files: List[str]) -> str:
|
|
| 836 |
return detected_language
|
| 837 |
|
| 838 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
@app.post("/analyze-inputs")
|
| 840 |
async def analyze_inputs(code_content: str = Form(...), language: str = Form(...)):
|
| 841 |
"""
|
|
@@ -1065,4 +1145,4 @@ input("Press enter to continue...")
|
|
| 1065 |
if __name__ == "__main__":
|
| 1066 |
import uvicorn
|
| 1067 |
|
| 1068 |
-
uvicorn.run("
|
|
|
|
| 71 |
)
|
| 72 |
resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY, MAX_MEMORY))
|
| 73 |
|
| 74 |
+
def check_available_memory(self) -> int:
|
| 75 |
+
"""Check available memory in MB"""
|
| 76 |
+
try:
|
| 77 |
+
with open('/proc/meminfo', 'r') as f:
|
| 78 |
+
meminfo = f.read()
|
| 79 |
+
|
| 80 |
+
available_match = re.search(r'MemAvailable:\s+(\d+)\s+kB', meminfo)
|
| 81 |
+
if available_match:
|
| 82 |
+
available_kb = int(available_match.group(1))
|
| 83 |
+
available_mb = available_kb // 1024
|
| 84 |
+
logger.info(f"Available memory: {available_mb}MB")
|
| 85 |
+
return available_mb
|
| 86 |
+
|
| 87 |
+
# Fallback to MemFree + Cached
|
| 88 |
+
free_match = re.search(r'MemFree:\s+(\d+)\s+kB', meminfo)
|
| 89 |
+
cached_match = re.search(r'Cached:\s+(\d+)\s+kB', meminfo)
|
| 90 |
+
|
| 91 |
+
if free_match and cached_match:
|
| 92 |
+
free_kb = int(free_match.group(1))
|
| 93 |
+
cached_kb = int(cached_match.group(1))
|
| 94 |
+
available_mb = (free_kb + cached_kb) // 1024
|
| 95 |
+
logger.info(f"Estimated available memory: {available_mb}MB")
|
| 96 |
+
return available_mb
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
logger.warning(f"Could not check memory: {e}")
|
| 100 |
+
|
| 101 |
+
return 0 # Unknown
|
| 102 |
+
|
| 103 |
+
async def _create_java_wrapper(self, workspace: str) -> str:
|
| 104 |
+
"""Create a wrapper script for Java execution with better memory handling"""
|
| 105 |
+
|
| 106 |
+
wrapper_script = """#!/bin/bash
|
| 107 |
+
# Java memory wrapper for container environments
|
| 108 |
+
|
| 109 |
+
# Get available memory
|
| 110 |
+
AVAILABLE_MEM=$(awk '/MemAvailable/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo "32")
|
| 111 |
+
|
| 112 |
+
if [ "$AVAILABLE_MEM" -lt 20 ]; then
|
| 113 |
+
echo "Error: Insufficient memory available ($AVAILABLE_MEM MB)" >&2
|
| 114 |
+
exit 1
|
| 115 |
+
fi
|
| 116 |
+
|
| 117 |
+
# Calculate heap size (30% of available, min 4MB, max 12MB)
|
| 118 |
+
HEAP_SIZE=$((AVAILABLE_MEM * 30 / 100))
|
| 119 |
+
if [ "$HEAP_SIZE" -lt 4 ]; then
|
| 120 |
+
HEAP_SIZE=4
|
| 121 |
+
elif [ "$HEAP_SIZE" -gt 12 ]; then
|
| 122 |
+
HEAP_SIZE=12
|
| 123 |
+
fi
|
| 124 |
+
|
| 125 |
+
echo "Using ${HEAP_SIZE}MB heap (${AVAILABLE_MEM}MB available)" >&2
|
| 126 |
+
|
| 127 |
+
# Execute Java with calculated settings
|
| 128 |
+
exec java -Xms1m -Xmx${HEAP_SIZE}m -Djava.awt.headless=true "$@"
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
wrapper_path = os.path.join(workspace, "java_wrapper.sh")
|
| 132 |
+
with open(wrapper_path, 'w') as f:
|
| 133 |
+
f.write(wrapper_script)
|
| 134 |
+
|
| 135 |
+
# Make executable
|
| 136 |
+
os.chmod(wrapper_path, 0o755)
|
| 137 |
+
|
| 138 |
+
return wrapper_path
|
| 139 |
+
|
| 140 |
def analyze_input_patterns(
|
| 141 |
self, language: str, file_contents: Dict[str, str]
|
| 142 |
) -> InputAnalysisResult:
|
|
|
|
| 525 |
java_files = list(Path(workspace).glob("*.java"))
|
| 526 |
needs_compilation = len(java_files) > 0
|
| 527 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 528 |
# If we have .java files, compile them
|
| 529 |
if needs_compilation:
|
| 530 |
logger.info(f"Found {len(java_files)} Java source files, compiling...")
|
| 531 |
|
| 532 |
+
# Compile with absolute minimal memory for javac
|
| 533 |
+
compile_cmd = ["javac", "-J-Xmx16m", "-J-Xms2m"] + [str(f) for f in java_files]
|
| 534 |
|
| 535 |
compile_process = await asyncio.create_subprocess_exec(
|
| 536 |
*compile_cmd,
|
|
|
|
| 574 |
|
| 575 |
logger.info("Using existing .class files, skipping compilation")
|
| 576 |
|
| 577 |
+
# Use memory-aware wrapper for execution
|
| 578 |
+
return await self._execute_java_with_wrapper(main_files, workspace, input_data)
|
| 579 |
+
|
| 580 |
+
async def _execute_java_with_wrapper(
|
| 581 |
+
self,
|
| 582 |
+
main_files: List[str],
|
| 583 |
+
workspace: str,
|
| 584 |
+
input_data: Optional[List[str]] = None,
|
| 585 |
+
) -> ExecutionResult:
|
| 586 |
+
"""Execute Java using a memory-aware wrapper script"""
|
| 587 |
+
|
| 588 |
+
try:
|
| 589 |
+
# Create wrapper script
|
| 590 |
+
wrapper_path = await self._create_java_wrapper(workspace)
|
| 591 |
+
|
| 592 |
+
results = []
|
| 593 |
+
for main_file in main_files:
|
| 594 |
+
# Determine class name
|
| 595 |
+
if main_file.endswith(".class"):
|
| 596 |
+
class_name = main_file.replace(".class", "")
|
| 597 |
+
elif main_file.endswith(".java"):
|
| 598 |
+
class_name = main_file.replace(".java", "")
|
| 599 |
+
else:
|
| 600 |
+
class_name = main_file
|
| 601 |
+
|
| 602 |
+
# Verify the .class file exists
|
| 603 |
+
class_file_path = os.path.join(workspace, f"{class_name}.class")
|
| 604 |
+
if not os.path.exists(class_file_path):
|
| 605 |
+
results.append(
|
| 606 |
+
ExecutionResult(
|
| 607 |
+
success=False,
|
| 608 |
+
stdout="",
|
| 609 |
+
stderr=f"Class file not found: {class_name}.class",
|
| 610 |
+
execution_time=0,
|
| 611 |
+
exit_code=-1,
|
| 612 |
+
)
|
| 613 |
)
|
| 614 |
+
continue
|
| 615 |
+
|
| 616 |
+
try:
|
| 617 |
+
start_time = asyncio.get_event_loop().time()
|
| 618 |
+
|
| 619 |
+
# Use wrapper script instead of direct java
|
| 620 |
+
java_cmd = [wrapper_path, class_name]
|
| 621 |
+
|
| 622 |
+
stdout, stderr, returncode = await self._execute_with_input(
|
| 623 |
+
java_cmd, workspace, input_data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
)
|
| 625 |
+
|
| 626 |
+
execution_time = asyncio.get_event_loop().time() - start_time
|
| 627 |
+
|
| 628 |
+
results.append(
|
| 629 |
+
ExecutionResult(
|
| 630 |
+
success=returncode == 0,
|
| 631 |
+
stdout=stdout.decode("utf-8", errors="replace"),
|
| 632 |
+
stderr=stderr.decode("utf-8", errors="replace"),
|
| 633 |
+
execution_time=execution_time,
|
| 634 |
+
exit_code=returncode,
|
| 635 |
+
)
|
| 636 |
)
|
| 637 |
+
|
| 638 |
+
except asyncio.TimeoutError:
|
| 639 |
+
results.append(
|
| 640 |
+
ExecutionResult(
|
| 641 |
+
success=False,
|
| 642 |
+
stdout="",
|
| 643 |
+
stderr="Execution timeout exceeded",
|
| 644 |
+
execution_time=MAX_EXECUTION_TIME,
|
| 645 |
+
exit_code=-1,
|
| 646 |
+
)
|
| 647 |
+
)
|
| 648 |
+
except Exception as e:
|
| 649 |
+
results.append(
|
| 650 |
+
ExecutionResult(
|
| 651 |
+
success=False,
|
| 652 |
+
stdout="",
|
| 653 |
+
stderr=str(e),
|
| 654 |
+
execution_time=0,
|
| 655 |
+
exit_code=-1,
|
| 656 |
+
error=str(e),
|
| 657 |
+
)
|
| 658 |
)
|
| 659 |
+
|
| 660 |
+
return self._combine_results(results, main_files)
|
| 661 |
+
|
| 662 |
+
except Exception as e:
|
| 663 |
+
return ExecutionResult(
|
| 664 |
+
success=False,
|
| 665 |
+
stdout="",
|
| 666 |
+
stderr=f"Wrapper execution failed: {str(e)}",
|
| 667 |
+
execution_time=0,
|
| 668 |
+
exit_code=-1,
|
| 669 |
+
error=str(e),
|
| 670 |
+
)
|
| 671 |
|
| 672 |
async def _execute_c_cpp(
|
| 673 |
self,
|
|
|
|
| 910 |
return detected_language
|
| 911 |
|
| 912 |
|
| 913 |
+
@app.get("/health")
|
| 914 |
+
async def health_check():
|
| 915 |
+
"""Health check endpoint"""
|
| 916 |
+
return {"status": "healthy", "timestamp": asyncio.get_event_loop().time()}
|
| 917 |
+
|
| 918 |
+
|
| 919 |
@app.post("/analyze-inputs")
|
| 920 |
async def analyze_inputs(code_content: str = Form(...), language: str = Form(...)):
|
| 921 |
"""
|
|
|
|
| 1145 |
if __name__ == "__main__":
|
| 1146 |
import uvicorn
|
| 1147 |
|
| 1148 |
+
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
|