prediction-market-agent-tooling 0.57.0__py3-none-any.whl → 0.57.2__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 (22) hide show
  1. prediction_market_agent_tooling/config.py +2 -2
  2. prediction_market_agent_tooling/markets/base_subgraph_handler.py +2 -2
  3. prediction_market_agent_tooling/markets/data_models.py +14 -1
  4. prediction_market_agent_tooling/markets/omen/data_models.py +25 -7
  5. prediction_market_agent_tooling/markets/omen/omen.py +4 -0
  6. prediction_market_agent_tooling/markets/omen/omen_contracts.py +1 -1
  7. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +38 -3
  8. prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +5 -3
  9. prediction_market_agent_tooling/monitor/monitor.py +1 -1
  10. prediction_market_agent_tooling/tools/caches/db_cache.py +9 -73
  11. prediction_market_agent_tooling/tools/caches/inmemory_cache.py +44 -4
  12. prediction_market_agent_tooling/tools/caches/serializers.py +61 -0
  13. prediction_market_agent_tooling/tools/db/db_manager.py +76 -0
  14. prediction_market_agent_tooling/tools/httpx_cached_client.py +4 -1
  15. prediction_market_agent_tooling/tools/langfuse_client_utils.py +2 -0
  16. prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py +7 -17
  17. prediction_market_agent_tooling/tools/transaction_cache.py +2 -2
  18. {prediction_market_agent_tooling-0.57.0.dist-info → prediction_market_agent_tooling-0.57.2.dist-info}/METADATA +3 -1
  19. {prediction_market_agent_tooling-0.57.0.dist-info → prediction_market_agent_tooling-0.57.2.dist-info}/RECORD +22 -20
  20. {prediction_market_agent_tooling-0.57.0.dist-info → prediction_market_agent_tooling-0.57.2.dist-info}/LICENSE +0 -0
  21. {prediction_market_agent_tooling-0.57.0.dist-info → prediction_market_agent_tooling-0.57.2.dist-info}/WHEEL +0 -0
  22. {prediction_market_agent_tooling-0.57.0.dist-info → prediction_market_agent_tooling-0.57.2.dist-info}/entry_points.txt +0 -0
@@ -188,14 +188,14 @@ class APIKeys(BaseSettings):
188
188
  return {
189
189
  k: v
190
190
  for k, v in self.model_dump().items()
191
- if APIKeys.model_fields[k].annotation not in SECRET_TYPES and v is not None
191
+ if self.model_fields[k].annotation not in SECRET_TYPES and v is not None
192
192
  }
193
193
 
194
194
  def model_dump_secrets(self) -> dict[str, t.Any]:
195
195
  return {
196
196
  k: v.get_secret_value() if isinstance(v, SecretStr) else v
197
197
  for k, v in self.model_dump().items()
198
- if APIKeys.model_fields[k].annotation in SECRET_TYPES and v is not None
198
+ if self.model_fields[k].annotation in SECRET_TYPES and v is not None
199
199
  }
200
200
 
201
201
  def check_if_is_safe_owner(self, ethereum_client: EthereumClient) -> bool:
@@ -12,8 +12,8 @@ T = t.TypeVar("T", bound=BaseModel)
12
12
 
13
13
 
14
14
  class BaseSubgraphHandler(metaclass=SingletonMeta):
15
- def __init__(self) -> None:
16
- self.sg = Subgrounds()
15
+ def __init__(self, timeout: int = 30) -> None:
16
+ self.sg = Subgrounds(timeout=timeout)
17
17
  # Patch methods to retry on failure.
18
18
  self.sg.query_json = tenacity.retry(
19
19
  stop=tenacity.stop_after_attempt(3),
@@ -142,7 +142,7 @@ class PlacedTrade(Trade):
142
142
  )
143
143
 
144
144
 
145
- class SimulationDetail(BaseModel):
145
+ class SimulatedBetDetail(BaseModel):
146
146
  strategy: str
147
147
  url: str
148
148
  market_p_yes: float
@@ -161,3 +161,16 @@ class SharpeOutput(BaseModel):
161
161
  annualized_volatility: float
162
162
  mean_daily_return: float
163
163
  annualized_sharpe_ratio: float
164
+
165
+
166
+ class SimulatedLifetimeDetail(BaseModel):
167
+ p_yes_mse: float
168
+ total_bet_amount: float
169
+ total_bet_profit: float
170
+ total_simulated_amount: float
171
+ total_simulated_profit: float
172
+ roi: float
173
+ simulated_roi: float
174
+ sharpe_output_original: SharpeOutput
175
+ sharpe_output_simulation: SharpeOutput
176
+ maximize: float
@@ -42,6 +42,10 @@ PRESAGIO_BASE_URL = "https://presagio.pages.dev"
42
42
  TEST_CATEGORY = "test" # This category is hidden on Presagio for testing purposes.
43
43
 
44
44
 
45
+ def construct_presagio_url(market_id: HexAddress) -> str:
46
+ return f"{PRESAGIO_BASE_URL}/markets?id={market_id}"
47
+
48
+
45
49
  def get_boolean_outcome(outcome_str: str) -> bool:
46
50
  if outcome_str == OMEN_TRUE_OUTCOME:
47
51
  return True
@@ -392,7 +396,7 @@ class OmenMarket(BaseModel):
392
396
 
393
397
  @property
394
398
  def url(self) -> str:
395
- return f"{PRESAGIO_BASE_URL}/markets?id={self.id}"
399
+ return construct_presagio_url(self.id)
396
400
 
397
401
  @staticmethod
398
402
  def from_created_market(model: "CreatedMarket") -> "OmenMarket":
@@ -499,13 +503,17 @@ class OmenBet(BaseModel):
499
503
  creator: OmenBetCreator
500
504
  creationTimestamp: int
501
505
  collateralAmount: Wei
502
- collateralAmountUSD: USD
503
506
  feeAmount: Wei
