om192006/github-issue-triage
1
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 the5# LICENSE file in the root directory of this source tree.6 7"""GitHub Issue Triage Environment Client."""8 9from typing import Dict10 11from openenv.core import EnvClient12from openenv.core.client_types import StepResult13from openenv.core.env_server.types import State14 15from .models import GithubIssueTriageAction, GithubIssueTriageObservation16 17 18class GithubIssueTriageEnv(19 EnvClient[GithubIssueTriageAction, GithubIssueTriageObservation, State]20):21 """22 Client for the GitHub Issue Triage Environment.23 24 Connects to the environment server via WebSocket and sends25 triage decisions, receiving graded observations back.26 27 Example (easy task):28 >>> with GithubIssueTriageEnv(base_url="http://localhost:8000") as env:29 ... result = env.reset()30 ... print(result.observation.issue_title)31 ... action = GithubIssueTriageAction(label="bug")32 ... result = env.step(action)33 ... print(result.reward)34 35 Example (hard task):36 >>> with GithubIssueTriageEnv(base_url="http://localhost:8000") as env:37 ... result = env.reset()38 ... action = GithubIssueTriageAction(39 ... label="bug",40 ... team="backend",41 ... priority="high",42 ... suggested_action="Check memory limit in file_handler.py line 42"43 ... )44 ... result = env.step(action)45 ... print(result.reward) # 0.0 to 1.046 ... print(result.observation.feedback)47 """48 49 def _step_payload(self, action: GithubIssueTriageAction) -> Dict:50 """51 Convert GithubIssueTriageAction to JSON payload for the step request.52 53 Args:54 action: The agent's triage decision55 56 Returns:57 Dictionary ready for JSON encoding58 """59 return {60 "label": action.label,61 "team": action.team,62 "priority": action.priority,63 "suggested_action": action.suggested_action,64 "reasoning": action.reasoning,65 }66 67 def _parse_result(self, payload: Dict) -> StepResult[GithubIssueTriageObservation]:68 """69 Parse the server's JSON response into a typed StepResult.70 71 Args:72 payload: Raw JSON response from the server73 74 Returns:75 StepResult containing the observation and reward76 """77 obs_data = payload.get("observation", {})78 79 observation = GithubIssueTriageObservation(80 # Issue content81 issue_id=obs_data.get("issue_id", ""),82 issue_title=obs_data.get("issue_title", ""),83 issue_body=obs_data.get("issue_body", ""),84 repo_name=obs_data.get("repo_name", ""),85 author=obs_data.get("author", ""),86 existing_comments=obs_data.get("existing_comments", []),87 88 # Task context89 task_id=obs_data.get("task_id", "easy"),90 task_description=obs_data.get("task_description", ""),91 92 # Feedback93 last_reward=obs_data.get("last_reward", 0.0),94 feedback=obs_data.get("feedback", ""),95 96 # Episode info97 done=payload.get("done", False),98 step_number=obs_data.get("step_number", 0),99 reward=payload.get("reward", 0.0),100 )101 102 return StepResult(103 observation=observation,104 reward=payload.get("reward", 0.0),105 done=payload.get("done", False),106 )107 108 def _parse_state(self, payload: Dict) -> State:109 """110 Parse the server's state response into a State object.111 112 Args:113 payload: Raw JSON from the /state endpoint114 115 Returns:116 State with episode_id and step_count117 """118 return State(119 episode_id=payload.get("episode_id"),120 step_count=payload.get("step_count", 0),121 )