Spaces:
Sleeping
Sleeping
fix(inference): resolve connection exception and align ports to 8002
Browse files- fix: asyncio connection exception by adding wait_for_server retry logic
- feat: implement structured logging [START], [STEP], [END] for hackathon validation
- chore: unify app port to 8002 across Dockerfile, app.py, README.md, and openenv.yaml
- chore: use mandated environment variables (API_BASE_URL, MODEL_NAME, HF_TOKEN)
- docs: update project summary and reminders
- Dockerfile +4 -4
- README.md +1 -1
- REMINDERS.md +16 -0
- inference.py +119 -25
- server/app.py +8 -3
Dockerfile
CHANGED
|
@@ -20,15 +20,15 @@ COPY . .
|
|
| 20 |
|
| 21 |
# Set environment variables
|
| 22 |
ENV PYTHONPATH="/app"
|
| 23 |
-
ENV PORT=
|
| 24 |
ENV HOST=0.0.0.0
|
| 25 |
|
| 26 |
# Expose the app port
|
| 27 |
-
EXPOSE
|
| 28 |
|
| 29 |
# Health check
|
| 30 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
| 31 |
-
CMD curl -f http://localhost:
|
| 32 |
|
| 33 |
# Start the server
|
| 34 |
-
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "
|
|
|
|
| 20 |
|
| 21 |
# Set environment variables
|
| 22 |
ENV PYTHONPATH="/app"
|
| 23 |
+
ENV PORT=8002
|
| 24 |
ENV HOST=0.0.0.0
|
| 25 |
|
| 26 |
# Expose the app port
|
| 27 |
+
EXPOSE 8002
|
| 28 |
|
| 29 |
# Health check
|
| 30 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
| 31 |
+
CMD curl -f http://localhost:8002/health || exit 1
|
| 32 |
|
| 33 |
# Start the server
|
| 34 |
+
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8002"]
|
README.md
CHANGED
|
@@ -3,7 +3,7 @@ title: MedTriage OpenEnv
|
|
| 3 |
emoji: 🏥
|
| 4 |
sdk: docker
|
| 5 |
pinned: false
|
| 6 |
-
app_port:
|
| 7 |
tags:
|
| 8 |
- openenv
|
| 9 |
- healthcare
|
|
|
|
| 3 |
emoji: 🏥
|
| 4 |
sdk: docker
|
| 5 |
pinned: false
|
| 6 |
+
app_port: 8002
|
| 7 |
tags:
|
| 8 |
- openenv
|
| 9 |
- healthcare
|
REMINDERS.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🗓️ Hackathon Schedule & Reminders
|
| 2 |
+
|
| 3 |
+
## 🚀 Key Dates
|
| 4 |
+
- **April 8, 2026**: Round 1 Submission Deadline (Completed ✓)
|
| 5 |
+
- **April 10, 2026**: Round 1 Results Announcement 🔔
|
| 6 |
+
- **April 25-26, 2026**: Grand Finale (If qualified)
|
| 7 |
+
|
| 8 |
+
## 🔗 Links
|
| 9 |
+
- **Dashboard**: [Meta PyTorch Hackathon Dashboard](https://www.scaler.com/school-of-technology/meta-pytorch-hackathon/dashboard)
|
| 10 |
+
- **Hugging Face Space**: [ashdev/med-triage-openenv](https://huggingface.co/spaces/ashdev/med-triage-openenv)
|
| 11 |
+
- **GitHub Repo**: [ash399/med-triage-openenv](https://github.com/ash399/med-triage-openenv)
|
| 12 |
+
|
| 13 |
+
## 📝 Post-Submission Notes
|
| 14 |
+
- All automated checks passed on March 30th.
|
| 15 |
+
- Environment is running on port 7860.
|
| 16 |
+
- `PROJECT_SUMMARY.md` contains the full technical breakdown.
|
inference.py
CHANGED
|
@@ -3,44 +3,138 @@
|
|
| 3 |
|
| 4 |
import os
|
| 5 |
import time
|
| 6 |
-
import
|
| 7 |
import requests
|
|
|
|
|
|
|
| 8 |
from client import MedTriageEnv
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
def run_baseline(base_url: str = "http://localhost:8002"):
|
| 11 |
"""Run baseline agent against all 3 tasks and return results."""
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
tasks = ["TASK_EASY", "TASK_MEDIUM", "TASK_HARD"]
|
| 15 |
-
|
| 16 |
|
| 17 |
-
# Simple heuristic-based baseline (no LLM required for this local test)
|
| 18 |
try:
|
| 19 |
from client import MedTriageEnv
|
| 20 |
except ImportError:
|
| 21 |
from .client import MedTriageEnv
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
|
|
|
|
|
|
|
| 43 |
|
| 44 |
if __name__ == "__main__":
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
import os
|
| 5 |
import time
|
| 6 |
+
import json
|
| 7 |
import requests
|
| 8 |
+
import asyncio
|
| 9 |
+
from typing import List, Dict, Any
|
| 10 |
from client import MedTriageEnv
|
| 11 |
|
| 12 |
+
# --- Mandatory Environment Configuration ---
|
| 13 |
+
API_BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8002/v1")
|
| 14 |
+
MODEL_NAME = os.getenv("MODEL_NAME", "med-triage-baseline")
|
| 15 |
+
HF_TOKEN = os.getenv("HF_TOKEN", "dummy-token")
|
| 16 |
+
|
| 17 |
+
# --- Structured Logging Functions ---
|
| 18 |
+
def log_start(task: str, env: str, model: str):
|
| 19 |
+
"""Emit structured [START] log."""
|
| 20 |
+
data = {
|
| 21 |
+
"timestamp": time.time(),
|
| 22 |
+
"task": task,
|
| 23 |
+
"env": env,
|
| 24 |
+
"model": model,
|
| 25 |
+
"config": {
|
| 26 |
+
"api_base": API_BASE_URL,
|
| 27 |
+
"max_steps": 10
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
print(f"[START] {json.dumps(data)}", flush=True)
|
| 31 |
+
|
| 32 |
+
def log_step(step: int, action: Any, reward: float, done: bool, error: str = None):
|
| 33 |
+
"""Emit structured [STEP] log."""
|
| 34 |
+
data = {
|
| 35 |
+
"timestamp": time.time(),
|
| 36 |
+
"step": step,
|
| 37 |
+
"action": action,
|
| 38 |
+
"reward": reward,
|
| 39 |
+
"done": done,
|
| 40 |
+
"error": error
|
| 41 |
+
}
|
| 42 |
+
print(f"[STEP] {json.dumps(data)}", flush=True)
|
| 43 |
+
|
| 44 |
+
def log_end(success: bool, steps: int, score: float, rewards: List[float]):
|
| 45 |
+
"""Emit structured [END] log."""
|
| 46 |
+
data = {
|
| 47 |
+
"timestamp": time.time(),
|
| 48 |
+
"success": success,
|
| 49 |
+
"steps_taken": steps,
|
| 50 |
+
"score": score,
|
| 51 |
+
"rewards": rewards
|
| 52 |
+
}
|
| 53 |
+
print(f"[END] {json.dumps(data)}", flush=True)
|
| 54 |
+
|
| 55 |
+
# --- Server Readiness Check ---
|
| 56 |
+
def wait_for_server(url: str, timeout: int = 30):
|
| 57 |
+
"""Wait for the server to be ready before proceeding."""
|
| 58 |
+
start_time = time.time()
|
| 59 |
+
health_url = f"{url.rstrip('/')}/health"
|
| 60 |
+
|
| 61 |
+
while time.time() - start_time < timeout:
|
| 62 |
+
try:
|
| 63 |
+
response = requests.get(health_url, timeout=2)
|
| 64 |
+
if response.status_code == 200:
|
| 65 |
+
return True
|
| 66 |
+
except (requests.RequestException, ConnectionError):
|
| 67 |
+
pass
|
| 68 |
+
time.sleep(1)
|
| 69 |
+
return False
|
| 70 |
+
|
| 71 |
def run_baseline(base_url: str = "http://localhost:8002"):
|
| 72 |
"""Run baseline agent against all 3 tasks and return results."""
|
| 73 |
+
# Ensure base_url uses the port from environment if available
|
| 74 |
+
env_port = os.environ.get("PORT", "8002")
|
| 75 |
+
if "localhost" in base_url and f":{env_port}" not in base_url:
|
| 76 |
+
base_url = f"http://localhost:{env_port}"
|
| 77 |
+
|
| 78 |
+
# Wait for server readiness
|
| 79 |
+
wait_for_server(base_url)
|
| 80 |
+
|
| 81 |
tasks = ["TASK_EASY", "TASK_MEDIUM", "TASK_HARD"]
|
| 82 |
+
all_scores = {}
|
| 83 |
|
|
|
|
| 84 |
try:
|
| 85 |
from client import MedTriageEnv
|
| 86 |
except ImportError:
|
| 87 |
from .client import MedTriageEnv
|
| 88 |
|
| 89 |
+
for task_id in tasks:
|
| 90 |
+
log_start(task=task_id, env="med_triage_env", model=MODEL_NAME)
|
| 91 |
+
|
| 92 |
+
rewards = []
|
| 93 |
+
steps_taken = 0
|
| 94 |
+
success = False
|
| 95 |
+
final_score = 0.0
|
| 96 |
+
|
| 97 |
+
try:
|
| 98 |
+
with MedTriageEnv(base_url=base_url).sync() as env:
|
| 99 |
+
obs = env.reset(task_id=task_id)
|
| 100 |
+
steps_taken = 1
|
| 101 |
+
|
| 102 |
+
# Heuristic Logic (mimes model output)
|
| 103 |
+
bp_sys = int(obs.vitals.get("bp", "120/80").split("/")[0])
|
| 104 |
+
if bp_sys > 150 or obs.age > 65:
|
| 105 |
+
level = 3 # EMERGENCY
|
| 106 |
+
elif "severe pain" in obs.symptoms_text.lower():
|
| 107 |
+
level = 2 # URGENT_CARE
|
| 108 |
+
else:
|
| 109 |
+
level = 0 # SELF_CARE
|
| 110 |
+
|
| 111 |
+
action = {"tool_name": "triage_patient", "arguments": {"level": level, "reasoning": "Heuristic baseline."}}
|
| 112 |
+
result = env.step(action)
|
| 113 |
+
|
| 114 |
+
reward = result.reward or 0.0
|
| 115 |
+
done = result.done
|
| 116 |
+
rewards.append(reward)
|
| 117 |
|
| 118 |
+
log_step(step=1, action=action, reward=reward, done=done)
|
| 119 |
+
|
| 120 |
+
final_score = reward
|
| 121 |
+
success = reward >= 1.0
|
| 122 |
+
all_scores[task_id] = reward
|
| 123 |
+
|
| 124 |
+
except Exception as e:
|
| 125 |
+
log_step(step=steps_taken, action=None, reward=0.0, done=True, error=str(e))
|
| 126 |
+
all_scores[task_id] = 0.0
|
| 127 |
|
| 128 |
+
log_end(success=success, steps=steps_taken, score=final_score, rewards=rewards)
|
| 129 |
+
|
| 130 |
+
return all_scores
|
| 131 |
|
| 132 |
if __name__ == "__main__":
|
| 133 |
+
try:
|
| 134 |
+
results = run_baseline()
|
| 135 |
+
# Final summary for human readability
|
| 136 |
+
print(f"\n📊 FINAL BASELINE SCORES: {results}")
|
| 137 |
+
except Exception as e:
|
| 138 |
+
print(f"FATAL: {e}")
|
| 139 |
+
import sys
|
| 140 |
+
sys.exit(1)
|
server/app.py
CHANGED
|
@@ -70,9 +70,12 @@ async def trigger_baseline():
|
|
| 70 |
if parent_dir not in sys.path:
|
| 71 |
sys.path.append(parent_dir)
|
| 72 |
from inference import run_baseline
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
| 74 |
# Execute actual baseline
|
| 75 |
-
scores = run_baseline(base_url="http://localhost:
|
| 76 |
|
| 77 |
return {
|
| 78 |
"status": "baseline_completed",
|
|
@@ -81,7 +84,9 @@ async def trigger_baseline():
|
|
| 81 |
|
| 82 |
def main():
|
| 83 |
import uvicorn
|
| 84 |
-
|
|
|
|
|
|
|
| 85 |
|
| 86 |
if __name__ == "__main__":
|
| 87 |
main()
|
|
|
|
| 70 |
if parent_dir not in sys.path:
|
| 71 |
sys.path.append(parent_dir)
|
| 72 |
from inference import run_baseline
|
| 73 |
+
|
| 74 |
+
# Get current port from environment or default to 8002
|
| 75 |
+
port = os.environ.get("PORT", "8002")
|
| 76 |
+
|
| 77 |
# Execute actual baseline
|
| 78 |
+
scores = run_baseline(base_url=f"http://localhost:{port}")
|
| 79 |
|
| 80 |
return {
|
| 81 |
"status": "baseline_completed",
|
|
|
|
| 84 |
|
| 85 |
def main():
|
| 86 |
import uvicorn
|
| 87 |
+
import os
|
| 88 |
+
port = int(os.environ.get("PORT", 8002))
|
| 89 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
| 90 |
|
| 91 |
if __name__ == "__main__":
|
| 92 |
main()
|