prediction-market-agent-tooling 0.54.0__py3-none-any.whl → 0.55.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.
@@ -36,6 +36,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
36
36
  AgentMarket,
37
37
  FilterBy,
38
38
  ProcessedMarket,
39
+ ProcessedTradedMarket,
39
40
  SortBy,
40
41
  )
41
42
  from prediction_market_agent_tooling.markets.data_models import (
@@ -165,9 +166,11 @@ class DeployableAgent:
165
166
  return f"{self.__class__.__name__} - {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}"
166
167
 
167
168
  def __init_subclass__(cls, **kwargs: t.Any) -> None:
168
- if "DeployableAgent" not in str(
169
- cls.__init__
170
- ) and "DeployableTraderAgent" not in str(cls.__init__):
169
+ if (
170
+ "DeployableAgent" not in str(cls.__init__)
171
+ and "DeployableTraderAgent" not in str(cls.__init__)
172
+ and "DeployablePredictionAgent" not in str(cls.__init__)
173
+ ):
171
174
  raise TypeError(
172
175
  "Cannot override __init__ method of deployable agent class, please override the `load` method to set up the agent."
173
176
  )
@@ -274,28 +277,21 @@ def {entrypoint_function_name}(request) -> str:
274
277
  return f"{self.__class__.__name__.lower()}-{market_type}-{utcnow().strftime('%Y-%m-%d--%H-%M-%S')}"
275
278
 
276
279
 
277
- class DeployableTraderAgent(DeployableAgent):
280
+ class DeployablePredictionAgent(DeployableAgent):
278
281
  bet_on_n_markets_per_run: int = 1
279
282
  min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
280
283
  allow_invalid_questions: bool = False
281
284
  same_market_bet_interval: timedelta = timedelta(hours=24)
285
+ # Only Metaculus allows to post predictions without trading (buying/selling of outcome tokens).
286
+ supported_markets: t.Sequence[MarketType] = [MarketType.METACULUS]
282
287
 
283
288
  def __init__(
284
289
  self,
285
290
  enable_langfuse: bool = APIKeys().default_enable_langfuse,
286
- place_bet: bool = True,
291
+ store_prediction: bool = True,
287
292
  ) -> None:
288
293
  super().__init__(enable_langfuse=enable_langfuse)
289
- self.place_bet = place_bet
290
-
291
- def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
292
- user_id = market.get_user_id(api_keys=APIKeys())
293
-
294
- total_amount = market.get_tiny_bet_amount().amount
295
- if existing_position := market.get_position(user_id=user_id):
296
- total_amount += existing_position.total_amount.amount
297
-
298
- return MaxAccuracyBettingStrategy(bet_amount=total_amount)
294
+ self.store_prediction = store_prediction
299
295
 
300
296
  def initialize_langfuse(self) -> None:
301
297
  super().initialize_langfuse()
@@ -304,7 +300,6 @@ class DeployableTraderAgent(DeployableAgent):
304
300
  self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
305
301
  self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
306
302
  self.process_market = observe()(self.process_market) # type: ignore[method-assign]
307
- self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
308
303
 
309
304
  def update_langfuse_trace_by_market(
310
305
  self, market_type: MarketType, market: AgentMarket
@@ -342,19 +337,6 @@ class DeployableTraderAgent(DeployableAgent):
342
337
  f"{api_keys=} doesn't have enough operational balance."
343
338
  )
344
339
 
345
- def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
346
- api_keys = APIKeys()
347
-
348
- # Get the strategy to know how much it will bet.
349
- strategy = self.get_betting_strategy(market)
350
- # Have a little bandwidth after the bet.
351
- min_required_balance_to_trade = strategy.maximum_possible_bet_amount * 1.01
352
-
353
- if market.get_trade_balance(api_keys) < min_required_balance_to_trade:
354
- raise OutOfFundsError(
355
- f"Minimum required balance {min_required_balance_to_trade} for agent is not met."
356
- )
357
-
358
340
  def have_bet_on_market_since(self, market: AgentMarket, since: timedelta) -> bool:
359
341
  return have_bet_on_market_since(keys=APIKeys(), market=market, since=since)
360
342
 
@@ -399,26 +381,11 @@ class DeployableTraderAgent(DeployableAgent):
399
381
  )
400
382
  return available_markets
401
383
 
