intentkit 0.8.11__py3-none-any.whl → 0.8.12__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 +4 -0
- intentkit/abstracts/skill.py +2 -140
- intentkit/clients/twitter.py +35 -28
- intentkit/core/agent.py +2 -374
- intentkit/core/asset.py +63 -16
- intentkit/core/engine.py +16 -7
- intentkit/core/scheduler.py +8 -8
- intentkit/models/agent.py +109 -94
- intentkit/models/agent_schema.json +6 -9
- intentkit/models/llm.csv +15 -12
- intentkit/models/skill.py +38 -40
- intentkit/skills/acolyt/__init__.py +2 -9
- intentkit/skills/acolyt/base.py +2 -5
- intentkit/skills/aixbt/__init__.py +2 -13
- intentkit/skills/aixbt/base.py +0 -4
- intentkit/skills/aixbt/projects.py +1 -2
- intentkit/skills/allora/__init__.py +2 -9
- intentkit/skills/allora/base.py +2 -5
- intentkit/skills/base.py +168 -27
- intentkit/skills/basename/__init__.py +1 -3
- intentkit/skills/carv/__init__.py +116 -121
- intentkit/skills/carv/base.py +184 -185
- intentkit/skills/casino/__init__.py +4 -15
- intentkit/skills/casino/base.py +0 -4
- intentkit/skills/casino/deck_draw.py +4 -6
- intentkit/skills/casino/deck_shuffle.py +5 -4
- intentkit/skills/casino/dice_roll.py +1 -2
- intentkit/skills/cdp/__init__.py +0 -5
- intentkit/skills/cdp/base.py +0 -4
- intentkit/skills/cdp/schema.json +1 -17
- intentkit/skills/chainlist/__init__.py +2 -7
- intentkit/skills/chainlist/base.py +0 -4
- intentkit/skills/common/__init__.py +2 -9
- intentkit/skills/common/base.py +0 -4
- intentkit/skills/cookiefun/__init__.py +6 -9
- intentkit/skills/cookiefun/base.py +0 -4
- intentkit/skills/cryptocompare/__init__.py +7 -24
- intentkit/skills/cryptocompare/base.py +4 -18
- intentkit/skills/cryptocompare/fetch_news.py +1 -1
- intentkit/skills/cryptocompare/fetch_price.py +1 -1
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +1 -1
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +1 -1
- intentkit/skills/cryptocompare/fetch_top_volume.py +1 -1
- intentkit/skills/cryptocompare/fetch_trading_signals.py +1 -1
- intentkit/skills/cryptopanic/__init__.py +3 -6
- intentkit/skills/cryptopanic/base.py +53 -55
- intentkit/skills/cryptopanic/fetch_crypto_news.py +0 -2
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +1 -3
- intentkit/skills/dapplooker/__init__.py +2 -9
- intentkit/skills/dapplooker/base.py +2 -5
- intentkit/skills/defillama/__init__.py +24 -74
- intentkit/skills/defillama/base.py +3 -13
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +2 -2
- intentkit/skills/defillama/coins/fetch_block.py +2 -2
- intentkit/skills/defillama/coins/fetch_current_prices.py +2 -2
- intentkit/skills/defillama/coins/fetch_first_price.py +2 -2
- intentkit/skills/defillama/coins/fetch_historical_prices.py +2 -2
- intentkit/skills/defillama/coins/fetch_price_chart.py +2 -2
- intentkit/skills/defillama/coins/fetch_price_percentage.py +2 -2
- intentkit/skills/defillama/fees/fetch_fees_overview.py +2 -2
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +2 -2
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +2 -2
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +2 -2
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +2 -2
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +2 -2
- intentkit/skills/defillama/tvl/fetch_chains.py +2 -2
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +2 -2
- intentkit/skills/defillama/tvl/fetch_protocol.py +2 -2
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +2 -2
- intentkit/skills/defillama/tvl/fetch_protocols.py +2 -2
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +2 -2
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +2 -2
- intentkit/skills/defillama/volumes/fetch_options_overview.py +2 -2
- intentkit/skills/defillama/yields/fetch_pool_chart.py +2 -2
- intentkit/skills/defillama/yields/fetch_pools.py +2 -2
- intentkit/skills/dexscreener/__init__.py +97 -102
- intentkit/skills/dexscreener/base.py +125 -130
- intentkit/skills/dexscreener/get_pair_info.py +2 -3
- intentkit/skills/dexscreener/get_token_pairs.py +2 -3
- intentkit/skills/dexscreener/get_tokens_info.py +2 -3
- intentkit/skills/dexscreener/search_token.py +2 -4
- intentkit/skills/dune_analytics/__init__.py +4 -6
- intentkit/skills/dune_analytics/base.py +50 -52
- intentkit/skills/dune_analytics/fetch_kol_buys.py +0 -2
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +0 -2
- intentkit/skills/elfa/__init__.py +5 -18
- intentkit/skills/elfa/base.py +8 -10
- intentkit/skills/enso/__init__.py +9 -29
- intentkit/skills/enso/base.py +3 -6
- intentkit/skills/enso/networks.py +1 -6
- intentkit/skills/enso/route.py +4 -8
- intentkit/skills/enso/tokens.py +2 -12
- intentkit/skills/erc20/__init__.py +1 -5
- intentkit/skills/erc721/__init__.py +1 -3
- intentkit/skills/firecrawl/__init__.py +5 -18
- intentkit/skills/firecrawl/base.py +2 -5
- intentkit/skills/firecrawl/clear.py +3 -6
- intentkit/skills/firecrawl/crawl.py +10 -9
- intentkit/skills/firecrawl/query.py +3 -1
- intentkit/skills/firecrawl/scrape.py +10 -14
- intentkit/skills/firecrawl/utils.py +39 -31
- intentkit/skills/github/__init__.py +2 -7
- intentkit/skills/github/base.py +0 -4
- intentkit/skills/heurist/__init__.py +8 -27
- intentkit/skills/heurist/base.py +2 -5
- intentkit/skills/heurist/image_generation_animagine_xl.py +5 -5
- intentkit/skills/heurist/image_generation_arthemy_comics.py +5 -5
- intentkit/skills/heurist/image_generation_arthemy_real.py +5 -5
- intentkit/skills/heurist/image_generation_braindance.py +5 -5
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +5 -5
- intentkit/skills/heurist/image_generation_flux_1_dev.py +5 -5
- intentkit/skills/heurist/image_generation_sdxl.py +5 -5
- intentkit/skills/http/__init__.py +4 -15
- intentkit/skills/http/base.py +0 -4
- intentkit/skills/lifi/__init__.py +1 -6
- intentkit/skills/lifi/base.py +0 -4
- intentkit/skills/lifi/token_execute.py +1 -4
- intentkit/skills/lifi/token_quote.py +1 -3
- intentkit/skills/moralis/__init__.py +3 -7
- intentkit/skills/moralis/base.py +2 -5
- intentkit/skills/morpho/__init__.py +1 -3
- intentkit/skills/nation/__init__.py +2 -7
- intentkit/skills/nation/base.py +4 -7
- intentkit/skills/openai/__init__.py +5 -18
- intentkit/skills/openai/base.py +8 -10
- intentkit/skills/openai/dalle_image_generation.py +2 -5
- intentkit/skills/openai/gpt_image_generation.py +2 -5
- intentkit/skills/openai/gpt_image_to_image.py +2 -5
- intentkit/skills/openai/image_to_text.py +2 -5
- intentkit/skills/portfolio/__init__.py +11 -35
- intentkit/skills/portfolio/base.py +2 -5
- intentkit/skills/pyth/__init__.py +1 -5
- intentkit/skills/slack/__init__.py +5 -17
- intentkit/skills/slack/base.py +0 -4
- intentkit/skills/supabase/__init__.py +7 -23
- intentkit/skills/supabase/base.py +0 -4
- intentkit/skills/superfluid/__init__.py +1 -3
- intentkit/skills/system/__init__.py +7 -24
- intentkit/skills/system/add_autonomous_task.py +2 -2
- intentkit/skills/system/delete_autonomous_task.py +2 -2
- intentkit/skills/system/edit_autonomous_task.py +2 -4
- intentkit/skills/system/list_autonomous_tasks.py +2 -2
- intentkit/skills/system/read_agent_api_key.py +6 -4
- intentkit/skills/system/regenerate_agent_api_key.py +6 -4
- intentkit/skills/tavily/__init__.py +3 -12
- intentkit/skills/tavily/base.py +2 -5
- intentkit/skills/tavily/tavily_extract.py +1 -2
- intentkit/skills/tavily/tavily_search.py +3 -3
- intentkit/skills/token/__init__.py +5 -10
- intentkit/skills/token/base.py +2 -6
- intentkit/skills/twitter/__init__.py +11 -35
- intentkit/skills/twitter/base.py +18 -29
- intentkit/skills/twitter/follow_user.py +1 -4
- intentkit/skills/twitter/get_mentions.py +2 -8
- intentkit/skills/twitter/get_timeline.py +3 -10
- intentkit/skills/twitter/get_user_by_username.py +1 -4
- intentkit/skills/twitter/get_user_tweets.py +3 -10
- intentkit/skills/twitter/like_tweet.py +1 -4
- intentkit/skills/twitter/post_tweet.py +3 -5
- intentkit/skills/twitter/reply_tweet.py +3 -5
- intentkit/skills/twitter/retweet.py +1 -4
- intentkit/skills/twitter/search_tweets.py +3 -10
- intentkit/skills/unrealspeech/__init__.py +2 -7
- intentkit/skills/unrealspeech/base.py +0 -4
- intentkit/skills/venice_audio/__init__.py +99 -106
- intentkit/skills/venice_audio/base.py +118 -121
- intentkit/skills/venice_audio/venice_audio.py +1 -5
- intentkit/skills/venice_image/__init__.py +147 -154
- intentkit/skills/venice_image/base.py +185 -192
- intentkit/skills/web_scraper/__init__.py +5 -18
- intentkit/skills/web_scraper/base.py +20 -4
- intentkit/skills/web_scraper/document_indexer.py +6 -4
- intentkit/skills/web_scraper/scrape_and_index.py +11 -10
- intentkit/skills/web_scraper/utils.py +38 -38
- intentkit/skills/web_scraper/website_indexer.py +7 -8
- intentkit/skills/weth/__init__.py +1 -5
- intentkit/skills/wow/__init__.py +1 -5
- intentkit/skills/xmtp/__init__.py +4 -15
- {intentkit-0.8.11.dist-info → intentkit-0.8.12.dist-info}/METADATA +1 -1
- {intentkit-0.8.11.dist-info → intentkit-0.8.12.dist-info}/RECORD +183 -183
- {intentkit-0.8.11.dist-info → intentkit-0.8.12.dist-info}/WHEEL +0 -0
- {intentkit-0.8.11.dist-info → intentkit-0.8.12.dist-info}/licenses/LICENSE +0 -0
intentkit/skills/acolyt/base.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Type
|
|
|
3
3
|
from langchain_core.tools.base import ToolException
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
|
-
from intentkit.
|
|
6
|
+
from intentkit.config.config import config
|
|
7
7
|
from intentkit.skills.base import IntentKitSkill
|
|
8
8
|
|
|
9
9
|
base_url = "https://acolyt-oracle-poc.vercel.app"
|
|
@@ -15,16 +15,13 @@ class AcolytBaseTool(IntentKitSkill):
|
|
|
15
15
|
name: str = Field(description="The name of the tool")
|
|
16
16
|
description: str = Field(description="A description of what the tool does")
|
|
17
17
|
args_schema: Type[BaseModel]
|
|
18
|
-
skill_store: SkillStoreABC = Field(
|
|
19
|
-
description="The skill store for persisting data"
|
|
20
|
-
)
|
|
21
18
|
|
|
22
19
|
def get_api_key(self) -> str:
|
|
23
20
|
context = self.get_context()
|
|
24
21
|
skill_config = context.agent.skill_config(self.category)
|
|
25
22
|
api_key_provider = skill_config.get("api_key_provider")
|
|
26
23
|
if api_key_provider == "platform":
|
|
27
|
-
return
|
|
24
|
+
return config.acolyt_api_key
|
|
28
25
|
# for backward compatibility, may only have api_key in skill_config
|
|
29
26
|
elif skill_config.get("api_key"):
|
|
30
27
|
return skill_config.get("api_key")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import TypedDict
|
|
2
2
|
|
|
3
|
-
from intentkit.abstracts.skill import SkillStoreABC
|
|
4
3
|
from intentkit.skills.aixbt.base import AIXBTBaseTool
|
|
5
4
|
from intentkit.skills.aixbt.projects import AIXBTProjects
|
|
6
5
|
from intentkit.skills.base import SkillConfig, SkillState
|
|
@@ -27,7 +26,6 @@ class Config(SkillConfig):
|
|
|
27
26
|
async def get_skills(
|
|
28
27
|
config: "Config",
|
|
29
28
|
is_private: bool,
|
|
30
|
-
store: SkillStoreABC,
|
|
31
29
|
**_,
|
|
32
30
|
) -> list[AIXBTBaseTool]:
|
|
33
31
|
"""Get all AIXBT API skills."""
|
|
@@ -44,26 +42,17 @@ async def get_skills(
|
|
|
44
42
|
available_skills.append(skill_name)
|
|
45
43
|
|
|
46
44
|
# Get each skill using the cached getter
|
|
47
|
-
return [
|
|
48
|
-
get_aixbt_skill(
|
|
49
|
-
name=name,
|
|
50
|
-
store=store,
|
|
51
|
-
)
|
|
52
|
-
for name in available_skills
|
|
53
|
-
]
|
|
45
|
+
return [get_aixbt_skill(name) for name in available_skills]
|
|
54
46
|
|
|
55
47
|
|
|
56
48
|
def get_aixbt_skill(
|
|
57
49
|
name: str,
|
|
58
|
-
store: SkillStoreABC,
|
|
59
50
|
) -> AIXBTBaseTool:
|
|
60
51
|
"""Get an AIXBT API skill by name."""
|
|
61
52
|
|
|
62
53
|
if name == "aixbt_projects":
|
|
63
54
|
if name not in _cache:
|
|
64
|
-
_cache[name] = AIXBTProjects(
|
|
65
|
-
skill_store=store,
|
|
66
|
-
)
|
|
55
|
+
_cache[name] = AIXBTProjects()
|
|
67
56
|
return _cache[name]
|
|
68
57
|
else:
|
|
69
58
|
raise ValueError(f"Unknown AIXBT skill: {name}")
|
intentkit/skills/aixbt/base.py
CHANGED
|
@@ -2,7 +2,6 @@ from typing import Type
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
-
from intentkit.abstracts.skill import SkillStoreABC
|
|
6
5
|
from intentkit.skills.base import IntentKitSkill
|
|
7
6
|
|
|
8
7
|
|
|
@@ -12,9 +11,6 @@ class AIXBTBaseTool(IntentKitSkill):
|
|
|
12
11
|
name: str = Field(description="The name of the tool")
|
|
13
12
|
description: str = Field(description="A description of what the tool does")
|
|
14
13
|
args_schema: Type[BaseModel]
|
|
15
|
-
skill_store: SkillStoreABC = Field(
|
|
16
|
-
description="The skill store for persisting data"
|
|
17
|
-
)
|
|
18
14
|
|
|
19
15
|
@property
|
|
20
16
|
def category(self) -> str:
|
|
@@ -89,9 +89,8 @@ class AIXBTProjects(AIXBTBaseTool):
|
|
|
89
89
|
"rate_limit_minutes"
|
|
90
90
|
):
|
|
91
91
|
await self.user_rate_limit_by_category(
|
|
92
|
-
context.user_id,
|
|
93
92
|
skill_config["rate_limit_number"],
|
|
94
|
-
skill_config["rate_limit_minutes"],
|
|
93
|
+
skill_config["rate_limit_minutes"] * 60,
|
|
95
94
|
)
|
|
96
95
|
|
|
97
96
|
# Get the API key from the agent's configuration
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import NotRequired, TypedDict
|
|
5
5
|
|
|
6
|
-
from intentkit.abstracts.skill import SkillStoreABC
|
|
7
6
|
from intentkit.skills.allora.base import AlloraBaseTool
|
|
8
7
|
from intentkit.skills.allora.price import AlloraGetPrice
|
|
9
8
|
from intentkit.skills.base import SkillConfig, SkillState
|
|
@@ -28,7 +27,6 @@ class Config(SkillConfig):
|
|
|
28
27
|
async def get_skills(
|
|
29
28
|
config: "Config",
|
|
30
29
|
is_private: bool,
|
|
31
|
-
store: SkillStoreABC,
|
|
32
30
|
**_,
|
|
33
31
|
) -> list[AlloraBaseTool]:
|
|
34
32
|
"""Get all Allora skills.
|
|
@@ -36,7 +34,6 @@ async def get_skills(
|
|
|
36
34
|
Args:
|
|
37
35
|
config: The configuration for Allora skills.
|
|
38
36
|
is_private: Whether to include private skills.
|
|
39
|
-
store: The skill store for persisting data.
|
|
40
37
|
|
|
41
38
|
Returns:
|
|
42
39
|
A list of Allora skills.
|
|
@@ -53,7 +50,7 @@ async def get_skills(
|
|
|
53
50
|
# Get each skill using the cached getter
|
|
54
51
|
result = []
|
|
55
52
|
for name in available_skills:
|
|
56
|
-
skill = get_allora_skill(name
|
|
53
|
+
skill = get_allora_skill(name)
|
|
57
54
|
if skill:
|
|
58
55
|
result.append(skill)
|
|
59
56
|
return result
|
|
@@ -61,22 +58,18 @@ async def get_skills(
|
|
|
61
58
|
|
|
62
59
|
def get_allora_skill(
|
|
63
60
|
name: str,
|
|
64
|
-
store: SkillStoreABC,
|
|
65
61
|
) -> AlloraBaseTool:
|
|
66
62
|
"""Get an Allora skill by name.
|
|
67
63
|
|
|
68
64
|
Args:
|
|
69
65
|
name: The name of the skill to get
|
|
70
|
-
store: The skill store for persisting data
|
|
71
66
|
|
|
72
67
|
Returns:
|
|
73
68
|
The requested Allora skill
|
|
74
69
|
"""
|
|
75
70
|
if name == "get_price_prediction":
|
|
76
71
|
if name not in _cache:
|
|
77
|
-
_cache[name] = AlloraGetPrice(
|
|
78
|
-
skill_store=store,
|
|
79
|
-
)
|
|
72
|
+
_cache[name] = AlloraGetPrice()
|
|
80
73
|
return _cache[name]
|
|
81
74
|
else:
|
|
82
75
|
logger.warning(f"Unknown Allora skill: {name}")
|
intentkit/skills/allora/base.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Type
|
|
|
3
3
|
from langchain_core.tools.base import ToolException
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
|
-
from intentkit.
|
|
6
|
+
from intentkit.config.config import config
|
|
7
7
|
from intentkit.skills.base import IntentKitSkill
|
|
8
8
|
|
|
9
9
|
base_url = "https://api.upshot.xyz/v2/allora"
|
|
@@ -15,16 +15,13 @@ class AlloraBaseTool(IntentKitSkill):
|
|
|
15
15
|
name: str = Field(description="The name of the tool")
|
|
16
16
|
description: str = Field(description="A description of what the tool does")
|
|
17
17
|
args_schema: Type[BaseModel]
|
|
18
|
-
skill_store: SkillStoreABC = Field(
|
|
19
|
-
description="The skill store for persisting data"
|
|
20
|
-
)
|
|
21
18
|
|
|
22
19
|
def get_api_key(self) -> str:
|
|
23
20
|
context = self.get_context()
|
|
24
21
|
skill_config = context.agent.skill_config(self.category)
|
|
25
22
|
api_key_provider = skill_config.get("api_key_provider")
|
|
26
23
|
if api_key_provider == "platform":
|
|
27
|
-
return
|
|
24
|
+
return config.allora_api_key
|
|
28
25
|
# for backward compatibility, may only have api_key in skill_config
|
|
29
26
|
elif skill_config.get("api_key"):
|
|
30
27
|
return skill_config.get("api_key")
|
intentkit/skills/base.py
CHANGED
|
@@ -29,10 +29,15 @@ from redis.exceptions import RedisError
|
|
|
29
29
|
from web3 import Web3
|
|
30
30
|
|
|
31
31
|
from intentkit.abstracts.graph import AgentContext
|
|
32
|
-
from intentkit.abstracts.skill import SkillStoreABC
|
|
33
32
|
from intentkit.clients import get_wallet_provider
|
|
34
33
|
from intentkit.clients.web3 import get_web3_client
|
|
35
34
|
from intentkit.models.redis import get_redis
|
|
35
|
+
from intentkit.models.skill import (
|
|
36
|
+
AgentSkillData,
|
|
37
|
+
AgentSkillDataCreate,
|
|
38
|
+
ChatSkillData,
|
|
39
|
+
ChatSkillDataCreate,
|
|
40
|
+
)
|
|
36
41
|
from intentkit.utils.error import IntentKitAPIError, RateLimitExceeded
|
|
37
42
|
|
|
38
43
|
if TYPE_CHECKING:
|
|
@@ -57,7 +62,6 @@ class IntentKitSkill(BaseTool):
|
|
|
57
62
|
Will have predefined abilities.
|
|
58
63
|
"""
|
|
59
64
|
|
|
60
|
-
skill_store: SkillStoreABC
|
|
61
65
|
# overwrite the value of BaseTool
|
|
62
66
|
handle_tool_error: Optional[Union[bool, str, Callable[[ToolException], str]]] = (
|
|
63
67
|
lambda e: f"tool error: {e}"
|
|
@@ -78,15 +82,12 @@ class IntentKitSkill(BaseTool):
|
|
|
78
82
|
"""Get the category of the skill."""
|
|
79
83
|
raise NotImplementedError
|
|
80
84
|
|
|
81
|
-
async def user_rate_limit(
|
|
82
|
-
self, user_id: str, limit: int, minutes: int, key: str
|
|
83
|
-
) -> None:
|
|
85
|
+
async def user_rate_limit(self, limit: int, seconds: int, key: str) -> None:
|
|
84
86
|
"""Check if a user has exceeded the rate limit for this skill.
|
|
85
87
|
|
|
86
88
|
Args:
|
|
87
|
-
user_id: The ID of the user to check
|
|
88
89
|
limit: Maximum number of requests allowed
|
|
89
|
-
|
|
90
|
+
seconds: Time window in seconds
|
|
90
91
|
key: The key to use for rate limiting (e.g., skill name or category)
|
|
91
92
|
|
|
92
93
|
Raises:
|
|
@@ -95,25 +96,48 @@ class IntentKitSkill(BaseTool):
|
|
|
95
96
|
Returns:
|
|
96
97
|
None: Always returns None if no exception is raised
|
|
97
98
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
try:
|
|
100
|
+
context = self.get_context()
|
|
101
|
+
except ValueError:
|
|
102
|
+
self.logger.info(
|
|
103
|
+
"AgentContext not available, skipping rate limit for %s",
|
|
104
|
+
key,
|
|
105
|
+
)
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
user_identifier = context.user_id or context.agent_id
|
|
109
|
+
if not user_identifier:
|
|
110
|
+
return None # No rate limiting when no identifier is available
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
max_requests = int(limit)
|
|
114
|
+
window_seconds = int(seconds)
|
|
115
|
+
except (TypeError, ValueError):
|
|
116
|
+
self.logger.info(
|
|
117
|
+
"Invalid user rate limit parameters for %s: limit=%r, seconds=%r",
|
|
118
|
+
key,
|
|
119
|
+
limit,
|
|
120
|
+
seconds,
|
|
121
|
+
)
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
if window_seconds <= 0 or max_requests <= 0:
|
|
125
|
+
return None
|
|
100
126
|
|
|
101
127
|
try:
|
|
102
128
|
redis = get_redis()
|
|
103
129
|
# Create a unique key for this rate limit and user
|
|
104
|
-
rate_limit_key = f"rate_limit:{key}:{
|
|
130
|
+
rate_limit_key = f"rate_limit:{key}:{user_identifier}"
|
|
105
131
|
|
|
106
132
|
# Get the current count
|
|
107
133
|
count = await redis.incr(rate_limit_key)
|
|
108
134
|
|
|
109
135
|
# Set expiration if this is the first request
|
|
110
136
|
if count == 1:
|
|
111
|
-
await redis.expire(
|
|
112
|
-
rate_limit_key, minutes * 60
|
|
113
|
-
) # Convert minutes to seconds
|
|
137
|
+
await redis.expire(rate_limit_key, window_seconds)
|
|
114
138
|
|
|
115
139
|
# Check if user has exceeded the limit
|
|
116
|
-
if count >
|
|
140
|
+
if count > max_requests:
|
|
117
141
|
raise RateLimitExceeded(f"Rate limit exceeded for {key}")
|
|
118
142
|
|
|
119
143
|
return None
|
|
@@ -129,40 +153,97 @@ class IntentKitSkill(BaseTool):
|
|
|
129
153
|
)
|
|
130
154
|
return None
|
|
131
155
|
|
|
132
|
-
async def user_rate_limit_by_skill(
|
|
133
|
-
self, user_id: str, limit: int, minutes: int
|
|
134
|
-
) -> None:
|
|
156
|
+
async def user_rate_limit_by_skill(self, limit: int, seconds: int) -> None:
|
|
135
157
|
"""Check if a user has exceeded the rate limit for this specific skill.
|
|
136
158
|
|
|
137
159
|
This uses the skill name as the rate limit key.
|
|
138
160
|
|
|
139
161
|
Args:
|
|
140
|
-
user_id: The ID of the user to check
|
|
141
162
|
limit: Maximum number of requests allowed
|
|
142
|
-
|
|
163
|
+
seconds: Time window in seconds
|
|
143
164
|
|
|
144
165
|
Raises:
|
|
145
166
|
RateLimitExceeded: If the user has exceeded the rate limit
|
|
146
167
|
"""
|
|
147
|
-
return await self.user_rate_limit(
|
|
168
|
+
return await self.user_rate_limit(limit, seconds, self.name)
|
|
148
169
|
|
|
149
|
-
async def user_rate_limit_by_category(
|
|
150
|
-
self, user_id: str, limit: int, minutes: int
|
|
151
|
-
) -> None:
|
|
170
|
+
async def user_rate_limit_by_category(self, limit: int, seconds: int) -> None:
|
|
152
171
|
"""Check if a user has exceeded the rate limit for this skill category.
|
|
153
172
|
|
|
154
173
|
This uses the skill category as the rate limit key, which means the limit
|
|
155
174
|
is shared across all skills in the same category.
|
|
156
175
|
|
|
157
176
|
Args:
|
|
158
|
-
user_id: The ID of the user to check
|
|
159
177
|
limit: Maximum number of requests allowed
|
|
160
|
-
|
|
178
|
+
seconds: Time window in seconds
|
|
161
179
|
|
|
162
180
|
Raises:
|
|
163
181
|
RateLimitExceeded: If the user has exceeded the rate limit
|
|
164
182
|
"""
|
|
165
|
-
return await self.user_rate_limit(
|
|
183
|
+
return await self.user_rate_limit(limit, seconds, self.category)
|
|
184
|
+
|
|
185
|
+
async def global_rate_limit(self, limit: int, seconds: int, key: str) -> None:
|
|
186
|
+
"""Check if a global rate limit has been exceeded for a given key.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
limit: Maximum number of requests allowed
|
|
190
|
+
seconds: Time window in seconds
|
|
191
|
+
key: The key to use for rate limiting (e.g., skill name or category)
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
RateLimitExceeded: If the global limit has been exceeded
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
None: Always returns None if no exception is raised
|
|
198
|
+
"""
|
|
199
|
+
try:
|
|
200
|
+
max_requests = int(limit)
|
|
201
|
+
window_seconds = int(seconds)
|
|
202
|
+
except (TypeError, ValueError):
|
|
203
|
+
self.logger.info(
|
|
204
|
+
"Invalid global rate limit parameters for %s: limit=%r, seconds=%r",
|
|
205
|
+
key,
|
|
206
|
+
limit,
|
|
207
|
+
seconds,
|
|
208
|
+
)
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
if window_seconds <= 0 or max_requests <= 0:
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
redis = get_redis()
|
|
216
|
+
rate_limit_key = f"rate_limit:{key}"
|
|
217
|
+
|
|
218
|
+
count = await redis.incr(rate_limit_key)
|
|
219
|
+
|
|
220
|
+
if count == 1:
|
|
221
|
+
await redis.expire(rate_limit_key, window_seconds)
|
|
222
|
+
|
|
223
|
+
if count > max_requests:
|
|
224
|
+
raise RateLimitExceeded(f"Global rate limit exceeded for {key}")
|
|
225
|
+
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
except RuntimeError:
|
|
229
|
+
self.logger.info(
|
|
230
|
+
"Redis not initialized, skipping global rate limit for %s",
|
|
231
|
+
key,
|
|
232
|
+
)
|
|
233
|
+
return None
|
|
234
|
+
except RedisError as e:
|
|
235
|
+
self.logger.info(
|
|
236
|
+
f"Redis error in global rate limiting: {e}, skipping rate limit for {key}"
|
|
237
|
+
)
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
async def global_rate_limit_by_skill(self, limit: int, seconds: int) -> None:
|
|
241
|
+
"""Apply a global rate limit scoped to this specific skill."""
|
|
242
|
+
return await self.global_rate_limit(limit, seconds, self.name)
|
|
243
|
+
|
|
244
|
+
async def global_rate_limit_by_category(self, limit: int, seconds: int) -> None:
|
|
245
|
+
"""Apply a global rate limit scoped to this skill category."""
|
|
246
|
+
return await self.global_rate_limit(limit, seconds, self.category)
|
|
166
247
|
|
|
167
248
|
def _run(self, *args: Any, **kwargs: Any) -> Any:
|
|
168
249
|
raise NotImplementedError(
|
|
@@ -184,10 +265,70 @@ class IntentKitSkill(BaseTool):
|
|
|
184
265
|
|
|
185
266
|
return get_web3_client(network_id)
|
|
186
267
|
|
|
268
|
+
async def get_agent_skill_data(
|
|
269
|
+
self,
|
|
270
|
+
key: str,
|
|
271
|
+
) -> Optional[Dict[str, Any]]:
|
|
272
|
+
"""Retrieve persisted data for this skill scoped to the active agent."""
|
|
273
|
+
return await self.get_agent_skill_data_raw(self.name, key)
|
|
274
|
+
|
|
275
|
+
async def get_agent_skill_data_raw(
|
|
276
|
+
self,
|
|
277
|
+
skill_name: str,
|
|
278
|
+
key: str,
|
|
279
|
+
) -> Optional[Dict[str, Any]]:
|
|
280
|
+
"""Retrieve persisted data for a specific skill scoped to the active agent."""
|
|
281
|
+
context = self.get_context()
|
|
282
|
+
return await AgentSkillData.get(context.agent_id, skill_name, key)
|
|
283
|
+
|
|
284
|
+
async def save_agent_skill_data(self, key: str, data: Dict[str, Any]) -> None:
|
|
285
|
+
"""Persist data for this skill scoped to the active agent."""
|
|
286
|
+
await self.save_agent_skill_data_raw(self.name, key, data)
|
|
287
|
+
|
|
288
|
+
async def save_agent_skill_data_raw(
|
|
289
|
+
self,
|
|
290
|
+
skill_name: str,
|
|
291
|
+
key: str,
|
|
292
|
+
data: Dict[str, Any],
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Persist data for a specific skill scoped to the active agent."""
|
|
295
|
+
context = self.get_context()
|
|
296
|
+
skill_data = AgentSkillDataCreate(
|
|
297
|
+
agent_id=context.agent_id,
|
|
298
|
+
skill=skill_name,
|
|
299
|
+
key=key,
|
|
300
|
+
data=data,
|
|
301
|
+
)
|
|
302
|
+
await skill_data.save()
|
|
303
|
+
|
|
304
|
+
async def delete_agent_skill_data(self, key: str) -> None:
|
|
305
|
+
"""Remove persisted data for this skill scoped to the active agent."""
|
|
306
|
+
context = self.get_context()
|
|
307
|
+
await AgentSkillData.delete(context.agent_id, self.name, key)
|
|
308
|
+
|
|
309
|
+
async def get_thread_skill_data(
|
|
310
|
+
self,
|
|
311
|
+
key: str,
|
|
312
|
+
) -> Optional[Dict[str, Any]]:
|
|
313
|
+
"""Retrieve persisted data for this skill scoped to the active chat."""
|
|
314
|
+
context = self.get_context()
|
|
315
|
+
return await ChatSkillData.get(context.chat_id, self.name, key)
|
|
316
|
+
|
|
317
|
+
async def save_thread_skill_data(self, key: str, data: Dict[str, Any]) -> None:
|
|
318
|
+
"""Persist data for this skill scoped to the active chat."""
|
|
319
|
+
context = self.get_context()
|
|
320
|
+
skill_data = ChatSkillDataCreate(
|
|
321
|
+
chat_id=context.chat_id,
|
|
322
|
+
agent_id=context.agent_id,
|
|
323
|
+
skill=self.name,
|
|
324
|
+
key=key,
|
|
325
|
+
data=data,
|
|
326
|
+
)
|
|
327
|
+
await skill_data.save()
|
|
328
|
+
|
|
187
329
|
|
|
188
330
|
async def get_agentkit_actions(
|
|
189
331
|
agent_id: str,
|
|
190
|
-
_store: SkillStoreABC,
|
|
191
332
|
provider_factories: Sequence[Callable[[], object]],
|
|
192
333
|
*,
|
|
193
334
|
agent: Optional["Agent"] = None,
|
|
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Optional, TypedDict
|
|
|
4
4
|
|
|
5
5
|
from coinbase_agentkit import basename_action_provider
|
|
6
6
|
|
|
7
|
-
from intentkit.abstracts.skill import SkillStoreABC
|
|
8
7
|
from intentkit.skills.base import (
|
|
9
8
|
SkillConfig,
|
|
10
9
|
SkillState,
|
|
@@ -30,7 +29,6 @@ class Config(SkillConfig):
|
|
|
30
29
|
async def get_skills(
|
|
31
30
|
config: "Config",
|
|
32
31
|
is_private: bool,
|
|
33
|
-
store: SkillStoreABC,
|
|
34
32
|
agent_id: str,
|
|
35
33
|
agent: Optional["Agent"] = None,
|
|
36
34
|
**_,
|
|
@@ -45,7 +43,7 @@ async def get_skills(
|
|
|
45
43
|
available_skills.append(skill_name)
|
|
46
44
|
|
|
47
45
|
actions = await get_agentkit_actions(
|
|
48
|
-
agent_id,
|
|
46
|
+
agent_id, [basename_action_provider], agent=agent
|
|
49
47
|
)
|
|
50
48
|
tools: list[BasenameBaseTool] = []
|
|
51
49
|
for skill in available_skills:
|