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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""GPT image-to-image generation skill for OpenAI."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import logging
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from typing import Literal, Type
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
import openai
|
|
10
|
+
from epyxid import XID
|
|
11
|
+
from langchain_core.runnables import RunnableConfig
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
from intentkit.skills.openai.base import OpenAIBaseTool
|
|
15
|
+
from intentkit.utils.s3 import store_image_bytes
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GPTImageToImageInput(BaseModel):
|
|
21
|
+
"""Input for GPTImageToImage tool."""
|
|
22
|
+
|
|
23
|
+
image_url: str = Field(
|
|
24
|
+
description="URL of the source image to edit.",
|
|
25
|
+
)
|
|
26
|
+
prompt: str = Field(
|
|
27
|
+
description="Text prompt describing the desired edits to the image.",
|
|
28
|
+
)
|
|
29
|
+
size: Literal["1024x1024", "1536x1024", "1024x1536", "auto"] = Field(
|
|
30
|
+
default="auto",
|
|
31
|
+
description="Size of the generated image. Options: 1024x1024, 1536x1024, 1024x1536, auto",
|
|
32
|
+
)
|
|
33
|
+
quality: Literal["high", "medium", "low", "auto"] = Field(
|
|
34
|
+
default="auto",
|
|
35
|
+
description="Quality of the generated image. Options: high, medium, low, auto",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class GPTImageToImage(OpenAIBaseTool):
|
|
40
|
+
"""Tool for editing images using OpenAI's GPT-Image-1 model.
|
|
41
|
+
|
|
42
|
+
This tool takes a source image URL and a text prompt, then uses OpenAI's API to
|
|
43
|
+
generate an edited version of the image based on the description.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
name: The name of the tool.
|
|
47
|
+
description: A description of what the tool does.
|
|
48
|
+
args_schema: The schema for the tool's input arguments.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
name: str = "gpt_image_to_image"
|
|
52
|
+
description: str = (
|
|
53
|
+
"Edit images using OpenAI's GPT-Image-1 model.\n"
|
|
54
|
+
"Provide a source image URL and a text prompt describing the desired edits.\n"
|
|
55
|
+
"GPT-Image-1 is a powerful image editing model capable of transforming images "
|
|
56
|
+
"based on text descriptions.\n"
|
|
57
|
+
"You can specify size and quality parameters for more control.\n"
|
|
58
|
+
)
|
|
59
|
+
args_schema: Type[BaseModel] = GPTImageToImageInput
|
|
60
|
+
|
|
61
|
+
async def _arun(
|
|
62
|
+
self,
|
|
63
|
+
image_url: str,
|
|
64
|
+
prompt: str,
|
|
65
|
+
size: Literal["1024x1024", "1536x1024", "1024x1536", "auto"] = "auto",
|
|
66
|
+
quality: Literal["high", "medium", "low", "auto"] = "auto",
|
|
67
|
+
config: RunnableConfig = None,
|
|
68
|
+
**kwargs,
|
|
69
|
+
) -> str:
|
|
70
|
+
"""Implementation of the tool to edit images using OpenAI's GPT-Image-1 model.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
image_url: URL of the source image to edit.
|
|
74
|
+
prompt: Text prompt describing the desired edits to the image.
|
|
75
|
+
size: Size of the generated image. Options: 1024x1024, 1536x1024, 1024x1536, auto
|
|
76
|
+
quality: Quality of the generated image. Options: high, medium, low, auto
|
|
77
|
+
config: Configuration for the runnable.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
str: URL of the edited image.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
Exception: If the image editing fails.
|
|
84
|
+
"""
|
|
85
|
+
context = self.context_from_config(config)
|
|
86
|
+
|
|
87
|
+
# Get the OpenAI API key from the skill store
|
|
88
|
+
api_key = self.get_api_key(context)
|
|
89
|
+
|
|
90
|
+
# Generate a unique job ID
|
|
91
|
+
job_id = str(XID())
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
# Download the image from the URL asynchronously
|
|
95
|
+
async with httpx.AsyncClient() as client:
|
|
96
|
+
response = await client.get(image_url, follow_redirects=True)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
image_data = response.content
|
|
99
|
+
|
|
100
|
+
# Initialize the OpenAI client
|
|
101
|
+
client = openai.OpenAI(api_key=api_key)
|
|
102
|
+
|
|
103
|
+
# Import required modules for file handling
|
|
104
|
+
import os
|
|
105
|
+
import tempfile
|
|
106
|
+
|
|
107
|
+
from PIL import Image
|
|
108
|
+
|
|
109
|
+
# Create a temporary file with .png extension
|
|
110
|
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
|
|
111
|
+
temp_path = temp_file.name
|
|
112
|
+
|
|
113
|
+
# Open the image, convert to RGB if needed, and save as PNG
|
|
114
|
+
img = Image.open(BytesIO(image_data))
|
|
115
|
+
if img.mode != "RGB":
|
|
116
|
+
img = img.convert("RGB")
|
|
117
|
+
img.save(temp_path, format="PNG")
|
|
118
|
+
|
|
119
|
+
# Open the temporary file in binary read mode
|
|
120
|
+
# This provides both .read() method and .name attribute that OpenAI SDK needs
|
|
121
|
+
image_file = open(temp_path, "rb")
|
|
122
|
+
|
|
123
|
+
# Make the API request to edit the image
|
|
124
|
+
try:
|
|
125
|
+
response = client.images.edit(
|
|
126
|
+
model="gpt-image-1",
|
|
127
|
+
image=image_file, # Use the file object with .read() method and .name attribute
|
|
128
|
+
prompt=prompt,
|
|
129
|
+
size=size,
|
|
130
|
+
quality=quality,
|
|
131
|
+
n=1,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# GPT-Image-1 always returns base64-encoded images
|
|
135
|
+
# Get the base64 image data from the response
|
|
136
|
+
base64_image = response.data[0].b64_json
|
|
137
|
+
|
|
138
|
+
# Log the usage information if available
|
|
139
|
+
if hasattr(response, "usage") and response.usage:
|
|
140
|
+
usage = response.usage
|
|
141
|
+
logger.info(
|
|
142
|
+
f"GPT-Image-1 edit usage: "
|
|
143
|
+
f"input_tokens={usage.input_tokens}, "
|
|
144
|
+
f"output_tokens={usage.output_tokens}, "
|
|
145
|
+
f"total_tokens={usage.total_tokens}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Log detailed input tokens information if available
|
|
149
|
+
if (
|
|
150
|
+
hasattr(usage, "input_tokens_details")
|
|
151
|
+
and usage.input_tokens_details
|
|
152
|
+
):
|
|
153
|
+
details = usage.input_tokens_details
|
|
154
|
+
logger.info(f"Input tokens details: {details}")
|
|
155
|
+
|
|
156
|
+
# Decode the base64 string to bytes
|
|
157
|
+
image_bytes = base64.b64decode(base64_image)
|
|
158
|
+
|
|
159
|
+
# Generate a key with agent ID as prefix
|
|
160
|
+
image_key = f"{context.agent.id}/gpt-image-edit/{job_id}"
|
|
161
|
+
|
|
162
|
+
# Store the image bytes and get the CDN URL
|
|
163
|
+
stored_url = await store_image_bytes(image_bytes, image_key)
|
|
164
|
+
finally:
|
|
165
|
+
# Close and remove the temporary file
|
|
166
|
+
image_file.close()
|
|
167
|
+
if os.path.exists(temp_path):
|
|
168
|
+
os.unlink(temp_path)
|
|
169
|
+
|
|
170
|
+
# Return the stored image URL
|
|
171
|
+
return stored_url
|
|
172
|
+
|
|
173
|
+
except httpx.HTTPError as e:
|
|
174
|
+
error_message = f"Failed to download image from URL {image_url}: {str(e)}"
|
|
175
|
+
logger.error(error_message)
|
|
176
|
+
raise Exception(error_message)
|
|
177
|
+
|
|
178
|
+
except openai.OpenAIError as e:
|
|
179
|
+
error_message = f"OpenAI API error: {str(e)}"
|
|
180
|
+
logger.error(error_message)
|
|
181
|
+
raise Exception(error_message)
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
error_message = f"Error editing image with GPT-Image-1: {str(e)}"
|
|
185
|
+
logger.error(error_message)
|
|
186
|
+
raise Exception(error_message)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Type
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
import openai
|
|
7
|
+
from langchain_core.runnables import RunnableConfig
|
|
8
|
+
from PIL import Image
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from intentkit.skills.openai.base import OpenAIBaseTool
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ImageToTextInput(BaseModel):
|
|
17
|
+
"""Input for ImageToText tool."""
|
|
18
|
+
|
|
19
|
+
image: str = Field(
|
|
20
|
+
description="URL of the image to convert to text.",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ImageToTextOutput(BaseModel):
|
|
25
|
+
"""Output for ImageToText tool."""
|
|
26
|
+
|
|
27
|
+
description: str = Field(description="Detailed text description of the image.")
|
|
28
|
+
width: int = Field(description="Width of the processed image.")
|
|
29
|
+
height: int = Field(description="Height of the processed image.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ImageToText(OpenAIBaseTool):
|
|
33
|
+
"""Tool for converting images to text using OpenAI's GPT-4o model.
|
|
34
|
+
|
|
35
|
+
This tool takes an image URL and uses OpenAI's vision capabilities
|
|
36
|
+
to generate a detailed text description of the image content.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
name: The name of the tool.
|
|
40
|
+
description: A description of what the tool does.
|
|
41
|
+
args_schema: The schema for the tool's input arguments.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
name: str = "image_to_text"
|
|
45
|
+
description: str = (
|
|
46
|
+
"Convert an image to detailed text description.\n"
|
|
47
|
+
"Provide a URL to the image to analyze and get a comprehensive textual description.\n"
|
|
48
|
+
"Optimized for DALL-E generated images and preserves as many details as possible."
|
|
49
|
+
)
|
|
50
|
+
args_schema: Type[BaseModel] = ImageToTextInput
|
|
51
|
+
|
|
52
|
+
async def _arun(
|
|
53
|
+
self, image: str, config: RunnableConfig, **kwargs
|
|
54
|
+
) -> ImageToTextOutput:
|
|
55
|
+
"""Implementation of the tool to convert images to text.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
image (str): URL of the image to convert to text.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
ImageToTextOutput: Object containing the text description and image dimensions.
|
|
62
|
+
"""
|
|
63
|
+
context = self.context_from_config(config)
|
|
64
|
+
logger.debug(f"context: {context}")
|
|
65
|
+
|
|
66
|
+
# Get the OpenAI client from the skill store
|
|
67
|
+
api_key = self.get_api_key(context)
|
|
68
|
+
client = openai.AsyncOpenAI(api_key=api_key)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# Download the image from the URL
|
|
72
|
+
async with aiohttp.ClientSession() as session:
|
|
73
|
+
async with session.get(image) as response:
|
|
74
|
+
if response.status != 200:
|
|
75
|
+
raise Exception(
|
|
76
|
+
f"Failed to download image from URL: {response.status}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Get image data
|
|
80
|
+
image_data = await response.read()
|
|
81
|
+
img = Image.open(io.BytesIO(image_data))
|
|
82
|
+
|
|
83
|
+
# Get original dimensions
|
|
84
|
+
orig_width, orig_height = img.size
|
|
85
|
+
|
|
86
|
+
# Calculate new dimensions with longest side as 1024 (for reference only)
|
|
87
|
+
max_size = 1024
|
|
88
|
+
if orig_width >= orig_height:
|
|
89
|
+
scaled_width = max_size
|
|
90
|
+
scaled_height = int(orig_height * (max_size / orig_width))
|
|
91
|
+
else:
|
|
92
|
+
scaled_height = max_size
|
|
93
|
+
scaled_width = int(orig_width * (max_size / orig_height))
|
|
94
|
+
|
|
95
|
+
# Use OpenAI API to analyze the image (using original image)
|
|
96
|
+
response = await client.chat.completions.create(
|
|
97
|
+
model="gpt-4o",
|
|
98
|
+
messages=[
|
|
99
|
+
{
|
|
100
|
+
"role": "system",
|
|
101
|
+
"content": "You are an expert image analyzer. Describe the image in great detail, capturing all visual elements, colors, composition, subjects, and context. If there are people in the picture, be sure to clearly describe the person's skin color, hair color, expression, direction, etc. For DALL-E generated images, pay special attention to artistic style, lighting effects, and fantastical elements. Preserve as many details as possible in your description.",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"role": "user",
|
|
105
|
+
"content": [
|
|
106
|
+
{"type": "text", "text": "Describe this image in detail:"},
|
|
107
|
+
{
|
|
108
|
+
"type": "image_url",
|
|
109
|
+
"image_url": {"url": image, "detail": "high"},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
max_tokens=1000,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Return the text description and scaled image dimensions
|
|
118
|
+
return ImageToTextOutput(
|
|
119
|
+
description=response.choices[0].message.content,
|
|
120
|
+
width=scaled_width,
|
|
121
|
+
height=scaled_height,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"Error converting image to text: {e}")
|
|
126
|
+
raise Exception(f"Error converting image to text: {str(e)}")
|
|
Binary file
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "OpenAI",
|
|
5
|
+
"description": "Skills for interacting with OpenAI services, including image generation, image-to-text conversion, and other AI capabilities",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/openai/openai.png",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"AI",
|
|
9
|
+
"Image Generation",
|
|
10
|
+
"Image Analysis"
|
|
11
|
+
],
|
|
12
|
+
"properties": {
|
|
13
|
+
"enabled": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"title": "Enabled",
|
|
16
|
+
"description": "Whether this skill is enabled",
|
|
17
|
+
"default": true
|
|
18
|
+
},
|
|
19
|
+
"states": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"image_to_text": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"title": "Image to Text",
|
|
25
|
+
"enum": [
|
|
26
|
+
"disabled",
|
|
27
|
+
"public",
|
|
28
|
+
"private"
|
|
29
|
+
],
|
|
30
|
+
"x-enum-title": [
|
|
31
|
+
"Disabled",
|
|
32
|
+
"Agent Owner + All Users",
|
|
33
|
+
"Agent Owner Only"
|
|
34
|
+
],
|
|
35
|
+
"description": "Convert images to detailed text descriptions using OpenAI's GPT-4o model",
|
|
36
|
+
"default": "private"
|
|
37
|
+
},
|
|
38
|
+
"dalle_image_generation": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"title": "Image Generation by DALL-E",
|
|
41
|
+
"enum": [
|
|
42
|
+
"disabled",
|
|
43
|
+
"public",
|
|
44
|
+
"private"
|
|
45
|
+
],
|
|
46
|
+
"x-enum-title": [
|
|
47
|
+
"Disabled",
|
|
48
|
+
"Agent Owner + All Users",
|
|
49
|
+
"Agent Owner Only"
|
|
50
|
+
],
|
|
51
|
+
"description": "Generate images using OpenAI's DALL-E model based on text prompts",
|
|
52
|
+
"default": "disabled"
|
|
53
|
+
},
|
|
54
|
+
"gpt_image_generation": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"title": "Image Generation by GPT",
|
|
57
|
+
"enum": [
|
|
58
|
+
"disabled",
|
|
59
|
+
"public",
|
|
60
|
+
"private"
|
|
61
|
+
],
|
|
62
|
+
"x-enum-title": [
|
|
63
|
+
"Disabled",
|
|
64
|
+
"Agent Owner + All Users",
|
|
65
|
+
"Agent Owner Only"
|
|
66
|
+
],
|
|
67
|
+
"description": "Generate images using OpenAI's GPT-Image-1 model based on text prompts",
|
|
68
|
+
"default": "private"
|
|
69
|
+
},
|
|
70
|
+
"gpt_image_to_image": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"title": "Image Editing by GPT",
|
|
73
|
+
"enum": [
|
|
74
|
+
"disabled",
|
|
75
|
+
"public",
|
|
76
|
+
"private"
|
|
77
|
+
],
|
|
78
|
+
"x-enum-title": [
|
|
79
|
+
"Disabled",
|
|
80
|
+
"Agent Owner + All Users",
|
|
81
|
+
"Agent Owner Only"
|
|
82
|
+
],
|
|
83
|
+
"description": "Edit images using OpenAI's GPT-Image-1 model based on text prompts",
|
|
84
|
+
"default": "private"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"description": "States for each OpenAI skill (disabled, public, or private)"
|
|
88
|
+
},
|
|
89
|
+
"api_key_provider": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"title": "API Key Provider",
|
|
92
|
+
"description": "Provider of the API key",
|
|
93
|
+
"enum": [
|
|
94
|
+
"platform",
|
|
95
|
+
"agent_owner"
|
|
96
|
+
],
|
|
97
|
+
"x-enum-title": [
|
|
98
|
+
"Nation Hosted",
|
|
99
|
+
"Owner Provided"
|
|
100
|
+
],
|
|
101
|
+
"default": "platform"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"required": [
|
|
105
|
+
"states",
|
|
106
|
+
"enabled"
|
|
107
|
+
],
|
|
108
|
+
"if": {
|
|
109
|
+
"properties": {
|
|
110
|
+
"api_key_provider": {
|
|
111
|
+
"const": "agent_owner"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"then": {
|
|
116
|
+
"properties": {
|
|
117
|
+
"api_key": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"title": "API Key",
|
|
120
|
+
"x-link": "[Get your API key](https://platform.openai.com/)",
|
|
121
|
+
"x-sensitive": true,
|
|
122
|
+
"description": "OpenAI API key for authentication"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"if": {
|
|
126
|
+
"properties": {
|
|
127
|
+
"enabled": {
|
|
128
|
+
"const": true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"then": {
|
|
133
|
+
"required": [
|
|
134
|
+
"api_key"
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"additionalProperties": true
|
|
139
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Blockchain Portfolio Analysis Skills
|
|
2
|
+
|
|
3
|
+
A set of skills for analyzing blockchain wallets and portfolios through the Moralis API, allowing agents to retrieve wallet data, token balances, and investment performance across various blockchain networks.
|
|
4
|
+
|
|
5
|
+
## Available Skills
|
|
6
|
+
|
|
7
|
+
| Skill | Description | Endpoint | Example Prompts |
|
|
8
|
+
|-------|-------------|----------|----------------|
|
|
9
|
+
| `wallet_history` | Gets transaction history including sends, receives, token transfers | `GET /wallets/{address}/history` | "Show me all transactions for wallet 0x123..." <br> "What are the recent transactions for this ETH address?" |
|
|
10
|
+
| `token_balances` | Gets token balances and USD value with spam filtering options | `GET /wallets/{address}/tokens` | "What tokens does wallet 0x123 hold?" <br> "Show me token balances with USD values for this address" |
|
|
11
|
+
| `wallet_approvals` | Lists active ERC20 token approvals to identify spend permissions | `GET /wallets/{address}/approvals` | "Check what contracts have approval to spend from wallet 0x123" <br> "Has this wallet approved any token spending?" |
|
|
12
|
+
| `wallet_swaps` | Lists all swap-related transactions (buy/sell) for trade analysis | `GET /wallets/{address}/swaps` | "Show me all token swaps for wallet 0x123" <br> "What trading activity has this address performed?" |
|
|
13
|
+
| `wallet_net_worth` | Calculates total wallet value in USD across multiple chains | `GET /wallets/{address}/net-worth` | "What's the total value of wallet 0x123?" <br> "Calculate the net worth of this address across all chains" |
|
|
14
|
+
| `wallet_profitability_summary` | Provides overview of wallet profitability metrics | `GET /wallets/{address}/profitability/summary` | "Is wallet 0x123 profitable overall?" <br> "Give me a summary of trading performance for this address" |
|
|
15
|
+
| `wallet_profitability` | Delivers detailed profitability by token with buy/sell prices | `GET /wallets/{address}/profitability` | "Show detailed profit/loss for each token in wallet 0x123" <br> "What's the cost basis of tokens in this wallet?" |
|
|
16
|
+
| `wallet_stats` | Provides statistics about NFTs, collections, and transactions | `GET /wallets/{address}/stats` | "How many NFTs does wallet 0x123 have?" <br> "Give me stats about this wallet's activity" |
|
|
17
|
+
| `wallet_defi_positions` | Get DeFi positions for a wallet | `GET /wallets/{address}/defi/positions` | "What DeFi positions does my wallet have?" <br> "Show my liquidity positions." |
|
|
18
|
+
| `wallet_nfts` | Get NFTs owned by a wallet address | `GET /{address}/nft` | "What NFTs does wallet 0x123 own?" <br> "Show me all the NFTs in my wallet." |
|
|
19
|
+
|
|
20
|
+
All endpoints use the base URL defined in `constants.py`: `https://deep-index.moralis.io/api/v2.2`
|
|
21
|
+
|
|
22
|
+
## Migration Note
|
|
23
|
+
|
|
24
|
+
If you're currently using the Moralis module, simply update your configuration by changing `moralis:` to `portfolio:`. All functionality remains identical.
|
|
25
|
+
|
|
26
|
+
## Authentication
|
|
27
|
+
|
|
28
|
+
All API requests include the Moralis API key in the header:
|
|
29
|
+
```
|
|
30
|
+
X-API-Key: YOUR_MORALIS_API_KEY
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Supported Chains
|
|
34
|
+
|
|
35
|
+
These skills support various EVM-compatible chains:
|
|
36
|
+
- Ethereum (eth)
|
|
37
|
+
- Polygon (polygon)
|
|
38
|
+
- Binance Smart Chain (bsc)
|
|
39
|
+
- Avalanche (avalanche)
|
|
40
|
+
- Arbitrum (arbitrum)
|
|
41
|
+
- Optimism (optimism)
|
|
42
|
+
- Base (base)
|
|
43
|
+
|
|
44
|
+
## Key Parameters
|
|
45
|
+
|
|
46
|
+
Most endpoints support these common parameters:
|
|
47
|
+
- `chain`: The chain to query (default: eth)
|
|
48
|
+
- `limit`: Number of results per page
|
|
49
|
+
- `cursor`: Pagination cursor for subsequent requests
|
|
50
|
+
|
|
51
|
+
## Getting a Moralis API Key
|
|
52
|
+
|
|
53
|
+
1. Create an account at [Moralis.io](https://moralis.io/)
|
|
54
|
+
2. Navigate to the API Keys section in your dashboard
|
|
55
|
+
3. Create a new key with appropriate permissions
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Portfolio skills for blockchain wallet analysis."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
6
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
7
|
+
from intentkit.skills.base import SkillConfig, SkillState
|
|
8
|
+
from intentkit.skills.portfolio.base import PortfolioBaseTool
|
|
9
|
+
from intentkit.skills.portfolio.token_balances import TokenBalances
|
|
10
|
+
from intentkit.skills.portfolio.wallet_approvals import WalletApprovals
|
|
11
|
+
from intentkit.skills.portfolio.wallet_defi_positions import WalletDefiPositions
|
|
12
|
+
from intentkit.skills.portfolio.wallet_history import WalletHistory
|
|
13
|
+
from intentkit.skills.portfolio.wallet_net_worth import WalletNetWorth
|
|
14
|
+
from intentkit.skills.portfolio.wallet_nfts import WalletNFTs
|
|
15
|
+
from intentkit.skills.portfolio.wallet_profitability import WalletProfitability
|
|
16
|
+
from intentkit.skills.portfolio.wallet_profitability_summary import (
|
|
17
|
+
WalletProfitabilitySummary,
|
|
18
|
+
)
|
|
19
|
+
from intentkit.skills.portfolio.wallet_stats import WalletStats
|
|
20
|
+
from intentkit.skills.portfolio.wallet_swaps import WalletSwaps
|
|
21
|
+
|
|
22
|
+
# Cache skills at the system level, because they are stateless
|
|
23
|
+
_cache: dict[str, PortfolioBaseTool] = {}
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SkillStates(TypedDict):
|
|
29
|
+
"""State configurations for Portfolio skills."""
|
|
30
|
+
|
|
31
|
+
wallet_history: SkillState
|
|
32
|
+
token_balances: SkillState
|
|
33
|
+
wallet_approvals: SkillState
|
|
34
|
+
wallet_swaps: SkillState
|
|
35
|
+
wallet_net_worth: SkillState
|
|
36
|
+
wallet_profitability_summary: SkillState
|
|
37
|
+
wallet_profitability: SkillState
|
|
38
|
+
wallet_stats: SkillState
|
|
39
|
+
wallet_defi_positions: SkillState
|
|
40
|
+
wallet_nfts: SkillState
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Config(SkillConfig):
|
|
44
|
+
"""Configuration for Portfolio blockchain analysis skills."""
|
|
45
|
+
|
|
46
|
+
states: SkillStates
|
|
47
|
+
api_key: str
|
|
48
|
+
api_key_provider: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def get_skills(
|
|
52
|
+
config: "Config",
|
|
53
|
+
is_private: bool,
|
|
54
|
+
store: SkillStoreABC,
|
|
55
|
+
**_,
|
|
56
|
+
) -> list[PortfolioBaseTool]:
|
|
57
|
+
"""Get all Portfolio blockchain analysis skills.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
config: The configuration for Portfolio skills.
|
|
61
|
+
is_private: Whether to include private skills.
|
|
62
|
+
store: The skill store for persisting data.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
A list of Portfolio blockchain analysis skills.
|
|
66
|
+
"""
|
|
67
|
+
available_skills = []
|
|
68
|
+
|
|
69
|
+
# Include skills based on their state
|
|
70
|
+
for skill_name, state in config["states"].items():
|
|
71
|
+
if state == "disabled":
|
|
72
|
+
continue
|
|
73
|
+
elif state == "public" or (state == "private" and is_private):
|
|
74
|
+
available_skills.append(skill_name)
|
|
75
|
+
|
|
76
|
+
# Get each skill using the cached getter
|
|
77
|
+
result = []
|
|
78
|
+
for name in available_skills:
|
|
79
|
+
skill = get_portfolio_skill(name, store)
|
|
80
|
+
if skill:
|
|
81
|
+
result.append(skill)
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_portfolio_skill(
|
|
86
|
+
name: str,
|
|
87
|
+
store: SkillStoreABC,
|
|
88
|
+
) -> PortfolioBaseTool:
|
|
89
|
+
"""Get a portfolio skill by name."""
|
|
90
|
+
if name == "wallet_history":
|
|
91
|
+
if name not in _cache:
|
|
92
|
+
_cache[name] = WalletHistory(
|
|
93
|
+
skill_store=store,
|
|
94
|
+
)
|
|
95
|
+
return _cache[name]
|
|
96
|
+
elif name == "token_balances":
|
|
97
|
+
if name not in _cache:
|
|
98
|
+
_cache[name] = TokenBalances(
|
|
99
|
+
skill_store=store,
|
|
100
|
+
)
|
|
101
|
+
return _cache[name]
|
|
102
|
+
elif name == "wallet_approvals":
|
|
103
|
+
if name not in _cache:
|
|
104
|
+
_cache[name] = WalletApprovals(
|
|
105
|
+
skill_store=store,
|
|
106
|
+
)
|
|
107
|
+
return _cache[name]
|
|
108
|
+
elif name == "wallet_swaps":
|
|
109
|
+
if name not in _cache:
|
|
110
|
+
_cache[name] = WalletSwaps(
|
|
111
|
+
skill_store=store,
|
|
112
|
+
)
|
|
113
|
+
return _cache[name]
|
|
114
|
+
elif name == "wallet_net_worth":
|
|
115
|
+
if name not in _cache:
|
|
116
|
+
_cache[name] = WalletNetWorth(
|
|
117
|
+
skill_store=store,
|
|
118
|
+
)
|
|
119
|
+
return _cache[name]
|
|
120
|
+
elif name == "wallet_profitability_summary":
|
|
121
|
+
if name not in _cache:
|
|
122
|
+
_cache[name] = WalletProfitabilitySummary(
|
|
123
|
+
skill_store=store,
|
|
124
|
+
)
|
|
125
|
+
return _cache[name]
|
|
126
|
+
elif name == "wallet_profitability":
|
|
127
|
+
if name not in _cache:
|
|
128
|
+
_cache[name] = WalletProfitability(
|
|
129
|
+
skill_store=store,
|
|
130
|
+
)
|
|
131
|
+
return _cache[name]
|
|
132
|
+
elif name == "wallet_stats":
|
|
133
|
+
if name not in _cache:
|
|
134
|
+
_cache[name] = WalletStats(
|
|
135
|
+
skill_store=store,
|
|
136
|
+
)
|
|
137
|
+
return _cache[name]
|
|
138
|
+
elif name == "wallet_defi_positions":
|
|
139
|
+
if name not in _cache:
|
|
140
|
+
_cache[name] = WalletDefiPositions(
|
|
141
|
+
skill_store=store,
|
|
142
|
+
)
|
|
143
|
+
return _cache[name]
|
|
144
|
+
elif name == "wallet_nfts":
|
|
145
|
+
if name not in _cache:
|
|
146
|
+
_cache[name] = WalletNFTs(
|
|
147
|
+
skill_store=store,
|
|
148
|
+
)
|
|
149
|
+
return _cache[name]
|
|
150
|
+
else:
|
|
151
|
+
raise ValueError(f"Unknown portfolio skill: {name}")
|