Spaces:
Running
Running
| """ | |
| Actions for Task 2: Property Inference. | |
| Defines the logic for each action type that the agent can take in the environment, including | |
| querying function code, NatSpec, related functions, and submitting the inferred property. | |
| ctx: context object containing episode state and data | |
| qkey: a unique key representing the specific query (used for tracking repeated queries) | |
| params: parameters for the action, such as the submitted property text for SUBMIT_PROPERTY | |
| """ | |
| from typing import Any, Dict, Tuple | |
| from data.data_loader import get_function_by_name, get_related_functions | |
| from utils import PropertyRetriever | |
| from env.schemas import ActionType, Reward | |
| PropertyRetrieverInstance = PropertyRetriever() # Load once at module level | |
| # TODO: Can separate into signature, visiblity | |
| def get_function_code(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_FUNCTION_CODE action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| fn = ctx._target_fn | |
| code = fn.get("code", "// no code available") | |
| return ( | |
| code, | |
| Reward(value=ActionType.GET_FUNCTION_CODE.cost, reason="get_function_code cost"), | |
| ) | |
| # TODO: Can separate comment and output_property(output_comment) | |
| def get_function_natspec(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_FUNCTION_NATSPEC action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| fn = ctx._target_fn | |
| name = fn["name"] | |
| natspec = fn.get("natspec") or fn.get("comment") or "No NatSpec available." | |
| out_prop = fn.get("output_property", "") | |
| result = f"NatSpec for '{name}':\n{natspec}" | |
| if out_prop: | |
| result += f"\n\nExpected output: {out_prop}" | |
| return result, Reward(value=ActionType.GET_FILE_NATSPEC.cost, reason="get_function_natspec cost") | |
| def get_file_natspec(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_FILE_NATSPEC action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| meta = ctx._contract.get("metadata", {}) | |
| natspec = meta.get("natspec") or meta.get("description", "No file NatSpec available.") | |
| return ( | |
| f"File NatSpec for {ctx._contract['contract_name']}:\n{natspec}", | |
| Reward(value=ActionType.GET_FILE_NATSPEC.cost, reason="get_file_natspec cost"), | |
| ) | |
| def get_related_functions_action(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_RELATED_FUNCTIONS action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| name = ctx._target_fn["name"] | |
| related = get_related_functions(ctx._contract, name) | |
| if not related: | |
| text = f"No related functions found for '{name}'." | |
| else: | |
| summaries = [] | |
| for rn in related: | |
| rfn = get_function_by_name(ctx._contract, rn) | |
| if rfn: | |
| sig = rfn.get("signature", rn) | |
| comment = rfn.get("comment", "") | |
| summaries.append(f" • {sig} — {comment}") | |
| text = f"Related functions for '{name}':\n" + "\n".join(summaries) | |
| return text, Reward(value=ActionType.GET_RELATED_FUNCTIONS.cost, reason="get_related_functions cost") | |
| def get_signature(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_SIGNATURE action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| fn = ctx._target_fn | |
| sig = fn.get("signature") | |
| return sig, Reward(value=ActionType.GET_SIGNATURE.cost, reason="get_signature cost") | |
| def get_similar_rule_action(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle GET_SIMILAR_RULE action.""" | |
| if ctx._is_repeated(qkey): | |
| return "Repeated query.", Reward(value=ActionType.REPEATED.cost, reason="Repeated query") | |
| PropertyRetrieverInstance.load_model() # Ensure model is loaded before querying | |
| similar_rule = PropertyRetrieverInstance.get_similar_property(ctx._target_fn["code"]) | |
| if similar_rule is None: | |
| return ( | |
| "No similar rule available for this function.", | |
| Reward(value=-0.20, reason="get_similar_rule cost (not found)"), | |
| ) | |
| return similar_rule, Reward(value=ActionType.GET_SIMILAR_RULE.cost, reason="get_similar_rule cost") | |
| def submit_property(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]: | |
| """Handle SUBMIT_PROPERTY action for Task 2. | |
| Expected params | |
| --------------- | |
| property : str – natural-language property describing the function's behaviour | |
| """ | |
| submitted_property = params.get("property", "").strip() | |
| if not submitted_property: | |
| return ( | |
| "submit_property requires a non-empty 'property' string in params.", | |
| Reward(value=ActionType.RESUBMIT.cost, reason="Malformed submission", partial=False), | |
| ) | |
| ctx._done = True | |
| score, confidence = ctx._grader.grade(submitted_property, ctx._step_count, ctx._cum_reward) | |
| return "", Reward( | |
| value=score, | |
| reason=f"submit_property confidence={confidence} score={score:.3f}", | |
| partial=False, | |
| ) | |
| def unknown_action(ctx: Any, qkey: str, params: Dict, action_type: str) -> Tuple[str, Reward]: | |
| """Fallback for unknown actions.""" | |
| ctx._done = True | |
| return ( | |
| f"Unknown action type: '{action_type}'. Valid: {[a.value for a in ActionType]}, \ | |
| Reset environment to start again.", | |
| Reward(value=ActionType.UNKNOWN.cost, reason="Unknown action"), | |
| ) |