intentkit 0.5.0__py3-none-any.whl → 0.5.2__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 +17 -0
- intentkit/abstracts/__init__.py +0 -0
- intentkit/abstracts/agent.py +60 -0
- intentkit/abstracts/api.py +4 -0
- intentkit/abstracts/engine.py +38 -0
- intentkit/abstracts/exception.py +9 -0
- intentkit/abstracts/graph.py +25 -0
- intentkit/abstracts/skill.py +129 -0
- intentkit/abstracts/twitter.py +54 -0
- intentkit/clients/__init__.py +14 -0
- intentkit/clients/cdp.py +53 -0
- intentkit/clients/twitter.py +445 -0
- intentkit/config/__init__.py +0 -0
- intentkit/config/config.py +164 -0
- intentkit/core/__init__.py +0 -0
- intentkit/core/agent.py +191 -0
- intentkit/core/api.py +40 -0
- intentkit/core/client.py +45 -0
- intentkit/core/credit.py +1767 -0
- intentkit/core/engine.py +1018 -0
- intentkit/core/node.py +223 -0
- intentkit/core/prompt.py +58 -0
- intentkit/core/skill.py +124 -0
- intentkit/models/agent.py +1689 -0
- intentkit/models/agent_data.py +810 -0
- intentkit/models/agent_schema.json +733 -0
- intentkit/models/app_setting.py +156 -0
- intentkit/models/base.py +9 -0
- intentkit/models/chat.py +581 -0
- intentkit/models/conversation.py +286 -0
- intentkit/models/credit.py +1406 -0
- intentkit/models/db.py +120 -0
- intentkit/models/db_mig.py +102 -0
- intentkit/models/generator.py +347 -0
- intentkit/models/llm.py +746 -0
- intentkit/models/redis.py +132 -0
- intentkit/models/skill.py +466 -0
- intentkit/models/user.py +243 -0
- intentkit/skills/__init__.py +12 -0
- intentkit/skills/acolyt/__init__.py +83 -0
- intentkit/skills/acolyt/acolyt.jpg +0 -0
- intentkit/skills/acolyt/ask.py +128 -0
- intentkit/skills/acolyt/base.py +28 -0
- intentkit/skills/acolyt/schema.json +89 -0
- intentkit/skills/aixbt/README.md +71 -0
- intentkit/skills/aixbt/__init__.py +73 -0
- intentkit/skills/aixbt/aixbt.jpg +0 -0
- intentkit/skills/aixbt/base.py +21 -0
- intentkit/skills/aixbt/projects.py +153 -0
- intentkit/skills/aixbt/schema.json +99 -0
- intentkit/skills/allora/__init__.py +83 -0
- intentkit/skills/allora/allora.jpeg +0 -0
- intentkit/skills/allora/base.py +28 -0
- intentkit/skills/allora/price.py +130 -0
- intentkit/skills/allora/schema.json +89 -0
- intentkit/skills/base.py +174 -0
- intentkit/skills/carv/README.md +95 -0
- intentkit/skills/carv/__init__.py +121 -0
- intentkit/skills/carv/base.py +183 -0
- intentkit/skills/carv/carv.webp +0 -0
- intentkit/skills/carv/fetch_news.py +92 -0
- intentkit/skills/carv/onchain_query.py +164 -0
- intentkit/skills/carv/schema.json +137 -0
- intentkit/skills/carv/token_info_and_price.py +110 -0
- intentkit/skills/cdp/__init__.py +137 -0
- intentkit/skills/cdp/base.py +21 -0
- intentkit/skills/cdp/cdp.png +0 -0
- intentkit/skills/cdp/get_balance.py +81 -0
- intentkit/skills/cdp/schema.json +473 -0
- intentkit/skills/chainlist/README.md +38 -0
- intentkit/skills/chainlist/__init__.py +54 -0
- intentkit/skills/chainlist/base.py +21 -0
- intentkit/skills/chainlist/chain_lookup.py +208 -0
- intentkit/skills/chainlist/chainlist.png +0 -0
- intentkit/skills/chainlist/schema.json +47 -0
- intentkit/skills/common/__init__.py +82 -0
- intentkit/skills/common/base.py +21 -0
- intentkit/skills/common/common.jpg +0 -0
- intentkit/skills/common/current_time.py +84 -0
- intentkit/skills/common/schema.json +57 -0
- intentkit/skills/cookiefun/README.md +121 -0
- intentkit/skills/cookiefun/__init__.py +78 -0
- intentkit/skills/cookiefun/base.py +41 -0
- intentkit/skills/cookiefun/constants.py +18 -0
- intentkit/skills/cookiefun/cookiefun.png +0 -0
- intentkit/skills/cookiefun/get_account_details.py +171 -0
- intentkit/skills/cookiefun/get_account_feed.py +282 -0
- intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
- intentkit/skills/cookiefun/get_sectors.py +128 -0
- intentkit/skills/cookiefun/schema.json +155 -0
- intentkit/skills/cookiefun/search_accounts.py +225 -0
- intentkit/skills/cryptocompare/__init__.py +130 -0
- intentkit/skills/cryptocompare/api.py +159 -0
- intentkit/skills/cryptocompare/base.py +303 -0
- intentkit/skills/cryptocompare/cryptocompare.png +0 -0
- intentkit/skills/cryptocompare/fetch_news.py +96 -0
- intentkit/skills/cryptocompare/fetch_price.py +99 -0
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
- intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
- intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
- intentkit/skills/cryptocompare/schema.json +168 -0
- intentkit/skills/cryptopanic/__init__.py +108 -0
- intentkit/skills/cryptopanic/base.py +51 -0
- intentkit/skills/cryptopanic/cryptopanic.png +0 -0
- intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
- intentkit/skills/cryptopanic/schema.json +103 -0
- intentkit/skills/dapplooker/README.md +92 -0
- intentkit/skills/dapplooker/__init__.py +83 -0
- intentkit/skills/dapplooker/base.py +26 -0
- intentkit/skills/dapplooker/dapplooker.jpg +0 -0
- intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
- intentkit/skills/dapplooker/schema.json +91 -0
- intentkit/skills/defillama/__init__.py +323 -0
- intentkit/skills/defillama/api.py +315 -0
- intentkit/skills/defillama/base.py +135 -0
- intentkit/skills/defillama/coins/__init__.py +0 -0
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
- intentkit/skills/defillama/coins/fetch_block.py +98 -0
- intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
- intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
- intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
- intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
- intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
- intentkit/skills/defillama/config/__init__.py +0 -0
- intentkit/skills/defillama/config/chains.py +433 -0
- intentkit/skills/defillama/defillama.jpeg +0 -0
- intentkit/skills/defillama/fees/__init__.py +0 -0
- intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
- intentkit/skills/defillama/schema.json +383 -0
- intentkit/skills/defillama/stablecoins/__init__.py +0 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
- intentkit/skills/defillama/tests/__init__.py +0 -0
- intentkit/skills/defillama/tests/api_integration.test.py +192 -0
- intentkit/skills/defillama/tests/api_unit.test.py +583 -0
- intentkit/skills/defillama/tvl/__init__.py +0 -0
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
- intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
- intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
- intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
- intentkit/skills/defillama/volumes/__init__.py +0 -0
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
- intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
- intentkit/skills/defillama/yields/__init__.py +0 -0
- intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
- intentkit/skills/defillama/yields/fetch_pools.py +126 -0
- intentkit/skills/dexscreener/__init__.py +93 -0
- intentkit/skills/dexscreener/base.py +133 -0
- intentkit/skills/dexscreener/dexscreener.png +0 -0
- intentkit/skills/dexscreener/model/__init__.py +0 -0
- intentkit/skills/dexscreener/model/search_token_response.py +82 -0
- intentkit/skills/dexscreener/schema.json +48 -0
- intentkit/skills/dexscreener/search_token.py +321 -0
- intentkit/skills/dune_analytics/__init__.py +103 -0
- intentkit/skills/dune_analytics/base.py +46 -0
- intentkit/skills/dune_analytics/dune.png +0 -0
- intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
- intentkit/skills/dune_analytics/schema.json +99 -0
- intentkit/skills/elfa/README.md +100 -0
- intentkit/skills/elfa/__init__.py +123 -0
- intentkit/skills/elfa/base.py +28 -0
- intentkit/skills/elfa/elfa.jpg +0 -0
- intentkit/skills/elfa/mention.py +504 -0
- intentkit/skills/elfa/schema.json +153 -0
- intentkit/skills/elfa/stats.py +118 -0
- intentkit/skills/elfa/tokens.py +126 -0
- intentkit/skills/enso/README.md +75 -0
- intentkit/skills/enso/__init__.py +114 -0
- intentkit/skills/enso/abi/__init__.py +0 -0
- intentkit/skills/enso/abi/approval.py +279 -0
- intentkit/skills/enso/abi/erc20.py +14 -0
- intentkit/skills/enso/abi/route.py +129 -0
- intentkit/skills/enso/base.py +44 -0
- intentkit/skills/enso/best_yield.py +286 -0
- intentkit/skills/enso/enso.jpg +0 -0
- intentkit/skills/enso/networks.py +105 -0
- intentkit/skills/enso/prices.py +93 -0
- intentkit/skills/enso/route.py +300 -0
- intentkit/skills/enso/schema.json +212 -0
- intentkit/skills/enso/tokens.py +223 -0
- intentkit/skills/enso/wallet.py +381 -0
- intentkit/skills/github/README.md +63 -0
- intentkit/skills/github/__init__.py +54 -0
- intentkit/skills/github/base.py +21 -0
- intentkit/skills/github/github.jpg +0 -0
- intentkit/skills/github/github_search.py +183 -0
- intentkit/skills/github/schema.json +59 -0
- intentkit/skills/heurist/__init__.py +143 -0
- intentkit/skills/heurist/base.py +26 -0
- intentkit/skills/heurist/heurist.png +0 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
- intentkit/skills/heurist/image_generation_braindance.py +162 -0
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
- intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
- intentkit/skills/heurist/image_generation_sdxl.py +161 -0
- intentkit/skills/heurist/schema.json +196 -0
- intentkit/skills/lifi/README.md +294 -0
- intentkit/skills/lifi/__init__.py +141 -0
- intentkit/skills/lifi/base.py +21 -0
- intentkit/skills/lifi/lifi.png +0 -0
- intentkit/skills/lifi/schema.json +89 -0
- intentkit/skills/lifi/token_execute.py +472 -0
- intentkit/skills/lifi/token_quote.py +190 -0
- intentkit/skills/lifi/utils.py +656 -0
- intentkit/skills/moralis/README.md +490 -0
- intentkit/skills/moralis/__init__.py +110 -0
- intentkit/skills/moralis/api.py +281 -0
- intentkit/skills/moralis/base.py +55 -0
- intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
- intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
- intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
- intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
- intentkit/skills/moralis/moralis.png +0 -0
- intentkit/skills/moralis/schema.json +156 -0
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +511 -0
- intentkit/skills/nation/__init__.py +62 -0
- intentkit/skills/nation/base.py +31 -0
- intentkit/skills/nation/nation.png +0 -0
- intentkit/skills/nation/nft_check.py +106 -0
- intentkit/skills/nation/schema.json +58 -0
- intentkit/skills/openai/__init__.py +107 -0
- intentkit/skills/openai/base.py +32 -0
- intentkit/skills/openai/dalle_image_generation.py +128 -0
- intentkit/skills/openai/gpt_image_generation.py +152 -0
- intentkit/skills/openai/gpt_image_to_image.py +186 -0
- intentkit/skills/openai/image_to_text.py +126 -0
- intentkit/skills/openai/openai.png +0 -0
- intentkit/skills/openai/schema.json +139 -0
- intentkit/skills/portfolio/README.md +55 -0
- intentkit/skills/portfolio/__init__.py +151 -0
- intentkit/skills/portfolio/base.py +107 -0
- intentkit/skills/portfolio/constants.py +9 -0
- intentkit/skills/portfolio/moralis.png +0 -0
- intentkit/skills/portfolio/schema.json +237 -0
- intentkit/skills/portfolio/token_balances.py +155 -0
- intentkit/skills/portfolio/wallet_approvals.py +102 -0
- intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
- intentkit/skills/portfolio/wallet_history.py +155 -0
- intentkit/skills/portfolio/wallet_net_worth.py +112 -0
- intentkit/skills/portfolio/wallet_nfts.py +139 -0
- intentkit/skills/portfolio/wallet_profitability.py +101 -0
- intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
- intentkit/skills/portfolio/wallet_stats.py +79 -0
- intentkit/skills/portfolio/wallet_swaps.py +147 -0
- intentkit/skills/skills.toml +103 -0
- intentkit/skills/slack/__init__.py +98 -0
- intentkit/skills/slack/base.py +55 -0
- intentkit/skills/slack/get_channel.py +109 -0
- intentkit/skills/slack/get_message.py +136 -0
- intentkit/skills/slack/schedule_message.py +92 -0
- intentkit/skills/slack/schema.json +135 -0
- intentkit/skills/slack/send_message.py +81 -0
- intentkit/skills/slack/slack.jpg +0 -0
- intentkit/skills/system/__init__.py +90 -0
- intentkit/skills/system/base.py +22 -0
- intentkit/skills/system/read_agent_api_key.py +87 -0
- intentkit/skills/system/regenerate_agent_api_key.py +77 -0
- intentkit/skills/system/schema.json +53 -0
- intentkit/skills/system/system.svg +76 -0
- intentkit/skills/tavily/README.md +86 -0
- intentkit/skills/tavily/__init__.py +91 -0
- intentkit/skills/tavily/base.py +27 -0
- intentkit/skills/tavily/schema.json +119 -0
- intentkit/skills/tavily/tavily.jpg +0 -0
- intentkit/skills/tavily/tavily_extract.py +147 -0
- intentkit/skills/tavily/tavily_search.py +139 -0
- intentkit/skills/token/README.md +89 -0
- intentkit/skills/token/__init__.py +107 -0
- intentkit/skills/token/base.py +154 -0
- intentkit/skills/token/constants.py +9 -0
- intentkit/skills/token/erc20_transfers.py +145 -0
- intentkit/skills/token/moralis.png +0 -0
- intentkit/skills/token/schema.json +141 -0
- intentkit/skills/token/token_analytics.py +81 -0
- intentkit/skills/token/token_price.py +132 -0
- intentkit/skills/token/token_search.py +121 -0
- intentkit/skills/twitter/__init__.py +146 -0
- intentkit/skills/twitter/base.py +68 -0
- intentkit/skills/twitter/follow_user.py +69 -0
- intentkit/skills/twitter/get_mentions.py +124 -0
- intentkit/skills/twitter/get_timeline.py +111 -0
- intentkit/skills/twitter/get_user_by_username.py +84 -0
- intentkit/skills/twitter/get_user_tweets.py +123 -0
- intentkit/skills/twitter/like_tweet.py +65 -0
- intentkit/skills/twitter/post_tweet.py +90 -0
- intentkit/skills/twitter/reply_tweet.py +98 -0
- intentkit/skills/twitter/retweet.py +76 -0
- intentkit/skills/twitter/schema.json +258 -0
- intentkit/skills/twitter/search_tweets.py +115 -0
- intentkit/skills/twitter/twitter.png +0 -0
- intentkit/skills/unrealspeech/__init__.py +55 -0
- intentkit/skills/unrealspeech/base.py +21 -0
- intentkit/skills/unrealspeech/schema.json +100 -0
- intentkit/skills/unrealspeech/text_to_speech.py +177 -0
- intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
- intentkit/skills/venice_audio/__init__.py +106 -0
- intentkit/skills/venice_audio/base.py +119 -0
- intentkit/skills/venice_audio/input.py +41 -0
- intentkit/skills/venice_audio/schema.json +152 -0
- intentkit/skills/venice_audio/venice_audio.py +240 -0
- intentkit/skills/venice_audio/venice_logo.jpg +0 -0
- intentkit/skills/venice_image/README.md +119 -0
- intentkit/skills/venice_image/__init__.py +154 -0
- intentkit/skills/venice_image/api.py +138 -0
- intentkit/skills/venice_image/base.py +188 -0
- intentkit/skills/venice_image/config.py +35 -0
- intentkit/skills/venice_image/image_enhance/README.md +119 -0
- intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
- intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
- intentkit/skills/venice_image/image_generation/README.md +144 -0
- intentkit/skills/venice_image/image_generation/__init__.py +0 -0
- intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
- intentkit/skills/venice_image/image_upscale/README.md +111 -0
- intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
- intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
- intentkit/skills/venice_image/image_vision/README.md +112 -0
- intentkit/skills/venice_image/image_vision/__init__.py +0 -0
- intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
- intentkit/skills/venice_image/schema.json +267 -0
- intentkit/skills/venice_image/utils.py +78 -0
- intentkit/skills/venice_image/venice_image.jpg +0 -0
- intentkit/skills/web_scraper/README.md +82 -0
- intentkit/skills/web_scraper/__init__.py +92 -0
- intentkit/skills/web_scraper/base.py +21 -0
- intentkit/skills/web_scraper/langchain.png +0 -0
- intentkit/skills/web_scraper/schema.json +115 -0
- intentkit/skills/web_scraper/scrape_and_index.py +327 -0
- intentkit/utils/__init__.py +1 -0
- intentkit/utils/chain.py +436 -0
- intentkit/utils/error.py +134 -0
- intentkit/utils/logging.py +70 -0
- intentkit/utils/middleware.py +61 -0
- intentkit/utils/random.py +16 -0
- intentkit/utils/s3.py +267 -0
- intentkit/utils/slack_alert.py +79 -0
- intentkit/utils/tx.py +37 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
- intentkit-0.5.2.dist-info/RECORD +365 -0
- intentkit-0.5.0.dist-info/RECORD +0 -4
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
intentkit/utils/chain.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import IntEnum, StrEnum
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Chain(StrEnum):
|
|
8
|
+
"""
|
|
9
|
+
Enum of supported blockchain chains, using QuickNode's naming conventions.
|
|
10
|
+
|
|
11
|
+
This list is based on common chain names used by QuickNode, but it's essential
|
|
12
|
+
to consult the official QuickNode documentation for the most accurate and
|
|
13
|
+
up-to-date list of supported chains and their exact names. Chain names can
|
|
14
|
+
sometimes be slightly different from what you might expect.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# EVM Chains
|
|
18
|
+
Ethereum = "eth" # Or "ethereum"
|
|
19
|
+
Avalanche = "avax" # Or "avalanche"
|
|
20
|
+
Binance = "bsc" # BNB Smart Chain
|
|
21
|
+
Polygon = "matic" # Or "polygon"
|
|
22
|
+
Gnosis = "gnosis" # Or "xdai"
|
|
23
|
+
Celo = "celo"
|
|
24
|
+
Fantom = "fantom"
|
|
25
|
+
Moonbeam = "moonbeam"
|
|
26
|
+
Aurora = "aurora"
|
|
27
|
+
Arbitrum = "arbitrum"
|
|
28
|
+
Optimism = "optimism"
|
|
29
|
+
Linea = "linea"
|
|
30
|
+
ZkSync = "zksync"
|
|
31
|
+
|
|
32
|
+
# Base
|
|
33
|
+
Base = "base"
|
|
34
|
+
|
|
35
|
+
# Cosmos Ecosystem
|
|
36
|
+
CosmosHub = "cosmos" # Or "cosmos-hub"
|
|
37
|
+
Osmosis = "osmosis"
|
|
38
|
+
Juno = "juno"
|
|
39
|
+
Evmos = "evmos"
|
|
40
|
+
Kava = "kava"
|
|
41
|
+
Persistence = "persistence"
|
|
42
|
+
Secret = "secret"
|
|
43
|
+
Stargaze = "stargaze"
|
|
44
|
+
Terra = "terra" # Or "terra-classic"
|
|
45
|
+
Axelar = "axelar"
|
|
46
|
+
|
|
47
|
+
# Solana
|
|
48
|
+
Solana = "sol" # Or "solana"
|
|
49
|
+
|
|
50
|
+
# Other Chains
|
|
51
|
+
Sonic = "sonic"
|
|
52
|
+
Bera = "bera"
|
|
53
|
+
Near = "near"
|
|
54
|
+
Frontera = "frontera"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Network(StrEnum):
|
|
58
|
+
"""
|
|
59
|
+
Enum of well-known blockchain network names, based on QuickNode API.
|
|
60
|
+
|
|
61
|
+
This list is not exhaustive and might not be completely up-to-date.
|
|
62
|
+
Always consult the official QuickNode documentation for the most accurate
|
|
63
|
+
and current list of supported networks. Network names can sometimes
|
|
64
|
+
be slightly different from what you might expect.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# Ethereum Mainnet and Testnets
|
|
68
|
+
EthereumMainnet = "ethereum-mainnet"
|
|
69
|
+
EthereumGoerli = "ethereum-goerli" # Goerli Testnet (deprecated, Sepolia preferred)
|
|
70
|
+
EthereumSepolia = "ethereum-sepolia"
|
|
71
|
+
|
|
72
|
+
# Layer 2s on Ethereum
|
|
73
|
+
ArbitrumMainnet = "arbitrum-mainnet"
|
|
74
|
+
OptimismMainnet = "optimism-mainnet" # Or just "optimism"
|
|
75
|
+
LineaMainnet = "linea-mainnet"
|
|
76
|
+
ZkSyncMainnet = "zksync-mainnet" # zkSync Era
|
|
77
|
+
|
|
78
|
+
# Other EVM Chains
|
|
79
|
+
AvalancheMainnet = "avalanche-mainnet"
|
|
80
|
+
BinanceMainnet = "bsc" # BNB Smart Chain (BSC)
|
|
81
|
+
PolygonMainnet = "matic" # Or "polygon-mainnet"
|
|
82
|
+
GnosisMainnet = "xdai" # Or "gnosis"
|
|
83
|
+
CeloMainnet = "celo-mainnet"
|
|
84
|
+
FantomMainnet = "fantom-mainnet"
|
|
85
|
+
MoonbeamMainnet = "moonbeam-mainnet"
|
|
86
|
+
AuroraMainnet = "aurora-mainnet"
|
|
87
|
+
|
|
88
|
+
# Base
|
|
89
|
+
BaseMainnet = "base-mainnet"
|
|
90
|
+
BaseSepolia = "base-sepolia"
|
|
91
|
+
|
|
92
|
+
# Cosmos Ecosystem (These can be tricky and may need updates)
|
|
93
|
+
CosmosHubMainnet = "cosmos-hub-mainnet" # Or just "cosmos"
|
|
94
|
+
OsmosisMainnet = "osmosis-mainnet" # Or just "osmosis"
|
|
95
|
+
JunoMainnet = "juno-mainnet" # Or just "juno"
|
|
96
|
+
|
|
97
|
+
# Solana (Note: Solana uses cluster names, not typical network names)
|
|
98
|
+
SolanaMainnet = "solana-mainnet" # Or "solana"
|
|
99
|
+
|
|
100
|
+
# Other Chains
|
|
101
|
+
SonicMainnet = "sonic-mainnet"
|
|
102
|
+
BeraMainnet = "bera-mainnet"
|
|
103
|
+
NearMainnet = "near-mainnet" # Or just "near"
|
|
104
|
+
KavaMainnet = "kava-mainnet" # Or just "kava"
|
|
105
|
+
EvmosMainnet = "evmos-mainnet" # Or just "evmos"
|
|
106
|
+
PersistenceMainnet = "persistence-mainnet" # Or just "persistence"
|
|
107
|
+
SecretMainnet = "secret-mainnet" # Or just "secret"
|
|
108
|
+
StargazeMainnet = "stargaze-mainnet" # Or just "stargaze"
|
|
109
|
+
TerraMainnet = "terra-mainnet" # Or "terra-classic"
|
|
110
|
+
AxelarMainnet = "axelar-mainnet" # Or just "axelar"
|
|
111
|
+
FronteraMainnet = "frontera-mainnet"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class NetworkId(IntEnum):
|
|
115
|
+
"""
|
|
116
|
+
Enum of well-known blockchain network IDs.
|
|
117
|
+
|
|
118
|
+
This list is not exhaustive and might not be completely up-to-date.
|
|
119
|
+
Always consult the official documentation for the specific blockchain
|
|
120
|
+
you are working with for the most accurate and current chain ID.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
# Ethereum Mainnet and Testnets
|
|
124
|
+
EthereumMainnet = 1
|
|
125
|
+
EthereumGoerli = 5 # Goerli Testnet (deprecated, Sepolia is preferred)
|
|
126
|
+
EthereumSepolia = 11155111
|
|
127
|
+
|
|
128
|
+
# Layer 2s on Ethereum
|
|
129
|
+
ArbitrumMainnet = 42161
|
|
130
|
+
OptimismMainnet = 10
|
|
131
|
+
LineaMainnet = 59144
|
|
132
|
+
ZkSyncMainnet = 324 # zkSync Era
|
|
133
|
+
|
|
134
|
+
# Other EVM Chains
|
|
135
|
+
AvalancheMainnet = 43114
|
|
136
|
+
BinanceMainnet = 56 # BNB Smart Chain (BSC)
|
|
137
|
+
PolygonMainnet = 137
|
|
138
|
+
GnosisMainnet = 100 # xDai Chain
|
|
139
|
+
CeloMainnet = 42220
|
|
140
|
+
FantomMainnet = 250
|
|
141
|
+
MoonbeamMainnet = 1284
|
|
142
|
+
AuroraMainnet = 1313161554
|
|
143
|
+
|
|
144
|
+
# Base
|
|
145
|
+
BaseMainnet = 8453
|
|
146
|
+
BaseSepolia = 84532
|
|
147
|
+
|
|
148
|
+
# Other Chains
|
|
149
|
+
SonicMainnet = 146
|
|
150
|
+
BeraMainnet = 80094
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# Mapping of Network enum members to their corresponding NetworkId enum members.
|
|
154
|
+
# This dictionary facilitates efficient lookup of network IDs given a network name.
|
|
155
|
+
# Note: SolanaMainnet is intentionally excluded as it does not have a numeric chain ID.
|
|
156
|
+
# Always refer to the official documentation for the most up-to-date mappings.
|
|
157
|
+
network_to_id: dict[Network, NetworkId] = {
|
|
158
|
+
Network.ArbitrumMainnet: NetworkId.ArbitrumMainnet,
|
|
159
|
+
Network.AvalancheMainnet: NetworkId.AvalancheMainnet,
|
|
160
|
+
Network.BaseMainnet: NetworkId.BaseMainnet,
|
|
161
|
+
Network.BaseSepolia: NetworkId.BaseSepolia,
|
|
162
|
+
Network.BeraMainnet: NetworkId.BeraMainnet,
|
|
163
|
+
Network.BinanceMainnet: NetworkId.BinanceMainnet,
|
|
164
|
+
Network.EthereumMainnet: NetworkId.EthereumMainnet,
|
|
165
|
+
Network.EthereumSepolia: NetworkId.EthereumSepolia,
|
|
166
|
+
Network.GnosisMainnet: NetworkId.GnosisMainnet,
|
|
167
|
+
Network.LineaMainnet: NetworkId.LineaMainnet,
|
|
168
|
+
Network.OptimismMainnet: NetworkId.OptimismMainnet,
|
|
169
|
+
Network.PolygonMainnet: NetworkId.PolygonMainnet,
|
|
170
|
+
Network.SonicMainnet: NetworkId.SonicMainnet,
|
|
171
|
+
Network.ZkSyncMainnet: NetworkId.ZkSyncMainnet,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Mapping of NetworkId enum members (chain IDs) to their corresponding
|
|
175
|
+
# Network enum members (network names). This dictionary allows for reverse
|
|
176
|
+
# lookup, enabling retrieval of the network name given a chain ID.
|
|
177
|
+
# Note: Solana is not included here as it does not use a standard numeric
|
|
178
|
+
# chain ID. Always consult official documentation for the most
|
|
179
|
+
# up-to-date mappings.
|
|
180
|
+
id_to_network: dict[NetworkId, Network] = {
|
|
181
|
+
NetworkId.ArbitrumMainnet: Network.ArbitrumMainnet,
|
|
182
|
+
NetworkId.AvalancheMainnet: Network.AvalancheMainnet,
|
|
183
|
+
NetworkId.BaseMainnet: Network.BaseMainnet,
|
|
184
|
+
NetworkId.BaseSepolia: Network.BaseSepolia,
|
|
185
|
+
NetworkId.BeraMainnet: Network.BeraMainnet,
|
|
186
|
+
NetworkId.BinanceMainnet: Network.BinanceMainnet,
|
|
187
|
+
NetworkId.EthereumMainnet: Network.EthereumMainnet,
|
|
188
|
+
NetworkId.EthereumSepolia: Network.EthereumSepolia,
|
|
189
|
+
NetworkId.GnosisMainnet: Network.GnosisMainnet,
|
|
190
|
+
NetworkId.LineaMainnet: Network.LineaMainnet,
|
|
191
|
+
NetworkId.OptimismMainnet: Network.OptimismMainnet,
|
|
192
|
+
NetworkId.PolygonMainnet: Network.PolygonMainnet,
|
|
193
|
+
NetworkId.SonicMainnet: Network.SonicMainnet,
|
|
194
|
+
NetworkId.ZkSyncMainnet: Network.ZkSyncMainnet,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ChainConfig:
|
|
199
|
+
"""
|
|
200
|
+
Configuration class for a specific blockchain chain.
|
|
201
|
+
|
|
202
|
+
This class encapsulates all the necessary information to interact with a
|
|
203
|
+
particular blockchain, including the chain type, network, RPC URLs, and ENS URL.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
def __init__(
|
|
207
|
+
self,
|
|
208
|
+
chain: Chain,
|
|
209
|
+
network: Network,
|
|
210
|
+
rpc_url: str,
|
|
211
|
+
ens_url: str,
|
|
212
|
+
wss_url: str,
|
|
213
|
+
):
|
|
214
|
+
"""
|
|
215
|
+
Initializes a ChainConfig object.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
chain: The Chain enum member representing the blockchain type (e.g., Ethereum, Solana).
|
|
219
|
+
network: The Network enum member representing the specific network (e.g., EthereumMainnet).
|
|
220
|
+
rpc_url: The URL for the RPC endpoint of the blockchain.
|
|
221
|
+
ens_url: The URL for the ENS (Ethereum Name Service) endpoint (can be None if not applicable).
|
|
222
|
+
wss_url: The URL for the WebSocket endpoint of the blockchain (can be None if not applicable).
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
self._chain = chain
|
|
226
|
+
self._network = network
|
|
227
|
+
self._rpc_url = rpc_url
|
|
228
|
+
self._ens_url = ens_url
|
|
229
|
+
self._wss_url = wss_url
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def chain(self) -> Chain:
|
|
233
|
+
"""
|
|
234
|
+
Returns the Chain enum member.
|
|
235
|
+
"""
|
|
236
|
+
return self._chain
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def network(self) -> Network:
|
|
240
|
+
"""
|
|
241
|
+
Returns the Network enum member.
|
|
242
|
+
"""
|
|
243
|
+
return self._network
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def network_id(self) -> int | None:
|
|
247
|
+
"""
|
|
248
|
+
Returns the network ID (chain ID) for the configured network, or None if not applicable.
|
|
249
|
+
Uses the global network_to_id mapping to retrieve the ID.
|
|
250
|
+
"""
|
|
251
|
+
return network_to_id.get(self._network)
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def rpc_url(self) -> str:
|
|
255
|
+
"""
|
|
256
|
+
Returns the RPC URL.
|
|
257
|
+
"""
|
|
258
|
+
return self._rpc_url
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def ens_url(self) -> str:
|
|
262
|
+
"""
|
|
263
|
+
Returns the ENS URL, or None if not applicable.
|
|
264
|
+
"""
|
|
265
|
+
return self._ens_url
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def wss_url(self) -> str:
|
|
269
|
+
"""
|
|
270
|
+
Returns the WebSocket URL, or None if not applicable.
|
|
271
|
+
"""
|
|
272
|
+
return self._wss_url
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class ChainProvider(ABC):
|
|
276
|
+
"""
|
|
277
|
+
Abstract base class for providing blockchain chain configurations.
|
|
278
|
+
|
|
279
|
+
This class defines the interface for classes responsible for managing and
|
|
280
|
+
providing access to `ChainConfig` objects. Subclasses *must* implement the
|
|
281
|
+
`init_chain_configs` method to populate the available chain configurations.
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
def __init__(self):
|
|
285
|
+
"""
|
|
286
|
+
Initializes the ChainProvider.
|
|
287
|
+
|
|
288
|
+
Sets up an empty dictionary `chain_configs` to store the configurations.
|
|
289
|
+
"""
|
|
290
|
+
self.chain_configs: dict[Network, ChainConfig] = {}
|
|
291
|
+
|
|
292
|
+
def get_chain_config(self, network: Network) -> ChainConfig:
|
|
293
|
+
"""
|
|
294
|
+
Retrieves the chain configuration for a specific network.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
network: The `Network` enum member representing the desired network.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
The `ChainConfig` object associated with the given network.
|
|
301
|
+
|
|
302
|
+
Raises:
|
|
303
|
+
Exception: If no chain configuration is found for the specified network.
|
|
304
|
+
"""
|
|
305
|
+
chain_config = self.chain_configs.get(network)
|
|
306
|
+
if not chain_config:
|
|
307
|
+
raise Exception(f"chain config for network {network} not found")
|
|
308
|
+
return chain_config
|
|
309
|
+
|
|
310
|
+
def get_chain_config_by_id(self, network_id: NetworkId) -> ChainConfig:
|
|
311
|
+
"""
|
|
312
|
+
Retrieves the chain configuration by network ID.
|
|
313
|
+
|
|
314
|
+
This method first looks up the `Network` enum member associated with the
|
|
315
|
+
provided `NetworkId` and then uses `get_chain_config` to retrieve the
|
|
316
|
+
configuration.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
network_id: The `NetworkId` enum member representing the desired network ID.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
The `ChainConfig` object associated with the network ID.
|
|
323
|
+
|
|
324
|
+
Raises:
|
|
325
|
+
Exception: If no network is found for the given ID or if the
|
|
326
|
+
chain configuration is not found for the resolved network.
|
|
327
|
+
"""
|
|
328
|
+
network = id_to_network.get(network_id)
|
|
329
|
+
if not network:
|
|
330
|
+
raise Exception(f"network with id {network_id} not found")
|
|
331
|
+
return self.get_chain_config(network)
|
|
332
|
+
|
|
333
|
+
@abstractmethod
|
|
334
|
+
def init_chain_configs(self, api_key: str) -> dict[Network, ChainConfig]:
|
|
335
|
+
"""
|
|
336
|
+
Initializes the chain configurations.
|
|
337
|
+
|
|
338
|
+
This *abstract* method *must* be implemented by subclasses. It is
|
|
339
|
+
responsible for populating the `chain_configs` dictionary with
|
|
340
|
+
`ChainConfig` objects, typically using the provided `api_key` to fetch
|
|
341
|
+
or generate the necessary configuration data.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
api_key: The API key used for initializing chain configurations.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
A dictionary mapping `Network` enum members to `ChainConfig` objects.
|
|
348
|
+
"""
|
|
349
|
+
raise NotImplementedError
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class QuicknodeChainProvider(ChainProvider):
|
|
353
|
+
"""
|
|
354
|
+
A concrete implementation of `ChainProvider` for QuickNode.
|
|
355
|
+
|
|
356
|
+
This class retrieves chain configuration data from the QuickNode API and
|
|
357
|
+
populates the `chain_configs` dictionary.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
def __init__(self, api_key):
|
|
361
|
+
"""
|
|
362
|
+
Initializes the QuicknodeChainProvider.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
api_key: Your QuickNode API key.
|
|
366
|
+
"""
|
|
367
|
+
super().__init__()
|
|
368
|
+
self.api_key = api_key
|
|
369
|
+
|
|
370
|
+
def init_chain_configs(
|
|
371
|
+
self, limit: int = 100, offset: int = 0
|
|
372
|
+
) -> dict[Network, ChainConfig]:
|
|
373
|
+
"""
|
|
374
|
+
Initializes chain configurations by fetching data from the QuickNode API.
|
|
375
|
+
|
|
376
|
+
This method retrieves a list of QuickNode endpoints using the provided
|
|
377
|
+
API key and populates the `chain_configs` dictionary with `ChainConfig`
|
|
378
|
+
objects.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
limit: The maximum number of endpoints to retrieve (default: 100).
|
|
382
|
+
offset: The number of endpoints to skip (default: 0).
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
A dictionary mapping `Network` enum members to `ChainConfig` objects.
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
Exception: If an error occurs during the API request or processing
|
|
389
|
+
the response. More specific exception types are used
|
|
390
|
+
for HTTP errors and request errors.
|
|
391
|
+
"""
|
|
392
|
+
url = "https://api.quicknode.com/v0/endpoints"
|
|
393
|
+
headers = {
|
|
394
|
+
"Accept": "application/json",
|
|
395
|
+
"x-api-key": self.api_key,
|
|
396
|
+
}
|
|
397
|
+
params = {
|
|
398
|
+
"limit": limit,
|
|
399
|
+
"offset": offset,
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
with httpx.Client(timeout=30) as client: # Set a timeout for the request
|
|
403
|
+
try:
|
|
404
|
+
response = client.get(url, timeout=30, headers=headers, params=params)
|
|
405
|
+
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
|
406
|
+
json_dict = response.json()
|
|
407
|
+
|
|
408
|
+
for item in json_dict["data"]:
|
|
409
|
+
# Assuming 'item' contains 'chain', 'network', 'http_url', 'wss_url'
|
|
410
|
+
# and that these values can be used to construct the ChainConfig object
|
|
411
|
+
chain = Chain(item["chain"])
|
|
412
|
+
network = Network(item["network"])
|
|
413
|
+
|
|
414
|
+
self.chain_configs[item["network"]] = ChainConfig(
|
|
415
|
+
chain,
|
|
416
|
+
network,
|
|
417
|
+
item["http_url"],
|
|
418
|
+
item[
|
|
419
|
+
"http_url"
|
|
420
|
+
], # ens_url is the same as http_url in this case.
|
|
421
|
+
item["wss_url"],
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
except httpx.HTTPStatusError as http_err:
|
|
425
|
+
raise (f"Quicknode API HTTP Error: {http_err}")
|
|
426
|
+
except httpx.RequestError as req_err:
|
|
427
|
+
raise (f"Quicknode API Request Error: {req_err}")
|
|
428
|
+
except (
|
|
429
|
+
KeyError,
|
|
430
|
+
TypeError,
|
|
431
|
+
) as e: # Handle potential data issues in the API response
|
|
432
|
+
raise Exception(
|
|
433
|
+
f"Error processing QuickNode API response: {e}. Check the API response format."
|
|
434
|
+
)
|
|
435
|
+
except Exception as e:
|
|
436
|
+
raise (f"Quicknode API An unexpected error occurred: {e}")
|
intentkit/utils/error.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Sequence
|
|
3
|
+
|
|
4
|
+
from fastapi.exceptions import RequestValidationError
|
|
5
|
+
from fastapi.utils import is_body_allowed_for_status_code
|
|
6
|
+
from langchain_core.tools.base import ToolException
|
|
7
|
+
from starlette.exceptions import HTTPException
|
|
8
|
+
from starlette.requests import Request
|
|
9
|
+
from starlette.responses import JSONResponse, Response
|
|
10
|
+
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IntentKitAPIError(Exception):
|
|
16
|
+
def __init__(self, status_code: int, key: str, message: str):
|
|
17
|
+
self.key = key
|
|
18
|
+
self.message = message
|
|
19
|
+
self.status_code = status_code
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
return f"{self.key}: {self.message}"
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return f"IntentKitAPIError({self.key}, {self.message}, {self.status_code})"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def intentkit_api_error_handler(
|
|
29
|
+
request: Request, exc: IntentKitAPIError
|
|
30
|
+
) -> Response:
|
|
31
|
+
if exc.status_code >= 500:
|
|
32
|
+
logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
|
|
33
|
+
else:
|
|
34
|
+
logger.info(f"Bad Request for request {request.url}: {str(exc)}")
|
|
35
|
+
return JSONResponse(
|
|
36
|
+
{"error": exc.key, "msg": exc.message},
|
|
37
|
+
status_code=exc.status_code,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def intentkit_other_error_handler(request: Request, exc: Exception) -> Response:
|
|
42
|
+
logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
|
|
43
|
+
return JSONResponse(
|
|
44
|
+
{"error": "ServerError", "msg": "Internal Server Error"},
|
|
45
|
+
status_code=500,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
|
|
50
|
+
headers = getattr(exc, "headers", None)
|
|
51
|
+
if not is_body_allowed_for_status_code(exc.status_code):
|
|
52
|
+
return Response(status_code=exc.status_code, headers=headers)
|
|
53
|
+
if exc.status_code >= 500:
|
|
54
|
+
logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
|
|
55
|
+
return JSONResponse(
|
|
56
|
+
{"error": "ServerError", "msg": "Internal Server Error"},
|
|
57
|
+
status_code=exc.status_code,
|
|
58
|
+
headers=headers,
|
|
59
|
+
)
|
|
60
|
+
logger.info(f"Bad Request for request {request.url}: {str(exc)}")
|
|
61
|
+
return JSONResponse(
|
|
62
|
+
{"error": "BadRequest", "msg": str(exc.detail)},
|
|
63
|
+
status_code=exc.status_code,
|
|
64
|
+
headers=headers,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def format_validation_errors(errors: Sequence) -> str:
|
|
69
|
+
"""Format validation errors into a more readable string."""
|
|
70
|
+
formatted_errors = []
|
|
71
|
+
|
|
72
|
+
for error in errors:
|
|
73
|
+
loc = error.get("loc", [])
|
|
74
|
+
msg = error.get("msg", "")
|
|
75
|
+
error_type = error.get("type", "")
|
|
76
|
+
|
|
77
|
+
# Build field path
|
|
78
|
+
field_path = " -> ".join(str(part) for part in loc if part != "body")
|
|
79
|
+
|
|
80
|
+
# Format the error message with type information
|
|
81
|
+
if field_path:
|
|
82
|
+
if error_type:
|
|
83
|
+
formatted_error = f"Field '{field_path}' ({error_type}): {msg}"
|
|
84
|
+
else:
|
|
85
|
+
formatted_error = f"Field '{field_path}': {msg}"
|
|
86
|
+
else:
|
|
87
|
+
formatted_error = msg
|
|
88
|
+
|
|
89
|
+
formatted_errors.append(formatted_error)
|
|
90
|
+
|
|
91
|
+
return "; ".join(formatted_errors)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def request_validation_exception_handler(
|
|
95
|
+
request: Request, exc: RequestValidationError
|
|
96
|
+
) -> JSONResponse:
|
|
97
|
+
formatted_msg = format_validation_errors(exc.errors())
|
|
98
|
+
return JSONResponse(
|
|
99
|
+
status_code=HTTP_422_UNPROCESSABLE_ENTITY,
|
|
100
|
+
content={"error": "ValidationError", "msg": formatted_msg},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class IntentKitLookUpError(LookupError):
|
|
105
|
+
"""Custom lookup error for IntentKit."""
|
|
106
|
+
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class AgentError(Exception):
|
|
111
|
+
"""Custom exception for agent-related errors."""
|
|
112
|
+
|
|
113
|
+
def __init__(self, agent_id: str, message: str | None = None):
|
|
114
|
+
self.agent_id = agent_id
|
|
115
|
+
if message is None:
|
|
116
|
+
message = f"Agent error occurred for agent_id: {agent_id}"
|
|
117
|
+
super().__init__(message)
|
|
118
|
+
|
|
119
|
+
def __str__(self):
|
|
120
|
+
return f"AgentError(agent_id={self.agent_id}): {super().__str__()}"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class SkillError(ToolException):
|
|
124
|
+
"""Custom exception for skill-related errors."""
|
|
125
|
+
|
|
126
|
+
def __init__(self, agent_id: str, skill_name: str, message: str | None = None):
|
|
127
|
+
self.agent_id = agent_id
|
|
128
|
+
self.skill_name = skill_name
|
|
129
|
+
if message is None:
|
|
130
|
+
message = f"Skill error occurred for agent_id: {agent_id}, skill_name: {skill_name}"
|
|
131
|
+
super().__init__(message)
|
|
132
|
+
|
|
133
|
+
def __str__(self):
|
|
134
|
+
return f"SkillError(agent_id={self.agent_id}, skill_name={self.skill_name}): {super().__str__()}"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging configuration module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Callable, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JsonFormatter(logging.Formatter):
|
|
11
|
+
def __init__(
|
|
12
|
+
self, filter_func: Optional[Callable[[logging.LogRecord], bool]] = None
|
|
13
|
+
):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.filter_func = filter_func
|
|
16
|
+
|
|
17
|
+
def format(self, record):
|
|
18
|
+
if self.filter_func and not self.filter_func(record):
|
|
19
|
+
return ""
|
|
20
|
+
|
|
21
|
+
log_obj = {
|
|
22
|
+
"timestamp": self.formatTime(record),
|
|
23
|
+
"name": record.name,
|
|
24
|
+
"level": record.levelname,
|
|
25
|
+
"message": record.getMessage(),
|
|
26
|
+
}
|
|
27
|
+
# Include any extra attributes
|
|
28
|
+
if hasattr(record, "extra"):
|
|
29
|
+
log_obj.update(record.extra)
|
|
30
|
+
elif record.__dict__.get("extra"):
|
|
31
|
+
log_obj.update(record.__dict__["extra"])
|
|
32
|
+
if record.exc_info:
|
|
33
|
+
log_obj["exc_info"] = self.formatException(record.exc_info)
|
|
34
|
+
return json.dumps(log_obj)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def setup_logging(env: str, debug: bool = False):
|
|
38
|
+
"""
|
|
39
|
+
Setup global logging configuration.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
env: Environment name ('local', 'prod', etc.)
|
|
43
|
+
debug: Debug mode flag
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
if env == "local" or debug:
|
|
47
|
+
# Set up logging configuration for local/debug
|
|
48
|
+
logging.basicConfig(
|
|
49
|
+
level=logging.DEBUG,
|
|
50
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
51
|
+
handlers=[logging.StreamHandler()],
|
|
52
|
+
)
|
|
53
|
+
# logging.getLogger("openai._base_client").setLevel(logging.INFO)
|
|
54
|
+
# logging.getLogger("httpcore.http11").setLevel(logging.INFO)
|
|
55
|
+
# logging.getLogger("sqlalchemy.engine").setLevel(logging.DEBUG)
|
|
56
|
+
else:
|
|
57
|
+
# For non-local environments, use JSON format
|
|
58
|
+
handler = logging.StreamHandler()
|
|
59
|
+
handler.setFormatter(JsonFormatter())
|
|
60
|
+
logging.basicConfig(level=logging.INFO, handlers=[handler])
|
|
61
|
+
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
|
|
62
|
+
# fastapi access log
|
|
63
|
+
uvicorn_access = logging.getLogger("uvicorn.access")
|
|
64
|
+
uvicorn_access.handlers = [] # Remove default handlers
|
|
65
|
+
handler = logging.StreamHandler()
|
|
66
|
+
handler.setFormatter(JsonFormatter())
|
|
67
|
+
uvicorn_access.addHandler(handler)
|
|
68
|
+
uvicorn_access.setLevel(logging.WARNING)
|
|
69
|
+
# telegram access log
|
|
70
|
+
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import jwt
|
|
5
|
+
from fastapi import Depends, HTTPException, Request
|
|
6
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
security = HTTPBearer(auto_error=False)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_jwt_middleware(enable: bool, jwt_secret: str):
|
|
14
|
+
"""Create a JWT verification middleware with configurable enable flag and secret.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
enable: Whether to enable JWT verification
|
|
18
|
+
jwt_secret: Secret key for JWT verification
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
A middleware function that can be used with FastAPI dependencies
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
async def verify_jwt(
|
|
25
|
+
request: Request,
|
|
26
|
+
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
27
|
+
) -> str:
|
|
28
|
+
"""Verify JWT token from Authorization header and return the subject claim.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
str: The subject claim from the JWT token
|
|
32
|
+
"""
|
|
33
|
+
host = request.headers.get("host", "").split(":")[0]
|
|
34
|
+
logger.debug(
|
|
35
|
+
f"verify_jwt: enable={enable}, credentials={credentials}, host={host}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if (
|
|
39
|
+
not enable
|
|
40
|
+
or host == "localhost"
|
|
41
|
+
or host == "127.0.0.1"
|
|
42
|
+
or host == "intent-api"
|
|
43
|
+
or host == "intent-readonly"
|
|
44
|
+
or host == "intent-singleton"
|
|
45
|
+
):
|
|
46
|
+
return ""
|
|
47
|
+
|
|
48
|
+
if not credentials:
|
|
49
|
+
raise HTTPException(
|
|
50
|
+
status_code=401, detail="Missing authentication credentials"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
payload = jwt.decode(
|
|
55
|
+
credentials.credentials, jwt_secret, algorithms=["HS256"]
|
|
56
|
+
)
|
|
57
|
+
return payload.get("sub", "")
|
|
58
|
+
except jwt.InvalidTokenError:
|
|
59
|
+
raise HTTPException(status_code=401, detail="Invalid authentication token")
|
|
60
|
+
|
|
61
|
+
return verify_jwt
|