cyberosa
commited on
Commit
·
acc99f2
1
Parent(s):
40ac692
cleaning and updating dashboards
Browse files- data/all_trades_profitability.parquet +2 -2
- data/delivers.parquet +2 -2
- data/fpmmTrades.parquet +2 -2
- data/fpmms.parquet +2 -2
- data/requests.parquet +2 -2
- data/summary_profitability.parquet +2 -2
- data/t_map.pkl +2 -2
- data/tools.parquet +2 -2
- scripts/get_mech_info.py +2 -0
- scripts/tools.py +11 -186
- scripts/utils.py +185 -2
data/all_trades_profitability.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:906e77fed8d2af6d7a4f4acf73640ce6aa3e3d4357e01c7b00af1c180f3b6eaf
|
3 |
+
size 2460157
|
data/delivers.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2d975d5fc2c3c7c50bec7136dffa135aef50fe3802bd1650cd46f236675318af
|
3 |
+
size 510466826
|
data/fpmmTrades.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:781a9625add04d8337bb3909befb6c0d4c39f3e0c9a5c47c1fb9398a96ba36ae
|
3 |
+
size 6482896
|
data/fpmms.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a2c07ed22be9463c8465d34717222f1553c5890254b350fb07aec2604e925795
|
3 |
+
size 336922
|
data/requests.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:bff6dbad037f5f9c3cee3631c126ba0f6e582b32b7e3fb75d79fc60a43a9dadb
|
3 |
+
size 11547978
|
data/summary_profitability.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c5db58ef61b7a256437ee87238249cd1da9d229a4ca5f9af79b02efe61fa73cc
|
3 |
+
size 40127
|
data/t_map.pkl
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:29c60bf39a93cad87fd54b7a87407cb8f50bb2cd2122e993762cec36d76e76fb
|
3 |
+
size 8982599
|
data/tools.parquet
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cf3ed51b3e32b32e3cd9bba607afbc1e6d67583e6ca9126df68585c339964a8a
|
3 |
+
size 378117330
|
scripts/get_mech_info.py
CHANGED
@@ -48,6 +48,8 @@ def fetch_block_number(timestamp_from: int, timestamp_to: int) -> dict:
|
|
48 |
result_json = response.json()
|
49 |
print(f"Response of the query={result_json}")
|
50 |
blocks = result_json.get("data", {}).get("blocks", "")
|
|
|
|
|
51 |
return blocks[0]
|
52 |
|
53 |
|
|
|
48 |
result_json = response.json()
|
49 |
print(f"Response of the query={result_json}")
|
50 |
blocks = result_json.get("data", {}).get("blocks", "")
|
51 |
+
if len(blocks) == 0:
|
52 |
+
raise ValueError(f"The query {query} did not return any results")
|
53 |
return blocks[0]
|
54 |
|
55 |
|
scripts/tools.py
CHANGED
@@ -17,23 +17,18 @@
|
|
17 |
#
|
18 |
# ------------------------------------------------------------------------------
|
19 |
|
20 |
-
import json
|
21 |
import os.path
|
22 |
import re
|
23 |
import time
|
24 |
import random
|
25 |
-
from dataclasses import dataclass
|
26 |
-
from enum import Enum
|
27 |
from typing import (
|
28 |
Optional,
|
29 |
List,
|
30 |
Dict,
|
31 |
-
Any,
|
32 |
Union,
|
33 |
)
|
34 |
import pandas as pd
|
35 |
import requests
|
36 |
-
from json.decoder import JSONDecodeError
|
37 |
from eth_typing import ChecksumAddress
|
38 |
from eth_utils import to_checksum_address
|
39 |
from requests.adapters import HTTPAdapter
|
@@ -62,6 +57,15 @@ from utils import (
|
|
62 |
limit_text,
|
63 |
DATA_DIR,
|
64 |
REQUEST_ID_FIELD,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
)
|
66 |
|
67 |
CONTRACTS_PATH = "contracts"
|
@@ -78,13 +82,6 @@ BLOCK_DATA_NUMBER = "number"
|
|
78 |
BLOCKS_CHUNK_SIZE = 10_000
|
79 |
EVENT_ARGUMENTS = "args"
|
80 |
DATA = "data"
|
81 |
-
REQUEST_ID = "requestId"
|
82 |
-
REQUEST_SENDER = "sender"
|
83 |
-
PROMPT_FIELD = "prompt"
|
84 |
-
CID_PREFIX = "f01701220"
|
85 |
-
HTTP = "http://"
|
86 |
-
HTTPS = HTTP[:4] + "s" + HTTP[4:]
|
87 |
-
IPFS_ADDRESS = f"{HTTPS}gateway.autonolas.tech/ipfs/"
|
88 |
IPFS_LINKS_SERIES_NAME = "ipfs_links"
|
89 |
BACKOFF_FACTOR = 1
|
90 |
STATUS_FORCELIST = [404, 500, 502, 503, 504]
|
@@ -96,7 +93,6 @@ N_IPFS_RETRIES = 1
|
|
96 |
N_RPC_RETRIES = 100
|
97 |
RPC_POLL_INTERVAL = 0.05
|
98 |
IPFS_POLL_INTERVAL = 0.05
|
99 |
-
FORMAT_UPDATE_BLOCK_NUMBER = 30411638
|
100 |
IRRELEVANT_TOOLS = [
|
101 |
"openai-text-davinci-002",
|
102 |
"openai-text-davinci-003",
|
@@ -117,178 +113,6 @@ NUM_WORKERS = 10
|
|
117 |
GET_CONTENTS_BATCH_SIZE = 1000
|
118 |
|
119 |
|
120 |
-
class MechEventName(Enum):
|
121 |
-
"""The mech's event names."""
|
122 |
-
|
123 |
-
REQUEST = "Request"
|
124 |
-
DELIVER = "Deliver"
|
125 |
-
|
126 |
-
|
127 |
-
@dataclass
|
128 |
-
class MechEvent:
|
129 |
-
"""A mech's on-chain event representation."""
|
130 |
-
|
131 |
-
for_block: int
|
132 |
-
requestId: int
|
133 |
-
data: bytes
|
134 |
-
sender: str
|
135 |
-
|
136 |
-
def _ipfs_link(self) -> Optional[str]:
|
137 |
-
"""Get the ipfs link for the data."""
|
138 |
-
return f"{IPFS_ADDRESS}{CID_PREFIX}{self.data.hex()}"
|
139 |
-
|
140 |
-
@property
|
141 |
-
def ipfs_request_link(self) -> Optional[str]:
|
142 |
-
"""Get the IPFS link for the request."""
|
143 |
-
return f"{self._ipfs_link()}/metadata.json"
|
144 |
-
|
145 |
-
@property
|
146 |
-
def ipfs_deliver_link(self) -> Optional[str]:
|
147 |
-
"""Get the IPFS link for the deliver."""
|
148 |
-
if self.requestId is None:
|
149 |
-
return None
|
150 |
-
return f"{self._ipfs_link()}/{self.requestId}"
|
151 |
-
|
152 |
-
def ipfs_link(self, event_name: MechEventName) -> Optional[str]:
|
153 |
-
"""Get the ipfs link based on the event."""
|
154 |
-
if event_name == MechEventName.REQUEST:
|
155 |
-
if self.for_block < FORMAT_UPDATE_BLOCK_NUMBER:
|
156 |
-
return self._ipfs_link()
|
157 |
-
return self.ipfs_request_link
|
158 |
-
if event_name == MechEventName.DELIVER:
|
159 |
-
return self.ipfs_deliver_link
|
160 |
-
return None
|
161 |
-
|
162 |
-
|
163 |
-
@dataclass(init=False)
|
164 |
-
class MechRequest:
|
165 |
-
"""A structure for a request to a mech."""
|
166 |
-
|
167 |
-
request_id: Optional[int]
|
168 |
-
request_block: Optional[int]
|
169 |
-
prompt_request: Optional[str]
|
170 |
-
tool: Optional[str]
|
171 |
-
nonce: Optional[str]
|
172 |
-
trader_address: Optional[str]
|
173 |
-
|
174 |
-
def __init__(self, **kwargs: Any) -> None:
|
175 |
-
"""Initialize the request ignoring extra keys."""
|
176 |
-
self.request_id = int(kwargs.pop(REQUEST_ID, 0))
|
177 |
-
self.request_block = int(kwargs.pop(BLOCK_FIELD, 0))
|
178 |
-
self.prompt_request = kwargs.pop(PROMPT_FIELD, None)
|
179 |
-
self.tool = kwargs.pop("tool", None)
|
180 |
-
self.nonce = kwargs.pop("nonce", None)
|
181 |
-
self.trader_address = kwargs.pop("sender", None)
|
182 |
-
|
183 |
-
|
184 |
-
@dataclass(init=False)
|
185 |
-
class PredictionResponse:
|
186 |
-
"""A response of a prediction."""
|
187 |
-
|
188 |
-
p_yes: float
|
189 |
-
p_no: float
|
190 |
-
confidence: float
|
191 |
-
info_utility: float
|
192 |
-
vote: Optional[str]
|
193 |
-
win_probability: Optional[float]
|
194 |
-
|
195 |
-
def __init__(self, **kwargs: Any) -> None:
|
196 |
-
"""Initialize the mech's prediction ignoring extra keys."""
|
197 |
-
try:
|
198 |
-
self.p_yes = float(kwargs.pop("p_yes"))
|
199 |
-
self.p_no = float(kwargs.pop("p_no"))
|
200 |
-
self.confidence = float(kwargs.pop("confidence"))
|
201 |
-
self.info_utility = float(kwargs.pop("info_utility"))
|
202 |
-
self.win_probability = 0
|
203 |
-
|
204 |
-
# Validate probabilities
|
205 |
-
probabilities = {
|
206 |
-
"p_yes": self.p_yes,
|
207 |
-
"p_no": self.p_no,
|
208 |
-
"confidence": self.confidence,
|
209 |
-
"info_utility": self.info_utility,
|
210 |
-
}
|
211 |
-
|
212 |
-
for name, prob in probabilities.items():
|
213 |
-
if not 0 <= prob <= 1:
|
214 |
-
raise ValueError(f"{name} probability is out of bounds: {prob}")
|
215 |
-
|
216 |
-
if self.p_yes + self.p_no != 1:
|
217 |
-
raise ValueError(
|
218 |
-
f"Sum of p_yes and p_no is not 1: {self.p_yes} + {self.p_no}"
|
219 |
-
)
|
220 |
-
|
221 |
-
self.vote = self.get_vote()
|
222 |
-
self.win_probability = self.get_win_probability()
|
223 |
-
|
224 |
-
except KeyError as e:
|
225 |
-
raise KeyError(f"Missing key in PredictionResponse: {e}")
|
226 |
-
except ValueError as e:
|
227 |
-
raise ValueError(f"Invalid value in PredictionResponse: {e}")
|
228 |
-
|
229 |
-
def get_vote(self) -> Optional[str]:
|
230 |
-
"""Return the vote."""
|
231 |
-
if self.p_no == self.p_yes:
|
232 |
-
return None
|
233 |
-
if self.p_no > self.p_yes:
|
234 |
-
return "No"
|
235 |
-
return "Yes"
|
236 |
-
|
237 |
-
def get_win_probability(self) -> Optional[float]:
|
238 |
-
"""Return the probability estimation for winning with vote."""
|
239 |
-
return max(self.p_no, self.p_yes)
|
240 |
-
|
241 |
-
|
242 |
-
@dataclass(init=False)
|
243 |
-
class MechResponse:
|
244 |
-
"""A structure for the response of a mech."""
|
245 |
-
|
246 |
-
request_id: int
|
247 |
-
deliver_block: Optional[int]
|
248 |
-
result: Optional[PredictionResponse]
|
249 |
-
error: Optional[str]
|
250 |
-
error_message: Optional[str]
|
251 |
-
prompt_response: Optional[str]
|
252 |
-
mech_address: Optional[str]
|
253 |
-
|
254 |
-
def __init__(self, **kwargs: Any) -> None:
|
255 |
-
"""Initialize the mech's response ignoring extra keys."""
|
256 |
-
self.error = kwargs.get("error", None)
|
257 |
-
self.request_id = int(kwargs.get(REQUEST_ID, 0))
|
258 |
-
self.deliver_block = int(kwargs.get(BLOCK_FIELD, 0))
|
259 |
-
self.result = kwargs.get("result", None)
|
260 |
-
self.prompt_response = kwargs.get(PROMPT_FIELD, None)
|
261 |
-
self.mech_address = kwargs.get("sender", None)
|
262 |
-
|
263 |
-
if self.result != "Invalid response":
|
264 |
-
self.error_message = kwargs.get("error_message", None)
|
265 |
-
|
266 |
-
try:
|
267 |
-
if isinstance(self.result, str):
|
268 |
-
kwargs = json.loads(self.result)
|
269 |
-
self.result = PredictionResponse(**kwargs)
|
270 |
-
self.error = 0
|
271 |
-
|
272 |
-
except JSONDecodeError:
|
273 |
-
self.error_message = "Response parsing error"
|
274 |
-
self.error = 1
|
275 |
-
|
276 |
-
except Exception as e:
|
277 |
-
self.error_message = str(e)
|
278 |
-
self.error = 1
|
279 |
-
|
280 |
-
else:
|
281 |
-
self.error_message = "Invalid response from tool"
|
282 |
-
self.error = 1
|
283 |
-
self.result = None
|
284 |
-
|
285 |
-
|
286 |
-
EVENT_TO_MECH_STRUCT = {
|
287 |
-
MechEventName.REQUEST: MechRequest,
|
288 |
-
MechEventName.DELIVER: MechResponse,
|
289 |
-
}
|
290 |
-
|
291 |
-
|
292 |
def get_events(
|
293 |
w3: Web3,
|
294 |
event: str,
|
@@ -554,7 +378,8 @@ def etl(
|
|
554 |
rpcs: List[str],
|
555 |
filename: Optional[str] = None,
|
556 |
) -> pd.DataFrame:
|
557 |
-
"""Fetch from on-chain events, process, store and return the tools' results on
|
|
|
558 |
w3s = [Web3(HTTPProvider(r)) for r in rpcs]
|
559 |
session = create_session()
|
560 |
event_to_transformer = {
|
|
|
17 |
#
|
18 |
# ------------------------------------------------------------------------------
|
19 |
|
|
|
20 |
import os.path
|
21 |
import re
|
22 |
import time
|
23 |
import random
|
|
|
|
|
24 |
from typing import (
|
25 |
Optional,
|
26 |
List,
|
27 |
Dict,
|
|
|
28 |
Union,
|
29 |
)
|
30 |
import pandas as pd
|
31 |
import requests
|
|
|
32 |
from eth_typing import ChecksumAddress
|
33 |
from eth_utils import to_checksum_address
|
34 |
from requests.adapters import HTTPAdapter
|
|
|
57 |
limit_text,
|
58 |
DATA_DIR,
|
59 |
REQUEST_ID_FIELD,
|
60 |
+
MechEvent,
|
61 |
+
MechEventName,
|
62 |
+
MechRequest,
|
63 |
+
MechResponse,
|
64 |
+
EVENT_TO_MECH_STRUCT,
|
65 |
+
REQUEST_ID,
|
66 |
+
HTTP,
|
67 |
+
HTTPS,
|
68 |
+
REQUEST_SENDER,
|
69 |
)
|
70 |
|
71 |
CONTRACTS_PATH = "contracts"
|
|
|
82 |
BLOCKS_CHUNK_SIZE = 10_000
|
83 |
EVENT_ARGUMENTS = "args"
|
84 |
DATA = "data"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
IPFS_LINKS_SERIES_NAME = "ipfs_links"
|
86 |
BACKOFF_FACTOR = 1
|
87 |
STATUS_FORCELIST = [404, 500, 502, 503, 504]
|
|
|
93 |
N_RPC_RETRIES = 100
|
94 |
RPC_POLL_INTERVAL = 0.05
|
95 |
IPFS_POLL_INTERVAL = 0.05
|
|
|
96 |
IRRELEVANT_TOOLS = [
|
97 |
"openai-text-davinci-002",
|
98 |
"openai-text-davinci-003",
|
|
|
113 |
GET_CONTENTS_BATCH_SIZE = 1000
|
114 |
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
def get_events(
|
117 |
w3: Web3,
|
118 |
event: str,
|
|
|
378 |
rpcs: List[str],
|
379 |
filename: Optional[str] = None,
|
380 |
) -> pd.DataFrame:
|
381 |
+
"""Fetch from on-chain events, process, store and return the tools' results on
|
382 |
+
all the questions as a Dataframe."""
|
383 |
w3s = [Web3(HTTPProvider(r)) for r in rpcs]
|
384 |
session = create_session()
|
385 |
event_to_transformer = {
|
scripts/utils.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1 |
import sys
|
|
|
2 |
import os
|
3 |
import time
|
4 |
from tqdm import tqdm
|
5 |
-
from
|
6 |
-
from typing import List
|
7 |
import pandas as pd
|
8 |
import gc
|
|
|
9 |
from pathlib import Path
|
|
|
|
|
10 |
|
11 |
REDUCE_FACTOR = 0.25
|
12 |
SLEEP = 0.5
|
@@ -15,6 +18,186 @@ SCRIPTS_DIR = Path(__file__).parent
|
|
15 |
ROOT_DIR = SCRIPTS_DIR.parent
|
16 |
DATA_DIR = ROOT_DIR / "data"
|
17 |
BLOCK_FIELD = "block"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
|
20 |
def parse_args() -> str:
|
|
|
1 |
import sys
|
2 |
+
import json
|
3 |
import os
|
4 |
import time
|
5 |
from tqdm import tqdm
|
6 |
+
from typing import List, Any, Optional
|
|
|
7 |
import pandas as pd
|
8 |
import gc
|
9 |
+
from dataclasses import dataclass
|
10 |
from pathlib import Path
|
11 |
+
from enum import Enum
|
12 |
+
from json.decoder import JSONDecodeError
|
13 |
|
14 |
REDUCE_FACTOR = 0.25
|
15 |
SLEEP = 0.5
|
|
|
18 |
ROOT_DIR = SCRIPTS_DIR.parent
|
19 |
DATA_DIR = ROOT_DIR / "data"
|
20 |
BLOCK_FIELD = "block"
|
21 |
+
CID_PREFIX = "f01701220"
|
22 |
+
REQUEST_ID = "requestId"
|
23 |
+
REQUEST_SENDER = "sender"
|
24 |
+
PROMPT_FIELD = "prompt"
|
25 |
+
HTTP = "http://"
|
26 |
+
HTTPS = HTTP[:4] + "s" + HTTP[4:]
|
27 |
+
IPFS_ADDRESS = f"{HTTPS}gateway.autonolas.tech/ipfs/"
|
28 |
+
FORMAT_UPDATE_BLOCK_NUMBER = 30411638
|
29 |
+
|
30 |
+
|
31 |
+
class MechEventName(Enum):
|
32 |
+
"""The mech's event names."""
|
33 |
+
|
34 |
+
REQUEST = "Request"
|
35 |
+
DELIVER = "Deliver"
|
36 |
+
|
37 |
+
|
38 |
+
@dataclass
|
39 |
+
class MechEvent:
|
40 |
+
"""A mech's on-chain event representation."""
|
41 |
+
|
42 |
+
for_block: int
|
43 |
+
requestId: int
|
44 |
+
data: bytes
|
45 |
+
sender: str
|
46 |
+
|
47 |
+
def _ipfs_link(self) -> Optional[str]:
|
48 |
+
"""Get the ipfs link for the data."""
|
49 |
+
return f"{IPFS_ADDRESS}{CID_PREFIX}{self.data.hex()}"
|
50 |
+
|
51 |
+
@property
|
52 |
+
def ipfs_request_link(self) -> Optional[str]:
|
53 |
+
"""Get the IPFS link for the request."""
|
54 |
+
return f"{self._ipfs_link()}/metadata.json"
|
55 |
+
|
56 |
+
@property
|
57 |
+
def ipfs_deliver_link(self) -> Optional[str]:
|
58 |
+
"""Get the IPFS link for the deliver."""
|
59 |
+
if self.requestId is None:
|
60 |
+
return None
|
61 |
+
return f"{self._ipfs_link()}/{self.requestId}"
|
62 |
+
|
63 |
+
def ipfs_link(self, event_name: MechEventName) -> Optional[str]:
|
64 |
+
"""Get the ipfs link based on the event."""
|
65 |
+
if event_name == MechEventName.REQUEST:
|
66 |
+
if self.for_block < FORMAT_UPDATE_BLOCK_NUMBER:
|
67 |
+
return self._ipfs_link()
|
68 |
+
return self.ipfs_request_link
|
69 |
+
if event_name == MechEventName.DELIVER:
|
70 |
+
return self.ipfs_deliver_link
|
71 |
+
return None
|
72 |
+
|
73 |
+
|
74 |
+
@dataclass(init=False)
|
75 |
+
class MechRequest:
|
76 |
+
"""A structure for a request to a mech."""
|
77 |
+
|
78 |
+
request_id: Optional[int]
|
79 |
+
request_block: Optional[int]
|
80 |
+
prompt_request: Optional[str]
|
81 |
+
tool: Optional[str]
|
82 |
+
nonce: Optional[str]
|
83 |
+
trader_address: Optional[str]
|
84 |
+
|
85 |
+
def __init__(self, **kwargs: Any) -> None:
|
86 |
+
"""Initialize the request ignoring extra keys."""
|
87 |
+
self.request_id = int(kwargs.pop(REQUEST_ID, 0))
|
88 |
+
self.request_block = int(kwargs.pop(BLOCK_FIELD, 0))
|
89 |
+
self.prompt_request = kwargs.pop(PROMPT_FIELD, None)
|
90 |
+
self.tool = kwargs.pop("tool", None)
|
91 |
+
self.nonce = kwargs.pop("nonce", None)
|
92 |
+
self.trader_address = kwargs.pop("sender", None)
|
93 |
+
|
94 |
+
|
95 |
+
@dataclass(init=False)
|
96 |
+
class PredictionResponse:
|
97 |
+
"""A response of a prediction."""
|
98 |
+
|
99 |
+
p_yes: float
|
100 |
+
p_no: float
|
101 |
+
confidence: float
|
102 |
+
info_utility: float
|
103 |
+
vote: Optional[str]
|
104 |
+
win_probability: Optional[float]
|
105 |
+
|
106 |
+
def __init__(self, **kwargs: Any) -> None:
|
107 |
+
"""Initialize the mech's prediction ignoring extra keys."""
|
108 |
+
try:
|
109 |
+
self.p_yes = float(kwargs.pop("p_yes"))
|
110 |
+
self.p_no = float(kwargs.pop("p_no"))
|
111 |
+
self.confidence = float(kwargs.pop("confidence"))
|
112 |
+
self.info_utility = float(kwargs.pop("info_utility"))
|
113 |
+
self.win_probability = 0
|
114 |
+
|
115 |
+
# Validate probabilities
|
116 |
+
probabilities = {
|
117 |
+
"p_yes": self.p_yes,
|
118 |
+
"p_no": self.p_no,
|
119 |
+
"confidence": self.confidence,
|
120 |
+
"info_utility": self.info_utility,
|
121 |
+
}
|
122 |
+
|
123 |
+
for name, prob in probabilities.items():
|
124 |
+
if not 0 <= prob <= 1:
|
125 |
+
raise ValueError(f"{name} probability is out of bounds: {prob}")
|
126 |
+
|
127 |
+
if self.p_yes + self.p_no != 1:
|
128 |
+
raise ValueError(
|
129 |
+
f"Sum of p_yes and p_no is not 1: {self.p_yes} + {self.p_no}"
|
130 |
+
)
|
131 |
+
|
132 |
+
self.vote = self.get_vote()
|
133 |
+
self.win_probability = self.get_win_probability()
|
134 |
+
|
135 |
+
except KeyError as e:
|
136 |
+
raise KeyError(f"Missing key in PredictionResponse: {e}")
|
137 |
+
except ValueError as e:
|
138 |
+
raise ValueError(f"Invalid value in PredictionResponse: {e}")
|
139 |
+
|
140 |
+
def get_vote(self) -> Optional[str]:
|
141 |
+
"""Return the vote."""
|
142 |
+
if self.p_no == self.p_yes:
|
143 |
+
return None
|
144 |
+
if self.p_no > self.p_yes:
|
145 |
+
return "No"
|
146 |
+
return "Yes"
|
147 |
+
|
148 |
+
def get_win_probability(self) -> Optional[float]:
|
149 |
+
"""Return the probability estimation for winning with vote."""
|
150 |
+
return max(self.p_no, self.p_yes)
|
151 |
+
|
152 |
+
|
153 |
+
@dataclass(init=False)
|
154 |
+
class MechResponse:
|
155 |
+
"""A structure for the response of a mech."""
|
156 |
+
|
157 |
+
request_id: int
|
158 |
+
deliver_block: Optional[int]
|
159 |
+
result: Optional[PredictionResponse]
|
160 |
+
error: Optional[str]
|
161 |
+
error_message: Optional[str]
|
162 |
+
prompt_response: Optional[str]
|
163 |
+
mech_address: Optional[str]
|
164 |
+
|
165 |
+
def __init__(self, **kwargs: Any) -> None:
|
166 |
+
"""Initialize the mech's response ignoring extra keys."""
|
167 |
+
self.error = kwargs.get("error", None)
|
168 |
+
self.request_id = int(kwargs.get(REQUEST_ID, 0))
|
169 |
+
self.deliver_block = int(kwargs.get(BLOCK_FIELD, 0))
|
170 |
+
self.result = kwargs.get("result", None)
|
171 |
+
self.prompt_response = kwargs.get(PROMPT_FIELD, None)
|
172 |
+
self.mech_address = kwargs.get("sender", None)
|
173 |
+
|
174 |
+
if self.result != "Invalid response":
|
175 |
+
self.error_message = kwargs.get("error_message", None)
|
176 |
+
|
177 |
+
try:
|
178 |
+
if isinstance(self.result, str):
|
179 |
+
kwargs = json.loads(self.result)
|
180 |
+
self.result = PredictionResponse(**kwargs)
|
181 |
+
self.error = 0
|
182 |
+
|
183 |
+
except JSONDecodeError:
|
184 |
+
self.error_message = "Response parsing error"
|
185 |
+
self.error = 1
|
186 |
+
|
187 |
+
except Exception as e:
|
188 |
+
self.error_message = str(e)
|
189 |
+
self.error = 1
|
190 |
+
|
191 |
+
else:
|
192 |
+
self.error_message = "Invalid response from tool"
|
193 |
+
self.error = 1
|
194 |
+
self.result = None
|
195 |
+
|
196 |
+
|
197 |
+
EVENT_TO_MECH_STRUCT = {
|
198 |
+
MechEventName.REQUEST: MechRequest,
|
199 |
+
MechEventName.DELIVER: MechResponse,
|
200 |
+
}
|
201 |
|
202 |
|
203 |
def parse_args() -> str:
|