402
- def build_trades(
403
- self,
404
- market: AgentMarket,
405
- answer: ProbabilisticAnswer,
406
- existing_position: Position | None,
407
- ) -> list[Trade]:
408
- strategy = self.get_betting_strategy(market=market)
409
- trades = strategy.calculate_trades(existing_position, answer, market)
410
- BettingStrategy.assert_trades_currency_match_markets(market, trades)
411
- return trades
412
-
413
384
  def before_process_market(
414
385
  self, market_type: MarketType, market: AgentMarket
415
386
  ) -> None:
416
- self.update_langfuse_trace_by_market(market_type, market)
417
-
418
387
  api_keys = APIKeys()
419
388
 
420
- self.check_min_required_balance_to_trade(market)
421
-
422
389
  if market_type.is_blockchain_market:
423
390
  # Exchange wxdai back to xdai if the balance is getting low, so we can keep paying for fees.
424
391
  if self.min_balance_to_keep_in_native_currency is not None:
@@ -434,65 +401,39 @@ class DeployableTraderAgent(DeployableAgent):
434
401
  market: AgentMarket,
435
402
  verify_market: bool = True,
436
403
  ) -> ProcessedMarket | None:
404
+ self.update_langfuse_trace_by_market(market_type, market)
437
405
  logger.info(f"Processing market {market.question=} from {market.url=}.")
438
406
 
439
- self.before_process_market(market_type, market)
440
-
407
+ answer: ProbabilisticAnswer | None
441
408
  if verify_market and not self.verify_market(market_type, market):
442
409
  logger.info(f"Market '{market.question}' doesn't meet the criteria.")
443
- self.update_langfuse_trace_by_processed_market(market_type, None)
444
- return None
445
-
446
- answer = self.answer_binary_market(market)
447
-
448
- if answer is None:
449
- logger.info(f"No answer for market '{market.question}'.")
450
- self.update_langfuse_trace_by_processed_market(market_type, None)
451
- return None
410
+ answer = None
411
+ else:
412
+ answer = self.answer_binary_market(market)
452
413
 
