prediction-market-agent-tooling 0.65.10__py3-none-any.whl → 0.65.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,268 @@
1
+ import os
2
+ from datetime import datetime, timedelta
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ import pandas as pd
7
+ import typer
8
+ from langfuse import Langfuse
9
+ from langfuse.client import TraceWithDetails
10
+ from pydantic import BaseModel
11
+
12
+ from prediction_market_agent_tooling.config import APIKeys
13
+ from prediction_market_agent_tooling.gtypes import DatetimeUTC, OutcomeStr, OutcomeToken
14
+ from prediction_market_agent_tooling.loggers import logger
15
+ from prediction_market_agent_tooling.markets.agent_market import AgentMarket
16
+ from prediction_market_agent_tooling.markets.data_models import Resolution
17
+ from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
18
+ from prediction_market_agent_tooling.markets.seer.seer import SeerAgentMarket
19
+ from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
20
+ SeerSubgraphHandler,
21
+ )
22
+ from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
23
+ from prediction_market_agent_tooling.tools.httpx_cached_client import HttpxCachedClient
24
+ from prediction_market_agent_tooling.tools.langfuse_client_utils import (
25
+ get_traces_for_agent,
26
+ )
27
+ from prediction_market_agent_tooling.tools.parallelism import par_map
28
+
29
+ PREDICTION_STATES = [
30
+ "predict_market",
31
+ "_make_prediction_categorical",
32
+ "make_prediction",
33
+ ]
34
+ REPORT_STATES = ["prepare_report"]
35
+
36
+ MARKET_RESOLUTION_PROVIDERS = {
37
+ "omen": lambda market_id: OmenAgentMarket.get_binary_market(market_id),
38
+ "seer": lambda market_id: SeerAgentMarket.from_data_model_with_subgraph(
39
+ model=SeerSubgraphHandler().get_market_by_id(HexBytes(market_id)),
40
+ seer_subgraph=SeerSubgraphHandler(),
41
+ must_have_prices=False,
42
+ ),
43
+ }
44
+
45
+
46
+ class TraceResult(BaseModel):
47
+ agent_name: str
48
+ trace_id: str
49
+ market_id: str
50
+ market_type: str
51
+ market_question: str
52
+ market_outcomes: list[str]
53
+ market_outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
54
+ market_created_time: DatetimeUTC | None
55
+ market_close_time: DatetimeUTC | None
56
+ analysis: str
57
+ prediction_reasoning: str
58
+ prediction_decision: str
59
+ prediction_p_yes: float
60
+ prediction_info_utility: float
61
+ market_resolution: str | None
62
+ resolution_is_valid: bool | None
63
+
64
+
65
+ def get_langfuse_client() -> Langfuse:
66
+ api_keys = APIKeys()
67
+ return Langfuse(
68
+ secret_key=api_keys.langfuse_secret_key.get_secret_value(),
69
+ public_key=api_keys.langfuse_public_key,
70
+ host=api_keys.langfuse_host,
71
+ httpx_client=HttpxCachedClient().get_client(),
72
+ )
73
+
74
+
75
+ def download_data(
76
+ agent_name: str,
77
+ date_from: DatetimeUTC,
78
+ date_to: DatetimeUTC,
79
+ only_resolved: bool,
80
+ output_folder: str,
81
+ ) -> None:
82
+ Path(output_folder).mkdir(parents=True, exist_ok=True)
83
+ index = 0
84
+ default_file_name = f"{agent_name}_{date_from.date()}_{date_to.date()}"
85
+ output_file = os.path.join(output_folder, f"{default_file_name}.csv")
86
+
87
+ if os.path.exists(output_file):
88
+ while os.path.exists(output_file):
89
+ index += 1
90
+ output_file = os.path.join(
91
+ output_folder, f"{default_file_name}_v{index}.csv"
92
+ )
93
+
94
+ langfuse_client_for_traces = get_langfuse_client()
95
+
96
+ traces = get_traces_for_agent(
97
+ agent_name=agent_name,
98
+ trace_name="process_market",
99
+ from_timestamp=date_from,
100
+ to_timestamp=date_to,
101
+ has_output=True,
102
+ client=langfuse_client_for_traces,
103
+ tags=["answered"],
104
+ )
105
+
106
+ if not traces:
107
+ raise ValueError("No traces found for the specified criteria")
108
+
109
+ trace_args = [
110
+ (
111
+ trace,
112
+ only_resolved,
113
+ )
114
+ for trace in traces
115
+ ]
116
+
117
+ results = par_map(
118
+ items=trace_args,
119
+ func=lambda args: process_trace(*args),
120
+ max_workers=5,
121
+ )
122
+
123
+ successful_results = [r for r in results if r is not None]
124
+ if successful_results:
125
+ results_data = [result.model_dump() for result in successful_results]
126
+ pd.DataFrame(results_data).to_csv(output_file, index=False)
127
+ logger.info(f"Saved {len(successful_results)} records to {output_file}")
128
+ else:
129
+ logger.warning("No results to save")
130
+
131
+
132
+ def process_trace(
133
+ trace: TraceWithDetails,
134
+ only_resolved: bool,
135
+ ) -> TraceResult | None:
136
+ langfuse_client = get_langfuse_client()
137
+ try:
138
+ observations = langfuse_client.fetch_observations(trace_id=trace.id)
139
+
140
+ market_state, market_type = get_agent_market_state(trace.input)
141
+
142
+ prepare_report_obs = [
143
+ obs for obs in observations.data if obs.name in REPORT_STATES
144
+ ]
145
+ predict_market_obs = [
146
+ obs for obs in observations.data if obs.name in PREDICTION_STATES
147
+ ]
148
+
149
+ if not prepare_report_obs or not predict_market_obs:
150
+ raise ValueError(f"Missing required observations for trace {trace.id}")
151
+
152
+ analysis = prepare_report_obs[0].output
153
+ prediction = predict_market_obs[0].output
154
+
155
+ resolution = get_market_resolution(market_state.id, market_type)
156
+
157
+ if only_resolved and not resolution:
158
+ raise ValueError(f"No resolution found for market {market_state.id}")
159
+
160
+ result = TraceResult(
161
+ agent_name=trace.metadata["agent_class"],
162
+ trace_id=trace.id,
163
+ market_id=market_state.id,
164
+ market_type=market_type,
165
+ market_question=market_state.question,
166
+ market_outcomes=list(market_state.outcomes),
167
+ market_outcome_token_pool=market_state.outcome_token_pool,
168
+ market_created_time=market_state.created_time,
169
+ market_close_time=market_state.close_time,
170
+ analysis=analysis,
171
+ prediction_reasoning=prediction["reasoning"],
172
+ prediction_decision="YES" if prediction["decision"] == "y" else "NO",
173
+ prediction_p_yes=prediction["p_yes"],
174
+ prediction_info_utility=prediction["info_utility"],
175
+ market_resolution=resolution.outcome if resolution else None,
176
+ resolution_is_valid=not resolution.invalid if resolution else None,
177
+ )
178
+
179
+ return result
180
+
181
+ except Exception as e:
182
+ logger.exception(f"Error processing trace {trace.id}: {e}")
183
+ return None
184
+
185
+
186
+ def get_agent_market_state(input_data: dict[str, Any]) -> tuple[AgentMarket, str]:
187
+ if not input_data or "args" not in input_data:
188
+ raise ValueError("Invalid input data: missing args")
189
+
190
+ args = input_data["args"]
191
+ if len(args) < 2:
192
+ raise ValueError("Invalid args: expected at least 2 elements")
193
+
194
+ market_type = args[0] # e.g., "omen", "seer"
195
+
196
+ if market_type not in MARKET_RESOLUTION_PROVIDERS:
197
+ raise ValueError(f"Unknown market type: {market_type}")
198
+
199
+ market_data = args[1] # market object data
200
+ market_state = AgentMarket.model_construct(**market_data)
201
+
202
+ return market_state, market_type
203
+
204
+
205
+ def get_market_resolution(market_id: str, market_type: str) -> Resolution:
206
+ market_type_lower = market_type.lower()
207
+
208
+ if market_type_lower not in MARKET_RESOLUTION_PROVIDERS:
209
+ raise ValueError(f"Unknown market type: {market_type}")
210
+
211
+ try:
212
+ market: AgentMarket | None = MARKET_RESOLUTION_PROVIDERS[market_type_lower](
213
+ market_id
214
+ )
215
+
216
+ if not market or not market.resolution:
217
+ raise ValueError(f"No resolution found for market: {market_id}")
218
+
219
+ return market.resolution
220
+
221
+ except Exception as e:
222
+ raise ValueError(
223
+ f"Failed to fetch {market_type} market {market_id} resolution: {e}"
224
+ ) from e
225
+
226
+
227
+ def parse_date(date_str: str, param_name: str) -> DatetimeUTC:
228
+ try:
229
+ return DatetimeUTC.to_datetime_utc(date_str)
230
+ except ValueError as e:
231
+ typer.echo(f"Error: Invalid date format for {param_name}: {date_str}")
232
+ typer.echo("Expected format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS")
233
+ raise typer.Exit(1) from e
234
+
235
+
236
+ def main(
237
+ agent_name: str = "DeployablePredictionProphetGPT4oAgent",
238
+ only_resolved: bool = True,
239
+ date_from: str = typer.Option(
240
+ None, help="Start date in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)"
241
+ ),
242
+ date_to: str = typer.Option(
243
+ None, help="End date in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)"
244
+ ),
245
+ output_folder: str = "./agent_trades_output/",
246
+ ) -> None:
247
+ date_from_dt = (
248
+ parse_date(date_from, "date_from")
249
+ if date_from
250
+ else DatetimeUTC.from_datetime(datetime.now() - timedelta(days=1))
251
+ )
252
+ date_to_dt = (
253
+ parse_date(date_to, "date_to")
254
+ if date_to
255
+ else DatetimeUTC.from_datetime(datetime.now())
256
+ )
257
+
258
+ download_data(
259
+ agent_name=agent_name,
260
+ date_from=date_from_dt,
261
+ date_to=date_to_dt,
262
+ only_resolved=only_resolved,
263
+ output_folder=output_folder,
264
+ )
265
+
266
+
267
+ if __name__ == "__main__":
268
+ typer.run(main)
@@ -10,6 +10,8 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
10
10
  from pythonjsonlogger import jsonlogger
