Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- inference.py +21 -61
- server/app.py +26 -31
inference.py
CHANGED
|
@@ -1,47 +1,3 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Inference Script Example
|
| 3 |
-
===================================
|
| 4 |
-
MANDATORY
|
| 5 |
-
- Before submitting, ensure the following variables are defined in your environment configuration:
|
| 6 |
-
API_BASE_URL The API endpoint for the LLM.
|
| 7 |
-
MODEL_NAME The model identifier to use for inference.
|
| 8 |
-
HF_TOKEN Your Hugging Face / API key.
|
| 9 |
-
LOCAL_IMAGE_NAME The name of the local image to use for the environment if you are using from_docker_image()
|
| 10 |
-
method
|
| 11 |
-
|
| 12 |
-
- Defaults are set only for API_BASE_URL and MODEL_NAME
|
| 13 |
-
(and should reflect your active inference setup):
|
| 14 |
-
API_BASE_URL = os.getenv("API_BASE_URL", "<your-active-endpoint>")
|
| 15 |
-
MODEL_NAME = os.getenv("MODEL_NAME", "<your-active-model>")
|
| 16 |
-
|
| 17 |
-
- The inference script must be named `inference.py` and placed in the root directory of the project
|
| 18 |
-
- Participants must use OpenAI Client for all LLM calls using above variables
|
| 19 |
-
|
| 20 |
-
STDOUT FORMAT
|
| 21 |
-
- The script must emit exactly three line types to stdout, in this order:
|
| 22 |
-
|
| 23 |
-
[START] task=<task_name> env=<benchmark> model=<model_name>
|
| 24 |
-
[STEP] step=<n> action=<action_str> reward=<0.00> done=<true|false> error=<msg|null>
|
| 25 |
-
[END] success=<true|false> steps=<n> score=<score> rewards=<r1,r2,...,rn>
|
| 26 |
-
|
| 27 |
-
Rules:
|
| 28 |
-
- One [START] line at episode begin.
|
| 29 |
-
- One [STEP] line per step, immediately after env.step() returns.
|
| 30 |
-
- One [END] line after env.close(), always emitted (even on exception).
|
| 31 |
-
- reward and rewards are formatted to 2 decimal places.
|
| 32 |
-
- done and success are lowercase booleans: true or false.
|
| 33 |
-
- error is the raw last_action_error string, or null if none.
|
| 34 |
-
- All fields on a single line with no newlines within a line.
|
| 35 |
-
- Each tasks should return score in [0, 1]
|
| 36 |
-
|
| 37 |
-
Example:
|
| 38 |
-
[START] task=click-test env=miniwob model=Qwen3-VL-30B
|
| 39 |
-
[STEP] step=1 action=click('123') reward=0.00 done=false error=null
|
| 40 |
-
[STEP] step=2 action=fill('456','text') reward=0.00 done=false error=null
|
| 41 |
-
[STEP] step=3 action=click('789') reward=1.00 done=true error=null
|
| 42 |
-
[END] success=true steps=3 score=1.00 rewards=0.00,0.00,1.00
|
| 43 |
-
"""
|
| 44 |
-
|
| 45 |
import asyncio
|
| 46 |
import os
|
| 47 |
import sys
|
|
@@ -53,7 +9,7 @@ from openai import OpenAI
|
|
| 53 |
from dotenv import load_dotenv
|
| 54 |
|
| 55 |
# --- PATH INJECTION ---
|
| 56 |
-
# Ensures local 'client' and 'models' are prioritized over installed
|
| 57 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 58 |
|
| 59 |
try:
|
|
@@ -68,11 +24,14 @@ load_dotenv()
|
|
| 68 |
# --- CONFIGURATION ---
|
| 69 |
# API_KEY is sourced from HF_TOKEN for hackathon compliance
|
| 70 |
API_KEY = os.environ.get("HF_TOKEN") or os.environ.get("API_KEY")
|
| 71 |
-
|
| 72 |
-
|
| 73 |
MODEL_NAME = os.environ.get("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
|
| 74 |
|
| 75 |
-
#
|
|
|
|
|
|
|
|
|
|
| 76 |
ADDRESS = (os.environ.get("ADDRESS") or "https://anonajay-lint-coding-agent.hf.space/v1").rstrip("/")
|
| 77 |
|
| 78 |
TASK_NAME = "multi-lang-lint-v1"
|
|
@@ -91,7 +50,7 @@ def log_start(task: str, env: str, model: str):
|
|
| 91 |
print(f"[START] task={task} env={env} model={model}", flush=True)
|
| 92 |
|
| 93 |
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]):
|
| 94 |
-
#
|
| 95 |
action_clean = str(action).replace("\n", " ").replace("\r", " ").strip()[:50]
|
| 96 |
err_str = str(error).replace("\n", " ") if error else "null"
|
| 97 |
print(f"[STEP] step={step} action={action_clean} reward={reward:.2f} done={str(done).lower()} error={err_str}", flush=True)
|
|
@@ -101,9 +60,8 @@ def log_end(success: bool, steps: int, score: float, rewards: List[float]):
|
|
| 101 |
print(f"[END] success={str(success).lower()} steps={steps} score={score:.2f} rewards={rewards_str}", flush=True)
|
| 102 |
|
| 103 |
async def main():
|
| 104 |
-
# OpenAI client pointing to the Hugging Face
|
| 105 |
-
|
| 106 |
-
llm_client = OpenAI(base_url=API_BASE_URL.rstrip("/"), api_key=API_KEY)
|
| 107 |
|
| 108 |
rewards, steps_taken, success = [], 0, False
|
| 109 |
last_error = None
|
|
@@ -111,7 +69,7 @@ async def main():
|
|
| 111 |
log_start(task=TASK_NAME, env=BENCHMARK, model=MODEL_NAME)
|
| 112 |
|
| 113 |
try:
|
| 114 |
-
# Connecting to the Environment Space
|
| 115 |
async with LintCodingAgentEnv(
|
| 116 |
base_url=ADDRESS,
|
| 117 |
headers={
|
|
@@ -121,14 +79,14 @@ async def main():
|
|
| 121 |
}
|
| 122 |
) as env:
|
| 123 |
|
| 124 |
-
# π
|
| 125 |
result = await env.reset()
|
| 126 |
|
| 127 |
for step in range(1, MAX_STEPS + 1):
|
| 128 |
obs = result.observation
|
| 129 |
steps_taken = step
|
| 130 |
|
| 131 |
-
# Context Construction
|
| 132 |
user_prompt = (
|
| 133 |
f"LEVEL: {obs.level}\n"
|
| 134 |
f"LANGUAGE: {obs.language}\n"
|
|
@@ -136,7 +94,7 @@ async def main():
|
|
| 136 |
f"OBJECTIVE: {obs.problem_statement}"
|
| 137 |
)
|
| 138 |
|
| 139 |
-
# LLM Inference with
|
| 140 |
completion = llm_client.chat.completions.create(
|
| 141 |
model=MODEL_NAME,
|
| 142 |
messages=[
|
|
@@ -149,7 +107,7 @@ async def main():
|
|
| 149 |
|
| 150 |
action_text = (completion.choices[0].message.content or "").strip()
|
| 151 |
|
| 152 |
-
#
|
| 153 |
result = await env.step(LintCodingAgentAction(
|
| 154 |
code_solution=action_text,
|
| 155 |
explanation=f"Architect Fix: Level {obs.level}"
|
|
@@ -161,18 +119,20 @@ async def main():
|
|
| 161 |
log_step(step, action_text, current_reward, result.done, None)
|
| 162 |
|
| 163 |
if result.done:
|
| 164 |
-
# Success
|
| 165 |
success = (sum(rewards) >= 1.0)
|
| 166 |
break
|
| 167 |
|
| 168 |
except Exception as e:
|
| 169 |
last_error = str(e)
|
|
|
|
| 170 |
log_step(steps_taken + 1, "execution_fault", 0.0, True, last_error)
|
| 171 |
|
| 172 |
finally:
|
| 173 |
-
# Score
|
| 174 |
-
|
| 175 |
-
|
|
|
|
| 176 |
|
| 177 |
if __name__ == "__main__":
|
| 178 |
asyncio.run(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import os
|
| 3 |
import sys
|
|
|
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
|
| 11 |
# --- PATH INJECTION ---
|
| 12 |
+
# Ensures local 'client' and 'models' are prioritized over installed packages
|
| 13 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 14 |
|
| 15 |
try:
|
|
|
|
| 24 |
# --- CONFIGURATION ---
|
| 25 |
# API_KEY is sourced from HF_TOKEN for hackathon compliance
|
| 26 |
API_KEY = os.environ.get("HF_TOKEN") or os.environ.get("API_KEY")
|
| 27 |
+
|
| 28 |
+
# MODEL IDENTIFIER
|
| 29 |
MODEL_NAME = os.environ.get("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
|
| 30 |
|
| 31 |
+
# API_BASE_URL: Set to the model-specific v1 endpoint to bypass Router 404s
|
| 32 |
+
API_BASE_URL = os.environ.get("API_BASE_URL") or f"https://api-inference.huggingface.co/models/{MODEL_NAME}/v1"
|
| 33 |
+
|
| 34 |
+
# ADDRESS: Points to your Hugging Face Space API mount (/v1)
|
| 35 |
ADDRESS = (os.environ.get("ADDRESS") or "https://anonajay-lint-coding-agent.hf.space/v1").rstrip("/")
|
| 36 |
|
| 37 |
TASK_NAME = "multi-lang-lint-v1"
|
|
|
|
| 50 |
print(f"[START] task={task} env={env} model={model}", flush=True)
|
| 51 |
|
| 52 |
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]):
|
| 53 |
+
# Sanitize action string for single-line STDOUT compliance
|
| 54 |
action_clean = str(action).replace("\n", " ").replace("\r", " ").strip()[:50]
|
| 55 |
err_str = str(error).replace("\n", " ") if error else "null"
|
| 56 |
print(f"[STEP] step={step} action={action_clean} reward={reward:.2f} done={str(done).lower()} error={err_str}", flush=True)
|
|
|
|
| 60 |
print(f"[END] success={str(success).lower()} steps={steps} score={score:.2f} rewards={rewards_str}", flush=True)
|
| 61 |
|
| 62 |
async def main():
|
| 63 |
+
# OpenAI client pointing to the model-specific Hugging Face endpoint
|
| 64 |
+
llm_client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
|
|
|
|
| 65 |
|
| 66 |
rewards, steps_taken, success = [], 0, False
|
| 67 |
last_error = None
|
|
|
|
| 69 |
log_start(task=TASK_NAME, env=BENCHMARK, model=MODEL_NAME)
|
| 70 |
|
| 71 |
try:
|
| 72 |
+
# Connecting to the Environment Space with security headers
|
| 73 |
async with LintCodingAgentEnv(
|
| 74 |
base_url=ADDRESS,
|
| 75 |
headers={
|
|
|
|
| 79 |
}
|
| 80 |
) as env:
|
| 81 |
|
| 82 |
+
# π Initial Reset
|
| 83 |
result = await env.reset()
|
| 84 |
|
| 85 |
for step in range(1, MAX_STEPS + 1):
|
| 86 |
obs = result.observation
|
| 87 |
steps_taken = step
|
| 88 |
|
| 89 |
+
# Context Construction for the LLM
|
| 90 |
user_prompt = (
|
| 91 |
f"LEVEL: {obs.level}\n"
|
| 92 |
f"LANGUAGE: {obs.language}\n"
|
|
|
|
| 94 |
f"OBJECTIVE: {obs.problem_statement}"
|
| 95 |
)
|
| 96 |
|
| 97 |
+
# LLM Inference with 'wait-for-model' to prevent 503/404 during cold starts
|
| 98 |
completion = llm_client.chat.completions.create(
|
| 99 |
model=MODEL_NAME,
|
| 100 |
messages=[
|
|
|
|
| 107 |
|
| 108 |
action_text = (completion.choices[0].message.content or "").strip()
|
| 109 |
|
| 110 |
+
# Execute Action via the Environment Bridge
|
| 111 |
result = await env.step(LintCodingAgentAction(
|
| 112 |
code_solution=action_text,
|
| 113 |
explanation=f"Architect Fix: Level {obs.level}"
|
|
|
|
| 119 |
log_step(step, action_text, current_reward, result.done, None)
|
| 120 |
|
| 121 |
if result.done:
|
| 122 |
+
# Success is defined as clearing the cumulative reward threshold
|
| 123 |
success = (sum(rewards) >= 1.0)
|
| 124 |
break
|
| 125 |
|
| 126 |
except Exception as e:
|
| 127 |
last_error = str(e)
|
| 128 |
+
# Log the fault as a step to maintain sequence
|
| 129 |
log_step(steps_taken + 1, "execution_fault", 0.0, True, last_error)
|
| 130 |
|
| 131 |
finally:
|
| 132 |
+
# Final Score Calculation (Normalized to 0.00 - 1.00)
|
| 133 |
+
avg_reward = sum(rewards) / len(rewards) if rewards else 0.0
|
| 134 |
+
# If any step succeeded (reward 1.0), success should be true for the sprint
|
| 135 |
+
log_end(success, steps_taken, min(avg_reward, 1.0), rewards)
|
| 136 |
|
| 137 |
if __name__ == "__main__":
|
| 138 |
asyncio.run(main())
|
server/app.py
CHANGED
|
@@ -17,7 +17,7 @@ logging.basicConfig(
|
|
| 17 |
logger = logging.getLogger("ArchitectApp")
|
| 18 |
|
| 19 |
# --- ARCHITECT'S PATH INJECTION ---
|
| 20 |
-
#
|
| 21 |
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 22 |
project_root = os.path.abspath(os.path.join(current_dir, ".."))
|
| 23 |
|
|
@@ -32,12 +32,11 @@ except ImportError:
|
|
| 32 |
from openenv.core.env_server.http_server import create_app
|
| 33 |
|
| 34 |
# --- DOMAIN SPECIFIC IMPORTS ---
|
| 35 |
-
# These are the schemas and logic we verified in previous steps
|
| 36 |
from models import LintCodingAgentAction, LintCodingAgentObservation
|
| 37 |
from server.lint_coding_agent_environment import LintCodingAgentEnvironment
|
| 38 |
|
| 39 |
# --- APP FACTORY ---
|
| 40 |
-
# 1. Create the
|
| 41 |
base_app = create_app(
|
| 42 |
LintCodingAgentEnvironment,
|
| 43 |
LintCodingAgentAction,
|
|
@@ -45,26 +44,30 @@ base_app = create_app(
|
|
| 45 |
)
|
| 46 |
|
| 47 |
# 2. Create the Main Wrapper App
|
| 48 |
-
# redirect_slashes=False is
|
| 49 |
app = FastAPI(
|
| 50 |
title="AnonAjay Architect API",
|
| 51 |
redirect_slashes=False
|
| 52 |
)
|
| 53 |
|
| 54 |
# --- DEBUG MIDDLEWARE ---
|
| 55 |
-
# Real-time telemetry for every request hitting the Space
|
| 56 |
@app.middleware("http")
|
| 57 |
async def debug_logging(request: Request, call_next):
|
| 58 |
-
logger.info(f"π‘
|
| 59 |
response = await call_next(request)
|
| 60 |
logger.info(f"β
Outcome: {response.status_code}")
|
| 61 |
return response
|
| 62 |
|
| 63 |
-
# --- MOUNTING (The
|
| 64 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
app.mount("/v1", base_app)
|
| 66 |
|
| 67 |
-
# --- CORS
|
| 68 |
app.add_middleware(
|
| 69 |
CORSMiddleware,
|
| 70 |
allow_origins=["*"],
|
|
@@ -75,39 +78,31 @@ app.add_middleware(
|
|
| 75 |
|
| 76 |
@app.get("/health")
|
| 77 |
def health_check():
|
| 78 |
-
"""
|
| 79 |
-
return {"status": "online", "
|
| 80 |
|
| 81 |
-
# Catch-all for the root to prevent confusing 404s in the browser
|
| 82 |
@app.get("/")
|
| 83 |
-
def
|
|
|
|
| 84 |
return {
|
| 85 |
-
"message": "
|
| 86 |
-
"
|
| 87 |
-
|
| 88 |
-
"health": "/health",
|
| 89 |
-
"api_root": "/v1"
|
| 90 |
-
}
|
| 91 |
}
|
| 92 |
|
| 93 |
def main():
|
| 94 |
-
"""
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
"""
|
| 98 |
-
parser = argparse.ArgumentParser()
|
| 99 |
-
parser.add_argument("--host", type=str, default="0.0.0.0")
|
| 100 |
-
parser.add_argument("--port", type=int, default=7860)
|
| 101 |
-
args = parser.parse_args()
|
| 102 |
|
| 103 |
-
logger.info(f"π OpenEnv Sandbox
|
| 104 |
-
logger.info(f"π Geometry: /v1
|
| 105 |
|
| 106 |
# proxy_headers + forwarded_allow_ips are vital for HF reverse proxy support
|
| 107 |
uvicorn.run(
|
| 108 |
app,
|
| 109 |
-
host=
|
| 110 |
-
port=
|
| 111 |
proxy_headers=True,
|
| 112 |
forwarded_allow_ips="*"
|
| 113 |
)
|
|
|
|
| 17 |
logger = logging.getLogger("ArchitectApp")
|
| 18 |
|
| 19 |
# --- ARCHITECT'S PATH INJECTION ---
|
| 20 |
+
# Ensures 'models' and environment logic are discoverable regardless of the launch root
|
| 21 |
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 22 |
project_root = os.path.abspath(os.path.join(current_dir, ".."))
|
| 23 |
|
|
|
|
| 32 |
from openenv.core.env_server.http_server import create_app
|
| 33 |
|
| 34 |
# --- DOMAIN SPECIFIC IMPORTS ---
|
|
|
|
| 35 |
from models import LintCodingAgentAction, LintCodingAgentObservation
|
| 36 |
from server.lint_coding_agent_environment import LintCodingAgentEnvironment
|
| 37 |
|
| 38 |
# --- APP FACTORY ---
|
| 39 |
+
# 1. Create the internal OpenEnv engine first
|
| 40 |
base_app = create_app(
|
| 41 |
LintCodingAgentEnvironment,
|
| 42 |
LintCodingAgentAction,
|
|
|
|
| 44 |
)
|
| 45 |
|
| 46 |
# 2. Create the Main Wrapper App
|
| 47 |
+
# redirect_slashes=False is non-negotiable for Hugging Face Proxy stability
|
| 48 |
app = FastAPI(
|
| 49 |
title="AnonAjay Architect API",
|
| 50 |
redirect_slashes=False
|
| 51 |
)
|
| 52 |
|
| 53 |
# --- DEBUG MIDDLEWARE ---
|
|
|
|
| 54 |
@app.middleware("http")
|
| 55 |
async def debug_logging(request: Request, call_next):
|
| 56 |
+
logger.info(f"π‘ Incoming: {request.method} {request.url.path}")
|
| 57 |
response = await call_next(request)
|
| 58 |
logger.info(f"β
Outcome: {response.status_code}")
|
| 59 |
return response
|
| 60 |
|
| 61 |
+
# --- MOUNTING (The "Zero-Failure" Strategy) ---
|
| 62 |
+
# Explicitly map the root of /v1 to prevent the 404s seen in previous logs
|
| 63 |
+
@app.get("/v1")
|
| 64 |
+
async def v1_root():
|
| 65 |
+
return {"message": "OpenEnv API Root Active", "docs": "/v1/docs"}
|
| 66 |
+
|
| 67 |
+
# Mount the OpenEnv engine at /v1
|
| 68 |
app.mount("/v1", base_app)
|
| 69 |
|
| 70 |
+
# --- CORS & HEALTH ---
|
| 71 |
app.add_middleware(
|
| 72 |
CORSMiddleware,
|
| 73 |
allow_origins=["*"],
|
|
|
|
| 78 |
|
| 79 |
@app.get("/health")
|
| 80 |
def health_check():
|
| 81 |
+
"""Heartbeat for Hugging Face and Docker health monitors."""
|
| 82 |
+
return {"status": "online", "port": 7860, "vfs_ready": True}
|
| 83 |
|
|
|
|
| 84 |
@app.get("/")
|
| 85 |
+
def root_redirect():
|
| 86 |
+
"""Provides a clear map for manual verification."""
|
| 87 |
return {
|
| 88 |
+
"message": "Lead Architect Server Live",
|
| 89 |
+
"v1_docs": "/v1/docs",
|
| 90 |
+
"health": "/health"
|
|
|
|
|
|
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
def main():
|
| 94 |
+
"""Standardized entry point for Hugging Face Spaces."""
|
| 95 |
+
# Use environment PORT or default to 7860
|
| 96 |
+
port = int(os.environ.get("PORT", 7860))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
+
logger.info(f"π Launching OpenEnv Sandbox on Port {port}")
|
| 99 |
+
logger.info(f"π Geometry: /v1 mounted with redirect_slashes=False")
|
| 100 |
|
| 101 |
# proxy_headers + forwarded_allow_ips are vital for HF reverse proxy support
|
| 102 |
uvicorn.run(
|
| 103 |
app,
|
| 104 |
+
host="0.0.0.0",
|
| 105 |
+
port=port,
|
| 106 |
proxy_headers=True,
|
| 107 |
forwarded_allow_ips="*"
|
| 108 |
)
|