intentkit 0.6.13.dev2__py3-none-any.whl → 0.8.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +1 -1
- intentkit/abstracts/agent.py +4 -5
- intentkit/abstracts/engine.py +5 -5
- intentkit/abstracts/graph.py +14 -7
- intentkit/abstracts/skill.py +6 -144
- intentkit/abstracts/twitter.py +4 -5
- intentkit/clients/__init__.py +5 -2
- intentkit/clients/cdp.py +101 -141
- intentkit/clients/twitter.py +83 -62
- intentkit/clients/web3.py +29 -0
- intentkit/config/config.py +8 -5
- intentkit/core/agent.py +472 -195
- intentkit/core/asset.py +253 -0
- intentkit/core/chat.py +51 -0
- intentkit/core/client.py +1 -1
- intentkit/core/credit.py +460 -130
- intentkit/core/engine.py +262 -233
- intentkit/core/node.py +15 -16
- intentkit/core/prompt.py +62 -28
- intentkit/core/scheduler.py +92 -0
- intentkit/core/statistics.py +168 -0
- intentkit/models/agent.py +1096 -949
- intentkit/models/agent_data.py +68 -38
- intentkit/models/agent_public.json +98 -0
- intentkit/models/agent_schema.json +54 -439
- intentkit/models/app_setting.py +96 -33
- intentkit/models/chat.py +74 -27
- intentkit/models/conversation.py +8 -8
- intentkit/models/credit.py +362 -74
- intentkit/models/db.py +26 -8
- intentkit/models/db_mig.py +2 -2
- intentkit/models/llm.csv +28 -0
- intentkit/models/llm.py +185 -350
- intentkit/models/redis.py +6 -4
- intentkit/models/skill.py +186 -72
- intentkit/models/skills.csv +174 -0
- intentkit/models/user.py +82 -24
- intentkit/skills/acolyt/__init__.py +2 -9
- intentkit/skills/acolyt/ask.py +3 -4
- intentkit/skills/acolyt/base.py +4 -9
- intentkit/skills/acolyt/schema.json +4 -3
- intentkit/skills/aixbt/__init__.py +2 -13
- intentkit/skills/aixbt/base.py +1 -7
- intentkit/skills/aixbt/projects.py +14 -15
- intentkit/skills/aixbt/schema.json +4 -4
- intentkit/skills/allora/__init__.py +2 -9
- intentkit/skills/allora/base.py +4 -9
- intentkit/skills/allora/price.py +3 -4
- intentkit/skills/allora/schema.json +3 -2
- intentkit/skills/base.py +248 -85
- intentkit/skills/basename/__init__.py +51 -0
- intentkit/skills/basename/base.py +11 -0
- intentkit/skills/basename/basename.svg +11 -0
- intentkit/skills/basename/schema.json +58 -0
- intentkit/skills/carv/__init__.py +115 -121
- intentkit/skills/carv/base.py +184 -185
- intentkit/skills/carv/fetch_news.py +3 -3
- intentkit/skills/carv/onchain_query.py +4 -4
- intentkit/skills/carv/schema.json +134 -137
- intentkit/skills/carv/token_info_and_price.py +5 -5
- intentkit/skills/casino/README.md +254 -0
- intentkit/skills/casino/__init__.py +86 -0
- intentkit/skills/casino/base.py +17 -0
- intentkit/skills/casino/casino.png +0 -0
- intentkit/skills/casino/deck_draw.py +127 -0
- intentkit/skills/casino/deck_shuffle.py +118 -0
- intentkit/skills/casino/dice_roll.py +100 -0
- intentkit/skills/casino/schema.json +77 -0
- intentkit/skills/casino/utils.py +107 -0
- intentkit/skills/cdp/__init__.py +22 -84
- intentkit/skills/cdp/base.py +1 -7
- intentkit/skills/cdp/schema.json +11 -314
- intentkit/skills/chainlist/__init__.py +2 -7
- intentkit/skills/chainlist/base.py +1 -7
- intentkit/skills/chainlist/chain_lookup.py +18 -18
- intentkit/skills/chainlist/schema.json +3 -5
- intentkit/skills/common/__init__.py +2 -9
- intentkit/skills/common/base.py +1 -7
- intentkit/skills/common/current_time.py +1 -2
- intentkit/skills/common/schema.json +2 -2
- intentkit/skills/cookiefun/__init__.py +6 -9
- intentkit/skills/cookiefun/base.py +2 -7
- intentkit/skills/cookiefun/get_account_details.py +7 -7
- intentkit/skills/cookiefun/get_account_feed.py +19 -19
- intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
- intentkit/skills/cookiefun/get_sectors.py +3 -3
- intentkit/skills/cookiefun/schema.json +1 -3
- intentkit/skills/cookiefun/search_accounts.py +9 -9
- intentkit/skills/cryptocompare/__init__.py +7 -24
- intentkit/skills/cryptocompare/api.py +2 -3
- intentkit/skills/cryptocompare/base.py +11 -25
- intentkit/skills/cryptocompare/fetch_news.py +4 -5
- intentkit/skills/cryptocompare/fetch_price.py +6 -7
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
- intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
- intentkit/skills/cryptocompare/schema.json +3 -3
- intentkit/skills/cryptopanic/__init__.py +7 -10
- intentkit/skills/cryptopanic/base.py +51 -55
- intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
- intentkit/skills/cryptopanic/schema.json +105 -103
- intentkit/skills/dapplooker/__init__.py +2 -9
- intentkit/skills/dapplooker/base.py +4 -9
- intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
- intentkit/skills/dapplooker/schema.json +3 -5
- intentkit/skills/defillama/__init__.py +24 -74
- intentkit/skills/defillama/api.py +6 -9
- intentkit/skills/defillama/base.py +11 -21
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_block.py +6 -8
- intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
- intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
- intentkit/skills/defillama/config/chains.py +1 -3
- intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
- intentkit/skills/defillama/schema.json +5 -1
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
- intentkit/skills/defillama/tests/api_integration.test.py +1 -1
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
- intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
- intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
- intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
- intentkit/skills/defillama/yields/fetch_pools.py +26 -30
- intentkit/skills/dexscreener/README.md +154 -0
- intentkit/skills/dexscreener/__init__.py +97 -93
- intentkit/skills/dexscreener/base.py +125 -133
- intentkit/skills/dexscreener/get_pair_info.py +158 -0
- intentkit/skills/dexscreener/get_token_pairs.py +165 -0
- intentkit/skills/dexscreener/get_tokens_info.py +212 -0
- intentkit/skills/dexscreener/model/search_token_response.py +80 -82
- intentkit/skills/dexscreener/schema.json +91 -48
- intentkit/skills/dexscreener/search_token.py +182 -321
- intentkit/skills/dexscreener/utils.py +420 -0
- intentkit/skills/dune_analytics/__init__.py +7 -9
- intentkit/skills/dune_analytics/base.py +48 -52
- intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
- intentkit/skills/dune_analytics/schema.json +104 -99
- intentkit/skills/elfa/__init__.py +5 -18
- intentkit/skills/elfa/base.py +10 -14
- intentkit/skills/elfa/mention.py +19 -21
- intentkit/skills/elfa/schema.json +3 -2
- intentkit/skills/elfa/stats.py +4 -4
- intentkit/skills/elfa/tokens.py +12 -12
- intentkit/skills/elfa/utils.py +26 -28
- intentkit/skills/enso/__init__.py +11 -31
- intentkit/skills/enso/base.py +50 -35
- intentkit/skills/enso/best_yield.py +16 -24
- intentkit/skills/enso/networks.py +6 -11
- intentkit/skills/enso/prices.py +11 -13
- intentkit/skills/enso/route.py +34 -38
- intentkit/skills/enso/schema.json +3 -2
- intentkit/skills/enso/tokens.py +29 -38
- intentkit/skills/enso/wallet.py +76 -191
- intentkit/skills/erc20/__init__.py +50 -0
- intentkit/skills/erc20/base.py +11 -0
- intentkit/skills/erc20/erc20.svg +5 -0
- intentkit/skills/erc20/schema.json +74 -0
- intentkit/skills/erc721/__init__.py +53 -0
- intentkit/skills/erc721/base.py +11 -0
- intentkit/skills/erc721/erc721.svg +5 -0
- intentkit/skills/erc721/schema.json +90 -0
- intentkit/skills/firecrawl/README.md +11 -5
- intentkit/skills/firecrawl/__init__.py +5 -18
- intentkit/skills/firecrawl/base.py +4 -11
- intentkit/skills/firecrawl/clear.py +4 -8
- intentkit/skills/firecrawl/crawl.py +19 -19
- intentkit/skills/firecrawl/query.py +4 -3
- intentkit/skills/firecrawl/schema.json +6 -8
- intentkit/skills/firecrawl/scrape.py +150 -40
- intentkit/skills/firecrawl/utils.py +50 -42
- intentkit/skills/github/__init__.py +2 -7
- intentkit/skills/github/base.py +1 -7
- intentkit/skills/github/github_search.py +1 -2
- intentkit/skills/github/schema.json +3 -4
- intentkit/skills/heurist/__init__.py +8 -27
- intentkit/skills/heurist/base.py +4 -9
- intentkit/skills/heurist/image_generation_animagine_xl.py +12 -13
- intentkit/skills/heurist/image_generation_arthemy_comics.py +12 -13
- intentkit/skills/heurist/image_generation_arthemy_real.py +12 -13
- intentkit/skills/heurist/image_generation_braindance.py +12 -13
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +12 -13
- intentkit/skills/heurist/image_generation_flux_1_dev.py +12 -13
- intentkit/skills/heurist/image_generation_sdxl.py +12 -13
- intentkit/skills/heurist/schema.json +2 -2
- intentkit/skills/http/__init__.py +4 -15
- intentkit/skills/http/base.py +1 -7
- intentkit/skills/http/get.py +21 -16
- intentkit/skills/http/post.py +23 -18
- intentkit/skills/http/put.py +23 -18
- intentkit/skills/http/schema.json +4 -5
- intentkit/skills/lifi/__init__.py +8 -13
- intentkit/skills/lifi/base.py +1 -7
- intentkit/skills/lifi/schema.json +17 -8
- intentkit/skills/lifi/token_execute.py +36 -30
- intentkit/skills/lifi/token_quote.py +8 -10
- intentkit/skills/lifi/utils.py +104 -51
- intentkit/skills/moralis/__init__.py +6 -10
- intentkit/skills/moralis/api.py +6 -7
- intentkit/skills/moralis/base.py +5 -10
- intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
- intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
- intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
- intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
- intentkit/skills/moralis/schema.json +7 -2
- intentkit/skills/morpho/__init__.py +52 -0
- intentkit/skills/morpho/base.py +11 -0
- intentkit/skills/morpho/morpho.svg +12 -0
- intentkit/skills/morpho/schema.json +73 -0
- intentkit/skills/nation/__init__.py +4 -9
- intentkit/skills/nation/base.py +5 -10
- intentkit/skills/nation/nft_check.py +3 -4
- intentkit/skills/nation/schema.json +4 -3
- intentkit/skills/onchain.py +23 -0
- intentkit/skills/openai/__init__.py +17 -18
- intentkit/skills/openai/base.py +10 -14
- intentkit/skills/openai/dalle_image_generation.py +3 -8
- intentkit/skills/openai/gpt_avatar_generator.py +102 -0
- intentkit/skills/openai/gpt_image_generation.py +4 -8
- intentkit/skills/openai/gpt_image_mini_generator.py +91 -0
- intentkit/skills/openai/gpt_image_to_image.py +4 -8
- intentkit/skills/openai/image_to_text.py +3 -7
- intentkit/skills/openai/schema.json +34 -3
- intentkit/skills/portfolio/__init__.py +11 -35
- intentkit/skills/portfolio/base.py +33 -19
- intentkit/skills/portfolio/schema.json +3 -5
- intentkit/skills/portfolio/token_balances.py +21 -21
- intentkit/skills/portfolio/wallet_approvals.py +17 -18
- intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
- intentkit/skills/portfolio/wallet_history.py +31 -31
- intentkit/skills/portfolio/wallet_net_worth.py +13 -13
- intentkit/skills/portfolio/wallet_nfts.py +19 -19
- intentkit/skills/portfolio/wallet_profitability.py +18 -18
- intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
- intentkit/skills/portfolio/wallet_stats.py +3 -3
- intentkit/skills/portfolio/wallet_swaps.py +19 -19
- intentkit/skills/pyth/__init__.py +50 -0
- intentkit/skills/pyth/base.py +11 -0
- intentkit/skills/pyth/pyth.svg +6 -0
- intentkit/skills/pyth/schema.json +75 -0
- intentkit/skills/skills.toml +40 -0
- intentkit/skills/slack/__init__.py +5 -17
- intentkit/skills/slack/base.py +3 -9
- intentkit/skills/slack/get_channel.py +8 -8
- intentkit/skills/slack/get_message.py +9 -9
- intentkit/skills/slack/schedule_message.py +5 -5
- intentkit/skills/slack/schema.json +2 -2
- intentkit/skills/slack/send_message.py +3 -5
- intentkit/skills/supabase/__init__.py +7 -23
- intentkit/skills/supabase/base.py +9 -13
- intentkit/skills/supabase/delete_data.py +5 -6
- intentkit/skills/supabase/fetch_data.py +13 -14
- intentkit/skills/supabase/insert_data.py +5 -6
- intentkit/skills/supabase/invoke_function.py +7 -8
- intentkit/skills/supabase/schema.json +2 -3
- intentkit/skills/supabase/update_data.py +7 -8
- intentkit/skills/supabase/upsert_data.py +5 -6
- intentkit/skills/superfluid/__init__.py +53 -0
- intentkit/skills/superfluid/base.py +11 -0
- intentkit/skills/superfluid/schema.json +89 -0
- intentkit/skills/superfluid/superfluid.svg +6 -0
- intentkit/skills/system/__init__.py +7 -24
- intentkit/skills/system/add_autonomous_task.py +10 -12
- intentkit/skills/system/delete_autonomous_task.py +2 -2
- intentkit/skills/system/edit_autonomous_task.py +14 -18
- intentkit/skills/system/list_autonomous_tasks.py +3 -5
- intentkit/skills/system/read_agent_api_key.py +6 -4
- intentkit/skills/system/regenerate_agent_api_key.py +6 -4
- intentkit/skills/system/schema.json +6 -8
- intentkit/skills/tavily/__init__.py +3 -12
- intentkit/skills/tavily/base.py +4 -9
- intentkit/skills/tavily/schema.json +3 -5
- intentkit/skills/tavily/tavily_extract.py +2 -4
- intentkit/skills/tavily/tavily_search.py +4 -6
- intentkit/skills/token/__init__.py +5 -10
- intentkit/skills/token/base.py +7 -11
- intentkit/skills/token/erc20_transfers.py +19 -19
- intentkit/skills/token/schema.json +3 -6
- intentkit/skills/token/token_analytics.py +3 -3
- intentkit/skills/token/token_price.py +13 -13
- intentkit/skills/token/token_search.py +9 -9
- intentkit/skills/twitter/__init__.py +11 -35
- intentkit/skills/twitter/base.py +23 -35
- intentkit/skills/twitter/follow_user.py +3 -7
- intentkit/skills/twitter/get_mentions.py +6 -13
- intentkit/skills/twitter/get_timeline.py +5 -13
- intentkit/skills/twitter/get_user_by_username.py +3 -7
- intentkit/skills/twitter/get_user_tweets.py +6 -14
- intentkit/skills/twitter/like_tweet.py +3 -7
- intentkit/skills/twitter/post_tweet.py +23 -12
- intentkit/skills/twitter/reply_tweet.py +21 -12
- intentkit/skills/twitter/retweet.py +3 -7
- intentkit/skills/twitter/schema.json +1 -0
- intentkit/skills/twitter/search_tweets.py +5 -13
- intentkit/skills/unrealspeech/__init__.py +2 -7
- intentkit/skills/unrealspeech/base.py +2 -8
- intentkit/skills/unrealspeech/schema.json +2 -5
- intentkit/skills/unrealspeech/text_to_speech.py +8 -8
- intentkit/skills/venice_audio/__init__.py +98 -106
- intentkit/skills/venice_audio/base.py +117 -121
- intentkit/skills/venice_audio/input.py +41 -41
- intentkit/skills/venice_audio/schema.json +151 -152
- intentkit/skills/venice_audio/venice_audio.py +38 -21
- intentkit/skills/venice_image/__init__.py +147 -154
- intentkit/skills/venice_image/api.py +138 -138
- intentkit/skills/venice_image/base.py +185 -192
- intentkit/skills/venice_image/config.py +33 -35
- intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
- intentkit/skills/venice_image/image_generation/image_generation_base.py +9 -9
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
- intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
- intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
- intentkit/skills/venice_image/schema.json +267 -267
- intentkit/skills/venice_image/utils.py +77 -78
- intentkit/skills/web_scraper/__init__.py +5 -18
- intentkit/skills/web_scraper/base.py +21 -7
- intentkit/skills/web_scraper/document_indexer.py +7 -6
- intentkit/skills/web_scraper/schema.json +2 -6
- intentkit/skills/web_scraper/scrape_and_index.py +15 -15
- intentkit/skills/web_scraper/utils.py +62 -63
- intentkit/skills/web_scraper/website_indexer.py +17 -19
- intentkit/skills/weth/__init__.py +49 -0
- intentkit/skills/weth/base.py +11 -0
- intentkit/skills/weth/schema.json +58 -0
- intentkit/skills/weth/weth.svg +6 -0
- intentkit/skills/wow/__init__.py +51 -0
- intentkit/skills/wow/base.py +11 -0
- intentkit/skills/wow/schema.json +89 -0
- intentkit/skills/wow/wow.svg +7 -0
- intentkit/skills/x402/__init__.py +61 -0
- intentkit/skills/x402/ask_agent.py +98 -0
- intentkit/skills/x402/base.py +99 -0
- intentkit/skills/x402/http_request.py +117 -0
- intentkit/skills/x402/schema.json +45 -0
- intentkit/skills/x402/x402.webp +0 -0
- intentkit/skills/xmtp/__init__.py +4 -15
- intentkit/skills/xmtp/base.py +61 -2
- intentkit/skills/xmtp/price.py +18 -13
- intentkit/skills/xmtp/schema.json +69 -71
- intentkit/skills/xmtp/swap.py +22 -25
- intentkit/skills/xmtp/transfer.py +71 -32
- intentkit/utils/chain.py +3 -3
- intentkit/utils/error.py +14 -1
- intentkit/utils/logging.py +2 -4
- intentkit/utils/s3.py +59 -7
- intentkit/utils/schema.py +100 -0
- intentkit/utils/slack_alert.py +7 -8
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/METADATA +14 -16
- intentkit-0.8.17.dist-info/RECORD +466 -0
- intentkit/abstracts/exception.py +0 -9
- intentkit/core/skill.py +0 -200
- intentkit/models/generator.py +0 -347
- intentkit/skills/cdp/get_balance.py +0 -110
- intentkit/skills/cdp/swap.py +0 -121
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +0 -511
- intentkit-0.6.13.dev2.dist-info/RECORD +0 -409
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/WHEEL +0 -0
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/licenses/LICENSE +0 -0
intentkit/skills/xmtp/price.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from typing import Literal
|
|
1
|
+
from typing import Literal
|
|
2
2
|
|
|
3
|
+
from langchain_core.tools.base import ToolException
|
|
3
4
|
from pydantic import BaseModel, Field
|
|
4
5
|
|
|
5
|
-
from intentkit.clients.cdp import
|
|
6
|
+
from intentkit.clients.cdp import get_cdp_client
|
|
6
7
|
from intentkit.skills.xmtp.base import XmtpBaseTool
|
|
7
8
|
|
|
8
9
|
|
|
@@ -21,9 +22,9 @@ class XmtpGetSwapPrice(XmtpBaseTool):
|
|
|
21
22
|
"""Skill for fetching indicative swap price using CDP SDK."""
|
|
22
23
|
|
|
23
24
|
name: str = "xmtp_get_swap_price"
|
|
24
|
-
description: str = "Get an indicative swap price/quote for token pair and amount on Base networks using CDP."
|
|
25
|
+
description: str = "Get an indicative swap price/quote for token pair and amount on Ethereum, Base, Arbitrum, and Optimism mainnet networks using CDP."
|
|
25
26
|
response_format: Literal["content", "content_and_artifact"] = "content"
|
|
26
|
-
args_schema:
|
|
27
|
+
args_schema: type[BaseModel] = SwapPriceInput
|
|
27
28
|
|
|
28
29
|
async def _arun(
|
|
29
30
|
self,
|
|
@@ -35,18 +36,22 @@ class XmtpGetSwapPrice(XmtpBaseTool):
|
|
|
35
36
|
context = self.get_context()
|
|
36
37
|
agent = context.agent
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
# Only support mainnet networks for price and swap
|
|
40
|
+
supported_networks = [
|
|
41
|
+
"ethereum-mainnet",
|
|
42
|
+
"base-mainnet",
|
|
43
|
+
"arbitrum-mainnet",
|
|
44
|
+
"optimism-mainnet",
|
|
45
|
+
]
|
|
46
|
+
if agent.network_id not in supported_networks:
|
|
47
|
+
raise ToolException(
|
|
48
|
+
f"Swap price only supported on {', '.join(supported_networks)}. Current: {agent.network_id}"
|
|
41
49
|
)
|
|
42
50
|
|
|
43
|
-
network_for_cdp =
|
|
44
|
-
"base-mainnet": "base",
|
|
45
|
-
"base-sepolia": "base-sepolia",
|
|
46
|
-
}[agent.network_id]
|
|
51
|
+
network_for_cdp = self.get_cdp_network(agent.network_id)
|
|
47
52
|
|
|
48
|
-
cdp_client =
|
|
49
|
-
# Note: Don't use async with context manager as
|
|
53
|
+
cdp_client = get_cdp_client()
|
|
54
|
+
# Note: Don't use async with context manager as get_cdp_client returns a managed global client
|
|
50
55
|
price = await cdp_client.evm.get_swap_price(
|
|
51
56
|
from_token=from_token,
|
|
52
57
|
to_token=to_token,
|
|
@@ -1,75 +1,73 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "XMTP",
|
|
5
|
+
"description": "Use this skill only if you want make an XMTP Agent. XMTP protocol skills for creating blockchain transaction requests that can be sent to users for signing",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/xmtp/xmtp.png",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"Communication",
|
|
9
|
+
"Crypto",
|
|
10
|
+
"DeFi"
|
|
11
|
+
],
|
|
12
|
+
"properties": {
|
|
13
|
+
"enabled": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"title": "Enabled",
|
|
16
|
+
"description": "Whether this skill is enabled",
|
|
17
|
+
"default": false
|
|
18
|
+
},
|
|
19
|
+
"states": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"xmtp_transfer": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"title": "XMTP Transfer",
|
|
25
|
+
"enum": [
|
|
26
|
+
"disabled",
|
|
27
|
+
"public",
|
|
28
|
+
"private"
|
|
29
|
+
],
|
|
30
|
+
"x-enum-title": [
|
|
31
|
+
"Disabled",
|
|
32
|
+
"Agent Owner + All Users",
|
|
33
|
+
"Agent Owner Only"
|
|
34
|
+
],
|
|
35
|
+
"description": "Create XMTP transaction requests for transferring ETH or ERC20 tokens on Base mainnet. Supports both native ETH transfers and ERC20 token transfers. Generates wallet_sendCalls transaction data that users can sign.",
|
|
36
|
+
"default": "disabled"
|
|
20
37
|
},
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
],
|
|
53
|
-
"description": "Create XMTP transaction requests for swapping tokens on Base using CDP swap quote. Returns a wallet_sendCalls payload that can include an optional approval call and the swap call. Only supports base-mainnet and base-sepolia.",
|
|
54
|
-
"default": "disabled"
|
|
55
|
-
},
|
|
56
|
-
"xmtp_get_swap_price": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
"title": "XMTP Get Swap Price",
|
|
59
|
-
"enum": [
|
|
60
|
-
"disabled",
|
|
61
|
-
"public",
|
|
62
|
-
"private"
|
|
63
|
-
],
|
|
64
|
-
"x-enum-title": [
|
|
65
|
-
"Disabled",
|
|
66
|
-
"Agent Owner + All Users",
|
|
67
|
-
"Agent Owner Only"
|
|
68
|
-
],
|
|
69
|
-
"description": "Get an indicative swap price/quote for token pair and amount on Base networks using CDP. Provides estimated output amounts for token swaps without creating transactions.",
|
|
70
|
-
"default": "disabled"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
38
|
+
"xmtp_swap": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"title": "XMTP Swap",
|
|
41
|
+
"enum": [
|
|
42
|
+
"disabled",
|
|
43
|
+
"public",
|
|
44
|
+
"private"
|
|
45
|
+
],
|
|
46
|
+
"x-enum-title": [
|
|
47
|
+
"Disabled",
|
|
48
|
+
"Agent Owner + All Users",
|
|
49
|
+
"Agent Owner Only"
|
|
50
|
+
],
|
|
51
|
+
"description": "Create XMTP transaction requests for swapping tokens on Base using CDP swap quote. Returns a wallet_sendCalls payload that can include an optional approval call and the swap call. Only supports base-mainnet and base-sepolia.",
|
|
52
|
+
"default": "disabled"
|
|
53
|
+
},
|
|
54
|
+
"xmtp_get_swap_price": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"title": "XMTP Get Swap Price",
|
|
57
|
+
"enum": [
|
|
58
|
+
"disabled",
|
|
59
|
+
"public",
|
|
60
|
+
"private"
|
|
61
|
+
],
|
|
62
|
+
"x-enum-title": [
|
|
63
|
+
"Disabled",
|
|
64
|
+
"Agent Owner + All Users",
|
|
65
|
+
"Agent Owner Only"
|
|
66
|
+
],
|
|
67
|
+
"description": "Get an indicative swap price/quote for token pair and amount on Base networks using CDP. Provides estimated output amounts for token swaps without creating transactions.",
|
|
68
|
+
"default": "disabled"
|
|
73
69
|
}
|
|
70
|
+
}
|
|
74
71
|
}
|
|
75
|
-
}
|
|
72
|
+
}
|
|
73
|
+
}
|
intentkit/skills/xmtp/swap.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
from typing import List, Tuple, Type
|
|
2
|
-
|
|
3
1
|
from pydantic import BaseModel, Field
|
|
4
2
|
|
|
5
|
-
from intentkit.clients.cdp import
|
|
3
|
+
from intentkit.clients.cdp import get_cdp_client
|
|
6
4
|
from intentkit.models.chat import ChatMessageAttachment, ChatMessageAttachmentType
|
|
7
5
|
from intentkit.skills.xmtp.base import XmtpBaseTool
|
|
8
6
|
|
|
@@ -33,16 +31,16 @@ class XmtpSwap(XmtpBaseTool):
|
|
|
33
31
|
|
|
34
32
|
Generates a wallet_sendCalls transaction request to perform a token swap.
|
|
35
33
|
May include an ERC20 approval call followed by the router swap call.
|
|
36
|
-
Supports Base
|
|
34
|
+
Supports Ethereum, Polygon, Base, Arbitrum, and Optimism networks (both mainnet and testnet).
|
|
37
35
|
"""
|
|
38
36
|
|
|
39
37
|
name: str = "xmtp_swap"
|
|
40
38
|
description: str = (
|
|
41
|
-
"Create an XMTP transaction request for swapping tokens
|
|
39
|
+
"Create an XMTP transaction request for swapping tokens using CDP swap quote. "
|
|
42
40
|
"Returns a wallet_sendCalls payload that can include an optional approval call and the swap call. "
|
|
43
|
-
"
|
|
41
|
+
"Supports Ethereum, Base, Arbitrum, and Optimism mainnet networks."
|
|
44
42
|
)
|
|
45
|
-
args_schema:
|
|
43
|
+
args_schema: type[BaseModel] = SwapInput
|
|
46
44
|
|
|
47
45
|
async def _arun(
|
|
48
46
|
self,
|
|
@@ -51,7 +49,7 @@ class XmtpSwap(XmtpBaseTool):
|
|
|
51
49
|
to_token: str,
|
|
52
50
|
from_amount: str,
|
|
53
51
|
slippage_bps: int = 100,
|
|
54
|
-
) ->
|
|
52
|
+
) -> tuple[str, list[ChatMessageAttachment]]:
|
|
55
53
|
# Input validation
|
|
56
54
|
if (
|
|
57
55
|
not from_address
|
|
@@ -87,36 +85,35 @@ class XmtpSwap(XmtpBaseTool):
|
|
|
87
85
|
context = self.get_context()
|
|
88
86
|
agent = context.agent
|
|
89
87
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
"base-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
# Only support mainnet networks for swap
|
|
89
|
+
supported_networks = [
|
|
90
|
+
"ethereum-mainnet",
|
|
91
|
+
"base-mainnet",
|
|
92
|
+
"arbitrum-mainnet",
|
|
93
|
+
"optimism-mainnet",
|
|
94
|
+
]
|
|
95
|
+
if agent.network_id not in supported_networks:
|
|
97
96
|
raise ValueError(
|
|
98
|
-
f"
|
|
97
|
+
f"Swap only supported on {', '.join(supported_networks)}. Current: {agent.network_id}"
|
|
99
98
|
)
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
# Validate network and get chain ID
|
|
101
|
+
chain_id_hex = self.validate_network_and_get_chain_id(agent.network_id, "swap")
|
|
102
102
|
|
|
103
|
-
# CDP network
|
|
103
|
+
# Get CDP network name
|
|
104
104
|
# Reference: CDP SDK examples for swap quote and price
|
|
105
105
|
# https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/swaps/create_swap_quote.py
|
|
106
|
-
network_for_cdp =
|
|
107
|
-
"base-mainnet": "base",
|
|
108
|
-
"base-sepolia": "base-sepolia",
|
|
109
|
-
}[agent.network_id]
|
|
106
|
+
network_for_cdp = self.get_cdp_network(agent.network_id)
|
|
110
107
|
|
|
111
|
-
# Get CDP client from global
|
|
112
|
-
cdp_client =
|
|
108
|
+
# Get CDP client from the global helper (server-side credentials)
|
|
109
|
+
cdp_client = get_cdp_client()
|
|
113
110
|
|
|
114
111
|
# Call CDP to create swap quote and extract call datas
|
|
115
112
|
# Be permissive with response shape across SDK versions
|
|
116
113
|
try:
|
|
117
114
|
# Attempt the canonical method per CDP SDK examples
|
|
118
115
|
# create_swap_quote(from_token, to_token, from_amount, network, taker, slippage_bps, signer_address)
|
|
119
|
-
# Note: Don't use async with context manager as
|
|
116
|
+
# Note: Don't use async with context manager as get_cdp_client returns a managed global client
|
|
120
117
|
quote = await cdp_client.evm.create_swap_quote(
|
|
121
118
|
from_token=from_token,
|
|
122
119
|
to_token=to_token,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from typing import List, Optional, Tuple, Type
|
|
2
|
-
|
|
3
1
|
from pydantic import BaseModel, Field
|
|
2
|
+
from web3.exceptions import ContractLogicError
|
|
4
3
|
|
|
5
4
|
from intentkit.models.chat import ChatMessageAttachment, ChatMessageAttachmentType
|
|
6
5
|
from intentkit.skills.xmtp.base import XmtpBaseTool
|
|
@@ -12,13 +11,10 @@ class TransferInput(BaseModel):
|
|
|
12
11
|
from_address: str = Field(description="The sender address for the transfer")
|
|
13
12
|
to_address: str = Field(description="The recipient address for the transfer")
|
|
14
13
|
amount: str = Field(
|
|
15
|
-
description="The amount to transfer (
|
|
16
|
-
)
|
|
17
|
-
decimals: int = Field(
|
|
18
|
-
description="Number of decimal places for the token (18 for ETH, varies for ERC20 tokens)"
|
|
14
|
+
description="The amount to transfer in human-readable format (e.g., '1.5' for 1.5 ETH, '100' for 100 USDC). Do NOT multiply by token decimals."
|
|
19
15
|
)
|
|
20
|
-
currency: str = Field(description="Currency symbol (e.g., 'ETH', 'USDC', '
|
|
21
|
-
token_contract_address:
|
|
16
|
+
currency: str = Field(description="Currency symbol (e.g., 'ETH', 'USDC', 'NATION')")
|
|
17
|
+
token_contract_address: str | None = Field(
|
|
22
18
|
default=None,
|
|
23
19
|
description="Token contract address for ERC20 transfers. Leave empty for ETH transfers.",
|
|
24
20
|
)
|
|
@@ -28,36 +24,29 @@ class XmtpTransfer(XmtpBaseTool):
|
|
|
28
24
|
"""Skill for creating XMTP transfer transactions."""
|
|
29
25
|
|
|
30
26
|
name: str = "xmtp_transfer"
|
|
31
|
-
description: str = """Create an XMTP transaction request for transferring ETH or ERC20 tokens
|
|
32
|
-
|
|
27
|
+
description: str = """Create an XMTP transaction request for transferring ETH or ERC20 tokens.
|
|
33
28
|
This skill generates a wallet_sendCalls transaction request according to XMTP protocol
|
|
34
|
-
that can be sent to users for signing.
|
|
35
|
-
|
|
36
|
-
- ERC20 tokens (when token_contract_address is provided)
|
|
37
|
-
|
|
38
|
-
Only supports Base mainnet network.
|
|
29
|
+
that can be sent to users for signing.
|
|
30
|
+
Supports Ethereum, Polygon, Base, Arbitrum, and Optimism networks (both mainnet and testnet).
|
|
39
31
|
"""
|
|
40
|
-
args_schema:
|
|
32
|
+
args_schema: type[BaseModel] = TransferInput
|
|
41
33
|
|
|
42
34
|
async def _arun(
|
|
43
35
|
self,
|
|
44
36
|
from_address: str,
|
|
45
37
|
to_address: str,
|
|
46
38
|
amount: str,
|
|
47
|
-
decimals: int,
|
|
48
39
|
currency: str,
|
|
49
|
-
token_contract_address:
|
|
50
|
-
) ->
|
|
40
|
+
token_contract_address: str | None,
|
|
41
|
+
) -> tuple[str, list[ChatMessageAttachment]]:
|
|
51
42
|
"""Create an XMTP transfer transaction request.
|
|
52
43
|
|
|
53
44
|
Args:
|
|
54
45
|
from_address: The sender address
|
|
55
46
|
to_address: The recipient address
|
|
56
47
|
amount: Amount to transfer
|
|
57
|
-
decimals: Token decimals
|
|
58
48
|
currency: Currency symbol
|
|
59
49
|
token_contract_address: Token contract address (None for ETH)
|
|
60
|
-
config: LangChain runnable config
|
|
61
50
|
|
|
62
51
|
Returns:
|
|
63
52
|
Tuple of (content_message, list_of_attachments)
|
|
@@ -66,19 +55,69 @@ class XmtpTransfer(XmtpBaseTool):
|
|
|
66
55
|
context = self.get_context()
|
|
67
56
|
agent = context.agent
|
|
68
57
|
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
58
|
+
# Validate network and get chain ID
|
|
59
|
+
chain_id_hex = self.validate_network_and_get_chain_id(
|
|
60
|
+
agent.network_id, "transfer"
|
|
61
|
+
)
|
|
74
62
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
63
|
+
# Validate token contract and get decimals
|
|
64
|
+
if token_contract_address:
|
|
65
|
+
# Validate ERC20 contract and get token info
|
|
66
|
+
web3 = self.web3_client()
|
|
80
67
|
|
|
81
|
-
|
|
68
|
+
# ERC20 ABI for symbol() and decimals() functions
|
|
69
|
+
erc20_abi = [
|
|
70
|
+
{
|
|
71
|
+
"constant": True,
|
|
72
|
+
"inputs": [],
|
|
73
|
+
"name": "symbol",
|
|
74
|
+
"outputs": [{"name": "", "type": "string"}],
|
|
75
|
+
"type": "function",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"constant": True,
|
|
79
|
+
"inputs": [],
|
|
80
|
+
"name": "decimals",
|
|
81
|
+
"outputs": [{"name": "", "type": "uint8"}],
|
|
82
|
+
"type": "function",
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
# Create contract instance
|
|
88
|
+
contract = web3.eth.contract(
|
|
89
|
+
address=web3.to_checksum_address(token_contract_address),
|
|
90
|
+
abi=erc20_abi,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Get token symbol and decimals
|
|
94
|
+
token_symbol = contract.functions.symbol().call()
|
|
95
|
+
decimals = contract.functions.decimals().call()
|
|
96
|
+
|
|
97
|
+
# Validate symbol matches currency (case insensitive)
|
|
98
|
+
if token_symbol.upper() != currency.upper():
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"Token symbol mismatch: contract symbol is '{token_symbol}', "
|
|
101
|
+
f"but currency parameter is '{currency}'"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
except ContractLogicError:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Invalid ERC20 contract address: {token_contract_address}. "
|
|
107
|
+
"The address does not point to a valid ERC20 token contract."
|
|
108
|
+
)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Failed to validate ERC20 contract {token_contract_address}: {str(e)}"
|
|
112
|
+
)
|
|
113
|
+
else:
|
|
114
|
+
# For ETH transfers, use 18 decimals
|
|
115
|
+
decimals = 18
|
|
116
|
+
# Validate currency is ETH for native transfers
|
|
117
|
+
if currency.upper() != "ETH":
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"For native transfers, currency must be 'ETH', got '{currency}'"
|
|
120
|
+
)
|
|
82
121
|
|
|
83
122
|
# Calculate amount in smallest unit (wei for ETH, token units for ERC20)
|
|
84
123
|
amount_int = int(float(amount) * (10**decimals))
|
intentkit/utils/chain.py
CHANGED
|
@@ -422,9 +422,9 @@ class QuicknodeChainProvider(ChainProvider):
|
|
|
422
422
|
)
|
|
423
423
|
|
|
424
424
|
except httpx.HTTPStatusError as http_err:
|
|
425
|
-
raise (f"Quicknode API HTTP Error: {http_err}")
|
|
425
|
+
raise Exception(f"Quicknode API HTTP Error: {http_err}")
|
|
426
426
|
except httpx.RequestError as req_err:
|
|
427
|
-
raise (f"Quicknode API Request Error: {req_err}")
|
|
427
|
+
raise Exception(f"Quicknode API Request Error: {req_err}")
|
|
428
428
|
except (
|
|
429
429
|
KeyError,
|
|
430
430
|
TypeError,
|
|
@@ -433,4 +433,4 @@ class QuicknodeChainProvider(ChainProvider):
|
|
|
433
433
|
f"Error processing QuickNode API response: {e}. Check the API response format."
|
|
434
434
|
)
|
|
435
435
|
except Exception as e:
|
|
436
|
-
raise (f"Quicknode API An unexpected error occurred: {e}")
|
|
436
|
+
raise Exception(f"Quicknode API An unexpected error occurred: {e}")
|
intentkit/utils/error.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Sequence
|
|
3
3
|
|
|
4
4
|
from fastapi.exceptions import RequestValidationError
|
|
5
5
|
from fastapi.utils import is_body_allowed_for_status_code
|
|
@@ -11,8 +11,21 @@ from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
+
# error messages in agent system message response
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RateLimitExceeded(Exception):
|
|
18
|
+
"""Rate limit exceeded"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, message: str | None = "Rate limit exceeded"):
|
|
21
|
+
self.message = message
|
|
22
|
+
super().__init__(self.message)
|
|
23
|
+
|
|
14
24
|
|
|
15
25
|
class IntentKitAPIError(Exception):
|
|
26
|
+
"""All 3 parameters: status_code, key and message is required.
|
|
27
|
+
The key is PascalCase string, to allow the frontend to test errors."""
|
|
28
|
+
|
|
16
29
|
def __init__(self, status_code: int, key: str, message: str):
|
|
17
30
|
self.key = key
|
|
18
31
|
self.message = message
|
intentkit/utils/logging.py
CHANGED
|
@@ -4,13 +4,11 @@ Logging configuration module
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
|
-
from
|
|
7
|
+
from collections.abc import Callable
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class JsonFormatter(logging.Formatter):
|
|
11
|
-
def __init__(
|
|
12
|
-
self, filter_func: Optional[Callable[[logging.LogRecord], bool]] = None
|
|
13
|
-
):
|
|
11
|
+
def __init__(self, filter_func: Callable[[logging.LogRecord], bool] | None = None):
|
|
14
12
|
super().__init__()
|
|
15
13
|
self.filter_func = filter_func
|
|
16
14
|
|
intentkit/utils/s3.py
CHANGED
|
@@ -5,7 +5,6 @@ S3 utility module for storing and retrieving images from AWS S3.
|
|
|
5
5
|
import logging
|
|
6
6
|
from enum import Enum
|
|
7
7
|
from io import BytesIO
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
import boto3
|
|
11
10
|
import filetype
|
|
@@ -16,10 +15,10 @@ from mypy_boto3_s3.client import S3Client
|
|
|
16
15
|
logger = logging.getLogger(__name__)
|
|
17
16
|
|
|
18
17
|
# Global variables for S3 configuration
|
|
19
|
-
_bucket:
|
|
20
|
-
_client:
|
|
21
|
-
_prefix:
|
|
22
|
-
_cdn_url:
|
|
18
|
+
_bucket: str | None = None
|
|
19
|
+
_client: S3Client | None = None
|
|
20
|
+
_prefix: str | None = None
|
|
21
|
+
_cdn_url: str | None = None
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def init_s3(bucket: str, cdn_url: str, env: str) -> None:
|
|
@@ -114,7 +113,7 @@ async def store_image(url: str, key: str) -> str:
|
|
|
114
113
|
|
|
115
114
|
|
|
116
115
|
async def store_image_bytes(
|
|
117
|
-
image_bytes: bytes, key: str, content_type:
|
|
116
|
+
image_bytes: bytes, key: str, content_type: str | None = None
|
|
118
117
|
) -> str:
|
|
119
118
|
"""
|
|
120
119
|
Store raw image bytes to S3.
|
|
@@ -182,11 +181,64 @@ class FileType(str, Enum):
|
|
|
182
181
|
PDF = "pdf"
|
|
183
182
|
|
|
184
183
|
|
|
184
|
+
async def store_file(
|
|
185
|
+
content: bytes,
|
|
186
|
+
key: str,
|
|
187
|
+
content_type: str | None = None,
|
|
188
|
+
size: int | None = None,
|
|
189
|
+
) -> str:
|
|
190
|
+
"""Store raw file bytes with automatic content type detection."""
|
|
191
|
+
if not _client or not _bucket or not _prefix or not _cdn_url:
|
|
192
|
+
logger.info("S3 not initialized. Cannot store file bytes.")
|
|
193
|
+
return ""
|
|
194
|
+
|
|
195
|
+
if not content:
|
|
196
|
+
raise ValueError("File content cannot be empty")
|
|
197
|
+
|
|
198
|
+
actual_size = len(content)
|
|
199
|
+
if size is not None and size != actual_size:
|
|
200
|
+
raise ValueError(
|
|
201
|
+
f"Provided size {size} does not match actual content size {actual_size} bytes"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
effective_size = size if size is not None else actual_size
|
|
205
|
+
|
|
206
|
+
detected_content_type = content_type
|
|
207
|
+
if not detected_content_type:
|
|
208
|
+
kind = filetype.guess(content)
|
|
209
|
+
detected_content_type = (
|
|
210
|
+
kind.mime if kind and kind.mime else "application/octet-stream"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
prefixed_key = f"{_prefix}{key}"
|
|
214
|
+
file_obj = BytesIO(content)
|
|
215
|
+
|
|
216
|
+
logger.info(
|
|
217
|
+
"Uploading file to S3 with content type %s and size %s bytes",
|
|
218
|
+
detected_content_type,
|
|
219
|
+
effective_size,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
_client.upload_fileobj(
|
|
223
|
+
file_obj,
|
|
224
|
+
_bucket,
|
|
225
|
+
prefixed_key,
|
|
226
|
+
ExtraArgs={
|
|
227
|
+
"ContentType": detected_content_type,
|
|
228
|
+
"ContentDisposition": "inline",
|
|
229
|
+
},
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
cdn_url = f"{_cdn_url}/{prefixed_key}"
|
|
233
|
+
logger.info("File uploaded successfully to %s", cdn_url)
|
|
234
|
+
return cdn_url
|
|
235
|
+
|
|
236
|
+
|
|
185
237
|
async def store_file_bytes(
|
|
186
238
|
file_bytes: bytes,
|
|
187
239
|
key: str,
|
|
188
240
|
file_type: FileType,
|
|
189
|
-
size_limit_bytes:
|
|
241
|
+
size_limit_bytes: int | None = None,
|
|
190
242
|
) -> str:
|
|
191
243
|
"""
|
|
192
244
|
Store raw file bytes (image, video, sound, pdf) to S3.
|