lint_coding_agent / server /lint_coding_agent_environment.py
anonajay's picture
Upload folder using huggingface_hub
b6835b7 verified
Raw History Blame Contribute Delete
4.26 kB
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
"""
Lint Coding Agent Environment Implementation.
A simple test environment that echoes back messages sent to it.
Perfect for testing HTTP server infrastructure.
"""
import json
import os
from uuid import uuid4
from openenv.core.env_server.interfaces import Environment
from openenv.core.env_server.types import State
try:
from ..models import LintCodingAgentAction, LintCodingAgentObservation
except ImportError:
from models import LintCodingAgentAction, LintCodingAgentObservation
class LintCodingAgentEnvironment(Environment):
"""
A data-driven environment that loads a 50-level curriculum from a JSON file.
Designed for professional code-linting agent evaluation.
"""
SUPPORTS_CONCURRENT_SESSIONS: bool = True
def __init__(self):
# Initialize internal state using OpenEnv's State object
self._state = State(episode_id=str(uuid4()), step_count=0)
self.level = 1
# --- DATA LOADING BLOCK ---
# Get the path to QUESTIONS.json relative to this file to avoid pathing errors in Docker
current_dir = os.path.dirname(os.path.abspath(__file__))
json_path = os.path.join(current_dir, "QUESTIONS.json")
try:
with open(json_path, "r") as f:
self.curriculum = json.load(f)
except Exception as e:
print(f"CRITICAL ERROR: Could not load QUESTIONS.json: {e}")
# Minimal fallback to prevent total server crash
self.curriculum = {"1": {"lang": "Python", "task": "Fallback", "context": "print 'Hi'", "ans": "print("}}
self.max_levels = len(self.curriculum)
def reset(self) -> LintCodingAgentObservation:
"""Reset the environment to the beginning of the curriculum."""
self._state = State(episode_id=str(uuid4()), step_count=0)
self.level = 1
return self._get_observation("Curriculum Loaded. Ready for Level 1.", 0.0, False)
def step(self, action: LintCodingAgentAction) -> LintCodingAgentObservation:
"""Process the agent's solution against the current JSON task."""
self._state.step_count += 1
# Get current level data (JSON keys are strings)
task_data = self.curriculum.get(str(self.level), self.curriculum.get("1"))
# Logic: Extract fields from the strict Action model
solution = action.code_solution
explanation = action.explanation
# Case-insensitive grading logic
is_correct = task_data["ans"].lower() in solution.lower()
if is_correct:
reward = 1.0
self.level += 1
feedback = f"Correct! Level {self.level - 1} cleared. Reason: {explanation}"
else:
reward = -0.1
feedback = f"Incorrect fix. Retry Level {self.level}. Hint: {task_data['task']}"
# Done if all levels complete or agent gives up
done = self.level > self.max_levels or "QUIT" in solution.upper()
return self._get_observation(feedback, reward, done)
def _get_observation(self, feedback: str, reward: float, done: bool) -> LintCodingAgentObservation:
"""Helper to package internal data into the strict Observation Pydantic model."""
# Use string indexing for JSON curriculum access
task_data = self.curriculum.get(str(self.level), self.curriculum.get("1"))
return LintCodingAgentObservation(
level=self.level,
language=task_data["lang"],
problem_statement=f"{feedback} | Goal: {task_data['task']}",
code_context=task_data["context"],
last_test_results=feedback,
reward=reward,
done=done,
metadata={
"step": self._state.step_count,
"episode_id": self._state.episode_id
}
)
@property
def state(self) -> State:
return self._state