Muditsharmaaa commited on
Commit
1ca96ae
·
1 Parent(s): 69e1691

fix: restore correct env.py content

Browse files
Files changed (1) hide show
  1. env.py +108 -121
env.py CHANGED
@@ -1,123 +1,110 @@
1
- import os
2
- import json
3
- import textwrap
4
  from typing import List, Optional
5
- from openai import OpenAI
6
-
7
- from models import BlockchainAction
8
-
9
-
10
- # --- Mandatory Hackathon Variables ---
11
- API_BASE_URL = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
12
- MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o-mini") # Swap to your active model
13
- HF_TOKEN = os.getenv("HF_TOKEN")
14
- API_KEY = HF_TOKEN or os.getenv("OPENAI_API_KEY")
15
- TASK_NAME = os.getenv("MY_ENV_TASK", "hard_gas_management")
16
- BENCHMARK = "blockchain_certificate_admin"
17
- MAX_STEPS = 5
18
-
19
- SYSTEM_PROMPT = textwrap.dedent(
20
- """
21
- You are a Smart Contract Administrator AI.
22
- You will receive a list of student certificate requests and your current gas balance.
23
- Rules:
24
- 1. Only 'mint' if final_score >= 70 AND wallet_address is exactly 42 chars starting with '0x'.
25
- 2. Otherwise, 'reject'.
26
- 3. Minting costs 0.1 gas. Do not mint if gas balance is insufficient.
27
-
28
- You MUST respond with valid JSON matching this schema exactly:
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
- raw_score = 0.0
114
-
115
- # Clamp STRICTLY between 0 and 1 — never exactly 0.0 or 1.0
116
- score = max(0.01, min(0.99, raw_score))
117
- success = score >= 0.6
118
-
119
- finally:
120
- log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
121
-
122
- if __name__ == "__main__":
123
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ )