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,133 +1,125 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
import httpx
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from intentkit.
|
|
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
|
-
|
|
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
|
-
error_details
|
|
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
|
-
error_details = {
|
|
127
|
-
"error": "An unexpected error occurred during API call",
|
|
128
|
-
"error_type": "unexpected_error",
|
|
129
|
-
"status_code": status_code, # Include if available
|
|
130
|
-
"details": str(e),
|
|
131
|
-
"url": url,
|
|
132
|
-
}
|
|
133
|
-
return None, error_details # Return unexpected error
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from intentkit.skills.base import IntentKitSkill
|
|
8
|
+
from intentkit.skills.dexscreener.utils import DEXSCREENER_BASE_URL
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
# ApiResult still represents (success_data, error_data)
|
|
13
|
+
ApiResult = tuple[dict[str, Any] | None, dict[str, Any] | None]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DexScreenerBaseTool(IntentKitSkill):
|
|
17
|
+
"""
|
|
18
|
+
Generic base class for tools interacting with the Dex Screener API.
|
|
19
|
+
Handles shared logic like API calls and error reporting via return values.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
base_url: str = DEXSCREENER_BASE_URL
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def category(self) -> str:
|
|
26
|
+
return "dexscreener"
|
|
27
|
+
|
|
28
|
+
async def _get(
|
|
29
|
+
self,
|
|
30
|
+
path: str,
|
|
31
|
+
params: dict[str, Any] | None = None,
|
|
32
|
+
) -> ApiResult:
|
|
33
|
+
"""
|
|
34
|
+
Makes an asynchronous GET request to the DexScreener API.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
path: The API endpoint path (e.g., "/dex/search").
|
|
38
|
+
params: Optional dictionary of query parameters.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A tuple (data, error_details):
|
|
42
|
+
- (dict, None): On HTTP 2xx success with valid JSON response.
|
|
43
|
+
- (None, dict): On any error (API error, connection error,
|
|
44
|
+
JSON parsing error, unexpected error). The dict
|
|
45
|
+
contains details including an 'error_type'.
|
|
46
|
+
"""
|
|
47
|
+
if not path.startswith("/"):
|
|
48
|
+
path = "/" + path
|
|
49
|
+
|
|
50
|
+
url = f"{self.base_url}{path}"
|
|
51
|
+
headers = {"Accept": "application/json"}
|
|
52
|
+
method = "GET"
|
|
53
|
+
|
|
54
|
+
logger.debug(f"Calling DexScreener API: {method} {url} with params: {params}")
|
|
55
|
+
response = None # Define response outside try block for access in except
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
async with httpx.AsyncClient() as client:
|
|
59
|
+
response = await client.request(
|
|
60
|
+
method, url, params=params, headers=headers
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Attempt to parse JSON response text
|
|
64
|
+
try:
|
|
65
|
+
response_data = response.json()
|
|
66
|
+
except json.JSONDecodeError as json_err:
|
|
67
|
+
logger.error(
|
|
68
|
+
f"Failed to parse JSON response from {url}. Status: {response.status_code}. Response text: {response.text}",
|
|
69
|
+
exc_info=True,
|
|
70
|
+
)
|
|
71
|
+
error_details = {
|
|
72
|
+
"error": "Failed to parse DexScreener API response",
|
|
73
|
+
"error_type": "parsing_error",
|
|
74
|
+
"status_code": response.status_code,
|
|
75
|
+
"details": response.text, # Raw text causing the error
|
|
76
|
+
"original_exception": str(json_err),
|
|
77
|
+
"url": url,
|
|
78
|
+
}
|
|
79
|
+
return None, error_details # Return parsing error
|
|
80
|
+
|
|
81
|
+
# Check HTTP status *after* attempting JSON parse
|
|
82
|
+
if response.is_success: # 2xx
|
|
83
|
+
logger.debug(
|
|
84
|
+
f"DexScreener API success response status: {response.status_code}"
|
|
85
|
+
)
|
|
86
|
+
return response_data, None # Success
|
|
87
|
+
else: # 4xx/5xx
|
|
88
|
+
logger.warning(
|
|
89
|
+
f"DexScreener API returned error status: {response.status_code} - {response.text}"
|
|
90
|
+
)
|
|
91
|
+
error_details = {
|
|
92
|
+
"error": "DexScreener API request failed",
|
|
93
|
+
"error_type": "api_error",
|
|
94
|
+
"status_code": response.status_code,
|
|
95
|
+
"response_body": response_data, # Parsed error body if available
|
|
96
|
+
"url": url,
|
|
97
|
+
}
|
|
98
|
+
return None, error_details # Return API error
|
|
99
|
+
|
|
100
|
+
except httpx.RequestError as req_err:
|
|
101
|
+
logger.error(
|
|
102
|
+
f"Request error connecting to DexScreener API: {req_err}", exc_info=True
|
|
103
|
+
)
|
|
104
|
+
error_details = {
|
|
105
|
+
"error": "Failed to connect to DexScreener API",
|
|
106
|
+
"error_type": "connection_error",
|
|
107
|
+
"details": str(req_err),
|
|
108
|
+
"url": url,
|
|
109
|
+
}
|
|
110
|
+
return None, error_details # Return connection error
|
|
111
|
+
|
|
112
|
+
except Exception as e:
|
|
113
|
+
# Catch any other unexpected errors during the process
|
|
114
|
+
logger.exception(
|
|
115
|
+
f"An unexpected error occurred during DexScreener API GET call: {e}"
|
|
116
|
+
)
|
|
117
|
+
status_code = response.status_code if response else None
|
|
118
|
+
error_details = {
|
|
119
|
+
"error": "An unexpected error occurred during API call",
|
|
120
|
+
"error_type": "unexpected_error",
|
|
121
|
+
"status_code": status_code, # Include if available
|
|
122
|
+
"details": str(e),
|
|
123
|
+
"url": url,
|
|
124
|
+
}
|
|
125
|
+
return None, error_details # Return unexpected error
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from intentkit.skills.dexscreener.base import DexScreenerBaseTool
|
|
7
|
+
from intentkit.skills.dexscreener.model.search_token_response import PairModel
|
|
8
|
+
from intentkit.skills.dexscreener.utils import (
|
|
9
|
+
API_ENDPOINTS,
|
|
10
|
+
RATE_LIMITS,
|
|
11
|
+
create_error_response,
|
|
12
|
+
format_success_response,
|
|
13
|
+
truncate_large_fields,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GetPairInfoInput(BaseModel):
|
|
20
|
+
"""Input schema for the DexScreener get_pair_info tool."""
|
|
21
|
+
|
|
22
|
+
chain_id: str = Field(
|
|
23
|
+
description="The blockchain chain ID (e.g., 'ethereum', 'solana', 'bsc', 'polygon', 'arbitrum', 'base', 'avalanche')"
|
|
24
|
+
)
|
|
25
|
+
pair_address: str = Field(
|
|
26
|
+
description="The trading pair contract address (e.g., '0x1234...abcd' for Ethereum-based chains)"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GetPairInfo(DexScreenerBaseTool):
|
|
31
|
+
"""
|
|
32
|
+
Tool to get detailed information about a specific trading pair on DexScreener.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
name: str = "dexscreener_get_pair_info"
|
|
36
|
+
description: str = (
|
|
37
|
+
"Retrieves detailed information about a specific trading pair using chain ID and pair address. "
|
|
38
|
+
"Returns comprehensive data including current price, volume, liquidity, price changes, "
|
|
39
|
+
"market cap, FDV, transaction counts, and social links. "
|
|
40
|
+
"Use this tool when you have a specific pair address and need detailed trading metrics."
|
|
41
|
+
)
|
|
42
|
+
args_schema: type[BaseModel] = GetPairInfoInput
|
|
43
|
+
|
|
44
|
+
async def _arun(
|
|
45
|
+
self,
|
|
46
|
+
chain_id: str,
|
|
47
|
+
pair_address: str,
|
|
48
|
+
**kwargs: Any,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Implementation to get specific pair information."""
|
|
51
|
+
|
|
52
|
+
# Apply rate limiting
|
|
53
|
+
await self.global_rate_limit_by_skill(
|
|
54
|
+
limit=RATE_LIMITS["pairs"],
|
|
55
|
+
seconds=60,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
logger.info(
|
|
59
|
+
f"Executing DexScreener get_pair_info tool with chain_id: '{chain_id}', "
|
|
60
|
+
f"pair_address: '{pair_address}'"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# Construct API path
|
|
65
|
+
api_path = f"{API_ENDPOINTS['pairs']}/{chain_id}/{pair_address}"
|
|
66
|
+
|
|
67
|
+
data, error_details = await self._get(path=api_path)
|
|
68
|
+
|
|
69
|
+
if error_details:
|
|
70
|
+
return await self._handle_error_response(error_details)
|
|
71
|
+
|
|
72
|
+
if not data:
|
|
73
|
+
logger.error(f"No data returned for pair {pair_address} on {chain_id}")
|
|
74
|
+
return create_error_response(
|
|
75
|
+
error_type="empty_success",
|
|
76
|
+
message="API call returned empty success response.",
|
|
77
|
+
additional_data={
|
|
78
|
+
"chain_id": chain_id,
|
|
79
|
+
"pair_address": pair_address,
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# The API returns a single pair object, not wrapped in a pairs array
|
|
84
|
+
if not isinstance(data, dict):
|
|
85
|
+
return create_error_response(
|
|
86
|
+
error_type="format_error",
|
|
87
|
+
message="Unexpected response format - expected object",
|
|
88
|
+
additional_data={
|
|
89
|
+
"chain_id": chain_id,
|
|
90
|
+
"pair_address": pair_address,
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
# Validate the response using our existing PairModel
|
|
96
|
+
pair_data = PairModel.model_validate(data)
|
|
97
|
+
logger.info(
|
|
98
|
+
f"Successfully retrieved pair info for {pair_address} on {chain_id}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return format_success_response(
|
|
102
|
+
{
|
|
103
|
+
"pair": pair_data.model_dump(),
|
|
104
|
+
"chain_id": chain_id,
|
|
105
|
+
"pair_address": pair_address,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
except Exception as validation_error:
|
|
110
|
+
logger.error(
|
|
111
|
+
f"Failed to validate pair response for {pair_address} on {chain_id}: {validation_error}",
|
|
112
|
+
exc_info=True,
|
|
113
|
+
)
|
|
114
|
+
# Return raw data if validation fails
|
|
115
|
+
return format_success_response(
|
|
116
|
+
{
|
|
117
|
+
"pair": data,
|
|
118
|
+
"chain_id": chain_id,
|
|
119
|
+
"pair_address": pair_address,
|
|
120
|
+
"validation_warning": "Response structure may have changed",
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
except Exception as e:
|
|
125
|
+
return await self._handle_unexpected_runtime_error(
|
|
126
|
+
e, f"{chain_id}/{pair_address}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
async def _handle_error_response(self, error_details: dict) -> str:
|
|
130
|
+
"""Formats error details (from _get) into a JSON string."""
|
|
131
|
+
if error_details.get("error_type") in [
|
|
132
|
+
"connection_error",
|
|
133
|
+
"parsing_error",
|
|
134
|
+
"unexpected_error",
|
|
135
|
+
]:
|
|
136
|
+
logger.error(
|
|
137
|
+
f"DexScreener get_pair_info tool encountered an error: {error_details}"
|
|
138
|
+
)
|
|
139
|
+
else: # api_error
|
|
140
|
+
logger.warning(f"DexScreener API returned an error: {error_details}")
|
|
141
|
+
|
|
142
|
+
# Truncate potentially large fields before returning to user/LLM
|
|
143
|
+
truncated_details = truncate_large_fields(error_details)
|
|
144
|
+
return format_success_response(truncated_details)
|
|
145
|
+
|
|
146
|
+
async def _handle_unexpected_runtime_error(
|
|
147
|
+
self, e: Exception, query_info: str
|
|
148
|
+
) -> str:
|
|
149
|
+
"""Formats unexpected runtime exception details into a JSON string."""
|
|
150
|
+
logger.exception(
|
|
151
|
+
f"An unexpected runtime error occurred in get_pair_info tool _arun method for {query_info}: {e}"
|
|
152
|
+
)
|
|
153
|
+
return create_error_response(
|
|
154
|
+
error_type="runtime_error",
|
|
155
|
+
message="An unexpected internal error occurred processing the pair info request",
|
|
156
|
+
details=str(e),
|
|
157
|
+
additional_data={"query_info": query_info},
|
|
158
|
+
)
|
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
RATE_LIMITS,
|
|
13
|
+
create_error_response,
|
|
14
|
+
create_no_results_response,
|
|
15
|
+
format_success_response,
|
|
16
|
+
get_liquidity_value,
|
|
17
|
+
handle_validation_error,
|
|
18
|
+
truncate_large_fields,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GetTokenPairsInput(BaseModel):
|
|
25
|
+
"""Input schema for the DexScreener get_token_pairs tool."""
|
|
26
|
+
|
|
27
|
+
chain_id: str = Field(
|
|
28
|
+
description="The blockchain chain ID (e.g., 'ethereum', 'solana', 'bsc', 'polygon', 'arbitrum', 'base', 'avalanche')"
|
|
29
|
+
)
|
|
30
|
+
token_address: str = Field(
|
|
31
|
+
description="The token contract address (e.g., '0x1234...abcd' for Ethereum-based chains)"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GetTokenPairs(DexScreenerBaseTool):
|
|
36
|
+
"""
|
|
37
|
+
Tool to get all trading pairs for a specific token on DexScreener.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
name: str = "dexscreener_get_token_pairs"
|
|
41
|
+
description: str = (
|
|
42
|
+
"Finds all trading pairs for a specific token using chain ID and token address. "
|
|
43
|
+
"Returns a list of all pools/pairs where this token is traded, including pair addresses, "
|
|
44
|
+
"DEX information, liquidity, volume, and pricing data for each pair. "
|
|
45
|
+
"Use this tool to analyze all available trading venues and liquidity sources for a specific token."
|
|
46
|
+
)
|
|
47
|
+
args_schema: type[BaseModel] = GetTokenPairsInput
|
|
48
|
+
|
|
49
|
+
async def _arun(
|
|
50
|
+
self,
|
|
51
|
+
chain_id: str,
|
|
52
|
+
token_address: str,
|
|
53
|
+
**kwargs: Any,
|
|
54
|
+
) -> str:
|
|
55
|
+
"""Implementation to get all pairs for a specific token."""
|
|
56
|
+
|
|
57
|
+
# Apply rate limiting
|
|
58
|
+
await self.global_rate_limit_by_skill(
|
|
59
|
+
limit=RATE_LIMITS["token_pairs"],
|
|
60
|
+
seconds=60,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
logger.info(
|
|
64
|
+
f"Executing DexScreener get_token_pairs tool with chain_id: '{chain_id}', "
|
|
65
|
+
f"token_address: '{token_address}'"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Construct API path
|
|
70
|
+
api_path = f"{API_ENDPOINTS['token_pairs']}/{chain_id}/{token_address}"
|
|
71
|
+
|
|
72
|
+
data, error_details = await self._get(path=api_path)
|
|
73
|
+
|
|
74
|
+
if error_details:
|
|
75
|
+
return await self._handle_error_response(error_details)
|
|
76
|
+
|
|
77
|
+
if not data:
|
|
78
|
+
logger.error(
|
|
79
|
+
f"No data returned for token {token_address} on {chain_id}"
|
|
80
|
+
)
|
|
81
|
+
return create_error_response(
|
|
82
|
+
error_type="empty_success",
|
|
83
|
+
message="API call returned empty success response.",
|
|
84
|
+
additional_data={
|
|
85
|
+
"chain_id": chain_id,
|
|
86
|
+
"token_address": token_address,
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
# Validate response using SearchTokenResponseModel since API returns similar structure
|
|
92
|
+
result = SearchTokenResponseModel.model_validate(data)
|
|
93
|
+
except ValidationError as e:
|
|
94
|
+
return handle_validation_error(
|
|
95
|
+
e, f"{chain_id}/{token_address}", len(str(data))
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if not result.pairs:
|
|
99
|
+
return create_no_results_response(
|
|
100
|
+
f"{chain_id}/{token_address}",
|
|
101
|
+
reason="no trading pairs found for this token",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
pairs_list = [p for p in result.pairs if p is not None]
|
|
105
|
+
|
|
106
|
+
if not pairs_list:
|
|
107
|
+
return create_no_results_response(
|
|
108
|
+
f"{chain_id}/{token_address}",
|
|
109
|
+
reason="all pairs were null or invalid",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Sort pairs by liquidity (highest first) for better UX
|
|
113
|
+
try:
|
|
114
|
+
pairs_list.sort(key=get_liquidity_value, reverse=True)
|
|
115
|
+
except Exception as sort_err:
|
|
116
|
+
logger.warning(f"Failed to sort pairs by liquidity: {sort_err}")
|
|
117
|
+
|
|
118
|
+
logger.info(
|
|
119
|
+
f"Found {len(pairs_list)} pairs for token {token_address} on {chain_id}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return format_success_response(
|
|
123
|
+
{
|
|
124
|
+
"pairs": [p.model_dump() for p in pairs_list],
|
|
125
|
+
"chain_id": chain_id,
|
|
126
|
+
"token_address": token_address,
|
|
127
|
+
"total_pairs": len(pairs_list),
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
return await self._handle_unexpected_runtime_error(
|
|
133
|
+
e, f"{chain_id}/{token_address}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def _handle_error_response(self, error_details: dict) -> str:
|
|
137
|
+
"""Formats error details (from _get) into a JSON string."""
|
|
138
|
+
if error_details.get("error_type") in [
|
|
139
|
+
"connection_error",
|
|
140
|
+
"parsing_error",
|
|
141
|
+
"unexpected_error",
|
|
142
|
+
]:
|
|
143
|
+
logger.error(
|
|
144
|
+
f"DexScreener get_token_pairs tool encountered an error: {error_details}"
|
|
145
|
+
)
|
|
146
|
+
else: # api_error
|
|
147
|
+
logger.warning(f"DexScreener API returned an error: {error_details}")
|
|
148
|
+
|
|
149
|
+
# Truncate potentially large fields before returning to user/LLM
|
|
150
|
+
truncated_details = truncate_large_fields(error_details)
|
|
151
|
+
return format_success_response(truncated_details)
|
|
152
|
+
|
|
153
|
+
async def _handle_unexpected_runtime_error(
|
|
154
|
+
self, e: Exception, query_info: str
|
|
155
|
+
) -> str:
|
|
156
|
+
"""Formats unexpected runtime exception details into a JSON string."""
|
|
157
|
+
logger.exception(
|
|
158
|
+
f"An unexpected runtime error occurred in get_token_pairs tool _arun method for {query_info}: {e}"
|
|
159
|
+
)
|
|
160
|
+
return create_error_response(
|
|
161
|
+
error_type="runtime_error",
|
|
162
|
+
message="An unexpected internal error occurred processing the token pairs request",
|
|
163
|
+
details=str(e),
|
|
164
|
+
additional_data={"query_info": query_info},
|
|
165
|
+
)
|