prediction-market-agent-tooling 0.49.1__py3-none-any.whl → 0.50.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 (43) hide show
  1. prediction_market_agent_tooling/benchmark/agents.py +6 -6
  2. prediction_market_agent_tooling/deploy/agent.py +5 -5
  3. prediction_market_agent_tooling/deploy/betting_strategy.py +83 -3
  4. prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +3 -4
  5. prediction_market_agent_tooling/gtypes.py +3 -2
  6. prediction_market_agent_tooling/jobs/jobs_models.py +3 -3
  7. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +2 -2
  8. prediction_market_agent_tooling/markets/agent_market.py +13 -17
  9. prediction_market_agent_tooling/markets/data_models.py +3 -3
  10. prediction_market_agent_tooling/markets/manifold/api.py +6 -6
  11. prediction_market_agent_tooling/markets/manifold/data_models.py +13 -23
  12. prediction_market_agent_tooling/markets/manifold/manifold.py +2 -2
  13. prediction_market_agent_tooling/markets/markets.py +7 -3
  14. prediction_market_agent_tooling/markets/metaculus/api.py +2 -3
  15. prediction_market_agent_tooling/markets/metaculus/data_models.py +11 -10
  16. prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -2
  17. prediction_market_agent_tooling/markets/omen/data_models.py +35 -22
  18. prediction_market_agent_tooling/markets/omen/omen.py +13 -9
  19. prediction_market_agent_tooling/markets/omen/omen_contracts.py +4 -2
  20. prediction_market_agent_tooling/markets/omen/omen_resolving.py +3 -4
  21. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +46 -33
  22. prediction_market_agent_tooling/markets/polymarket/api.py +1 -1
  23. prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -6
  24. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +14 -14
  25. prediction_market_agent_tooling/markets/polymarket/polymarket.py +2 -2
  26. prediction_market_agent_tooling/monitor/markets/manifold.py +2 -2
  27. prediction_market_agent_tooling/monitor/markets/metaculus.py +2 -2
  28. prediction_market_agent_tooling/monitor/markets/omen.py +2 -2
  29. prediction_market_agent_tooling/monitor/markets/polymarket.py +2 -2
  30. prediction_market_agent_tooling/monitor/monitor.py +5 -15
  31. prediction_market_agent_tooling/monitor/monitor_app.py +7 -8
  32. prediction_market_agent_tooling/tools/contract.py +3 -7
  33. prediction_market_agent_tooling/tools/datetime_utc.py +74 -0
  34. prediction_market_agent_tooling/tools/httpx_cached_client.py +11 -0
  35. prediction_market_agent_tooling/tools/is_predictable.py +1 -1
  36. prediction_market_agent_tooling/tools/langfuse_client_utils.py +16 -11
  37. prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +5 -5
  38. prediction_market_agent_tooling/tools/utils.py +28 -52
  39. {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/METADATA +5 -2
  40. {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/RECORD +43 -41
  41. {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/LICENSE +0 -0
  42. {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/WHEEL +0 -0
  43. {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  import typing as t
2
- from datetime import datetime
3
2
 
4
3
  from prediction_market_agent_tooling.markets.agent_market import (
5
4
  AgentMarket,
@@ -16,6 +15,7 @@ from prediction_market_agent_tooling.markets.polymarket.data_models import (
16
15
  from prediction_market_agent_tooling.markets.polymarket.data_models_web import (
17
16
  POLYMARKET_BASE_URL,
18
17
  )
18
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC
19
19
 
20
20
 
21
21
  class PolymarketAgentMarket(AgentMarket):
@@ -54,7 +54,7 @@ class PolymarketAgentMarket(AgentMarket):
54
54
  limit: int,
55
55
  sort_by: SortBy = SortBy.NONE,
56
56
  filter_by: FilterBy = FilterBy.OPEN,
57
- created_after: t.Optional[datetime] = None,
57
+ created_after: t.Optional[DatetimeUTC] = None,
58
58
  excluded_questions: set[str] | None = None,
59
59
  ) -> t.Sequence["PolymarketAgentMarket"]:
60
60
  if sort_by != SortBy.NONE:
@@ -15,7 +15,7 @@ from prediction_market_agent_tooling.monitor.monitor import (
15
15
  DeployedAgent,
16
16
  KubernetesCronJob,
17
17
  )
18
- from prediction_market_agent_tooling.tools.utils import DatetimeWithTimezone
18
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC
19
19
 
20
20
 
21
21
  class DeployedManifoldAgent(DeployedAgent):
@@ -57,7 +57,7 @@ class DeployedManifoldAgent(DeployedAgent):
57
57
  @staticmethod
58
58
  def from_api_keys(
59
59
  name: str,
60
- start_time: DatetimeWithTimezone,
60
+ start_time: DatetimeUTC,
61
61
  api_keys: APIKeys,
62
62
  ) -> "DeployedManifoldAgent":
63
63
  return DeployedManifoldAgent(
@@ -4,7 +4,7 @@ from google.cloud.functions_v2.types.functions import Function
4
4
 
5
5
  from prediction_market_agent_tooling.config import APIKeys
6
6
  from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
7
- from prediction_market_agent_tooling.gtypes import DatetimeWithTimezone
7
+ from prediction_market_agent_tooling.gtypes import DatetimeUTC
8
8
  from prediction_market_agent_tooling.markets.data_models import ResolvedBet
9
9
  from prediction_market_agent_tooling.markets.markets import MarketType
10
10
  from prediction_market_agent_tooling.monitor.monitor import DeployedAgent
@@ -23,7 +23,7 @@ class DeployedMetaculusAgent(DeployedAgent):
23
23
  @staticmethod
24
24
  def from_api_keys(
25
25
  name: str,
26
- start_time: DatetimeWithTimezone,
26
+ start_time: DatetimeUTC,
27
27
  api_keys: APIKeys,
28
28
  ) -> "DeployedMetaculusAgent":
29
29
  return DeployedMetaculusAgent(
@@ -4,7 +4,7 @@ from google.cloud.functions_v2.types.functions import Function
4
4
 
5
5
  from prediction_market_agent_tooling.config import APIKeys
6
6
  from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
7
- from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeWithTimezone
7
+ from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeUTC
8
8
  from prediction_market_agent_tooling.markets.data_models import ResolvedBet
9
9
  from prediction_market_agent_tooling.markets.markets import MarketType
10
10
  from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
@@ -57,7 +57,7 @@ class DeployedOmenAgent(DeployedAgent):
57
57
  @staticmethod
58
58
  def from_api_keys(
59
59
  name: str,
60
- start_time: DatetimeWithTimezone,
60
+ start_time: DatetimeUTC,
61
61
  api_keys: APIKeys,
62
62
  ) -> "DeployedOmenAgent":
63
63
  return DeployedOmenAgent(
@@ -4,7 +4,7 @@ from google.cloud.functions_v2.types.functions import Function
4
4
 
5
5
  from prediction_market_agent_tooling.config import APIKeys
6
6
  from prediction_market_agent_tooling.deploy.constants import MARKET_TYPE_KEY
7
- from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeWithTimezone
7
+ from prediction_market_agent_tooling.gtypes import ChecksumAddress, DatetimeUTC
8
8
  from prediction_market_agent_tooling.markets.data_models import ResolvedBet
9
9
  from prediction_market_agent_tooling.markets.markets import MarketType
10
10
  from prediction_market_agent_tooling.monitor.monitor import DeployedAgent
@@ -25,7 +25,7 @@ class DeployedPolymarketAgent(DeployedAgent):
25
25
  @staticmethod
26
26
  def from_api_keys(
27
27
  name: str,
28
- start_time: DatetimeWithTimezone,
28
+ start_time: DatetimeUTC,
29
29
  api_keys: APIKeys,
30
30
  ) -> "DeployedPolymarketAgent":
31
31
  return DeployedPolymarketAgent(
@@ -8,7 +8,7 @@ import numpy as np
8
8
  import pandas as pd
9
9
  import streamlit as st
10
10
  from google.cloud.functions_v2.types.functions import Function
11
- from pydantic import BaseModel, field_validator
11
+ from pydantic import BaseModel
12
12
 
13
13
  from prediction_market_agent_tooling.config import APIKeys
14
14
  from prediction_market_agent_tooling.deploy.gcp.kubernetes_models import (
@@ -26,9 +26,8 @@ from prediction_market_agent_tooling.markets.agent_market import AgentMarket
26
26
  from prediction_market_agent_tooling.markets.data_models import Resolution, ResolvedBet
27
27
  from prediction_market_agent_tooling.tools.parallelism import par_map
28
28
  from prediction_market_agent_tooling.tools.utils import (
29
- DatetimeWithTimezone,
29
+ DatetimeUTC,
30
30
  check_not_none,
31
- convert_to_utc_datetime,
32
31
  should_not_happen,
33
32
  )
34
33
 
@@ -40,21 +39,12 @@ class DeployedAgent(BaseModel):
40
39
 
41
40
  name: str
42
41
 
43
- start_time: DatetimeWithTimezone
44
- end_time: t.Optional[
45
- DatetimeWithTimezone
46
- ] = None # TODO: If we want end time, we need to store agents somewhere, not just query them from functions.
42
+ start_time: DatetimeUTC
43
+ end_time: DatetimeUTC | None = None # TODO: If we want end time, we need to store agents somewhere, not just query them from functions.
47
44
 
48
45
  raw_labels: dict[str, str] | None = None
49
46
  raw_env_vars: dict[str, str] | None = None
50
47
 
51
- _add_timezone_validator_start_time = field_validator("start_time")(
52
- convert_to_utc_datetime
53
- )
54
- _add_timezone_validator_end_time = field_validator("end_time")(
55
- convert_to_utc_datetime
56
- )
57
-
58
48
  def model_dump_prefixed(self) -> dict[str, t.Any]:
59
49
  return {
60
50
  self.PREFIX + k: v for k, v in self.model_dump().items() if v is not None
@@ -93,7 +83,7 @@ class DeployedAgent(BaseModel):
93
83
  @staticmethod
94
84
  def from_api_keys(
95
85
  name: str,
96
- start_time: DatetimeWithTimezone,
86
+ start_time: DatetimeUTC,
97
87
  api_keys: APIKeys,
98
88
  ) -> "DeployedAgent":
99
89
  raise NotImplementedError("Subclasses must implement this method.")
@@ -1,7 +1,6 @@
1
1
  import typing as t
2
2
  from datetime import date, datetime, timedelta
3
3
 
4
- import pytz
5
4
  import streamlit as st
6
5
 
7
6
  from prediction_market_agent_tooling.markets.agent_market import (
@@ -27,9 +26,9 @@ from prediction_market_agent_tooling.monitor.monitor import (
27
26
  )
28
27
  from prediction_market_agent_tooling.monitor.monitor_settings import MonitorSettings
29
28
  from prediction_market_agent_tooling.tools.utils import (
30
- DatetimeWithTimezone,
29
+ DatetimeUTC,
31
30
  check_not_none,
32
- convert_to_utc_datetime,
31
+ utc_datetime,
33
32
  utcnow,
34
33
  )
35
34
 
@@ -46,7 +45,7 @@ MARKET_TYPE_TO_DEPLOYED_AGENT: dict[MarketType, type[DeployedAgent]] = {
46
45
  def get_deployed_agents(
47
46
  market_type: MarketType,
48
47
  settings: MonitorSettings,
49
- start_time: DatetimeWithTimezone | None,
48
+ start_time: DatetimeUTC | None,
50
49
  ) -> list[DeployedAgent]:
51
50
  cls = MARKET_TYPE_TO_DEPLOYED_AGENT.get(market_type)
52
51
  if cls is None:
@@ -76,7 +75,7 @@ def get_deployed_agents(
76
75
 
77
76
 
78
77
  def get_open_and_resolved_markets(
79
- start_time: datetime,
78
+ start_time: DatetimeUTC,
80
79
  market_type: MarketType,
81
80
  ) -> tuple[t.Sequence[AgentMarket], t.Sequence[AgentMarket]]:
82
81
  cls = market_type.market_class
@@ -103,8 +102,8 @@ def monitor_app(
103
102
  market_type: MarketType = check_not_none(
104
103
  st.selectbox(label="Market type", options=enabled_market_types, index=0)
105
104
  )
106
- start_time: DatetimeWithTimezone | None = (
107
- convert_to_utc_datetime(
105
+ start_time: DatetimeUTC | None = (
106
+ DatetimeUTC.from_datetime(
108
107
  datetime.combine(
109
108
  t.cast(
110
109
  # This will be always a date for us, so casting.
@@ -131,7 +130,7 @@ def monitor_app(
131
130
  oldest_start_time = (
132
131
  min(agent.start_time for agent in agents)
133
132
  if agents
134
- else datetime(2020, 1, 1, tzinfo=pytz.UTC)
133
+ else utc_datetime(2020, 1, 1)
135
134
  )
136
135
 
137
136
  st.header("Market Info")
@@ -22,11 +22,7 @@ from prediction_market_agent_tooling.tools.gnosis_rpc import (
22
22
  GNOSIS_NETWORK_ID,
23
23
  GNOSIS_RPC_URL,
24
24
  )
25
- from prediction_market_agent_tooling.tools.utils import (
26
- DatetimeWithTimezone,
27
- should_not_happen,
28
- utc_timestamp_to_utc_datetime,
29
- )
25
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
30
26
  from prediction_market_agent_tooling.tools.web3_utils import (
31
27
  call_function_on_contract,
32
28
  send_function_on_contract_tx,
@@ -457,8 +453,8 @@ class DebuggingContract(ContractOnGnosisChain):
457
453
  def get_now(
458
454
  self,
459
455
  web3: Web3 | None = None,
460
- ) -> DatetimeWithTimezone:
461
- return utc_timestamp_to_utc_datetime(self.getNow(web3))
456
+ ) -> DatetimeUTC:
457
+ return DatetimeUTC.to_datetime_utc(self.getNow(web3))
462
458
 
463
459
  def inc(
464
460
  self,
@@ -0,0 +1,74 @@
1
+ import typing as t
2
+ from datetime import datetime, timedelta
3
+
4
+ import pytz
5
+ from dateutil import parser
6
+ from pydantic import GetCoreSchemaHandler
7
+ from pydantic_core import CoreSchema, core_schema
8
+
9
+ from prediction_market_agent_tooling.loggers import logger
10
+
11
+
12
+ class DatetimeUTC(datetime):
13
+ """
14
+ As a subclass of `datetime` instead of `NewType` because otherwise it doesn't work with issubclass command which is required for SQLModel/Pydantic.
15
+ """
16
+
17
+ def __new__(cls, *args, **kwargs) -> "DatetimeUTC": # type: ignore[no-untyped-def] # Pickling doesn't work if I copy-paste arguments from datetime's __new__.
18
+ if len(args) >= 8:
19
+ # Start of Selection
20
+ args = args[:7] + (pytz.UTC,) + args[8:]
21
+ else:
22
+ kwargs["tzinfo"] = pytz.UTC
23
+ return super().__new__(cls, *args, **kwargs)
24
+
25
+ @classmethod
26
+ def _validate(cls, value: t.Any) -> "DatetimeUTC":
27
+ if not isinstance(value, (datetime, int, str)):
28
+ raise TypeError(
29
+ f"Expected datetime, timestamp or string, got {type(value)}"
30
+ )
31
+ return cls.to_datetime_utc(value)
32
+
33
+ @classmethod
34
+ def __get_pydantic_core_schema__(
35
+ cls, source_type: t.Any, handler: GetCoreSchemaHandler
36
+ ) -> CoreSchema:
37
+ dt_schema = handler(datetime)
38
+ return core_schema.no_info_after_validator_function(cls._validate, dt_schema)
39
+
40
+ @staticmethod
41
+ def from_datetime(dt: datetime) -> "DatetimeUTC":
42
+ """
43
+ Converts a datetime object to DatetimeUTC, ensuring it is timezone-aware in UTC.
44
+ """
45
+ if dt.tzinfo is None:
46
+ logger.warning(
47
+ f"tzinfo not provided, assuming the timezone of {dt=} is UTC."
48
+ )
49
+ dt = dt.replace(tzinfo=pytz.UTC)
50
+ else:
51
+ dt = dt.astimezone(pytz.UTC)
52
+ return DatetimeUTC(
53
+ dt.year,
54
+ dt.month,
55
+ dt.day,
56
+ dt.hour,
57
+ dt.minute,
58
+ dt.second,
59
+ dt.microsecond,
60
+ tzinfo=pytz.UTC,
61
+ )
62
+
63
+ @staticmethod
64
+ def to_datetime_utc(value: datetime | int | str) -> "DatetimeUTC":
65
+ if isinstance(value, int):
66
+ # Divide by 1000 if the timestamp is assumed to be in miliseconds (if not, 1e11 would be year 5138).
67
+ value = int(value / 1000) if value > 1e11 else value
68
+ # In the past, we had bugged data where timestamp was huge and Python errored out.
69
+ max_timestamp = int((datetime.max - timedelta(days=1)).timestamp())
70
+ value = min(value, max_timestamp)
71
+ value = datetime.fromtimestamp(value, tz=pytz.UTC)
72
+ elif isinstance(value, str):
73
+ value = parser.parse(value)
74
+ return DatetimeUTC.from_datetime(value)
@@ -0,0 +1,11 @@
1
+ import hishel
2
+
3
+
4
+ class HttpxCachedClient:
5
+ def __init__(self) -> None:
6
+ storage = hishel.FileStorage(ttl=3600, check_ttl_every=600)
7
+ controller = hishel.Controller(force_cache=True)
8
+ self.client = hishel.CacheClient(storage=storage, controller=controller)
9
+
10
+ def get_client(self) -> hishel.CacheClient:
11
+ return self.client
@@ -1,7 +1,7 @@
1
1
  import tenacity
2
- from loguru import logger
3
2
 
4
3
  from prediction_market_agent_tooling.config import APIKeys
4
+ from prediction_market_agent_tooling.loggers import logger
5
5
  from prediction_market_agent_tooling.tools.cache import persistent_inmemory_cache
6
6
  from prediction_market_agent_tooling.tools.langfuse_ import (
7
7
  get_langfuse_langchain_config,
@@ -1,5 +1,4 @@
1
1
  import typing as t
2
- from datetime import datetime
3
2
 
4
3
  import numpy as np
5
4
  from langfuse import Langfuse
@@ -14,15 +13,19 @@ from prediction_market_agent_tooling.markets.data_models import (
14
13
  TradeType,
15
14
  )
16
15
  from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
17
- from prediction_market_agent_tooling.tools.utils import convert_to_utc_datetime
16
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC
18
17
 
19
18
 
20
19
  class ProcessMarketTrace(BaseModel):
21
- timestamp: datetime
20
+ timestamp: int
22
21
  market: OmenAgentMarket
23
22
  answer: ProbabilisticAnswer
24
23
  trades: list[PlacedTrade]
25
24
 
25
+ @property
26
+ def timestamp_datetime(self) -> DatetimeUTC:
27
+ return DatetimeUTC.to_datetime_utc(self.timestamp)
28
+
26
29
  @property
27
30
  def buy_trade(self) -> PlacedTrade | None:
28
31
  buy_trades = [t for t in self.trades if t.trade_type == TradeType.BUY]
@@ -45,7 +48,7 @@ class ProcessMarketTrace(BaseModel):
45
48
  market=market,
46
49
  answer=answer,
47
50
  trades=trades,
48
- timestamp=trace.timestamp,
51
+ timestamp=int(trace.timestamp.timestamp()),
49
52
  )
50
53
 
51
54
 
@@ -57,7 +60,7 @@ class ResolvedBetWithTrace(BaseModel):
57
60
  def get_traces_for_agent(
58
61
  agent_name: str,
59
62
  trace_name: str,
60
- from_timestamp: datetime,
63
+ from_timestamp: DatetimeUTC,
61
64
  has_output: bool,
62
65
  client: Langfuse,
63
66
  ) -> list[TraceWithDetails]:
@@ -115,7 +118,7 @@ def trace_to_trades(trace: TraceWithDetails) -> list[PlacedTrade]:
115
118
 
116
119
 
117
120
  def get_closest_datetime_from_list(
118
- ref_datetime: datetime, datetimes: list[datetime]
121
+ ref_datetime: DatetimeUTC, datetimes: list[DatetimeUTC]
119
122
  ) -> int:
120
123
  """Get the index of the closest datetime to the reference datetime"""
121
124
  if len(datetimes) == 1:
@@ -149,20 +152,22 @@ def get_trace_for_bet(
149
152
  else:
150
153
  # In-case there are multiple traces for the same market, get the closest
151
154
  # trace to the bet
152
- bet_timestamp = convert_to_utc_datetime(bet.created_time)
153
155
  closest_trace_index = get_closest_datetime_from_list(
154
- bet_timestamp,
155
- [t.timestamp for t in traces_for_bet],
156
+ bet.created_time,
157
+ [t.timestamp_datetime for t in traces_for_bet],
156
158
  )
157
159
 
158
160
  # Sanity check: Let's say the upper bound for time between
159
161
  # `agent.process_market` being called and the bet being placed is 20
160
162
  # minutes
161
163
  candidate_trace = traces_for_bet[closest_trace_index]
162
- if abs(candidate_trace.timestamp - bet_timestamp).total_seconds() > 1200:
164
+ if (
165
+ abs(candidate_trace.timestamp_datetime - bet.created_time).total_seconds()
166
+ > 1200
167
+ ):
163
168
  logger.info(
164
169
  f"Closest trace to bet has timestamp {candidate_trace.timestamp}, "
165
- f"but bet was created at {bet_timestamp}. Not matching"
170
+ f"but bet was created at {bet.created_time}. Not matching"
166
171
  )
167
172
  return None
168
173
 
@@ -1,8 +1,7 @@
1
1
  import typing as t
2
- from datetime import datetime, timedelta
2
+ from datetime import timedelta
3
3
 
4
4
  import tenacity
5
- from loguru import logger
6
5
  from pydantic import BaseModel
7
6
  from sqlalchemy import Column
8
7
  from sqlalchemy.dialects.postgresql import JSONB
@@ -18,7 +17,8 @@ from sqlmodel import (
18
17
  )
19
18
 
20
19
  from prediction_market_agent_tooling.config import APIKeys
21
- from prediction_market_agent_tooling.tools.utils import utcnow
20
+ from prediction_market_agent_tooling.loggers import logger
21
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC, utcnow
22
22
 
23
23
 
24
24
  class TavilyResult(BaseModel):
@@ -59,7 +59,7 @@ class TavilyResponseModel(SQLModel, table=True):
59
59
  include_images: bool
60
60
  use_cache: bool
61
61
  # Datetime at the time of search response and response from the search
62
- datetime_: datetime = Field(index=True, nullable=False)
62
+ datetime_: DatetimeUTC = Field(index=True, nullable=False)
63
63
  response: dict[str, t.Any] = Field(sa_column=Column(JSONB, nullable=False))
64
64
 
65
65
  @staticmethod
@@ -89,7 +89,7 @@ class TavilyResponseModel(SQLModel, table=True):
89
89
  include_raw_content=include_raw_content,
90
90
  include_images=include_images,
91
91
  use_cache=use_cache,
92
- datetime_=datetime.now(),
92
+ datetime_=utcnow(),
93
93
  response=response.model_dump(),
94
94
  )
95
95
 
@@ -1,20 +1,18 @@
1
1
  import json
2
2
  import os
3
3
  import subprocess
4
- import typing as t
5
4
  from datetime import datetime
6
- from typing import Any, NoReturn, Optional, Type, TypeVar, cast
5
+ from typing import Any, NoReturn, Optional, Type, TypeVar
7
6
 
8
7
  import pytz
9
8
  import requests
10
9
  from google.cloud import secretmanager
11
10
  from pydantic import BaseModel, ValidationError
12
- from pydantic.functional_validators import BeforeValidator
13
11
  from scipy.optimize import newton
14
12
  from scipy.stats import entropy
15
13
 
16
14
  from prediction_market_agent_tooling.gtypes import (
17
- DatetimeWithTimezone,
15
+ DatetimeUTC,
18
16
  PrivateKey,
19
17
  Probability,
20
18
  SecretStr,
@@ -85,55 +83,33 @@ def export_requirements_from_toml(output_dir: str) -> None:
85
83
  logger.debug(f"Saved requirements to {output_dir}/requirements.txt")
86
84
 
87
85
 
88
- @t.overload
89
- def convert_to_utc_datetime(value: datetime) -> DatetimeWithTimezone:
90
- ...
91
-
92
-
93
- @t.overload
94
- def convert_to_utc_datetime(value: None) -> None:
95
- ...
96
-
97
-
98
- def convert_to_utc_datetime(value: datetime | None) -> DatetimeWithTimezone | None:
99
- """
100
- If datetime doesn't come with a timezone, we assume it to be UTC.
101
- Note: Not great, but at least the error will be constant.
102
- """
103
- if value is None:
104
- return None
105
- if value.tzinfo is None:
106
- value = value.replace(tzinfo=pytz.UTC)
107
- if value.tzinfo != pytz.UTC:
108
- value = value.astimezone(pytz.UTC)
109
- return cast(DatetimeWithTimezone, value)
110
-
111
-
112
- @t.overload
113
- def utc_timestamp_to_utc_datetime(ts: int) -> DatetimeWithTimezone:
114
- ...
115
-
116
-
117
- @t.overload
118
- def utc_timestamp_to_utc_datetime(ts: None) -> None:
119
- ...
120
-
121
-
122
- def utc_timestamp_to_utc_datetime(ts: int | None) -> DatetimeWithTimezone | None:
123
- return (
124
- convert_to_utc_datetime(datetime.fromtimestamp(ts, tz=pytz.UTC))
125
- if ts is not None
126
- else None
86
+ def utcnow() -> DatetimeUTC:
87
+ return DatetimeUTC.from_datetime(datetime.now(pytz.UTC))
88
+
89
+
90
+ def utc_datetime(
91
+ year: int,
92
+ month: int,
93
+ day: int,
94
+ hour: int = 0,
95
+ minute: int = 0,
96
+ second: int = 0,
97
+ microsecond: int = 0,
98
+ *,
99
+ fold: int = 0,
100
+ ) -> DatetimeUTC:
101
+ dt = datetime(
102
+ year=year,
103
+ month=month,
104
+ day=day,
105
+ hour=hour,
106
+ minute=minute,
107
+ second=second,
108
+ microsecond=microsecond,
109
+ tzinfo=pytz.UTC,
110
+ fold=fold,
127
111
  )
128
-
129
-
130
- UTCDatetimeFromUTCTimestamp = t.Annotated[
131
- datetime, BeforeValidator(utc_timestamp_to_utc_datetime)
132
- ]
133
-
134
-
135
- def utcnow() -> DatetimeWithTimezone:
136
- return convert_to_utc_datetime(datetime.now(pytz.UTC))
112
+ return DatetimeUTC.from_datetime(dt)
137
113
 
138
114
 
139
115
  def get_current_git_commit_sha() -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.49.1
3
+ Version: 0.50.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
@@ -20,6 +20,7 @@ Requires-Dist: google-api-python-client (==2.95.0) ; extra == "google"
20
20
  Requires-Dist: google-cloud-functions (>=1.16.0,<2.0.0)
21
21
  Requires-Dist: google-cloud-resource-manager (>=1.12.0,<2.0.0)
22
22
  Requires-Dist: google-cloud-secret-manager (>=2.18.2,<3.0.0)
23
+ Requires-Dist: hishel (>=0.0.31,<0.0.32)
23
24
  Requires-Dist: isort (>=5.13.2,<6.0.0)
24
25
  Requires-Dist: langchain (>=0.2.6,<0.3.0) ; extra == "langchain"
25
26
  Requires-Dist: langchain-community (>=0.0.19)
@@ -35,16 +36,18 @@ Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
35
36
  Requires-Dist: pydantic (>=2.6.1,<3.0.0)
36
37
  Requires-Dist: pydantic-settings (>=2.4.0,<3.0.0)
37
38
  Requires-Dist: pymongo (>=4.8.0,<5.0.0)
39
+ Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
38
40
  Requires-Dist: safe-cli (>=1.0.0,<2.0.0)
39
41
  Requires-Dist: safe-eth-py (>=6.0.0b14,<7.0.0)
40
42
  Requires-Dist: scikit-learn (>=1.3.1,<2.0.0)
41
- Requires-Dist: sqlmodel (>=0.0.21,<0.0.22)
43
+ Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
42
44
  Requires-Dist: streamlit (>=1.31.0,<2.0.0)
43
45
  Requires-Dist: subgrounds (>=1.9.1,<2.0.0)
44
46
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
45
47
  Requires-Dist: tavily-python (>=0.3.9,<0.4.0)
46
48
  Requires-Dist: tqdm (>=4.66.2,<5.0.0)
47
49
  Requires-Dist: typer (>=0.9.0,<1.0.0)
50
+ Requires-Dist: types-python-dateutil (>=2.9.0.20240906,<3.0.0.0)
48
51
  Requires-Dist: types-pytz (>=2024.1.0.20240203,<2025.0.0.0)
49
52
  Requires-Dist: types-requests (>=2.31.0.0,<3.0.0.0)
50
53
  Requires-Dist: web3 (>=6.15.1,<7.0.0)