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
|
@@ -1,321 +1,182 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
class
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
and p.baseToken.symbol
|
|
184
|
-
and p.baseToken.symbol.upper() == target_ticker
|
|
185
|
-
]
|
|
186
|
-
if not pairs_list:
|
|
187
|
-
return await self._no_pairs_found_response(
|
|
188
|
-
query, reason=f"no match for ticker '${target_ticker}'"
|
|
189
|
-
)
|
|
190
|
-
elif query_type == QueryType.ADDRESS:
|
|
191
|
-
# Filter by address (checking pairAddress, baseToken.address, quoteToken.address)
|
|
192
|
-
pairs_list = [
|
|
193
|
-
p
|
|
194
|
-
for p in pairs_list
|
|
195
|
-
if (p.pairAddress and p.pairAddress.lower() == search_query.lower())
|
|
196
|
-
or (
|
|
197
|
-
p.baseToken
|
|
198
|
-
and p.baseToken.address
|
|
199
|
-
and p.baseToken.address.lower() == search_query.lower()
|
|
200
|
-
)
|
|
201
|
-
or (
|
|
202
|
-
p.quoteToken
|
|
203
|
-
and p.quoteToken.address
|
|
204
|
-
and p.quoteToken.address.lower() == search_query.lower()
|
|
205
|
-
)
|
|
206
|
-
]
|
|
207
|
-
if not pairs_list:
|
|
208
|
-
return await self._no_pairs_found_response(
|
|
209
|
-
query, reason=f"no match for address '{search_query}'"
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
try:
|
|
213
|
-
sort_func = get_sort_key_func()
|
|
214
|
-
pairs_list.sort(key=sort_func, reverse=True)
|
|
215
|
-
except Exception as sort_err:
|
|
216
|
-
logger.error(f"Sorting failed: {sort_err}", exc_info=True)
|
|
217
|
-
return json.dumps(
|
|
218
|
-
{
|
|
219
|
-
"error": "Failed to sort results.",
|
|
220
|
-
"error_type": "sorting_error",
|
|
221
|
-
"details": str(sort_err),
|
|
222
|
-
"unsorted_results": [
|
|
223
|
-
p.model_dump() for p in pairs_list[:MAX_RESULTS_LIMIT]
|
|
224
|
-
],
|
|
225
|
-
**DISCLAIMER_TEXT,
|
|
226
|
-
},
|
|
227
|
-
indent=2,
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
final_count = min(len(pairs_list), MAX_RESULTS_LIMIT)
|
|
231
|
-
logger.info(f"Returning {final_count} pairs for query '{query}'")
|
|
232
|
-
return json.dumps(
|
|
233
|
-
{
|
|
234
|
-
**DISCLAIMER_TEXT,
|
|
235
|
-
"pairs": [p.model_dump() for p in pairs_list[:MAX_RESULTS_LIMIT]],
|
|
236
|
-
},
|
|
237
|
-
indent=2,
|
|
238
|
-
)
|
|
239
|
-
except Exception as e:
|
|
240
|
-
return await self._handle_unexpected_runtime_error(e, query)
|
|
241
|
-
|
|
242
|
-
def get_query_type(self, query: str) -> QueryType:
|
|
243
|
-
"""
|
|
244
|
-
Determine whether the query is a TEXT, TICKER, or ADDRESS.
|
|
245
|
-
|
|
246
|
-
TICKER: starts with '$'
|
|
247
|
-
ADDRESS: starts with '0x'.
|
|
248
|
-
TEXT: anything else.
|
|
249
|
-
"""
|
|
250
|
-
if query.startswith("0x"):
|
|
251
|
-
return QueryType.ADDRESS
|
|
252
|
-
if query.startswith("$"):
|
|
253
|
-
return QueryType.TICKER
|
|
254
|
-
return QueryType.TEXT
|
|
255
|
-
|
|
256
|
-
async def _handle_error_response(self, error_details: dict) -> str:
|
|
257
|
-
"""Formats error details (from _get) into a JSON string."""
|
|
258
|
-
if error_details.get("error_type") in [
|
|
259
|
-
"connection_error",
|
|
260
|
-
"parsing_error",
|
|
261
|
-
"unexpected_error",
|
|
262
|
-
]:
|
|
263
|
-
logger.error(f"DexScreener tool encountered an error: {error_details}")
|
|
264
|
-
else: # api_error
|
|
265
|
-
logger.warning(f"DexScreener API returned an error: {error_details}")
|
|
266
|
-
|
|
267
|
-
# Truncate potentially large fields before returning to user/LLM
|
|
268
|
-
for key in ["details", "response_body"]:
|
|
269
|
-
if (
|
|
270
|
-
isinstance(error_details.get(key), str)
|
|
271
|
-
and len(error_details[key]) > 500
|
|
272
|
-
):
|
|
273
|
-
error_details[key] = error_details[key][:500] + "... (truncated)"
|
|
274
|
-
|
|
275
|
-
return json.dumps(error_details, indent=2)
|
|
276
|
-
|
|
277
|
-
async def _handle_validation_error(
|
|
278
|
-
self, e: ValidationError, query: str, data: Any
|
|
279
|
-
) -> str:
|
|
280
|
-
"""Formats validation error details into a JSON string."""
|
|
281
|
-
logger.error(
|
|
282
|
-
f"Failed to validate DexScreener response structure for query '{query}'. Error: {e}. Raw data length: {len(str(data))}",
|
|
283
|
-
exc_info=True,
|
|
284
|
-
)
|
|
285
|
-
# Avoid sending potentially huge raw data back
|
|
286
|
-
return json.dumps(
|
|
287
|
-
{
|
|
288
|
-
"error": "Failed to parse successful DexScreener API response",
|
|
289
|
-
"error_type": "validation_error",
|
|
290
|
-
"details": e.errors(),
|
|
291
|
-
},
|
|
292
|
-
indent=2,
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
async def _handle_unexpected_runtime_error(self, e: Exception, query: str) -> str:
|
|
296
|
-
"""Formats unexpected runtime exception details into a JSON string."""
|
|
297
|
-
logger.exception(
|
|
298
|
-
f"An unexpected runtime error occurred in search_token tool _arun method for query '{query}': {e}"
|
|
299
|
-
)
|
|
300
|
-
return json.dumps(
|
|
301
|
-
{
|
|
302
|
-
"error": "An unexpected internal error occurred processing the search request",
|
|
303
|
-
"error_type": "runtime_error",
|
|
304
|
-
"details": str(e),
|
|
305
|
-
},
|
|
306
|
-
indent=2,
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
async def _no_pairs_found_response(
|
|
310
|
-
self, query: str, reason: str = "returned no matching pairs"
|
|
311
|
-
) -> str:
|
|
312
|
-
"""Generates the standard 'no pairs found' JSON response."""
|
|
313
|
-
logger.info(f"DexScreener search for query '{query}': {reason}.")
|
|
314
|
-
return json.dumps(
|
|
315
|
-
{
|
|
316
|
-
"message": f"No matching pairs found for the query '{query}'. Reason: {reason}.",
|
|
317
|
-
"query": query,
|
|
318
|
-
"pairs": [],
|
|
319
|
-
},
|
|
320
|
-
indent=2,
|
|
321
|
-
)
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
5
|
+
|
|
6
|
+
from intentkit.skills.dexscreener.base import DexScreenerBaseTool
|
|
7
|
+
from intentkit.skills.dexscreener.model.search_token_response import (
|
|
8
|
+
SearchTokenResponseModel,
|
|
9
|
+
)
|
|
10
|
+
from intentkit.skills.dexscreener.utils import (
|
|
11
|
+
API_ENDPOINTS,
|
|
12
|
+
MAX_SEARCH_RESULTS,
|
|
13
|
+
SEARCH_DISCLAIMER,
|
|
14
|
+
QueryType,
|
|
15
|
+
SortBy,
|
|
16
|
+
VolumeTimeframe,
|
|
17
|
+
create_error_response,
|
|
18
|
+
create_no_results_response,
|
|
19
|
+
determine_query_type,
|
|
20
|
+
filter_address_pairs,
|
|
21
|
+
filter_ticker_pairs,
|
|
22
|
+
format_success_response,
|
|
23
|
+
handle_validation_error,
|
|
24
|
+
sort_pairs_by_criteria,
|
|
25
|
+
truncate_large_fields,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SearchTokenInput(BaseModel):
|
|
32
|
+
"""Input schema for the DexScreener search_token tool."""
|
|
33
|
+
|
|
34
|
+
query: str = Field(
|
|
35
|
+
description="The search query string (e.g., token symbol 'WIF', pair address, token address '0x...', token name 'Dogwifhat', or ticker '$WIF'). Prefixing with '$' filters results to match the base token symbol exactly (case-insensitive)."
|
|
36
|
+
)
|
|
37
|
+
sort_by: SortBy | None = Field(
|
|
38
|
+
default=SortBy.LIQUIDITY,
|
|
39
|
+
description="Sort preference for the results. Options: 'liquidity' (default) or 'volume'",
|
|
40
|
+
)
|
|
41
|
+
volume_timeframe: VolumeTimeframe | None = Field(
|
|
42
|
+
default=VolumeTimeframe.TWENTY_FOUR_HOUR,
|
|
43
|
+
description="Define which timeframe should we use if the 'sort_by' is 'volume'. Available options: '5_minutes', '1_hour', '6_hour', '24_hour'",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SearchToken(DexScreenerBaseTool):
|
|
48
|
+
"""
|
|
49
|
+
Tool to search for token pairs on DexScreener based on a query string.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
name: str = "dexscreener_search_token"
|
|
53
|
+
description: str = (
|
|
54
|
+
f"Searches DexScreener for token pairs matching the provided query string "
|
|
55
|
+
f"(e.g., token symbol like 'WIF', pair address, token name like 'Dogwifhat', or ticker like '$WIF'). "
|
|
56
|
+
f"If the query starts with '$', it filters results to only include pairs where the base token symbol exactly matches the ticker (case-insensitive). "
|
|
57
|
+
f"Returns a list of matching pairs with details like price, volume, liquidity, etc., "
|
|
58
|
+
f"sorted by the specified criteria (via 'sort_by': 'liquidity', 'volume'; defaults to 'liquidity'), "
|
|
59
|
+
f"limited to the top {MAX_SEARCH_RESULTS}. "
|
|
60
|
+
f"Use this tool to find token information based on user queries."
|
|
61
|
+
)
|
|
62
|
+
args_schema: type[BaseModel] = SearchTokenInput
|
|
63
|
+
|
|
64
|
+
async def _arun(
|
|
65
|
+
self,
|
|
66
|
+
query: str,
|
|
67
|
+
sort_by: SortBy | None = SortBy.LIQUIDITY,
|
|
68
|
+
volume_timeframe: VolumeTimeframe | None = VolumeTimeframe.TWENTY_FOUR_HOUR,
|
|
69
|
+
**kwargs: Any,
|
|
70
|
+
) -> str:
|
|
71
|
+
"""Implementation to search token, with filtering based on query type."""
|
|
72
|
+
|
|
73
|
+
# dexscreener 300 request per minute (across all user) based on dexscreener docs
|
|
74
|
+
# https://docs.dexscreener.com/api/reference#get-latest-dex-search
|
|
75
|
+
await self.global_rate_limit_by_skill(
|
|
76
|
+
limit=300,
|
|
77
|
+
seconds=60,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
sort_by = sort_by or SortBy.LIQUIDITY
|
|
81
|
+
volume_timeframe = volume_timeframe or VolumeTimeframe.TWENTY_FOUR_HOUR
|
|
82
|
+
|
|
83
|
+
# Determine query type
|
|
84
|
+
query_type = determine_query_type(query)
|
|
85
|
+
|
|
86
|
+
# Process query based on type
|
|
87
|
+
if query_type == QueryType.TICKER:
|
|
88
|
+
search_query = query[1:] # Remove the '$' prefix
|
|
89
|
+
target_ticker = search_query.upper()
|
|
90
|
+
else:
|
|
91
|
+
search_query = query
|
|
92
|
+
target_ticker = None
|
|
93
|
+
|
|
94
|
+
logger.info(
|
|
95
|
+
f"Executing DexScreener search_token tool with query: '{query}' "
|
|
96
|
+
f"(interpreted as {query_type.value} search for '{search_query}'), "
|
|
97
|
+
f"sort_by: {sort_by}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
data, error_details = await self._get(
|
|
102
|
+
path=API_ENDPOINTS["search"], params={"q": search_query}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if error_details:
|
|
106
|
+
return await self._handle_error_response(error_details)
|
|
107
|
+
if not data:
|
|
108
|
+
logger.error(f"No data or error details returned for query '{query}'")
|
|
109
|
+
return create_error_response(
|
|
110
|
+
error_type="empty_success",
|
|
111
|
+
message="API call returned empty success response.",
|
|
112
|
+
additional_data={"query": query},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
result = SearchTokenResponseModel.model_validate(data)
|
|
117
|
+
except ValidationError as e:
|
|
118
|
+
return handle_validation_error(e, query, len(str(data)))
|
|
119
|
+
|
|
120
|
+
if not result.pairs:
|
|
121
|
+
return create_no_results_response(
|
|
122
|
+
query, reason="returned null or empty for pairs"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
pairs_list = [p for p in result.pairs if p is not None]
|
|
126
|
+
|
|
127
|
+
# Apply filtering based on query type
|
|
128
|
+
if query_type == QueryType.TICKER and target_ticker:
|
|
129
|
+
pairs_list = filter_ticker_pairs(pairs_list, target_ticker)
|
|
130
|
+
if not pairs_list:
|
|
131
|
+
return create_no_results_response(
|
|
132
|
+
query, reason=f"no match for ticker '${target_ticker}'"
|
|
133
|
+
)
|
|
134
|
+
elif query_type == QueryType.ADDRESS:
|
|
135
|
+
pairs_list = filter_address_pairs(pairs_list, search_query)
|
|
136
|
+
if not pairs_list:
|
|
137
|
+
return create_no_results_response(
|
|
138
|
+
query, reason=f"no match for address '{search_query}'"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Sort pairs by specified criteria
|
|
142
|
+
pairs_list = sort_pairs_by_criteria(pairs_list, sort_by, volume_timeframe)
|
|
143
|
+
|
|
144
|
+
# If sorting failed, pairs_list will be returned unchanged by the utility function
|
|
145
|
+
|
|
146
|
+
final_count = min(len(pairs_list), MAX_SEARCH_RESULTS)
|
|
147
|
+
logger.info(f"Returning {final_count} pairs for query '{query}'")
|
|
148
|
+
return format_success_response(
|
|
149
|
+
{
|
|
150
|
+
**SEARCH_DISCLAIMER,
|
|
151
|
+
"pairs": [p.model_dump() for p in pairs_list[:MAX_SEARCH_RESULTS]],
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return await self._handle_unexpected_runtime_error(e, query)
|
|
156
|
+
|
|
157
|
+
async def _handle_error_response(self, error_details: dict) -> str:
|
|
158
|
+
"""Formats error details (from _get) into a JSON string."""
|
|
159
|
+
if error_details.get("error_type") in [
|
|
160
|
+
"connection_error",
|
|
161
|
+
"parsing_error",
|
|
162
|
+
"unexpected_error",
|
|
163
|
+
]:
|
|
164
|
+
logger.error(f"DexScreener tool encountered an error: {error_details}")
|
|
165
|
+
else: # api_error
|
|
166
|
+
logger.warning(f"DexScreener API returned an error: {error_details}")
|
|
167
|
+
|
|
168
|
+
# Truncate potentially large fields before returning to user/LLM
|
|
169
|
+
truncated_details = truncate_large_fields(error_details)
|
|
170
|
+
return format_success_response(truncated_details)
|
|
171
|
+
|
|
172
|
+
async def _handle_unexpected_runtime_error(self, e: Exception, query: str) -> str:
|
|
173
|
+
"""Formats unexpected runtime exception details into a JSON string."""
|
|
174
|
+
logger.exception(
|
|
175
|
+
f"An unexpected runtime error occurred in search_token tool _arun method for query '{query}': {e}"
|
|
176
|
+
)
|
|
177
|
+
return create_error_response(
|
|
178
|
+
error_type="runtime_error",
|
|
179
|
+
message="An unexpected internal error occurred processing the search request",
|
|
180
|
+
details=str(e),
|
|
181
|
+
additional_data={"query": query},
|
|
182
|
+
)
|