ajaxwin
fix: Update file paths and ensure model loading in PropertyRetriever
45bd962
Raw
History Blame
5.57 kB
"""
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=-0.40, reason="Repeated query")
fn = ctx._target_fn
code = fn.get("code", "// no code available")
return (
code,
Reward(value=-0.06, 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=-0.40, 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=-0.08, 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=-0.40, 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=-0.03, 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=-0.40, 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=-0.06, 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=-0.40, reason="Repeated query")
fn = ctx._target_fn
sig = fn.get("signature")
return sig, Reward(value=-0.04, 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=-0.40, 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=-0.20, reason="get_similar_rule cost")
def submit_property(ctx: Any, qkey: str, params: Dict) -> Tuple[str, Reward]:
"""Handle SUBMIT_PROPERTY action."""
if ctx._submitted:
return (
"❌ You have already submitted a property for this episode. "
"Only one submission is allowed.",
Reward(value=-1.0, reason="Second submit_property attempt", partial=False),
)
submitted_text = params.get("property", "").strip()
if not submitted_text:
return (
"Submit requires 'property' key in params with a non-empty string.",
Reward(value=-0.5, reason="Empty property submission"),
)
ctx._submitted = True
ctx._done = True
score, confidence = ctx._grader.grade(submitted_text)
reward = round(score * 5.0, 4)
msg = f'Score: {score:.2f}/1.00 → Confidence: {confidence}\n'
return msg, Reward(
value=reward,
reason=f"Property submission score={score:.3f}",
partial=False,
)
def unknown_action(ctx: Any, qkey: str, params: Dict, action_type: str) -> Tuple[str, Reward]:
"""Fallback for unknown actions."""
return (
f"Unknown action type: '{action_type}'. Valid: {[a.value for a in ActionType]}",
Reward(value=-0.10, reason="Unknown action"),
)