453
- existing_position = market.get_position(user_id=APIKeys().bet_from_address)
454
- trades = self.build_trades(
455
- market=market,
456
- answer=answer,
457
- existing_position=existing_position,
414
+ processed_market = (
415
+ ProcessedMarket(answer=answer) if answer is not None else None
458
416
  )
459
417
 
460
- placed_trades = []
461
- if self.place_bet:
462
- for trade in trades:
463
- logger.info(f"Executing trade {trade} on market {market.id}")
464
-
465
- match trade.trade_type:
466
- case TradeType.BUY:
467
- id = market.buy_tokens(
468
- outcome=trade.outcome, amount=trade.amount
469
- )
470
- case TradeType.SELL:
471
- id = market.sell_tokens(
472
- outcome=trade.outcome, amount=trade.amount
473
- )
474
- case _:
475
- raise ValueError(f"Unexpected trade type {trade.trade_type}.")
476
- placed_trades.append(PlacedTrade.from_trade(trade, id))
477
-
478
- processed_market = ProcessedMarket(answer=answer, trades=placed_trades)
479
418
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
480
-
481
- self.after_process_market(
482
- market_type, market, processed_market=processed_market
419
+ logger.info(
420
+ f"Processed market {market.question=} from {market.url=} with {answer=}."
483
421
  )
484
-
485
- logger.info(f"Processed market {market.question=} from {market.url=}.")
486
422
  return processed_market
487
423
 
488
424
  def after_process_market(
489
425
  self,
490
426
  market_type: MarketType,
491
427
  market: AgentMarket,
492
- processed_market: ProcessedMarket,
428
+ processed_market: ProcessedMarket | None,
493
429
  ) -> None:
494
430
  keys = APIKeys()
495
- market.store_prediction(processed_market=processed_market, keys=keys)
431
+ if self.store_prediction:
432
+ market.store_prediction(processed_market=processed_market, keys=keys)
433
+ else:
434
+ logger.info(
435
+ f"Prediction {processed_market} not stored because {self.store_prediction=}."
436
+ )
496
437
 
497
438
  def before_process_markets(self, market_type: MarketType) -> None:
498
439
  """
@@ -514,7 +455,9 @@ class DeployableTraderAgent(DeployableAgent):
514
455
  processed = 0
515
456
 
516
457
  for market in available_markets:
458
+ self.before_process_market(market_type, market)
517
459
  processed_market = self.process_market(market_type, market)
460
+ self.after_process_market(market_type, market, processed_market)
518
461
 
519
462
  if processed_market is not None:
520
463
  processed += 1
@@ -528,6 +471,139 @@ class DeployableTraderAgent(DeployableAgent):
528
471
  "Executes actions that occur after bets are placed."
529
472
 
530
473
  def run(self, market_type: MarketType) -> None:
474
+ if market_type not in self.supported_markets:
475
+ raise ValueError(
476
+ f"Only {self.supported_markets} are supported by this agent."
477
+ )
531
478
  self.before_process_markets(market_type)
532
479
  self.process_markets(market_type)
533
480
  self.after_process_markets(market_type)
481
+
482
+
483
+ class DeployableTraderAgent(DeployablePredictionAgent):
484
+ # These markets require place of bet, not just predictions.
485
+ supported_markets: t.Sequence[MarketType] = [
486
+ MarketType.OMEN,
487
+ MarketType.MANIFOLD,
488
+ MarketType.POLYMARKET,
489
+ ]
490
+
491
+ def __init__(
492
+ self,
493
+ enable_langfuse: bool = APIKeys().default_enable_langfuse,
494
+ store_prediction: bool = True,
495
+ store_trades: bool = True,
496
+ place_trades: bool = True,
497
+ ) -> None:
498
+ super().__init__(
499
+ enable_langfuse=enable_langfuse, store_prediction=store_prediction
500
+ )
501
+ self.store_trades = store_trades
502
+ self.place_trades = place_trades
503
+
504
+ def initialize_langfuse(self) -> None:
505
+ super().initialize_langfuse()
506
+ # Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
507
+ self.get_betting_strategy = observe()(self.get_betting_strategy) # type: ignore[method-assign]
508
+ self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
509
+
510
+ def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
511
+ api_keys = APIKeys()
512
+
513
+ # Get the strategy to know how much it will bet.
514
+ strategy = self.get_betting_strategy(market)
515
+ # Have a little bandwidth after the bet.
516
+ min_required_balance_to_trade = strategy.maximum_possible_bet_amount * 1.01
517
+
518
+ if market.get_trade_balance(api_keys) < min_required_balance_to_trade:
519
+ raise OutOfFundsError(
520
+ f"Minimum required balance {min_required_balance_to_trade} for agent is not met."
521
+ )
522
+
523
+ def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
524
+ user_id = market.get_user_id(api_keys=APIKeys())
525
+
526
+ total_amount = market.get_tiny_bet_amount().amount
527
+ if existing_position := market.get_position(user_id=user_id):
528
+ total_amount += existing_position.total_amount.amount
529
+
530
+ return MaxAccuracyBettingStrategy(bet_amount=total_amount)
531
+
532
+ def build_trades(
533
+ self,
534
+ market: AgentMarket,
535
+ answer: ProbabilisticAnswer,
536
+ existing_position: Position | None,
537
+ ) -> list[Trade]:
538
+ strategy = self.get_betting_strategy(market=market)
539
+ trades = strategy.calculate_trades(existing_position, answer, market)
540
+ BettingStrategy.assert_trades_currency_match_markets(market, trades)
541
+ return trades
542
+
543
+ def before_process_market(
544
+ self, market_type: MarketType, market: AgentMarket
545
+ ) -> None:
546
+ super().before_process_market(market_type, market)
547
+ self.check_min_required_balance_to_trade(market)
548
+
549
+ def process_market(
550
+ self,
551
+ market_type: MarketType,
552
+ market: AgentMarket,
553
+ verify_market: bool = True,
554
+ ) -> ProcessedTradedMarket | None:
555
+ processed_market = super().process_market(market_type, market, verify_market)
556
+ if processed_market is None:
557
+ return None
558
+
559
+ api_keys = APIKeys()
560
+ existing_position = market.get_position(
561
+ user_id=market.get_user_id(api_keys=api_keys)
562
+ )
563
+ trades = self.build_trades(
564
+ market=market,
565
+ answer=processed_market.answer,
566
+ existing_position=existing_position,
567
+ )
568
+
569
+ placed_trades = []
570
+ for trade in trades:
571
+ logger.info(f"Executing trade {trade} on market {market.id} ({market.url})")
572
+
573
+ if self.place_trades:
574
+ match trade.trade_type:
575
+ case TradeType.BUY:
576
+ id = market.buy_tokens(
577
+ outcome=trade.outcome, amount=trade.amount
578
+ )
579
+ case TradeType.SELL:
580
+ id = market.sell_tokens(
581
+ outcome=trade.outcome, amount=trade.amount
582
+ )
583
+ case _:
584
+ raise ValueError(f"Unexpected trade type {trade.trade_type}.")
585
+ placed_trades.append(PlacedTrade.from_trade(trade, id))
586
+ else:
587
+ logger.info(f"Trade execution skipped because {self.place_trades=}.")
588
+
589
+ traded_market = ProcessedTradedMarket(
590
+ answer=processed_market.answer, trades=placed_trades
591
+ )
592
+ logger.info(f"Traded market {market.question=} from {market.url=}.")
593
+ return traded_market
594
+
595
+ def after_process_market(
596
+ self,
597
+ market_type: MarketType,
598
+ market: AgentMarket,
599
+ processed_market: ProcessedMarket | None,
600
+ ) -> None:
601
+ api_keys = APIKeys()
602
+ super().after_process_market(market_type, market, processed_market)
603
+ if isinstance(processed_market, ProcessedTradedMarket):
604
+ if self.store_trades:
605
+ market.store_trades(processed_market, api_keys)
606
+ else:
607
+ logger.info(
608
+ f"Trades {processed_market.trades} not stored because {self.store_trades=}."
609
+ )
@@ -24,6 +24,10 @@ from prediction_market_agent_tooling.tools.betting_strategies.utils import Simpl
24
24
  from prediction_market_agent_tooling.tools.utils import check_not_none
25
25
 
26
26
 
27
+ class GuaranteedLossError(RuntimeError):
28
+ pass
29
+
30
+
27
31
  class BettingStrategy(ABC):
28
32
  @abstractmethod
29
33
  def calculate_trades(
@@ -63,7 +67,7 @@ class BettingStrategy(ABC):
63
67
  )
64
68
 
65
69
  if outcome_tokens_to_get.amount < trade.amount.amount:
66
- raise RuntimeError(
70
+ raise GuaranteedLossError(
67
71
  f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}."
68
72
  )
69
73
 
@@ -29,6 +29,9 @@ from prediction_market_agent_tooling.tools.utils import (
29
29
 
30
30
  class ProcessedMarket(BaseModel):
31
31
  answer: ProbabilisticAnswer
32
+
33
+
34
+ class ProcessedTradedMarket(ProcessedMarket):
32
35
  trades: list[PlacedTrade]
33
36
 
34
37
 
@@ -228,13 +231,21 @@ class AgentMarket(BaseModel):
228
231
  raise NotImplementedError("Subclasses must implement this method")
229
232
 
230
233
  def store_prediction(
231
- self, processed_market: ProcessedMarket, keys: APIKeys
234
+ self, processed_market: ProcessedMarket | None, keys: APIKeys
232
235
  ) -> None:
233
236
  """
234
237
  If market allows to upload predictions somewhere, implement it in this method.
235
238
  """
236
239
  raise NotImplementedError("Subclasses must implement this method")
237
240
 
241
+ def store_trades(
242
+ self, traded_market: ProcessedTradedMarket | None, keys: APIKeys
243
+ ) -> None:
244
+ """
245
+ If market allows to upload trades somewhere, implement it in this method.
246
+ """
247
+ raise NotImplementedError("Subclasses must implement this method")
248
+
238
249
  @staticmethod
239
250
  def get_bets_made_since(
240
251
  better_address: ChecksumAddress, start_time: DatetimeUTC
@@ -56,6 +56,9 @@ class Question(BaseModel):
56
56
  my_forecasts: MyAggregation
57
57
  type: QuestionType
58
58
  possibilities: dict[str, str] | None
59
+ description: str
60
+ fine_print: str
61
+ resolution_criteria: str
59
62
 
60
63
 
61
64
  class MetaculusQuestion(BaseModel):
@@ -64,7 +67,7 @@ class MetaculusQuestion(BaseModel):
64
67
  author_username: str
65
68
  title: str
66
69
  created_at: DatetimeUTC
67
- published_at: DatetimeUTC
70
+ published_at: DatetimeUTC | None
68
71
  scheduled_close_time: DatetimeUTC
69
72
  scheduled_resolve_time: DatetimeUTC
70
73
  user_permission: str
@@ -1,11 +1,11 @@
1
1
  import typing as t
2
2
 
3
3
  from prediction_market_agent_tooling.config import APIKeys
4
- from prediction_market_agent_tooling.gtypes import Probability
5
4
  from prediction_market_agent_tooling.markets.agent_market import (
6
5
  AgentMarket,
7
6
  FilterBy,
8
7
  MarketFees,
8
+ ProcessedMarket,
9
9
  SortBy,
10
10
  )
11
11
  from prediction_market_agent_tooling.markets.metaculus.api import (
@@ -17,7 +17,7 @@ from prediction_market_agent_tooling.markets.metaculus.api import (
17
17
  from prediction_market_agent_tooling.markets.metaculus.data_models import (
18
18
  MetaculusQuestion,
19
19
  )
20
- from prediction_market_agent_tooling.tools.utils import DatetimeUTC
20
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC, check_not_none
21
21
 
22
22
 
23
23
  class MetaculusAgentMarket(AgentMarket):
@@ -27,9 +27,9 @@ class MetaculusAgentMarket(AgentMarket):
27
27
 
28
28
  have_predicted: bool
29
29
  base_url: t.ClassVar[str] = METACULUS_API_BASE_URL
30
- description: str | None = (
31
- None # Metaculus markets don't have a description, so just default to None.
32
- )
30
+ description: str
31
+ fine_print: str
32
+ resolution_criteria: str
33
33
  fees: MarketFees = MarketFees.get_zero_fees() # No fees on Metaculus.
34
34
 
35
35
  @staticmethod
@@ -46,6 +46,9 @@ class MetaculusAgentMarket(AgentMarket):
46
46
  volume=None,
47
47
  have_predicted=model.question.my_forecasts.latest is not None,
48
48
  outcome_token_pool=None,
49
+ description=model.question.description,
50
+ fine_print=model.question.fine_print,
51
+ resolution_criteria=model.question.resolution_criteria,
49
52
  )
50
53
 
51
54
  @staticmethod
@@ -103,10 +106,29 @@ class MetaculusAgentMarket(AgentMarket):
103
106
  break
104
107
  return [MetaculusAgentMarket.from_data_model(q) for q in all_questions[:limit]]
105
108
 
106
- def submit_prediction(self, p_yes: Probability, reasoning: str) -> None:
107
- make_prediction(self.id, p_yes)
108
- post_question_comment(self.id, reasoning)
109
+ def store_prediction(
110
+ self, processed_market: ProcessedMarket | None, keys: APIKeys
111
+ ) -> None:
112
+ if processed_market is not None:
113
+ make_prediction(self.id, processed_market.answer.p_yes)
114
+ post_question_comment(
115
+ self.id,
116
+ check_not_none(
117
+ processed_market.answer.reasoning,
118
+ "Reasoning must be provided for Metaculus.",
119
+ ),
120
+ )
109
121
 
110
122
  @staticmethod
111
123
  def get_user_id(api_keys: APIKeys) -> str:
112
124
  return str(api_keys.metaculus_user_id)
125
+
126
+ @staticmethod
127
+ def verify_operational_balance(api_keys: APIKeys) -> bool:
128
+ # No operational balance for Metaculus.
129
+ return True
130
+
131
+ @staticmethod
132
+ def redeem_winnings(api_keys: APIKeys) -> None:
133
+ # Nothing to redeem on Metaculus.
134
+ pass
@@ -25,6 +25,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
25
25
  FilterBy,
26
26
  MarketFees,
27
27
  ProcessedMarket,
28
+ ProcessedTradedMarket,
28
29
  SortBy,
29
30
  )
30
31
  from prediction_market_agent_tooling.markets.data_models import (
@@ -417,12 +418,19 @@ class OmenAgentMarket(AgentMarket):
417
418
  ) > xdai_type(0.001)
418
419
 
419
420
  def store_prediction(
420
- self, processed_market: ProcessedMarket, keys: APIKeys
421
+ self, processed_market: ProcessedMarket | None, keys: APIKeys
421
422
  ) -> None:
423
+ """On Omen, we have to store predictions along with trades, see `store_trades`."""
424
+
425
+ def store_trades(
426
+ self, traded_market: ProcessedTradedMarket | None, keys: APIKeys
427
+ ) -> None:
428
+ if traded_market is None:
429
+ logger.warning(f"No prediction for market {self.id}, not storing anything.")
430
+ return
431
+
422
432
  reasoning = (
423
- processed_market.answer.reasoning
424
- if processed_market.answer.reasoning
425
- else ""
433
+ traded_market.answer.reasoning if traded_market.answer.reasoning else ""
426
434
  )
427
435
 
428
436
  ipfs_hash_decoded = HexBytes(HASH_ZERO)
@@ -434,13 +442,13 @@ class OmenAgentMarket(AgentMarket):
434
442
  ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
435
443
 
436
444
  tx_hashes = [
437
- HexBytes(HexStr(i.id)) for i in processed_market.trades if i.id is not None
445
+ HexBytes(HexStr(i.id)) for i in traded_market.trades if i.id is not None
438
446
  ]
439
447
  prediction = ContractPrediction(
440
448
  publisher=keys.public_key,
441
449
  ipfs_hash=ipfs_hash_decoded,
442
450
  tx_hashes=tx_hashes,
443
- estimated_probability_bps=int(processed_market.answer.p_yes * 10000),
451
+ estimated_probability_bps=int(traded_market.answer.p_yes * 10000),
444
452
  )
445
453
  tx_receipt = OmenAgentResultMappingContract().add_prediction(
446
454
  api_keys=keys,
@@ -24,7 +24,7 @@ QUESTION_IS_INVALID_PROMPT = """Main signs about an invalid question (sometimes
24
24
  - The violent event can be caused by a single conscious being.
25
25
  - The violent event is done illegally.
26
26
  - The market should not directly incentivize immoral violent (such as murder, rape or unjust imprisonment) actions which could likely be performed by any participant.
27
- - Invalid: Will Donald Trump be alive on the 01/12/2021? (Anyone could bet on No and kill him for a guaranteed profit. Anyone could bet on Yes to effectively put a bounty on his head).
27
+ - Invalid: Will Donald Trump be alive on the 01/12/2021? (Anyone could bet on "No" and kill him for a guaranteed profit. Anyone could bet on "Yes" to effectively put a bounty on his head).
28
28
  - Invalid: Will Hera be a victim of swatting in 2020? (Anyone could falsely call the emergency services on him in order to win the bet)
29
29
  - This does not prevent markets:
30
30
  - Whose topics are violent events not caused by conscious beings.
@@ -35,9 +35,10 @@ QUESTION_IS_INVALID_PROMPT = """Main signs about an invalid question (sometimes
35
35
  - Valid: Will the US be engaged in a military conflict with a UN member state in 2021? (It’s unlikely for the US to declare war in order to win a bet on this market).
36
36
  - Valid: Will Derek Chauvin go to jail for the murder of George Flyod? (It’s unlikely that the jurors would collude to make a wrong verdict in order to win this market).
37
37
  - Questions with relative dates will resolve as invalid. Dates must be stated in absolute terms, not relative depending on the current time.
38
- - Invalid: Who will be the president of the United States in 6 months? (in 6 months depends on the current time).
38
+ - Invalid: Who will be the president of the United States in 6 months? ("in 6 months depends on the current time").
39
+ - Invalid: In the next 14 days, will Gnosis Chain gain another 1M users? ("in the next 14 days depends on the current time").
39
40
  - Questions about moral values and not facts will be resolved as invalid.
40
- - Invalid: Is it ethical to eat meat?”.
41
+ - Invalid: "Is it ethical to eat meat?".
41
42
 
42
43
  Follow a chain of thought to evaluate if the question is invalid:
43
44
 
@@ -33,6 +33,11 @@ def tavily_search(
33
33
 
34
34
  Argument default values are different from the original method, to return everything by default, because it can be handy in the future and it doesn't increase the costs.
35
35
  """
36
+ if topic == "news" and days is None:
37
+ raise ValueError("When topic is 'news', days must be an integer")
38
+ if topic == "general" and days is not None:
39
+ raise ValueError("When topic is 'general', days must be None")
40
+
36
41
  if tavily_storage and (
37
42
  response_parsed := tavily_storage.find(
38
43
  query=query,
@@ -103,11 +108,15 @@ def _tavily_search(
103
108
  tavily = TavilyClient(
104
109
  api_key=(api_keys or APIKeys()).tavily_api_key.get_secret_value()
105
110
  )
111
+
112
+ # Optional `days` arg can only be specified if not None, otherwise Tavily
113
+ # will throw an error
114
+ kwargs = {"days": days} if days else {}
115
+
106
116
  response: dict[str, t.Any] = tavily.search(
107
117
  query=query,
108
118
  search_depth=search_depth,
109
119
  topic=topic,
110
- days=days,
111
120
  max_results=max_results,
112
121
  include_domains=include_domains,
113
122
  exclude_domains=exclude_domains,
@@ -115,6 +124,7 @@ def _tavily_search(
115
124
  include_raw_content=include_raw_content,
116
125
  include_images=include_images,
117
126
  use_cache=use_cache,
127
+ **kwargs,
118
128
  )
119
129
  return response
120
130
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.54.0
3
+ Version: 0.55.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
@@ -44,7 +44,7 @@ Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
44
44
  Requires-Dist: streamlit (>=1.31.0,<2.0.0)
45
45
  Requires-Dist: subgrounds (>=1.9.1,<2.0.0)
46
46
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
47
- Requires-Dist: tavily-python (>=0.3.9,<0.4.0)
47
+ Requires-Dist: tavily-python (>=0.5.0,<0.6.0)
48
48
  Requires-Dist: tqdm (>=4.66.2,<5.0.0)
49
49
  Requires-Dist: typer (>=0.9.0,<1.0.0)
50
50
  Requires-Dist: types-python-dateutil (>=2.9.0.20240906,<3.0.0.0)
@@ -17,9 +17,9 @@ prediction_market_agent_tooling/benchmark/agents.py,sha256=B1-uWdyeN4GGKMWGK_-Cc
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
19
  prediction_market_agent_tooling/config.py,sha256=WC30Nr16RGueTafA9i67OIB-6KDHZRryhiLPzebg9_I,6740
20
- prediction_market_agent_tooling/deploy/agent.py,sha256=OAfD9B0rN6k3eISvRFJpHTS_dW4yMM2GlTvG8FMejd8,19232
20
+ prediction_market_agent_tooling/deploy/agent.py,sha256=1a1VPaCA77MhK9wQwX1MjEycareP_NYfsm73YFXwyxY,22222
21
21
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
22
- prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=egarsbIiFDwCLQ0YtqzP3jdht2_PfhlT0yAmwYGJ10o,13009
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
24
24
  prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5yp0GE9Mw5caYqlFUZQ2j3ks,3739
25
25
  prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py,sha256=OsPboCFGiZKsvGyntGZHwdqPlLTthITkNF5rJFvGgU8,2582
@@ -30,7 +30,7 @@ prediction_market_agent_tooling/jobs/jobs.py,sha256=I07yh0GJ-xhlvQaOUQB8xlSnihhc
30
30
  prediction_market_agent_tooling/jobs/jobs_models.py,sha256=I5uBTHJ2S1Wi3H4jDxxU7nsswSIP9r3BevHmljLh5Pg,1370
31
31
  prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=I2_vGrEJj1reSI8M377ab5QCsYNp_l4l4QeYEmDBkFM,3989
32
32
  prediction_market_agent_tooling/loggers.py,sha256=Am6HHXRNO545BO3l7Ue9Wb2TkYE1OK8KKhGbI3XypVU,3751
33
- prediction_market_agent_tooling/markets/agent_market.py,sha256=A72Lf7bdFOTelLhUTSHmfLBOuTZ8b0Qd_IJIgdjzeB0,12470
33
+ prediction_market_agent_tooling/markets/agent_market.py,sha256=OgB6bvDGfTAxbh6cDGD3XFO0iy0MAaOQvXEP6nw8xW8,12817
34
34
  prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
35
35
  prediction_market_agent_tooling/markets/data_models.py,sha256=jMqrSFO_w2z-5N3PFVgZqTHdVdkzSDhhzky2lHsGGKA,3621
36
36
  prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -41,11 +41,11 @@ prediction_market_agent_tooling/markets/manifold/utils.py,sha256=cPPFWXm3vCYH1jy
41
41
  prediction_market_agent_tooling/markets/market_fees.py,sha256=Q64T9uaJx0Vllt0BkrPmpMEz53ra-hMVY8Czi7CEP7s,1227
42
42
  prediction_market_agent_tooling/markets/markets.py,sha256=mwubc567OIlA32YKqlIdTloYV8FGJia9gPv0wE0xUEA,3368
43
43
  prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42DCg2M_JWYPAuNjqZ3eBqaQBLkNks,2736
44
- prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=2wDZ0BmK9O5Lud-q-FCzgW0tsK9GxMU0rUMlcPxSS04,3184
45
- prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=d5tHhekLfPGO6CfmWWJQWpJu5HyoAWBM2aGSR874Cms,3695
44
+ prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=Suxa7xELdYuFNKqvGvFh8qyfVtAg79E-vaQ6dqNZOtA,3261
45
+ prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=E_TUf5q73lWzdMp40Ne-3w4MjEd7AHcaif4pvFh9FMU,4360
46
46
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  prediction_market_agent_tooling/markets/omen/data_models.py,sha256=nCjsc-ylIzQOCK_1BW-5NoYrS-NIXz2Hg9N1-IqhhC8,27516
48
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=-9u8Tb3ADGy7vd0E0peTV8OqQi9zMaXBa4b0gJXpQpc,50773
48
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=LqNZjngo6LoktKecfYmGmJJ9D5rj-s0Poy4x4_GZfp0,51116
49
49
  prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=Zq7SncCq-hvpgXKsVruGBGCn1OhKZTe7r1qLdCTrT2w,28297
50
50
  prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=iDWdjICGkt968exwCjY-6nsnQyrrNAg3YjnDdP430GQ,9415
51
51
  prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=zQH3iu0SVH1RmE-W3NMEpcKVMILXJYxMhL6w1wh5RUo,37348
@@ -79,7 +79,7 @@ prediction_market_agent_tooling/tools/httpx_cached_client.py,sha256=0-N1r0zcGKlY
79
79
  prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
80
80
  prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=8A3U2uxsCsOfLjru-6R_PPIAuiKY4qFkWp_GSBPV6-s,1280
81
81
  prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py,sha256=CTTMfTvs_8PH4kAtlQby2aeEKwgpmxtuGbd4oYIdJ2A,1201
82
- prediction_market_agent_tooling/tools/is_invalid.py,sha256=Lc5fWB4fmx7tFvRakmUOzo0Oq5EizorddZ2xjesEopY,4984
82
+ prediction_market_agent_tooling/tools/is_invalid.py,sha256=VjjOCrkt6S8ytOg_0s2gUL8IKX2muWq1QLIZX0MPMxY,5094
83
83
  prediction_market_agent_tooling/tools/is_predictable.py,sha256=NIoR2bTNMmADcyNY2aKNMWkiDw7Z_9kZMcFXEdyewy4,6771
84
84
  prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
85
85
  prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=B0PhAQyviFnVbtOCYMxYmcCn66cu9nbqAOIAZcdgiRI,5771
@@ -89,12 +89,12 @@ prediction_market_agent_tooling/tools/safe.py,sha256=h0xOO0eNtitClf0fPkn-0oTc6A_
89
89
  prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
90
90
  prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
91
91
  prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=Rz4tZzwCRzPaq49SFT33SCRQrqHXtqWdD9ajb2tGCWc,2723
92
- prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=MK_ozeQbJ014HGiKFPDScjFYq0OGcjY1KPgc9A6qO0M,4511
92
+ prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=inGgWo_TFLA7Q0agYx2MYdEsh659oIdovQMniiG_Q20,4895
93
93
  prediction_market_agent_tooling/tools/tavily/tavily_storage.py,sha256=t-tZzbCzBBdFedRZDuVBn3A3mIDX8Z5wza6SxWswu_E,4093
94
94
  prediction_market_agent_tooling/tools/utils.py,sha256=W-9SqeCKd51BYMRhDjYPQ7lfNO_zE9EvYpmu2r5WXGA,7163
95
95
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=dkcjG-LtuaWRh7WEMzRGmZ5B5rsxZTlliFOI6fj-EJ8,11842
96
- prediction_market_agent_tooling-0.54.0.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
97
- prediction_market_agent_tooling-0.54.0.dist-info/METADATA,sha256=iAGimZ2Kjtbx94FpzKsdYXKvhFO9e3Bd6xaAG5eQVg4,8056
98
- prediction_market_agent_tooling-0.54.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
99
- prediction_market_agent_tooling-0.54.0.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
100
- prediction_market_agent_tooling-0.54.0.dist-info/RECORD,,
96
+ prediction_market_agent_tooling-0.55.0.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
97
+ prediction_market_agent_tooling-0.55.0.dist-info/METADATA,sha256=2a-TbhiR5XyBGScQZDd1pV1vzC-LBWt7zFa6WorpmD8,8056
98
+ prediction_market_agent_tooling-0.55.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
99
+ prediction_market_agent_tooling-0.55.0.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
100
+ prediction_market_agent_tooling-0.55.0.dist-info/RECORD,,