504
507
  outcomeIndex: int
505
508
  outcomeTokensTraded: Wei
506
509
  transactionHash: HexBytes
507
510
  fpmm: OmenMarket
508
511
 
512
+ @property
513
+ def collateral_amount_usd(self) -> USD:
514
+ # Convert manually instad of using the field `collateralAmountUSD` available on the graph, because it's bugged, it's 0 for non-xDai markets.
515
+ return USD(wei_to_xdai(self.collateralAmount))
516
+
509
517
  @property
510
518
  def creation_datetime(self) -> DatetimeUTC:
511
519
  return DatetimeUTC.to_datetime_utc(self.creationTimestamp)
@@ -540,7 +548,7 @@ class OmenBet(BaseModel):
540
548
  return Bet(
541
549
  id=str(self.transactionHash),
542
550
  # 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.
543
- amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
551
+ amount=BetAmount(amount=self.collateral_amount_usd, currency=Currency.xDai),
544
552
  outcome=self.boolean_outcome,
545
553
  created_time=self.creation_datetime,
546
554
  market_question=self.title,
@@ -556,7 +564,7 @@ class OmenBet(BaseModel):
556
564
  return ResolvedBet(
557
565
  id=self.transactionHash.hex(),
558
566
  # 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.
559
- amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
567
+ amount=BetAmount(amount=self.collateral_amount_usd, currency=Currency.xDai),
560
568
  outcome=self.boolean_outcome,
561
569
  created_time=self.creation_datetime,
562
570
  market_question=self.title,
@@ -775,6 +783,12 @@ class CreatedMarket(BaseModel):
775
783
  fee: Wei
776
784
  distribution_hint: list[OmenOutcomeToken] | None
777
785
 
786
+ @property
787
+ def url(self) -> str:
788
+ return construct_presagio_url(
789
+ self.market_event.fixed_product_market_maker_checksummed
790
+ )
791
+
778
792
 
779
793
  class ContractPrediction(BaseModel):
780
794
  model_config = ConfigDict(populate_by_name=True)
@@ -797,9 +811,13 @@ class ContractPrediction(BaseModel):
797
811
  return Web3.to_checksum_address(self.publisher)
798
812
 
799
813
  @staticmethod
800
- def from_tuple(values: tuple[t.Any]) -> "ContractPrediction":
801
- data = {k: v for k, v in zip(ContractPrediction.model_fields.keys(), values)}
802
- return ContractPrediction.model_validate(data)
814
+ def from_tuple(values: tuple[t.Any, ...]) -> "ContractPrediction":
815
+ return ContractPrediction(
816
+ publisher=values[0],
817
+ ipfs_hash=values[1],
818
+ tx_hashes=values[2],
819
+ estimated_probability_bps=values[3],
820
+ )
803
821
 
804
822
 
805
823
  class IPFSAgentResult(BaseModel):
@@ -480,6 +480,8 @@ class OmenAgentMarket(AgentMarket):
480
480
  better_address: ChecksumAddress,
481
481
  start_time: DatetimeUTC,
482
482
  end_time: DatetimeUTC | None,
483
+ market_resolved_before: DatetimeUTC | None = None,
484
+ market_resolved_after: DatetimeUTC | None = None,
483
485
  ) -> list[ResolvedBet]:
484
486
  subgraph_handler = OmenSubgraphHandler()
485
487
  bets = subgraph_handler.get_resolved_bets_with_valid_answer(
@@ -487,6 +489,8 @@ class OmenAgentMarket(AgentMarket):
487
489
  start_time=start_time,
488
490
  end_time=end_time,
489
491
  market_id=None,
492
+ market_resolved_before=market_resolved_before,
493
+ market_resolved_after=market_resolved_after,
490
494
  )
491
495
  generic_bets = [b.to_generic_resolved_bet() for b in bets]
492
496
  return generic_bets
@@ -538,7 +538,7 @@ class OmenKlerosContract(ContractOnGnosisChain):
538
538
  address = "0xe40DD83a262da3f56976038F1554Fe541Fa75ecd"
539
539
 
540
540
  elif arbitrator == Arbitrator.KLEROS_31_JURORS_WITH_APPEAL:
541
- address = "0x29f39de98d750eb77b5fafb31b2837f079fce222"
541
+ address = "0x5562Ac605764DC4039fb6aB56a74f7321396Cdf2"
542
542
 
543
543
  else:
544
544
  raise ValueError(f"Unsupported arbitrator: {arbitrator=}")
@@ -33,6 +33,9 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
33
33
  WrappedxDaiContract,
34
34
  sDaiContract,
35
35
  )
36
+ from prediction_market_agent_tooling.tools.caches.inmemory_cache import (
37
+ persistent_inmemory_cache,
38
+ )
36
39
  from prediction_market_agent_tooling.tools.utils import (
37
40
  DatetimeUTC,
38
41
  to_int_timestamp,
@@ -112,7 +115,6 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
112
115
  bets_field.creator.id,
113
116
  bets_field.creationTimestamp,
114
117
  bets_field.collateralAmount,
115
- bets_field.collateralAmountUSD,
116
118
  bets_field.feeAmount,
117
119
  bets_field.outcomeIndex,
118
120
  bets_field.outcomeTokensTraded,
@@ -524,6 +526,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
524
526
  filter_by_answer_finalized_not_null: bool = False,
525
527
  type_: t.Literal["Buy", "Sell"] | None = None,
526
528
  market_opening_after: DatetimeUTC | None = None,
529
+ market_resolved_before: DatetimeUTC | None = None,
530
+ market_resolved_after: DatetimeUTC | None = None,
527
531
  collateral_amount_more_than: Wei | None = None,
528
532
  sort_by_field: FieldPath | None = None,
529
533
  sort_direction: str | None = None,
@@ -549,6 +553,15 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
549
553
  where_stms.append(
550
554
  trade.fpmm.openingTimestamp > to_int_timestamp(market_opening_after)
551
555
  )
556
+ if market_resolved_after is not None:
557
+ where_stms.append(
558
+ trade.fpmm.resolutionTimestamp > to_int_timestamp(market_resolved_after)
559
+ )
560
+ if market_resolved_before is not None:
561
+ where_stms.append(
562
+ trade.fpmm.resolutionTimestamp
563
+ < to_int_timestamp(market_resolved_before)
564
+ )
552
565
  if collateral_amount_more_than is not None:
553
566
  where_stms.append(trade.collateralAmount > collateral_amount_more_than)
554
567
 
@@ -577,6 +590,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
577
590
  market_id: t.Optional[ChecksumAddress] = None,
578
591
  filter_by_answer_finalized_not_null: bool = False,
579
592
  market_opening_after: DatetimeUTC | None = None,
593
+ market_resolved_before: DatetimeUTC | None = None,
594
+ market_resolved_after: DatetimeUTC | None = None,
580
595
  collateral_amount_more_than: Wei | None = None,
581
596
  ) -> list[OmenBet]:
