Spaces:
Sleeping
Sleeping
Commit ·
1ca96ae
1
Parent(s): 69e1691
fix: restore correct env.py content
Browse files
env.py
CHANGED
|
@@ -1,123 +1,110 @@
|
|
| 1 |
-
import
|
| 2 |
-
import
|
| 3 |
-
import
|
| 4 |
from typing import List, Optional
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
"decisions": [
|
| 31 |
-
{"request_id": "REQ-1234", "decision": "mint", "reason": "Valid score and wallet"},
|
| 32 |
-
{"request_id": "REQ-5678", "decision": "reject", "reason": "Invalid wallet length"}
|
| 33 |
-
]
|
| 34 |
-
}
|
| 35 |
-
Output ONLY JSON. No markdown formatting, no backticks.
|
| 36 |
-
"""
|
| 37 |
-
).strip()
|
| 38 |
-
|
| 39 |
-
def log_start(task: str, env: str, model: str) -> None:
|
| 40 |
-
print(f"[START] task={task} env={env} model={model}", flush=True)
|
| 41 |
-
|
| 42 |
-
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
|
| 43 |
-
error_val = error if error else "null"
|
| 44 |
-
print(f"[STEP] step={step} action={action} reward={reward:.2f} done={str(done).lower()} error={error_val}", flush=True)
|
| 45 |
-
|
| 46 |
-
def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
|
| 47 |
-
rewards_str = ",".join(f"{r:.2f}" for r in rewards)
|
| 48 |
-
print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}", flush=True)
|
| 49 |
-
|
| 50 |
-
def main():
|
| 51 |
-
client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
|
| 52 |
-
env = BlockchainEnv(max_steps=MAX_STEPS)
|
| 53 |
-
|
| 54 |
-
history: List[str] = []
|
| 55 |
-
rewards: List[float] = []
|
| 56 |
-
steps_taken = 0
|
| 57 |
-
score = 0.0
|
| 58 |
-
success = False
|
| 59 |
-
|
| 60 |
-
log_start(task=TASK_NAME, env=BENCHMARK, model=MODEL_NAME)
|
| 61 |
-
|
| 62 |
-
# Initialize Environment
|
| 63 |
-
result = env.reset()
|
| 64 |
-
|
| 65 |
-
try:
|
| 66 |
-
for step in range(1, MAX_STEPS + 1):
|
| 67 |
-
if result.done:
|
| 68 |
-
break
|
| 69 |
-
|
| 70 |
-
obs = result.observation
|
| 71 |
-
user_prompt = f"Pending Requests: {obs.pending_requests}\nGas Balance: {obs.gas_balance}\nStep: {step}"
|
| 72 |
-
|
| 73 |
-
try:
|
| 74 |
-
# Call LLM
|
| 75 |
-
completion = client.chat.completions.create(
|
| 76 |
-
model=MODEL_NAME,
|
| 77 |
-
messages=[
|
| 78 |
-
{"role": "system", "content": SYSTEM_PROMPT},
|
| 79 |
-
{"role": "user", "content": user_prompt},
|
| 80 |
-
],
|
| 81 |
-
temperature=0.0, # Deterministic logic preferred here
|
| 82 |
-
)
|
| 83 |
-
raw_response = (completion.choices[0].message.content or "").strip()
|
| 84 |
-
|
| 85 |
-
# Parse JSON and execute action
|
| 86 |
-
action_dict = json.loads(raw_response)
|
| 87 |
-
action = BlockchainAction(**action_dict)
|
| 88 |
-
result = env.step(action)
|
| 89 |
-
|
| 90 |
-
reward = result.reward
|
| 91 |
-
error = None
|
| 92 |
-
action_str = json.dumps(action_dict).replace(" ", "") # Compress for STDOUT
|
| 93 |
-
|
| 94 |
-
except Exception as e:
|
| 95 |
-
# If the LLM hallucinates or JSON parsing fails
|
| 96 |
-
reward = 0.01 # Strictly above 0.0
|
| 97 |
-
error = str(e).replace("\n", " ")
|
| 98 |
-
action_str = "invalid_format"
|
| 99 |
-
result.done = True # End episode on fatal format error
|
| 100 |
-
|
| 101 |
-
rewards.append(reward)
|
| 102 |
-
steps_taken = step
|
| 103 |
-
|
| 104 |
-
log_step(step=step, action=action_str, reward=reward, done=result.done, error=error)
|
| 105 |
-
|
| 106 |
-
if result.done:
|
| 107 |
-
break
|
| 108 |
-
|
| 109 |
-
# Calculate final normalized score — strictly between 0 and 1
|
| 110 |
-
if steps_taken > 0:
|
| 111 |
-
raw_score = sum(rewards) / steps_taken
|
| 112 |
else:
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import string
|
| 3 |
+
from pydantic import BaseModel
|
| 4 |
from typing import List, Optional
|
| 5 |
+
|
| 6 |
+
from models import (
|
| 7 |
+
CertificateRequest, ActionDecision, BlockchainObservation,
|
| 8 |
+
BlockchainAction, BlockchainReward
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
class EnvResult(BaseModel):
|
| 12 |
+
observation: BlockchainObservation
|
| 13 |
+
reward: float = 0.0
|
| 14 |
+
done: bool = False
|
| 15 |
+
info: dict = {}
|
| 16 |
+
|
| 17 |
+
class BlockchainEnv:
|
| 18 |
+
def __init__(self, max_steps: int = 5):
|
| 19 |
+
self.max_steps = max_steps
|
| 20 |
+
self.current_step = 0
|
| 21 |
+
self.gas_balance = 0.0
|
| 22 |
+
self.pending_requests: List[CertificateRequest] = []
|
| 23 |
+
self.last_error: Optional[str] = None
|
| 24 |
+
self.gas_cost_per_mint = 0.1
|
| 25 |
+
|
| 26 |
+
def _generate_mock_wallet(self, valid: bool) -> str:
|
| 27 |
+
chars = ''.join(random.choices(string.ascii_lowercase + string.digits, k=40))
|
| 28 |
+
if valid:
|
| 29 |
+
return f"0x{chars}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
else:
|
| 31 |
+
return random.choice([f"0x{chars[:30]}", f"1x{chars}", chars])
|
| 32 |
+
|
| 33 |
+
def _generate_requests(self, batch_size: int = 3) -> List[CertificateRequest]:
|
| 34 |
+
requests = []
|
| 35 |
+
for i in range(batch_size):
|
| 36 |
+
is_valid = random.choice([True, False])
|
| 37 |
+
score = random.uniform(70.0, 99.0) if is_valid else random.uniform(40.0, 69.9)
|
| 38 |
+
wallet = self._generate_mock_wallet(is_valid)
|
| 39 |
+
requests.append(CertificateRequest(
|
| 40 |
+
request_id=f"REQ-{random.randint(1000, 9999)}",
|
| 41 |
+
student_name=f"Student_{i}",
|
| 42 |
+
course_name="Intro to Web3",
|
| 43 |
+
final_score=round(score, 2),
|
| 44 |
+
wallet_address=wallet
|
| 45 |
+
))
|
| 46 |
+
return requests
|
| 47 |
+
|
| 48 |
+
def state(self) -> BlockchainObservation:
|
| 49 |
+
return BlockchainObservation(
|
| 50 |
+
pending_requests=self.pending_requests,
|
| 51 |
+
gas_balance=round(self.gas_balance, 2),
|
| 52 |
+
current_step=self.current_step,
|
| 53 |
+
last_error=self.last_error
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
def reset(self) -> EnvResult:
|
| 57 |
+
self.current_step = 0
|
| 58 |
+
self.gas_balance = 2.0
|
| 59 |
+
self.last_error = None
|
| 60 |
+
self.pending_requests = self._generate_requests(batch_size=3)
|
| 61 |
+
return EnvResult(observation=self.state())
|
| 62 |
+
|
| 63 |
+
def step(self, action: BlockchainAction) -> EnvResult:
|
| 64 |
+
self.current_step += 1
|
| 65 |
+
self.last_error = None
|
| 66 |
+
step_reward = 0.0
|
| 67 |
+
correct_decisions = 0
|
| 68 |
+
total_requests = len(self.pending_requests)
|
| 69 |
+
|
| 70 |
+
decision_map = {dec.request_id: dec.decision for dec in action.decisions}
|
| 71 |
+
|
| 72 |
+
for req in self.pending_requests:
|
| 73 |
+
is_valid_score = req.final_score >= 70.0
|
| 74 |
+
is_valid_wallet = req.wallet_address.startswith("0x") and len(req.wallet_address) == 42
|
| 75 |
+
should_mint = is_valid_score and is_valid_wallet
|
| 76 |
+
agent_decision = decision_map.get(req.request_id)
|
| 77 |
+
|
| 78 |
+
if agent_decision == "mint":
|
| 79 |
+
if should_mint and self.gas_balance >= self.gas_cost_per_mint:
|
| 80 |
+
self.gas_balance -= self.gas_cost_per_mint
|
| 81 |
+
correct_decisions += 1
|
| 82 |
+
else:
|
| 83 |
+
step_reward -= 0.5
|
| 84 |
+
elif agent_decision == "reject":
|
| 85 |
+
if not should_mint:
|
| 86 |
+
correct_decisions += 1
|
| 87 |
+
else:
|
| 88 |
+
step_reward -= 0.5
|
| 89 |
+
else:
|
| 90 |
+
self.last_error = f"Missing or invalid decision for {req.request_id}"
|
| 91 |
+
|
| 92 |
+
if total_requests > 0:
|
| 93 |
+
step_reward += (correct_decisions / total_requests)
|
| 94 |
+
|
| 95 |
+
# Strictly between 0 and 1 — never exactly 0.0 or 1.0
|
| 96 |
+
step_reward = max(0.01, min(0.99, step_reward))
|
| 97 |
+
|
| 98 |
+
done = self.current_step >= self.max_steps or self.gas_balance <= 0
|
| 99 |
+
|
| 100 |
+
if not done:
|
| 101 |
+
self.pending_requests = self._generate_requests(batch_size=3)
|
| 102 |
+
else:
|
| 103 |
+
self.pending_requests = []
|
| 104 |
+
|
| 105 |
+
return EnvResult(
|
| 106 |
+
observation=self.state(),
|
| 107 |
+
reward=step_reward,
|
| 108 |
+
done=done,
|
| 109 |
+
info={"correct_decisions": correct_decisions, "total_batch": total_requests}
|
| 110 |
+
)
|