intentkit 0.6.9.dev2__py3-none-any.whl → 0.6.10__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 +215 -11
- intentkit/models/agent_schema.json +4 -0
- intentkit/models/chat.py +9 -1
- intentkit/models/llm.py +53 -0
- 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 +43 -0
- intentkit/skills/xmtp/transfer.py +155 -0
- intentkit/skills/xmtp/xmtp.png +0 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/METADATA +4 -3
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/RECORD +170 -164
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/WHEEL +0 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,237 +1,234 @@
|
|
|
1
|
-
"""Skill to fetch Crestal Nation metrics from Dune Analytics API.
|
|
2
|
-
|
|
3
|
-
Supports predefined metrics (e.g., total_users, unique_ai_citizens) or direct query IDs.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import difflib
|
|
7
|
-
import re
|
|
8
|
-
from typing import Any, Dict, Type
|
|
9
|
-
|
|
10
|
-
import httpx
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
from intentkit.
|
|
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
|
-
response
|
|
150
|
-
response.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _run(self, question: str):
|
|
237
|
-
raise NotImplementedError("Use _arun for async execution")
|
|
1
|
+
"""Skill to fetch Crestal Nation metrics from Dune Analytics API.
|
|
2
|
+
|
|
3
|
+
Supports predefined metrics (e.g., total_users, unique_ai_citizens) or direct query IDs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import difflib
|
|
7
|
+
import re
|
|
8
|
+
from typing import Any, Dict, Type
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
13
|
+
|
|
14
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
15
|
+
from intentkit.skills.dune_analytics.base import DuneBaseTool
|
|
16
|
+
|
|
17
|
+
SUPPORTED_QUERIES = {
|
|
18
|
+
"total_users": 4858003,
|
|
19
|
+
"weekly_active_users": 4867200,
|
|
20
|
+
"unique_ai_citizens": 4857629,
|
|
21
|
+
"unique_creators": 4844506,
|
|
22
|
+
"ai_citizens_over_time": 4857629,
|
|
23
|
+
"chat_messages_over_time": 4857870,
|
|
24
|
+
"onchain_transactions": 4859895,
|
|
25
|
+
"total_chat_messages": 4857870,
|
|
26
|
+
"daily_skill_executions": 4861785,
|
|
27
|
+
"goods_services": 4859895,
|
|
28
|
+
"agent_tvl": 4859887,
|
|
29
|
+
"citizen_market_cap": 4859887,
|
|
30
|
+
}
|
|
31
|
+
QUERY_ALIASES = {
|
|
32
|
+
"agents": "unique_ai_citizens",
|
|
33
|
+
"citizens": "unique_ai_citizens",
|
|
34
|
+
"market_cap": "citizen_market_cap",
|
|
35
|
+
"nation_market_cap": "citizen_market_cap",
|
|
36
|
+
"number_of_agents": "unique_ai_citizens",
|
|
37
|
+
"number_of_citizens": "unique_ai_citizens",
|
|
38
|
+
"ai_citizens": "unique_ai_citizens",
|
|
39
|
+
"users": "total_users",
|
|
40
|
+
"active_users": "weekly_active_users",
|
|
41
|
+
"creators": "unique_creators",
|
|
42
|
+
"transactions": "onchain_transactions",
|
|
43
|
+
"messages": "total_chat_messages",
|
|
44
|
+
"skill_executions": "daily_skill_executions",
|
|
45
|
+
"tvl": "agent_tvl",
|
|
46
|
+
}
|
|
47
|
+
BASE_URL = "https://api.dune.com/api/v1/query"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class NationMetricsInput(BaseModel):
|
|
51
|
+
"""Input schema for fetching Crestal Nation metrics."""
|
|
52
|
+
|
|
53
|
+
metric: str = Field(
|
|
54
|
+
default="",
|
|
55
|
+
description="Metric name (e.g., total_users, agents) or query ID (e.g., 4858003). Empty for all configured metrics.",
|
|
56
|
+
)
|
|
57
|
+
limit: int = Field(
|
|
58
|
+
default=1000, description="Maximum number of results to fetch (default 1000)."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MetricData(BaseModel):
|
|
63
|
+
"""Data model for a single metric result."""
|
|
64
|
+
|
|
65
|
+
metric: str = Field(description="Metric name or query ID")
|
|
66
|
+
data: Dict[str, Any] = Field(description="Metric data from Dune API")
|
|
67
|
+
error: str = Field(default="", description="Error message if fetch failed")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class NationMetricsOutput(BaseModel):
|
|
71
|
+
"""Output schema for Crestal Nation metrics."""
|
|
72
|
+
|
|
73
|
+
metrics: Dict[str, MetricData] = Field(
|
|
74
|
+
description="Dictionary of metric names or query IDs to their data"
|
|
75
|
+
)
|
|
76
|
+
summary: str = Field(description="Summary of fetched metrics")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class FetchNationMetrics(DuneBaseTool):
|
|
80
|
+
"""Skill to fetch Crestal Nation metrics from Dune Analytics API."""
|
|
81
|
+
|
|
82
|
+
name: str = "dune_fetch_nation_metrics"
|
|
83
|
+
description: str = (
|
|
84
|
+
"Fetches Crestal Nation metrics (e.g., total_users, agents/citizens, market_cap) from Dune Analytics API. "
|
|
85
|
+
"Supports predefined metrics, direct query IDs, or all configured metrics if none specified. "
|
|
86
|
+
"Handles rate limits with retries."
|
|
87
|
+
)
|
|
88
|
+
args_schema: Type[BaseModel] = NationMetricsInput
|
|
89
|
+
skill_store: SkillStoreABC = Field(description="Skill store for data persistence")
|
|
90
|
+
|
|
91
|
+
def normalize_metric(self, metric: str) -> str:
|
|
92
|
+
"""Normalize a metric string for matching.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
metric: Raw metric string from input.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Normalized metric string (lowercase, underscores, no punctuation).
|
|
99
|
+
"""
|
|
100
|
+
if not metric:
|
|
101
|
+
return ""
|
|
102
|
+
metric = re.sub(r"[^\w\s]", "", metric.lower()).replace(" ", "_")
|
|
103
|
+
return re.sub(r"_+", "_", metric).strip("_")
|
|
104
|
+
|
|
105
|
+
def find_closest_metrics(self, metric: str, max_suggestions: int = 3) -> list[str]:
|
|
106
|
+
"""Find the closest matching metrics using fuzzy matching.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
metric: Input metric to match against.
|
|
110
|
+
max_suggestions: Maximum number of suggestions to return.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of closest metric names.
|
|
114
|
+
"""
|
|
115
|
+
all_metrics = list(SUPPORTED_QUERIES.keys()) + list(QUERY_ALIASES.keys())
|
|
116
|
+
if not metric or not all_metrics:
|
|
117
|
+
return []
|
|
118
|
+
return difflib.get_close_matches(
|
|
119
|
+
metric, all_metrics, n=max_suggestions, cutoff=0.6
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@retry(
|
|
123
|
+
stop=stop_after_attempt(3), wait=wait_exponential(multiplier=5, min=5, max=60)
|
|
124
|
+
)
|
|
125
|
+
async def fetch_data(
|
|
126
|
+
self, query_id: int, api_key: str, limit: int = 1000
|
|
127
|
+
) -> Dict[str, Any]:
|
|
128
|
+
"""Fetch data for a specific Dune query.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
query_id: Dune query ID.
|
|
132
|
+
api_key: Dune API key.
|
|
133
|
+
limit: Maximum number of results (default 1000).
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Dictionary of query results.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ToolException: If the API request fails.
|
|
140
|
+
"""
|
|
141
|
+
from langchain.tools.base import ToolException
|
|
142
|
+
|
|
143
|
+
url = f"{BASE_URL}/{query_id}/results?limit={limit}"
|
|
144
|
+
headers = {"X-Dune-API-Key": api_key}
|
|
145
|
+
|
|
146
|
+
async with httpx.AsyncClient() as client:
|
|
147
|
+
try:
|
|
148
|
+
response = await client.get(url, headers=headers, timeout=10)
|
|
149
|
+
response.raise_for_status()
|
|
150
|
+
return response.json().get("result", {})
|
|
151
|
+
except (httpx.RequestError, httpx.HTTPStatusError) as e:
|
|
152
|
+
raise ToolException(f"Error fetching data from Dune API: {e}")
|
|
153
|
+
|
|
154
|
+
async def _arun(
|
|
155
|
+
self,
|
|
156
|
+
metric: str = "",
|
|
157
|
+
limit: int = 1000,
|
|
158
|
+
**kwargs,
|
|
159
|
+
) -> NationMetricsOutput:
|
|
160
|
+
"""Fetch Crestal Nation metrics asynchronously.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
metric: Metric name (e.g., total_users) or query ID (e.g., 4858003). Empty for all configured metrics.
|
|
164
|
+
limit: Maximum number of results (default 1000).
|
|
165
|
+
config: Runnable configuration.
|
|
166
|
+
**kwargs: Additional keyword arguments.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
NationMetricsOutput with metric data and summary.
|
|
170
|
+
"""
|
|
171
|
+
import logging
|
|
172
|
+
|
|
173
|
+
logger = logging.getLogger(__name__)
|
|
174
|
+
api_key = self.get_api_key()
|
|
175
|
+
|
|
176
|
+
metric = self.normalize_metric(metric)
|
|
177
|
+
metric = QUERY_ALIASES.get(metric, metric)
|
|
178
|
+
|
|
179
|
+
results = {}
|
|
180
|
+
metrics_to_fetch = {}
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
query_id = int(metric)
|
|
184
|
+
metrics_to_fetch[str(query_id)] = query_id
|
|
185
|
+
except (ValueError, TypeError):
|
|
186
|
+
metrics_to_fetch = (
|
|
187
|
+
SUPPORTED_QUERIES
|
|
188
|
+
if not metric
|
|
189
|
+
else (
|
|
190
|
+
{metric: SUPPORTED_QUERIES[metric]}
|
|
191
|
+
if metric in SUPPORTED_QUERIES
|
|
192
|
+
else {}
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if not metrics_to_fetch:
|
|
197
|
+
closest_metrics = self.find_closest_metrics(metric)
|
|
198
|
+
supported = ", ".join(SUPPORTED_QUERIES.keys())
|
|
199
|
+
suggestions = (
|
|
200
|
+
f" Did you mean: {', '.join(closest_metrics)}?"
|
|
201
|
+
if closest_metrics
|
|
202
|
+
else ""
|
|
203
|
+
)
|
|
204
|
+
logger.warning(
|
|
205
|
+
"Unrecognized metric or query ID: %s. Suggested: %s",
|
|
206
|
+
metric,
|
|
207
|
+
closest_metrics,
|
|
208
|
+
)
|
|
209
|
+
return NationMetricsOutput(
|
|
210
|
+
metrics={},
|
|
211
|
+
summary=(
|
|
212
|
+
f"Invalid metric or query ID: {metric}. Supported metrics include: {supported}.{suggestions} "
|
|
213
|
+
"Try 'fetch nation metrics total_users' or a valid query ID, or submit a feature request at "
|
|
214
|
+
"https://github.com/crestalnetwork/intentkit."
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
for metric_name, query_id in metrics_to_fetch.items():
|
|
219
|
+
try:
|
|
220
|
+
data = await self.fetch_data(query_id, api_key, limit)
|
|
221
|
+
results[metric_name] = MetricData(metric=metric_name, data=data)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
results[metric_name] = MetricData(
|
|
224
|
+
metric=metric_name, data={}, error=str(e)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
summary = f"Fetched data for {len([m for m in results.values() if not m.error])}/{len(metrics_to_fetch)} metrics."
|
|
228
|
+
if any(m.error for m in results.values()):
|
|
229
|
+
summary += f" Errors occurred for: {', '.join(m.metric for m in results.values() if m.error)}."
|
|
230
|
+
|
|
231
|
+
return NationMetricsOutput(metrics=results, summary=summary)
|
|
232
|
+
|
|
233
|
+
def _run(self, question: str):
|
|
234
|
+
raise NotImplementedError("Use _arun for async execution")
|
intentkit/skills/elfa/base.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Type
|
|
2
2
|
|
|
3
|
+
from langchain.tools.base import ToolException
|
|
3
4
|
from pydantic import BaseModel, Field
|
|
4
5
|
|
|
5
6
|
from intentkit.abstracts.skill import SkillStoreABC
|
|
6
|
-
from intentkit.skills.base import IntentKitSkill
|
|
7
|
+
from intentkit.skills.base import IntentKitSkill
|
|
7
8
|
|
|
8
9
|
base_url = "https://api.elfa.ai/v2"
|
|
9
10
|
|
|
@@ -18,10 +19,19 @@ class ElfaBaseTool(IntentKitSkill):
|
|
|
18
19
|
description="The skill store for persisting data"
|
|
19
20
|
)
|
|
20
21
|
|
|
21
|
-
def get_api_key(self
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
def get_api_key(self) -> str:
|
|
23
|
+
context = self.get_context()
|
|
24
|
+
skill_config = context.agent.skill_config(self.category)
|
|
25
|
+
api_key_provider = skill_config.get("api_key_provider")
|
|
26
|
+
if api_key_provider == "platform":
|
|
27
|
+
return self.skill_store.get_system_config("elfa_api_key")
|
|
28
|
+
# for backward compatibility, may only have api_key in skill_config
|
|
29
|
+
elif skill_config.get("api_key"):
|
|
30
|
+
return skill_config.get("api_key")
|
|
31
|
+
else:
|
|
32
|
+
raise ToolException(
|
|
33
|
+
f"Invalid API key provider: {api_key_provider}, or no api_key in config"
|
|
34
|
+
)
|
|
25
35
|
|
|
26
36
|
@property
|
|
27
37
|
def category(self) -> str:
|
intentkit/skills/elfa/mention.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional, Type
|
|
4
4
|
|
|
5
|
-
from langchain_core.runnables import RunnableConfig
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
6
|
|
|
8
7
|
from .base import ElfaBaseTool
|
|
@@ -55,7 +54,6 @@ class ElfaGetTopMentions(ElfaBaseTool):
|
|
|
55
54
|
timeWindow: str = "1h",
|
|
56
55
|
page: int = 1,
|
|
57
56
|
pageSize: int = 10,
|
|
58
|
-
config: RunnableConfig = None,
|
|
59
57
|
**kwargs,
|
|
60
58
|
) -> ElfaGetTopMentionsOutput:
|
|
61
59
|
"""
|
|
@@ -76,8 +74,7 @@ class ElfaGetTopMentions(ElfaBaseTool):
|
|
|
76
74
|
ValueError: If API key is not found
|
|
77
75
|
ToolException: If there's an error with the API request
|
|
78
76
|
"""
|
|
79
|
-
|
|
80
|
-
api_key = self.get_api_key(context)
|
|
77
|
+
api_key = self.get_api_key()
|
|
81
78
|
|
|
82
79
|
# Prepare parameters according to API spec
|
|
83
80
|
params = {
|
|
@@ -163,7 +160,6 @@ class ElfaSearchMentions(ElfaBaseTool):
|
|
|
163
160
|
limit: int = 20,
|
|
164
161
|
searchType: str = "or",
|
|
165
162
|
cursor: Optional[str] = None,
|
|
166
|
-
config: RunnableConfig = None,
|
|
167
163
|
**kwargs,
|
|
168
164
|
) -> ElfaSearchMentionsOutput:
|
|
169
165
|
"""
|
|
@@ -186,8 +182,7 @@ class ElfaSearchMentions(ElfaBaseTool):
|
|
|
186
182
|
ValueError: If API key is not found or neither keywords nor accountName provided
|
|
187
183
|
ToolException: If there's an error with the API request
|
|
188
184
|
"""
|
|
189
|
-
|
|
190
|
-
api_key = self.get_api_key(context)
|
|
185
|
+
api_key = self.get_api_key()
|
|
191
186
|
|
|
192
187
|
# Validate that at least one search criteria is provided
|
|
193
188
|
if not keywords and not accountName:
|
intentkit/skills/elfa/stats.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, Optional, Type
|
|
4
4
|
|
|
5
|
-
from langchain_core.runnables import RunnableConfig
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
6
|
|
|
8
7
|
from .base import ElfaBaseTool
|
|
@@ -46,9 +45,7 @@ class ElfaGetSmartStats(ElfaBaseTool):
|
|
|
46
45
|
and social media performance audits."""
|
|
47
46
|
args_schema: Type[BaseModel] = ElfaGetSmartStatsInput
|
|
48
47
|
|
|
49
|
-
async def _arun(
|
|
50
|
-
self, username: str, config: RunnableConfig = None, **kwargs
|
|
51
|
-
) -> ElfaGetSmartStatsOutput:
|
|
48
|
+
async def _arun(self, username: str, **kwargs) -> ElfaGetSmartStatsOutput:
|
|
52
49
|
"""
|
|
53
50
|
Execute the smart stats request.
|
|
54
51
|
|
|
@@ -64,8 +61,7 @@ class ElfaGetSmartStats(ElfaBaseTool):
|
|
|
64
61
|
ValueError: If API key is not found
|
|
65
62
|
ToolException: If there's an error with the API request
|
|
66
63
|
"""
|
|
67
|
-
|
|
68
|
-
api_key = self.get_api_key(context)
|
|
64
|
+
api_key = self.get_api_key()
|
|
69
65
|
|
|
70
66
|
# Prepare parameters according to API spec
|
|
71
67
|
params = {"username": username}
|
intentkit/skills/elfa/tokens.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional, Type
|
|
4
4
|
|
|
5
|
-
from langchain_core.runnables import RunnableConfig
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
6
|
|
|
8
7
|
from .base import ElfaBaseTool
|
|
@@ -69,7 +68,6 @@ class ElfaGetTrendingTokens(ElfaBaseTool):
|
|
|
69
68
|
page: int = 1,
|
|
70
69
|
pageSize: int = 50,
|
|
71
70
|
minMentions: int = 5,
|
|
72
|
-
config: RunnableConfig = None,
|
|
73
71
|
**kwargs,
|
|
74
72
|
) -> ElfaGetTrendingTokensOutput:
|
|
75
73
|
"""
|
|
@@ -90,8 +88,7 @@ class ElfaGetTrendingTokens(ElfaBaseTool):
|
|
|
90
88
|
ValueError: If API key is not found
|
|
91
89
|
ToolException: If there's an error with the API request
|
|
92
90
|
"""
|
|
93
|
-
|
|
94
|
-
api_key = self.get_api_key(context)
|
|
91
|
+
api_key = self.get_api_key()
|
|
95
92
|
|
|
96
93
|
# Prepare parameters according to API spec
|
|
97
94
|
params = {
|