582
597
  return self.get_trades(
@@ -587,15 +602,19 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
587
602
  filter_by_answer_finalized_not_null=filter_by_answer_finalized_not_null,
588
603
  type_="Buy", # We consider `bet` to be only the `Buy` trade types.
589
604
  market_opening_after=market_opening_after,
605
+ market_resolved_before=market_resolved_before,
606
+ market_resolved_after=market_resolved_after,
590
607
  collateral_amount_more_than=collateral_amount_more_than,
591
608
  )
592
609
 
593
610
  def get_resolved_bets(
594
611
  self,
595
612
  better_address: ChecksumAddress,
596
- start_time: DatetimeUTC,
613
+ start_time: DatetimeUTC | None = None,
597
614
  end_time: t.Optional[DatetimeUTC] = None,
598
615
  market_id: t.Optional[ChecksumAddress] = None,
616
+ market_resolved_before: DatetimeUTC | None = None,
617
+ market_resolved_after: DatetimeUTC | None = None,
599
618
  ) -> list[OmenBet]:
600
619
  omen_bets = self.get_bets(
601
620
  better_address=better_address,
@@ -603,14 +622,18 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
603
622
  end_time=end_time,
604
623
  market_id=market_id,
605
624
  filter_by_answer_finalized_not_null=True,
625
+ market_resolved_before=market_resolved_before,
626
+ market_resolved_after=market_resolved_after,
606
627
  )
607
628
  return [b for b in omen_bets if b.fpmm.is_resolved]
608
629
 
609
630
  def get_resolved_bets_with_valid_answer(
610
631
  self,
611
632
  better_address: ChecksumAddress,
612
- start_time: DatetimeUTC,
633
+ start_time: DatetimeUTC | None = None,
613
634
  end_time: t.Optional[DatetimeUTC] = None,
635
+ market_resolved_before: DatetimeUTC | None = None,
636
+ market_resolved_after: DatetimeUTC | None = None,
614
637
  market_id: t.Optional[ChecksumAddress] = None,
615
638
  ) -> list[OmenBet]:
616
639
  bets = self.get_resolved_bets(
@@ -618,6 +641,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
618
641
  start_time=start_time,
619
642
  end_time=end_time,
620
643
  market_id=market_id,
644
+ market_resolved_before=market_resolved_before,
645
+ market_resolved_after=market_resolved_after,
621
646
  )
622
647
  return [b for b in bets if b.fpmm.is_resolved_with_valid_answer]
623
648
 
@@ -926,3 +951,13 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
926
951
  raise RuntimeError("Multiple results found for a single bet.")
927
952
 
928
953
  return results[0]
954
+
955
+
956
+ @persistent_inmemory_cache
957
+ def get_omen_market_by_market_id_cached(
958
+ market_id: HexAddress,
959
+ block_number: int, # Force `block_number` to be provided, because `latest` block constantly updates.
960
+ ) -> OmenMarket:
961
+ return OmenSubgraphHandler().get_omen_market_by_market_id(
962
+ market_id, block_number=block_number
963
+ )
@@ -3,13 +3,13 @@ import pandas as pd
3
3
 
4
4
  from prediction_market_agent_tooling.markets.data_models import (
5
5
  SharpeOutput,
6
- SimulationDetail,
6
+ SimulatedBetDetail,
7
7
  )
8
8
 
9
9
 
10
10
  class SharpeRatioCalculator:
11
11
  def __init__(
12
- self, details: list[SimulationDetail], risk_free_rate: float = 0.0
12
+ self, details: list[SimulatedBetDetail], risk_free_rate: float = 0.0
13
13
  ) -> None:
14
14
  self.details = details
15
15
  self.df = pd.DataFrame([d.model_dump() for d in self.details])
@@ -19,7 +19,9 @@ class SharpeRatioCalculator:
19
19
  self, required_columns: list[str]
20
20
  ) -> None:
21
21
  if not set(required_columns).issubset(self.df.columns):
22
- raise ValueError("Dataframe doesn't contain all the required columns.")
22
+ raise ValueError(
23
+ f"Dataframe doesn't contain all the required columns. {required_columns=} {self.df.columns=}"
24
+ )
23
25
 
24
26
  def prepare_wallet_daily_balance_df(
25
27
  self, timestamp_col_name: str, profit_col_name: str
@@ -223,7 +223,7 @@ def monitor_agent(agent: DeployedAgent) -> None:
223
223
  )
224
224
  .interactive()
225
225
  )
