anonajay commited on
Commit
19f02f2
Β·
verified Β·
1 Parent(s): 598f6bf

Upload folder using huggingface_hub

Browse files
Dockerfile CHANGED
@@ -56,4 +56,8 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
56
 
57
  # Start the server on 7860 with proxy support
58
  # 'proxy_headers' is vital for HF reverse proxies to handle WebSockets correctly
59
- CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860", "--proxy-headers", "--forwarded-allow-ips", "*"]
 
 
 
 
 
56
 
57
  # Start the server on 7860 with proxy support
58
  # 'proxy_headers' is vital for HF reverse proxies to handle WebSockets correctly
59
+ CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860", "--proxy-headers", "--forwarded-allow-ips", "*"]
60
+
61
+ WORKDIR /app/env
62
+ COPY . .
63
+ # This places QUESTIONS.json at /app/env/server/QUESTIONS.json
server/app.py CHANGED
@@ -1,32 +1,5 @@
1
  # Copyright (c) Meta Platforms, Inc. and affiliates.
2
  # All rights reserved.
3
- #
4
- # This source code is licensed under the BSD-style license found in the
5
- # LICENSE file in the root directory of this source tree.
6
-
7
- """
8
- FastAPI application for the Lint Coding Agent Environment.
9
-
10
- This module creates an HTTP server that exposes the LintCodingAgentEnvironment
11
- over HTTP and WebSocket endpoints, compatible with EnvClient.
12
-
13
- Endpoints:
14
- - POST /reset: Reset the environment
15
- - POST /step: Execute an action
16
- - GET /state: Get current environment state
17
- - GET /schema: Get action/observation schemas
18
- - WS /ws: WebSocket endpoint for persistent sessions
19
-
20
- Usage:
21
- # Development (with auto-reload):
22
- uvicorn server.app:app --reload --host 0.0.0.0 --port 8000
23
-
24
- # Production:
25
- uvicorn server.app:app --host 0.0.0.0 --port 8000 --workers 4
26
-
27
- # Or run directly:
28
- python -m server.app
29
- """
30
 
31
  import sys
32
  import os