11
11
  from tenacity import RetryError
12
12
 
13
+ UNPATCHED_PRINT_FN = builtins.print
14
+
13
15
 
14
16
  class LogFormat(str, Enum):
15
17
  DEFAULT = "default"
@@ -153,7 +155,13 @@ def print_using_logger_info(
153
155
  end: str = "\n",
154
156
  **kwargs: t.Any,
155
157
  ) -> None:
156
- logger.info(sep.join(map(str, values)) + end)
158
+ # If `logger.exception` is used, loguru+traceback somehow uses `print` statement to format the error stack,
159
+ # if that happens, without this if condition, it errors out because of deadlock and/or recursion errors.
160
+ # This is hacky, but that's exactly how loguru is checking for it internally..
161
+ if any(getattr(handler._lock_acquired, "acquired", False) for handler in logger._core.handlers.values()): # type: ignore # They use stubs and didn't type this.
162
+ UNPATCHED_PRINT_FN(*values, sep=sep, end=end, **kwargs)
163
+ else:
164
+ logger.info(sep.join(map(str, values)) + end)
157
165
 
158
166
 
159
167
  patch_logger()
@@ -6,3 +6,6 @@ WRAPPED_XDAI_CONTRACT_ADDRESS = Web3.to_checksum_address(
6
6
  SDAI_CONTRACT_ADDRESS = Web3.to_checksum_address(
7
7
  "0xaf204776c7245bF4147c2612BF6e5972Ee483701"
8
8
  )
9
+ METRI_SUPER_GROUP_CONTRACT_ADDRESS = Web3.to_checksum_address(
10
+ "0x7147A7405fCFe5CFa30c6d5363f9f357a317d082"
11
+ )
@@ -33,6 +33,7 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
33
33
  format_realitio_question,
34
34
  )
