prediction-market-agent-tooling 0.56.3.dev135__py3-none-any.whl → 0.57.0__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.
Files changed (28) hide show
  1. prediction_market_agent_tooling/config.py +29 -0
  2. prediction_market_agent_tooling/deploy/agent.py +71 -48
  3. prediction_market_agent_tooling/markets/data_models.py +21 -0
  4. prediction_market_agent_tooling/markets/manifold/data_models.py +0 -1
  5. prediction_market_agent_tooling/markets/omen/data_models.py +4 -7
  6. prediction_market_agent_tooling/markets/omen/omen.py +2 -1
  7. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +8 -4
  8. prediction_market_agent_tooling/markets/polymarket/utils.py +1 -1
  9. prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +66 -0
  10. prediction_market_agent_tooling/tools/caches/db_cache.py +11 -13
  11. prediction_market_agent_tooling/tools/contract.py +3 -7
  12. prediction_market_agent_tooling/tools/custom_exceptions.py +6 -0
  13. prediction_market_agent_tooling/tools/{google.py → google_utils.py} +28 -1
  14. prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +1 -1
  15. prediction_market_agent_tooling/tools/is_invalid.py +1 -1
  16. prediction_market_agent_tooling/tools/is_predictable.py +8 -3
  17. prediction_market_agent_tooling/tools/langfuse_client_utils.py +1 -0
  18. prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py +6 -3
  19. prediction_market_agent_tooling/tools/transaction_cache.py +48 -0
  20. prediction_market_agent_tooling/tools/utils.py +1 -23
  21. prediction_market_agent_tooling/tools/web3_utils.py +5 -1
  22. {prediction_market_agent_tooling-0.56.3.dev135.dist-info → prediction_market_agent_tooling-0.57.0.dist-info}/METADATA +1 -1
  23. {prediction_market_agent_tooling-0.56.3.dev135.dist-info → prediction_market_agent_tooling-0.57.0.dist-info}/RECORD +26 -25
  24. prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -6
  25. prediction_market_agent_tooling/tools/pickle_utils.py +0 -31
  26. {prediction_market_agent_tooling-0.56.3.dev135.dist-info → prediction_market_agent_tooling-0.57.0.dist-info}/LICENSE +0 -0
  27. {prediction_market_agent_tooling-0.56.3.dev135.dist-info → prediction_market_agent_tooling-0.57.0.dist-info}/WHEEL +0 -0
  28. {prediction_market_agent_tooling-0.56.3.dev135.dist-info → prediction_market_agent_tooling-0.57.0.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import typing as t
2
2
 
3
+ from pydantic import Field
3
4
  from pydantic.types import SecretStr
4
5
  from pydantic.v1.types import SecretStr as SecretStrV1
5
6
  from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -7,6 +8,7 @@ from safe_eth.eth import EthereumClient
7
8
  from safe_eth.safe.safe import SafeV141
8
9
 
9
10
  from prediction_market_agent_tooling.gtypes import (
11
+ ChainID,
10
12
  ChecksumAddress,
11
13
  PrivateKey,
12
14
  secretstr_to_v1_secretstr,
@@ -203,3 +205,30 @@ class APIKeys(BaseSettings):
203
205
  s = SafeV141(self.SAFE_ADDRESS, ethereum_client)
204
206
  public_key_from_signer = private_key_to_public_key(self.bet_from_private_key)
205
207
  return s.retrieve_is_owner(public_key_from_signer)
208
+
209
+
210
+ class RPCConfig(BaseSettings):
211
+ model_config = SettingsConfigDict(
212
+ env_file=".env", env_file_encoding="utf-8", extra="ignore"
213
+ )
214
+
215
+ GNOSIS_RPC_URL: str = Field(default="https://rpc.gnosischain.com")
216
+ CHAIN_ID: ChainID = Field(default=ChainID(100))
217
+
218
+ @property
219
+ def gnosis_rpc_url(self) -> str:
220
+ return check_not_none(
221
+ self.GNOSIS_RPC_URL, "GNOSIS_RPC_URL missing in the environment."
222
+ )
223
+
224
+ @property
225
+ def chain_id(self) -> ChainID:
226
+ return check_not_none(self.CHAIN_ID, "CHAIN_ID missing in the environment.")
227
+
228
+
229
+ class CloudCredentials(BaseSettings):
230
+ model_config = SettingsConfigDict(
231
+ env_file=".env", env_file_encoding="utf-8", extra="ignore"
232
+ )
233
+
234
+ GOOGLE_APPLICATION_CREDENTIALS: t.Optional[str] = None
@@ -8,8 +8,7 @@ from datetime import timedelta
8
8
  from enum import Enum
9
9
  from functools import cached_property
10
10
 
11
- from pydantic import BeforeValidator, computed_field
12
- from typing_extensions import Annotated
11
+ from pydantic import computed_field
13
12
 
14
13
  from prediction_market_agent_tooling.config import APIKeys
15
14
  from prediction_market_agent_tooling.deploy.betting_strategy import (
@@ -59,33 +58,16 @@ from prediction_market_agent_tooling.markets.omen.omen import (
59
58
  from prediction_market_agent_tooling.monitor.monitor_app import (
60
59
  MARKET_TYPE_TO_DEPLOYED_AGENT,
61
60
  )
61
+ from prediction_market_agent_tooling.tools.custom_exceptions import (
62
+ CantPayForGasError,
63
+ OutOfFundsError,
64
+ )
62
65
  from prediction_market_agent_tooling.tools.is_invalid import is_invalid
63
66
  from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
64
67
  from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
65
68
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, utcnow
66
69
 
67
70
  MAX_AVAILABLE_MARKETS = 20
68
- TRADER_TAG = "trader"
69
-
70
-
71
- def to_boolean_outcome(value: str | bool) -> bool:
72
- if isinstance(value, bool):
73
- return value
74
-
75
- elif isinstance(value, str):
76
- value = value.lower().strip()
77
-
78
- if value in {"true", "yes", "y", "1"}:
79
- return True
80
-
81
- elif value in {"false", "no", "n", "0"}:
82
- return False
83
-
84
- else:
85
- raise ValueError(f"Expected a boolean string, but got {value}")
86
-
87
- else:
88
- raise ValueError(f"Expected a boolean or a string, but got {value}")
89
71
 
90
72
 
91
73
  def initialize_langfuse(enable_langfuse: bool) -> None:
@@ -103,23 +85,21 @@ def initialize_langfuse(enable_langfuse: bool) -> None:
103
85
  langfuse_context.configure(enabled=enable_langfuse)
104
86
 
105
87
 
106
- Decision = Annotated[bool, BeforeValidator(to_boolean_outcome)]
107
-
108
-
109
- class CantPayForGasError(ValueError):
110
- pass
111
-
112
-
113
- class OutOfFundsError(ValueError):
114
- pass
115
-
116
-
117
88
  class AnsweredEnum(str, Enum):
118
89
  ANSWERED = "answered"
119
90
  NOT_ANSWERED = "not_answered"
120
91
 
121
92
 
93
+ class AgentTagEnum(str, Enum):
94
+ PREDICTOR = "predictor"
95
+ TRADER = "trader"
96
+
97
+
122
98
  class DeployableAgent:
99
+ """
100
+ Subclass this class to create agent with standardized interface.
101
+ """
102
+
123
103
  def __init__(
124
104
  self,
125
105
  enable_langfuse: bool = APIKeys().default_enable_langfuse,
@@ -180,20 +160,25 @@ class DeployableAgent:
180
160
  )
181
161
 
182
162
  def load(self) -> None:
183
- pass
163
+ """
164
+ Implement this method to load arbitrary instances needed across the whole run of the agent.
165
+
166
+ Do not customize __init__ method.
167
+ """
184
168
 
185
169
  def deploy_local(
186
170
  self,
187
171
  market_type: MarketType,
188
172
  sleep_time: float,
189
- timeout: float,
173
+ run_time: float | None,
190
174
  ) -> None:
175
+ """
176
+ Run the agent in the forever cycle every `sleep_time` seconds, until the `run_time` is met.
177
+ """
191
178
  start_time = time.time()
192
- while True:
179
+ while run_time is None or time.time() - start_time < run_time:
193
180
  self.run(market_type=market_type)
194
181
  time.sleep(sleep_time)
195
- if time.time() - start_time > timeout:
196
- break
197
182
 
198
183
  def deploy_gcp(
199
184
  self,
@@ -209,6 +194,9 @@ class DeployableAgent:
209
194
  start_time: DatetimeUTC | None = None,
210
195
  timeout: int = 180,
211
196
  ) -> None:
197
+ """
198
+ Deploy the agent as GCP Function.
199
+ """
212
200
  path_to_agent_file = os.path.relpath(inspect.getfile(self.__class__))
213
201
 
214
202
  entrypoint_function_name = "main"
@@ -275,6 +263,9 @@ def {entrypoint_function_name}(request) -> str:
275
263
  schedule_deployed_gcp_function(fname, cron_schedule=cron_schedule)
276
264
 
277
265
  def run(self, market_type: MarketType) -> None:
266
+ """
267
+ Run single iteration of the agent.
268
+ """
278
269
  raise NotImplementedError("This method must be implemented by the subclass.")
279
270
 
280
271
  def get_gcloud_fname(self, market_type: MarketType) -> str:
@@ -282,6 +273,14 @@ def {entrypoint_function_name}(request) -> str:
282
273
 
283
274
 
284
275
  class DeployablePredictionAgent(DeployableAgent):
276
+ """
277
+ Subclass this class to create your own prediction market agent.
278
+
279
+ The agent will process markets and make predictions.
280
+ """
281
+
282
+ AGENT_TAG: AgentTagEnum = AgentTagEnum.PREDICTOR
283
+
285
284
  bet_on_n_markets_per_run: int = 1
286
285
 
287
286
  # Agent behaviour when fetching markets
@@ -301,10 +300,10 @@ class DeployablePredictionAgent(DeployableAgent):
301
300
  def __init__(
302
301
  self,
303
302
  enable_langfuse: bool = APIKeys().default_enable_langfuse,
304
- store_prediction: bool = True,
303
+ store_predictions: bool = True,
305
304
  ) -> None:
306
305
  super().__init__(enable_langfuse=enable_langfuse)
307
- self.store_prediction = store_prediction
306
+ self.store_predictions = store_predictions
308
307
 
309
308
  def initialize_langfuse(self) -> None:
310
309
  super().initialize_langfuse()
@@ -332,7 +331,7 @@ class DeployablePredictionAgent(DeployableAgent):
332
331
  ) -> None:
333
332
  self.langfuse_update_current_trace(
334
333
  tags=[
335
- TRADER_TAG,
334
+ self.AGENT_TAG,
336
335
  (
337
336
  AnsweredEnum.ANSWERED
338
337
  if processed_market is not None
@@ -390,6 +389,9 @@ class DeployablePredictionAgent(DeployableAgent):
390
389
  self,
391
390
  market_type: MarketType,
392
391
  ) -> t.Sequence[AgentMarket]:
392
+ """
393
+ Override this method to customize what markets will fetch for processing.
394
+ """
393
395
  cls = market_type.market_class
394
396
  # Fetch the soonest closing markets to choose from
395
397
  available_markets = cls.get_binary_markets(
@@ -403,6 +405,9 @@ class DeployablePredictionAgent(DeployableAgent):
403
405
  def before_process_market(
404
406
  self, market_type: MarketType, market: AgentMarket
405
407
  ) -> None:
408
+ """
409
+ Executed before processing of each market.
410
+ """
406
411
  api_keys = APIKeys()
407
412
 
408
413
  if market_type.is_blockchain_market:
@@ -446,19 +451,22 @@ class DeployablePredictionAgent(DeployableAgent):
446
451
  market: AgentMarket,
447
452
  processed_market: ProcessedMarket | None,
448
453
  ) -> None:
454
+ """
455
+ Executed after processing of each market.
456
+ """
449
457
  keys = APIKeys()
450
- if self.store_prediction:
458
+ if self.store_predictions:
451
459
  market.store_prediction(
452
460
  processed_market=processed_market, keys=keys, agent_name=self.agent_name
453
461
  )
454
462
  else:
455
463
  logger.info(
456
- f"Prediction {processed_market} not stored because {self.store_prediction=}."
464
+ f"Prediction {processed_market} not stored because {self.store_predictions=}."
457
465
  )
458
466
 
459
467
  def before_process_markets(self, market_type: MarketType) -> None:
460
468
  """
461
- Executes actions that occur before bets are placed.
469
+ Executed before market processing loop starts.
462
470
  """
463
471
  api_keys = APIKeys()
464
472
  self.check_min_required_balance_to_operate(market_type)
@@ -489,7 +497,9 @@ class DeployablePredictionAgent(DeployableAgent):
489
497
  logger.info("All markets processed.")
490
498
 
491
499
  def after_process_markets(self, market_type: MarketType) -> None:
492
- "Executes actions that occur after bets are placed."
500
+ """
501
+ Executed after market processing loop ends.
502
+ """
493
503
 
494
504
  def run(self, market_type: MarketType) -> None:
495
505
  if market_type not in self.supported_markets:
@@ -502,6 +512,14 @@ class DeployablePredictionAgent(DeployableAgent):
502
512
 
503
513
 
504
514
  class DeployableTraderAgent(DeployablePredictionAgent):
515
+ """
516
+ Subclass this class to create your own prediction market trading agent.
517
+
518
+ The agent will process markets, make predictions and place trades (bets) based off these predictions.
519
+ """
520
+
521
+ AGENT_TAG: AgentTagEnum = AgentTagEnum.TRADER
522
+
505
523
  # These markets require place of bet, not just predictions.
506
524
  supported_markets: t.Sequence[MarketType] = [
507
525
  MarketType.OMEN,
@@ -512,12 +530,12 @@ class DeployableTraderAgent(DeployablePredictionAgent):
512
530
  def __init__(
513
531
  self,
514
532
  enable_langfuse: bool = APIKeys().default_enable_langfuse,
515
- store_prediction: bool = True,
533
+ store_predictions: bool = True,
516
534
  store_trades: bool = True,
517
535
  place_trades: bool = True,
518
536
  ) -> None:
519
537
  super().__init__(
520
- enable_langfuse=enable_langfuse, store_prediction=store_prediction
538
+ enable_langfuse=enable_langfuse, store_predictions=store_predictions
521
539
  )
522
540
  self.store_trades = store_trades
523
541
  self.place_trades = place_trades
@@ -541,6 +559,11 @@ class DeployableTraderAgent(DeployablePredictionAgent):
541
559
  )
542
560
 
543
561
  def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
562
+ """
563
+ Override this method to customize betting strategy of your agent.
564
+
565
+ Given the market and prediction, agent uses this method to calculate optimal outcome and bet size.
566
+ """
544
567
  user_id = market.get_user_id(api_keys=APIKeys())
545
568
 
546
569
  total_amount = market.get_tiny_bet_amount().amount
@@ -140,3 +140,24 @@ class PlacedTrade(Trade):
140
140
  amount=trade.amount,
141
141
  id=id,
142
142
  )
143
+
144
+
145
+ class SimulationDetail(BaseModel):
146
+ strategy: str
147
+ url: str
148
+ market_p_yes: float
149
+ agent_p_yes: float
150
+ agent_conf: float
151
+ org_bet: float
152
+ sim_bet: float
153
+ org_dir: bool
154
+ sim_dir: bool
155
+ org_profit: float
156
+ sim_profit: float
157
+ timestamp: DatetimeUTC
158
+
159
+
160
+ class SharpeOutput(BaseModel):
161
+ annualized_volatility: float
162
+ mean_daily_return: float
163
+ annualized_sharpe_ratio: float
@@ -200,7 +200,6 @@ class ManifoldBet(BaseModel):
200
200
  if self.get_resolved_boolean_outcome() == market_outcome
201
201
  else -self.amount
202
202
  )
203
- profit -= self.fees.get_total()
204
203
  return ProfitAmount(
205
204
  amount=profit,
206
205
  currency=Currency.Mana,
@@ -531,7 +531,6 @@ class OmenBet(BaseModel):
531
531
  if self.boolean_outcome == self.fpmm.boolean_outcome
532
532
  else -bet_amount_xdai
533
533
  )
534
- profit -= wei_to_xdai(self.feeAmount)
535
534
  return ProfitAmount(
536
535
  amount=profit,
537
536
  currency=Currency.xDai,
@@ -539,9 +538,8 @@ class OmenBet(BaseModel):
539
538
 
540
539
  def to_bet(self) -> Bet:
541
540
  return Bet(
542
- id=str(
543
- self.transactionHash
544
- ), # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
541
+ id=str(self.transactionHash),
542
+ # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
545
543
  amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
546
544
  outcome=self.boolean_outcome,
547
545
  created_time=self.creation_datetime,
@@ -556,9 +554,8 @@ class OmenBet(BaseModel):
556
554
  )
557
555
 
558
556
  return ResolvedBet(
559
- id=str(
560
- self.transactionHash
561
- ), # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
557
+ id=self.transactionHash.hex(),
558
+ # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
562
559
  amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
563
560
  outcome=self.boolean_outcome,
564
561
  created_time=self.creation_datetime,
@@ -75,6 +75,7 @@ from prediction_market_agent_tooling.tools.contract import (
75
75
  init_collateral_token_contract,
76
76
  to_gnosis_chain_contract,
77
77
  )
78
+ from prediction_market_agent_tooling.tools.custom_exceptions import OutOfFundsError
78
79
  from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
79
80
  from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
80
81
  from prediction_market_agent_tooling.tools.utils import (
@@ -1339,7 +1340,7 @@ def withdraw_wxdai_to_xdai_to_keep_balance(
1339
1340
  )
1340
1341
 
1341
1342
  if current_balances.wxdai < need_to_withdraw:
1342
- raise ValueError(
1343
+ raise OutOfFundsError(
1343
1344
  f"Current wxDai balance {current_balances.wxdai} is less than the required minimum wxDai to withdraw {need_to_withdraw}."
1344
1345
  )
1345
1346
 
@@ -435,10 +435,14 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
435
435
  omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket)
436
436
  return omen_markets
437
437
 
438
- def get_omen_market_by_market_id(self, market_id: HexAddress) -> OmenMarket:
439
- markets = self.trades_subgraph.Query.fixedProductMarketMaker(
440
- id=market_id.lower()
441
- )
438
+ def get_omen_market_by_market_id(
439
+ self, market_id: HexAddress, block_number: int | None = None
440
+ ) -> OmenMarket:
441
+ query_filters: dict[str, t.Any] = {"id": market_id.lower()}
442
+ if block_number:
443
+ query_filters["block"] = {"number": block_number}
444
+
445
+ markets = self.trades_subgraph.Query.fixedProductMarketMaker(**query_filters)
442
446
 
443
447
  fields = self._get_fields_for_markets(markets)
444
448
  omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket)
@@ -3,7 +3,7 @@ from prediction_market_agent_tooling.markets.markets import MarketType
3
3
  from prediction_market_agent_tooling.markets.polymarket.data_models_web import (
4
4
  PolymarketFullMarket,
5
5
  )
6
- from prediction_market_agent_tooling.tools.google import search_google
6
+ from prediction_market_agent_tooling.tools.google_utils import search_google
7
7
 
8
8
 
9
9
  def find_resolution_on_polymarket(question: str) -> Resolution | None:
@@ -0,0 +1,66 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ from prediction_market_agent_tooling.markets.data_models import (
5
+ SharpeOutput,
6
+ SimulationDetail,
7
+ )
8
+
9
+
10
+ class SharpeRatioCalculator:
11
+ def __init__(
12
+ self, details: list[SimulationDetail], risk_free_rate: float = 0.0
13
+ ) -> None:
14
+ self.details = details
15
+ self.df = pd.DataFrame([d.model_dump() for d in self.details])
16
+ self.risk_free_rate = risk_free_rate
17
+
18
+ def __has_df_valid_columns_else_exception(
19
+ self, required_columns: list[str]
20
+ ) -> None:
21
+ if not set(required_columns).issubset(self.df.columns):
22
+ raise ValueError("Dataframe doesn't contain all the required columns.")
23
+
24
+ def prepare_wallet_daily_balance_df(
25
+ self, timestamp_col_name: str, profit_col_name: str
26
+ ) -> pd.DataFrame:
27
+ self.__has_df_valid_columns_else_exception(
28
+ [timestamp_col_name, profit_col_name]
29
+ )
30
+ df = self.df.copy()
31
+ df[timestamp_col_name] = pd.to_datetime(df[timestamp_col_name])
32
+ df.sort_values(timestamp_col_name, ascending=True, inplace=True)
33
+
34
+ df["profit_cumsum"] = df[profit_col_name].cumsum()
35
+ df["profit_cumsum"] = df["profit_cumsum"] + 50
36
+
37
+ df = df.drop_duplicates(subset=timestamp_col_name, keep="last")
38
+ df.set_index(timestamp_col_name, inplace=True)
39
+ # We generate a new Dataframe with daily wallet balances, derived by the final wallet balance
40
+ # from the previous day.
41
+ wallet_balance_daily_df = df[["profit_cumsum"]].resample("D").ffill()
42
+ wallet_balance_daily_df.dropna(inplace=True)
43
+ wallet_balance_daily_df["returns"] = wallet_balance_daily_df[
44
+ "profit_cumsum"
45
+ ].pct_change()
46
+ return wallet_balance_daily_df
47
+
48
+ def calculate_annual_sharpe_ratio(
49
+ self, timestamp_col_name: str = "timestamp", profit_col_name: str = "sim_profit"
50
+ ) -> SharpeOutput:
51
+ wallet_daily_balance_df = self.prepare_wallet_daily_balance_df(
52
+ timestamp_col_name=timestamp_col_name, profit_col_name=profit_col_name
53
+ )
54
+
55
+ daily_volatility = wallet_daily_balance_df["returns"].std()
56
+ annualized_volatility = daily_volatility * np.sqrt(365)
57
+ mean_daily_return = wallet_daily_balance_df["returns"].mean()
58
+ daily_sharpe_ratio = (
59
+ mean_daily_return - self.risk_free_rate
60
+ ) / daily_volatility
61
+ annualized_sharpe_ratio = daily_sharpe_ratio * np.sqrt(365)
62
+ return SharpeOutput(
63
+ annualized_volatility=annualized_volatility,
64
+ mean_daily_return=mean_daily_return,
65
+ annualized_sharpe_ratio=annualized_sharpe_ratio,
66
+ )
@@ -22,7 +22,6 @@ from sqlmodel import Field, Session, SQLModel, create_engine, desc, select
22
22
  from prediction_market_agent_tooling.config import APIKeys
23
23
  from prediction_market_agent_tooling.loggers import logger
24
24
  from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
25
- from prediction_market_agent_tooling.tools.pickle_utils import InitialiseNonPickable
26
25
  from prediction_market_agent_tooling.tools.utils import utcnow
27
26
 
28
27
  FunctionT = TypeVar("FunctionT", bound=Callable[..., Any])
@@ -91,17 +90,6 @@ def db_cache(
91
90
  return decorator
92
91
 
93
92
  api_keys = api_keys if api_keys is not None else APIKeys()
94
- wrapped_engine = InitialiseNonPickable(
95
- lambda: create_engine(
96
- api_keys.sqlalchemy_db_url.get_secret_value(),
97
- # Use custom json serializer and deserializer, because otherwise, for example `datetime` serialization would fail.
98
- json_serializer=json_serializer,
99
- json_deserializer=json_deserializer,
100
- )
101
- )
102
-
103
- if api_keys.ENABLE_CACHE:
104
- SQLModel.metadata.create_all(wrapped_engine.get_value())
105
93
 
106
94
  @wraps(func)
107
95
  def wrapper(*args: Any, **kwargs: Any) -> Any:
@@ -109,7 +97,16 @@ def db_cache(
109
97
  if not api_keys.ENABLE_CACHE:
110
98
  return func(*args, **kwargs)
111
99
 
112
- engine = wrapped_engine.get_value()
100
+ engine = create_engine(
101
+ api_keys.sqlalchemy_db_url.get_secret_value(),
102
+ # Use custom json serializer and deserializer, because otherwise, for example `datetime` serialization would fail.
103
+ json_serializer=json_serializer,
104
+ json_deserializer=json_deserializer,
105
+ pool_size=1,
106
+ )
107
+
108
+ # Create table if it doesn't exist
109
+ SQLModel.metadata.create_all(engine)
113
110
 
114
111
  # Convert *args and **kwargs to a single dictionary, where we have names for arguments passed as args as well.
115
112
  signature = inspect.signature(func)
@@ -213,6 +210,7 @@ def db_cache(
213
210
  session.add(cache_entry)
214
211
  session.commit()
215
212
 
213
+ engine.dispose()
216
214
  return computed_result
217
215
 
218
216
  return cast(FunctionT, wrapper)
@@ -8,7 +8,7 @@ from pydantic import BaseModel, field_validator
8
8
  from web3 import Web3
9
9
  from web3.contract.contract import Contract as Web3Contract
10
10
 
11
- from prediction_market_agent_tooling.config import APIKeys
11
+ from prediction_market_agent_tooling.config import APIKeys, RPCConfig
12
12
  from prediction_market_agent_tooling.gtypes import (
13
13
  ABI,
14
14
  ChainID,
@@ -18,10 +18,6 @@ from prediction_market_agent_tooling.gtypes import (
18
18
  TxReceipt,
19
19
  Wei,
20
20
  )
21
- from prediction_market_agent_tooling.tools.gnosis_rpc import (
22
- GNOSIS_NETWORK_ID,
23
- GNOSIS_RPC_URL,
24
- )
25
21
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
26
22
  from prediction_market_agent_tooling.tools.web3_utils import (
27
23
  call_function_on_contract,
@@ -391,8 +387,8 @@ class ContractOnGnosisChain(ContractBaseClass):
391
387
  Contract base class with Gnosis Chain configuration.
392
388
  """
393
389
 
394
- CHAIN_ID = GNOSIS_NETWORK_ID
395
- CHAIN_RPC_URL = GNOSIS_RPC_URL
390
+ CHAIN_ID = RPCConfig().chain_id
391
+ CHAIN_RPC_URL = RPCConfig().gnosis_rpc_url
396
392
 
397
393
 
398
394
  class ContractProxyOnGnosisChain(ContractProxyBaseClass, ContractOnGnosisChain):
@@ -0,0 +1,6 @@
1
+ class CantPayForGasError(ValueError):
2
+ pass
3
+
4
+
5
+ class OutOfFundsError(ValueError):
6
+ pass
@@ -1,10 +1,14 @@
1
+ import json
1
2
  import typing as t
2
3
  from datetime import timedelta
3
4
 
4
5
  import tenacity
6
+ from google.cloud import secretmanager
5
7
  from googleapiclient.discovery import build
8
+ from pydantic import SecretStr
6
9
 
7
- from prediction_market_agent_tooling.config import APIKeys
10
+ from prediction_market_agent_tooling.config import APIKeys, CloudCredentials
11
+ from prediction_market_agent_tooling.gtypes import PrivateKey
8
12
  from prediction_market_agent_tooling.loggers import logger
9
13
  from prediction_market_agent_tooling.tools.caches.db_cache import db_cache
10
14
 
@@ -51,3 +55,26 @@ def search_google(
51
55
  )
52
56
  except KeyError as e:
53
57
  raise ValueError(f"Can not parse results: {search}") from e
58
+
59
+
60
+ def get_private_key_from_gcp_secret(
61
+ secret_id: str,
62
+ project_id: str = "582587111398", # Gnosis AI default project_id
63
+ version_id: str = "latest",
64
+ ) -> PrivateKey:
65
+ # If credentials filename specified, use that, else read using default credentials path.
66
+ google_application_credentials_filename = (
67
+ CloudCredentials().GOOGLE_APPLICATION_CREDENTIALS
68
+ )
69
+ if google_application_credentials_filename is not None:
70
+ # mypy interprets incorrectly that from_service_account_json requires further args.
71
+ client = secretmanager.SecretManagerServiceClient.from_service_account_json(filename=google_application_credentials_filename) # type: ignore [call-arg]
72
+ else:
73
+ client = secretmanager.SecretManagerServiceClient()
74
+ name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
75
+ response = client.access_secret_version(request={"name": name})
76
+ secret_payload = response.payload.data.decode("UTF-8")
77
+ secret_json = json.loads(secret_payload)
78
+ if "private_key" not in secret_json:
79
+ raise ValueError(f"Private key not found in gcp secret {secret_id}")
80
+ return PrivateKey(SecretStr(secret_json["private_key"]))
@@ -17,7 +17,7 @@ def rewrite_question_into_image_generation_prompt(question: str) -> str:
17
17
  "openai not installed, please install extras `langchain` to use this function."
18
18
  )
19
19
  llm = ChatOpenAI(
20
- model="gpt-4-turbo",
20
+ model="gpt-4o-2024-08-06",
21
21
  temperature=0.0,
22
22
  api_key=APIKeys().openai_api_key_secretstr_v1,
23
23
  )
@@ -60,7 +60,7 @@ Finally, write your final decision, write `decision: ` followed by either "yes i
60
60
  @db_cache
61
61
  def is_invalid(
62
62
  question: str,
63
- engine: str = "gpt-4o",
63
+ engine: str = "gpt-4o-2024-08-06",
64
64
  temperature: float = LLM_SUPER_LOW_TEMPERATURE,
65
65
  seed: int = LLM_SEED,
66
66
  prompt_template: str = QUESTION_IS_INVALID_PROMPT,
@@ -7,7 +7,10 @@ from prediction_market_agent_tooling.tools.langfuse_ import (
7
7
  get_langfuse_langchain_config,
8
8
  observe,
9
9
  )
10
- from prediction_market_agent_tooling.tools.utils import LLM_SUPER_LOW_TEMPERATURE
10
+ from prediction_market_agent_tooling.tools.utils import (
11
+ LLM_SEED,
12
+ LLM_SUPER_LOW_TEMPERATURE,
13
+ )
11
14
 
12
15
  # I tried to make it return a JSON, but it didn't work well in combo with asking it to do chain of thought.
13
16
  QUESTION_IS_PREDICTABLE_BINARY_PROMPT = """Main signs about a fully qualified question (sometimes referred to as a "market"):
@@ -81,7 +84,7 @@ Finally, write your final decision, write `decision: ` followed by either "yes i
81
84
  @db_cache
82
85
  def is_predictable_binary(
83
86
  question: str,
84
- engine: str = "gpt-4-1106-preview",
87
+ engine: str = "gpt-4o-2024-08-06",
85
88
  prompt_template: str = QUESTION_IS_PREDICTABLE_BINARY_PROMPT,
86
89
  max_tokens: int = 1024,
87
90
  ) -> bool:
@@ -98,6 +101,7 @@ def is_predictable_binary(
98
101
  llm = ChatOpenAI(
99
102
  model=engine,
100
103
  temperature=LLM_SUPER_LOW_TEMPERATURE,
104
+ seed=LLM_SEED,
101
105
  api_key=APIKeys().openai_api_key_secretstr_v1,
102
106
  )
103
107
 
@@ -118,7 +122,7 @@ def is_predictable_binary(
118
122
  def is_predictable_without_description(
119
123
  question: str,
120
124
  description: str,
121
- engine: str = "gpt-4-1106-preview",
125
+ engine: str = "gpt-4o-2024-08-06",
122
126
  prompt_template: str = QUESTION_IS_PREDICTABLE_WITHOUT_DESCRIPTION_PROMPT,
123
127
  max_tokens: int = 1024,
124
128
  ) -> bool:
@@ -137,6 +141,7 @@ def is_predictable_without_description(
137
141
  llm = ChatOpenAI(
138
142
  model=engine,
139
143
  temperature=LLM_SUPER_LOW_TEMPERATURE,
144
+ seed=LLM_SEED,
140
145
  api_key=APIKeys().openai_api_key_secretstr_v1,
141
146
  )
142
147
 
@@ -70,6 +70,7 @@ def get_traces_for_agent(
70
70
  page = 1 # index starts from 1
71
71
  all_agent_traces = []
72
72
  while True:
73
+ logger.debug(f"fetching page {page}")
73
74
  traces = client.fetch_traces(
74
75
  name=trace_name,
75
76
  limit=100,
@@ -25,9 +25,12 @@ class RelevantNewsCacheModel(SQLModel, table=True):
25
25
  class RelevantNewsResponseCache:
26
26
  def __init__(self, sqlalchemy_db_url: str | None = None):
27
27
  self.engine = create_engine(
28
- sqlalchemy_db_url
29
- if sqlalchemy_db_url
30
- else APIKeys().sqlalchemy_db_url.get_secret_value()
28
+ (
29
+ sqlalchemy_db_url
30
+ if sqlalchemy_db_url
31
+ else APIKeys().sqlalchemy_db_url.get_secret_value()
32
+ ),
33
+ pool_size=1,
31
34
  )
32
35
  self._initialize_db()
33
36
 
@@ -0,0 +1,48 @@
1
+ import diskcache as dc
2
+ import tenacity
3
+ from eth_typing import HexStr
4
+ from tenacity import wait_exponential
5
+ from web3 import Web3
6
+
7
+ from prediction_market_agent_tooling.loggers import logger
8
+
9
+
10
+ class TransactionBlockCache:
11
+ def __init__(self, web3: Web3):
12
+ self.block_number_cache = dc.Cache("block_cache_dir")
13
+ self.block_timestamp_cache = dc.Cache("timestamp_cache_dir")
14
+ self.web3 = web3
15
+
16
+ @tenacity.retry(
17
+ wait=wait_exponential(multiplier=1, min=1, max=4),
18
+ stop=tenacity.stop_after_attempt(7),
19
+ after=lambda x: logger.debug(f"fetch tx failed, {x.attempt_number=}."),
20
+ )
21
+ def fetch_block_number(self, transaction_hash: str) -> int:
22
+ tx = self.web3.eth.get_transaction(HexStr(transaction_hash))
23
+ return tx["blockNumber"]
24
+
25
+ @tenacity.retry(
26
+ wait=wait_exponential(multiplier=1, min=1, max=4),
27
+ stop=tenacity.stop_after_attempt(7),
28
+ after=lambda x: logger.debug(f"fetch tx failed, {x.attempt_number=}."),
29
+ )
30
+ def fetch_block_timestamp(self, block_number: int) -> int:
31
+ block = self.web3.eth.get_block(block_number)
32
+ return block["timestamp"]
33
+
34
+ def get_block_number(self, tx_hash: str) -> int:
35
+ if tx_hash in self.block_number_cache:
36
+ return int(self.block_number_cache[tx_hash])
37
+
38
+ block_number = self.fetch_block_number(tx_hash)
39
+ self.block_number_cache[tx_hash] = block_number
40
+ return block_number
41
+
42
+ def get_block_timestamp(self, block_number: int) -> int:
43
+ if block_number in self.block_timestamp_cache:
44
+ return int(self.block_timestamp_cache[block_number])
45
+
46
+ block_timestamp = self.fetch_block_timestamp(block_number)
47
+ self.block_timestamp_cache[block_number] = block_timestamp
48
+ return block_timestamp
@@ -1,4 +1,3 @@
1
- import json
2
1
  import os
3
2
  import subprocess
4
3
  from datetime import datetime
@@ -6,17 +5,11 @@ from typing import Any, NoReturn, Optional, Type, TypeVar
6
5
 
7
6
  import pytz
8
7
  import requests
9
- from google.cloud import secretmanager
10
8
  from pydantic import BaseModel, ValidationError
11
9
  from scipy.optimize import newton
12
10
  from scipy.stats import entropy
13
11
 
14
- from prediction_market_agent_tooling.gtypes import (
15
- DatetimeUTC,
16
- PrivateKey,
17
- Probability,
18
- SecretStr,
19
- )
12
+ from prediction_market_agent_tooling.gtypes import DatetimeUTC, Probability, SecretStr
20
13
  from prediction_market_agent_tooling.loggers import logger
21
14
  from prediction_market_agent_tooling.markets.market_fees import MarketFees
22
15
 
@@ -214,18 +207,3 @@ def calculate_sell_amount_in_collateral(
214
207
 
215
208
  amount_to_sell = newton(f, 0)
216
209
  return float(amount_to_sell) * 0.999999 # Avoid rounding errors
217
-
218
-
219
- def get_private_key_from_gcp_secret(
220
- secret_id: str,
221
- project_id: str = "582587111398", # Gnosis AI default project_id
222
- version_id: str = "latest",
223
- ) -> PrivateKey:
224
- client = secretmanager.SecretManagerServiceClient()
225
- name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
226
- response = client.access_secret_version(request={"name": name})
227
- secret_payload = response.payload.data.decode("UTF-8")
228
- secret_json = json.loads(secret_payload)
229
- if "private_key" not in secret_json:
230
- raise ValueError(f"Private key not found in gcp secret {secret_id}")
231
- return PrivateKey(SecretStr(secret_json["private_key"]))
@@ -263,7 +263,11 @@ def send_function_on_contract_tx_using_safe(
263
263
  )
264
264
  safe_tx.sign(from_private_key.get_secret_value())
265
265
  safe_tx.call() # simulate call
266
- tx_hash, tx = safe_tx.execute(from_private_key.get_secret_value())
266
+ eoa_nonce = web3.eth.get_transaction_count(eoa_public_key)
267
+ tx_hash, tx = safe_tx.execute(
268
+ from_private_key.get_secret_value(),
269
+ tx_nonce=eoa_nonce,
270
+ )
267
271
  receipt_tx = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout)
268
272
  check_tx_receipt(receipt_tx)
269
273
  return receipt_tx
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.56.3.dev135
3
+ Version: 0.57.0
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -16,8 +16,8 @@ prediction_market_agent_tooling/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-
16
16
  prediction_market_agent_tooling/benchmark/agents.py,sha256=B1-uWdyeN4GGKMWGK_-CcAFJg1m9Y_XuaeIHPB29QR8,3971
17
17
  prediction_market_agent_tooling/benchmark/benchmark.py,sha256=MqTiaaJ3cYiOLUVR7OyImLWxcEya3Rl5JyFYW-K0lwM,17097
18
18
  prediction_market_agent_tooling/benchmark/utils.py,sha256=D0MfUkVZllmvcU0VOurk9tcKT7JTtwwOp-63zuCBVuc,2880
19
- prediction_market_agent_tooling/config.py,sha256=6iB8A4byexiemUajIw2SzRDQ8E8w0catyg7aH_mHpsY,6732
20
- prediction_market_agent_tooling/deploy/agent.py,sha256=dpc94DUo8Gq1LdRdw6k78vm_47OeJIfomG9CRVpgzk0,22757
19
+ prediction_market_agent_tooling/config.py,sha256=13qhBE68CZTnHYVDUxGs4yj_d75WM8QqJVg0a-wAoWA,7559
20
+ prediction_market_agent_tooling/deploy/agent.py,sha256=58Ms6wFcRM3yhpdDVT-ohryZhHJNinn1Z4MdrmCMXvM,23627
21
21
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
22
22
  prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=kMrIE3wMv_IB6nJd_1DmDXDkEZhsXFOgyTd7JZ0gqHI,13068
23
23
  prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
@@ -33,10 +33,10 @@ prediction_market_agent_tooling/loggers.py,sha256=Am6HHXRNO545BO3l7Ue9Wb2TkYE1OK
33
33
  prediction_market_agent_tooling/markets/agent_market.py,sha256=W2ME57-CSAhrt8qm8-b5r7yLq-Sk7R_BZMaApvjhrUE,12901
34
34
  prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=IxDTwX4tej9j5olNkXcLIE0RCF1Nh2icZQUT2ERMmZo,1937
35
35
  prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
36
- prediction_market_agent_tooling/markets/data_models.py,sha256=jMqrSFO_w2z-5N3PFVgZqTHdVdkzSDhhzky2lHsGGKA,3621
36
+ prediction_market_agent_tooling/markets/data_models.py,sha256=VoEWm338qQMQIRvWpWpDgDjVk9D_3tCHrZPd0Gy0dAA,4033
37
37
  prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  prediction_market_agent_tooling/markets/manifold/api.py,sha256=Fd0HYnstvvHO6AZkp1xiRlvCwQUc8kLR8DAj6PAZu0s,7297
39
- prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=ylXIEHymx2RcCdOVUpKP4BcTqbzLu_Fd5gV1TGBntVk,6099
39
+ prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=eiGS4rEkxseZNpEb2BICKnjF0qqgkQTMuUPbSe7_04I,6059
40
40
  prediction_market_agent_tooling/markets/manifold/manifold.py,sha256=qemQIwuFg4yf6egGWFp9lWpz1lXr02QiBeZ2akcT6II,5026
41
41
  prediction_market_agent_tooling/markets/manifold/utils.py,sha256=cPPFWXm3vCYH1jy7_ctJZuQH9ZDaPL4_AgAYzGWkoow,513
42
42
  prediction_market_agent_tooling/markets/market_fees.py,sha256=Q64T9uaJx0Vllt0BkrPmpMEz53ra-hMVY8Czi7CEP7s,1227
@@ -45,18 +45,19 @@ prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42D
45
45
  prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=Suxa7xELdYuFNKqvGvFh8qyfVtAg79E-vaQ6dqNZOtA,3261
46
46
  prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=86TIx6cavEWc8Cv4KpZxSvwiSw9oFybXE3YB49pg-CA,4377
47
47
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- prediction_market_agent_tooling/markets/omen/data_models.py,sha256=0eky-RO0TuJysUXHd52A6DSU2mx1ZWiJvvntS4xQsUc,27794
49
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=uOuV2DgQmxz6kzPcMovyGg0xYS0c6x12fEFNtLmN-uY,51260
48
+ prediction_market_agent_tooling/markets/omen/data_models.py,sha256=RM5EigkxBArGRAyolgqqHsdIap-FT3FfbvtlWZJwo10,27711
49
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=6zo8iBBLdI9g9keJ1dw9PXs4rf4V4tCW8D4e08f3g0Q,51349
50
50
  prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=Zq7SncCq-hvpgXKsVruGBGCn1OhKZTe7r1qLdCTrT2w,28297
51
51
  prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=iDWdjICGkt968exwCjY-6nsnQyrrNAg3YjnDdP430GQ,9415
52
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=cXgtBfc5uv7d8598SJ537LxFGVfF_mv-VoQoqI4_G84,36330
52
+ prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=lG-2hCmmkjKHX1Ca-SipLBeETnTheXpjg5Qfed0tqFo,36506
53
53
  prediction_market_agent_tooling/markets/polymarket/api.py,sha256=UZ4_TG8ceb9Y-qgsOKs8Qiv8zDt957QkT8IX2c83yqo,4800
54
54
  prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=Fd5PI5y3mJM8VHExBhWFWEnuuIKxQmIAXgBuoPDvNjw,4341
55
55
  prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=VZhVccTApygSKMmy6Au2G02JCJOKJnR_oVeKlaesuSg,12548
56
56
  prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=NRoZK71PtH8kkangMqme7twcAXhRJSSabbmOir-UnAI,3418
57
- prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=m4JG6WULh5epCJt4XBMHg0ae5NoVhqlOvAl0A7DR9iM,2023
57
+ prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=DImFxeMg8lTfsEDZ8FavndW38TfUsCkawcVGnucsuGo,2029
58
58
  prediction_market_agent_tooling/markets/seer/data_models.py,sha256=2f6DOXhGerYbTSk5vUvw_y87TcUxOtSfca8-Et7imtU,643
59
59
  prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py,sha256=Jola8AxmNDljBCzl6Gvjb9qH75Y7D5dAwbdZl_jndN8,5478
60
+ prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py,sha256=fEiD-upH7vm5FWSND86r_qW0oV5ZonhaJEBxLK0OZAs,2672
60
61
  prediction_market_agent_tooling/monitor/markets/manifold.py,sha256=TS4ERwTfQnot8dhekNyVNhJYf5ysYsjF-9v5_kM3aVI,3334
61
62
  prediction_market_agent_tooling/monitor/markets/metaculus.py,sha256=LOnyWWBFdg10-cTWdb76nOsNjDloO8OfMT85GBzRCFI,1455
62
63
  prediction_market_agent_tooling/monitor/markets/omen.py,sha256=EqiJYTvDbSu7fBpbrBmCuf3fc6GHr4MxWrBGa69MIyc,3305
@@ -71,37 +72,37 @@ prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256
71
72
  prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py,sha256=-FUSuQQgjcWSSnoFxnlAyTeilY6raJABJVM2QKkFqAY,438
72
73
  prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py,sha256=THMXwFlskvzbjnX_OiYtDSzI8XVFyULWfP2525_9UGc,429
73
74
  prediction_market_agent_tooling/tools/betting_strategies/utils.py,sha256=kpIb-ci67Vc1Yqqaa-_S4OUkbhWSIYog4_Iwp69HU_k,97
74
- prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=x3v-ZnMpnSzmdwnzubh9oQ7ebExONXjWSFqY48CYQwc,12847
75
+ prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=yQ5sbccFf75jdj71zmlbJ8z5u2cfQzSRgY4haFG43I0,12712
75
76
  prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=tGHHd9HCiE_hCCtPtloHZQdDfBuiow9YsqJNYi2Tx_0,499
76
- prediction_market_agent_tooling/tools/contract.py,sha256=s3yo8IbXTcvAJcPfLM0_NbgaEsWwLsPmyVnOgyjq_xI,20919
77
+ prediction_market_agent_tooling/tools/contract.py,sha256=Um_nQlpYJRBy4MnNR1CPKESehVwx_w35LwYX6T6py_0,20837
77
78
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
79
+ prediction_market_agent_tooling/tools/custom_exceptions.py,sha256=Fh8z1fbwONvP4-j7AmV_PuEcoqb6-QXa9PJ9m7guMcM,93
78
80
  prediction_market_agent_tooling/tools/datetime_utc.py,sha256=2JSWF7AXQnv04_D_cu9Vmdkq0TWmGJ1QcK9AeqrA-U8,2765
79
- prediction_market_agent_tooling/tools/gnosis_rpc.py,sha256=ctBfB1os-MvZ1tm0Rwdyn9b3dvFnlM9naKvZmzywc3A,197
80
- prediction_market_agent_tooling/tools/google.py,sha256=jwXhu4lKfF0cuu02fMX-mGCRntRgiGQWkZ2CstaprK4,1828
81
+ prediction_market_agent_tooling/tools/google_utils.py,sha256=t3_UEEvKX3L0biSIQ560GdRbllQ6eprhK_upE243A-0,3185
81
82
  prediction_market_agent_tooling/tools/hexbytes_custom.py,sha256=Bp94qgPjvjWf1Vb4lNzGFDXRdThw1rJ91vL6r2PWq5E,2096
82
83
  prediction_market_agent_tooling/tools/httpx_cached_client.py,sha256=0-N1r0zcGKlY9Rk-Ab8hbqwc54eMbsoa3jXL0_yCCiM,355
83
84
  prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
84
- prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=8A3U2uxsCsOfLjru-6R_PPIAuiKY4qFkWp_GSBPV6-s,1280
85
+ prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=jgOow4zw6g8dIJrog6Tp-3NQs4wjUJ3VgVKyMQv-0QM,1286
85
86
  prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py,sha256=CTTMfTvs_8PH4kAtlQby2aeEKwgpmxtuGbd4oYIdJ2A,1201
86
- prediction_market_agent_tooling/tools/is_invalid.py,sha256=GSMwSWUZy-xviaFoIl0L34AVfLLTdh7zegjsTFE7_1M,5323
87
- prediction_market_agent_tooling/tools/is_predictable.py,sha256=VGkxSoJ8CSLknloOLzm5J4-us7XImYxVzvpsAzxbpCc,6730
87
+ prediction_market_agent_tooling/tools/is_invalid.py,sha256=vuX0TKKhc_7iVnwAjXvfZeFwjr9nyYgREaYQDe-GYgA,5334
88
+ prediction_market_agent_tooling/tools/is_predictable.py,sha256=dSuL8eLnGNKd_MQ3CaT1OuQkAykr3m5kn7f8HHKgDcM,6797
88
89
  prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
89
- prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=B0PhAQyviFnVbtOCYMxYmcCn66cu9nbqAOIAZcdgiRI,5771
90
+ prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=_m5SFnJJy4E0ubvY8ohzEEjES4nMJ_2zfa3wp4x3gSA,5817
90
91
  prediction_market_agent_tooling/tools/omen/reality_accuracy.py,sha256=M1SF7iSW1gVlQSTskdVFTn09uPLST23YeipVIWj54io,2236
91
92
  prediction_market_agent_tooling/tools/parallelism.py,sha256=6Gou0hbjtMZrYvxjTDFUDZuxmE2nqZVbb6hkg1hF82A,1022
92
- prediction_market_agent_tooling/tools/pickle_utils.py,sha256=PUNRJGdURUnLlzbZoSCiOrikRbEC596hi--Z3q8Wt84,1144
93
93
  prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py,sha256=95l84aztFaxcRLLcRQ46yKJbIlOEuDAbIGLouyliDzA,1316
94
94
  prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py,sha256=CddJem7tk15NAudJDwmuL8znTycbR-YI8kTNtd3LzG8,5474
95
- prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py,sha256=2yxtBIDyMT_6CsTpZyuIv_2dy2B9WgEOaTT1fSloBu0,3223
95
+ prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py,sha256=Dcde74ZwoZuohhJ8gIO5GQkdAz1YrAKlvrM8CNgRqWw,3289
96
96
  prediction_market_agent_tooling/tools/safe.py,sha256=9vxGGLvSPnfy-sxUFDpBTe8omqpGXP7MzvGPp6bRxrU,5197
97
97
  prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
98
98
  prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
99
99
  prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=5ldQs1pZe6uJ5eDAuP4OLpzmcqYShlIV67kttNFvGS0,342
100
100
  prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=Kw2mXNkMTYTEe1MBSTqhQmLoeXtgb6CkmHlcAJvhtqE,3809
101
- prediction_market_agent_tooling/tools/utils.py,sha256=1VvunbTmzGzpIlRukFhArreFNxJPbsg4lLtQNk0r2bY,7185
102
- prediction_market_agent_tooling/tools/web3_utils.py,sha256=44W8siSLNQxeib98bbwAe7V5C609NHNlUuxwuWIRDiY,11838
103
- prediction_market_agent_tooling-0.56.3.dev135.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
104
- prediction_market_agent_tooling-0.56.3.dev135.dist-info/METADATA,sha256=gjOGIMtO-vCZp9OV_wf43wWMkLN3fgmyT2zg3vElf8s,8113
105
- prediction_market_agent_tooling-0.56.3.dev135.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
106
- prediction_market_agent_tooling-0.56.3.dev135.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
107
- prediction_market_agent_tooling-0.56.3.dev135.dist-info/RECORD,,
101
+ prediction_market_agent_tooling/tools/transaction_cache.py,sha256=C96OHkNqPJr_yDiHesSRgekeXIbX62qFl9qldArleuo,1800
102
+ prediction_market_agent_tooling/tools/utils.py,sha256=WvuUCHgMCiMq8_wMm5PHNwvLhcdDk2zGKaAM8OUC-qY,6438
103
+ prediction_market_agent_tooling/tools/web3_utils.py,sha256=hWDa7D-jP-iW647t0ATPyiUgKbAq25iH97KUnha25SQ,11944
104
+ prediction_market_agent_tooling-0.57.0.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
105
+ prediction_market_agent_tooling-0.57.0.dist-info/METADATA,sha256=yK4VmpYuw1CWWmbpu7cLSWEltKzm-6BMi5vbtI2PTfM,8106
106
+ prediction_market_agent_tooling-0.57.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
107
+ prediction_market_agent_tooling-0.57.0.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
108
+ prediction_market_agent_tooling-0.57.0.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- import os
2
-
3
- from prediction_market_agent_tooling.gtypes import ChainID
4
-
5
- GNOSIS_NETWORK_ID = ChainID(100) # xDai network.
6
- GNOSIS_RPC_URL = os.getenv("GNOSIS_RPC_URL", "https://rpc.gnosischain.com")
@@ -1,31 +0,0 @@
1
- import typing as t
2
-
3
- InitialisedValue = t.TypeVar("InitialisedValue")
4
-
5
-
6
- class InitialiseNonPickable(t.Generic[InitialisedValue]):
7
- """
8
- Use this class to wrap values that you want to be shared within a thread,
9
- but they are re-initialised for a new processes.
10
-
11
- Initialiser for the value still needs to be pickable.
12
- """
13
-
14
- def __init__(self, initialiser: t.Callable[[], InitialisedValue]) -> None:
15
- self.value: InitialisedValue | None = None
16
- self.initialiser = initialiser
17
-
18
- def __getstate__(self) -> dict[str, t.Any]:
19
- # During pickling, always return `value` as just None, which is pickable and this class will re-initialise it in `get_value` when called.
20
- return {"value": None, "initialiser": self.initialiser}
21
-
22
- def __setstate__(self, d: dict[str, t.Any]) -> None:
23
- self.value = d["value"]
24
- self.initialiser = d["initialiser"]
25
-
26
- def get_value(self) -> InitialisedValue:
27
- """Use this function to get the wrapped value, which will be initialised if necessary."""
28
- if self.value is None:
29
- self.value = self.initialiser()
30
-
31
- return self.value