@@ -44,10 +17,11 @@ logging.basicConfig(
44
  logger = logging.getLogger("ArchitectApp")
45
 
46
  # --- ARCHITECT'S PATH INJECTION ---
 
47
  current_dir = os.path.dirname(os.path.abspath(__file__))
48
  project_root = os.path.abspath(os.path.join(current_dir, ".."))
49
 
50
- for path in [project_root, current_dir]:
51
  if path not in sys.path:
52
  sys.path.insert(0, path)
53
 
@@ -58,11 +32,12 @@ except ImportError:
58
  from openenv.core.env_server.http_server import create_app
59
 
60
  # --- DOMAIN SPECIFIC IMPORTS ---
 
61
  from models import LintCodingAgentAction, LintCodingAgentObservation
62
  from server.lint_coding_agent_environment import LintCodingAgentEnvironment
63
 
64
  # --- APP FACTORY ---
65
- # 1. Create the environment logic app
66
  base_app = create_app(
67
  LintCodingAgentEnvironment,
68
  LintCodingAgentAction,
@@ -70,19 +45,23 @@ base_app = create_app(
70
  )
71
 
72
  # 2. Create the Main Wrapper App
73
- app = FastAPI(title="AnonAjay Architect API")
 
 
 
 
74
 
75
  # --- DEBUG MIDDLEWARE ---
76
- # This will print every single request coming from Hugging Face to your logs
77
  @app.middleware("http")
78
  async def debug_logging(request: Request, call_next):
79
  logger.info(f"πŸ“‘ Request: {request.method} {request.url.path}")
80
  response = await call_next(request)
81
- logger.info(f"βœ… Response Status: {response.status_code}")
82
  return response
83
 
84
  # --- MOUNTING (The /v1 Fix) ---
85
- # This ensures that /v1/reset and /v1/ws are explicitly mapped
86
  app.mount("/v1", base_app)
87
 
88
  # --- CORS SECURITY ---
@@ -96,7 +75,20 @@ app.add_middleware(
96
 
97
  @app.get("/health")
98
  def health_check():
99
- return {"status": "online", "message": "Lead Architect Server is Active"}
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  def main():
102
  """
@@ -109,8 +101,9 @@ def main():
109
  args = parser.parse_args()
110
 
111
  logger.info(f"πŸš€ OpenEnv Sandbox Starting on {args.host}:{args.port}")
112
- logger.info(f"πŸ“ Mount Point: /v1 mapped to OpenEnv Core")
113
 
 
114
  uvicorn.run(
115
  app,
116
  host=args.host,
 
1
  # Copyright (c) Meta Platforms, Inc. and affiliates.
2
  # All rights reserved.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  import sys
5
  import os
 
17
  logger = logging.getLogger("ArchitectApp")
18
 
19
  # --- ARCHITECT'S PATH INJECTION ---
20
+ # We force the root and server dirs into sys.path to ensure 'models' is found
21
  current_dir = os.path.dirname(os.path.abspath(__file__))
22
  project_root = os.path.abspath(os.path.join(current_dir, ".."))
23
 
24
+ for path in [project_root, current_dir, os.getcwd()]:
25
  if path not in sys.path:
26
  sys.path.insert(0, path)
27
 
 
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 environment logic app (The core OpenEnv engine)
41
  base_app = create_app(
42
  LintCodingAgentEnvironment,
43
  LintCodingAgentAction,
 
45
  )
46
 
47
  # 2. Create the Main Wrapper App
48
+ # redirect_slashes=False is CRITICAL to prevent 307 Redirect loops on HF
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"πŸ“‘ Request: {request.method} {request.url.path}")
59
  response = await call_next(request)
60
+ logger.info(f"βœ… Outcome: {response.status_code}")
61
  return response
62
 
63
  # --- MOUNTING (The /v1 Fix) ---
64
+ # This ensures that /v1/reset and /v1/ws are mapped to the core engine
65
  app.mount("/v1", base_app)
66
 
67
  # --- CORS SECURITY ---
 
75
 
76
  @app.get("/health")
77
  def health_check():
78
+ """Simple health probe for Docker and HF status checks."""
79
+ return {"status": "online", "message": "Lead Architect Server is Active", "port": 7860}
80
+
81
+ # Catch-all for the root to prevent confusing 404s in the browser
82
+ @app.get("/")
83
+ def root_info():
84
+ return {
85
+ "message": "Lint Coding Agent API is Running",
86
+ "endpoints": {
87
+ "docs": "/v1/docs",
88
+ "health": "/health",
89
+ "api_root": "/v1"
90
+ }
91
+ }
92
 
93
  def main():
94
  """
 
101
  args = parser.parse_args()
102
 
103
  logger.info(f"πŸš€ OpenEnv Sandbox Starting on {args.host}:{args.port}")
104
+ logger.info(f"πŸ“ Geometry: /v1 mapped to OpenEnv Core")
105
 
106
+ # proxy_headers + forwarded_allow_ips are vital for HF reverse proxy support
107
  uvicorn.run(
108
  app,
109
  host=args.host,
server/lint_coding_agent_environment.py CHANGED
@@ -1,8 +1,5 @@
1
  # Copyright (c) Meta Platforms, Inc. and affiliates.
2
  # All rights reserved.
3
- #
4
- # This source code is licensed under the BSD-style license found in the
5
- # LICENSE file in the root directory of this source tree.
6
 
7
  import json
8
  import os
@@ -15,7 +12,7 @@ from typing import Tuple, Dict, Any, List
15
  from openenv.core.env_server.interfaces import Environment
16
  from openenv.core.env_server.types import State
17
 
18
- # Setup Logging
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger("ArchitectEnvironment")
21
 
@@ -28,7 +25,7 @@ except ImportError:
28
  class LintCodingAgentEnvironment(Environment):
29
  """
30
  Advanced Repository Sandbox.
31
- Fixed for Docker/Hugging Face pathing with robust resource discovery.
32
  """
33
  SUPPORTS_CONCURRENT_SESSIONS: bool = True
34
 
@@ -38,61 +35,65 @@ class LintCodingAgentEnvironment(Environment):
38
  self.failed_queue = []
39
  self.vfs: Dict[str, str] = {}
40
 
41
- # --- ROBUST PATH DISCOVERY ---
42
- # 1. Try directory relative to this file
43
- current_file_dir = os.path.dirname(os.path.abspath(__file__))
44
-
45
- # Candidate paths for QUESTIONS.json
46
- candidates = [
47
- os.path.join(current_file_dir, "QUESTIONS.json"),
48
- os.path.join(os.getcwd(), "server", "QUESTIONS.json"),
49
- os.path.join(os.getcwd(), "QUESTIONS.json"),
50
- "/app/server/QUESTIONS.json"
51
- ]
52
-
53
- json_path = None
54
- for path in candidates:
55
- if os.path.exists(path):
56
- json_path = path
57
- self.templates_base = os.path.join(os.path.dirname(path), "templates")
58
- break
59
 
60
  if json_path:
61
- logger.info(f"πŸ—οΈ Environment Resources Found: {json_path}")
 
62
  try:
63
  with open(json_path, "r") as f:
64
  self.curriculum = json.load(f)
65
  self.all_levels = sorted(list(self.curriculum.keys()), key=int)
66
- logger.info(f"πŸ“š Curriculum Loaded: {len(self.all_levels)} levels.")
67
  except Exception as e:
68
- logger.error(f"❌ Error reading QUESTIONS.json: {e}")
69
- self._set_fallback_curriculum()
70
  else:
71
- logger.error(f"❌ CRITICAL: QUESTIONS.json not found in any candidate path: {candidates}")
72
- self._set_fallback_curriculum()
73
 
74
  self.max_levels = int(self.all_levels[-1]) if self.all_levels else 1
75
 
76
- def _set_fallback_curriculum(self):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  self.curriculum = {"1": {"lang": "Python", "task": "Fallback: Init", "template_dir": "level_1", "ans": "print"}}
78
  self.all_levels = ["1"]
79
- self.templates_base = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
80
 
81
  def reset(self) -> LintCodingAgentObservation:
82
  """Full state reset for Phase 2 scoring."""
83
- logger.info("πŸ”„ Resetting Environment State...")
84
  self._state = State(episode_id=str(uuid4()), step_count=0)
85
  self.level = 1
86
  self.failed_queue = []
87
  return self._load_template_state("Environment Reset: 15-Level Sprint Initialized.")
88
 
89
  def _load_template_state(self, msg: str) -> LintCodingAgentObservation:
90
- """Loads physical files into the VFS with debug tracking."""
91
  task_data = self.curriculum.get(str(self.level), {})
92
  template_dir_name = task_data.get("template_dir", f"level_{self.level}")
93
  template_path = os.path.join(self.templates_base, template_dir_name)
94
 
95
- logger.info(f"πŸ“‚ Loading Template for Level {self.level}: {template_path}")
96
 
97
  self.vfs = {}
98
  if os.path.exists(template_path):
@@ -103,19 +104,19 @@ class LintCodingAgentEnvironment(Environment):
103
  try:
104
  with open(full_p, 'r', encoding='utf-8') as file:
105
  self.vfs[rel_p] = file.read()
106
- logger.info(f" πŸ“„ VFS Mounted: {rel_p}")
107
  except Exception as e:
108
- logger.error(f" ⚠️ VFS Read Error {rel_p}: {e}")
109
  else:
110
- logger.error(f"❌ CRITICAL: Level path missing: {template_path}")
111
 
112
  return self._get_observation(msg, 0.0, False)
113
 
114
  def _check_hijacking(self, action: LintCodingAgentAction) -> Tuple[bool, str, float]:
115
- """Anti-adversarial monitor to prevent agent shortcuts."""
116
  new_code = action.code_solution or ""
117
  if len(new_code.strip()) < 10:
118
- return True, "HIJACK: Solution rejected for insufficient depth.", -2.0
119
 
120
  code_only = re.sub(r'#.*', '', new_code).strip()
121
  if len(code_only) < 5:
@@ -124,14 +125,14 @@ class LintCodingAgentEnvironment(Environment):
124
  return False, "", 0.0
125
 
126
  def _is_syntactically_valid(self, code: str, lang: str) -> Tuple[bool, str]:
127
- """Verifies solution validity based on language type."""
128
  if lang.lower() == "python":
129
  try:
130
  ast.parse(code)
131
  return True, "Syntax Valid"
132
  except SyntaxError as e:
133
- return False, f"SyntaxError: {e.msg} (Line {e.lineno})"
134
- return (True, "Non-Python/No-Verify") if len(code) > 5 else (False, "Empty Solution")
135
 
136
  def step(self, action: LintCodingAgentAction) -> LintCodingAgentObservation:
137
  self._state.step_count += 1
@@ -139,35 +140,33 @@ class LintCodingAgentEnvironment(Environment):
139
 
140
  logger.info(f"πŸ‘£ STEP {self._state.step_count} | Level: {self.level}")
141
 
142
- # 1. Monitoring
143
  is_hijacked, hijack_msg, penalty = self._check_hijacking(action)
144
  if is_hijacked:
145
  logger.warning(f"🚫 {hijack_msg}")
146
  return self._get_observation(hijack_msg, penalty, False)
147
 
148
- # 2. Execution & Verification
149
  syntax_ok, error_msg = self._is_syntactically_valid(action.code_solution, task_data["lang"])
150
 
151
  if not syntax_ok:
152
  reward = -0.2
153
  feedback = f"Transition Failed: {error_msg}"
154
- logger.info(f"❌ Syntax Invalid: {error_msg}")
155
  else:
 
156
  logic_ok = task_data["ans"].lower() in action.code_solution.lower()
157
 
158
  if logic_ok:
159
  reward = 1.0
160
  feedback = f"Level {self.level} Success."
161
- logger.info(f"βœ… Success! Solution matches logic check.")
162
  self.level += 1
163
  else:
164
  reward = 0.1
165
  feedback = f"Syntax OK | Logic Missing: {task_data['task']}"
166
- logger.info(f"⚠️ Logic Mismatch.")
167
  if str(self.level) not in self.failed_queue:
168
  self.failed_queue.append(str(self.level))
169
 
170
- # 3. Handle Progression
171
  done = self.level > self.max_levels
172
 
173
  if not done and reward > 0.8:
@@ -182,16 +181,12 @@ class LintCodingAgentEnvironment(Environment):
182
  return LintCodingAgentObservation(
183
  level=self.level,
184
  language=task_data["lang"],
185
- problem_statement=f"{feedback} | Current Task: {task_data['task']}",
186
  code_context=json.dumps(self.vfs),
187
  last_test_results=feedback,
188
  reward=reward,
189
  done=done,
190
- metadata={
191
- "step": self._state.step_count,
192
- "vfs_files": list(self.vfs.keys()),
193
- "total_levels": self.max_levels
194
- }
195
  )
196
 
197
  @property
 
1
  # Copyright (c) Meta Platforms, Inc. and affiliates.
2
  # All rights reserved.
 
 
 
3
 
4
  import json
5
  import os
 
12
  from openenv.core.env_server.interfaces import Environment
13
  from openenv.core.env_server.types import State
14
 
15
+ # Lead Architect Telemetry
16
  logging.basicConfig(level=logging.INFO)
17
  logger = logging.getLogger("ArchitectEnvironment")
18
 
 
25
  class LintCodingAgentEnvironment(Environment):
26
  """
27
  Advanced Repository Sandbox.
28
+ Enhanced with Deep Path Discovery to eliminate Docker 'File Not Found' errors.
29
  """
30
  SUPPORTS_CONCURRENT_SESSIONS: bool = True
31
 
 
35
  self.failed_queue = []
36
  self.vfs: Dict[str, str] = {}
37
 
38
+ # --- ELITE DEEP RESOURCE DISCOVERY ---
39
+ # We don't guess paths; we find them.
40
+ json_path = self._discover_resource("QUESTIONS.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  if json_path:
43
+ logger.info(f"🎯 Resource Secured: {json_path}")
44
+ self.templates_base = os.path.join(os.path.dirname(json_path), "templates")
45
  try:
46
  with open(json_path, "r") as f:
47
  self.curriculum = json.load(f)
48
  self.all_levels = sorted(list(self.curriculum.keys()), key=int)
49
+ logger.info(f"πŸ“š Curriculum Validated: {len(self.all_levels)} levels loaded.")
50
  except Exception as e:
51
+ logger.error(f"❌ Read Failure on QUESTIONS.json: {e}")
52
+ self._apply_fallback()
53
  else:
54
+ logger.error("❌ CRITICAL: Manifest QUESTIONS.json missing from all search roots.")
55
+ self._apply_fallback()
56
 
57
  self.max_levels = int(self.all_levels[-1]) if self.all_levels else 1
58
 
59
+ def _discover_resource(self, filename: str) -> str:
60
+ """Scans standard Docker and local roots to locate critical engine files."""
61
+ # Order of search: Current Dir -> Env Root -> App Root -> Explicit Docker Path
62
+ roots = [
63
+ os.path.dirname(os.path.abspath(__file__)),
64
+ os.getcwd(),
65
+ "/app/env/server",
66
+ "/app/env",
67
+ "/app"
68
+ ]
69
+
70
+ for root in roots:
71
+ candidate = os.path.join(root, filename)
72
+ if os.path.exists(candidate):
73
+ return candidate
74
+ return None
75
+
76
+ def _apply_fallback(self):
77
+ """Prevents server crash by providing a baseline Level 1."""
78
  self.curriculum = {"1": {"lang": "Python", "task": "Fallback: Init", "template_dir": "level_1", "ans": "print"}}
79
  self.all_levels = ["1"]
80
+ self.templates_base = os.path.join(os.getcwd(), "templates")
81
 
82
  def reset(self) -> LintCodingAgentObservation:
83
  """Full state reset for Phase 2 scoring."""
84
+ logger.info("πŸ”„ Resetting Sandbox State...")
85
  self._state = State(episode_id=str(uuid4()), step_count=0)
86
  self.level = 1
87
  self.failed_queue = []
88
  return self._load_template_state("Environment Reset: 15-Level Sprint Initialized.")
89
 
90
  def _load_template_state(self, msg: str) -> LintCodingAgentObservation:
91
+ """Loads physical files into the VFS with rigorous logging."""
92
  task_data = self.curriculum.get(str(self.level), {})
93
  template_dir_name = task_data.get("template_dir", f"level_{self.level}")
94
  template_path = os.path.join(self.templates_base, template_dir_name)
95
 
96
+ logger.info(f"πŸ“‚ VFS Switch | Level {self.level} | Root: {template_path}")
97
 
98
  self.vfs = {}
99
  if os.path.exists(template_path):
 
104
  try:
105
  with open(full_p, 'r', encoding='utf-8') as file:
106
  self.vfs[rel_p] = file.read()
107
+ logger.info(f" πŸ“„ File Mounted: {rel_p}")
108
  except Exception as e:
109
+ logger.error(f" ⚠️ Read Warning {rel_p}: {e}")
110
  else:
111
+ logger.error(f"❌ Directory Missing: {template_path}")
112
 
113
  return self._get_observation(msg, 0.0, False)
114
 
115
  def _check_hijacking(self, action: LintCodingAgentAction) -> Tuple[bool, str, float]:
116
+ """Enforces architectural integrity."""
117
  new_code = action.code_solution or ""
118
  if len(new_code.strip()) < 10:
119
+ return True, "HIJACK: Solution rejected (insufficient depth).", -2.0
120
 
121
  code_only = re.sub(r'#.*', '', new_code).strip()
122
  if len(code_only) < 5:
 
125
  return False, "", 0.0
126
 
127
  def _is_syntactically_valid(self, code: str, lang: str) -> Tuple[bool, str]:
128
+ """Structural validation using AST."""
129
  if lang.lower() == "python":
130
  try:
131
  ast.parse(code)
132
  return True, "Syntax Valid"
133
  except SyntaxError as e:
134
+ return False, f"SyntaxError: {e.msg}"
135
+ return (True, "Non-Python") if len(code) > 5 else (False, "Empty")
136
 
137
  def step(self, action: LintCodingAgentAction) -> LintCodingAgentObservation:
138
  self._state.step_count += 1
 
140
 
141
  logger.info(f"πŸ‘£ STEP {self._state.step_count} | Level: {self.level}")
142
 
143
+ # 1. Integrity Check
144
  is_hijacked, hijack_msg, penalty = self._check_hijacking(action)
145
  if is_hijacked:
146
  logger.warning(f"🚫 {hijack_msg}")
147
  return self._get_observation(hijack_msg, penalty, False)
148
 
149
+ # 2. Evaluation Logic
150
  syntax_ok, error_msg = self._is_syntactically_valid(action.code_solution, task_data["lang"])
151
 
152
  if not syntax_ok:
153
  reward = -0.2
154
  feedback = f"Transition Failed: {error_msg}"
 
155
  else:
156
+ # Check logic against manifest 'ans'
157
  logic_ok = task_data["ans"].lower() in action.code_solution.lower()
158
 
159
  if logic_ok:
160
  reward = 1.0
161
  feedback = f"Level {self.level} Success."
162
+ logger.info("βœ… Verification Passed.")
163
  self.level += 1
164
  else:
165
  reward = 0.1
166
  feedback = f"Syntax OK | Logic Missing: {task_data['task']}"
 
167
  if str(self.level) not in self.failed_queue:
168
  self.failed_queue.append(str(self.level))
169
 
 
170
  done = self.level > self.max_levels
171
 
172
  if not done and reward > 0.8:
 
181
  return LintCodingAgentObservation(
182
  level=self.level,
183
  language=task_data["lang"],
184
+ problem_statement=f"{feedback} | Task: {task_data['task']}",
185
  code_context=json.dumps(self.vfs),
186
  last_test_results=feedback,
187
  reward=reward,
188
  done=done,
189
+ metadata={"step": self._state.step_count, "vfs_files": list(self.vfs.keys())}
 
 
 
 
190
  )
191
 
192
  @property