35
35
  from prediction_market_agent_tooling.markets.omen.omen_constants import (
36
+ METRI_SUPER_GROUP_CONTRACT_ADDRESS,
36
37
  SDAI_CONTRACT_ADDRESS,
37
38
  WRAPPED_XDAI_CONTRACT_ADDRESS,
38
39
  )
@@ -433,7 +434,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
433
434
 
434
435
  class MetriSuperGroup(ContractERC20OnGnosisChain):
435
436
  address: ChecksumAddress = Web3.to_checksum_address(
436
- "0x7147A7405fCFe5CFa30c6d5363f9f357a317d082"
437
+ METRI_SUPER_GROUP_CONTRACT_ADDRESS
437
438
  )
438
439
 
439
440
 
@@ -284,6 +284,9 @@ def omen_resolve_market_tx(
284
284
  logger.warning(
285
285
  f"Market {market.url=} not resolved, because `condition not prepared or found`, skipping."
286
286
  )
287
+ elif "payout denominator already set" in str(e):
288
+ # We can just skip, it's been resolved already.
289
+ logger.info(f"Market {market.url=} is already resolved.")
287
290
  else:
288
291
  raise
289
292
 
@@ -32,6 +32,7 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
32
32
  COWContract,
33
33
  EUReContract,
34
34
  GNOContract,
35
+ MetriSuperGroup,
35
36
  OmenThumbnailMapping,
36
37
  SAFEContract,
37
38
  WETHContract,
@@ -60,6 +61,7 @@ SAFE_COLLATERAL_TOKENS = (
60
61
  EUReContract(),
61
62
  SAFEContract(),
62
63
  COWContract(),
64
+ MetriSuperGroup(),
63
65
  )
64
66
  SAFE_COLLATERAL_TOKENS_ADDRESSES = tuple(
65
67
  contract.address for contract in SAFE_COLLATERAL_TOKENS
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.65.10
3
+ Version: 0.65.12
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.13
@@ -25,6 +25,7 @@ prediction_market_agent_tooling/benchmark/benchmark.py,sha256=hpIpjjePDFTLhK841s
25
25
  prediction_market_agent_tooling/benchmark/utils.py,sha256=xQd7p9H08-OtN3iC4QT2i9bkUTmrXa6rxGXeg9yMhgU,2986
26
26
  prediction_market_agent_tooling/chains.py,sha256=1qQstoqXMwqwM7k-KH7MjMz8Ei-D83KZByvDbCZpAxs,116
27
27
  prediction_market_agent_tooling/config.py,sha256=-kJfdDr-m0R-tGZ1KRI-hJJk0mXDt142CAlvwaJ2N2I,11778
28
+ prediction_market_agent_tooling/data_download/langfuse_data_downloader.py,sha256=EOtFKEtljwts1ftkcSN_i9Wk8jbfnhMehPRC63BU7l0,8842
28
29
  prediction_market_agent_tooling/deploy/agent.py,sha256=ONuXYe9zRO37lzume5z-2hPKTBrCljBg5OD_38gNRc0,25289
29
30
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=yS1fWkHynr9MYGNOM2WsCnRWLPaffY4bOc6bIudrdd4,1377
30
31
  prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=YYayGjTKW02d3BUavJ8M3NmFk41oldEM3FHbwppZGRM,17184
@@ -37,7 +38,7 @@ prediction_market_agent_tooling/gtypes.py,sha256=bUIZfZIGvIi3aiZNu5rVE9kRevw8sfM
37
38
  prediction_market_agent_tooling/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
39
  prediction_market_agent_tooling/jobs/jobs_models.py,sha256=DoZ9dlvVhpNrnINiR1uy6YUOsuzI_L-avBt362y5xXM,2467
39
40
  prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=qbTZ9HVvu_iP4dDxuvOZxAp6JsRKejvEW2YDYCnRmd4,5039
40
- prediction_market_agent_tooling/loggers.py,sha256=kFZ1BrI8hvWgZO1vzptFnYiOEDx9Ozs86DA9yF3bSgY,5212
41
+ prediction_market_agent_tooling/loggers.py,sha256=o1HyvwtK1DbuC0YWQwJNqzXLLbSC41gNBkEUxiAziEg,5796
41
42
  prediction_market_agent_tooling/logprobs_parser.py,sha256=DBlBQtWX8_URXhzTU3YWIPa76Zx3QDHlx1ARqbgJsVI,5008
42
43
  prediction_market_agent_tooling/markets/agent_market.py,sha256=pnyxTMUrL8cQsP4lmBBpe07R-Mi7S4vNjrvNLdOzAa8,19258
43
44
  prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=7RaYO_4qAmQ6ZGM8oPK2-CkiJfKmV9MxM-rJlduaecU,1971
@@ -58,10 +59,10 @@ prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TI
58
59
  prediction_market_agent_tooling/markets/omen/cow_contracts.py,sha256=sl1L4cK5nAJwZ2wdhLzqh8p7h_IEValNvLwKUlInKxw,957
59
60
  prediction_market_agent_tooling/markets/omen/data_models.py,sha256=0jCxgUEVpaggt_avkNqgXAnZWdA6D-ew9PE2gm0aqDk,29930
60
61
  prediction_market_agent_tooling/markets/omen/omen.py,sha256=q3SqvQEpZAsbFBbSid_rhRn-hVi-roCRN4piGj2wxU8,50301
61
- prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=D9oflYKafLQiHYtB5sScMHqmXyzM8JP8J0yATmc4SQQ,233
62
- prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=f0dKbdVM2OUTSpkCSZdPtKqeckS2c48j3rjnK8oD3wE,29716
63
- prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=_9FjpH-y_yDwua2-mZ0Iop49cRBs2m7w-Z1s3mOofFk,10848
64
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=149T_tJxK2GFnGCulOlGr2Mwiaa7sa74E4lqYsuCv68,39962
62
+ prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=pYcdK5a1nPte9x7E154vkIiM6cLw1JvqpLibs9HsBbo,347
63
+ prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=_Zkyqad_ft4OFSDrNKsepkRQ-Uda4me_4Rbk0ZFmsVw,29746
64
+ prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=D-ubf_LumHs_c5rBAAntQ8wGKprtO2V1JZeedmChNIE,11035
65
+ prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=h-YFRLY5rlhM9RqqceyfbHlno3elltN8Nr_Mnu1kJ90,40006
65
66
  prediction_market_agent_tooling/markets/polymarket/api.py,sha256=UZ4_TG8ceb9Y-qgsOKs8Qiv8zDt957QkT8IX2c83yqo,4800
66
67
  prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=U1SXTz93FIG3GO1h5BJWSt1hPKn_YAMBeZ3k8IS-ook,4552
67
68
  prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=wCwpZ8Kppy8JG_1HZDfEK_Gg1g1sL7NCbGoPeTeMSko,12756
@@ -123,8 +124,8 @@ prediction_market_agent_tooling/tools/tokens/usd.py,sha256=yuW8iPPtcpP4eLH2nORMD
123
124
  prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
124
125
  prediction_market_agent_tooling/tools/utils.py,sha256=RlWSlzS2LavMIWrpwn1fevbzgPZruD4VcXTa-XxjWnE,7343
125
126
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=0r26snqCXGdLKCWA8jpe7DV8x2NPYWZwOy4oyKyDCYk,12615
126
- prediction_market_agent_tooling-0.65.10.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
127
- prediction_market_agent_tooling-0.65.10.dist-info/METADATA,sha256=vlhpH2uSRf9E9HKpHsW3ovlHqMl-rFt_GqyJqxjH5ao,8735
128
- prediction_market_agent_tooling-0.65.10.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
129
- prediction_market_agent_tooling-0.65.10.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
130
- prediction_market_agent_tooling-0.65.10.dist-info/RECORD,,
127
+ prediction_market_agent_tooling-0.65.12.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
128
+ prediction_market_agent_tooling-0.65.12.dist-info/METADATA,sha256=9-GeVeUmwe58cQ3nQOv99cBxQyRoT3TCb9xa_jVdhCM,8735
129
+ prediction_market_agent_tooling-0.65.12.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
130
+ prediction_market_agent_tooling-0.65.12.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
131
+ prediction_market_agent_tooling-0.65.12.dist-info/RECORD,,