226
- st.altair_chart( # type: ignore # Doesn't expect `LayerChart`, but `Chart`, yet it works.
226
+ st.altair_chart(
227
227
  per_day_accuracy_chart.mark_line()
228
228
  + per_day_accuracy_chart.transform_loess("x-axis-day", "Is Correct").mark_line(
229
229
  color="red", strokeDash=[5, 5]
@@ -1,7 +1,7 @@
1
1
  import hashlib
2
2
  import inspect
3
3
  import json
4
- from datetime import date, timedelta
4
+ from datetime import timedelta
5
5
  from functools import wraps
6
6
  from typing import (
7
7
  Any,
@@ -17,11 +17,12 @@ from typing import (
17
17
  from pydantic import BaseModel
18
18
  from sqlalchemy import Column
19
19
  from sqlalchemy.dialects.postgresql import JSONB
20
- from sqlmodel import Field, Session, SQLModel, create_engine, desc, select
20
+ from sqlmodel import Field, SQLModel, desc, select
21
21
 
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.db.db_manager import DBManager
25
26
  from prediction_market_agent_tooling.tools.utils import utcnow
26
27
 
27
28
  FunctionT = TypeVar("FunctionT", bound=Callable[..., Any])
@@ -97,16 +98,7 @@ def db_cache(
97
98
  if not api_keys.ENABLE_CACHE:
98
99
  return func(*args, **kwargs)
99
100
 
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)
101
+ DBManager(api_keys).create_tables([FunctionCache])
110
102
 
111
103
  # Convert *args and **kwargs to a single dictionary, where we have names for arguments passed as args as well.
112
104
  signature = inspect.signature(func)
@@ -147,12 +139,11 @@ def db_cache(
147
139
 
148
140
  # Determine if the function returns or contains Pydantic BaseModel(s)
149
141
  return_type = func.__annotations__.get("return", None)
150
- is_pydantic_model = False
151
-
152
- if return_type is not None and contains_pydantic_model(return_type):
153
- is_pydantic_model = True
142
+ is_pydantic_model = return_type is not None and contains_pydantic_model(
143
+ return_type
144
+ )
154
145
 
155
- with Session(engine) as session:
146
+ with DBManager(api_keys).get_session() as session:
156
147
  # Try to get cached result
157
148
  statement = (
158
149
  select(FunctionCache)
@@ -205,12 +196,11 @@ def db_cache(
205
196
  result=computed_result,
206
197
  created_at=utcnow(),
207
198
  )
208
- with Session(engine) as session:
199
+ with DBManager(api_keys).get_session() as session:
209
200
  logger.info(f"Saving {cache_entry} into database.")
210
201
  session.add(cache_entry)
211
202
  session.commit()
212
203
 
213
- engine.dispose()
214
204
  return computed_result
215
205
 
216
206
  return cast(FunctionT, wrapper)
@@ -230,60 +220,6 @@ def contains_pydantic_model(return_type: Any) -> bool:
230
220
  return False
231
221
 
232
222
 
233
- def json_serializer_default_fn(
234
- y: DatetimeUTC | timedelta | date | BaseModel,
235
- ) -> str | dict[str, Any]:
236
- """
237
- Used to serialize objects that don't support it by default into a specific string that can be deserialized out later.
238
- If this function returns a dictionary, it will be called recursivelly.
239
- If you add something here, also add it to `replace_custom_stringified_objects` below.
240
- """
241
- if isinstance(y, DatetimeUTC):
242
- return f"DatetimeUTC::{y.isoformat()}"
243
- elif isinstance(y, timedelta):
244
- return f"timedelta::{y.total_seconds()}"
245
- elif isinstance(y, date):
246
- return f"date::{y.isoformat()}"
247
- elif isinstance(y, BaseModel):
248
- return y.model_dump()
249
- raise TypeError(
250
- f"Unsuported type for the default json serialize function, value is {y}."
251
- )
252
-
253
-
254
- def json_serializer(x: Any) -> str:
255
- return json.dumps(x, default=json_serializer_default_fn)
256
-
257
-
258
- def replace_custom_stringified_objects(obj: Any) -> Any:
259
- """
260
- Used to deserialize objects from `json_serializer_default_fn` into their proper form.
261
- """
262
- if isinstance(obj, str):
263
- if obj.startswith("DatetimeUTC::"):
264
- iso_str = obj[len("DatetimeUTC::") :]
265
- return DatetimeUTC.to_datetime_utc(iso_str)
266
- elif obj.startswith("timedelta::"):
267
- total_seconds_str = obj[len("timedelta::") :]
268
- return timedelta(seconds=float(total_seconds_str))
269
- elif obj.startswith("date::"):
270
- iso_str = obj[len("date::") :]
271
- return date.fromisoformat(iso_str)
272
- else:
273
- return obj
274
- elif isinstance(obj, dict):
275
- return {k: replace_custom_stringified_objects(v) for k, v in obj.items()}
276
- elif isinstance(obj, list):
277
- return [replace_custom_stringified_objects(item) for item in obj]
278
- else:
279
- return obj
280
-
281
-
282
- def json_deserializer(s: str) -> Any:
283
- data = json.loads(s)
284
- return replace_custom_stringified_objects(data)
285
-
286
-
287
223
  def convert_cached_output_to_pydantic(return_type: Any, data: Any) -> Any:
288
224
  """
289
225
  Used to initialize Pydantic models from anything cached that was originally a Pydantic model in the output. Including models in nested structures.
@@ -1,5 +1,5 @@
1
1
  from functools import cache
2
- from typing import Any, Callable, TypeVar, cast
2
+ from typing import Any, Callable, TypeVar, cast, overload
3
3
 
4
4
  from joblib import Memory
5
5
 
@@ -7,11 +7,51 @@ from prediction_market_agent_tooling.config import APIKeys
7
7
 
8
8
  MEMORY = Memory(APIKeys().CACHE_DIR, verbose=0)
9
9
 
10
+
10
11
  T = TypeVar("T", bound=Callable[..., Any])
11
12
 
12
13
 
13
- def persistent_inmemory_cache(func: T) -> T:
14
+ @overload
15
+ def persistent_inmemory_cache(
16
+ func: None = None,
17
+ *,
18
+ in_memory_cache: bool = True,
19
+ ) -> Callable[[T], T]:
20
+ ...
21
+
22
+
23
+ @overload
24
+ def persistent_inmemory_cache(
25
+ func: T,
26
+ *,
27
+ in_memory_cache: bool = True,
28
+ ) -> T:
29
+ ...
30
+
31
+
32
+ def persistent_inmemory_cache(
33
+ func: T | None = None,
34
+ *,
35
+ in_memory_cache: bool = True,
36
+ ) -> T | Callable[[T], T]:
14
37
  """
15
- Wraps a function with both file cache (for persistent cache) and in-memory cache (for speed).
38
+ Wraps a function with both file cache (for persistent cache) and optional in-memory cache (for speed).
39
+ Can be used as @persistent_inmemory_cache or @persistent_inmemory_cache(in_memory_cache=False)
16
40
  """
17
- return cast(T, cache(MEMORY.cache(func)) if APIKeys().ENABLE_CACHE else func)
41
+ if func is None:
42
+ # Ugly Pythonic way to support this decorator as `@persistent_inmemory_cache` but also `@persistent_inmemory_cache(in_memory_cache=False)`
43
+ def decorator(func: T) -> T:
44
+ return persistent_inmemory_cache(
45
+ func,
46
+ in_memory_cache=in_memory_cache,
47
+ )
48
+
49
+ return decorator
50
+ else:
51
+ # The decorator is called without arguments.
52
+ if not APIKeys().ENABLE_CACHE:
53
+ return func
54
+ cached_func = MEMORY.cache(func)
55
+ if in_memory_cache:
56
+ cached_func = cache(cached_func)
57
+ return cast(T, cached_func)
@@ -0,0 +1,61 @@
1
+ import json
2
+ import typing as t
3
+ from datetime import date, timedelta
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
8
+
9
+
10
+ def json_serializer(x: t.Any) -> str:
11
+ return json.dumps(x, default=json_serializer_default_fn)
12
+
13
+
14
+ def json_serializer_default_fn(
15
+ y: DatetimeUTC | timedelta | date | BaseModel,
16
+ ) -> str | dict[str, t.Any]:
17
+ """
18
+ Used to serialize objects that don't support it by default into a specific string that can be deserialized out later.
19
+ If this function returns a dictionary, it will be called recursivelly.
20
+ If you add something here, also add it to `replace_custom_stringified_objects` below.
21
+ """
22
+ if isinstance(y, DatetimeUTC):
23
+ return f"DatetimeUTC::{y.isoformat()}"
24
+ elif isinstance(y, timedelta):
25
+ return f"timedelta::{y.total_seconds()}"
26
+ elif isinstance(y, date):
27
+ return f"date::{y.isoformat()}"
28
+ elif isinstance(y, BaseModel):
29
+ return y.model_dump()
30
+ raise TypeError(
31
+ f"Unsuported type for the default json serialize function, value is {y}."
32
+ )
33
+
34
+
35
+ def json_deserializer(s: str) -> t.Any:
36
+ data = json.loads(s)
37
+ return replace_custom_stringified_objects(data)
38
+
39
+
40
+ def replace_custom_stringified_objects(obj: t.Any) -> t.Any:
41
+ """
42
+ Used to deserialize objects from `json_serializer_default_fn` into their proper form.
43
+ """
44
+ if isinstance(obj, str):
45
+ if obj.startswith("DatetimeUTC::"):
46
+ iso_str = obj[len("DatetimeUTC::") :]
47
+ return DatetimeUTC.to_datetime_utc(iso_str)
48
+ elif obj.startswith("timedelta::"):
49
+ total_seconds_str = obj[len("timedelta::") :]
50
+ return timedelta(seconds=float(total_seconds_str))
51
+ elif obj.startswith("date::"):
52
+ iso_str = obj[len("date::") :]
53
+ return date.fromisoformat(iso_str)
54
+ else:
55
+ return obj
56
+ elif isinstance(obj, dict):
57
+ return {k: replace_custom_stringified_objects(v) for k, v in obj.items()}
58
+ elif isinstance(obj, list):
59
+ return [replace_custom_stringified_objects(item) for item in obj]
60
+ else:
61
+ return obj
@@ -0,0 +1,76 @@
1
+ import hashlib
2
+ from contextlib import contextmanager
3
+ from typing import Generator, Sequence
4
+
5
+ from sqlalchemy import Connection
6
+ from sqlmodel import Session, SQLModel, create_engine
7
+
8
+ from prediction_market_agent_tooling.config import APIKeys
9
+ from prediction_market_agent_tooling.tools.caches.serializers import (
10
+ json_deserializer,
11
+ json_serializer,
12
+ )
13
+
14
+
15
+ class DBManager:
16
+ _instances: dict[str, "DBManager"] = {}
17
+
18
+ def __new__(cls, api_keys: APIKeys | None = None) -> "DBManager":
19
+ sqlalchemy_db_url = (api_keys or APIKeys()).sqlalchemy_db_url
20
+ # Hash the secret value to not store secrets in plain text.
21
+ url_hash = hashlib.md5(
22
+ sqlalchemy_db_url.get_secret_value().encode()
23
+ ).hexdigest()
24
+ # Return singleton per database connection.
25
+ if url_hash not in cls._instances:
26
+ instance = super(DBManager, cls).__new__(cls)
27
+ cls._instances[url_hash] = instance
28
+ return cls._instances[url_hash]
29
+
30
+ def __init__(self, api_keys: APIKeys | None = None) -> None:
31
+ sqlalchemy_db_url = (api_keys or APIKeys()).sqlalchemy_db_url
32
+ self._engine = create_engine(
33
+ sqlalchemy_db_url.get_secret_value(),
34
+ json_serializer=json_serializer,
35
+ json_deserializer=json_deserializer,
36
+ pool_size=20,
37
+ pool_recycle=3600,
38
+ echo=True,
39
+ )
40
+ self.cache_table_initialized: dict[str, bool] = {}
41
+
42
+ @contextmanager
43
+ def get_session(self) -> Generator[Session, None, None]:
44
+ with Session(self._engine) as session:
45
+ yield session
46
+
47
+ @contextmanager
48
+ def get_connection(self) -> Generator[Connection, None, None]:
49
+ with self.get_session() as session:
50
+ yield session.connection()
51
+
52
+ def create_tables(
53
+ self, sqlmodel_tables: Sequence[type[SQLModel]] | None = None
54
+ ) -> None:
55
+ # Determine tables to create
56
+ if sqlmodel_tables is not None:
57
+ tables_to_create = []
58
+ for sqlmodel_table in sqlmodel_tables:
59
+ table_name = (
60
+ sqlmodel_table.__tablename__()
61
+ if callable(sqlmodel_table.__tablename__)
62
+ else sqlmodel_table.__tablename__
63
+ )
64
+ table = SQLModel.metadata.tables[table_name]
65
+ if not self.cache_table_initialized.get(table.name):
66
+ tables_to_create.append(table)
67
+ else:
68
+ tables_to_create = None
69
+
70
+ # Create tables in the database
71
+ SQLModel.metadata.create_all(self._engine, tables=tables_to_create)
72
+
73
+ # Update cache to mark tables as initialized
74
+ if tables_to_create:
75
+ for table in tables_to_create:
76
+ self.cache_table_initialized[table.name] = True
@@ -3,7 +3,10 @@ import hishel
3
3
 
4
4
  class HttpxCachedClient:
5
5
  def __init__(self) -> None:
6
- storage = hishel.FileStorage(ttl=3600, check_ttl_every=600)
6
+ storage = hishel.FileStorage(
7
+ ttl=24 * 60 * 60,
8
+ check_ttl_every=1 * 60 * 60,
9
+ )
7
10
  controller = hishel.Controller(force_cache=True)
8
11
  self.client = hishel.CacheClient(storage=storage, controller=controller)
9
12
 
@@ -63,6 +63,7 @@ def get_traces_for_agent(
63
63
  from_timestamp: DatetimeUTC,
64
64
  has_output: bool,
65
65
  client: Langfuse,
66
+ to_timestamp: DatetimeUTC | None = None,
66
67
  ) -> list[TraceWithDetails]:
67
68
  """
68
69
  Fetch agent traces using pagination
@@ -76,6 +77,7 @@ def get_traces_for_agent(
76
77
  limit=100,
77
78
  page=page,
78
79
  from_timestamp=from_timestamp,
80
+ to_timestamp=to_timestamp,
79
81
  )
80
82
  if not traces.data:
81
83
  break
@@ -1,10 +1,11 @@
1
1
  from datetime import datetime, timedelta
2
2
 
3
3
  from pydantic import ValidationError
4
- from sqlmodel import Field, Session, SQLModel, create_engine, desc, select
4
+ from sqlmodel import Field, SQLModel, desc, select
5
5
 
6
6
  from prediction_market_agent_tooling.config import APIKeys
7
7
  from prediction_market_agent_tooling.loggers import logger
8
+ from prediction_market_agent_tooling.tools.db.db_manager import DBManager
8
9
  from prediction_market_agent_tooling.tools.relevant_news_analysis.data_models import (
9
10
  NoRelevantNews,
10
11
  RelevantNews,
@@ -23,33 +24,22 @@ class RelevantNewsCacheModel(SQLModel, table=True):
23
24
 
24
25
 
25
26
  class RelevantNewsResponseCache:
26
- def __init__(self, sqlalchemy_db_url: str | None = None):
27
- self.engine = create_engine(
28
- (
29
- sqlalchemy_db_url
30
- if sqlalchemy_db_url
31
- else APIKeys().sqlalchemy_db_url.get_secret_value()
32
- ),
33
- pool_size=1,
34
- )
27
+ def __init__(self, api_keys: APIKeys | None = None):
28
+ self.db_manager = DBManager(api_keys)
35
29
  self._initialize_db()
36
30
 
37
31
  def _initialize_db(self) -> None:
38
32
  """
39
33
  Creates the tables if they don't exist
40
34
  """
41
- with self.engine.connect() as conn:
42
- SQLModel.metadata.create_all(
43
- conn,
44
- tables=[SQLModel.metadata.tables[RelevantNewsCacheModel.__tablename__]],
45
- )
35
+ self.db_manager.create_tables([RelevantNewsCacheModel])
46
36
 
47
37
  def find(
48
38
  self,
49
39
  question: str,
50
40
  days_ago: int,
51
41
  ) -> RelevantNews | NoRelevantNews | None:
52
- with Session(self.engine) as session:
42
+ with self.db_manager.get_session() as session:
53
43
  query = (
54
44
  select(RelevantNewsCacheModel)
55
45
  .where(RelevantNewsCacheModel.question == question)
@@ -82,7 +72,7 @@ class RelevantNewsResponseCache:
82
72
  days_ago: int,
83
73
  relevant_news: RelevantNews | None,
84
74
  ) -> None:
85
- with Session(self.engine) as session:
75
+ with self.db_manager.get_session() as session:
86
76
  cached = RelevantNewsCacheModel(
87
77
  question=question,
88
78
  days_ago=days_ago,
@@ -9,8 +9,8 @@ from prediction_market_agent_tooling.loggers import logger
9
9
 
10
10
  class TransactionBlockCache:
11
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")
12
+ self.block_number_cache = dc.Cache(".cache/block_cache_dir")
13
+ self.block_timestamp_cache = dc.Cache(".cache/timestamp_cache_dir")
14
14
  self.web3 = web3
15
15
 
16
16
  @tenacity.retry(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.57.0
3
+ Version: 0.57.2
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Provides-Extra: google
11
11
  Provides-Extra: langchain
12
12
  Provides-Extra: openai
13
+ Provides-Extra: optuna
13
14
  Requires-Dist: autoflake (>=2.2.1,<3.0.0)
14
15
  Requires-Dist: base58 (>=1.0.2,<2.0)
15
16
  Requires-Dist: cron-validator (>=1.0.8,<2.0.0)
@@ -30,6 +31,7 @@ Requires-Dist: loguru (>=0.7.2,<0.8.0)
30
31
  Requires-Dist: loky (>=3.4.1,<4.0.0)
31
32
  Requires-Dist: numpy (>=1.26.4,<2.0.0)
32
33
  Requires-Dist: openai (>=1.0.0,<2.0.0) ; extra == "openai"
34
+ Requires-Dist: optuna (>=4.1.0,<5.0.0) ; extra == "optuna"
33
35
  Requires-Dist: pinatapy-vourhey (>=0.2.0,<0.3.0)
34
36
  Requires-Dist: prompt-toolkit (>=3.0.43,<4.0.0)
35
37
  Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
@@ -16,7 +16,7 @@ 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=13qhBE68CZTnHYVDUxGs4yj_d75WM8QqJVg0a-wAoWA,7559
19
+ prediction_market_agent_tooling/config.py,sha256=Ffb1ftRiguAxa5cS0nXvKK01HomdqzFDCsp8rFd5taM,7553
20
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
@@ -31,9 +31,9 @@ prediction_market_agent_tooling/jobs/jobs_models.py,sha256=GOtsNm7URhzZM5fPY64r8
31
31
  prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=N0_jGDyXQeVXXlYg4oA_pOfqIjscHsLQbr0pBwFGoRo,5178
32
32
  prediction_market_agent_tooling/loggers.py,sha256=Am6HHXRNO545BO3l7Ue9Wb2TkYE1OK8KKhGbI3XypVU,3751
33
33
  prediction_market_agent_tooling/markets/agent_market.py,sha256=W2ME57-CSAhrt8qm8-b5r7yLq-Sk7R_BZMaApvjhrUE,12901
34
- prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=IxDTwX4tej9j5olNkXcLIE0RCF1Nh2icZQUT2ERMmZo,1937
34
+ prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=7RaYO_4qAmQ6ZGM8oPK2-CkiJfKmV9MxM-rJlduaecU,1971
35
35
  prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
36
- prediction_market_agent_tooling/markets/data_models.py,sha256=VoEWm338qQMQIRvWpWpDgDjVk9D_3tCHrZPd0Gy0dAA,4033
36
+ prediction_market_agent_tooling/markets/data_models.py,sha256=oMoHxxiNekGgXX1FFExS_MNuHjte2pV1OEynVDIKVc8,4368
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
39
  prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=eiGS4rEkxseZNpEb2BICKnjF0qqgkQTMuUPbSe7_04I,6059
@@ -45,11 +45,11 @@ 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=RM5EigkxBArGRAyolgqqHsdIap-FT3FfbvtlWZJwo10,27711
49
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=6zo8iBBLdI9g9keJ1dw9PXs4rf4V4tCW8D4e08f3g0Q,51349
50
- prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=Zq7SncCq-hvpgXKsVruGBGCn1OhKZTe7r1qLdCTrT2w,28297
48
+ prediction_market_agent_tooling/markets/omen/data_models.py,sha256=MBaWnUDGjYzbnDOHKPGrhAn0CP7iETVmsulRPqTf00I,28270
49
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=jrpfeMk8SUwk1p5QWCOm3Qt9HOHsux7DIBm77WRHqW4,51582
50
+ prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=baXJwk-jSI3-FV-k239oCNOA4oKz6LT47juX8AKpW3A,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=lG-2hCmmkjKHX1Ca-SipLBeETnTheXpjg5Qfed0tqFo,36506
52
+ prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=IsnWiVSmaR5I82D49MpJpRu7uE-wkMEgubsLzt0qZVI,38125
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
@@ -57,12 +57,12 @@ prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=NRoZK71P
57
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
+ prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py,sha256=fjIgjDIx5MhH5mwf7S0cspLOOSU3elYLhGYoIiM26mU,2746
61
61
  prediction_market_agent_tooling/monitor/markets/manifold.py,sha256=TS4ERwTfQnot8dhekNyVNhJYf5ysYsjF-9v5_kM3aVI,3334
62
62
  prediction_market_agent_tooling/monitor/markets/metaculus.py,sha256=LOnyWWBFdg10-cTWdb76nOsNjDloO8OfMT85GBzRCFI,1455
63
63
  prediction_market_agent_tooling/monitor/markets/omen.py,sha256=EqiJYTvDbSu7fBpbrBmCuf3fc6GHr4MxWrBGa69MIyc,3305
64
64
  prediction_market_agent_tooling/monitor/markets/polymarket.py,sha256=wak8o4BYaGbLpshQD12OrsqNABdanyID6ql95lEG2io,1870
65
- prediction_market_agent_tooling/monitor/monitor.py,sha256=-82COVrQwabUN7N-c8DbotRut9MbYcpfRGo6Cks92d0,14309
65
+ prediction_market_agent_tooling/monitor/monitor.py,sha256=4kMev1iGHQiEFslEqt5bWiXAyM_Qxb4rdMVKx2H9e1U,14235
66
66
  prediction_market_agent_tooling/monitor/monitor_app.py,sha256=zNHSwH_KEiv8aOwvfo1JrNuSFMefpzXPWtellhnJpTI,4775
67
67
  prediction_market_agent_tooling/monitor/monitor_settings.py,sha256=Xiozs3AsufuJ04JOe1vjUri-IAMWHjjmc2ugGGiHNH4,947
68
68
  prediction_market_agent_tooling/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -72,37 +72,39 @@ prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256
72
72
  prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py,sha256=-FUSuQQgjcWSSnoFxnlAyTeilY6raJABJVM2QKkFqAY,438
73
73
  prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py,sha256=THMXwFlskvzbjnX_OiYtDSzI8XVFyULWfP2525_9UGc,429
74
74
  prediction_market_agent_tooling/tools/betting_strategies/utils.py,sha256=kpIb-ci67Vc1Yqqaa-_S4OUkbhWSIYog4_Iwp69HU_k,97
75
- prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=yQ5sbccFf75jdj71zmlbJ8z5u2cfQzSRgY4haFG43I0,12712
76
- prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=tGHHd9HCiE_hCCtPtloHZQdDfBuiow9YsqJNYi2Tx_0,499
75
+ prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=rkA7somZZo5G6OZVvNrCuCVc4kn7TUoPxmOq4J6BZ54,10391
76
+ prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=ZW5iI5rmjqeAebu5T7ftRnlkxiL02IC-MxCfDB80x7w,1506
77
+ prediction_market_agent_tooling/tools/caches/serializers.py,sha256=upSXN5__rmRlzJ6tv1h7FodKzJu9eCkFrN_zeuwroJM,2151
77
78
  prediction_market_agent_tooling/tools/contract.py,sha256=Um_nQlpYJRBy4MnNR1CPKESehVwx_w35LwYX6T6py_0,20837
78
79
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
79
80
  prediction_market_agent_tooling/tools/custom_exceptions.py,sha256=Fh8z1fbwONvP4-j7AmV_PuEcoqb6-QXa9PJ9m7guMcM,93
80
81
  prediction_market_agent_tooling/tools/datetime_utc.py,sha256=2JSWF7AXQnv04_D_cu9Vmdkq0TWmGJ1QcK9AeqrA-U8,2765
82
+ prediction_market_agent_tooling/tools/db/db_manager.py,sha256=-WGYY-ZyqPveXb642gw4-Zh-RicMrBFgx7uqCB8TuDg,2801
81
83
  prediction_market_agent_tooling/tools/google_utils.py,sha256=t3_UEEvKX3L0biSIQ560GdRbllQ6eprhK_upE243A-0,3185
82
84
  prediction_market_agent_tooling/tools/hexbytes_custom.py,sha256=Bp94qgPjvjWf1Vb4lNzGFDXRdThw1rJ91vL6r2PWq5E,2096
83
- prediction_market_agent_tooling/tools/httpx_cached_client.py,sha256=0-N1r0zcGKlY9Rk-Ab8hbqwc54eMbsoa3jXL0_yCCiM,355
85
+ prediction_market_agent_tooling/tools/httpx_cached_client.py,sha256=RxD-hwtZCMctnMwfzy8t51W9Z9gxFGtDYxBIMChazpc,406
84
86
  prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
85
87
  prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=jgOow4zw6g8dIJrog6Tp-3NQs4wjUJ3VgVKyMQv-0QM,1286
86
88
  prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py,sha256=CTTMfTvs_8PH4kAtlQby2aeEKwgpmxtuGbd4oYIdJ2A,1201
87
89
  prediction_market_agent_tooling/tools/is_invalid.py,sha256=vuX0TKKhc_7iVnwAjXvfZeFwjr9nyYgREaYQDe-GYgA,5334
88
90
  prediction_market_agent_tooling/tools/is_predictable.py,sha256=dSuL8eLnGNKd_MQ3CaT1OuQkAykr3m5kn7f8HHKgDcM,6797
89
91
  prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
90
- prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=_m5SFnJJy4E0ubvY8ohzEEjES4nMJ_2zfa3wp4x3gSA,5817
92
+ prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=83T31s-YbsnNBLyYCjmBI2BBKUEqJUuYFa0uCdkoqy8,5901
91
93
  prediction_market_agent_tooling/tools/omen/reality_accuracy.py,sha256=M1SF7iSW1gVlQSTskdVFTn09uPLST23YeipVIWj54io,2236
92
94
  prediction_market_agent_tooling/tools/parallelism.py,sha256=6Gou0hbjtMZrYvxjTDFUDZuxmE2nqZVbb6hkg1hF82A,1022
93
95
  prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py,sha256=95l84aztFaxcRLLcRQ46yKJbIlOEuDAbIGLouyliDzA,1316
94
96
  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=Dcde74ZwoZuohhJ8gIO5GQkdAz1YrAKlvrM8CNgRqWw,3289
97
+ prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py,sha256=ErLp284YlD7k_M8Si9OT86HuzfmSo1bPlLJtegXx1TA,3011
96
98
  prediction_market_agent_tooling/tools/safe.py,sha256=9vxGGLvSPnfy-sxUFDpBTe8omqpGXP7MzvGPp6bRxrU,5197
97
99
  prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
98
100
  prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
99
101
  prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=5ldQs1pZe6uJ5eDAuP4OLpzmcqYShlIV67kttNFvGS0,342
100
102
  prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=Kw2mXNkMTYTEe1MBSTqhQmLoeXtgb6CkmHlcAJvhtqE,3809
101
- prediction_market_agent_tooling/tools/transaction_cache.py,sha256=C96OHkNqPJr_yDiHesSRgekeXIbX62qFl9qldArleuo,1800
103
+ prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
102
104
  prediction_market_agent_tooling/tools/utils.py,sha256=WvuUCHgMCiMq8_wMm5PHNwvLhcdDk2zGKaAM8OUC-qY,6438
103
105
  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,,
106
+ prediction_market_agent_tooling-0.57.2.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
107
+ prediction_market_agent_tooling-0.57.2.dist-info/METADATA,sha256=tzVRrpZtV9Jty0w9XLwxGagZiC3scgqPP6d2cBhqp1A,8188
108
+ prediction_market_agent_tooling-0.57.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
109
+ prediction_market_agent_tooling-0.57.2.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
110
+ prediction_market_agent_tooling-0.57.2.dist-info/RECORD,,