GitReviewerPro / server /git_commit_reviewer_environment.py
at0day's picture
Upload 17 files
884b136 verified
Raw History Blame Contribute Delete
6.84 kB
import json
from uuid import uuid4
from openenv.core.env_server.interfaces import Environment
from openenv.core.env_server.types import State
from typing import List, Dict, Optional
try:
from ..models import GitCommitReviewerAction, GitCommitReviewerObservation
except ImportError:
from models import GitCommitReviewerAction, GitCommitReviewerObservation
import tasks
# Global cache to handle stateless server environments
# Shared across instances
GLOBAL_SESSION_STORE = {}
class GitCommitReviewerEnvironment(Environment):
SUPPORTS_CONCURRENT_SESSIONS: bool = True
def __init__(self):
self._state = State(episode_id=str(uuid4()), step_count=0)
self._reset_count = 0
self.current_task_commits = []
self.current_idx = 0
self.total_rewards = 0.0
self.last_task = "easy"
def reset(self, task: str = "easy") -> GitCommitReviewerObservation:
self.last_task = str(task).lower()
self._state = State(episode_id=self._state.episode_id, step_count=0) # Keep same EP ID if possible or new one
self._state.episode_id = str(uuid4()) # Generate new unique session
self._reset_count += 1
self.current_task_commits = tasks.get_task_data(self.last_task)
if not self.current_task_commits:
self.current_task_commits = tasks.get_task_data("easy")
self.current_idx = 0
self.total_rewards = 0.0
# Store initial state in global cache
GLOBAL_SESSION_STORE[self._state.episode_id] = {
"current_idx": self.current_idx,
"total_rewards": self.total_rewards,
"commits": self.current_task_commits,
"task": self.last_task,
"step_count": self._state.step_count
}
return GitCommitReviewerObservation(
commit_message=self.current_task_commits[0]["commit_message"],
commit_idx=0,
total_commits=len(self.current_task_commits),
done=False,
reward=0.0,
session_id=self._state.episode_id,
metadata={"session_id": self._state.episode_id}
)
def step(self, action: GitCommitReviewerAction) -> GitCommitReviewerObservation:
# Try to restore from global cache if session matches
sid = getattr(action, "session_id", None)
if sid and sid in GLOBAL_SESSION_STORE:
cached = GLOBAL_SESSION_STORE[sid]
# Only restore if current instance is uninitialized or mismatched
if not self.current_task_commits or self._state.episode_id != sid:
self.current_task_commits = cached["commits"]
self.current_idx = cached["current_idx"]
self.total_rewards = cached["total_rewards"]
self._state.episode_id = sid
self._state.step_count = cached["step_count"]
self.last_task = cached["task"]
self._state.step_count += 1
if not self.current_task_commits or self.current_idx >= len(self.current_task_commits):
return GitCommitReviewerObservation(
commit_message="DONE",
commit_idx=self.current_idx,
total_commits=len(self.current_task_commits) if self.current_task_commits else 0,
done=True,
reward=0.0,
session_id=self._state.episode_id,
metadata={"session_id": self._state.episode_id}
)
gt = self.current_task_commits[self.current_idx]["ground_truth"]
# Grading Logic
reward = 0.0
# 1. Score matching
score_diff = abs(action.score - gt["score"])
if score_diff <= 0.05: reward += 0.45
elif score_diff <= 0.15: reward += 0.3
elif score_diff <= 0.3: reward += 0.15
# 2. Type Valid matching
if action.type_valid == gt["type_valid"]: reward += 0.2
# 3. Summary length proximity
length_diff = abs(action.summary_length - gt["summary_length"])
if length_diff == 0: reward += 0.15
elif length_diff <= 2: reward += 0.05
# 4. Suggestion/Issues quality
if gt["issues"] and len(action.issues) > 0: reward += 0.1
elif not gt["issues"] and len(action.issues) == 0: reward += 0.1
if action.suggestion and ":" in action.suggestion and len(action.suggestion.split(":")[0]) <= 20:
reward += 0.1
reward = max(0.01, min(0.99, reward)) # Clamp for OpenEnv safety
self.total_rewards += reward
self.current_idx += 1
done = (self.current_idx >= len(self.current_task_commits))
# Update global cache for this session
GLOBAL_SESSION_STORE[self._state.episode_id] = {
"current_idx": self.current_idx,
"total_rewards": self.total_rewards,
"commits": self.current_task_commits,
"task": self.last_task,
"step_count": self._state.step_count
}
return GitCommitReviewerObservation(
commit_message=self.current_task_commits[self.current_idx]["commit_message"] if not done else "DONE",
commit_idx=self.current_idx,
total_commits=len(self.current_task_commits),
done=done,
reward=reward,
session_id=self._state.episode_id,
metadata={
"step": self._state.step_count,
"total_rewards": self.total_rewards,
"session_id": self._state.episode_id,
"reward": reward
}
)
@property
def state(self) -> dict:
# Crucial: Check global cache to ensure we return the latest state
# even if this is a newly created instance for a stateless request.
eid = self._state.episode_id
current_rewards = self.total_rewards
current_steps = self._state.step_count
if eid in GLOBAL_SESSION_STORE:
cached = GLOBAL_SESSION_STORE[eid]
current_rewards = max(current_rewards, cached.get("total_rewards", 0))
current_steps = max(current_steps, cached.get("step_count", 0))
return {
"episode_id": eid,
"step_count": current_steps,
"total_rewards": current_rewards,
"current_idx": self.current_idx,
"total_commits": len(self.current_task_commits),
"is_done": self.current_idx >= len(self.current_task_commits) if self.current_task_commits else True,
"task": self.last_task
}