From 90168ca4a2d00be5b23113cbe4ea15b3bc08a0f5 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Tue, 5 Nov 2024 19:24:00 -0300 Subject: [PATCH 01/13] Added block_time fetching for more accurate outcome tokens received. --- .../match_bets_with_langfuse_traces.py | 171 ++++++---- examples/monitor/transaction_cache.py | 25 ++ poetry.lock | 29 +- .../markets/omen/data_models.py | 2 +- .../markets/omen/omen_subgraph_handler.py | 305 +++++++++--------- .../tools/langfuse_client_utils.py | 1 + pyproject.toml | 1 + 7 files changed, 311 insertions(+), 223 deletions(-) create mode 100644 examples/monitor/transaction_cache.py diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index 93e24391..680d89fc 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -1,6 +1,14 @@ from pathlib import Path from typing import Any +import dotenv +from eth_typing import HexAddress, HexStr + +from examples.monitor.transaction_cache import TransactionBlockCache +from prediction_market_agent_tooling.markets.omen.omen_contracts import OmenConditionalTokenContract +from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import OmenSubgraphHandler + +dotenv.load_dotenv() import pandas as pd from langfuse import Langfuse from pydantic import BaseModel @@ -10,9 +18,6 @@ BettingStrategy, GuaranteedLossError, KellyBettingStrategy, - MaxAccuracyBettingStrategy, - MaxAccuracyWithKellyScaledBetsStrategy, - MaxExpectedValueBettingStrategy, ProbabilisticAnswer, TradeType, ) @@ -44,9 +49,11 @@ class MSEProfit(BaseModel): def get_outcome_for_trace( - strategy: BettingStrategy, - trace: ProcessMarketTrace, - market_outcome: bool, + strategy: BettingStrategy, + trace: ProcessMarketTrace, + market_outcome: bool, + actual_placed_bet: ResolvedBet, + tx_block_cache: TransactionBlockCache ) -> SimulatedOutcome | None: market = trace.market answer = trace.answer @@ -66,24 +73,37 @@ def get_outcome_for_trace( if not trades: return None assert ( - len(trades) == 1 + len(trades) == 1 ), f"Should be always one trade if no existing position is given: {trades=}; {answer=}; {market=}" assert ( - trades[0].trade_type == TradeType.BUY + trades[0].trade_type == TradeType.BUY ), "Can only buy without previous position." buy_trade = trades[0] - - received_outcome_tokens = market.get_buy_token_amount( - bet_amount=market.get_bet_amount(buy_trade.amount.amount), - direction=buy_trade.outcome, - ).amount - correct = buy_trade.outcome == market_outcome - profit = ( - received_outcome_tokens - buy_trade.amount.amount - if correct - else -buy_trade.amount.amount - ) + # If not correct, stop early because profit is known. + if not correct: + profit = -buy_trade.amount.amount + else: + # We use a historical state (by passing in a block_number as arg) to get the correct outcome token balances. + tx_block_number = tx_block_cache.get_block_number(actual_placed_bet.id) + market_at_block = OmenSubgraphHandler().get_omen_market_by_market_id(HexAddress(HexStr(market.id)), + block_number=tx_block_number) + omen_agent_market_at_block = OmenAgentMarket.from_data_model(market_at_block) + + received_outcome_tokens = omen_agent_market_at_block.get_buy_token_amount( + bet_amount=omen_agent_market_at_block.get_bet_amount(buy_trade.amount.amount), + direction=buy_trade.outcome, + ).amount + profit = ( + received_outcome_tokens - buy_trade.amount.amount + if correct + else -buy_trade.amount.amount + ) + + # received_outcome_tokens = market.get_buy_token_amount( + # bet_amount=market.get_bet_amount(buy_trade.amount.amount), + # direction=buy_trade.outcome, + # ).amount return SimulatedOutcome( size=buy_trade.amount.amount, @@ -100,14 +120,14 @@ def get_outcome_for_trace( # Get the private keys for the agents from GCP Secret Manager agent_gcp_secret_map = { "DeployablePredictionProphetGPT4TurboFinalAgent": "pma-prophetgpt4turbo-final", - "DeployablePredictionProphetGPT4TurboPreviewAgent": "pma-prophetgpt4", - "DeployablePredictionProphetGPT4oAgent": "pma-prophetgpt3", - "DeployablePredictionProphetGPTo1PreviewAgent": "pma-prophet-o1-preview", - "DeployablePredictionProphetGPTo1MiniAgent": "pma-prophet-o1-mini", - "DeployableOlasEmbeddingOAAgent": "pma-evo-olas-embeddingoa", - "DeployableThinkThoroughlyAgent": "pma-think-thoroughly", - "DeployableThinkThoroughlyProphetResearchAgent": "pma-think-thoroughly-prophet-research", - "DeployableKnownOutcomeAgent": "pma-knownoutcome", + # "DeployablePredictionProphetGPT4TurboPreviewAgent": "pma-prophetgpt4", + # "DeployablePredictionProphetGPT4oAgent": "pma-prophetgpt3", + # "DeployablePredictionProphetGPTo1PreviewAgent": "pma-prophet-o1-preview", + # "DeployablePredictionProphetGPTo1MiniAgent": "pma-prophet-o1-mini", + # "DeployableOlasEmbeddingOAAgent": "pma-evo-olas-embeddingoa", + # "DeployableThinkThoroughlyAgent": "pma-think-thoroughly", + # "DeployableThinkThoroughlyProphetResearchAgent": "pma-think-thoroughly-prophet-research", + # "DeployableKnownOutcomeAgent": "pma-knownoutcome", } agent_pkey_map = { @@ -116,44 +136,44 @@ def get_outcome_for_trace( # Define strategies we want to test out strategies = [ - MaxAccuracyBettingStrategy(bet_amount=1), - MaxAccuracyBettingStrategy(bet_amount=2), - MaxAccuracyBettingStrategy(bet_amount=25), - KellyBettingStrategy(max_bet_amount=1), - KellyBettingStrategy(max_bet_amount=2), + # MaxAccuracyBettingStrategy(bet_amount=1), + # MaxAccuracyBettingStrategy(bet_amount=2), + # MaxAccuracyBettingStrategy(bet_amount=25), + # KellyBettingStrategy(max_bet_amount=1), + # KellyBettingStrategy(max_bet_amount=2), KellyBettingStrategy(max_bet_amount=5), - KellyBettingStrategy(max_bet_amount=25), - MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=1), - MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=2), - MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=25), - MaxExpectedValueBettingStrategy(bet_amount=1), - MaxExpectedValueBettingStrategy(bet_amount=2), - MaxExpectedValueBettingStrategy(bet_amount=5), - MaxExpectedValueBettingStrategy(bet_amount=25), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.01), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.05), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.1), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.15), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.2), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.25), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.3), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.4), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.5), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.6), - KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.7), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.1), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.15), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.2), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.3), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.4), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.5), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.6), - KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.7), + # KellyBettingStrategy(max_bet_amount=25), + # MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=1), + # MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=2), + # MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=25), + # MaxExpectedValueBettingStrategy(bet_amount=1), + # MaxExpectedValueBettingStrategy(bet_amount=2), + # MaxExpectedValueBettingStrategy(bet_amount=5), + # MaxExpectedValueBettingStrategy(bet_amount=25), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.01), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.05), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.1), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.15), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.2), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.25), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.3), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.4), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.5), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.6), + # KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.7), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.1), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.15), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.2), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.3), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.4), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.5), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.6), + # KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.7), KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.1), - KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.2), - KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.3), - KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.5), - KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.7), + # KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.2), + # KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.3), + # KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.5), + # KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.7), ] httpx_client = HttpxCachedClient().get_client() @@ -165,12 +185,17 @@ def get_outcome_for_trace( strat_mse_profits[repr(strategy)] = MSEProfit(p_yes_mse=[], total_profit=[]) print("# Agent Bet vs Simulated Bet Comparison") + + w3 = OmenConditionalTokenContract().get_web3() + + tx_block_cache = TransactionBlockCache(web3=w3) + for agent_name, private_key in agent_pkey_map.items(): print(f"\n## {agent_name}\n") api_keys = APIKeys(BET_FROM_PRIVATE_KEY=private_key) # Pick a time after pool token number is stored in OmenAgentMarket - start_time = utc_datetime(2024, 10, 1) + start_time = utc_datetime(2024, 10, 28) langfuse = Langfuse( secret_key=api_keys.langfuse_secret_key.get_secret_value(), @@ -202,6 +227,8 @@ def get_outcome_for_trace( bets_with_traces: list[ResolvedBetWithTrace] = [] for bet in bets: trace = get_trace_for_bet(bet, process_market_traces) + if not trace: + print('no trace') if trace: bets_with_traces.append(ResolvedBetWithTrace(bet=bet, trace=trace)) @@ -224,14 +251,18 @@ def get_outcome_for_trace( agent_balance = starting_balance simulated_outcomes: list[SimulatedOutcome] = [] + # ToDo - Can we add the value of tokens that weren't redeemed yet? + # Like a portfolio tracking. for bet_with_trace in bets_with_traces: bet = bet_with_trace.bet trace = bet_with_trace.trace simulated_outcome = get_outcome_for_trace( - strategy=strategy, trace=trace, market_outcome=bet.market_outcome + strategy=strategy, trace=trace, market_outcome=bet.market_outcome, + actual_placed_bet=bet, tx_block_cache=tx_block_cache ) if simulated_outcome is None: continue + # ToDo - Add new metrics simulated_outcomes.append(simulated_outcome) agent_balance += simulated_outcome.profit @@ -247,6 +278,7 @@ def get_outcome_for_trace( "sim_dir": simulated_outcome.direction, "org_profit": round(bet.profit.amount, 4), "sim_profit": round(simulated_outcome.profit, 4), + "timestamp": bet_with_trace.trace.timestamp_datetime, } ) @@ -300,14 +332,17 @@ def get_outcome_for_trace( "p_yes mse": p_yes_mse, "start_balance": starting_balance, "end_balance": agent_balance, + "daily_return": 123, # ToDo + "annualized_sharpe_ratio": 123, # ToDo + "annualized_volatility": 123, # ToDo } ) simulations_df = pd.DataFrame.from_records(simulations) simulations_df.sort_values(by="bet_profit", ascending=False, inplace=True) overall_md += ( - f"\n\n## {agent_name}\n\n{len(bets_with_traces)} bets\n\n" - + simulations_df.to_markdown(index=False) + f"\n\n## {agent_name}\n\n{len(bets_with_traces)} bets\n\n" + + simulations_df.to_markdown(index=False) ) # export details per agent pd.DataFrame.from_records(details).to_csv( @@ -322,6 +357,6 @@ def get_outcome_for_trace( print(f"{strategy_name}: {correlation=}") with open( - output_directory / "match_bets_with_langfuse_traces_overall.md", "w" + output_directory / "match_bets_with_langfuse_traces_overall.md", "w" ) as overall_f: overall_f.write(overall_md) diff --git a/examples/monitor/transaction_cache.py b/examples/monitor/transaction_cache.py new file mode 100644 index 00000000..69b3ca0a --- /dev/null +++ b/examples/monitor/transaction_cache.py @@ -0,0 +1,25 @@ +import diskcache as dc +import tenacity +from eth_typing import HexStr +from tenacity import wait_exponential +from web3 import Web3 + +from prediction_market_agent_tooling.loggers import logger +from prediction_market_agent_tooling.tools.cache import persistent_inmemory_cache + + +class TransactionBlockCache: + + def __init__(self, web3: Web3): + self.cache = dc.Cache("block_cache_dir") + self.web3 = web3 + + @persistent_inmemory_cache + @tenacity.retry( + wait=wait_exponential(multiplier=1, min=1, max=4), + stop=tenacity.stop_after_attempt(7), + after=lambda x: logger.debug(f"fetch tx failed, {x.attempt_number=}."), + ) + def get_block_number(self, transaction_hash: str) -> int: + tx = self.web3.eth.get_transaction(HexStr(transaction_hash)) + return tx['blockNumber'] diff --git a/poetry.lock b/poetry.lock index 2c96d145..78d5094a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -993,6 +993,17 @@ files = [ [package.dependencies] packaging = "*" +[[package]] +name = "diskcache" +version = "5.6.3" +description = "Disk Cache -- Disk and file backed persistent cache." +optional = false +python-versions = ">=3" +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + [[package]] name = "distro" version = "1.9.0" @@ -3338,31 +3349,43 @@ python-versions = ">=3.9" files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, @@ -4071,7 +4094,6 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] @@ -4082,7 +4104,6 @@ description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] @@ -6063,4 +6084,4 @@ openai = ["openai"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "8c5d0f948b7fc3ff1c5647064bc8f90daaad8c0c431e64749bfcfc8ef1e8cee9" +content-hash = "ad7a5b9939524a3e2fbdb4f3a90fc29b057960f63fa5a75fbb3c61b865e0603e" diff --git a/prediction_market_agent_tooling/markets/omen/data_models.py b/prediction_market_agent_tooling/markets/omen/data_models.py index d5613877..9f27fbbd 100644 --- a/prediction_market_agent_tooling/markets/omen/data_models.py +++ b/prediction_market_agent_tooling/markets/omen/data_models.py @@ -530,7 +530,7 @@ def get_profit(self) -> ProfitAmount: if self.boolean_outcome == self.fpmm.boolean_outcome else -bet_amount_xdai ) - profit -= wei_to_xdai(self.feeAmount) + #profit -= wei_to_xdai(self.feeAmount) return ProfitAmount( amount=profit, currency=Currency.xDai, diff --git a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py index 491134fc..d8233b37 100644 --- a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +++ b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py @@ -121,7 +121,7 @@ def _get_fields_for_bets(self, bets_field: FieldPath) -> list[FieldPath]: return fields_for_bets + fields_for_markets def _get_fields_for_reality_questions( - self, questions_field: FieldPath + self, questions_field: FieldPath ) -> list[FieldPath]: # Note: Fields available on the Omen's subgraph Question are different from the Reality's subgraph Question. return [ @@ -160,7 +160,7 @@ def _get_fields_for_responses(self, responses_field: FieldPath) -> list[FieldPat ] + self._get_fields_for_reality_questions(responses_field.question) def _get_fields_for_market_questions( - self, questions_field: FieldPath + self, questions_field: FieldPath ) -> list[FieldPath]: # Note: Fields available on the Omen's subgraph Question are different from the Reality's subgraph Question. return [ @@ -202,26 +202,26 @@ def _get_fields_for_markets(self, markets_field: FieldPath) -> list[FieldPath]: ] + self._get_fields_for_market_questions(markets_field.question) def _build_where_statements( - self, - creator: HexAddress | None, - creator_in: t.Sequence[HexAddress] | None, - outcomes: list[str], - created_after: DatetimeUTC | None, - question_opened_before: DatetimeUTC | None, - question_opened_after: DatetimeUTC | None, - question_finalized_before: DatetimeUTC | None, - question_finalized_after: DatetimeUTC | None, - question_with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - question_current_answer_before: DatetimeUTC | None, - question_excluded_titles: set[str] | None, - resolved: bool | None, - liquidity_bigger_than: Wei | None, - condition_id_in: list[HexBytes] | None, - id_in: list[str] | None, - collateral_token_address_in: tuple[ChecksumAddress, ...] | None, - category: str | None, + self, + creator: HexAddress | None, + creator_in: t.Sequence[HexAddress] | None, + outcomes: list[str], + created_after: DatetimeUTC | None, + question_opened_before: DatetimeUTC | None, + question_opened_after: DatetimeUTC | None, + question_finalized_before: DatetimeUTC | None, + question_finalized_after: DatetimeUTC | None, + question_with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + question_current_answer_before: DatetimeUTC | None, + question_excluded_titles: set[str] | None, + resolved: bool | None, + liquidity_bigger_than: Wei | None, + condition_id_in: list[HexBytes] | None, + id_in: list[str] | None, + collateral_token_address_in: tuple[ChecksumAddress, ...] | None, + category: str | None, ) -> dict[str, t.Any]: where_stms: dict[str, t.Any] = { "isPendingArbitration": False, @@ -278,7 +278,7 @@ def _build_where_statements( return where_stms def _build_sort_params( - self, sort_by: SortBy + self, sort_by: SortBy ) -> tuple[str | None, FieldPath | None]: sort_direction: str | None sort_by_field: FieldPath | None @@ -313,19 +313,19 @@ def _build_sort_params( return sort_direction, sort_by_field def get_omen_binary_markets_simple( - self, - limit: t.Optional[int], - # Enumerated values for simpler usage. - filter_by: FilterBy, - sort_by: SortBy, - # Additional filters, these can not be modified by the enums above. - created_after: DatetimeUTC | None = None, - excluded_questions: set[str] | None = None, # question titles - collateral_token_address_in: ( - tuple[ChecksumAddress, ...] | None - ) = SAFE_COLLATERAL_TOKEN_MARKETS, - category: str | None = None, - creator_in: t.Sequence[HexAddress] | None = None, + self, + limit: t.Optional[int], + # Enumerated values for simpler usage. + filter_by: FilterBy, + sort_by: SortBy, + # Additional filters, these can not be modified by the enums above. + created_after: DatetimeUTC | None = None, + excluded_questions: set[str] | None = None, # question titles + collateral_token_address_in: ( + tuple[ChecksumAddress, ...] | None + ) = SAFE_COLLATERAL_TOKEN_MARKETS, + category: str | None = None, + creator_in: t.Sequence[HexAddress] | None = None, ) -> t.List[OmenMarket]: """ Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values. @@ -365,31 +365,31 @@ def get_omen_binary_markets_simple( ) def get_omen_binary_markets( - self, - limit: t.Optional[int], - creator: HexAddress | None = None, - creator_in: t.Sequence[HexAddress] | None = None, - created_after: DatetimeUTC | None = None, - question_opened_before: DatetimeUTC | None = None, - question_opened_after: DatetimeUTC | None = None, - question_finalized_before: DatetimeUTC | None = None, - question_finalized_after: DatetimeUTC | None = None, - question_with_answers: bool | None = None, - question_id: HexBytes | None = None, - question_id_in: list[HexBytes] | None = None, - question_current_answer_before: DatetimeUTC | None = None, - question_excluded_titles: set[str] | None = None, - resolved: bool | None = None, - liquidity_bigger_than: Wei | None = None, - condition_id_in: list[HexBytes] | None = None, - id_in: list[str] | None = None, - sort_by_field: FieldPath | None = None, - sort_direction: str | None = None, - outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES, - collateral_token_address_in: ( - tuple[ChecksumAddress, ...] | None - ) = SAFE_COLLATERAL_TOKEN_MARKETS, - category: str | None = None, + self, + limit: t.Optional[int], + creator: HexAddress | None = None, + creator_in: t.Sequence[HexAddress] | None = None, + created_after: DatetimeUTC | None = None, + question_opened_before: DatetimeUTC | None = None, + question_opened_after: DatetimeUTC | None = None, + question_finalized_before: DatetimeUTC | None = None, + question_finalized_after: DatetimeUTC | None = None, + question_with_answers: bool | None = None, + question_id: HexBytes | None = None, + question_id_in: list[HexBytes] | None = None, + question_current_answer_before: DatetimeUTC | None = None, + question_excluded_titles: set[str] | None = None, + resolved: bool | None = None, + liquidity_bigger_than: Wei | None = None, + condition_id_in: list[HexBytes] | None = None, + id_in: list[str] | None = None, + sort_by_field: FieldPath | None = None, + sort_direction: str | None = None, + outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES, + collateral_token_address_in: ( + tuple[ChecksumAddress, ...] | None + ) = SAFE_COLLATERAL_TOKEN_MARKETS, + category: str | None = None, ) -> t.List[OmenMarket]: """ Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums. @@ -435,9 +435,14 @@ def get_omen_binary_markets( omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket) return omen_markets - def get_omen_market_by_market_id(self, market_id: HexAddress) -> OmenMarket: + def get_omen_market_by_market_id(self, market_id: HexAddress, block_number: int | None = None) -> OmenMarket: + + query_filters = {"id": market_id.lower()} + if block_number: + query_filters["block"] = {"number": block_number} + markets = self.trades_subgraph.Query.fixedProductMarketMaker( - id=market_id.lower() + **query_filters ) fields = self._get_fields_for_markets(markets) @@ -451,7 +456,7 @@ def get_omen_market_by_market_id(self, market_id: HexAddress) -> OmenMarket: return omen_markets[0] def _get_fields_for_user_positions( - self, user_positions: FieldPath + self, user_positions: FieldPath ) -> list[FieldPath]: return [ user_positions.id, @@ -469,8 +474,8 @@ def _get_fields_for_positions(self, positions: FieldPath) -> list[FieldPath]: ] def get_positions( - self, - condition_id: HexBytes | None = None, + self, + condition_id: HexBytes | None = None, ) -> list[OmenPosition]: where_stms: dict[str, t.Any] = {} @@ -486,10 +491,10 @@ def get_positions( return [OmenPosition.model_validate(i) for i in items] def get_user_positions( - self, - better_address: ChecksumAddress, - position_id_in: list[HexBytes] | None = None, - total_balance_bigger_than: Wei | None = None, + self, + better_address: ChecksumAddress, + position_id_in: list[HexBytes] | None = None, + total_balance_bigger_than: Wei | None = None, ) -> list[OmenUserPosition]: where_stms: dict[str, t.Any] = { "user": better_address.lower(), @@ -511,18 +516,18 @@ def get_user_positions( return [OmenUserPosition.model_validate(i) for i in items] def get_trades( - self, - limit: int | None = None, - better_address: ChecksumAddress | None = None, - start_time: DatetimeUTC | None = None, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, - filter_by_answer_finalized_not_null: bool = False, - type_: t.Literal["Buy", "Sell"] | None = None, - market_opening_after: DatetimeUTC | None = None, - collateral_amount_more_than: Wei | None = None, - sort_by_field: FieldPath | None = None, - sort_direction: str | None = None, + self, + limit: int | None = None, + better_address: ChecksumAddress | None = None, + start_time: DatetimeUTC | None = None, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, + filter_by_answer_finalized_not_null: bool = False, + type_: t.Literal["Buy", "Sell"] | None = None, + market_opening_after: DatetimeUTC | None = None, + collateral_amount_more_than: Wei | None = None, + sort_by_field: FieldPath | None = None, + sort_direction: str | None = None, ) -> list[OmenBet]: if not end_time: end_time = utcnow() @@ -566,14 +571,14 @@ def get_trades( return [OmenBet.model_validate(i) for i in items] def get_bets( - self, - better_address: ChecksumAddress | None = None, - start_time: DatetimeUTC | None = None, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, - filter_by_answer_finalized_not_null: bool = False, - market_opening_after: DatetimeUTC | None = None, - collateral_amount_more_than: Wei | None = None, + self, + better_address: ChecksumAddress | None = None, + start_time: DatetimeUTC | None = None, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, + filter_by_answer_finalized_not_null: bool = False, + market_opening_after: DatetimeUTC | None = None, + collateral_amount_more_than: Wei | None = None, ) -> list[OmenBet]: return self.get_trades( better_address=better_address, @@ -587,11 +592,11 @@ def get_bets( ) def get_resolved_bets( - self, - better_address: ChecksumAddress, - start_time: DatetimeUTC, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, + self, + better_address: ChecksumAddress, + start_time: DatetimeUTC, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, ) -> list[OmenBet]: omen_bets = self.get_bets( better_address=better_address, @@ -603,11 +608,11 @@ def get_resolved_bets( return [b for b in omen_bets if b.fpmm.is_resolved] def get_resolved_bets_with_valid_answer( - self, - better_address: ChecksumAddress, - start_time: DatetimeUTC, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, + self, + better_address: ChecksumAddress, + start_time: DatetimeUTC, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, ) -> list[OmenBet]: bets = self.get_resolved_bets( better_address=better_address, @@ -619,17 +624,17 @@ def get_resolved_bets_with_valid_answer( @staticmethod def get_reality_question_filters( - user: HexAddress | None, - claimed: bool | None, - current_answer_before: DatetimeUTC | None, - finalized_before: DatetimeUTC | None, - finalized_after: DatetimeUTC | None, - with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - opened_before: t.Optional[DatetimeUTC], - opened_after: t.Optional[DatetimeUTC], - excluded_titles: set[str] | None, + user: HexAddress | None, + claimed: bool | None, + current_answer_before: DatetimeUTC | None, + finalized_before: DatetimeUTC | None, + finalized_after: DatetimeUTC | None, + with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + opened_before: t.Optional[DatetimeUTC], + opened_after: t.Optional[DatetimeUTC], + excluded_titles: set[str] | None, ) -> dict[str, t.Any]: """ Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different. @@ -687,15 +692,15 @@ def get_reality_question_filters( @staticmethod def get_omen_question_filters( - current_answer_before: DatetimeUTC | None, - finalized_before: DatetimeUTC | None, - finalized_after: DatetimeUTC | None, - with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - opened_before: t.Optional[DatetimeUTC], - opened_after: t.Optional[DatetimeUTC], - excluded_titles: set[str] | None, + current_answer_before: DatetimeUTC | None, + finalized_before: DatetimeUTC | None, + finalized_after: DatetimeUTC | None, + with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + opened_before: t.Optional[DatetimeUTC], + opened_after: t.Optional[DatetimeUTC], + excluded_titles: set[str] | None, ) -> dict[str, t.Any]: """ Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different. @@ -743,19 +748,19 @@ def get_omen_question_filters( return where_stms def get_questions( - self, - limit: int | None, - user: HexAddress | None = None, - claimed: bool | None = None, - current_answer_before: DatetimeUTC | None = None, - finalized_before: DatetimeUTC | None = None, - finalized_after: DatetimeUTC | None = None, - with_answers: bool | None = None, - question_id_in: list[HexBytes] | None = None, - question_id: HexBytes | None = None, - opened_before: DatetimeUTC | None = None, - opened_after: DatetimeUTC | None = None, - excluded_titles: set[str] | None = None, + self, + limit: int | None, + user: HexAddress | None = None, + claimed: bool | None = None, + current_answer_before: DatetimeUTC | None = None, + finalized_before: DatetimeUTC | None = None, + finalized_after: DatetimeUTC | None = None, + with_answers: bool | None = None, + question_id_in: list[HexBytes] | None = None, + question_id: HexBytes | None = None, + opened_before: DatetimeUTC | None = None, + opened_after: DatetimeUTC | None = None, + excluded_titles: set[str] | None = None, ) -> list[RealityQuestion]: where_stms: dict[str, t.Any] = self.get_reality_question_filters( user=user, @@ -795,20 +800,20 @@ def get_answers(self, question_id: HexBytes) -> list[RealityAnswer]: return [RealityAnswer.model_validate(i) for i in items] def get_responses( - self, - limit: int | None, - user: HexAddress | None = None, - question_user: HexAddress | None = None, - question_claimed: bool | None = None, - question_opened_before: t.Optional[DatetimeUTC] = None, - question_opened_after: t.Optional[DatetimeUTC] = None, - question_finalized_before: t.Optional[DatetimeUTC] = None, - question_finalized_after: t.Optional[DatetimeUTC] = None, - question_with_answers: bool | None = None, - question_id: HexBytes | None = None, - question_id_in: list[HexBytes] | None = None, - question_current_answer_before: DatetimeUTC | None = None, - question_excluded_titles: set[str] | None = None, + self, + limit: int | None, + user: HexAddress | None = None, + question_user: HexAddress | None = None, + question_claimed: bool | None = None, + question_opened_before: t.Optional[DatetimeUTC] = None, + question_opened_after: t.Optional[DatetimeUTC] = None, + question_finalized_before: t.Optional[DatetimeUTC] = None, + question_finalized_after: t.Optional[DatetimeUTC] = None, + question_with_answers: bool | None = None, + question_id: HexBytes | None = None, + question_id_in: list[HexBytes] | None = None, + question_current_answer_before: DatetimeUTC | None = None, + question_excluded_titles: set[str] | None = None, ) -> list[RealityResponse]: where_stms: dict[str, t.Any] = {} @@ -841,7 +846,7 @@ def get_responses( return [RealityResponse.model_validate(i) for i in items] def get_markets_from_all_user_positions( - self, user_positions: list[OmenUserPosition] + self, user_positions: list[OmenUserPosition] ) -> list[OmenMarket]: unique_condition_ids: list[HexBytes] = list( set(sum([u.position.conditionIds for u in user_positions], [])) @@ -852,7 +857,7 @@ def get_markets_from_all_user_positions( return markets def get_market_from_user_position( - self, user_position: OmenUserPosition + self, user_position: OmenUserPosition ) -> OmenMarket: """Markets and user positions are uniquely connected via condition_ids""" condition_ids = user_position.position.conditionIds @@ -884,7 +889,7 @@ def get_market_image(self, market_id: HexAddress) -> ImageType | None: ) def get_agent_results_for_market( - self, market_id: HexAddress | None = None + self, market_id: HexAddress | None = None ) -> list[ContractPrediction]: where_stms = {} if market_id: diff --git a/prediction_market_agent_tooling/tools/langfuse_client_utils.py b/prediction_market_agent_tooling/tools/langfuse_client_utils.py index 9c052800..1f888aab 100644 --- a/prediction_market_agent_tooling/tools/langfuse_client_utils.py +++ b/prediction_market_agent_tooling/tools/langfuse_client_utils.py @@ -70,6 +70,7 @@ def get_traces_for_agent( page = 1 # index starts from 1 all_agent_traces = [] while True: + logger.debug(f"fetching page {page}") traces = client.fetch_traces( name=trace_name, limit=100, diff --git a/pyproject.toml b/pyproject.toml index faad7c84..099c845f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ mypy = "^1.11.1" black = "^23.12.1" ape-foundry = "^0.8.2" eth-ape = "^0.8.10,<0.8.17" # 0.8.17 doesn't work with the current configuration and needs a fix, see https://github.com/gnosis/prediction-market-agent-tooling/issues/518. +diskcache = "^5.6.3" [build-system] requires = ["poetry-core"] From 2e0ac96a1fe8255ab8e648af1efe6c45580d53af Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 14:35:53 -0300 Subject: [PATCH 02/13] Financial metrics being published --- examples/monitor/data_models.py | 24 ++++++++ examples/monitor/financial_metrics.py | 55 +++++++++++++++++++ .../match_bets_with_langfuse_traces.py | 55 +++++++++++-------- examples/monitor/transaction_cache.py | 12 +++- 4 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 examples/monitor/data_models.py create mode 100644 examples/monitor/financial_metrics.py diff --git a/examples/monitor/data_models.py b/examples/monitor/data_models.py new file mode 100644 index 00000000..18aa5963 --- /dev/null +++ b/examples/monitor/data_models.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel + +from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC + + +class SimulationDetail(BaseModel): + strategy: str + url: str + market_p_yes: float + agent_p_yes: float + agent_conf: float + org_bet: float + sim_bet: float + org_dir: bool + sim_dir: bool + org_profit: float + sim_profit: float + timestamp: DatetimeUTC + + +class SharpeOutput(BaseModel): + annualized_volatility: float + mean_daily_return: float + annualized_sharpe_ratio: float diff --git a/examples/monitor/financial_metrics.py b/examples/monitor/financial_metrics.py new file mode 100644 index 00000000..3bfada20 --- /dev/null +++ b/examples/monitor/financial_metrics.py @@ -0,0 +1,55 @@ +import numpy as np +import pandas as pd + +from examples.monitor.data_models import SimulationDetail, SharpeOutput + + +class SharpeRatioCalculator: + def __init__(self, details: list[SimulationDetail], + + risk_free_rate: float = 0.) -> None: + self.details = details + self.df = pd.DataFrame([d.model_dump() for d in self.details]) + self.risk_free_rate = risk_free_rate + + def __has_df_valid_columns_else_exception(self, + required_columns: list[str] + ) -> None: + if not set(required_columns).issubset(self.df.columns): + raise ValueError("Dataframe doesn't contain all the required columns.") + + def prepare_wallet_daily_balance_df(self, timestamp_col_name: str, + profit_col_name: str) -> pd.DataFrame: + self.__has_df_valid_columns_else_exception([timestamp_col_name, profit_col_name]) + df2 = self.df.copy() + df2[timestamp_col_name] = pd.to_datetime(df2[timestamp_col_name]) + df2.sort_values(timestamp_col_name, ascending=True, inplace=True) + + df2['profit_cumsum'] = df2[profit_col_name].cumsum() + df2['profit_cumsum'] = df2['profit_cumsum'] + 50 + + df2 = df2.drop_duplicates(subset=timestamp_col_name, keep="last") + df2.set_index(timestamp_col_name, inplace=True) + # We generate a new Dataframe with daily wallet balances, derived by the final wallet balance + # from the previous day. + wallet_balance_daily_df = df2[['profit_cumsum']].resample('D').ffill() + wallet_balance_daily_df.dropna(inplace=True) + wallet_balance_daily_df["returns"] = wallet_balance_daily_df["profit_cumsum"].pct_change() + return wallet_balance_daily_df + + # ToDo - Write tests + def calculate_annual_sharpe_ratio(self, timestamp_col_name: str = "timestamp", + profit_col_name: str = "sim_profit") -> SharpeOutput: + wallet_daily_balance_df = self.prepare_wallet_daily_balance_df(timestamp_col_name=timestamp_col_name, + profit_col_name=profit_col_name) + + daily_volatility = wallet_daily_balance_df["returns"].std() + annualized_volatility = daily_volatility * np.sqrt(365) + mean_daily_return = wallet_daily_balance_df["returns"].mean() + daily_sharpe_ratio = (mean_daily_return - self.risk_free_rate) / daily_volatility + annualized_sharpe_ratio = daily_sharpe_ratio * np.sqrt(365) + return SharpeOutput( + annualized_volatility=annualized_volatility, + mean_daily_return=mean_daily_return, + annualized_sharpe_ratio=annualized_sharpe_ratio, + ) diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index 680d89fc..39370eea 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -4,6 +4,8 @@ import dotenv from eth_typing import HexAddress, HexStr +from examples.monitor.data_models import SimulationDetail +from examples.monitor.financial_metrics import SharpeRatioCalculator from examples.monitor.transaction_cache import TransactionBlockCache from prediction_market_agent_tooling.markets.omen.omen_contracts import OmenConditionalTokenContract from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import OmenSubgraphHandler @@ -186,9 +188,7 @@ def get_outcome_for_trace( print("# Agent Bet vs Simulated Bet Comparison") - w3 = OmenConditionalTokenContract().get_web3() - - tx_block_cache = TransactionBlockCache(web3=w3) + tx_block_cache = TransactionBlockCache(web3=OmenConditionalTokenContract().get_web3()) for agent_name, private_key in agent_pkey_map.items(): print(f"\n## {agent_name}\n") @@ -238,12 +238,13 @@ def get_outcome_for_trace( continue if len(bets_with_traces) != len(bets): + pct_bets_without_traces = (len(bets) - len(bets_with_traces)) / len(bets) print( - f"{len(bets) - len(bets_with_traces)} bets do not have a corresponding trace, ignoring them." + f"{len(bets) - len(bets_with_traces)} bets do not have a corresponding trace ({pct_bets_without_traces * 100:.2f}%), ignoring them." ) simulations: list[dict[str, Any]] = [] - details = [] + details: list[SimulationDetail] = [] for strategy_idx, strategy in enumerate(strategies): # "Born" agent with initial funding, simulate as if he was doing bets one by one. @@ -266,28 +267,34 @@ def get_outcome_for_trace( simulated_outcomes.append(simulated_outcome) agent_balance += simulated_outcome.profit - details.append( - { - "url": trace.market.url, - "market_p_yes": round(trace.market.current_p_yes, 4), - "agent_p_yes": round(trace.answer.p_yes, 4), - "agent_conf": round(trace.answer.confidence, 4), - "org_bet": round(bet.amount.amount, 4), - "sim_bet": round(simulated_outcome.size, 4), - "org_dir": bet.outcome, - "sim_dir": simulated_outcome.direction, - "org_profit": round(bet.profit.amount, 4), - "sim_profit": round(simulated_outcome.profit, 4), - "timestamp": bet_with_trace.trace.timestamp_datetime, - } + simulation_detail = SimulationDetail( + strategy=repr(strategy), + url=trace.market.url, + market_p_yes=round(trace.market.current_p_yes, 4), + agent_p_yes=round(trace.answer.p_yes, 4), + agent_conf=round(trace.answer.confidence, 4), + org_bet=round(bet.amount.amount, 4), + sim_bet=round(simulated_outcome.size, 4), + org_dir=bet.outcome, + sim_dir=simulated_outcome.direction, + org_profit=round(bet.profit.amount, 4), + sim_profit=round(simulated_outcome.profit, 4), + timestamp=bet_with_trace.trace.timestamp_datetime, ) + details.append(simulation_detail) - details.sort(key=lambda x: x["sim_profit"], reverse=True) - pd.DataFrame.from_records(details).to_csv( + details.sort(key=lambda x: x.sim_profit, reverse=True) + details_df = pd.DataFrame.from_records([d.model_dump() for d in details]) + details_df.to_csv( output_directory / f"{agent_name} - {strategy} - all bets.csv", index=False, ) + # Financial analysis + calc = SharpeRatioCalculator(details=details) + sharpe_output_simulation = calc.calculate_annual_sharpe_ratio() + sharpe_output_original = calc.calculate_annual_sharpe_ratio(profit_col_name="org_profit") + sum_squared_errors = 0.0 for bet_with_trace in bets_with_traces: bet = bet_with_trace.bet @@ -305,6 +312,7 @@ def get_outcome_for_trace( # At the beginning, add also the agent's current strategy. if strategy_idx == 0: + # ToDo - Add sharpe also to original strategy - see bets with traces (create new df) simulations.append( { "strategy": "original", @@ -315,6 +323,7 @@ def get_outcome_for_trace( # We don't know these for the original run. "start_balance": None, "end_balance": None, + **sharpe_output_original.model_dump(), } ) else: @@ -332,9 +341,7 @@ def get_outcome_for_trace( "p_yes mse": p_yes_mse, "start_balance": starting_balance, "end_balance": agent_balance, - "daily_return": 123, # ToDo - "annualized_sharpe_ratio": 123, # ToDo - "annualized_volatility": 123, # ToDo + **sharpe_output_simulation.model_dump(), } ) diff --git a/examples/monitor/transaction_cache.py b/examples/monitor/transaction_cache.py index 69b3ca0a..7210b2c5 100644 --- a/examples/monitor/transaction_cache.py +++ b/examples/monitor/transaction_cache.py @@ -5,7 +5,6 @@ from web3 import Web3 from prediction_market_agent_tooling.loggers import logger -from prediction_market_agent_tooling.tools.cache import persistent_inmemory_cache class TransactionBlockCache: @@ -14,12 +13,19 @@ def __init__(self, web3: Web3): self.cache = dc.Cache("block_cache_dir") self.web3 = web3 - @persistent_inmemory_cache @tenacity.retry( wait=wait_exponential(multiplier=1, min=1, max=4), stop=tenacity.stop_after_attempt(7), after=lambda x: logger.debug(f"fetch tx failed, {x.attempt_number=}."), ) - def get_block_number(self, transaction_hash: str) -> int: + def fetch_block_number(self, transaction_hash: str) -> int: tx = self.web3.eth.get_transaction(HexStr(transaction_hash)) return tx['blockNumber'] + + def get_block_number(self, tx_hash: str) -> int: + if tx_hash in self.cache: + return self.cache[tx_hash] + + block_number = self.fetch_block_number(tx_hash) + self.cache[tx_hash] = block_number + return block_number From ed67ff4825138537cee1c5adc096eba53ead655f Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 17:03:19 -0300 Subject: [PATCH 03/13] Reverted caching to diskcache --- .../match_bets_with_langfuse_traces.py | 6 ++--- examples/monitor/transaction_cache.py | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index 39370eea..c1bd8efd 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -227,8 +227,6 @@ def get_outcome_for_trace( bets_with_traces: list[ResolvedBetWithTrace] = [] for bet in bets: trace = get_trace_for_bet(bet, process_market_traces) - if not trace: - print('no trace') if trace: bets_with_traces.append(ResolvedBetWithTrace(bet=bet, trace=trace)) @@ -263,7 +261,7 @@ def get_outcome_for_trace( ) if simulated_outcome is None: continue - # ToDo - Add new metrics + simulated_outcomes.append(simulated_outcome) agent_balance += simulated_outcome.profit @@ -312,7 +310,7 @@ def get_outcome_for_trace( # At the beginning, add also the agent's current strategy. if strategy_idx == 0: - # ToDo - Add sharpe also to original strategy - see bets with traces (create new df) + simulations.append( { "strategy": "original", diff --git a/examples/monitor/transaction_cache.py b/examples/monitor/transaction_cache.py index 7210b2c5..d7b39fba 100644 --- a/examples/monitor/transaction_cache.py +++ b/examples/monitor/transaction_cache.py @@ -10,7 +10,8 @@ class TransactionBlockCache: def __init__(self, web3: Web3): - self.cache = dc.Cache("block_cache_dir") + self.block_number_cache = dc.Cache("block_cache_dir") + self.block_timestamp_cache = dc.Cache("timestamp_cache_dir") self.web3 = web3 @tenacity.retry( @@ -22,10 +23,28 @@ def fetch_block_number(self, transaction_hash: str) -> int: tx = self.web3.eth.get_transaction(HexStr(transaction_hash)) return tx['blockNumber'] + @tenacity.retry( + wait=wait_exponential(multiplier=1, min=1, max=4), + stop=tenacity.stop_after_attempt(7), + after=lambda x: logger.debug(f"fetch tx failed, {x.attempt_number=}."), + ) + def fetch_block_timestamp(self, block_number: int) -> int: + block = self.web3.eth.get_block(block_number) + return block['timestamp'] + def get_block_number(self, tx_hash: str) -> int: - if tx_hash in self.cache: - return self.cache[tx_hash] + if tx_hash in self.block_number_cache: + return self.block_number_cache[tx_hash] block_number = self.fetch_block_number(tx_hash) - self.cache[tx_hash] = block_number + self.block_number_cache[tx_hash] = block_number return block_number + + def get_block_timestamp(self, block_number: int) -> int: + if block_number in self.block_timestamp_cache: + print(f'cached block {block_number=}, result {self.block_timestamp_cache[block_number]=}') + return self.block_timestamp_cache[block_number] + + block_timestamp = self.fetch_block_timestamp(block_number) + self.block_timestamp_cache[block_number] = block_timestamp + return block_timestamp From 0535a86afdd03dd4aebafbf0941e8bd96dcead24 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 17:32:58 -0300 Subject: [PATCH 04/13] Fixing CI --- examples/monitor/financial_metrics.py | 58 ++-- .../match_bets_with_langfuse_traces.py | 53 +-- examples/monitor/transaction_cache.py | 10 +- .../markets/manifold/data_models.py | 1 - .../markets/omen/data_models.py | 11 +- .../markets/omen/omen_subgraph_handler.py | 307 +++++++++--------- 6 files changed, 227 insertions(+), 213 deletions(-) diff --git a/examples/monitor/financial_metrics.py b/examples/monitor/financial_metrics.py index 3bfada20..f25a13ae 100644 --- a/examples/monitor/financial_metrics.py +++ b/examples/monitor/financial_metrics.py @@ -1,52 +1,60 @@ import numpy as np import pandas as pd -from examples.monitor.data_models import SimulationDetail, SharpeOutput +from examples.monitor.data_models import SharpeOutput, SimulationDetail class SharpeRatioCalculator: - def __init__(self, details: list[SimulationDetail], - - risk_free_rate: float = 0.) -> None: + def __init__( + self, details: list[SimulationDetail], risk_free_rate: float = 0.0 + ) -> None: self.details = details self.df = pd.DataFrame([d.model_dump() for d in self.details]) self.risk_free_rate = risk_free_rate - def __has_df_valid_columns_else_exception(self, - required_columns: list[str] - ) -> None: + def __has_df_valid_columns_else_exception( + self, required_columns: list[str] + ) -> None: if not set(required_columns).issubset(self.df.columns): raise ValueError("Dataframe doesn't contain all the required columns.") - def prepare_wallet_daily_balance_df(self, timestamp_col_name: str, - profit_col_name: str) -> pd.DataFrame: - self.__has_df_valid_columns_else_exception([timestamp_col_name, profit_col_name]) - df2 = self.df.copy() - df2[timestamp_col_name] = pd.to_datetime(df2[timestamp_col_name]) - df2.sort_values(timestamp_col_name, ascending=True, inplace=True) + def prepare_wallet_daily_balance_df( + self, timestamp_col_name: str, profit_col_name: str + ) -> pd.DataFrame: + self.__has_df_valid_columns_else_exception( + [timestamp_col_name, profit_col_name] + ) + df = self.df.copy() + df[timestamp_col_name] = pd.to_datetime(df[timestamp_col_name]) + df.sort_values(timestamp_col_name, ascending=True, inplace=True) - df2['profit_cumsum'] = df2[profit_col_name].cumsum() - df2['profit_cumsum'] = df2['profit_cumsum'] + 50 + df["profit_cumsum"] = df[profit_col_name].cumsum() + df["profit_cumsum"] = df["profit_cumsum"] + 50 - df2 = df2.drop_duplicates(subset=timestamp_col_name, keep="last") - df2.set_index(timestamp_col_name, inplace=True) + df = df.drop_duplicates(subset=timestamp_col_name, keep="last") + df.set_index(timestamp_col_name, inplace=True) # We generate a new Dataframe with daily wallet balances, derived by the final wallet balance # from the previous day. - wallet_balance_daily_df = df2[['profit_cumsum']].resample('D').ffill() + wallet_balance_daily_df = df[["profit_cumsum"]].resample("D").ffill() wallet_balance_daily_df.dropna(inplace=True) - wallet_balance_daily_df["returns"] = wallet_balance_daily_df["profit_cumsum"].pct_change() + wallet_balance_daily_df["returns"] = wallet_balance_daily_df[ + "profit_cumsum" + ].pct_change() return wallet_balance_daily_df - # ToDo - Write tests - def calculate_annual_sharpe_ratio(self, timestamp_col_name: str = "timestamp", - profit_col_name: str = "sim_profit") -> SharpeOutput: - wallet_daily_balance_df = self.prepare_wallet_daily_balance_df(timestamp_col_name=timestamp_col_name, - profit_col_name=profit_col_name) + def calculate_annual_sharpe_ratio( + self, timestamp_col_name: str = "timestamp", profit_col_name: str = "sim_profit" + ) -> SharpeOutput: + wallet_daily_balance_df = self.prepare_wallet_daily_balance_df( + timestamp_col_name=timestamp_col_name, profit_col_name=profit_col_name + ) daily_volatility = wallet_daily_balance_df["returns"].std() annualized_volatility = daily_volatility * np.sqrt(365) mean_daily_return = wallet_daily_balance_df["returns"].mean() - daily_sharpe_ratio = (mean_daily_return - self.risk_free_rate) / daily_volatility + daily_sharpe_ratio = ( + mean_daily_return - self.risk_free_rate + ) / daily_volatility annualized_sharpe_ratio = daily_sharpe_ratio * np.sqrt(365) return SharpeOutput( annualized_volatility=annualized_volatility, diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index c1bd8efd..c436891b 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -7,8 +7,12 @@ from examples.monitor.data_models import SimulationDetail from examples.monitor.financial_metrics import SharpeRatioCalculator from examples.monitor.transaction_cache import TransactionBlockCache -from prediction_market_agent_tooling.markets.omen.omen_contracts import OmenConditionalTokenContract -from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import OmenSubgraphHandler +from prediction_market_agent_tooling.markets.omen.omen_contracts import ( + OmenConditionalTokenContract, +) +from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import ( + OmenSubgraphHandler, +) dotenv.load_dotenv() import pandas as pd @@ -51,11 +55,11 @@ class MSEProfit(BaseModel): def get_outcome_for_trace( - strategy: BettingStrategy, - trace: ProcessMarketTrace, - market_outcome: bool, - actual_placed_bet: ResolvedBet, - tx_block_cache: TransactionBlockCache + strategy: BettingStrategy, + trace: ProcessMarketTrace, + market_outcome: bool, + actual_placed_bet: ResolvedBet, + tx_block_cache: TransactionBlockCache, ) -> SimulatedOutcome | None: market = trace.market answer = trace.answer @@ -75,10 +79,10 @@ def get_outcome_for_trace( if not trades: return None assert ( - len(trades) == 1 + len(trades) == 1 ), f"Should be always one trade if no existing position is given: {trades=}; {answer=}; {market=}" assert ( - trades[0].trade_type == TradeType.BUY + trades[0].trade_type == TradeType.BUY ), "Can only buy without previous position." buy_trade = trades[0] correct = buy_trade.outcome == market_outcome @@ -88,12 +92,15 @@ def get_outcome_for_trace( else: # We use a historical state (by passing in a block_number as arg) to get the correct outcome token balances. tx_block_number = tx_block_cache.get_block_number(actual_placed_bet.id) - market_at_block = OmenSubgraphHandler().get_omen_market_by_market_id(HexAddress(HexStr(market.id)), - block_number=tx_block_number) + market_at_block = OmenSubgraphHandler().get_omen_market_by_market_id( + HexAddress(HexStr(market.id)), block_number=tx_block_number + ) omen_agent_market_at_block = OmenAgentMarket.from_data_model(market_at_block) received_outcome_tokens = omen_agent_market_at_block.get_buy_token_amount( - bet_amount=omen_agent_market_at_block.get_bet_amount(buy_trade.amount.amount), + bet_amount=omen_agent_market_at_block.get_bet_amount( + buy_trade.amount.amount + ), direction=buy_trade.outcome, ).amount profit = ( @@ -188,7 +195,9 @@ def get_outcome_for_trace( print("# Agent Bet vs Simulated Bet Comparison") - tx_block_cache = TransactionBlockCache(web3=OmenConditionalTokenContract().get_web3()) + tx_block_cache = TransactionBlockCache( + web3=OmenConditionalTokenContract().get_web3() + ) for agent_name, private_key in agent_pkey_map.items(): print(f"\n## {agent_name}\n") @@ -256,8 +265,11 @@ def get_outcome_for_trace( bet = bet_with_trace.bet trace = bet_with_trace.trace simulated_outcome = get_outcome_for_trace( - strategy=strategy, trace=trace, market_outcome=bet.market_outcome, - actual_placed_bet=bet, tx_block_cache=tx_block_cache + strategy=strategy, + trace=trace, + market_outcome=bet.market_outcome, + actual_placed_bet=bet, + tx_block_cache=tx_block_cache, ) if simulated_outcome is None: continue @@ -291,7 +303,9 @@ def get_outcome_for_trace( # Financial analysis calc = SharpeRatioCalculator(details=details) sharpe_output_simulation = calc.calculate_annual_sharpe_ratio() - sharpe_output_original = calc.calculate_annual_sharpe_ratio(profit_col_name="org_profit") + sharpe_output_original = calc.calculate_annual_sharpe_ratio( + profit_col_name="org_profit" + ) sum_squared_errors = 0.0 for bet_with_trace in bets_with_traces: @@ -310,7 +324,6 @@ def get_outcome_for_trace( # At the beginning, add also the agent's current strategy. if strategy_idx == 0: - simulations.append( { "strategy": "original", @@ -346,8 +359,8 @@ def get_outcome_for_trace( simulations_df = pd.DataFrame.from_records(simulations) simulations_df.sort_values(by="bet_profit", ascending=False, inplace=True) overall_md += ( - f"\n\n## {agent_name}\n\n{len(bets_with_traces)} bets\n\n" - + simulations_df.to_markdown(index=False) + f"\n\n## {agent_name}\n\n{len(bets_with_traces)} bets\n\n" + + simulations_df.to_markdown(index=False) ) # export details per agent pd.DataFrame.from_records(details).to_csv( @@ -362,6 +375,6 @@ def get_outcome_for_trace( print(f"{strategy_name}: {correlation=}") with open( - output_directory / "match_bets_with_langfuse_traces_overall.md", "w" + output_directory / "match_bets_with_langfuse_traces_overall.md", "w" ) as overall_f: overall_f.write(overall_md) diff --git a/examples/monitor/transaction_cache.py b/examples/monitor/transaction_cache.py index d7b39fba..6542e5a2 100644 --- a/examples/monitor/transaction_cache.py +++ b/examples/monitor/transaction_cache.py @@ -8,7 +8,6 @@ class TransactionBlockCache: - def __init__(self, web3: Web3): self.block_number_cache = dc.Cache("block_cache_dir") self.block_timestamp_cache = dc.Cache("timestamp_cache_dir") @@ -21,7 +20,7 @@ def __init__(self, web3: Web3): ) def fetch_block_number(self, transaction_hash: str) -> int: tx = self.web3.eth.get_transaction(HexStr(transaction_hash)) - return tx['blockNumber'] + return tx["blockNumber"] @tenacity.retry( wait=wait_exponential(multiplier=1, min=1, max=4), @@ -30,11 +29,11 @@ def fetch_block_number(self, transaction_hash: str) -> int: ) def fetch_block_timestamp(self, block_number: int) -> int: block = self.web3.eth.get_block(block_number) - return block['timestamp'] + return block["timestamp"] def get_block_number(self, tx_hash: str) -> int: if tx_hash in self.block_number_cache: - return self.block_number_cache[tx_hash] + return int(self.block_number_cache[tx_hash]) block_number = self.fetch_block_number(tx_hash) self.block_number_cache[tx_hash] = block_number @@ -42,8 +41,7 @@ def get_block_number(self, tx_hash: str) -> int: def get_block_timestamp(self, block_number: int) -> int: if block_number in self.block_timestamp_cache: - print(f'cached block {block_number=}, result {self.block_timestamp_cache[block_number]=}') - return self.block_timestamp_cache[block_number] + return int(self.block_timestamp_cache[block_number]) block_timestamp = self.fetch_block_timestamp(block_number) self.block_timestamp_cache[block_number] = block_timestamp diff --git a/prediction_market_agent_tooling/markets/manifold/data_models.py b/prediction_market_agent_tooling/markets/manifold/data_models.py index 0503dc84..939de111 100644 --- a/prediction_market_agent_tooling/markets/manifold/data_models.py +++ b/prediction_market_agent_tooling/markets/manifold/data_models.py @@ -200,7 +200,6 @@ def get_profit(self, market_outcome: bool) -> ProfitAmount: if self.get_resolved_boolean_outcome() == market_outcome else -self.amount ) - profit -= self.fees.get_total() return ProfitAmount( amount=profit, currency=Currency.Mana, diff --git a/prediction_market_agent_tooling/markets/omen/data_models.py b/prediction_market_agent_tooling/markets/omen/data_models.py index 9f27fbbd..4bdf19ad 100644 --- a/prediction_market_agent_tooling/markets/omen/data_models.py +++ b/prediction_market_agent_tooling/markets/omen/data_models.py @@ -530,7 +530,6 @@ def get_profit(self) -> ProfitAmount: if self.boolean_outcome == self.fpmm.boolean_outcome else -bet_amount_xdai ) - #profit -= wei_to_xdai(self.feeAmount) return ProfitAmount( amount=profit, currency=Currency.xDai, @@ -538,9 +537,8 @@ def get_profit(self) -> ProfitAmount: def to_bet(self) -> Bet: return Bet( - id=str( - self.transactionHash - ), # 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. + id=str(self.transactionHash), + # 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. amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai), outcome=self.boolean_outcome, created_time=self.creation_datetime, @@ -555,9 +553,8 @@ def to_generic_resolved_bet(self) -> ResolvedBet: ) return ResolvedBet( - id=str( - self.transactionHash - ), # 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. + id=str(self.transactionHash), + # 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. amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai), outcome=self.boolean_outcome, created_time=self.creation_datetime, diff --git a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py index d8233b37..abb3dd53 100644 --- a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +++ b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py @@ -121,7 +121,7 @@ def _get_fields_for_bets(self, bets_field: FieldPath) -> list[FieldPath]: return fields_for_bets + fields_for_markets def _get_fields_for_reality_questions( - self, questions_field: FieldPath + self, questions_field: FieldPath ) -> list[FieldPath]: # Note: Fields available on the Omen's subgraph Question are different from the Reality's subgraph Question. return [ @@ -160,7 +160,7 @@ def _get_fields_for_responses(self, responses_field: FieldPath) -> list[FieldPat ] + self._get_fields_for_reality_questions(responses_field.question) def _get_fields_for_market_questions( - self, questions_field: FieldPath + self, questions_field: FieldPath ) -> list[FieldPath]: # Note: Fields available on the Omen's subgraph Question are different from the Reality's subgraph Question. return [ @@ -202,26 +202,26 @@ def _get_fields_for_markets(self, markets_field: FieldPath) -> list[FieldPath]: ] + self._get_fields_for_market_questions(markets_field.question) def _build_where_statements( - self, - creator: HexAddress | None, - creator_in: t.Sequence[HexAddress] | None, - outcomes: list[str], - created_after: DatetimeUTC | None, - question_opened_before: DatetimeUTC | None, - question_opened_after: DatetimeUTC | None, - question_finalized_before: DatetimeUTC | None, - question_finalized_after: DatetimeUTC | None, - question_with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - question_current_answer_before: DatetimeUTC | None, - question_excluded_titles: set[str] | None, - resolved: bool | None, - liquidity_bigger_than: Wei | None, - condition_id_in: list[HexBytes] | None, - id_in: list[str] | None, - collateral_token_address_in: tuple[ChecksumAddress, ...] | None, - category: str | None, + self, + creator: HexAddress | None, + creator_in: t.Sequence[HexAddress] | None, + outcomes: list[str], + created_after: DatetimeUTC | None, + question_opened_before: DatetimeUTC | None, + question_opened_after: DatetimeUTC | None, + question_finalized_before: DatetimeUTC | None, + question_finalized_after: DatetimeUTC | None, + question_with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + question_current_answer_before: DatetimeUTC | None, + question_excluded_titles: set[str] | None, + resolved: bool | None, + liquidity_bigger_than: Wei | None, + condition_id_in: list[HexBytes] | None, + id_in: list[str] | None, + collateral_token_address_in: tuple[ChecksumAddress, ...] | None, + category: str | None, ) -> dict[str, t.Any]: where_stms: dict[str, t.Any] = { "isPendingArbitration": False, @@ -278,7 +278,7 @@ def _build_where_statements( return where_stms def _build_sort_params( - self, sort_by: SortBy + self, sort_by: SortBy ) -> tuple[str | None, FieldPath | None]: sort_direction: str | None sort_by_field: FieldPath | None @@ -313,19 +313,19 @@ def _build_sort_params( return sort_direction, sort_by_field def get_omen_binary_markets_simple( - self, - limit: t.Optional[int], - # Enumerated values for simpler usage. - filter_by: FilterBy, - sort_by: SortBy, - # Additional filters, these can not be modified by the enums above. - created_after: DatetimeUTC | None = None, - excluded_questions: set[str] | None = None, # question titles - collateral_token_address_in: ( - tuple[ChecksumAddress, ...] | None - ) = SAFE_COLLATERAL_TOKEN_MARKETS, - category: str | None = None, - creator_in: t.Sequence[HexAddress] | None = None, + self, + limit: t.Optional[int], + # Enumerated values for simpler usage. + filter_by: FilterBy, + sort_by: SortBy, + # Additional filters, these can not be modified by the enums above. + created_after: DatetimeUTC | None = None, + excluded_questions: set[str] | None = None, # question titles + collateral_token_address_in: ( + tuple[ChecksumAddress, ...] | None + ) = SAFE_COLLATERAL_TOKEN_MARKETS, + category: str | None = None, + creator_in: t.Sequence[HexAddress] | None = None, ) -> t.List[OmenMarket]: """ Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values. @@ -365,31 +365,31 @@ def get_omen_binary_markets_simple( ) def get_omen_binary_markets( - self, - limit: t.Optional[int], - creator: HexAddress | None = None, - creator_in: t.Sequence[HexAddress] | None = None, - created_after: DatetimeUTC | None = None, - question_opened_before: DatetimeUTC | None = None, - question_opened_after: DatetimeUTC | None = None, - question_finalized_before: DatetimeUTC | None = None, - question_finalized_after: DatetimeUTC | None = None, - question_with_answers: bool | None = None, - question_id: HexBytes | None = None, - question_id_in: list[HexBytes] | None = None, - question_current_answer_before: DatetimeUTC | None = None, - question_excluded_titles: set[str] | None = None, - resolved: bool | None = None, - liquidity_bigger_than: Wei | None = None, - condition_id_in: list[HexBytes] | None = None, - id_in: list[str] | None = None, - sort_by_field: FieldPath | None = None, - sort_direction: str | None = None, - outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES, - collateral_token_address_in: ( - tuple[ChecksumAddress, ...] | None - ) = SAFE_COLLATERAL_TOKEN_MARKETS, - category: str | None = None, + self, + limit: t.Optional[int], + creator: HexAddress | None = None, + creator_in: t.Sequence[HexAddress] | None = None, + created_after: DatetimeUTC | None = None, + question_opened_before: DatetimeUTC | None = None, + question_opened_after: DatetimeUTC | None = None, + question_finalized_before: DatetimeUTC | None = None, + question_finalized_after: DatetimeUTC | None = None, + question_with_answers: bool | None = None, + question_id: HexBytes | None = None, + question_id_in: list[HexBytes] | None = None, + question_current_answer_before: DatetimeUTC | None = None, + question_excluded_titles: set[str] | None = None, + resolved: bool | None = None, + liquidity_bigger_than: Wei | None = None, + condition_id_in: list[HexBytes] | None = None, + id_in: list[str] | None = None, + sort_by_field: FieldPath | None = None, + sort_direction: str | None = None, + outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES, + collateral_token_address_in: ( + tuple[ChecksumAddress, ...] | None + ) = SAFE_COLLATERAL_TOKEN_MARKETS, + category: str | None = None, ) -> t.List[OmenMarket]: """ Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums. @@ -435,15 +435,14 @@ def get_omen_binary_markets( omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket) return omen_markets - def get_omen_market_by_market_id(self, market_id: HexAddress, block_number: int | None = None) -> OmenMarket: - - query_filters = {"id": market_id.lower()} + def get_omen_market_by_market_id( + self, market_id: HexAddress, block_number: int | None = None + ) -> OmenMarket: + query_filters: dict[str, t.Any] = {"id": market_id.lower()} if block_number: query_filters["block"] = {"number": block_number} - markets = self.trades_subgraph.Query.fixedProductMarketMaker( - **query_filters - ) + markets = self.trades_subgraph.Query.fixedProductMarketMaker(**query_filters) fields = self._get_fields_for_markets(markets) omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket) @@ -456,7 +455,7 @@ def get_omen_market_by_market_id(self, market_id: HexAddress, block_number: int return omen_markets[0] def _get_fields_for_user_positions( - self, user_positions: FieldPath + self, user_positions: FieldPath ) -> list[FieldPath]: return [ user_positions.id, @@ -474,8 +473,8 @@ def _get_fields_for_positions(self, positions: FieldPath) -> list[FieldPath]: ] def get_positions( - self, - condition_id: HexBytes | None = None, + self, + condition_id: HexBytes | None = None, ) -> list[OmenPosition]: where_stms: dict[str, t.Any] = {} @@ -491,10 +490,10 @@ def get_positions( return [OmenPosition.model_validate(i) for i in items] def get_user_positions( - self, - better_address: ChecksumAddress, - position_id_in: list[HexBytes] | None = None, - total_balance_bigger_than: Wei | None = None, + self, + better_address: ChecksumAddress, + position_id_in: list[HexBytes] | None = None, + total_balance_bigger_than: Wei | None = None, ) -> list[OmenUserPosition]: where_stms: dict[str, t.Any] = { "user": better_address.lower(), @@ -516,18 +515,18 @@ def get_user_positions( return [OmenUserPosition.model_validate(i) for i in items] def get_trades( - self, - limit: int | None = None, - better_address: ChecksumAddress | None = None, - start_time: DatetimeUTC | None = None, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, - filter_by_answer_finalized_not_null: bool = False, - type_: t.Literal["Buy", "Sell"] | None = None, - market_opening_after: DatetimeUTC | None = None, - collateral_amount_more_than: Wei | None = None, - sort_by_field: FieldPath | None = None, - sort_direction: str | None = None, + self, + limit: int | None = None, + better_address: ChecksumAddress | None = None, + start_time: DatetimeUTC | None = None, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, + filter_by_answer_finalized_not_null: bool = False, + type_: t.Literal["Buy", "Sell"] | None = None, + market_opening_after: DatetimeUTC | None = None, + collateral_amount_more_than: Wei | None = None, + sort_by_field: FieldPath | None = None, + sort_direction: str | None = None, ) -> list[OmenBet]: if not end_time: end_time = utcnow() @@ -571,14 +570,14 @@ def get_trades( return [OmenBet.model_validate(i) for i in items] def get_bets( - self, - better_address: ChecksumAddress | None = None, - start_time: DatetimeUTC | None = None, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, - filter_by_answer_finalized_not_null: bool = False, - market_opening_after: DatetimeUTC | None = None, - collateral_amount_more_than: Wei | None = None, + self, + better_address: ChecksumAddress | None = None, + start_time: DatetimeUTC | None = None, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, + filter_by_answer_finalized_not_null: bool = False, + market_opening_after: DatetimeUTC | None = None, + collateral_amount_more_than: Wei | None = None, ) -> list[OmenBet]: return self.get_trades( better_address=better_address, @@ -592,11 +591,11 @@ def get_bets( ) def get_resolved_bets( - self, - better_address: ChecksumAddress, - start_time: DatetimeUTC, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, + self, + better_address: ChecksumAddress, + start_time: DatetimeUTC, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, ) -> list[OmenBet]: omen_bets = self.get_bets( better_address=better_address, @@ -608,11 +607,11 @@ def get_resolved_bets( return [b for b in omen_bets if b.fpmm.is_resolved] def get_resolved_bets_with_valid_answer( - self, - better_address: ChecksumAddress, - start_time: DatetimeUTC, - end_time: t.Optional[DatetimeUTC] = None, - market_id: t.Optional[ChecksumAddress] = None, + self, + better_address: ChecksumAddress, + start_time: DatetimeUTC, + end_time: t.Optional[DatetimeUTC] = None, + market_id: t.Optional[ChecksumAddress] = None, ) -> list[OmenBet]: bets = self.get_resolved_bets( better_address=better_address, @@ -624,17 +623,17 @@ def get_resolved_bets_with_valid_answer( @staticmethod def get_reality_question_filters( - user: HexAddress | None, - claimed: bool | None, - current_answer_before: DatetimeUTC | None, - finalized_before: DatetimeUTC | None, - finalized_after: DatetimeUTC | None, - with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - opened_before: t.Optional[DatetimeUTC], - opened_after: t.Optional[DatetimeUTC], - excluded_titles: set[str] | None, + user: HexAddress | None, + claimed: bool | None, + current_answer_before: DatetimeUTC | None, + finalized_before: DatetimeUTC | None, + finalized_after: DatetimeUTC | None, + with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + opened_before: t.Optional[DatetimeUTC], + opened_after: t.Optional[DatetimeUTC], + excluded_titles: set[str] | None, ) -> dict[str, t.Any]: """ Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different. @@ -692,15 +691,15 @@ def get_reality_question_filters( @staticmethod def get_omen_question_filters( - current_answer_before: DatetimeUTC | None, - finalized_before: DatetimeUTC | None, - finalized_after: DatetimeUTC | None, - with_answers: bool | None, - question_id: HexBytes | None, - question_id_in: list[HexBytes] | None, - opened_before: t.Optional[DatetimeUTC], - opened_after: t.Optional[DatetimeUTC], - excluded_titles: set[str] | None, + current_answer_before: DatetimeUTC | None, + finalized_before: DatetimeUTC | None, + finalized_after: DatetimeUTC | None, + with_answers: bool | None, + question_id: HexBytes | None, + question_id_in: list[HexBytes] | None, + opened_before: t.Optional[DatetimeUTC], + opened_after: t.Optional[DatetimeUTC], + excluded_titles: set[str] | None, ) -> dict[str, t.Any]: """ Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different. @@ -748,19 +747,19 @@ def get_omen_question_filters( return where_stms def get_questions( - self, - limit: int | None, - user: HexAddress | None = None, - claimed: bool | None = None, - current_answer_before: DatetimeUTC | None = None, - finalized_before: DatetimeUTC | None = None, - finalized_after: DatetimeUTC | None = None, - with_answers: bool | None = None, - question_id_in: list[HexBytes] | None = None, - question_id: HexBytes | None = None, - opened_before: DatetimeUTC | None = None, - opened_after: DatetimeUTC | None = None, - excluded_titles: set[str] | None = None, + self, + limit: int | None, + user: HexAddress | None = None, + claimed: bool | None = None, + current_answer_before: DatetimeUTC | None = None, + finalized_before: DatetimeUTC | None = None, + finalized_after: DatetimeUTC | None = None, + with_answers: bool | None = None, + question_id_in: list[HexBytes] | None = None, + question_id: HexBytes | None = None, + opened_before: DatetimeUTC | None = None, + opened_after: DatetimeUTC | None = None, + excluded_titles: set[str] | None = None, ) -> list[RealityQuestion]: where_stms: dict[str, t.Any] = self.get_reality_question_filters( user=user, @@ -800,20 +799,20 @@ def get_answers(self, question_id: HexBytes) -> list[RealityAnswer]: return [RealityAnswer.model_validate(i) for i in items] def get_responses( - self, - limit: int | None, - user: HexAddress | None = None, - question_user: HexAddress | None = None, - question_claimed: bool | None = None, - question_opened_before: t.Optional[DatetimeUTC] = None, - question_opened_after: t.Optional[DatetimeUTC] = None, - question_finalized_before: t.Optional[DatetimeUTC] = None, - question_finalized_after: t.Optional[DatetimeUTC] = None, - question_with_answers: bool | None = None, - question_id: HexBytes | None = None, - question_id_in: list[HexBytes] | None = None, - question_current_answer_before: DatetimeUTC | None = None, - question_excluded_titles: set[str] | None = None, + self, + limit: int | None, + user: HexAddress | None = None, + question_user: HexAddress | None = None, + question_claimed: bool | None = None, + question_opened_before: t.Optional[DatetimeUTC] = None, + question_opened_after: t.Optional[DatetimeUTC] = None, + question_finalized_before: t.Optional[DatetimeUTC] = None, + question_finalized_after: t.Optional[DatetimeUTC] = None, + question_with_answers: bool | None = None, + question_id: HexBytes | None = None, + question_id_in: list[HexBytes] | None = None, + question_current_answer_before: DatetimeUTC | None = None, + question_excluded_titles: set[str] | None = None, ) -> list[RealityResponse]: where_stms: dict[str, t.Any] = {} @@ -846,7 +845,7 @@ def get_responses( return [RealityResponse.model_validate(i) for i in items] def get_markets_from_all_user_positions( - self, user_positions: list[OmenUserPosition] + self, user_positions: list[OmenUserPosition] ) -> list[OmenMarket]: unique_condition_ids: list[HexBytes] = list( set(sum([u.position.conditionIds for u in user_positions], [])) @@ -857,7 +856,7 @@ def get_markets_from_all_user_positions( return markets def get_market_from_user_position( - self, user_position: OmenUserPosition + self, user_position: OmenUserPosition ) -> OmenMarket: """Markets and user positions are uniquely connected via condition_ids""" condition_ids = user_position.position.conditionIds @@ -889,7 +888,7 @@ def get_market_image(self, market_id: HexAddress) -> ImageType | None: ) def get_agent_results_for_market( - self, market_id: HexAddress | None = None + self, market_id: HexAddress | None = None ) -> list[ContractPrediction]: where_stms = {} if market_id: From 05c7142d6729e0c1fbd11db05658368e438b4d8b Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 17:33:40 -0300 Subject: [PATCH 05/13] lock file --- poetry.lock | 603 +++++++++++++++++++++++++--------------------------- 1 file changed, 295 insertions(+), 308 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5dc178c7..e0d2d88e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -993,6 +993,17 @@ files = [ [package.dependencies] packaging = "*" +[[package]] +name = "diskcache" +version = "5.6.3" +description = "Disk Cache -- Disk and file backed persistent cache." +optional = false +python-versions = ">=3" +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + [[package]] name = "distro" version = "1.9.0" @@ -1679,13 +1690,13 @@ uritemplate = ">=3.0.1,<5" [[package]] name = "google-auth" -version = "2.35.0" +version = "2.36.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, - {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, ] [package.dependencies] @@ -2618,13 +2629,13 @@ langchain-core = ">=0.2.38,<0.3.0" [[package]] name = "langfuse" -version = "2.53.8" +version = "2.53.9" description = "A client library for accessing langfuse" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langfuse-2.53.8-py3-none-any.whl", hash = "sha256:47d74d83d0cf4146dccb00c821004d62440c0367b83aae73277475a8d6c1bd18"}, - {file = "langfuse-2.53.8.tar.gz", hash = "sha256:99801776565f83b7c1e91476bb325efd3ea08f61de3e701963ba60d1274e920e"}, + {file = "langfuse-2.53.9-py3-none-any.whl", hash = "sha256:04363bc323f7513621c88a997003f7b906ae8f5d096bd54221cfcb6bf7a6f16a"}, + {file = "langfuse-2.53.9.tar.gz", hash = "sha256:6bfecf86e28c684034ae52a0b19535c94cc86923085267b548d63e5c1ce2b82c"}, ] [package.dependencies] @@ -3243,13 +3254,13 @@ files = [ [[package]] name = "openai" -version = "1.54.1" +version = "1.54.3" description = "The official Python library for the openai API" optional = true python-versions = ">=3.8" files = [ - {file = "openai-1.54.1-py3-none-any.whl", hash = "sha256:3cb49ccb6bfdc724ad01cc397d323ef8314fc7d45e19e9de2afdd6484a533324"}, - {file = "openai-1.54.1.tar.gz", hash = "sha256:5b832bf82002ba8c4f6e5e25c1c0f5d468c22f043711544c716eaffdb30dd6f1"}, + {file = "openai-1.54.3-py3-none-any.whl", hash = "sha256:f18dbaf09c50d70c4185b892a2a553f80681d1d866323a2da7f7be2f688615d5"}, + {file = "openai-1.54.3.tar.gz", hash = "sha256:7511b74eeb894ac0b0253dc71f087a15d2e4d71d22d0088767205143d880cca6"}, ] [package.dependencies] @@ -3484,95 +3495,90 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "10.4.0" +version = "11.0.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, - {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, - {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -4161,7 +4167,6 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] @@ -4172,7 +4177,6 @@ description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] @@ -4724,105 +4728,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -4901,114 +4905,101 @@ test = ["hypothesis (==5.19.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "rpds-py" -version = "0.20.1" +version = "0.21.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a649dfd735fff086e8a9d0503a9f0c7d01b7912a333c7ae77e1515c08c146dad"}, - {file = "rpds_py-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f16bc1334853e91ddaaa1217045dd7be166170beec337576818461268a3de67f"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14511a539afee6f9ab492b543060c7491c99924314977a55c98bfa2ee29ce78c"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ccb8ac2d3c71cda472b75af42818981bdacf48d2e21c36331b50b4f16930163"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c142b88039b92e7e0cb2552e8967077e3179b22359e945574f5e2764c3953dcf"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f19169781dddae7478a32301b499b2858bc52fc45a112955e798ee307e294977"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13c56de6518e14b9bf6edde23c4c39dac5b48dcf04160ea7bce8fca8397cdf86"}, - {file = "rpds_py-0.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:925d176a549f4832c6f69fa6026071294ab5910e82a0fe6c6228fce17b0706bd"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78f0b6877bfce7a3d1ff150391354a410c55d3cdce386f862926a4958ad5ab7e"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3dd645e2b0dcb0fd05bf58e2e54c13875847687d0b71941ad2e757e5d89d4356"}, - {file = "rpds_py-0.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4f676e21db2f8c72ff0936f895271e7a700aa1f8d31b40e4e43442ba94973899"}, - {file = "rpds_py-0.20.1-cp310-none-win32.whl", hash = "sha256:648386ddd1e19b4a6abab69139b002bc49ebf065b596119f8f37c38e9ecee8ff"}, - {file = "rpds_py-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:d9ecb51120de61e4604650666d1f2b68444d46ae18fd492245a08f53ad2b7711"}, - {file = "rpds_py-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:762703bdd2b30983c1d9e62b4c88664df4a8a4d5ec0e9253b0231171f18f6d75"}, - {file = "rpds_py-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0b581f47257a9fce535c4567782a8976002d6b8afa2c39ff616edf87cbeff712"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842c19a6ce894493563c3bd00d81d5100e8e57d70209e84d5491940fdb8b9e3a"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42cbde7789f5c0bcd6816cb29808e36c01b960fb5d29f11e052215aa85497c93"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c8e9340ce5a52f95fa7d3b552b35c7e8f3874d74a03a8a69279fd5fca5dc751"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ba6f89cac95c0900d932c9efb7f0fb6ca47f6687feec41abcb1bd5e2bd45535"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a916087371afd9648e1962e67403c53f9c49ca47b9680adbeef79da3a7811b0"}, - {file = "rpds_py-0.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:200a23239781f46149e6a415f1e870c5ef1e712939fe8fa63035cd053ac2638e"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:58b1d5dd591973d426cbb2da5e27ba0339209832b2f3315928c9790e13f159e8"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6b73c67850ca7cae0f6c56f71e356d7e9fa25958d3e18a64927c2d930859b8e4"}, - {file = "rpds_py-0.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d8761c3c891cc51e90bc9926d6d2f59b27beaf86c74622c8979380a29cc23ac3"}, - {file = "rpds_py-0.20.1-cp311-none-win32.whl", hash = "sha256:cd945871335a639275eee904caef90041568ce3b42f402c6959b460d25ae8732"}, - {file = "rpds_py-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:7e21b7031e17c6b0e445f42ccc77f79a97e2687023c5746bfb7a9e45e0921b84"}, - {file = "rpds_py-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:36785be22066966a27348444b40389f8444671630063edfb1a2eb04318721e17"}, - {file = "rpds_py-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:142c0a5124d9bd0e2976089484af5c74f47bd3298f2ed651ef54ea728d2ea42c"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbddc10776ca7ebf2a299c41a4dde8ea0d8e3547bfd731cb87af2e8f5bf8962d"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15a842bb369e00295392e7ce192de9dcbf136954614124a667f9f9f17d6a216f"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be5ef2f1fc586a7372bfc355986226484e06d1dc4f9402539872c8bb99e34b01"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbcf360c9e3399b056a238523146ea77eeb2a596ce263b8814c900263e46031a"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd27a66740ffd621d20b9a2f2b5ee4129a56e27bfb9458a3bcc2e45794c96cb"}, - {file = "rpds_py-0.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0b937b2a1988f184a3e9e577adaa8aede21ec0b38320d6009e02bd026db04fa"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6889469bfdc1eddf489729b471303739bf04555bb151fe8875931f8564309afc"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19b73643c802f4eaf13d97f7855d0fb527fbc92ab7013c4ad0e13a6ae0ed23bd"}, - {file = "rpds_py-0.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c6afcf2338e7f374e8edc765c79fbcb4061d02b15dd5f8f314a4af2bdc7feb5"}, - {file = "rpds_py-0.20.1-cp312-none-win32.whl", hash = "sha256:dc73505153798c6f74854aba69cc75953888cf9866465196889c7cdd351e720c"}, - {file = "rpds_py-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:8bbe951244a838a51289ee53a6bae3a07f26d4e179b96fc7ddd3301caf0518eb"}, - {file = "rpds_py-0.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6ca91093a4a8da4afae7fe6a222c3b53ee4eef433ebfee4d54978a103435159e"}, - {file = "rpds_py-0.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b9c2fe36d1f758b28121bef29ed1dee9b7a2453e997528e7d1ac99b94892527c"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f009c69bc8c53db5dfab72ac760895dc1f2bc1b62ab7408b253c8d1ec52459fc"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6740a3e8d43a32629bb9b009017ea5b9e713b7210ba48ac8d4cb6d99d86c8ee8"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32b922e13d4c0080d03e7b62991ad7f5007d9cd74e239c4b16bc85ae8b70252d"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe00a9057d100e69b4ae4a094203a708d65b0f345ed546fdef86498bf5390982"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fe9b04b6fa685bd39237d45fad89ba19e9163a1ccaa16611a812e682913496"}, - {file = "rpds_py-0.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa7ac11e294304e615b43f8c441fee5d40094275ed7311f3420d805fde9b07b4"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aa97af1558a9bef4025f8f5d8c60d712e0a3b13a2fe875511defc6ee77a1ab7"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:483b29f6f7ffa6af845107d4efe2e3fa8fb2693de8657bc1849f674296ff6a5a"}, - {file = "rpds_py-0.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37fe0f12aebb6a0e3e17bb4cd356b1286d2d18d2e93b2d39fe647138458b4bcb"}, - {file = "rpds_py-0.20.1-cp313-none-win32.whl", hash = "sha256:a624cc00ef2158e04188df5e3016385b9353638139a06fb77057b3498f794782"}, - {file = "rpds_py-0.20.1-cp313-none-win_amd64.whl", hash = "sha256:b71b8666eeea69d6363248822078c075bac6ed135faa9216aa85f295ff009b1e"}, - {file = "rpds_py-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5b48e790e0355865197ad0aca8cde3d8ede347831e1959e158369eb3493d2191"}, - {file = "rpds_py-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3e310838a5801795207c66c73ea903deda321e6146d6f282e85fa7e3e4854804"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249280b870e6a42c0d972339e9cc22ee98730a99cd7f2f727549af80dd5a963"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e79059d67bea28b53d255c1437b25391653263f0e69cd7dec170d778fdbca95e"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b431c777c9653e569986ecf69ff4a5dba281cded16043d348bf9ba505486f36"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da584ff96ec95e97925174eb8237e32f626e7a1a97888cdd27ee2f1f24dd0ad8"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a0629ec053fc013808a85178524e3cb63a61dbc35b22499870194a63578fb9"}, - {file = "rpds_py-0.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fbf15aff64a163db29a91ed0868af181d6f68ec1a3a7d5afcfe4501252840bad"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:07924c1b938798797d60c6308fa8ad3b3f0201802f82e4a2c41bb3fafb44cc28"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4a5a844f68776a7715ecb30843b453f07ac89bad393431efbf7accca3ef599c1"}, - {file = "rpds_py-0.20.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:518d2ca43c358929bf08f9079b617f1c2ca6e8848f83c1225c88caeac46e6cbc"}, - {file = "rpds_py-0.20.1-cp38-none-win32.whl", hash = "sha256:3aea7eed3e55119635a74bbeb80b35e776bafccb70d97e8ff838816c124539f1"}, - {file = "rpds_py-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:7dca7081e9a0c3b6490a145593f6fe3173a94197f2cb9891183ef75e9d64c425"}, - {file = "rpds_py-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b41b6321805c472f66990c2849e152aff7bc359eb92f781e3f606609eac877ad"}, - {file = "rpds_py-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a90c373ea2975519b58dece25853dbcb9779b05cc46b4819cb1917e3b3215b6"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16d4477bcb9fbbd7b5b0e4a5d9b493e42026c0bf1f06f723a9353f5153e75d30"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84b8382a90539910b53a6307f7c35697bc7e6ffb25d9c1d4e998a13e842a5e83"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4888e117dd41b9d34194d9e31631af70d3d526efc363085e3089ab1a62c32ed1"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5265505b3d61a0f56618c9b941dc54dc334dc6e660f1592d112cd103d914a6db"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e75ba609dba23f2c95b776efb9dd3f0b78a76a151e96f96cc5b6b1b0004de66f"}, - {file = "rpds_py-0.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1791ff70bc975b098fe6ecf04356a10e9e2bd7dc21fa7351c1742fdeb9b4966f"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d126b52e4a473d40232ec2052a8b232270ed1f8c9571aaf33f73a14cc298c24f"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c14937af98c4cc362a1d4374806204dd51b1e12dded1ae30645c298e5a5c4cb1"}, - {file = "rpds_py-0.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3d089d0b88996df627693639d123c8158cff41c0651f646cd8fd292c7da90eaf"}, - {file = "rpds_py-0.20.1-cp39-none-win32.whl", hash = "sha256:653647b8838cf83b2e7e6a0364f49af96deec64d2a6578324db58380cff82aca"}, - {file = "rpds_py-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:fa41a64ac5b08b292906e248549ab48b69c5428f3987b09689ab2441f267d04d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a07ced2b22f0cf0b55a6a510078174c31b6d8544f3bc00c2bcee52b3d613f74"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:68cb0a499f2c4a088fd2f521453e22ed3527154136a855c62e148b7883b99f9a"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3060d885657abc549b2a0f8e1b79699290e5d83845141717c6c90c2df38311"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95f3b65d2392e1c5cec27cff08fdc0080270d5a1a4b2ea1d51d5f4a2620ff08d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cc3712a4b0b76a1d45a9302dd2f53ff339614b1c29603a911318f2357b04dd2"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d4eea0761e37485c9b81400437adb11c40e13ef513375bbd6973e34100aeb06"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f5179583d7a6cdb981151dd349786cbc318bab54963a192692d945dd3f6435d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fbb0ffc754490aff6dabbf28064be47f0f9ca0b9755976f945214965b3ace7e"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a94e52537a0e0a85429eda9e49f272ada715506d3b2431f64b8a3e34eb5f3e75"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:92b68b79c0da2a980b1c4197e56ac3dd0c8a149b4603747c4378914a68706979"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:93da1d3db08a827eda74356f9f58884adb254e59b6664f64cc04cdff2cc19b0d"}, - {file = "rpds_py-0.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:754bbed1a4ca48479e9d4182a561d001bbf81543876cdded6f695ec3d465846b"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ca449520e7484534a2a44faf629362cae62b660601432d04c482283c47eaebab"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9c4cb04a16b0f199a8c9bf807269b2f63b7b5b11425e4a6bd44bd6961d28282c"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63804105143c7e24cee7db89e37cb3f3941f8e80c4379a0b355c52a52b6780"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55cd1fa4ecfa6d9f14fbd97ac24803e6f73e897c738f771a9fe038f2f11ff07c"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f8f741b6292c86059ed175d80eefa80997125b7c478fb8769fd9ac8943a16c0"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fc212779bf8411667234b3cdd34d53de6c2b8b8b958e1e12cb473a5f367c338"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad56edabcdb428c2e33bbf24f255fe2b43253b7d13a2cdbf05de955217313e6"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a3a1e9ee9728b2c1734f65d6a1d376c6f2f6fdcc13bb007a08cc4b1ff576dc5"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e13de156137b7095442b288e72f33503a469aa1980ed856b43c353ac86390519"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:07f59760ef99f31422c49038964b31c4dfcfeb5d2384ebfc71058a7c9adae2d2"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:59240685e7da61fb78f65a9f07f8108e36a83317c53f7b276b4175dc44151684"}, - {file = "rpds_py-0.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:83cba698cfb3c2c5a7c3c6bac12fe6c6a51aae69513726be6411076185a8b24a"}, - {file = "rpds_py-0.20.1.tar.gz", hash = "sha256:e1791c4aabd117653530dccd24108fa03cc6baf21f58b950d0a73c3b3b29a350"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, + {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, + {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, + {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, + {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, + {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, + {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, + {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, + {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, + {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, + {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, ] [[package]] @@ -5104,11 +5095,6 @@ files = [ {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, - {file = "scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5"}, - {file = "scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908"}, - {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3"}, - {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12"}, - {file = "scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, @@ -5403,13 +5389,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "streamlit" -version = "1.39.0" +version = "1.40.0" description = "A faster way to build and share data apps" optional = false python-versions = "!=3.9.7,>=3.8" files = [ - {file = "streamlit-1.39.0-py2.py3-none-any.whl", hash = "sha256:a359fc54ed568b35b055ff1d453c320735539ad12e264365a36458aef55a5fba"}, - {file = "streamlit-1.39.0.tar.gz", hash = "sha256:fef9de7983c4ee65c08e85607d7ffccb56b00482b1041fa62f90e4815d39df3a"}, + {file = "streamlit-1.40.0-py2.py3-none-any.whl", hash = "sha256:05d22bc111d682ef4deaf7ededeec2305051b99dd6d7d564788705e4ce6f8029"}, + {file = "streamlit-1.40.0.tar.gz", hash = "sha256:6e4d3b90c4934951f97d790daf7953df5beb2916e447ac9f78e1b76a9ef83327"}, ] [package.dependencies] @@ -5421,7 +5407,7 @@ gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4" numpy = ">=1.20,<3" packaging = ">=20,<25" pandas = ">=1.4.0,<3" -pillow = ">=7.1.0,<11" +pillow = ">=7.1.0,<12" protobuf = ">=3.20,<6" pyarrow = ">=7.0" pydeck = ">=0.8.0b4,<1" @@ -5617,13 +5603,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.6" +version = "4.67.0" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, - {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, + {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, + {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, ] [package.dependencies] @@ -5631,6 +5617,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -6196,4 +6183,4 @@ openai = ["openai"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "74d3b83df9a67ca2a05b705831dd0ee01531f5f78c69768de0bf9f95db5c5648" +content-hash = "c05f4e8cffb50025acf124d6fbf3c953dd151977c9ee5e72741a4a09c0e2cc1f" From e6f2349485cd5367bcad14d6f2209e351776a169 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 19:14:14 -0300 Subject: [PATCH 06/13] Updated network foundry --- tests_integration_with_local_chain/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integration_with_local_chain/conftest.py b/tests_integration_with_local_chain/conftest.py index 981d4d97..791d772a 100644 --- a/tests_integration_with_local_chain/conftest.py +++ b/tests_integration_with_local_chain/conftest.py @@ -43,7 +43,7 @@ def local_web3( else: print("using foundry") with chain.network_manager.parse_network_choice( - "gnosis:mainnet_fork:foundry" + "gnosis:mainnet-fork:foundry" ) as provider: w3 = Web3(Web3.HTTPProvider(provider.http_uri)) yield w3 From 3c4ba383b065c62744f6e18aceb7c54d0c3b1245 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 19:47:28 -0300 Subject: [PATCH 07/13] Updated RPC to gateway --- ape-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ape-config.yaml b/ape-config.yaml index bc11a968..71f4898e 100644 --- a/ape-config.yaml +++ b/ape-config.yaml @@ -8,7 +8,7 @@ default_ecosystem: gnosis node: gnosis: mainnet: - uri: https://rpc.gnosischain.com + uri: https://rpc.gnosis.gateway.fm #https://rpc.gnosischain.com networks: From b8cbc51f8d07686f55bc23419094d9b5242ebe55 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 19:59:59 -0300 Subject: [PATCH 08/13] Moved local_chain test to proper folder --- tests_integration_with_local_chain/conftest.py | 2 +- .../markets/omen}/test_contracts.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) rename {tests/contracts => tests_integration_with_local_chain/markets/omen}/test_contracts.py (71%) diff --git a/tests_integration_with_local_chain/conftest.py b/tests_integration_with_local_chain/conftest.py index 791d772a..981d4d97 100644 --- a/tests_integration_with_local_chain/conftest.py +++ b/tests_integration_with_local_chain/conftest.py @@ -43,7 +43,7 @@ def local_web3( else: print("using foundry") with chain.network_manager.parse_network_choice( - "gnosis:mainnet-fork:foundry" + "gnosis:mainnet_fork:foundry" ) as provider: w3 = Web3(Web3.HTTPProvider(provider.http_uri)) yield w3 diff --git a/tests/contracts/test_contracts.py b/tests_integration_with_local_chain/markets/omen/test_contracts.py similarity index 71% rename from tests/contracts/test_contracts.py rename to tests_integration_with_local_chain/markets/omen/test_contracts.py index 1b2ea2e7..8c92ba95 100644 --- a/tests/contracts/test_contracts.py +++ b/tests_integration_with_local_chain/markets/omen/test_contracts.py @@ -3,10 +3,11 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import sDaiContract -def test_sdai_asset_balance_of() -> None: +def test_sdai_asset_balance_of(local_web3: Web3) -> None: assert ( sDaiContract().get_asset_token_balance( - Web3.to_checksum_address("0x7d3A0DA18e14CCb63375cdC250E8A8399997816F") + Web3.to_checksum_address("0x7d3A0DA18e14CCb63375cdC250E8A8399997816F"), + web3=local_web3 ) >= 0 ) From f0511ff9ae82860a6111302781105d96e06b39bc Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 7 Nov 2024 20:06:01 -0300 Subject: [PATCH 09/13] Fixed black --- .../markets/omen/test_contracts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integration_with_local_chain/markets/omen/test_contracts.py b/tests_integration_with_local_chain/markets/omen/test_contracts.py index 8c92ba95..16259a54 100644 --- a/tests_integration_with_local_chain/markets/omen/test_contracts.py +++ b/tests_integration_with_local_chain/markets/omen/test_contracts.py @@ -7,7 +7,7 @@ def test_sdai_asset_balance_of(local_web3: Web3) -> None: assert ( sDaiContract().get_asset_token_balance( Web3.to_checksum_address("0x7d3A0DA18e14CCb63375cdC250E8A8399997816F"), - web3=local_web3 + web3=local_web3, ) >= 0 ) From d9a14660f77577f46c44fa6a1a9668b9255f8b30 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Mon, 11 Nov 2024 11:07:50 -0300 Subject: [PATCH 10/13] Applied PR comments --- examples/monitor/data_models.py | 24 ------------------- examples/monitor/financial_metrics.py | 5 +++- .../match_bets_with_langfuse_traces.py | 13 ++++------ .../markets/data_models.py | 21 ++++++++++++++++ 4 files changed, 30 insertions(+), 33 deletions(-) delete mode 100644 examples/monitor/data_models.py diff --git a/examples/monitor/data_models.py b/examples/monitor/data_models.py deleted file mode 100644 index 18aa5963..00000000 --- a/examples/monitor/data_models.py +++ /dev/null @@ -1,24 +0,0 @@ -from pydantic import BaseModel - -from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC - - -class SimulationDetail(BaseModel): - strategy: str - url: str - market_p_yes: float - agent_p_yes: float - agent_conf: float - org_bet: float - sim_bet: float - org_dir: bool - sim_dir: bool - org_profit: float - sim_profit: float - timestamp: DatetimeUTC - - -class SharpeOutput(BaseModel): - annualized_volatility: float - mean_daily_return: float - annualized_sharpe_ratio: float diff --git a/examples/monitor/financial_metrics.py b/examples/monitor/financial_metrics.py index f25a13ae..e1361855 100644 --- a/examples/monitor/financial_metrics.py +++ b/examples/monitor/financial_metrics.py @@ -1,7 +1,10 @@ import numpy as np import pandas as pd -from examples.monitor.data_models import SharpeOutput, SimulationDetail +from prediction_market_agent_tooling.markets.data_models import ( + SharpeOutput, + SimulationDetail, +) class SharpeRatioCalculator: diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index c436891b..a3d475fe 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -4,7 +4,6 @@ import dotenv from eth_typing import HexAddress, HexStr -from examples.monitor.data_models import SimulationDetail from examples.monitor.financial_metrics import SharpeRatioCalculator from examples.monitor.transaction_cache import TransactionBlockCache from prediction_market_agent_tooling.markets.omen.omen_contracts import ( @@ -27,7 +26,10 @@ ProbabilisticAnswer, TradeType, ) -from prediction_market_agent_tooling.markets.data_models import ResolvedBet +from prediction_market_agent_tooling.markets.data_models import ( + ResolvedBet, + SimulationDetail, +) from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket from prediction_market_agent_tooling.tools.httpx_cached_client import HttpxCachedClient from prediction_market_agent_tooling.tools.langfuse_client_utils import ( @@ -109,11 +111,6 @@ def get_outcome_for_trace( else -buy_trade.amount.amount ) - # received_outcome_tokens = market.get_buy_token_amount( - # bet_amount=market.get_bet_amount(buy_trade.amount.amount), - # direction=buy_trade.outcome, - # ).amount - return SimulatedOutcome( size=buy_trade.amount.amount, direction=buy_trade.outcome, @@ -204,7 +201,7 @@ def get_outcome_for_trace( api_keys = APIKeys(BET_FROM_PRIVATE_KEY=private_key) # Pick a time after pool token number is stored in OmenAgentMarket - start_time = utc_datetime(2024, 10, 28) + start_time = utc_datetime(2024, 10, 1) langfuse = Langfuse( secret_key=api_keys.langfuse_secret_key.get_secret_value(), diff --git a/prediction_market_agent_tooling/markets/data_models.py b/prediction_market_agent_tooling/markets/data_models.py index f542e51b..1993e2f1 100644 --- a/prediction_market_agent_tooling/markets/data_models.py +++ b/prediction_market_agent_tooling/markets/data_models.py @@ -140,3 +140,24 @@ def from_trade(trade: Trade, id: str) -> "PlacedTrade": amount=trade.amount, id=id, ) + + +class SimulationDetail(BaseModel): + strategy: str + url: str + market_p_yes: float + agent_p_yes: float + agent_conf: float + org_bet: float + sim_bet: float + org_dir: bool + sim_dir: bool + org_profit: float + sim_profit: float + timestamp: DatetimeUTC + + +class SharpeOutput(BaseModel): + annualized_volatility: float + mean_daily_return: float + annualized_sharpe_ratio: float From 66b43312a6f41153483023a798790fd3b887d583 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Wed, 13 Nov 2024 17:12:44 -0300 Subject: [PATCH 11/13] Small fixes after PR review --- examples/monitor/match_bets_with_langfuse_traces.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index a3d475fe..725860c6 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -105,11 +105,7 @@ def get_outcome_for_trace( ), direction=buy_trade.outcome, ).amount - profit = ( - received_outcome_tokens - buy_trade.amount.amount - if correct - else -buy_trade.amount.amount - ) + profit = received_outcome_tokens - buy_trade.amount.amount return SimulatedOutcome( size=buy_trade.amount.amount, From 77815e75c8fcf42caffefb95bda1f2e736e6c623 Mon Sep 17 00:00:00 2001 From: gabrielfior Date: Thu, 14 Nov 2024 10:45:55 -0300 Subject: [PATCH 12/13] Small refactoring to address PR comments --- ape-config.yaml | 2 +- examples/monitor/match_bets_with_langfuse_traces.py | 2 +- .../monitor/financial_metrics}/financial_metrics.py | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {examples/monitor => prediction_market_agent_tooling/monitor/financial_metrics}/financial_metrics.py (100%) diff --git a/ape-config.yaml b/ape-config.yaml index 71f4898e..bc11a968 100644 --- a/ape-config.yaml +++ b/ape-config.yaml @@ -8,7 +8,7 @@ default_ecosystem: gnosis node: gnosis: mainnet: - uri: https://rpc.gnosis.gateway.fm #https://rpc.gnosischain.com + uri: https://rpc.gnosischain.com networks: diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index 725860c6..fd6b925b 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -4,7 +4,6 @@ import dotenv from eth_typing import HexAddress, HexStr -from examples.monitor.financial_metrics import SharpeRatioCalculator from examples.monitor.transaction_cache import TransactionBlockCache from prediction_market_agent_tooling.markets.omen.omen_contracts import ( OmenConditionalTokenContract, @@ -12,6 +11,7 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import ( OmenSubgraphHandler, ) +from prediction_market_agent_tooling.monitor.financial_metrics.financial_metrics import SharpeRatioCalculator dotenv.load_dotenv() import pandas as pd diff --git a/examples/monitor/financial_metrics.py b/prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py similarity index 100% rename from examples/monitor/financial_metrics.py rename to prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py From 06aae1d24b940ea8443b76efb1ed183512f30fff Mon Sep 17 00:00:00 2001 From: Gabriel Fior Date: Tue, 19 Nov 2024 15:46:59 -0300 Subject: [PATCH 13/13] =?UTF-8?q?Added=20gnosis=5Frpc=5Fconfig,=20google?= =?UTF-8?q?=5Fcredentials=5Ffilename=20and=20chain=5Fid=20to=20=E2=80=A6?= =?UTF-8?q?=20(#552)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Lower amount of DB connections (#547) * Single shared engine instance * fixes * revert accidental bump * fix pickle in github ci * fiiiix * fix loguru * revert * ah * ah2 * revert * Added gnosis_rpc_config, google_credentials_filename and chain_id to BaseSettings * Fixed black * Fixed black (2) | changed rpc * Raise OutOfFunds in withdraw_wxdai_to_xdai_to_keep_balance (#553) * Implemented PR comments * Fixed CI * Merged --------- Co-authored-by: Peter Jung --- .../match_bets_with_langfuse_traces.py | 32 +++++++++---------- prediction_market_agent_tooling/config.py | 31 +++++++++++++++++- .../deploy/agent.py | 12 +++---- .../markets/omen/data_models.py | 2 +- .../markets/omen/omen.py | 3 +- .../markets/polymarket/utils.py | 2 +- .../tools/caches/db_cache.py | 4 ++- .../tools/contract.py | 10 ++---- .../tools/custom_exceptions.py | 6 ++++ .../tools/gnosis_rpc.py | 6 ---- .../tools/{google.py => google_utils.py} | 29 ++++++++++++++++- .../relevant_news_cache.py | 9 ++++-- .../tools}/transaction_cache.py | 0 .../tools/utils.py | 24 +------------- pyproject.toml | 2 +- scripts/create_safe_for_agent.py | 7 ++-- .../markets/omen/test_omen.py | 2 +- 17 files changed, 106 insertions(+), 75 deletions(-) create mode 100644 prediction_market_agent_tooling/tools/custom_exceptions.py delete mode 100644 prediction_market_agent_tooling/tools/gnosis_rpc.py rename prediction_market_agent_tooling/tools/{google.py => google_utils.py} (55%) rename {examples/monitor => prediction_market_agent_tooling/tools}/transaction_cache.py (100%) diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index fd6b925b..bd0c955c 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -1,20 +1,8 @@ from pathlib import Path from typing import Any -import dotenv -from eth_typing import HexAddress, HexStr - -from examples.monitor.transaction_cache import TransactionBlockCache -from prediction_market_agent_tooling.markets.omen.omen_contracts import ( - OmenConditionalTokenContract, -) -from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import ( - OmenSubgraphHandler, -) -from prediction_market_agent_tooling.monitor.financial_metrics.financial_metrics import SharpeRatioCalculator - -dotenv.load_dotenv() import pandas as pd +from eth_typing import HexAddress, HexStr from langfuse import Langfuse from pydantic import BaseModel @@ -31,6 +19,18 @@ SimulationDetail, ) from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket +from prediction_market_agent_tooling.markets.omen.omen_contracts import ( + OmenConditionalTokenContract, +) +from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import ( + OmenSubgraphHandler, +) +from prediction_market_agent_tooling.monitor.financial_metrics.financial_metrics import ( + SharpeRatioCalculator, +) +from prediction_market_agent_tooling.tools.google_utils import ( + get_private_key_from_gcp_secret, +) from prediction_market_agent_tooling.tools.httpx_cached_client import HttpxCachedClient from prediction_market_agent_tooling.tools.langfuse_client_utils import ( ProcessMarketTrace, @@ -38,10 +38,10 @@ get_trace_for_bet, get_traces_for_agent, ) -from prediction_market_agent_tooling.tools.utils import ( - get_private_key_from_gcp_secret, - utc_datetime, +from prediction_market_agent_tooling.tools.transaction_cache import ( + TransactionBlockCache, ) +from prediction_market_agent_tooling.tools.utils import utc_datetime class SimulatedOutcome(BaseModel): diff --git a/prediction_market_agent_tooling/config.py b/prediction_market_agent_tooling/config.py index 9a9efdd6..f2842533 100644 --- a/prediction_market_agent_tooling/config.py +++ b/prediction_market_agent_tooling/config.py @@ -1,5 +1,6 @@ import typing as t +from pydantic import Field from pydantic.types import SecretStr from pydantic.v1.types import SecretStr as SecretStrV1 from pydantic_settings import BaseSettings, SettingsConfigDict @@ -7,6 +8,7 @@ from safe_eth.safe.safe import SafeV141 from prediction_market_agent_tooling.gtypes import ( + ChainID, ChecksumAddress, PrivateKey, secretstr_to_v1_secretstr, @@ -53,7 +55,7 @@ class APIKeys(BaseSettings): SQLALCHEMY_DB_URL: t.Optional[SecretStr] = None - ENABLE_CACHE: bool = True + ENABLE_CACHE: bool = False CACHE_DIR: str = "./.cache" @property @@ -203,3 +205,30 @@ def check_if_is_safe_owner(self, ethereum_client: EthereumClient) -> bool: s = SafeV141(self.SAFE_ADDRESS, ethereum_client) public_key_from_signer = private_key_to_public_key(self.bet_from_private_key) return s.retrieve_is_owner(public_key_from_signer) + + +class RPCConfig(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", env_file_encoding="utf-8", extra="ignore" + ) + + GNOSIS_RPC_URL: str = Field(default="https://rpc.gnosischain.com") + CHAIN_ID: ChainID = Field(default=ChainID(100)) + + @property + def gnosis_rpc_url(self) -> str: + return check_not_none( + self.GNOSIS_RPC_URL, "GNOSIS_RPC_URL missing in the environment." + ) + + @property + def chain_id(self) -> ChainID: + return check_not_none(self.CHAIN_ID, "CHAIN_ID missing in the environment.") + + +class CloudCredentials(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", env_file_encoding="utf-8", extra="ignore" + ) + + GOOGLE_APPLICATION_CREDENTIALS: t.Optional[str] = None diff --git a/prediction_market_agent_tooling/deploy/agent.py b/prediction_market_agent_tooling/deploy/agent.py index ae413968..e8132fbc 100644 --- a/prediction_market_agent_tooling/deploy/agent.py +++ b/prediction_market_agent_tooling/deploy/agent.py @@ -59,6 +59,10 @@ from prediction_market_agent_tooling.monitor.monitor_app import ( MARKET_TYPE_TO_DEPLOYED_AGENT, ) +from prediction_market_agent_tooling.tools.custom_exceptions import ( + CantPayForGasError, + OutOfFundsError, +) from prediction_market_agent_tooling.tools.is_invalid import is_invalid from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe @@ -106,14 +110,6 @@ def initialize_langfuse(enable_langfuse: bool) -> None: Decision = Annotated[bool, BeforeValidator(to_boolean_outcome)] -class CantPayForGasError(ValueError): - pass - - -class OutOfFundsError(ValueError): - pass - - class AnsweredEnum(str, Enum): ANSWERED = "answered" NOT_ANSWERED = "not_answered" diff --git a/prediction_market_agent_tooling/markets/omen/data_models.py b/prediction_market_agent_tooling/markets/omen/data_models.py index 7be72ee2..f7015a33 100644 --- a/prediction_market_agent_tooling/markets/omen/data_models.py +++ b/prediction_market_agent_tooling/markets/omen/data_models.py @@ -554,7 +554,7 @@ def to_generic_resolved_bet(self) -> ResolvedBet: ) return ResolvedBet( - id=str(self.transactionHash), + id=self.transactionHash.hex(), # 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. amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai), outcome=self.boolean_outcome, diff --git a/prediction_market_agent_tooling/markets/omen/omen.py b/prediction_market_agent_tooling/markets/omen/omen.py index 049a2089..7b50242f 100644 --- a/prediction_market_agent_tooling/markets/omen/omen.py +++ b/prediction_market_agent_tooling/markets/omen/omen.py @@ -75,6 +75,7 @@ init_collateral_token_contract, to_gnosis_chain_contract, ) +from prediction_market_agent_tooling.tools.custom_exceptions import OutOfFundsError from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler from prediction_market_agent_tooling.tools.utils import ( @@ -1339,7 +1340,7 @@ def withdraw_wxdai_to_xdai_to_keep_balance( ) if current_balances.wxdai < need_to_withdraw: - raise ValueError( + raise OutOfFundsError( f"Current wxDai balance {current_balances.wxdai} is less than the required minimum wxDai to withdraw {need_to_withdraw}." ) diff --git a/prediction_market_agent_tooling/markets/polymarket/utils.py b/prediction_market_agent_tooling/markets/polymarket/utils.py index e7335721..c83ff23b 100644 --- a/prediction_market_agent_tooling/markets/polymarket/utils.py +++ b/prediction_market_agent_tooling/markets/polymarket/utils.py @@ -3,7 +3,7 @@ from prediction_market_agent_tooling.markets.polymarket.data_models_web import ( PolymarketFullMarket, ) -from prediction_market_agent_tooling.tools.google import search_google +from prediction_market_agent_tooling.tools.google_utils import search_google def find_resolution_on_polymarket(question: str) -> Resolution | None: diff --git a/prediction_market_agent_tooling/tools/caches/db_cache.py b/prediction_market_agent_tooling/tools/caches/db_cache.py index 500b686c..4da6ffb8 100644 --- a/prediction_market_agent_tooling/tools/caches/db_cache.py +++ b/prediction_market_agent_tooling/tools/caches/db_cache.py @@ -102,6 +102,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: # Use custom json serializer and deserializer, because otherwise, for example `datetime` serialization would fail. json_serializer=json_serializer, json_deserializer=json_deserializer, + pool_size=1, ) # Create table if it doesn't exist @@ -195,7 +196,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: ) # If postgres access was specified, save it. - if engine is not None and (cache_none or computed_result is not None): + if cache_none or computed_result is not None: cache_entry = FunctionCache( function_name=function_name, full_function_name=full_function_name, @@ -209,6 +210,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: session.add(cache_entry) session.commit() + engine.dispose() return computed_result return cast(FunctionT, wrapper) diff --git a/prediction_market_agent_tooling/tools/contract.py b/prediction_market_agent_tooling/tools/contract.py index 609bf8ba..b04ed271 100644 --- a/prediction_market_agent_tooling/tools/contract.py +++ b/prediction_market_agent_tooling/tools/contract.py @@ -8,7 +8,7 @@ from web3 import Web3 from web3.contract.contract import Contract as Web3Contract -from prediction_market_agent_tooling.config import APIKeys +from prediction_market_agent_tooling.config import APIKeys, RPCConfig from prediction_market_agent_tooling.gtypes import ( ABI, ChainID, @@ -18,10 +18,6 @@ TxReceipt, Wei, ) -from prediction_market_agent_tooling.tools.gnosis_rpc import ( - GNOSIS_NETWORK_ID, - GNOSIS_RPC_URL, -) from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen from prediction_market_agent_tooling.tools.web3_utils import ( call_function_on_contract, @@ -391,8 +387,8 @@ class ContractOnGnosisChain(ContractBaseClass): Contract base class with Gnosis Chain configuration. """ - CHAIN_ID = GNOSIS_NETWORK_ID - CHAIN_RPC_URL = GNOSIS_RPC_URL + CHAIN_ID = RPCConfig().chain_id + CHAIN_RPC_URL = RPCConfig().gnosis_rpc_url class ContractProxyOnGnosisChain(ContractProxyBaseClass, ContractOnGnosisChain): diff --git a/prediction_market_agent_tooling/tools/custom_exceptions.py b/prediction_market_agent_tooling/tools/custom_exceptions.py new file mode 100644 index 00000000..7225102c --- /dev/null +++ b/prediction_market_agent_tooling/tools/custom_exceptions.py @@ -0,0 +1,6 @@ +class CantPayForGasError(ValueError): + pass + + +class OutOfFundsError(ValueError): + pass diff --git a/prediction_market_agent_tooling/tools/gnosis_rpc.py b/prediction_market_agent_tooling/tools/gnosis_rpc.py deleted file mode 100644 index a6129bff..00000000 --- a/prediction_market_agent_tooling/tools/gnosis_rpc.py +++ /dev/null @@ -1,6 +0,0 @@ -import os - -from prediction_market_agent_tooling.gtypes import ChainID - -GNOSIS_NETWORK_ID = ChainID(100) # xDai network. -GNOSIS_RPC_URL = os.getenv("GNOSIS_RPC_URL", "https://rpc.gnosischain.com") diff --git a/prediction_market_agent_tooling/tools/google.py b/prediction_market_agent_tooling/tools/google_utils.py similarity index 55% rename from prediction_market_agent_tooling/tools/google.py rename to prediction_market_agent_tooling/tools/google_utils.py index c92d1858..2e9a69b0 100644 --- a/prediction_market_agent_tooling/tools/google.py +++ b/prediction_market_agent_tooling/tools/google_utils.py @@ -1,10 +1,14 @@ +import json import typing as t from datetime import timedelta import tenacity +from google.cloud import secretmanager from googleapiclient.discovery import build +from pydantic import SecretStr -from prediction_market_agent_tooling.config import APIKeys +from prediction_market_agent_tooling.config import APIKeys, CloudCredentials +from prediction_market_agent_tooling.gtypes import PrivateKey from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.tools.caches.db_cache import db_cache @@ -51,3 +55,26 @@ def search_google( ) except KeyError as e: raise ValueError(f"Can not parse results: {search}") from e + + +def get_private_key_from_gcp_secret( + secret_id: str, + project_id: str = "582587111398", # Gnosis AI default project_id + version_id: str = "latest", +) -> PrivateKey: + # If credentials filename specified, use that, else read using default credentials path. + google_application_credentials_filename = ( + CloudCredentials().GOOGLE_APPLICATION_CREDENTIALS + ) + if google_application_credentials_filename is not None: + # mypy interprets incorrectly that from_service_account_json requires further args. + client = secretmanager.SecretManagerServiceClient.from_service_account_json(filename=google_application_credentials_filename) # type: ignore [call-arg] + else: + client = secretmanager.SecretManagerServiceClient() + name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}" + response = client.access_secret_version(request={"name": name}) + secret_payload = response.payload.data.decode("UTF-8") + secret_json = json.loads(secret_payload) + if "private_key" not in secret_json: + raise ValueError(f"Private key not found in gcp secret {secret_id}") + return PrivateKey(SecretStr(secret_json["private_key"])) diff --git a/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py b/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py index d1257ec5..8fc280cc 100644 --- a/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py +++ b/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py @@ -25,9 +25,12 @@ class RelevantNewsCacheModel(SQLModel, table=True): class RelevantNewsResponseCache: def __init__(self, sqlalchemy_db_url: str | None = None): self.engine = create_engine( - sqlalchemy_db_url - if sqlalchemy_db_url - else APIKeys().sqlalchemy_db_url.get_secret_value() + ( + sqlalchemy_db_url + if sqlalchemy_db_url + else APIKeys().sqlalchemy_db_url.get_secret_value() + ), + pool_size=1, ) self._initialize_db() diff --git a/examples/monitor/transaction_cache.py b/prediction_market_agent_tooling/tools/transaction_cache.py similarity index 100% rename from examples/monitor/transaction_cache.py rename to prediction_market_agent_tooling/tools/transaction_cache.py diff --git a/prediction_market_agent_tooling/tools/utils.py b/prediction_market_agent_tooling/tools/utils.py index 0bbf63a0..0af53076 100644 --- a/prediction_market_agent_tooling/tools/utils.py +++ b/prediction_market_agent_tooling/tools/utils.py @@ -1,4 +1,3 @@ -import json import os import subprocess from datetime import datetime @@ -6,17 +5,11 @@ import pytz import requests -from google.cloud import secretmanager from pydantic import BaseModel, ValidationError from scipy.optimize import newton from scipy.stats import entropy -from prediction_market_agent_tooling.gtypes import ( - DatetimeUTC, - PrivateKey, - Probability, - SecretStr, -) +from prediction_market_agent_tooling.gtypes import DatetimeUTC, Probability, SecretStr from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.markets.market_fees import MarketFees @@ -214,18 +207,3 @@ def f(r: float) -> float: amount_to_sell = newton(f, 0) return float(amount_to_sell) * 0.999999 # Avoid rounding errors - - -def get_private_key_from_gcp_secret( - secret_id: str, - project_id: str = "582587111398", # Gnosis AI default project_id - version_id: str = "latest", -) -> PrivateKey: - client = secretmanager.SecretManagerServiceClient() - name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}" - response = client.access_secret_version(request={"name": name}) - secret_payload = response.payload.data.decode("UTF-8") - secret_json = json.loads(secret_payload) - if "private_key" not in secret_json: - raise ValueError(f"Private key not found in gcp secret {secret_id}") - return PrivateKey(SecretStr(secret_json["private_key"])) diff --git a/pyproject.toml b/pyproject.toml index c03eda98..9ab20624 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "prediction-market-agent-tooling" -version = "0.56.1" +version = "0.56.3" description = "Tools to benchmark, deploy and monitor prediction market agents." authors = ["Gnosis"] readme = "README.md" diff --git a/scripts/create_safe_for_agent.py b/scripts/create_safe_for_agent.py index 0b37bc8d..3f364093 100644 --- a/scripts/create_safe_for_agent.py +++ b/scripts/create_safe_for_agent.py @@ -7,10 +7,10 @@ from safe_eth.eth import EthereumClient from web3 import Web3 +from prediction_market_agent_tooling.config import RPCConfig from prediction_market_agent_tooling.gtypes import PrivateKey, xDai from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.tools.balances import get_balances -from prediction_market_agent_tooling.tools.gnosis_rpc import GNOSIS_RPC_URL from prediction_market_agent_tooling.tools.safe import create_safe from prediction_market_agent_tooling.tools.web3_utils import send_xdai_to, xdai_to_wei @@ -36,9 +36,8 @@ def create_safe_for_agent( """ salt_nonce = salt_nonce or secrets.randbits(256) - ethereum_client = EthereumClient(URI(GNOSIS_RPC_URL)) - if rpc_url: - ethereum_client = EthereumClient(URI(rpc_url)) + rpc_url = rpc_url if rpc_url else RPCConfig().gnosis_rpc_url + ethereum_client = EthereumClient(URI(rpc_url)) account = Account.from_key(from_private_key) safe_address = create_safe( ethereum_client=ethereum_client, diff --git a/tests_integration_with_local_chain/markets/omen/test_omen.py b/tests_integration_with_local_chain/markets/omen/test_omen.py index be29316a..9cfd5cde 100644 --- a/tests_integration_with_local_chain/markets/omen/test_omen.py +++ b/tests_integration_with_local_chain/markets/omen/test_omen.py @@ -410,7 +410,7 @@ def test_place_bet_with_autodeposit( local_web3 ) assert ( - collateral_token_contract.symbol() == expected_symbol + collateral_token_contract.symbol(local_web3) == expected_symbol ), f"Should have retrieve {expected_symbol} market." assert isinstance( collateral_token_contract, ContractDepositableWrapperERC20OnGnosisChain