prediction-market-agent-tooling 0.55.0__py3-none-any.whl → 0.55.1__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.
- prediction_market_agent_tooling/config.py +3 -3
- prediction_market_agent_tooling/deploy/agent.py +10 -4
- prediction_market_agent_tooling/deploy/trade_interval.py +46 -0
- prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py +44 -0
- prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py +162 -0
- prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py +90 -0
- prediction_market_agent_tooling/tools/safe.py +6 -6
- prediction_market_agent_tooling/tools/tavily/tavily_search.py +1 -1
- prediction_market_agent_tooling/tools/web3_utils.py +4 -4
- {prediction_market_agent_tooling-0.55.0.dist-info → prediction_market_agent_tooling-0.55.1.dist-info}/METADATA +2 -2
- {prediction_market_agent_tooling-0.55.0.dist-info → prediction_market_agent_tooling-0.55.1.dist-info}/RECORD +14 -10
- {prediction_market_agent_tooling-0.55.0.dist-info → prediction_market_agent_tooling-0.55.1.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.55.0.dist-info → prediction_market_agent_tooling-0.55.1.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.55.0.dist-info → prediction_market_agent_tooling-0.55.1.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,10 @@
|
|
1
1
|
import typing as t
|
2
2
|
|
3
|
-
from gnosis.eth import EthereumClient
|
4
|
-
from gnosis.safe import Safe
|
5
3
|
from pydantic.types import SecretStr
|
6
4
|
from pydantic.v1.types import SecretStr as SecretStrV1
|
7
5
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
6
|
+
from safe_eth.eth import EthereumClient
|
7
|
+
from safe_eth.safe.safe import SafeV141
|
8
8
|
|
9
9
|
from prediction_market_agent_tooling.gtypes import (
|
10
10
|
ChecksumAddress,
|
@@ -200,6 +200,6 @@ class APIKeys(BaseSettings):
|
|
200
200
|
if not self.SAFE_ADDRESS:
|
201
201
|
raise ValueError("Cannot check ownership if safe_address is not defined.")
|
202
202
|
|
203
|
-
s =
|
203
|
+
s = SafeV141(self.SAFE_ADDRESS, ethereum_client)
|
204
204
|
public_key_from_signer = private_key_to_public_key(self.bet_from_private_key)
|
205
205
|
return s.retrieve_is_owner(public_key_from_signer)
|
@@ -30,6 +30,10 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
|
|
30
30
|
gcp_function_is_active,
|
31
31
|
gcp_resolve_api_keys_secrets,
|
32
32
|
)
|
33
|
+
from prediction_market_agent_tooling.deploy.trade_interval import (
|
34
|
+
FixedInterval,
|
35
|
+
TradeInterval,
|
36
|
+
)
|
33
37
|
from prediction_market_agent_tooling.gtypes import xDai, xdai_type
|
34
38
|
from prediction_market_agent_tooling.loggers import logger
|
35
39
|
from prediction_market_agent_tooling.markets.agent_market import (
|
@@ -279,9 +283,10 @@ def {entrypoint_function_name}(request) -> str:
|
|
279
283
|
|
280
284
|
class DeployablePredictionAgent(DeployableAgent):
|
281
285
|
bet_on_n_markets_per_run: int = 1
|
286
|
+
n_markets_to_fetch: int = MAX_AVAILABLE_MARKETS
|
282
287
|
min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
|
283
288
|
allow_invalid_questions: bool = False
|
284
|
-
|
289
|
+
same_market_trade_interval: TradeInterval = FixedInterval(timedelta(hours=24))
|
285
290
|
# Only Metaculus allows to post predictions without trading (buying/selling of outcome tokens).
|
286
291
|
supported_markets: t.Sequence[MarketType] = [MarketType.METACULUS]
|
287
292
|
|
@@ -345,7 +350,9 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
345
350
|
Subclasses can implement their own logic instead of this one, or on top of this one.
|
346
351
|
By default, it allows only markets where user didn't bet recently and it's a reasonable question.
|
347
352
|
"""
|
348
|
-
if self.have_bet_on_market_since(
|
353
|
+
if self.have_bet_on_market_since(
|
354
|
+
market, since=self.same_market_trade_interval.get(market=market)
|
355
|
+
):
|
349
356
|
return False
|
350
357
|
|
351
358
|
# Manifold allows to bet only on markets with probability between 1 and 99.
|
@@ -370,14 +377,13 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
370
377
|
def get_markets(
|
371
378
|
self,
|
372
379
|
market_type: MarketType,
|
373
|
-
limit: int = MAX_AVAILABLE_MARKETS,
|
374
380
|
sort_by: SortBy = SortBy.CLOSING_SOONEST,
|
375
381
|
filter_by: FilterBy = FilterBy.OPEN,
|
376
382
|
) -> t.Sequence[AgentMarket]:
|
377
383
|
cls = market_type.market_class
|
378
384
|
# Fetch the soonest closing markets to choose from
|
379
385
|
available_markets = cls.get_binary_markets(
|
380
|
-
limit=
|
386
|
+
limit=self.n_markets_to_fetch, sort_by=sort_by, filter_by=filter_by
|
381
387
|
)
|
382
388
|
return available_markets
|
383
389
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from datetime import timedelta
|
3
|
+
|
4
|
+
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
5
|
+
from prediction_market_agent_tooling.tools.utils import check_not_none
|
6
|
+
|
7
|
+
|
8
|
+
class TradeInterval(ABC):
|
9
|
+
@abstractmethod
|
10
|
+
def get(
|
11
|
+
self,
|
12
|
+
market: AgentMarket,
|
13
|
+
) -> timedelta:
|
14
|
+
raise NotImplementedError("Subclass should implement this.")
|
15
|
+
|
16
|
+
|
17
|
+
class FixedInterval(TradeInterval):
|
18
|
+
"""
|
19
|
+
For trades at a fixed interval.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, interval: timedelta):
|
23
|
+
self.interval = interval
|
24
|
+
|
25
|
+
def get(
|
26
|
+
self,
|
27
|
+
market: AgentMarket,
|
28
|
+
) -> timedelta:
|
29
|
+
return self.interval
|
30
|
+
|
31
|
+
|
32
|
+
class MarketLifetimeProportionalInterval(TradeInterval):
|
33
|
+
"""
|
34
|
+
For uniformly distributed trades over the market's lifetime.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self, max_trades: int):
|
38
|
+
self.max_trades = max_trades
|
39
|
+
|
40
|
+
def get(
|
41
|
+
self,
|
42
|
+
market: AgentMarket,
|
43
|
+
) -> timedelta:
|
44
|
+
created_time = check_not_none(market.created_time)
|
45
|
+
close_time = check_not_none(market.close_time)
|
46
|
+
return (close_time - created_time) / self.max_trades
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
|
3
|
+
from prediction_market_agent_tooling.tools.tavily.tavily_models import TavilyResult
|
4
|
+
|
5
|
+
|
6
|
+
class RelevantNewsAnalysis(BaseModel):
|
7
|
+
reasoning: str = Field(
|
8
|
+
...,
|
9
|
+
description="The reason why the news contains information relevant to the given question. Or if no news is relevant, why not.",
|
10
|
+
)
|
11
|
+
contains_relevant_news: bool = Field(
|
12
|
+
...,
|
13
|
+
description="A boolean flag for whether the news contains information relevant to the given question.",
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class RelevantNews(BaseModel):
|
18
|
+
question: str
|
19
|
+
url: str
|
20
|
+
summary: str
|
21
|
+
relevance_reasoning: str
|
22
|
+
days_ago: int
|
23
|
+
|
24
|
+
@staticmethod
|
25
|
+
def from_tavily_result_and_analysis(
|
26
|
+
question: str,
|
27
|
+
days_ago: int,
|
28
|
+
tavily_result: TavilyResult,
|
29
|
+
relevant_news_analysis: RelevantNewsAnalysis,
|
30
|
+
) -> "RelevantNews":
|
31
|
+
return RelevantNews(
|
32
|
+
question=question,
|
33
|
+
url=tavily_result.url,
|
34
|
+
summary=tavily_result.content,
|
35
|
+
relevance_reasoning=relevant_news_analysis.reasoning,
|
36
|
+
days_ago=days_ago,
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
class NoRelevantNews(BaseModel):
|
41
|
+
"""
|
42
|
+
A placeholder model for when no relevant news is found. Enables ability to
|
43
|
+
distinguish between 'a cache hit with no news' and 'a cache miss'.
|
44
|
+
"""
|
@@ -0,0 +1,162 @@
|
|
1
|
+
from datetime import datetime, timedelta
|
2
|
+
|
3
|
+
from langchain_core.output_parsers import PydanticOutputParser
|
4
|
+
from langchain_core.prompts import PromptTemplate
|
5
|
+
from langchain_openai import ChatOpenAI
|
6
|
+
|
7
|
+
from prediction_market_agent_tooling.config import APIKeys
|
8
|
+
from prediction_market_agent_tooling.tools.langfuse_ import (
|
9
|
+
get_langfuse_langchain_config,
|
10
|
+
observe,
|
11
|
+
)
|
12
|
+
from prediction_market_agent_tooling.tools.relevant_news_analysis.data_models import (
|
13
|
+
NoRelevantNews,
|
14
|
+
RelevantNews,
|
15
|
+
RelevantNewsAnalysis,
|
16
|
+
)
|
17
|
+
from prediction_market_agent_tooling.tools.relevant_news_analysis.relevant_news_cache import (
|
18
|
+
RelevantNewsResponseCache,
|
19
|
+
)
|
20
|
+
from prediction_market_agent_tooling.tools.tavily.tavily_search import (
|
21
|
+
get_relevant_news_since,
|
22
|
+
)
|
23
|
+
from prediction_market_agent_tooling.tools.tavily.tavily_storage import TavilyStorage
|
24
|
+
from prediction_market_agent_tooling.tools.utils import check_not_none, utcnow
|
25
|
+
|
26
|
+
SUMMARISE_RELEVANT_NEWS_PROMPT_TEMPLATE = """
|
27
|
+
You are an expert news analyst, tracking stories that may affect your prediction to the outcome of a particular QUESTION.
|
28
|
+
|
29
|
+
Your role is to identify only the relevant information from a scraped news site (RAW_CONTENT), analyse it, and determine whether it contains developments or announcements occurring **after** the DATE_OF_INTEREST that could affect the outcome of the QUESTION.
|
30
|
+
|
31
|
+
Note that the news article may be published after the DATE_OF_INTEREST, but reference information that is older than the DATE_OF_INTEREST.
|
32
|
+
|
33
|
+
[QUESTION]
|
34
|
+
{question}
|
35
|
+
|
36
|
+
[DATE_OF_INTEREST]
|
37
|
+
{date_of_interest}
|
38
|
+
|
39
|
+
[RAW_CONTENT]
|
40
|
+
{raw_content}
|
41
|
+
|
42
|
+
For your analysis, you should:
|
43
|
+
- Discard the 'noise' from the raw content (e.g. ads, irrelevant content)
|
44
|
+
- Consider ONLY information that would have a notable impact on the outcome of the question.
|
45
|
+
- Consider ONLY information relating to an announcement or development that occurred **after** the DATE_OF_INTEREST.
|
46
|
+
- Present this information concisely in your reasoning.
|
47
|
+
- In your reasoning, do not use the term 'DATE_OF_INTEREST' directly. Use the actual date you are referring to instead.
|
48
|
+
- In your reasoning, do not use the term 'RAW_CONTENT' directly. Refer to it as 'the article', or quote the content you are referring to.
|
49
|
+
|
50
|
+
{format_instructions}
|
51
|
+
"""
|
52
|
+
|
53
|
+
|
54
|
+
@observe()
|
55
|
+
def analyse_news_relevance(
|
56
|
+
raw_content: str,
|
57
|
+
question: str,
|
58
|
+
date_of_interest: datetime,
|
59
|
+
model: str,
|
60
|
+
temperature: float,
|
61
|
+
) -> RelevantNewsAnalysis:
|
62
|
+
"""
|
63
|
+
Analyse whether the news contains new (relative to the given date)
|
64
|
+
information relevant to the given question.
|
65
|
+
"""
|
66
|
+
parser = PydanticOutputParser(pydantic_object=RelevantNewsAnalysis)
|
67
|
+
prompt = PromptTemplate(
|
68
|
+
template=SUMMARISE_RELEVANT_NEWS_PROMPT_TEMPLATE,
|
69
|
+
input_variables=["question", "date_of_interest", "raw_content"],
|
70
|
+
partial_variables={"format_instructions": parser.get_format_instructions()},
|
71
|
+
)
|
72
|
+
llm = ChatOpenAI(
|
73
|
+
temperature=temperature,
|
74
|
+
model=model,
|
75
|
+
api_key=APIKeys().openai_api_key_secretstr_v1,
|
76
|
+
)
|
77
|
+
chain = prompt | llm | parser
|
78
|
+
|
79
|
+
relevant_news_analysis: RelevantNewsAnalysis = chain.invoke(
|
80
|
+
{
|
81
|
+
"raw_content": raw_content,
|
82
|
+
"question": question,
|
83
|
+
"date_of_interest": str(date_of_interest),
|
84
|
+
},
|
85
|
+
config=get_langfuse_langchain_config(),
|
86
|
+
)
|
87
|
+
return relevant_news_analysis
|
88
|
+
|
89
|
+
|
90
|
+
@observe()
|
91
|
+
def get_certified_relevant_news_since(
|
92
|
+
question: str,
|
93
|
+
days_ago: int,
|
94
|
+
tavily_storage: TavilyStorage | None = None,
|
95
|
+
) -> RelevantNews | None:
|
96
|
+
"""
|
97
|
+
Get relevant news since a given date for a given question. Retrieves
|
98
|
+
possibly relevant news from tavily, then checks that it is relevant via
|
99
|
+
an LLM call.
|
100
|
+
"""
|
101
|
+
results = get_relevant_news_since(
|
102
|
+
question=question,
|
103
|
+
days_ago=days_ago,
|
104
|
+
score_threshold=0.0, # Be conservative to avoid missing relevant information
|
105
|
+
max_results=3, # A tradeoff between cost and quality. 3 seems to be a good balance.
|
106
|
+
tavily_storage=tavily_storage,
|
107
|
+
)
|
108
|
+
|
109
|
+
# Sort results by descending 'relevance score' to maximise the chance of
|
110
|
+
# finding relevant news early
|
111
|
+
results = sorted(
|
112
|
+
results,
|
113
|
+
key=lambda result: result.score,
|
114
|
+
reverse=True,
|
115
|
+
)
|
116
|
+
|
117
|
+
for result in results:
|
118
|
+
relevant_news_analysis = analyse_news_relevance(
|
119
|
+
raw_content=check_not_none(result.raw_content),
|
120
|
+
question=question,
|
121
|
+
date_of_interest=utcnow() - timedelta(days=days_ago),
|
122
|
+
model="gpt-4o", # 4o-mini isn't good enough, 1o and 1o-mini are too expensive
|
123
|
+
temperature=0.0,
|
124
|
+
)
|
125
|
+
|
126
|
+
# Return first relevant news found
|
127
|
+
if relevant_news_analysis.contains_relevant_news:
|
128
|
+
return RelevantNews.from_tavily_result_and_analysis(
|
129
|
+
question=question,
|
130
|
+
days_ago=days_ago,
|
131
|
+
tavily_result=result,
|
132
|
+
relevant_news_analysis=relevant_news_analysis,
|
133
|
+
)
|
134
|
+
|
135
|
+
# No relevant news found
|
136
|
+
return None
|
137
|
+
|
138
|
+
|
139
|
+
def get_certified_relevant_news_since_cached(
|
140
|
+
question: str,
|
141
|
+
days_ago: int,
|
142
|
+
cache: RelevantNewsResponseCache,
|
143
|
+
tavily_storage: TavilyStorage | None = None,
|
144
|
+
) -> RelevantNews | None:
|
145
|
+
cached = cache.find(question=question, days_ago=days_ago)
|
146
|
+
|
147
|
+
if isinstance(cached, NoRelevantNews):
|
148
|
+
return None
|
149
|
+
elif cached is None:
|
150
|
+
relevant_news = get_certified_relevant_news_since(
|
151
|
+
question=question,
|
152
|
+
days_ago=days_ago,
|
153
|
+
tavily_storage=tavily_storage,
|
154
|
+
)
|
155
|
+
cache.save(
|
156
|
+
question=question,
|
157
|
+
days_ago=days_ago,
|
158
|
+
relevant_news=relevant_news,
|
159
|
+
)
|
160
|
+
return relevant_news
|
161
|
+
else:
|
162
|
+
return cached
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from datetime import datetime, timedelta
|
2
|
+
|
3
|
+
from pydantic import ValidationError
|
4
|
+
from sqlmodel import Field, Session, SQLModel, create_engine, desc, select
|
5
|
+
|
6
|
+
from prediction_market_agent_tooling.config import APIKeys
|
7
|
+
from prediction_market_agent_tooling.loggers import logger
|
8
|
+
from prediction_market_agent_tooling.tools.relevant_news_analysis.data_models import (
|
9
|
+
NoRelevantNews,
|
10
|
+
RelevantNews,
|
11
|
+
)
|
12
|
+
from prediction_market_agent_tooling.tools.utils import utcnow
|
13
|
+
|
14
|
+
|
15
|
+
class RelevantNewsCacheModel(SQLModel, table=True):
|
16
|
+
__tablename__ = "relevant_news_response_cache"
|
17
|
+
__table_args__ = {"extend_existing": True}
|
18
|
+
id: int | None = Field(default=None, primary_key=True)
|
19
|
+
question: str = Field(index=True)
|
20
|
+
datetime_: datetime = Field(index=True)
|
21
|
+
days_ago: int
|
22
|
+
json_dump: str | None
|
23
|
+
|
24
|
+
|
25
|
+
class RelevantNewsResponseCache:
|
26
|
+
def __init__(self, sqlalchemy_db_url: str | None = None):
|
27
|
+
self.engine = create_engine(
|
28
|
+
sqlalchemy_db_url
|
29
|
+
if sqlalchemy_db_url
|
30
|
+
else APIKeys().sqlalchemy_db_url.get_secret_value()
|
31
|
+
)
|
32
|
+
self._initialize_db()
|
33
|
+
|
34
|
+
def _initialize_db(self) -> None:
|
35
|
+
"""
|
36
|
+
Creates the tables if they don't exist
|
37
|
+
"""
|
38
|
+
with self.engine.connect() as conn:
|
39
|
+
SQLModel.metadata.create_all(
|
40
|
+
conn,
|
41
|
+
tables=[SQLModel.metadata.tables[RelevantNewsCacheModel.__tablename__]],
|
42
|
+
)
|
43
|
+
|
44
|
+
def find(
|
45
|
+
self,
|
46
|
+
question: str,
|
47
|
+
days_ago: int,
|
48
|
+
) -> RelevantNews | NoRelevantNews | None:
|
49
|
+
with Session(self.engine) as session:
|
50
|
+
query = (
|
51
|
+
select(RelevantNewsCacheModel)
|
52
|
+
.where(RelevantNewsCacheModel.question == question)
|
53
|
+
.where(RelevantNewsCacheModel.days_ago <= days_ago)
|
54
|
+
.where(
|
55
|
+
RelevantNewsCacheModel.datetime_ >= utcnow() - timedelta(days=1)
|
56
|
+
) # Cache entries expire after 1 day
|
57
|
+
)
|
58
|
+
item = session.exec(
|
59
|
+
query.order_by(desc(RelevantNewsCacheModel.datetime_))
|
60
|
+
).first()
|
61
|
+
|
62
|
+
if item is None:
|
63
|
+
return None
|
64
|
+
else:
|
65
|
+
if item.json_dump is None:
|
66
|
+
return NoRelevantNews()
|
67
|
+
else:
|
68
|
+
try:
|
69
|
+
return RelevantNews.model_validate_json(item.json_dump)
|
70
|
+
except ValidationError as e:
|
71
|
+
logger.error(
|
72
|
+
f"Error deserializing RelevantNews from cache for {question=}, {days_ago=} and {item=}: {e}"
|
73
|
+
)
|
74
|
+
return None
|
75
|
+
|
76
|
+
def save(
|
77
|
+
self,
|
78
|
+
question: str,
|
79
|
+
days_ago: int,
|
80
|
+
relevant_news: RelevantNews | None,
|
81
|
+
) -> None:
|
82
|
+
with Session(self.engine) as session:
|
83
|
+
cached = RelevantNewsCacheModel(
|
84
|
+
question=question,
|
85
|
+
days_ago=days_ago,
|
86
|
+
datetime_=utcnow(), # Assumes that the cache is being updated at the time the news is found
|
87
|
+
json_dump=relevant_news.model_dump_json() if relevant_news else None,
|
88
|
+
)
|
89
|
+
session.add(cached)
|
90
|
+
session.commit()
|
@@ -1,16 +1,16 @@
|
|
1
1
|
from eth_account.signers.local import LocalAccount
|
2
2
|
from eth_typing import ChecksumAddress
|
3
|
-
from gnosis.eth import EthereumClient
|
4
|
-
from gnosis.eth.constants import NULL_ADDRESS
|
5
|
-
from gnosis.eth.contracts import get_safe_V1_4_1_contract
|
6
|
-
from gnosis.safe.proxy_factory import ProxyFactoryV141
|
7
|
-
from gnosis.safe.safe import Safe
|
8
3
|
from safe_cli.safe_addresses import (
|
9
4
|
get_default_fallback_handler_address,
|
10
5
|
get_proxy_factory_address,
|
11
6
|
get_safe_contract_address,
|
12
7
|
get_safe_l2_contract_address,
|
13
8
|
)
|
9
|
+
from safe_eth.eth import EthereumClient
|
10
|
+
from safe_eth.eth.constants import NULL_ADDRESS
|
11
|
+
from safe_eth.eth.contracts import get_safe_V1_4_1_contract
|
12
|
+
from safe_eth.safe.proxy_factory import ProxyFactoryV141
|
13
|
+
from safe_eth.safe.safe import SafeV141
|
14
14
|
from web3.types import Wei
|
15
15
|
|
16
16
|
from prediction_market_agent_tooling.loggers import logger
|
@@ -87,7 +87,7 @@ def create_safe(
|
|
87
87
|
|
88
88
|
# We ignore mypy below because using the proper class SafeV141 yields an error and mypy
|
89
89
|
# doesn't understand that there is a hacky factory method (__new__) on this abstract class.
|
90
|
-
safe_version =
|
90
|
+
safe_version = SafeV141(safe_contract_address, ethereum_client).retrieve_version()
|
91
91
|
logger.info(
|
92
92
|
f"Safe-master-copy={safe_contract_address} version={safe_version}\n"
|
93
93
|
f"Fallback-handler={fallback_handler}\n"
|
@@ -5,9 +5,9 @@ import base58
|
|
5
5
|
import tenacity
|
6
6
|
from eth_account import Account
|
7
7
|
from eth_typing import URI
|
8
|
-
from gnosis.eth import EthereumClient
|
9
|
-
from gnosis.safe.safe import Safe
|
10
8
|
from pydantic.types import SecretStr
|
9
|
+
from safe_eth.eth import EthereumClient
|
10
|
+
from safe_eth.safe.safe import SafeV141
|
11
11
|
from web3 import Web3
|
12
12
|
from web3.constants import HASH_ZERO
|
13
13
|
from web3.types import AccessList, AccessListEntry, Nonce, TxParams, TxReceipt, Wei
|
@@ -200,7 +200,7 @@ def send_function_on_contract_tx(
|
|
200
200
|
# Don't retry on `reverted` messages, as they would always fail again.
|
201
201
|
retry=tenacity.retry_if_exception_message(match=NOT_REVERTED_ICASE_REGEX_PATTERN),
|
202
202
|
wait=tenacity.wait_chain(*[tenacity.wait_fixed(n) for n in range(1, 10)]),
|
203
|
-
stop=tenacity.stop_after_attempt(
|
203
|
+
stop=tenacity.stop_after_attempt(5),
|
204
204
|
after=lambda x: logger.debug(
|
205
205
|
f"send_function_on_contract_tx_using_safe failed, {x.attempt_number=}."
|
206
206
|
),
|
@@ -219,7 +219,7 @@ def send_function_on_contract_tx_using_safe(
|
|
219
219
|
if not web3.provider.endpoint_uri: # type: ignore
|
220
220
|
raise EnvironmentError("RPC_URL not available in web3 object.")
|
221
221
|
ethereum_client = EthereumClient(ethereum_node_url=URI(web3.provider.endpoint_uri)) # type: ignore
|
222
|
-
s =
|
222
|
+
s = SafeV141(safe_address, ethereum_client)
|
223
223
|
safe_master_copy_address = s.retrieve_master_copy_address()
|
224
224
|
eoa_public_key = private_key_to_public_key(from_private_key)
|
225
225
|
# See https://ethereum.stackexchange.com/questions/123750/how-to-implement-eip-2930-access-list for details,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: prediction-market-agent-tooling
|
3
|
-
Version: 0.55.
|
3
|
+
Version: 0.55.1
|
4
4
|
Summary: Tools to benchmark, deploy and monitor prediction market agents.
|
5
5
|
Author: Gnosis
|
6
6
|
Requires-Python: >=3.10,<3.12
|
@@ -38,7 +38,7 @@ Requires-Dist: pydantic-settings (>=2.4.0,<3.0.0)
|
|
38
38
|
Requires-Dist: pymongo (>=4.8.0,<5.0.0)
|
39
39
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
40
40
|
Requires-Dist: safe-cli (>=1.0.0,<2.0.0)
|
41
|
-
Requires-Dist: safe-eth-py (>=6.0.
|
41
|
+
Requires-Dist: safe-eth-py (>=6.0.0b41,<7.0.0)
|
42
42
|
Requires-Dist: scikit-learn (>=1.3.1,<2.0.0)
|
43
43
|
Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
|
44
44
|
Requires-Dist: streamlit (>=1.31.0,<2.0.0)
|
@@ -16,14 +16,15 @@ 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=
|
20
|
-
prediction_market_agent_tooling/deploy/agent.py,sha256=
|
19
|
+
prediction_market_agent_tooling/config.py,sha256=114f3V9abaok27p5jX3UVr5b5gRUiSxBIYn8Snid34I,6731
|
20
|
+
prediction_market_agent_tooling/deploy/agent.py,sha256=s3XVNeuJ9mtlfsRB1RWLWUR0q9fMoptZqu1u-I6oiws,22420
|
21
21
|
prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
|
22
22
|
prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=kMrIE3wMv_IB6nJd_1DmDXDkEZhsXFOgyTd7JZ0gqHI,13068
|
23
23
|
prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
|
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
|
26
26
|
prediction_market_agent_tooling/deploy/gcp/utils.py,sha256=oyW0jgrUT2Tr49c7GlpcMsYNQjoCSOcWis3q-MmVAhU,6089
|
27
|
+
prediction_market_agent_tooling/deploy/trade_interval.py,sha256=Xk9j45alQ_vrasGvsNyuW70XHIQ7wfvjoxNR3F6HYCw,1155
|
27
28
|
prediction_market_agent_tooling/gtypes.py,sha256=tqp03PyY0Yhievl4XELfwAn0xOoecaTvBZ1Co6b-A7o,2541
|
28
29
|
prediction_market_agent_tooling/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
30
|
prediction_market_agent_tooling/jobs/jobs.py,sha256=I07yh0GJ-xhlvQaOUQB8xlSnihhcbU2c7DZ4ZND14c0,1246
|
@@ -85,16 +86,19 @@ prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_A
|
|
85
86
|
prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=B0PhAQyviFnVbtOCYMxYmcCn66cu9nbqAOIAZcdgiRI,5771
|
86
87
|
prediction_market_agent_tooling/tools/omen/reality_accuracy.py,sha256=M1SF7iSW1gVlQSTskdVFTn09uPLST23YeipVIWj54io,2236
|
87
88
|
prediction_market_agent_tooling/tools/parallelism.py,sha256=6Gou0hbjtMZrYvxjTDFUDZuxmE2nqZVbb6hkg1hF82A,1022
|
88
|
-
prediction_market_agent_tooling/tools/
|
89
|
+
prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py,sha256=95l84aztFaxcRLLcRQ46yKJbIlOEuDAbIGLouyliDzA,1316
|
90
|
+
prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py,sha256=OWLzwCbQS2b9hjwTRXTOjjplWXcGXFf3yjKEeK4kGbQ,5720
|
91
|
+
prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py,sha256=2yxtBIDyMT_6CsTpZyuIv_2dy2B9WgEOaTT1fSloBu0,3223
|
92
|
+
prediction_market_agent_tooling/tools/safe.py,sha256=9vxGGLvSPnfy-sxUFDpBTe8omqpGXP7MzvGPp6bRxrU,5197
|
89
93
|
prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
|
90
94
|
prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
|
91
95
|
prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=Rz4tZzwCRzPaq49SFT33SCRQrqHXtqWdD9ajb2tGCWc,2723
|
92
|
-
prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=
|
96
|
+
prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=UPSp0S5Sql52X6UlU2Ki_iO-gmDJSMs5enn9AV_IZRM,4896
|
93
97
|
prediction_market_agent_tooling/tools/tavily/tavily_storage.py,sha256=t-tZzbCzBBdFedRZDuVBn3A3mIDX8Z5wza6SxWswu_E,4093
|
94
98
|
prediction_market_agent_tooling/tools/utils.py,sha256=W-9SqeCKd51BYMRhDjYPQ7lfNO_zE9EvYpmu2r5WXGA,7163
|
95
|
-
prediction_market_agent_tooling/tools/web3_utils.py,sha256=
|
96
|
-
prediction_market_agent_tooling-0.55.
|
97
|
-
prediction_market_agent_tooling-0.55.
|
98
|
-
prediction_market_agent_tooling-0.55.
|
99
|
-
prediction_market_agent_tooling-0.55.
|
100
|
-
prediction_market_agent_tooling-0.55.
|
99
|
+
prediction_market_agent_tooling/tools/web3_utils.py,sha256=44W8siSLNQxeib98bbwAe7V5C609NHNlUuxwuWIRDiY,11838
|
100
|
+
prediction_market_agent_tooling-0.55.1.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
101
|
+
prediction_market_agent_tooling-0.55.1.dist-info/METADATA,sha256=chzNuISP7K3sgt_Mj0fFX6bQu9JFkOXTvYgmqaLFcMU,8056
|
102
|
+
prediction_market_agent_tooling-0.55.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
103
|
+
prediction_market_agent_tooling-0.55.1.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
104
|
+
prediction_market_agent_tooling-0.55.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|