intentkit 0.6.9.dev2__py3-none-any.whl → 0.6.10.dev2__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/graph.py +17 -2
- intentkit/core/engine.py +49 -30
- intentkit/core/node.py +10 -20
- intentkit/models/agent.py +3 -0
- intentkit/models/chat.py +9 -1
- intentkit/skills/acolyt/ask.py +2 -5
- intentkit/skills/acolyt/base.py +16 -6
- intentkit/skills/aixbt/__init__.py +3 -7
- intentkit/skills/aixbt/projects.py +12 -36
- intentkit/skills/allora/base.py +16 -6
- intentkit/skills/allora/price.py +2 -4
- intentkit/skills/base.py +8 -1
- intentkit/skills/carv/base.py +12 -10
- intentkit/skills/carv/fetch_news.py +90 -92
- intentkit/skills/carv/onchain_query.py +162 -164
- intentkit/skills/carv/token_info_and_price.py +108 -110
- intentkit/skills/chainlist/chain_lookup.py +1 -2
- intentkit/skills/common/current_time.py +1 -2
- intentkit/skills/cookiefun/base.py +20 -12
- intentkit/skills/cookiefun/get_account_details.py +1 -3
- intentkit/skills/cookiefun/get_account_feed.py +1 -3
- intentkit/skills/cookiefun/get_account_smart_followers.py +1 -3
- intentkit/skills/cookiefun/get_sectors.py +2 -3
- intentkit/skills/cookiefun/search_accounts.py +1 -3
- intentkit/skills/cryptocompare/fetch_news.py +3 -4
- intentkit/skills/cryptocompare/fetch_price.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_volume.py +3 -4
- intentkit/skills/cryptocompare/fetch_trading_signals.py +3 -4
- intentkit/skills/cryptopanic/base.py +13 -9
- intentkit/skills/cryptopanic/fetch_crypto_news.py +150 -153
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +133 -136
- intentkit/skills/dapplooker/base.py +16 -6
- intentkit/skills/dapplooker/dapplooker_token_data.py +2 -4
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +2 -3
- intentkit/skills/defillama/coins/fetch_block.py +2 -3
- intentkit/skills/defillama/coins/fetch_current_prices.py +2 -5
- intentkit/skills/defillama/coins/fetch_first_price.py +2 -5
- intentkit/skills/defillama/coins/fetch_historical_prices.py +2 -3
- intentkit/skills/defillama/coins/fetch_price_chart.py +2 -5
- intentkit/skills/defillama/coins/fetch_price_percentage.py +2 -5
- intentkit/skills/defillama/fees/fetch_fees_overview.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +2 -3
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +2 -5
- intentkit/skills/defillama/tvl/fetch_chains.py +2 -3
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +2 -3
- intentkit/skills/defillama/tvl/fetch_protocol.py +2 -5
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +2 -5
- intentkit/skills/defillama/tvl/fetch_protocols.py +2 -3
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +2 -3
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +2 -5
- intentkit/skills/defillama/volumes/fetch_options_overview.py +2 -3
- intentkit/skills/defillama/yields/fetch_pool_chart.py +2 -5
- intentkit/skills/defillama/yields/fetch_pools.py +2 -3
- intentkit/skills/dune_analytics/base.py +15 -9
- intentkit/skills/dune_analytics/fetch_kol_buys.py +125 -128
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +234 -237
- intentkit/skills/elfa/base.py +16 -6
- intentkit/skills/elfa/mention.py +2 -7
- intentkit/skills/elfa/stats.py +2 -6
- intentkit/skills/elfa/tokens.py +1 -4
- intentkit/skills/enso/base.py +25 -13
- intentkit/skills/enso/best_yield.py +1 -4
- intentkit/skills/enso/networks.py +2 -5
- intentkit/skills/enso/prices.py +1 -5
- intentkit/skills/enso/route.py +2 -5
- intentkit/skills/enso/tokens.py +1 -4
- intentkit/skills/enso/wallet.py +3 -9
- intentkit/skills/firecrawl/base.py +16 -6
- intentkit/skills/firecrawl/clear.py +1 -3
- intentkit/skills/firecrawl/crawl.py +7 -8
- intentkit/skills/firecrawl/query.py +7 -9
- intentkit/skills/firecrawl/scrape.py +7 -8
- intentkit/skills/github/github_search.py +1 -3
- intentkit/skills/heurist/base.py +15 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +3 -4
- intentkit/skills/heurist/image_generation_arthemy_comics.py +3 -4
- intentkit/skills/heurist/image_generation_arthemy_real.py +3 -4
- intentkit/skills/heurist/image_generation_braindance.py +3 -4
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +3 -4
- intentkit/skills/heurist/image_generation_flux_1_dev.py +3 -4
- intentkit/skills/heurist/image_generation_sdxl.py +3 -4
- intentkit/skills/http/get.py +0 -2
- intentkit/skills/http/post.py +0 -2
- intentkit/skills/http/put.py +0 -2
- intentkit/skills/lifi/token_execute.py +1 -3
- intentkit/skills/lifi/token_quote.py +0 -2
- intentkit/skills/moralis/base.py +15 -1
- intentkit/skills/nation/nft_check.py +2 -5
- intentkit/skills/openai/base.py +14 -5
- intentkit/skills/openai/dalle_image_generation.py +6 -5
- intentkit/skills/openai/gpt_image_generation.py +6 -5
- intentkit/skills/openai/gpt_image_to_image.py +6 -5
- intentkit/skills/openai/image_to_text.py +6 -6
- intentkit/skills/portfolio/base.py +4 -3
- intentkit/skills/portfolio/token_balances.py +2 -4
- intentkit/skills/portfolio/wallet_approvals.py +2 -4
- intentkit/skills/portfolio/wallet_defi_positions.py +3 -4
- intentkit/skills/portfolio/wallet_history.py +2 -4
- intentkit/skills/portfolio/wallet_net_worth.py +2 -4
- intentkit/skills/portfolio/wallet_nfts.py +2 -4
- intentkit/skills/portfolio/wallet_profitability.py +2 -4
- intentkit/skills/portfolio/wallet_profitability_summary.py +2 -4
- intentkit/skills/portfolio/wallet_stats.py +2 -4
- intentkit/skills/portfolio/wallet_swaps.py +2 -4
- intentkit/skills/slack/base.py +18 -0
- intentkit/skills/slack/get_channel.py +3 -4
- intentkit/skills/slack/get_message.py +3 -4
- intentkit/skills/slack/schedule_message.py +3 -4
- intentkit/skills/slack/send_message.py +3 -4
- intentkit/skills/supabase/delete_data.py +3 -6
- intentkit/skills/supabase/fetch_data.py +3 -6
- intentkit/skills/supabase/insert_data.py +3 -6
- intentkit/skills/supabase/invoke_function.py +3 -6
- intentkit/skills/supabase/update_data.py +3 -6
- intentkit/skills/supabase/upsert_data.py +3 -6
- intentkit/skills/system/add_autonomous_task.py +1 -3
- intentkit/skills/system/delete_autonomous_task.py +1 -3
- intentkit/skills/system/edit_autonomous_task.py +1 -3
- intentkit/skills/system/list_autonomous_tasks.py +1 -3
- intentkit/skills/system/read_agent_api_key.py +2 -3
- intentkit/skills/system/regenerate_agent_api_key.py +2 -5
- intentkit/skills/tavily/base.py +14 -5
- intentkit/skills/tavily/tavily_extract.py +7 -8
- intentkit/skills/tavily/tavily_search.py +11 -9
- intentkit/skills/token/base.py +4 -6
- intentkit/skills/token/erc20_transfers.py +2 -4
- intentkit/skills/token/token_analytics.py +2 -4
- intentkit/skills/token/token_price.py +2 -4
- intentkit/skills/token/token_search.py +2 -4
- intentkit/skills/twitter/base.py +41 -0
- intentkit/skills/twitter/follow_user.py +4 -4
- intentkit/skills/twitter/get_mentions.py +4 -4
- intentkit/skills/twitter/get_timeline.py +4 -4
- intentkit/skills/twitter/get_user_by_username.py +4 -4
- intentkit/skills/twitter/get_user_tweets.py +4 -4
- intentkit/skills/twitter/like_tweet.py +4 -4
- intentkit/skills/twitter/post_tweet.py +3 -4
- intentkit/skills/twitter/reply_tweet.py +3 -4
- intentkit/skills/twitter/retweet.py +4 -4
- intentkit/skills/twitter/search_tweets.py +4 -4
- intentkit/skills/unrealspeech/base.py +16 -0
- intentkit/skills/unrealspeech/text_to_speech.py +4 -4
- intentkit/skills/venice_audio/base.py +11 -9
- intentkit/skills/venice_audio/venice_audio.py +238 -240
- intentkit/skills/venice_image/base.py +23 -19
- intentkit/skills/venice_image/image_enhance/image_enhance.py +78 -80
- intentkit/skills/venice_image/image_generation/image_generation_base.py +115 -117
- intentkit/skills/venice_image/image_upscale/image_upscale.py +88 -90
- intentkit/skills/venice_image/image_vision/image_vision.py +98 -100
- intentkit/skills/web_scraper/document_indexer.py +3 -5
- intentkit/skills/web_scraper/scrape_and_index.py +14 -17
- intentkit/skills/web_scraper/website_indexer.py +8 -10
- intentkit/skills/xmtp/README.md +110 -0
- intentkit/skills/xmtp/__init__.py +82 -0
- intentkit/skills/xmtp/base.py +15 -0
- intentkit/skills/xmtp/schema.json +41 -0
- intentkit/skills/xmtp/transfer.py +155 -0
- intentkit/skills/xmtp/xmtp.svg +26 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/METADATA +3 -3
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/RECORD +168 -162
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/WHEEL +0 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,92 +1,90 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import Any, Dict, Type
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
await self.
|
|
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
|
-
"details": str(e),
|
|
92
|
-
}
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Type
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from intentkit.skills.carv.base import CarvBaseTool
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CarvNewsInput(BaseModel):
|
|
12
|
+
"""
|
|
13
|
+
Input schema for CARV News API.
|
|
14
|
+
This API endpoint does not require any specific parameters from the user.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FetchNewsTool(CarvBaseTool):
|
|
21
|
+
"""
|
|
22
|
+
Tool for fetching the latest news articles from the CARV API.
|
|
23
|
+
This tool retrieves a list of recent news items, each including a title, URL, and a short description (card_text).
|
|
24
|
+
It's useful for getting up-to-date information on various topics covered by CARV's news aggregation.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: str = "carv_fetch_news"
|
|
28
|
+
description: str = (
|
|
29
|
+
"Fetches the latest news articles from the CARV API. "
|
|
30
|
+
"Returns a list of news items, each with a title, URL, and a short summary (card_text)."
|
|
31
|
+
)
|
|
32
|
+
args_schema: Type[BaseModel] = CarvNewsInput
|
|
33
|
+
|
|
34
|
+
async def _arun(
|
|
35
|
+
self, # type: ignore
|
|
36
|
+
**kwargs: Any,
|
|
37
|
+
) -> Dict[str, Any]:
|
|
38
|
+
"""
|
|
39
|
+
Fetches news from the CARV API and returns the response.
|
|
40
|
+
The expected successful response structure is a dictionary containing an "infos" key,
|
|
41
|
+
which holds a list of news articles.
|
|
42
|
+
Example: {"infos": [{"title": "...", "url": "...", "card_text": "..."}, ...]}
|
|
43
|
+
"""
|
|
44
|
+
context = self.get_context()
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
await self.apply_rate_limit(context)
|
|
48
|
+
|
|
49
|
+
result, error = await self._call_carv_api(
|
|
50
|
+
context=context,
|
|
51
|
+
endpoint="/ai-agent-backend/news",
|
|
52
|
+
method="GET",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if error is not None or result is None:
|
|
56
|
+
logger.error(f"Error returned from CARV API (News): {error}")
|
|
57
|
+
return {
|
|
58
|
+
"error": True,
|
|
59
|
+
"error_type": "APIError",
|
|
60
|
+
"message": "Failed to fetch news from CARV API.",
|
|
61
|
+
"details": error, # error is the detailed error dict from _call_carv_api
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# _call_carv_api returns response_json.get("data", response_json) on success.
|
|
65
|
+
# For this endpoint, the "data" field should be {"infos": [...]}.
|
|
66
|
+
# So, 'result' should be {"infos": [...]}.
|
|
67
|
+
if "infos" not in result or not isinstance(result.get("infos"), list):
|
|
68
|
+
logger.warning(
|
|
69
|
+
f"CARV API (News) response did not contain 'infos' list as expected: {result}"
|
|
70
|
+
)
|
|
71
|
+
return {
|
|
72
|
+
"error": True,
|
|
73
|
+
"error_type": "UnexpectedResponseFormat",
|
|
74
|
+
"message": "News data from CARV API is missing the 'infos' list or has incorrect format.",
|
|
75
|
+
"details": result,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Successfully fetched and validated news data
|
|
79
|
+
return result # This will be {"infos": [...]}
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logger.error(
|
|
83
|
+
f"An unexpected error occurred while fetching news: {e}", exc_info=True
|
|
84
|
+
)
|
|
85
|
+
return {
|
|
86
|
+
"error": True,
|
|
87
|
+
"error_type": type(e).__name__,
|
|
88
|
+
"message": "An unexpected error occurred while processing the news request.",
|
|
89
|
+
"details": str(e),
|
|
90
|
+
}
|
|
@@ -1,164 +1,162 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from decimal import Decimal, InvalidOperation
|
|
3
|
-
from typing import Any, Dict, Literal, Type
|
|
4
|
-
|
|
5
|
-
from
|
|
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
|
-
"
|
|
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
|
-
value_decimal = Decimal(normalized)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
format(converted, "f")
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
except Exception as e:
|
|
164
|
-
logger.warning(f"Unable to normalize value '{original_value}': {e}")
|
|
1
|
+
import logging
|
|
2
|
+
from decimal import Decimal, InvalidOperation
|
|
3
|
+
from typing import Any, Dict, Literal, Type
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from intentkit.skills.carv.base import CarvBaseTool
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CarvInput(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Input schema for CARV SQL Query API.
|
|
15
|
+
Defines parameters controllable by the user when invoking the tool.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
question: str = Field(
|
|
19
|
+
...,
|
|
20
|
+
description="The question to query on-chain data.",
|
|
21
|
+
)
|
|
22
|
+
chain: Literal["ethereum", "base", "bitcoin", "solana"] = Field(
|
|
23
|
+
...,
|
|
24
|
+
description="supported chain is ethereum, base, bitcoin, solana",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class OnchainQueryTool(CarvBaseTool):
|
|
29
|
+
"""
|
|
30
|
+
Tool for querying on-chain data using natural language via the CARV SQL Query API.
|
|
31
|
+
|
|
32
|
+
This tool allows you to ask questions about blockchain data in plain English, and it will return
|
|
33
|
+
the relevant information. Behind the scenes, it uses the CARV API to convert your question into a SQL query
|
|
34
|
+
and retrieve the results.
|
|
35
|
+
|
|
36
|
+
Supported Blockchains: Ethereum, Base, Bitcoin, and Solana.
|
|
37
|
+
|
|
38
|
+
If the question is about a blockchain other than the ones listed above, or is not a clear question, the
|
|
39
|
+
tool will return an error.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
name: str = "carv_onchain_query"
|
|
43
|
+
description: str = (
|
|
44
|
+
"Query blockchain data from Ethereum, Base, Bitcoin, or Solana using natural language. "
|
|
45
|
+
"This tool provides access to detailed metrics including block information (timestamps, hashes, miners, gas used/limits), "
|
|
46
|
+
"transaction details (hashes, sender/receiver addresses, amounts, gas prices), and overall network utilization. "
|
|
47
|
+
"It supports aggregate analytics such as daily transaction counts, average gas prices, top wallet activity, and blockchain growth trends. "
|
|
48
|
+
"You can filter results by time range, address type, transaction value, and other parameters.\n\n"
|
|
49
|
+
"IMPORTANT Rules:\n"
|
|
50
|
+
"- Only Ethereum, Base, Bitcoin, and Solana are supported.\n"
|
|
51
|
+
"- Always infer the target blockchain from the user's query.\n"
|
|
52
|
+
"- If an unsupported blockchain is requested, clearly explain the limitation.\n"
|
|
53
|
+
"- Convert user input into a specific and actionable natural language query (e.g., "
|
|
54
|
+
'"What\'s the most active address on Ethereum over the past 24 hours?" or '
|
|
55
|
+
'"Show the largest ETH transaction in the last 30 days").\n'
|
|
56
|
+
"- Respond in clear, concise natural language using only the data returned by the tool.\n"
|
|
57
|
+
"- Avoid markdown or bullet points unless explicitly requested.\n"
|
|
58
|
+
"- ETH values are denominated in 18 decimals—consider 10^18 when interpreting amounts.\n"
|
|
59
|
+
"- Never fabricate or infer data beyond what the tool provides."
|
|
60
|
+
)
|
|
61
|
+
args_schema: Type[BaseModel] = CarvInput
|
|
62
|
+
|
|
63
|
+
async def _arun(
|
|
64
|
+
self,
|
|
65
|
+
question: str,
|
|
66
|
+
chain: str, # type: ignore
|
|
67
|
+
**kwargs: Any,
|
|
68
|
+
) -> Dict[str, Any]:
|
|
69
|
+
"""
|
|
70
|
+
Queries the CARV SQL Query API and returns the response.
|
|
71
|
+
"""
|
|
72
|
+
context = self.get_context()
|
|
73
|
+
try:
|
|
74
|
+
await self.apply_rate_limit(context)
|
|
75
|
+
|
|
76
|
+
payload = {"question": question}
|
|
77
|
+
|
|
78
|
+
result, error = await self._call_carv_api(
|
|
79
|
+
context=context,
|
|
80
|
+
endpoint="/ai-agent-backend/sql_query_by_llm",
|
|
81
|
+
method="POST",
|
|
82
|
+
payload=payload,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if error is not None or result is None:
|
|
86
|
+
logger.error(f"Error returned from CARV API: {error}")
|
|
87
|
+
return {
|
|
88
|
+
"error": True,
|
|
89
|
+
"error_type": "APIError",
|
|
90
|
+
"message": "Failed to fetch data from CARV API.",
|
|
91
|
+
"details": error,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_normalize_unit(result, chain)
|
|
95
|
+
return {"success": True, **result}
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.error(f"An unexpected error occurred: {e}", exc_info=True)
|
|
99
|
+
return {
|
|
100
|
+
"error": True,
|
|
101
|
+
"error_type": type(e).__name__,
|
|
102
|
+
"message": "An unexpected error occurred.",
|
|
103
|
+
"details": str(e),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _normalize_unit(response_data: Dict[str, Any], chain: str) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Normalizes the 'value' field in on-chain response data to a human-readable format.
|
|
110
|
+
Adds the corresponding token ticker after the value.
|
|
111
|
+
|
|
112
|
+
Supported chains:
|
|
113
|
+
- Ethereum: 10^18 -> ETH
|
|
114
|
+
- Base: 10^18 -> ETH
|
|
115
|
+
- Solana: 10^9 -> SOL
|
|
116
|
+
- Bitcoin: 10^8 -> BTC
|
|
117
|
+
"""
|
|
118
|
+
column_infos = response_data.get("column_infos", [])
|
|
119
|
+
rows = response_data.get("rows", [])
|
|
120
|
+
|
|
121
|
+
if "value" not in column_infos:
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
value_index = column_infos.index("value")
|
|
125
|
+
|
|
126
|
+
chain = chain.lower()
|
|
127
|
+
if chain == "ethereum":
|
|
128
|
+
divisor = Decimal("1e18")
|
|
129
|
+
ticker = "ETH"
|
|
130
|
+
elif chain == "base":
|
|
131
|
+
divisor = Decimal("1e18")
|
|
132
|
+
ticker = "ETH"
|
|
133
|
+
elif chain == "solana":
|
|
134
|
+
divisor = Decimal("1e9")
|
|
135
|
+
ticker = "SOL"
|
|
136
|
+
elif chain == "bitcoin":
|
|
137
|
+
divisor = Decimal("1e8")
|
|
138
|
+
ticker = "BTC"
|
|
139
|
+
else:
|
|
140
|
+
logger.warning(f"Unsupported chain '{chain}' for unit normalization.")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
for row in rows:
|
|
144
|
+
items = row.get("items", [])
|
|
145
|
+
if len(items) > value_index:
|
|
146
|
+
original_value = items[value_index]
|
|
147
|
+
try:
|
|
148
|
+
normalized = str(original_value).strip()
|
|
149
|
+
try:
|
|
150
|
+
value_decimal = Decimal(normalized)
|
|
151
|
+
except InvalidOperation:
|
|
152
|
+
value_decimal = Decimal.from_float(float(normalized))
|
|
153
|
+
|
|
154
|
+
converted = value_decimal / divisor
|
|
155
|
+
formatted_value = (
|
|
156
|
+
format(converted, "f").rstrip("0").rstrip(".")
|
|
157
|
+
if "." in format(converted, "f")
|
|
158
|
+
else format(converted, "f")
|
|
159
|
+
)
|
|
160
|
+
items[value_index] = f"{formatted_value} {ticker}"
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.warning(f"Unable to normalize value '{original_value}': {e}")
|