intentkit 0.7.5.dev3__py3-none-any.whl → 0.8.34.dev7__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.
- intentkit/MANIFEST.in +14 -0
- intentkit/README.md +88 -0
- intentkit/__init__.py +6 -4
- intentkit/abstracts/agent.py +4 -5
- intentkit/abstracts/engine.py +5 -5
- intentkit/abstracts/graph.py +15 -8
- intentkit/abstracts/skill.py +6 -144
- intentkit/abstracts/twitter.py +4 -5
- intentkit/clients/__init__.py +9 -2
- intentkit/clients/cdp.py +129 -153
- intentkit/{utils → clients}/s3.py +109 -34
- intentkit/clients/twitter.py +83 -62
- intentkit/clients/web3.py +4 -7
- intentkit/config/config.py +123 -90
- intentkit/core/account_checking.py +802 -0
- intentkit/core/agent.py +313 -498
- intentkit/core/asset.py +267 -0
- intentkit/core/chat.py +5 -3
- intentkit/core/client.py +1 -1
- intentkit/core/credit.py +49 -41
- intentkit/core/draft.py +201 -0
- intentkit/core/draft_chat.py +118 -0
- intentkit/core/engine.py +378 -287
- intentkit/core/manager/__init__.py +25 -0
- intentkit/core/manager/engine.py +220 -0
- intentkit/core/manager/service.py +172 -0
- intentkit/core/manager/skills.py +178 -0
- intentkit/core/middleware.py +231 -0
- intentkit/core/prompt.py +74 -114
- intentkit/core/scheduler.py +143 -0
- intentkit/core/statistics.py +168 -0
- intentkit/models/agent.py +931 -518
- intentkit/models/agent_data.py +165 -106
- intentkit/models/agent_schema.json +38 -251
- intentkit/models/app_setting.py +15 -13
- intentkit/models/chat.py +86 -140
- intentkit/models/credit.py +182 -162
- intentkit/models/db.py +42 -23
- intentkit/models/db_mig.py +120 -3
- intentkit/models/draft.py +222 -0
- intentkit/models/llm.csv +31 -0
- intentkit/models/llm.py +262 -370
- intentkit/models/redis.py +6 -4
- intentkit/models/skill.py +222 -101
- intentkit/models/skills.csv +173 -0
- intentkit/models/team.py +189 -0
- intentkit/models/user.py +103 -31
- intentkit/skills/acolyt/__init__.py +2 -9
- intentkit/skills/acolyt/ask.py +3 -4
- intentkit/skills/acolyt/base.py +4 -9
- intentkit/skills/acolyt/schema.json +4 -3
- intentkit/skills/aixbt/__init__.py +2 -13
- intentkit/skills/aixbt/base.py +1 -7
- intentkit/skills/aixbt/projects.py +14 -15
- intentkit/skills/aixbt/schema.json +4 -4
- intentkit/skills/allora/__init__.py +2 -9
- intentkit/skills/allora/base.py +4 -9
- intentkit/skills/allora/price.py +3 -4
- intentkit/skills/allora/schema.json +3 -2
- intentkit/skills/base.py +241 -41
- intentkit/skills/basename/__init__.py +51 -0
- intentkit/skills/basename/base.py +11 -0
- intentkit/skills/basename/basename.svg +11 -0
- intentkit/skills/basename/schema.json +58 -0
- intentkit/skills/carv/__init__.py +115 -121
- intentkit/skills/carv/base.py +184 -185
- intentkit/skills/carv/fetch_news.py +3 -3
- intentkit/skills/carv/onchain_query.py +4 -4
- intentkit/skills/carv/schema.json +134 -137
- intentkit/skills/carv/token_info_and_price.py +6 -6
- intentkit/skills/casino/__init__.py +4 -15
- intentkit/skills/casino/base.py +1 -7
- intentkit/skills/casino/deck_draw.py +5 -8
- intentkit/skills/casino/deck_shuffle.py +6 -6
- intentkit/skills/casino/dice_roll.py +2 -4
- intentkit/skills/casino/schema.json +0 -1
- intentkit/skills/cdp/__init__.py +22 -84
- intentkit/skills/cdp/base.py +1 -7
- intentkit/skills/cdp/schema.json +11 -314
- intentkit/skills/chainlist/__init__.py +2 -7
- intentkit/skills/chainlist/base.py +1 -7
- intentkit/skills/chainlist/chain_lookup.py +18 -18
- intentkit/skills/chainlist/schema.json +3 -5
- intentkit/skills/common/__init__.py +2 -9
- intentkit/skills/common/base.py +1 -7
- intentkit/skills/common/current_time.py +1 -2
- intentkit/skills/common/schema.json +2 -2
- intentkit/skills/cookiefun/__init__.py +6 -9
- intentkit/skills/cookiefun/base.py +2 -7
- intentkit/skills/cookiefun/get_account_details.py +7 -7
- intentkit/skills/cookiefun/get_account_feed.py +19 -19
- intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
- intentkit/skills/cookiefun/get_sectors.py +3 -3
- intentkit/skills/cookiefun/schema.json +1 -3
- intentkit/skills/cookiefun/search_accounts.py +9 -9
- intentkit/skills/cryptocompare/__init__.py +7 -24
- intentkit/skills/cryptocompare/api.py +2 -3
- intentkit/skills/cryptocompare/base.py +10 -24
- intentkit/skills/cryptocompare/fetch_news.py +4 -5
- intentkit/skills/cryptocompare/fetch_price.py +6 -7
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
- intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
- intentkit/skills/cryptocompare/schema.json +3 -3
- intentkit/skills/cryptopanic/__init__.py +7 -10
- intentkit/skills/cryptopanic/base.py +51 -55
- intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
- intentkit/skills/cryptopanic/schema.json +105 -103
- intentkit/skills/dapplooker/__init__.py +2 -9
- intentkit/skills/dapplooker/base.py +4 -9
- intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
- intentkit/skills/dapplooker/schema.json +3 -5
- intentkit/skills/defillama/__init__.py +24 -74
- intentkit/skills/defillama/api.py +6 -9
- intentkit/skills/defillama/base.py +8 -19
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_block.py +6 -8
- intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
- intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
- intentkit/skills/defillama/config/chains.py +1 -3
- intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
- intentkit/skills/defillama/schema.json +5 -1
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
- intentkit/skills/defillama/tests/api_integration.test.py +1 -1
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
- intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
- intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
- intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
- intentkit/skills/defillama/yields/fetch_pools.py +26 -30
- intentkit/skills/dexscreener/__init__.py +97 -102
- intentkit/skills/dexscreener/base.py +125 -130
- intentkit/skills/dexscreener/get_pair_info.py +4 -5
- intentkit/skills/dexscreener/get_token_pairs.py +4 -5
- intentkit/skills/dexscreener/get_tokens_info.py +7 -8
- intentkit/skills/dexscreener/model/search_token_response.py +80 -82
- intentkit/skills/dexscreener/schema.json +91 -93
- intentkit/skills/dexscreener/search_token.py +182 -184
- intentkit/skills/dexscreener/utils.py +15 -14
- intentkit/skills/dune_analytics/__init__.py +7 -9
- intentkit/skills/dune_analytics/base.py +48 -52
- intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
- intentkit/skills/dune_analytics/schema.json +104 -99
- intentkit/skills/elfa/__init__.py +5 -18
- intentkit/skills/elfa/base.py +10 -14
- intentkit/skills/elfa/mention.py +19 -21
- intentkit/skills/elfa/schema.json +3 -2
- intentkit/skills/elfa/stats.py +4 -4
- intentkit/skills/elfa/tokens.py +12 -12
- intentkit/skills/elfa/utils.py +26 -28
- intentkit/skills/enso/__init__.py +11 -31
- intentkit/skills/enso/base.py +54 -35
- intentkit/skills/enso/best_yield.py +16 -24
- intentkit/skills/enso/networks.py +6 -11
- intentkit/skills/enso/prices.py +11 -13
- intentkit/skills/enso/route.py +34 -38
- intentkit/skills/enso/schema.json +3 -2
- intentkit/skills/enso/tokens.py +29 -38
- intentkit/skills/enso/wallet.py +76 -191
- intentkit/skills/erc20/__init__.py +50 -0
- intentkit/skills/erc20/base.py +11 -0
- intentkit/skills/erc20/erc20.svg +5 -0
- intentkit/skills/erc20/schema.json +74 -0
- intentkit/skills/erc721/__init__.py +53 -0
- intentkit/skills/erc721/base.py +11 -0
- intentkit/skills/erc721/erc721.svg +5 -0
- intentkit/skills/erc721/schema.json +90 -0
- intentkit/skills/firecrawl/__init__.py +5 -18
- intentkit/skills/firecrawl/base.py +4 -9
- intentkit/skills/firecrawl/clear.py +4 -8
- intentkit/skills/firecrawl/crawl.py +19 -19
- intentkit/skills/firecrawl/query.py +4 -3
- intentkit/skills/firecrawl/schema.json +2 -6
- intentkit/skills/firecrawl/scrape.py +17 -22
- intentkit/skills/firecrawl/utils.py +50 -42
- intentkit/skills/github/__init__.py +2 -7
- intentkit/skills/github/base.py +1 -7
- intentkit/skills/github/github_search.py +1 -2
- intentkit/skills/github/schema.json +3 -4
- intentkit/skills/heurist/__init__.py +8 -27
- intentkit/skills/heurist/base.py +4 -9
- intentkit/skills/heurist/image_generation_animagine_xl.py +13 -15
- intentkit/skills/heurist/image_generation_arthemy_comics.py +13 -15
- intentkit/skills/heurist/image_generation_arthemy_real.py +13 -15
- intentkit/skills/heurist/image_generation_braindance.py +13 -15
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +13 -15
- intentkit/skills/heurist/image_generation_flux_1_dev.py +13 -15
- intentkit/skills/heurist/image_generation_sdxl.py +13 -15
- intentkit/skills/heurist/schema.json +2 -2
- intentkit/skills/http/__init__.py +4 -15
- intentkit/skills/http/base.py +1 -7
- intentkit/skills/http/get.py +21 -16
- intentkit/skills/http/post.py +23 -18
- intentkit/skills/http/put.py +23 -18
- intentkit/skills/http/schema.json +4 -5
- intentkit/skills/lifi/__init__.py +8 -13
- intentkit/skills/lifi/base.py +3 -9
- intentkit/skills/lifi/schema.json +17 -8
- intentkit/skills/lifi/token_execute.py +150 -60
- intentkit/skills/lifi/token_quote.py +8 -10
- intentkit/skills/lifi/utils.py +104 -51
- intentkit/skills/moralis/__init__.py +6 -10
- intentkit/skills/moralis/api.py +6 -7
- intentkit/skills/moralis/base.py +5 -10
- intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
- intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
- intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
- intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
- intentkit/skills/moralis/schema.json +7 -2
- intentkit/skills/morpho/__init__.py +52 -0
- intentkit/skills/morpho/base.py +11 -0
- intentkit/skills/morpho/morpho.svg +12 -0
- intentkit/skills/morpho/schema.json +73 -0
- intentkit/skills/nation/__init__.py +4 -9
- intentkit/skills/nation/base.py +5 -10
- intentkit/skills/nation/nft_check.py +3 -4
- intentkit/skills/nation/schema.json +4 -3
- intentkit/skills/onchain.py +30 -0
- intentkit/skills/openai/__init__.py +17 -18
- intentkit/skills/openai/base.py +10 -14
- intentkit/skills/openai/dalle_image_generation.py +4 -9
- intentkit/skills/openai/gpt_avatar_generator.py +102 -0
- intentkit/skills/openai/gpt_image_generation.py +5 -9
- intentkit/skills/openai/gpt_image_mini_generator.py +92 -0
- intentkit/skills/openai/gpt_image_to_image.py +5 -9
- intentkit/skills/openai/image_to_text.py +3 -7
- intentkit/skills/openai/schema.json +34 -3
- intentkit/skills/portfolio/__init__.py +11 -35
- intentkit/skills/portfolio/base.py +33 -19
- intentkit/skills/portfolio/schema.json +3 -5
- intentkit/skills/portfolio/token_balances.py +21 -21
- intentkit/skills/portfolio/wallet_approvals.py +17 -18
- intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
- intentkit/skills/portfolio/wallet_history.py +31 -31
- intentkit/skills/portfolio/wallet_net_worth.py +13 -13
- intentkit/skills/portfolio/wallet_nfts.py +19 -19
- intentkit/skills/portfolio/wallet_profitability.py +18 -18
- intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
- intentkit/skills/portfolio/wallet_stats.py +3 -3
- intentkit/skills/portfolio/wallet_swaps.py +19 -19
- intentkit/skills/pyth/__init__.py +50 -0
- intentkit/skills/pyth/base.py +11 -0
- intentkit/skills/pyth/pyth.svg +6 -0
- intentkit/skills/pyth/schema.json +75 -0
- intentkit/skills/skills.toml +36 -0
- intentkit/skills/slack/__init__.py +5 -17
- intentkit/skills/slack/base.py +3 -9
- intentkit/skills/slack/get_channel.py +8 -8
- intentkit/skills/slack/get_message.py +9 -9
- intentkit/skills/slack/schedule_message.py +5 -5
- intentkit/skills/slack/schema.json +2 -2
- intentkit/skills/slack/send_message.py +3 -5
- intentkit/skills/supabase/__init__.py +7 -23
- intentkit/skills/supabase/base.py +1 -7
- intentkit/skills/supabase/delete_data.py +4 -4
- intentkit/skills/supabase/fetch_data.py +12 -12
- intentkit/skills/supabase/insert_data.py +4 -4
- intentkit/skills/supabase/invoke_function.py +6 -6
- intentkit/skills/supabase/schema.json +2 -3
- intentkit/skills/supabase/update_data.py +6 -6
- intentkit/skills/supabase/upsert_data.py +4 -4
- intentkit/skills/superfluid/__init__.py +53 -0
- intentkit/skills/superfluid/base.py +11 -0
- intentkit/skills/superfluid/schema.json +89 -0
- intentkit/skills/superfluid/superfluid.svg +6 -0
- intentkit/skills/system/__init__.py +7 -24
- intentkit/skills/system/add_autonomous_task.py +10 -12
- intentkit/skills/system/delete_autonomous_task.py +2 -2
- intentkit/skills/system/edit_autonomous_task.py +14 -18
- intentkit/skills/system/list_autonomous_tasks.py +3 -5
- intentkit/skills/system/read_agent_api_key.py +6 -4
- intentkit/skills/system/regenerate_agent_api_key.py +6 -4
- intentkit/skills/system/schema.json +6 -8
- intentkit/skills/tavily/__init__.py +3 -12
- intentkit/skills/tavily/base.py +4 -9
- intentkit/skills/tavily/schema.json +3 -5
- intentkit/skills/tavily/tavily_extract.py +2 -4
- intentkit/skills/tavily/tavily_search.py +4 -6
- intentkit/skills/token/__init__.py +5 -10
- intentkit/skills/token/base.py +7 -11
- intentkit/skills/token/erc20_transfers.py +19 -19
- intentkit/skills/token/schema.json +3 -6
- intentkit/skills/token/token_analytics.py +3 -3
- intentkit/skills/token/token_price.py +13 -13
- intentkit/skills/token/token_search.py +9 -9
- intentkit/skills/twitter/__init__.py +11 -35
- intentkit/skills/twitter/base.py +22 -34
- intentkit/skills/twitter/follow_user.py +2 -6
- intentkit/skills/twitter/get_mentions.py +5 -12
- intentkit/skills/twitter/get_timeline.py +4 -12
- intentkit/skills/twitter/get_user_by_username.py +2 -6
- intentkit/skills/twitter/get_user_tweets.py +5 -13
- intentkit/skills/twitter/like_tweet.py +2 -6
- intentkit/skills/twitter/post_tweet.py +6 -9
- intentkit/skills/twitter/reply_tweet.py +6 -9
- intentkit/skills/twitter/retweet.py +2 -6
- intentkit/skills/twitter/schema.json +1 -0
- intentkit/skills/twitter/search_tweets.py +4 -12
- intentkit/skills/unrealspeech/__init__.py +2 -7
- intentkit/skills/unrealspeech/base.py +2 -8
- intentkit/skills/unrealspeech/schema.json +2 -5
- intentkit/skills/unrealspeech/text_to_speech.py +8 -8
- intentkit/skills/venice_audio/__init__.py +98 -106
- intentkit/skills/venice_audio/base.py +117 -121
- intentkit/skills/venice_audio/input.py +41 -41
- intentkit/skills/venice_audio/schema.json +151 -152
- intentkit/skills/venice_audio/venice_audio.py +38 -21
- intentkit/skills/venice_image/__init__.py +147 -154
- intentkit/skills/venice_image/api.py +138 -138
- intentkit/skills/venice_image/base.py +185 -192
- intentkit/skills/venice_image/config.py +33 -35
- intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
- intentkit/skills/venice_image/image_generation/image_generation_base.py +11 -10
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
- intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
- intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
- intentkit/skills/venice_image/schema.json +267 -267
- intentkit/skills/venice_image/utils.py +77 -78
- intentkit/skills/web_scraper/__init__.py +5 -18
- intentkit/skills/web_scraper/base.py +21 -7
- intentkit/skills/web_scraper/document_indexer.py +7 -6
- intentkit/skills/web_scraper/schema.json +2 -6
- intentkit/skills/web_scraper/scrape_and_index.py +15 -15
- intentkit/skills/web_scraper/utils.py +62 -63
- intentkit/skills/web_scraper/website_indexer.py +17 -19
- intentkit/skills/weth/__init__.py +49 -0
- intentkit/skills/weth/base.py +11 -0
- intentkit/skills/weth/schema.json +58 -0
- intentkit/skills/weth/weth.svg +6 -0
- intentkit/skills/wow/__init__.py +51 -0
- intentkit/skills/wow/base.py +11 -0
- intentkit/skills/wow/schema.json +89 -0
- intentkit/skills/wow/wow.svg +7 -0
- intentkit/skills/x402/__init__.py +58 -0
- intentkit/skills/x402/base.py +99 -0
- intentkit/skills/x402/http_request.py +117 -0
- intentkit/skills/x402/schema.json +40 -0
- intentkit/skills/x402/x402.webp +0 -0
- intentkit/skills/xmtp/__init__.py +4 -15
- intentkit/skills/xmtp/base.py +5 -5
- intentkit/skills/xmtp/price.py +7 -6
- intentkit/skills/xmtp/schema.json +69 -71
- intentkit/skills/xmtp/swap.py +6 -8
- intentkit/skills/xmtp/transfer.py +4 -6
- intentkit/utils/__init__.py +4 -0
- intentkit/utils/chain.py +198 -96
- intentkit/utils/ens.py +135 -0
- intentkit/utils/error.py +5 -2
- intentkit/utils/logging.py +9 -11
- intentkit/utils/schema.py +100 -0
- intentkit/utils/slack_alert.py +8 -8
- intentkit/utils/tx.py +16 -8
- intentkit/uv.lock +3377 -0
- {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/METADATA +13 -15
- intentkit-0.8.34.dev7.dist-info/RECORD +478 -0
- intentkit-0.8.34.dev7.dist-info/licenses/LICENSE +21 -0
- intentkit/core/node.py +0 -215
- intentkit/models/conversation.py +0 -286
- intentkit/models/generator.py +0 -347
- intentkit/skills/cdp/get_balance.py +0 -110
- intentkit/skills/cdp/swap.py +0 -121
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +0 -511
- intentkit-0.7.5.dev3.dist-info/RECORD +0 -424
- {intentkit-0.7.5.dev3.dist-info/licenses → intentkit}/LICENSE +0 -0
- {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/WHEEL +0 -0
intentkit/models/agent.py
CHANGED
|
@@ -1,43 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import hashlib
|
|
2
4
|
import json
|
|
3
5
|
import logging
|
|
4
6
|
import re
|
|
5
7
|
import textwrap
|
|
6
|
-
from datetime import
|
|
8
|
+
from datetime import UTC, datetime
|
|
7
9
|
from decimal import Decimal
|
|
8
10
|
from pathlib import Path
|
|
9
|
-
from typing import Annotated, Any,
|
|
11
|
+
from typing import Annotated, Any, Literal
|
|
10
12
|
|
|
11
13
|
import jsonref
|
|
12
14
|
import yaml
|
|
13
15
|
from cron_validator import CronValidator
|
|
14
16
|
from epyxid import XID
|
|
15
|
-
from fastapi import HTTPException
|
|
16
|
-
from intentkit.models.agent_data import AgentData
|
|
17
|
-
from intentkit.models.base import Base
|
|
18
|
-
from intentkit.models.credit import CreditAccount
|
|
19
|
-
from intentkit.models.db import get_session
|
|
20
|
-
from intentkit.models.llm import LLMModelInfo, LLMModelInfoTable, LLMProvider
|
|
21
|
-
from intentkit.models.skill import SkillTable
|
|
22
17
|
from pydantic import BaseModel, ConfigDict, field_validator
|
|
23
18
|
from pydantic import Field as PydanticField
|
|
24
19
|
from pydantic.json_schema import SkipJsonSchema
|
|
25
|
-
from
|
|
26
|
-
|
|
27
|
-
Column,
|
|
28
|
-
DateTime,
|
|
29
|
-
Float,
|
|
30
|
-
Numeric,
|
|
31
|
-
String,
|
|
32
|
-
func,
|
|
33
|
-
select,
|
|
34
|
-
)
|
|
20
|
+
from pydantic.main import IncEx
|
|
21
|
+
from sqlalchemy import Boolean, DateTime, Float, Numeric, String, func, select
|
|
35
22
|
from sqlalchemy.dialects.postgresql import JSON, JSONB
|
|
23
|
+
from sqlalchemy.exc import IntegrityError
|
|
36
24
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
25
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
26
|
+
|
|
27
|
+
from intentkit.models.agent_data import AgentData
|
|
28
|
+
from intentkit.models.base import Base
|
|
29
|
+
from intentkit.models.credit import CreditAccount
|
|
30
|
+
from intentkit.models.db import get_session
|
|
31
|
+
from intentkit.models.llm import LLMModelInfo, LLMProvider
|
|
32
|
+
from intentkit.models.skill import Skill
|
|
33
|
+
from intentkit.utils.ens import resolve_ens_to_address
|
|
34
|
+
from intentkit.utils.error import IntentKitAPIError
|
|
37
35
|
|
|
38
36
|
logger = logging.getLogger(__name__)
|
|
39
37
|
|
|
40
38
|
|
|
39
|
+
ENS_NAME_PATTERN = re.compile(
|
|
40
|
+
r"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:eth|base\.eth)$",
|
|
41
|
+
re.IGNORECASE,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
class AgentAutonomous(BaseModel):
|
|
42
46
|
"""Autonomous agent configuration."""
|
|
43
47
|
|
|
@@ -55,7 +59,7 @@ class AgentAutonomous(BaseModel):
|
|
|
55
59
|
),
|
|
56
60
|
]
|
|
57
61
|
name: Annotated[
|
|
58
|
-
|
|
62
|
+
str | None,
|
|
59
63
|
PydanticField(
|
|
60
64
|
default=None,
|
|
61
65
|
description="Display name of the autonomous configuration",
|
|
@@ -66,7 +70,7 @@ class AgentAutonomous(BaseModel):
|
|
|
66
70
|
),
|
|
67
71
|
]
|
|
68
72
|
description: Annotated[
|
|
69
|
-
|
|
73
|
+
str | None,
|
|
70
74
|
PydanticField(
|
|
71
75
|
default=None,
|
|
72
76
|
description="Description of the autonomous configuration",
|
|
@@ -77,7 +81,7 @@ class AgentAutonomous(BaseModel):
|
|
|
77
81
|
),
|
|
78
82
|
]
|
|
79
83
|
minutes: Annotated[
|
|
80
|
-
|
|
84
|
+
int | None,
|
|
81
85
|
PydanticField(
|
|
82
86
|
default=None,
|
|
83
87
|
description="Interval in minutes between operations, mutually exclusive with cron",
|
|
@@ -87,7 +91,7 @@ class AgentAutonomous(BaseModel):
|
|
|
87
91
|
),
|
|
88
92
|
]
|
|
89
93
|
cron: Annotated[
|
|
90
|
-
|
|
94
|
+
str | None,
|
|
91
95
|
PydanticField(
|
|
92
96
|
default=None,
|
|
93
97
|
description="Cron expression for scheduling operations, mutually exclusive with minutes",
|
|
@@ -107,7 +111,7 @@ class AgentAutonomous(BaseModel):
|
|
|
107
111
|
),
|
|
108
112
|
]
|
|
109
113
|
enabled: Annotated[
|
|
110
|
-
|
|
114
|
+
bool | None,
|
|
111
115
|
PydanticField(
|
|
112
116
|
default=False,
|
|
113
117
|
description="Whether the autonomous configuration is enabled",
|
|
@@ -140,7 +144,7 @@ class AgentExample(BaseModel):
|
|
|
140
144
|
description="Name of the example",
|
|
141
145
|
max_length=50,
|
|
142
146
|
json_schema_extra={
|
|
143
|
-
"x-
|
|
147
|
+
"x-placeholder": "Add a name for the example",
|
|
144
148
|
},
|
|
145
149
|
),
|
|
146
150
|
]
|
|
@@ -150,7 +154,7 @@ class AgentExample(BaseModel):
|
|
|
150
154
|
description="Description of the example",
|
|
151
155
|
max_length=200,
|
|
152
156
|
json_schema_extra={
|
|
153
|
-
"x-
|
|
157
|
+
"x-placeholder": "Add a short description for the example",
|
|
154
158
|
},
|
|
155
159
|
),
|
|
156
160
|
]
|
|
@@ -160,7 +164,7 @@ class AgentExample(BaseModel):
|
|
|
160
164
|
description="Example prompt",
|
|
161
165
|
max_length=2000,
|
|
162
166
|
json_schema_extra={
|
|
163
|
-
"x-
|
|
167
|
+
"x-placeholder": "The prompt will be sent to the agent",
|
|
164
168
|
},
|
|
165
169
|
),
|
|
166
170
|
]
|
|
@@ -172,62 +176,62 @@ class AgentUserInputColumns:
|
|
|
172
176
|
__abstract__ = True
|
|
173
177
|
|
|
174
178
|
# Basic information fields from AgentCore
|
|
175
|
-
name =
|
|
179
|
+
name: Mapped[str | None] = mapped_column(
|
|
176
180
|
String,
|
|
177
181
|
nullable=True,
|
|
178
182
|
comment="Display name of the agent",
|
|
179
183
|
)
|
|
180
|
-
picture =
|
|
184
|
+
picture: Mapped[str | None] = mapped_column(
|
|
181
185
|
String,
|
|
182
186
|
nullable=True,
|
|
183
187
|
comment="Picture of the agent",
|
|
184
188
|
)
|
|
185
|
-
purpose =
|
|
189
|
+
purpose: Mapped[str | None] = mapped_column(
|
|
186
190
|
String,
|
|
187
191
|
nullable=True,
|
|
188
192
|
comment="Purpose or role of the agent",
|
|
189
193
|
)
|
|
190
|
-
personality =
|
|
194
|
+
personality: Mapped[str | None] = mapped_column(
|
|
191
195
|
String,
|
|
192
196
|
nullable=True,
|
|
193
197
|
comment="Personality traits of the agent",
|
|
194
198
|
)
|
|
195
|
-
principles =
|
|
199
|
+
principles: Mapped[str | None] = mapped_column(
|
|
196
200
|
String,
|
|
197
201
|
nullable=True,
|
|
198
202
|
comment="Principles or values of the agent",
|
|
199
203
|
)
|
|
200
204
|
|
|
201
205
|
# AI model configuration fields from AgentCore
|
|
202
|
-
model =
|
|
206
|
+
model: Mapped[str | None] = mapped_column(
|
|
203
207
|
String,
|
|
204
208
|
nullable=True,
|
|
205
209
|
default="gpt-5-mini",
|
|
206
210
|
comment="AI model identifier to be used by this agent for processing requests. Available models: gpt-4o, gpt-4o-mini, deepseek-chat, deepseek-reasoner, grok-2, eternalai",
|
|
207
211
|
)
|
|
208
|
-
prompt =
|
|
212
|
+
prompt: Mapped[str | None] = mapped_column(
|
|
209
213
|
String,
|
|
210
214
|
nullable=True,
|
|
211
215
|
comment="Base system prompt that defines the agent's behavior and capabilities",
|
|
212
216
|
)
|
|
213
|
-
prompt_append =
|
|
217
|
+
prompt_append: Mapped[str | None] = mapped_column(
|
|
214
218
|
String,
|
|
215
219
|
nullable=True,
|
|
216
220
|
comment="Additional system prompt that has higher priority than the base prompt",
|
|
217
221
|
)
|
|
218
|
-
temperature =
|
|
222
|
+
temperature: Mapped[float | None] = mapped_column(
|
|
219
223
|
Float,
|
|
220
224
|
nullable=True,
|
|
221
225
|
default=0.7,
|
|
222
226
|
comment="Controls response randomness (0.0~2.0). Higher values increase creativity but may reduce accuracy. For rigorous tasks, use lower values.",
|
|
223
227
|
)
|
|
224
|
-
frequency_penalty =
|
|
228
|
+
frequency_penalty: Mapped[float | None] = mapped_column(
|
|
225
229
|
Float,
|
|
226
230
|
nullable=True,
|
|
227
231
|
default=0.0,
|
|
228
232
|
comment="Controls repetition in responses (-2.0~2.0). Higher values reduce repetition, lower values allow more repetition.",
|
|
229
233
|
)
|
|
230
|
-
presence_penalty =
|
|
234
|
+
presence_penalty: Mapped[float | None] = mapped_column(
|
|
231
235
|
Float,
|
|
232
236
|
nullable=True,
|
|
233
237
|
default=0.0,
|
|
@@ -235,17 +239,17 @@ class AgentUserInputColumns:
|
|
|
235
239
|
)
|
|
236
240
|
|
|
237
241
|
# Wallet and network configuration fields from AgentCore
|
|
238
|
-
wallet_provider =
|
|
242
|
+
wallet_provider: Mapped[str | None] = mapped_column(
|
|
239
243
|
String,
|
|
240
244
|
nullable=True,
|
|
241
245
|
comment="Provider of the agent's wallet",
|
|
242
246
|
)
|
|
243
|
-
readonly_wallet_address =
|
|
247
|
+
readonly_wallet_address: Mapped[str | None] = mapped_column(
|
|
244
248
|
String,
|
|
245
249
|
nullable=True,
|
|
246
250
|
comment="Readonly wallet address of the agent",
|
|
247
251
|
)
|
|
248
|
-
network_id =
|
|
252
|
+
network_id: Mapped[str | None] = mapped_column(
|
|
249
253
|
String,
|
|
250
254
|
nullable=True,
|
|
251
255
|
default="base-mainnet",
|
|
@@ -253,41 +257,52 @@ class AgentUserInputColumns:
|
|
|
253
257
|
)
|
|
254
258
|
|
|
255
259
|
# Skills configuration from AgentCore
|
|
256
|
-
skills =
|
|
260
|
+
skills: Mapped[dict[str, Any] | None] = mapped_column(
|
|
257
261
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
258
262
|
nullable=True,
|
|
259
263
|
comment="Dict of skills and their corresponding configurations",
|
|
260
264
|
)
|
|
261
265
|
|
|
262
266
|
# Additional fields from AgentUserInput
|
|
263
|
-
short_term_memory_strategy =
|
|
267
|
+
short_term_memory_strategy: Mapped[str | None] = mapped_column(
|
|
264
268
|
String,
|
|
265
269
|
nullable=True,
|
|
266
270
|
default="trim",
|
|
267
271
|
comment="Strategy for managing short-term memory when context limit is reached. 'trim' removes oldest messages, 'summarize' creates summaries.",
|
|
268
272
|
)
|
|
269
|
-
autonomous =
|
|
273
|
+
autonomous: Mapped[dict[str, Any] | None] = mapped_column(
|
|
270
274
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
271
275
|
nullable=True,
|
|
272
276
|
comment="Autonomous agent configurations",
|
|
273
277
|
)
|
|
274
|
-
telegram_entrypoint_enabled =
|
|
278
|
+
telegram_entrypoint_enabled: Mapped[bool | None] = mapped_column(
|
|
275
279
|
Boolean,
|
|
276
280
|
nullable=True,
|
|
277
281
|
default=False,
|
|
278
282
|
comment="Whether the agent can receive events from Telegram",
|
|
279
283
|
)
|
|
280
|
-
telegram_entrypoint_prompt =
|
|
284
|
+
telegram_entrypoint_prompt: Mapped[str | None] = mapped_column(
|
|
281
285
|
String,
|
|
282
286
|
nullable=True,
|
|
283
287
|
comment="Extra prompt for telegram entrypoint",
|
|
284
288
|
)
|
|
285
|
-
telegram_config =
|
|
289
|
+
telegram_config: Mapped[dict[str, Any] | None] = mapped_column(
|
|
286
290
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
287
291
|
nullable=True,
|
|
288
292
|
comment="Telegram integration configuration settings",
|
|
289
293
|
)
|
|
290
|
-
|
|
294
|
+
discord_entrypoint_enabled: Mapped[bool | None] = mapped_column(
|
|
295
|
+
Boolean,
|
|
296
|
+
nullable=True,
|
|
297
|
+
default=False,
|
|
298
|
+
comment="Whether the agent can receive events from Discord",
|
|
299
|
+
)
|
|
300
|
+
discord_config: Mapped[dict[str, Any] | None] = mapped_column(
|
|
301
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
302
|
+
nullable=True,
|
|
303
|
+
comment="Discord integration configuration settings",
|
|
304
|
+
)
|
|
305
|
+
xmtp_entrypoint_prompt: Mapped[str | None] = mapped_column(
|
|
291
306
|
String,
|
|
292
307
|
nullable=True,
|
|
293
308
|
comment="Extra prompt for xmtp entrypoint",
|
|
@@ -299,112 +314,137 @@ class AgentTable(Base, AgentUserInputColumns):
|
|
|
299
314
|
|
|
300
315
|
__tablename__ = "agents"
|
|
301
316
|
|
|
302
|
-
id =
|
|
317
|
+
id: Mapped[str] = mapped_column(
|
|
303
318
|
String,
|
|
304
319
|
primary_key=True,
|
|
305
320
|
comment="Unique identifier for the agent. Must be URL-safe, containing only lowercase letters, numbers, and hyphens",
|
|
306
321
|
)
|
|
307
|
-
slug =
|
|
322
|
+
slug: Mapped[str | None] = mapped_column(
|
|
308
323
|
String,
|
|
309
324
|
nullable=True,
|
|
310
325
|
comment="Slug of the agent, used for URL generation",
|
|
311
326
|
)
|
|
312
|
-
owner =
|
|
327
|
+
owner: Mapped[str | None] = mapped_column(
|
|
313
328
|
String,
|
|
314
329
|
nullable=True,
|
|
315
330
|
comment="Owner identifier of the agent, used for access control",
|
|
316
331
|
)
|
|
317
|
-
|
|
332
|
+
team_id: Mapped[str | None] = mapped_column(
|
|
333
|
+
String,
|
|
334
|
+
nullable=True,
|
|
335
|
+
comment="Team identifier of the agent, used for access control",
|
|
336
|
+
)
|
|
337
|
+
upstream_id: Mapped[str | None] = mapped_column(
|
|
318
338
|
String,
|
|
319
339
|
index=True,
|
|
320
340
|
nullable=True,
|
|
321
341
|
comment="Upstream reference ID for idempotent operations",
|
|
322
342
|
)
|
|
323
|
-
upstream_extra =
|
|
343
|
+
upstream_extra: Mapped[dict[str, Any] | None] = mapped_column(
|
|
324
344
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
325
345
|
nullable=True,
|
|
326
346
|
comment="Additional data store for upstream use",
|
|
327
347
|
)
|
|
328
|
-
version =
|
|
348
|
+
version: Mapped[str | None] = mapped_column(
|
|
329
349
|
String,
|
|
330
350
|
nullable=True,
|
|
331
351
|
comment="Version hash of the agent",
|
|
332
352
|
)
|
|
333
|
-
statistics =
|
|
353
|
+
statistics: Mapped[dict[str, Any] | None] = mapped_column(
|
|
334
354
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
335
355
|
nullable=True,
|
|
336
356
|
comment="Statistics of the agent, update every 1 hour for query",
|
|
337
357
|
)
|
|
338
|
-
assets =
|
|
358
|
+
assets: Mapped[dict[str, Any] | None] = mapped_column(
|
|
339
359
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
340
360
|
nullable=True,
|
|
341
361
|
comment="Assets of the agent, update every 1 hour for query",
|
|
342
362
|
)
|
|
343
|
-
account_snapshot =
|
|
363
|
+
account_snapshot: Mapped[dict[str, Any] | None] = mapped_column(
|
|
344
364
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
345
365
|
nullable=True,
|
|
346
366
|
comment="Account snapshot of the agent, update every 1 hour for query",
|
|
347
367
|
)
|
|
348
|
-
extra =
|
|
368
|
+
extra: Mapped[dict[str, Any] | None] = mapped_column(
|
|
349
369
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
350
370
|
nullable=True,
|
|
351
371
|
comment="Other helper data fields for query, come from agent and agent data",
|
|
352
372
|
)
|
|
353
373
|
|
|
354
374
|
# Fields moved from AgentUserInputColumns that are no longer in AgentUserInput
|
|
355
|
-
description =
|
|
375
|
+
description: Mapped[str | None] = mapped_column(
|
|
356
376
|
String,
|
|
357
377
|
nullable=True,
|
|
358
378
|
comment="Description of the agent, for public view, not contained in prompt",
|
|
359
379
|
)
|
|
360
|
-
external_website =
|
|
380
|
+
external_website: Mapped[str | None] = mapped_column(
|
|
361
381
|
String,
|
|
362
382
|
nullable=True,
|
|
363
383
|
comment="Link of external website of the agent, if you have one",
|
|
364
384
|
)
|
|
365
|
-
ticker =
|
|
385
|
+
ticker: Mapped[str | None] = mapped_column(
|
|
366
386
|
String,
|
|
367
387
|
nullable=True,
|
|
368
388
|
comment="Ticker symbol of the agent",
|
|
369
389
|
)
|
|
370
|
-
token_address =
|
|
390
|
+
token_address: Mapped[str | None] = mapped_column(
|
|
371
391
|
String,
|
|
372
392
|
nullable=True,
|
|
373
393
|
comment="Token address of the agent",
|
|
374
394
|
)
|
|
375
|
-
token_pool =
|
|
395
|
+
token_pool: Mapped[str | None] = mapped_column(
|
|
376
396
|
String,
|
|
377
397
|
nullable=True,
|
|
378
398
|
comment="Pool of the agent token",
|
|
379
399
|
)
|
|
380
|
-
fee_percentage =
|
|
400
|
+
fee_percentage: Mapped[Decimal | None] = mapped_column(
|
|
381
401
|
Numeric(22, 4),
|
|
382
402
|
nullable=True,
|
|
383
403
|
comment="Fee percentage of the agent",
|
|
384
404
|
)
|
|
385
|
-
example_intro =
|
|
405
|
+
example_intro: Mapped[str | None] = mapped_column(
|
|
386
406
|
String,
|
|
387
407
|
nullable=True,
|
|
388
408
|
comment="Introduction for example interactions",
|
|
389
409
|
)
|
|
390
|
-
examples =
|
|
410
|
+
examples: Mapped[dict[str, Any] | None] = mapped_column(
|
|
391
411
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
392
412
|
nullable=True,
|
|
393
413
|
comment="List of example interactions for the agent",
|
|
394
414
|
)
|
|
415
|
+
public_extra: Mapped[dict[str, Any] | None] = mapped_column(
|
|
416
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
417
|
+
nullable=True,
|
|
418
|
+
comment="Public extra data of the agent",
|
|
419
|
+
)
|
|
420
|
+
deployed_at: Mapped[datetime | None] = mapped_column(
|
|
421
|
+
DateTime(timezone=True),
|
|
422
|
+
nullable=True,
|
|
423
|
+
comment="Timestamp when the agent was deployed",
|
|
424
|
+
)
|
|
425
|
+
public_info_updated_at: Mapped[datetime | None] = mapped_column(
|
|
426
|
+
DateTime(timezone=True),
|
|
427
|
+
nullable=True,
|
|
428
|
+
comment="Timestamp when the agent public info was last updated",
|
|
429
|
+
)
|
|
430
|
+
x402_price: Mapped[float | None] = mapped_column(
|
|
431
|
+
Float,
|
|
432
|
+
nullable=True,
|
|
433
|
+
comment="Price of the x402 request",
|
|
434
|
+
)
|
|
395
435
|
|
|
396
436
|
# auto timestamp
|
|
397
|
-
created_at =
|
|
437
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
398
438
|
DateTime(timezone=True),
|
|
399
439
|
nullable=False,
|
|
400
440
|
server_default=func.now(),
|
|
401
441
|
comment="Timestamp when the agent was created",
|
|
402
442
|
)
|
|
403
|
-
updated_at =
|
|
443
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
404
444
|
DateTime(timezone=True),
|
|
405
445
|
nullable=False,
|
|
406
446
|
server_default=func.now(),
|
|
407
|
-
onupdate=lambda: datetime.now(
|
|
447
|
+
onupdate=lambda: datetime.now(UTC),
|
|
408
448
|
comment="Timestamp when the agent was last updated",
|
|
409
449
|
)
|
|
410
450
|
|
|
@@ -413,75 +453,43 @@ class AgentCore(BaseModel):
|
|
|
413
453
|
"""Agent core model."""
|
|
414
454
|
|
|
415
455
|
name: Annotated[
|
|
416
|
-
|
|
456
|
+
str | None,
|
|
417
457
|
PydanticField(
|
|
418
458
|
default=None,
|
|
419
459
|
title="Name",
|
|
420
460
|
description="Display name of the agent",
|
|
421
461
|
max_length=50,
|
|
422
|
-
json_schema_extra={
|
|
423
|
-
"x-group": "basic",
|
|
424
|
-
"x-placeholder": "Name your agent",
|
|
425
|
-
},
|
|
426
462
|
),
|
|
427
463
|
]
|
|
428
464
|
picture: Annotated[
|
|
429
|
-
|
|
465
|
+
str | None,
|
|
430
466
|
PydanticField(
|
|
431
467
|
default=None,
|
|
432
|
-
description="
|
|
433
|
-
json_schema_extra={
|
|
434
|
-
"x-group": "experimental",
|
|
435
|
-
"x-placeholder": "Upload a picture of your agent",
|
|
436
|
-
},
|
|
468
|
+
description="Avatar of the agent",
|
|
437
469
|
),
|
|
438
470
|
]
|
|
439
471
|
purpose: Annotated[
|
|
440
|
-
|
|
472
|
+
str | None,
|
|
441
473
|
PydanticField(
|
|
442
474
|
default=None,
|
|
443
475
|
description="Purpose or role of the agent",
|
|
444
476
|
max_length=20000,
|
|
445
|
-
json_schema_extra={
|
|
446
|
-
"x-group": "basic",
|
|
447
|
-
"x-placeholder": "Enter agent purpose, it will be a part of the system prompt",
|
|
448
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
449
|
-
"errorMessage": {
|
|
450
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
477
|
),
|
|
454
478
|
]
|
|
455
479
|
personality: Annotated[
|
|
456
|
-
|
|
480
|
+
str | None,
|
|
457
481
|
PydanticField(
|
|
458
482
|
default=None,
|
|
459
483
|
description="Personality traits of the agent",
|
|
460
484
|
max_length=20000,
|
|
461
|
-
json_schema_extra={
|
|
462
|
-
"x-group": "basic",
|
|
463
|
-
"x-placeholder": "Enter agent personality, it will be a part of the system prompt",
|
|
464
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
465
|
-
"errorMessage": {
|
|
466
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
485
|
),
|
|
470
486
|
]
|
|
471
487
|
principles: Annotated[
|
|
472
|
-
|
|
488
|
+
str | None,
|
|
473
489
|
PydanticField(
|
|
474
490
|
default=None,
|
|
475
491
|
description="Principles or values of the agent",
|
|
476
492
|
max_length=20000,
|
|
477
|
-
json_schema_extra={
|
|
478
|
-
"x-group": "basic",
|
|
479
|
-
"x-placeholder": "Enter agent principles, it will be a part of the system prompt",
|
|
480
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
481
|
-
"errorMessage": {
|
|
482
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
493
|
),
|
|
486
494
|
]
|
|
487
495
|
# AI part
|
|
@@ -489,128 +497,86 @@ class AgentCore(BaseModel):
|
|
|
489
497
|
str,
|
|
490
498
|
PydanticField(
|
|
491
499
|
default="gpt-5-mini",
|
|
492
|
-
description="
|
|
493
|
-
json_schema_extra={
|
|
494
|
-
"x-group": "ai",
|
|
495
|
-
},
|
|
500
|
+
description="LLM of the agent",
|
|
496
501
|
),
|
|
497
502
|
]
|
|
498
503
|
prompt: Annotated[
|
|
499
|
-
|
|
504
|
+
str | None,
|
|
500
505
|
PydanticField(
|
|
501
506
|
default=None,
|
|
502
507
|
description="Base system prompt that defines the agent's behavior and capabilities",
|
|
503
508
|
max_length=20000,
|
|
504
|
-
json_schema_extra={
|
|
505
|
-
"x-group": "ai",
|
|
506
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
507
|
-
"errorMessage": {
|
|
508
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
509
|
),
|
|
512
510
|
]
|
|
513
511
|
prompt_append: Annotated[
|
|
514
|
-
|
|
512
|
+
str | None,
|
|
515
513
|
PydanticField(
|
|
516
514
|
default=None,
|
|
517
515
|
description="Additional system prompt that has higher priority than the base prompt",
|
|
518
516
|
max_length=20000,
|
|
519
|
-
json_schema_extra={
|
|
520
|
-
"x-group": "ai",
|
|
521
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
522
|
-
"errorMessage": {
|
|
523
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
524
|
-
},
|
|
525
|
-
},
|
|
526
517
|
),
|
|
527
518
|
]
|
|
528
519
|
temperature: Annotated[
|
|
529
|
-
|
|
520
|
+
float | None,
|
|
530
521
|
PydanticField(
|
|
531
522
|
default=0.7,
|
|
532
523
|
description="The randomness of the generated results is such that the higher the number, the more creative the results will be. However, this also makes them wilder and increases the likelihood of errors. For creative tasks, you can adjust it to above 1, but for rigorous tasks, such as quantitative trading, it's advisable to set it lower, around 0.2. (0.0~2.0)",
|
|
533
524
|
ge=0.0,
|
|
534
525
|
le=2.0,
|
|
535
|
-
json_schema_extra={
|
|
536
|
-
"x-group": "ai",
|
|
537
|
-
},
|
|
538
526
|
),
|
|
539
527
|
]
|
|
540
528
|
frequency_penalty: Annotated[
|
|
541
|
-
|
|
529
|
+
float | None,
|
|
542
530
|
PydanticField(
|
|
543
531
|
default=0.0,
|
|
544
532
|
description="The frequency penalty is a measure of how much the AI is allowed to repeat itself. A lower value means the AI is more likely to repeat previous responses, while a higher value means the AI is more likely to generate new content. For creative tasks, you can adjust it to 1 or a bit higher. (-2.0~2.0)",
|
|
545
533
|
ge=-2.0,
|
|
546
534
|
le=2.0,
|
|
547
|
-
json_schema_extra={
|
|
548
|
-
"x-group": "ai",
|
|
549
|
-
},
|
|
550
535
|
),
|
|
551
536
|
]
|
|
552
537
|
presence_penalty: Annotated[
|
|
553
|
-
|
|
538
|
+
float | None,
|
|
554
539
|
PydanticField(
|
|
555
540
|
default=0.0,
|
|
556
541
|
description="The presence penalty is a measure of how much the AI is allowed to deviate from the topic. A higher value means the AI is more likely to deviate from the topic, while a lower value means the AI is more likely to follow the topic. For creative tasks, you can adjust it to 1 or a bit higher. (-2.0~2.0)",
|
|
557
542
|
ge=-2.0,
|
|
558
543
|
le=2.0,
|
|
559
|
-
json_schema_extra={
|
|
560
|
-
"x-group": "ai",
|
|
561
|
-
},
|
|
562
544
|
),
|
|
563
545
|
]
|
|
564
546
|
wallet_provider: Annotated[
|
|
565
|
-
|
|
547
|
+
Literal["cdp", "readonly", "none"] | None,
|
|
566
548
|
PydanticField(
|
|
567
|
-
default=
|
|
549
|
+
default=None,
|
|
568
550
|
description="Provider of the agent's wallet",
|
|
569
|
-
json_schema_extra={
|
|
570
|
-
"x-group": "onchain",
|
|
571
|
-
},
|
|
572
551
|
),
|
|
573
552
|
]
|
|
574
553
|
readonly_wallet_address: Annotated[
|
|
575
|
-
|
|
554
|
+
str | None,
|
|
576
555
|
PydanticField(
|
|
577
556
|
default=None,
|
|
578
557
|
description="Address of the agent's wallet, only used when wallet_provider is readonly. Agent will not be able to sign transactions.",
|
|
579
558
|
),
|
|
580
559
|
]
|
|
581
560
|
network_id: Annotated[
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"arbitrum-sepolia",
|
|
592
|
-
"optimism-mainnet",
|
|
593
|
-
"optimism-sepolia",
|
|
594
|
-
"solana",
|
|
595
|
-
]
|
|
596
|
-
],
|
|
561
|
+
Literal[
|
|
562
|
+
"base-mainnet",
|
|
563
|
+
"ethereum-mainnet",
|
|
564
|
+
"polygon-mainnet",
|
|
565
|
+
"arbitrum-mainnet",
|
|
566
|
+
"optimism-mainnet",
|
|
567
|
+
"solana",
|
|
568
|
+
]
|
|
569
|
+
| None,
|
|
597
570
|
PydanticField(
|
|
598
571
|
default="base-mainnet",
|
|
599
572
|
description="Network identifier",
|
|
600
|
-
json_schema_extra={
|
|
601
|
-
"x-group": "onchain",
|
|
602
|
-
},
|
|
603
573
|
),
|
|
604
574
|
]
|
|
605
575
|
skills: Annotated[
|
|
606
|
-
|
|
576
|
+
dict[str, Any] | None,
|
|
607
577
|
PydanticField(
|
|
608
578
|
default=None,
|
|
609
579
|
description="Dict of skills and their corresponding configurations",
|
|
610
|
-
json_schema_extra={
|
|
611
|
-
"x-group": "skills",
|
|
612
|
-
"x-inline": True,
|
|
613
|
-
},
|
|
614
580
|
),
|
|
615
581
|
]
|
|
616
582
|
|
|
@@ -647,21 +613,21 @@ class AgentUserInput(AgentCore):
|
|
|
647
613
|
model_config = ConfigDict(
|
|
648
614
|
title="AgentUserInput",
|
|
649
615
|
from_attributes=True,
|
|
616
|
+
json_schema_extra={
|
|
617
|
+
"required": ["name"],
|
|
618
|
+
},
|
|
650
619
|
)
|
|
651
620
|
|
|
652
621
|
short_term_memory_strategy: Annotated[
|
|
653
|
-
|
|
622
|
+
Literal["trim", "summarize"] | None,
|
|
654
623
|
PydanticField(
|
|
655
624
|
default="trim",
|
|
656
625
|
description="Strategy for managing short-term memory when context limit is reached. 'trim' removes oldest messages, 'summarize' creates summaries.",
|
|
657
|
-
json_schema_extra={
|
|
658
|
-
"x-group": "ai",
|
|
659
|
-
},
|
|
660
626
|
),
|
|
661
627
|
]
|
|
662
628
|
# autonomous mode
|
|
663
629
|
autonomous: Annotated[
|
|
664
|
-
|
|
630
|
+
list[AgentAutonomous] | None,
|
|
665
631
|
PydanticField(
|
|
666
632
|
default=None,
|
|
667
633
|
description=(
|
|
@@ -678,53 +644,57 @@ class AgentUserInput(AgentCore):
|
|
|
678
644
|
" prompt: |-\n"
|
|
679
645
|
" Say hi [sequence], use number for sequence.\n"
|
|
680
646
|
),
|
|
681
|
-
json_schema_extra={
|
|
682
|
-
"x-group": "autonomous",
|
|
683
|
-
"x-inline": True,
|
|
684
|
-
},
|
|
685
647
|
),
|
|
686
648
|
]
|
|
687
649
|
# if telegram_entrypoint_enabled, the telegram_entrypoint_enabled will be enabled, telegram_config will be checked
|
|
688
650
|
telegram_entrypoint_enabled: Annotated[
|
|
689
|
-
|
|
651
|
+
bool | None,
|
|
690
652
|
PydanticField(
|
|
691
653
|
default=False,
|
|
692
654
|
description="Whether the agent can play telegram bot",
|
|
693
|
-
json_schema_extra={
|
|
694
|
-
"x-group": "entrypoint",
|
|
695
|
-
},
|
|
696
655
|
),
|
|
697
656
|
]
|
|
698
657
|
telegram_entrypoint_prompt: Annotated[
|
|
699
|
-
|
|
658
|
+
str | None,
|
|
700
659
|
PydanticField(
|
|
701
660
|
default=None,
|
|
702
661
|
description="Extra prompt for telegram entrypoint",
|
|
703
662
|
max_length=10000,
|
|
663
|
+
),
|
|
664
|
+
]
|
|
665
|
+
telegram_config: Annotated[
|
|
666
|
+
dict[str, object] | None,
|
|
667
|
+
PydanticField(
|
|
668
|
+
default=None,
|
|
669
|
+
description="Telegram integration configuration settings",
|
|
670
|
+
),
|
|
671
|
+
]
|
|
672
|
+
discord_entrypoint_enabled: Annotated[
|
|
673
|
+
bool | None,
|
|
674
|
+
PydanticField(
|
|
675
|
+
default=False,
|
|
676
|
+
description="Whether the agent can play discord bot",
|
|
704
677
|
json_schema_extra={
|
|
705
678
|
"x-group": "entrypoint",
|
|
706
679
|
},
|
|
707
680
|
),
|
|
708
681
|
]
|
|
709
|
-
|
|
710
|
-
|
|
682
|
+
discord_config: Annotated[
|
|
683
|
+
dict | None,
|
|
711
684
|
PydanticField(
|
|
712
685
|
default=None,
|
|
713
|
-
description="
|
|
686
|
+
description="Discord integration configuration settings including token, whitelists, and behavior settings",
|
|
714
687
|
json_schema_extra={
|
|
715
688
|
"x-group": "entrypoint",
|
|
716
689
|
},
|
|
717
690
|
),
|
|
718
691
|
]
|
|
719
692
|
xmtp_entrypoint_prompt: Annotated[
|
|
720
|
-
|
|
693
|
+
str | None,
|
|
721
694
|
PydanticField(
|
|
722
695
|
default=None,
|
|
723
696
|
description="Extra prompt for xmtp entrypoint, xmtp support is in beta",
|
|
724
697
|
max_length=10000,
|
|
725
|
-
json_schema_extra={
|
|
726
|
-
"x-group": "entrypoint",
|
|
727
|
-
},
|
|
728
698
|
),
|
|
729
699
|
]
|
|
730
700
|
|
|
@@ -741,7 +711,7 @@ class AgentUpdate(AgentUserInput):
|
|
|
741
711
|
)
|
|
742
712
|
|
|
743
713
|
upstream_id: Annotated[
|
|
744
|
-
|
|
714
|
+
str | None,
|
|
745
715
|
PydanticField(
|
|
746
716
|
default=None,
|
|
747
717
|
description="External reference ID for idempotent operations",
|
|
@@ -749,7 +719,7 @@ class AgentUpdate(AgentUserInput):
|
|
|
749
719
|
),
|
|
750
720
|
]
|
|
751
721
|
upstream_extra: Annotated[
|
|
752
|
-
|
|
722
|
+
dict[str, Any] | None,
|
|
753
723
|
PydanticField(
|
|
754
724
|
default=None,
|
|
755
725
|
description="Additional data store for upstream use",
|
|
@@ -761,7 +731,7 @@ class AgentUpdate(AgentUserInput):
|
|
|
761
731
|
|
|
762
732
|
@field_validator("purpose", "personality", "principles", "prompt", "prompt_append")
|
|
763
733
|
@classmethod
|
|
764
|
-
def validate_no_level1_level2_headings(cls, v:
|
|
734
|
+
def validate_no_level1_level2_headings(cls, v: str | None) -> str | None:
|
|
765
735
|
"""Validate that the text doesn't contain level 1 or level 2 headings."""
|
|
766
736
|
if v is None:
|
|
767
737
|
return v
|
|
@@ -788,20 +758,25 @@ class AgentUpdate(AgentUserInput):
|
|
|
788
758
|
for autonomous_config in self.autonomous:
|
|
789
759
|
# Check that exactly one scheduling method is provided
|
|
790
760
|
if not autonomous_config.minutes and not autonomous_config.cron:
|
|
791
|
-
raise
|
|
792
|
-
status_code=400,
|
|
761
|
+
raise IntentKitAPIError(
|
|
762
|
+
status_code=400,
|
|
763
|
+
key="InvalidAutonomousConfig",
|
|
764
|
+
message="either minutes or cron must have a value",
|
|
793
765
|
)
|
|
794
766
|
|
|
795
767
|
if autonomous_config.minutes and autonomous_config.cron:
|
|
796
|
-
raise
|
|
797
|
-
status_code=400,
|
|
768
|
+
raise IntentKitAPIError(
|
|
769
|
+
status_code=400,
|
|
770
|
+
key="InvalidAutonomousConfig",
|
|
771
|
+
message="only one of minutes or cron can be set",
|
|
798
772
|
)
|
|
799
773
|
|
|
800
774
|
# Validate minimum interval of 5 minutes
|
|
801
775
|
if autonomous_config.minutes and autonomous_config.minutes < 5:
|
|
802
|
-
raise
|
|
776
|
+
raise IntentKitAPIError(
|
|
803
777
|
status_code=400,
|
|
804
|
-
|
|
778
|
+
key="InvalidAutonomousInterval",
|
|
779
|
+
message="The shortest execution interval is 5 minutes",
|
|
805
780
|
)
|
|
806
781
|
|
|
807
782
|
# Validate cron expression to ensure interval is at least 5 minutes
|
|
@@ -811,15 +786,18 @@ class AgentUpdate(AgentUserInput):
|
|
|
811
786
|
try:
|
|
812
787
|
CronValidator.parse(autonomous_config.cron)
|
|
813
788
|
except ValueError:
|
|
814
|
-
raise
|
|
789
|
+
raise IntentKitAPIError(
|
|
815
790
|
status_code=400,
|
|
816
|
-
|
|
791
|
+
key="InvalidCronExpression",
|
|
792
|
+
message=f"Invalid cron expression format: {autonomous_config.cron}",
|
|
817
793
|
)
|
|
818
794
|
|
|
819
795
|
parts = autonomous_config.cron.split()
|
|
820
796
|
if len(parts) < 5:
|
|
821
|
-
raise
|
|
822
|
-
status_code=400,
|
|
797
|
+
raise IntentKitAPIError(
|
|
798
|
+
status_code=400,
|
|
799
|
+
key="InvalidCronExpression",
|
|
800
|
+
message="Invalid cron expression format",
|
|
823
801
|
)
|
|
824
802
|
|
|
825
803
|
minute, hour, day_of_month, month, day_of_week = parts[:5]
|
|
@@ -827,25 +805,28 @@ class AgentUpdate(AgentUserInput):
|
|
|
827
805
|
# Check if minutes or hours have too frequent intervals
|
|
828
806
|
if "*" in minute and "*" in hour:
|
|
829
807
|
# If both minute and hour are wildcards, it would run every minute
|
|
830
|
-
raise
|
|
808
|
+
raise IntentKitAPIError(
|
|
831
809
|
status_code=400,
|
|
832
|
-
|
|
810
|
+
key="InvalidAutonomousInterval",
|
|
811
|
+
message="The shortest execution interval is 5 minutes",
|
|
833
812
|
)
|
|
834
813
|
|
|
835
814
|
if "/" in minute:
|
|
836
815
|
# Check step value in minute field (e.g., */15)
|
|
837
816
|
step = int(minute.split("/")[1])
|
|
838
817
|
if step < 5 and hour == "*":
|
|
839
|
-
raise
|
|
818
|
+
raise IntentKitAPIError(
|
|
840
819
|
status_code=400,
|
|
841
|
-
|
|
820
|
+
key="InvalidAutonomousInterval",
|
|
821
|
+
message="The shortest execution interval is 5 minutes",
|
|
842
822
|
)
|
|
843
823
|
|
|
844
824
|
# Check for comma-separated values or ranges that might result in multiple executions per hour
|
|
845
825
|
if ("," in minute or "-" in minute) and hour == "*":
|
|
846
|
-
raise
|
|
826
|
+
raise IntentKitAPIError(
|
|
847
827
|
status_code=400,
|
|
848
|
-
|
|
828
|
+
key="InvalidAutonomousInterval",
|
|
829
|
+
message="The shortest execution interval is 5 minutes",
|
|
849
830
|
)
|
|
850
831
|
|
|
851
832
|
# deprecated, use override instead
|
|
@@ -857,10 +838,16 @@ class AgentUpdate(AgentUserInput):
|
|
|
857
838
|
async with get_session() as db:
|
|
858
839
|
db_agent = await db.get(AgentTable, id)
|
|
859
840
|
if not db_agent:
|
|
860
|
-
raise
|
|
841
|
+
raise IntentKitAPIError(
|
|
842
|
+
status_code=404,
|
|
843
|
+
key="AgentNotFound",
|
|
844
|
+
message="Agent not found",
|
|
845
|
+
)
|
|
861
846
|
# update
|
|
862
847
|
for key, value in self.model_dump(exclude_unset=True).items():
|
|
863
848
|
setattr(db_agent, key, value)
|
|
849
|
+
db_agent.version = self.hash()
|
|
850
|
+
db_agent.deployed_at = func.now()
|
|
864
851
|
await db.commit()
|
|
865
852
|
await db.refresh(db_agent)
|
|
866
853
|
return Agent.model_validate(db_agent)
|
|
@@ -873,12 +860,17 @@ class AgentUpdate(AgentUserInput):
|
|
|
873
860
|
async with get_session() as db:
|
|
874
861
|
db_agent = await db.get(AgentTable, id)
|
|
875
862
|
if not db_agent:
|
|
876
|
-
raise
|
|
863
|
+
raise IntentKitAPIError(
|
|
864
|
+
status_code=404,
|
|
865
|
+
key="AgentNotFound",
|
|
866
|
+
message="Agent not found",
|
|
867
|
+
)
|
|
877
868
|
# update
|
|
878
869
|
for key, value in self.model_dump().items():
|
|
879
870
|
setattr(db_agent, key, value)
|
|
880
871
|
# version
|
|
881
872
|
db_agent.version = self.hash()
|
|
873
|
+
db_agent.deployed_at = func.now()
|
|
882
874
|
await db.commit()
|
|
883
875
|
await db.refresh(db_agent)
|
|
884
876
|
return Agent.model_validate(db_agent)
|
|
@@ -898,13 +890,21 @@ class AgentCreate(AgentUpdate):
|
|
|
898
890
|
),
|
|
899
891
|
]
|
|
900
892
|
owner: Annotated[
|
|
901
|
-
|
|
893
|
+
str | None,
|
|
902
894
|
PydanticField(
|
|
903
895
|
default=None,
|
|
904
896
|
description="Owner identifier of the agent, used for access control",
|
|
905
897
|
max_length=50,
|
|
906
898
|
),
|
|
907
899
|
]
|
|
900
|
+
team_id: Annotated[
|
|
901
|
+
str | None,
|
|
902
|
+
PydanticField(
|
|
903
|
+
default=None,
|
|
904
|
+
description="Team identifier of the agent",
|
|
905
|
+
max_length=50,
|
|
906
|
+
),
|
|
907
|
+
]
|
|
908
908
|
|
|
909
909
|
async def check_upstream_id(self) -> None:
|
|
910
910
|
if not self.upstream_id:
|
|
@@ -914,12 +914,13 @@ class AgentCreate(AgentUpdate):
|
|
|
914
914
|
select(AgentTable).where(AgentTable.upstream_id == self.upstream_id)
|
|
915
915
|
)
|
|
916
916
|
if existing:
|
|
917
|
-
raise
|
|
917
|
+
raise IntentKitAPIError(
|
|
918
918
|
status_code=400,
|
|
919
|
-
|
|
919
|
+
key="UpstreamIdConflict",
|
|
920
|
+
message="Upstream id already in use",
|
|
920
921
|
)
|
|
921
922
|
|
|
922
|
-
async def get_by_upstream_id(self) ->
|
|
923
|
+
async def get_by_upstream_id(self) -> Agent | None:
|
|
923
924
|
if not self.upstream_id:
|
|
924
925
|
return None
|
|
925
926
|
async with get_session() as db:
|
|
@@ -936,12 +937,21 @@ class AgentCreate(AgentUpdate):
|
|
|
936
937
|
self.validate_autonomous_schedule()
|
|
937
938
|
|
|
938
939
|
async with get_session() as db:
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
940
|
+
try:
|
|
941
|
+
db_agent = AgentTable(**self.model_dump())
|
|
942
|
+
db_agent.version = self.hash()
|
|
943
|
+
db_agent.deployed_at = func.now()
|
|
944
|
+
db.add(db_agent)
|
|
945
|
+
await db.commit()
|
|
946
|
+
await db.refresh(db_agent)
|
|
947
|
+
return Agent.model_validate(db_agent)
|
|
948
|
+
except IntegrityError:
|
|
949
|
+
await db.rollback()
|
|
950
|
+
raise IntentKitAPIError(
|
|
951
|
+
status_code=400,
|
|
952
|
+
key="AgentExists",
|
|
953
|
+
message=f"Agent with ID '{self.id}' already exists",
|
|
954
|
+
)
|
|
945
955
|
|
|
946
956
|
|
|
947
957
|
class AgentPublicInfo(BaseModel):
|
|
@@ -952,100 +962,188 @@ class AgentPublicInfo(BaseModel):
|
|
|
952
962
|
from_attributes=True,
|
|
953
963
|
)
|
|
954
964
|
|
|
965
|
+
x402_price: Annotated[
|
|
966
|
+
float | None,
|
|
967
|
+
PydanticField(
|
|
968
|
+
default=0.01,
|
|
969
|
+
description="Price($) of the x402 request",
|
|
970
|
+
ge=0.01,
|
|
971
|
+
le=1.0,
|
|
972
|
+
json_schema_extra={
|
|
973
|
+
"x-placeholder": "USDC price per request",
|
|
974
|
+
"x-step": 0.01,
|
|
975
|
+
},
|
|
976
|
+
),
|
|
977
|
+
]
|
|
955
978
|
description: Annotated[
|
|
956
|
-
|
|
979
|
+
str | None,
|
|
957
980
|
PydanticField(
|
|
958
981
|
default=None,
|
|
959
982
|
description="Description of the agent, for public view, not contained in prompt",
|
|
960
983
|
json_schema_extra={
|
|
961
|
-
"x-group": "basic",
|
|
962
984
|
"x-placeholder": "Introduce your agent",
|
|
963
985
|
},
|
|
964
986
|
),
|
|
965
987
|
]
|
|
966
988
|
external_website: Annotated[
|
|
967
|
-
|
|
989
|
+
str | None,
|
|
968
990
|
PydanticField(
|
|
969
991
|
default=None,
|
|
970
992
|
description="Link of external website of the agent, if you have one",
|
|
971
993
|
json_schema_extra={
|
|
972
|
-
"x-group": "basic",
|
|
973
994
|
"x-placeholder": "Enter agent external website url",
|
|
974
995
|
"format": "uri",
|
|
975
996
|
},
|
|
976
997
|
),
|
|
977
998
|
]
|
|
978
999
|
ticker: Annotated[
|
|
979
|
-
|
|
1000
|
+
str | None,
|
|
980
1001
|
PydanticField(
|
|
981
1002
|
default=None,
|
|
982
1003
|
description="Ticker symbol of the agent",
|
|
983
1004
|
max_length=10,
|
|
984
1005
|
min_length=1,
|
|
985
1006
|
json_schema_extra={
|
|
986
|
-
"x-group": "basic",
|
|
987
1007
|
"x-placeholder": "If one day, your agent has it's own token, what will it be?",
|
|
988
1008
|
},
|
|
989
1009
|
),
|
|
990
1010
|
]
|
|
991
1011
|
token_address: Annotated[
|
|
992
|
-
|
|
1012
|
+
str | None,
|
|
993
1013
|
PydanticField(
|
|
994
1014
|
default=None,
|
|
995
1015
|
description="Token address of the agent",
|
|
996
|
-
max_length=
|
|
1016
|
+
max_length=66,
|
|
997
1017
|
json_schema_extra={
|
|
998
|
-
"x-
|
|
999
|
-
"readOnly": True,
|
|
1018
|
+
"x-placeholder": "The contract address of the agent token",
|
|
1000
1019
|
},
|
|
1001
1020
|
),
|
|
1002
1021
|
]
|
|
1003
1022
|
token_pool: Annotated[
|
|
1004
|
-
|
|
1023
|
+
str | None,
|
|
1005
1024
|
PydanticField(
|
|
1006
1025
|
default=None,
|
|
1007
1026
|
description="Pool of the agent token",
|
|
1008
|
-
max_length=
|
|
1027
|
+
max_length=66,
|
|
1009
1028
|
json_schema_extra={
|
|
1010
|
-
"x-
|
|
1011
|
-
"readOnly": True,
|
|
1029
|
+
"x-placeholder": "The contract address of the agent token pool",
|
|
1012
1030
|
},
|
|
1013
1031
|
),
|
|
1014
1032
|
]
|
|
1015
1033
|
fee_percentage: Annotated[
|
|
1016
|
-
|
|
1034
|
+
Decimal | None,
|
|
1017
1035
|
PydanticField(
|
|
1018
1036
|
default=None,
|
|
1019
1037
|
description="Fee percentage of the agent",
|
|
1020
1038
|
ge=Decimal("0.0"),
|
|
1021
1039
|
json_schema_extra={
|
|
1022
|
-
"x-
|
|
1040
|
+
"x-placeholder": "Agent will charge service fee according to this ratio.",
|
|
1023
1041
|
},
|
|
1024
1042
|
),
|
|
1025
1043
|
]
|
|
1026
1044
|
example_intro: Annotated[
|
|
1027
|
-
|
|
1045
|
+
str | None,
|
|
1028
1046
|
PydanticField(
|
|
1029
1047
|
default=None,
|
|
1030
1048
|
description="Introduction of the example",
|
|
1031
1049
|
max_length=2000,
|
|
1032
1050
|
json_schema_extra={
|
|
1033
|
-
"x-
|
|
1051
|
+
"x-placeholder": "Add a short introduction in new chat",
|
|
1034
1052
|
},
|
|
1035
1053
|
),
|
|
1036
1054
|
]
|
|
1037
1055
|
examples: Annotated[
|
|
1038
|
-
|
|
1056
|
+
list[AgentExample] | None,
|
|
1039
1057
|
PydanticField(
|
|
1040
1058
|
default=None,
|
|
1041
1059
|
description="List of example prompts for the agent",
|
|
1042
1060
|
max_length=6,
|
|
1043
1061
|
json_schema_extra={
|
|
1044
|
-
"x-group": "examples",
|
|
1045
1062
|
"x-inline": True,
|
|
1046
1063
|
},
|
|
1047
1064
|
),
|
|
1048
1065
|
]
|
|
1066
|
+
public_extra: Annotated[
|
|
1067
|
+
dict[str, Any] | None,
|
|
1068
|
+
PydanticField(
|
|
1069
|
+
default=None,
|
|
1070
|
+
description="Public extra data of the agent",
|
|
1071
|
+
),
|
|
1072
|
+
]
|
|
1073
|
+
|
|
1074
|
+
async def update(self, agent_id: str) -> "Agent":
|
|
1075
|
+
"""Update agent public info with only the fields that are explicitly provided.
|
|
1076
|
+
|
|
1077
|
+
This method only updates fields that are explicitly set in this instance,
|
|
1078
|
+
leaving other fields unchanged. This is more efficient than override as it
|
|
1079
|
+
reduces context usage and minimizes the risk of accidentally changing fields.
|
|
1080
|
+
|
|
1081
|
+
Args:
|
|
1082
|
+
agent_id: The ID of the agent to update
|
|
1083
|
+
|
|
1084
|
+
Returns:
|
|
1085
|
+
The updated Agent instance
|
|
1086
|
+
"""
|
|
1087
|
+
async with get_session() as session:
|
|
1088
|
+
# Get the agent from database
|
|
1089
|
+
result = await session.execute(
|
|
1090
|
+
select(AgentTable).where(AgentTable.id == agent_id)
|
|
1091
|
+
)
|
|
1092
|
+
db_agent = result.scalar_one_or_none()
|
|
1093
|
+
|
|
1094
|
+
if not db_agent:
|
|
1095
|
+
raise IntentKitAPIError(404, "NotFound", f"Agent {agent_id} not found")
|
|
1096
|
+
|
|
1097
|
+
# Get only the fields that are explicitly provided (exclude_unset=True)
|
|
1098
|
+
update_data = self.model_dump(exclude_unset=True)
|
|
1099
|
+
|
|
1100
|
+
# Apply the updates to the database agent
|
|
1101
|
+
for key, value in update_data.items():
|
|
1102
|
+
if hasattr(db_agent, key):
|
|
1103
|
+
setattr(db_agent, key, value)
|
|
1104
|
+
|
|
1105
|
+
# Update public_info_updated_at timestamp
|
|
1106
|
+
db_agent.public_info_updated_at = func.now()
|
|
1107
|
+
|
|
1108
|
+
# Commit changes
|
|
1109
|
+
await session.commit()
|
|
1110
|
+
await session.refresh(db_agent)
|
|
1111
|
+
|
|
1112
|
+
return Agent.model_validate(db_agent)
|
|
1113
|
+
|
|
1114
|
+
async def override(self, agent_id: str) -> "Agent":
|
|
1115
|
+
"""Override agent public info with all fields from this instance.
|
|
1116
|
+
|
|
1117
|
+
Args:
|
|
1118
|
+
agent_id: The ID of the agent to override
|
|
1119
|
+
|
|
1120
|
+
Returns:
|
|
1121
|
+
The updated Agent instance
|
|
1122
|
+
"""
|
|
1123
|
+
async with get_session() as session:
|
|
1124
|
+
# Get the agent from database
|
|
1125
|
+
result = await session.execute(
|
|
1126
|
+
select(AgentTable).where(AgentTable.id == agent_id)
|
|
1127
|
+
)
|
|
1128
|
+
db_agent = result.scalar_one_or_none()
|
|
1129
|
+
|
|
1130
|
+
if not db_agent:
|
|
1131
|
+
raise IntentKitAPIError(404, "NotFound", f"Agent {agent_id} not found")
|
|
1132
|
+
|
|
1133
|
+
# Update public info fields
|
|
1134
|
+
update_data = self.model_dump()
|
|
1135
|
+
for key, value in update_data.items():
|
|
1136
|
+
if hasattr(db_agent, key):
|
|
1137
|
+
setattr(db_agent, key, value)
|
|
1138
|
+
|
|
1139
|
+
# Update public_info_updated_at timestamp
|
|
1140
|
+
db_agent.public_info_updated_at = func.now()
|
|
1141
|
+
|
|
1142
|
+
# Commit changes
|
|
1143
|
+
await session.commit()
|
|
1144
|
+
await session.refresh(db_agent)
|
|
1145
|
+
|
|
1146
|
+
return Agent.model_validate(db_agent)
|
|
1049
1147
|
|
|
1050
1148
|
|
|
1051
1149
|
class Agent(AgentCreate, AgentPublicInfo):
|
|
@@ -1054,43 +1152,64 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1054
1152
|
model_config = ConfigDict(from_attributes=True)
|
|
1055
1153
|
|
|
1056
1154
|
slug: Annotated[
|
|
1057
|
-
|
|
1155
|
+
str | None,
|
|
1058
1156
|
PydanticField(
|
|
1059
1157
|
default=None,
|
|
1060
1158
|
description="Slug of the agent, used for URL generation",
|
|
1061
|
-
max_length=
|
|
1159
|
+
max_length=100,
|
|
1062
1160
|
min_length=2,
|
|
1063
1161
|
),
|
|
1064
1162
|
]
|
|
1065
1163
|
version: Annotated[
|
|
1066
|
-
|
|
1164
|
+
str | None,
|
|
1067
1165
|
PydanticField(
|
|
1068
1166
|
default=None,
|
|
1069
1167
|
description="Version hash of the agent",
|
|
1070
1168
|
),
|
|
1071
1169
|
]
|
|
1072
1170
|
statistics: Annotated[
|
|
1073
|
-
|
|
1171
|
+
dict[str, Any] | None,
|
|
1074
1172
|
PydanticField(
|
|
1075
|
-
|
|
1173
|
+
default=None,
|
|
1174
|
+
description="Statistics of the agent, update every 1 hour for query",
|
|
1076
1175
|
),
|
|
1077
1176
|
]
|
|
1078
1177
|
assets: Annotated[
|
|
1079
|
-
|
|
1080
|
-
PydanticField(
|
|
1178
|
+
dict[str, Any] | None,
|
|
1179
|
+
PydanticField(
|
|
1180
|
+
default=None,
|
|
1181
|
+
description="Assets of the agent, update every 1 hour for query",
|
|
1182
|
+
),
|
|
1081
1183
|
]
|
|
1082
1184
|
account_snapshot: Annotated[
|
|
1083
|
-
|
|
1185
|
+
CreditAccount | None,
|
|
1084
1186
|
PydanticField(
|
|
1085
|
-
|
|
1187
|
+
default=None,
|
|
1188
|
+
description="Account snapshot of the agent, update every 1 hour for query",
|
|
1086
1189
|
),
|
|
1087
1190
|
]
|
|
1088
1191
|
extra: Annotated[
|
|
1089
|
-
|
|
1192
|
+
dict[str, Any] | None,
|
|
1090
1193
|
PydanticField(
|
|
1091
|
-
|
|
1194
|
+
default=None,
|
|
1195
|
+
description="Other helper data fields for query, come from agent and agent data",
|
|
1092
1196
|
),
|
|
1093
1197
|
]
|
|
1198
|
+
deployed_at: Annotated[
|
|
1199
|
+
datetime | None,
|
|
1200
|
+
PydanticField(
|
|
1201
|
+
default=None,
|
|
1202
|
+
description="Timestamp when the agent was deployed",
|
|
1203
|
+
),
|
|
1204
|
+
]
|
|
1205
|
+
public_info_updated_at: Annotated[
|
|
1206
|
+
datetime | None,
|
|
1207
|
+
PydanticField(
|
|
1208
|
+
default=None,
|
|
1209
|
+
description="Timestamp when the agent public info was last updated",
|
|
1210
|
+
),
|
|
1211
|
+
]
|
|
1212
|
+
|
|
1094
1213
|
# auto timestamp
|
|
1095
1214
|
created_at: Annotated[
|
|
1096
1215
|
datetime,
|
|
@@ -1125,8 +1244,37 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1125
1244
|
return False
|
|
1126
1245
|
|
|
1127
1246
|
async def is_model_support_image(self) -> bool:
|
|
1128
|
-
|
|
1129
|
-
|
|
1247
|
+
try:
|
|
1248
|
+
model = await LLMModelInfo.get(self.model)
|
|
1249
|
+
return model.supports_image_input
|
|
1250
|
+
except Exception:
|
|
1251
|
+
return False
|
|
1252
|
+
|
|
1253
|
+
def has_search(self) -> bool:
|
|
1254
|
+
texts = [
|
|
1255
|
+
self.prompt,
|
|
1256
|
+
self.prompt_append,
|
|
1257
|
+
self.purpose,
|
|
1258
|
+
self.personality,
|
|
1259
|
+
self.principles,
|
|
1260
|
+
]
|
|
1261
|
+
for t in texts:
|
|
1262
|
+
if t and (re.search(r"@search\b", t) or re.search(r"@web\b", t)):
|
|
1263
|
+
return True
|
|
1264
|
+
return False
|
|
1265
|
+
|
|
1266
|
+
def has_super(self) -> bool:
|
|
1267
|
+
texts = [
|
|
1268
|
+
self.prompt,
|
|
1269
|
+
self.prompt_append,
|
|
1270
|
+
self.purpose,
|
|
1271
|
+
self.personality,
|
|
1272
|
+
self.principles,
|
|
1273
|
+
]
|
|
1274
|
+
for t in texts:
|
|
1275
|
+
if t and re.search(r"@super\b", t):
|
|
1276
|
+
return True
|
|
1277
|
+
return False
|
|
1130
1278
|
|
|
1131
1279
|
def to_yaml(self) -> str:
|
|
1132
1280
|
"""
|
|
@@ -1219,7 +1367,7 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1219
1367
|
)
|
|
1220
1368
|
yaml_lines.append(yaml_value.rstrip())
|
|
1221
1369
|
elif isinstance(value, list) and value and hasattr(value[0], "model_dump"):
|
|
1222
|
-
# Handle list of Pydantic models (e.g.,
|
|
1370
|
+
# Handle list of Pydantic models (e.g., list[AgentAutonomous])
|
|
1223
1371
|
yaml_lines.append(f"{field_name}:")
|
|
1224
1372
|
# Convert each Pydantic model to dict
|
|
1225
1373
|
model_dicts = [item.model_dump(exclude_none=True) for item in value]
|
|
@@ -1234,20 +1382,22 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1234
1382
|
yaml_lines.append(indented_yaml.rstrip())
|
|
1235
1383
|
elif hasattr(value, "model_dump"):
|
|
1236
1384
|
# Handle individual Pydantic model
|
|
1237
|
-
|
|
1385
|
+
yaml_lines.append(f"{field_name}:")
|
|
1238
1386
|
yaml_value = yaml.dump(
|
|
1239
|
-
|
|
1387
|
+
value.model_dump(exclude_none=True),
|
|
1240
1388
|
default_flow_style=False,
|
|
1241
1389
|
allow_unicode=True,
|
|
1242
1390
|
)
|
|
1243
|
-
yaml_lines
|
|
1391
|
+
# Indent all lines and append to yaml_lines
|
|
1392
|
+
indented_yaml = "\n".join(
|
|
1393
|
+
f" {line}" for line in yaml_value.split("\n") if line.strip()
|
|
1394
|
+
)
|
|
1395
|
+
yaml_lines.append(indented_yaml)
|
|
1244
1396
|
else:
|
|
1245
|
-
# Handle Decimal
|
|
1397
|
+
# Handle Decimal and other types
|
|
1246
1398
|
if isinstance(value, Decimal):
|
|
1247
|
-
|
|
1248
|
-
yaml_lines.append(f"{field_name}: {value}")
|
|
1399
|
+
yaml_lines.append(f"{field_name}: {str(value)}")
|
|
1249
1400
|
else:
|
|
1250
|
-
# Handle other non-string values
|
|
1251
1401
|
yaml_value = yaml.dump(
|
|
1252
1402
|
{field_name: value},
|
|
1253
1403
|
default_flow_style=False,
|
|
@@ -1263,18 +1413,158 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1263
1413
|
return await db.scalar(select(func.count(AgentTable.id)))
|
|
1264
1414
|
|
|
1265
1415
|
@classmethod
|
|
1266
|
-
async def get(cls, agent_id: str) ->
|
|
1416
|
+
async def get(cls, agent_id: str) -> "Agent" | None:
|
|
1267
1417
|
async with get_session() as db:
|
|
1268
1418
|
item = await db.scalar(select(AgentTable).where(AgentTable.id == agent_id))
|
|
1269
1419
|
if item is None:
|
|
1270
1420
|
return None
|
|
1271
1421
|
return cls.model_validate(item)
|
|
1272
1422
|
|
|
1273
|
-
|
|
1423
|
+
@classmethod
|
|
1424
|
+
async def get_by_id_or_slug(cls, agent_id: str) -> "Agent" | None:
|
|
1425
|
+
"""Get agent by ID or slug.
|
|
1426
|
+
|
|
1427
|
+
First tries to get by ID if agent_id length <= 20,
|
|
1428
|
+
then falls back to searching by slug if not found.
|
|
1429
|
+
|
|
1430
|
+
Args:
|
|
1431
|
+
agent_id: Agent ID or slug to search for
|
|
1432
|
+
|
|
1433
|
+
Returns:
|
|
1434
|
+
Agent if found, None otherwise
|
|
1435
|
+
"""
|
|
1436
|
+
query_id = agent_id
|
|
1437
|
+
if ENS_NAME_PATTERN.fullmatch(agent_id):
|
|
1438
|
+
query_id = await resolve_ens_to_address(agent_id)
|
|
1439
|
+
|
|
1440
|
+
async with get_session() as db:
|
|
1441
|
+
agent = None
|
|
1442
|
+
|
|
1443
|
+
# Try to get by ID if length <= 20
|
|
1444
|
+
if len(query_id) <= 20 or query_id.startswith("0x"):
|
|
1445
|
+
agent = await Agent.get(query_id)
|
|
1446
|
+
|
|
1447
|
+
# If not found, try to get by slug
|
|
1448
|
+
if agent is None:
|
|
1449
|
+
slug_stmt = select(AgentTable).where(AgentTable.slug == query_id)
|
|
1450
|
+
agent_row = await db.scalar(slug_stmt)
|
|
1451
|
+
if agent_row is not None:
|
|
1452
|
+
agent = Agent.model_validate(agent_row)
|
|
1453
|
+
|
|
1454
|
+
return agent
|
|
1455
|
+
|
|
1456
|
+
@staticmethod
|
|
1457
|
+
def _deserialize_autonomous(
|
|
1458
|
+
autonomous_data: list[Any] | None,
|
|
1459
|
+
) -> list[AgentAutonomous]:
|
|
1460
|
+
if not autonomous_data:
|
|
1461
|
+
return []
|
|
1462
|
+
|
|
1463
|
+
deserialized: list[AgentAutonomous] = []
|
|
1464
|
+
for entry in autonomous_data:
|
|
1465
|
+
if isinstance(entry, AgentAutonomous):
|
|
1466
|
+
deserialized.append(entry)
|
|
1467
|
+
else:
|
|
1468
|
+
deserialized.append(AgentAutonomous.model_validate(entry))
|
|
1469
|
+
return deserialized
|
|
1470
|
+
|
|
1471
|
+
@staticmethod
|
|
1472
|
+
def _serialize_autonomous(tasks: list[AgentAutonomous]) -> list[dict[str, Any]]:
|
|
1473
|
+
return [task.model_dump() for task in tasks]
|
|
1474
|
+
|
|
1475
|
+
@staticmethod
|
|
1476
|
+
def _autonomous_not_allowed_error() -> IntentKitAPIError:
|
|
1477
|
+
return IntentKitAPIError(
|
|
1478
|
+
400,
|
|
1479
|
+
"AgentNotDeployed",
|
|
1480
|
+
"Only deployed agents can call this feature.",
|
|
1481
|
+
)
|
|
1482
|
+
|
|
1483
|
+
async def list_autonomous_tasks(self) -> list[AgentAutonomous]:
|
|
1484
|
+
persisted = await Agent.get(self.id)
|
|
1485
|
+
if persisted is None:
|
|
1486
|
+
raise self._autonomous_not_allowed_error()
|
|
1487
|
+
|
|
1488
|
+
tasks = persisted.autonomous or []
|
|
1489
|
+
# Keep local state in sync with persisted data
|
|
1490
|
+
self.autonomous = tasks
|
|
1491
|
+
return tasks
|
|
1492
|
+
|
|
1493
|
+
async def add_autonomous_task(self, task: AgentAutonomous) -> AgentAutonomous:
|
|
1494
|
+
async with get_session() as session:
|
|
1495
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1496
|
+
if db_agent is None:
|
|
1497
|
+
raise self._autonomous_not_allowed_error()
|
|
1498
|
+
|
|
1499
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1500
|
+
current_tasks.append(task)
|
|
1501
|
+
|
|
1502
|
+
db_agent.autonomous = self._serialize_autonomous(current_tasks)
|
|
1503
|
+
await session.commit()
|
|
1504
|
+
|
|
1505
|
+
self.autonomous = current_tasks
|
|
1506
|
+
return task
|
|
1507
|
+
|
|
1508
|
+
async def delete_autonomous_task(self, task_id: str) -> None:
|
|
1509
|
+
async with get_session() as session:
|
|
1510
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1511
|
+
if db_agent is None:
|
|
1512
|
+
raise self._autonomous_not_allowed_error()
|
|
1513
|
+
|
|
1514
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1515
|
+
|
|
1516
|
+
updated_tasks = [task for task in current_tasks if task.id != task_id]
|
|
1517
|
+
if len(updated_tasks) == len(current_tasks):
|
|
1518
|
+
raise IntentKitAPIError(
|
|
1519
|
+
404,
|
|
1520
|
+
"TaskNotFound",
|
|
1521
|
+
f"Autonomous task with ID {task_id} not found.",
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1524
|
+
db_agent.autonomous = self._serialize_autonomous(updated_tasks)
|
|
1525
|
+
await session.commit()
|
|
1526
|
+
|
|
1527
|
+
self.autonomous = updated_tasks
|
|
1528
|
+
|
|
1529
|
+
async def update_autonomous_task(
|
|
1530
|
+
self, task_id: str, task_updates: dict
|
|
1531
|
+
) -> AgentAutonomous:
|
|
1532
|
+
async with get_session() as session:
|
|
1533
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1534
|
+
if db_agent is None:
|
|
1535
|
+
raise self._autonomous_not_allowed_error()
|
|
1536
|
+
|
|
1537
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1538
|
+
|
|
1539
|
+
updated_task: AgentAutonomous | None = None
|
|
1540
|
+
rewritten_tasks: list[AgentAutonomous] = []
|
|
1541
|
+
for task in current_tasks:
|
|
1542
|
+
if task.id == task_id:
|
|
1543
|
+
task_dict = task.model_dump()
|
|
1544
|
+
task_dict.update(task_updates)
|
|
1545
|
+
updated_task = AgentAutonomous.model_validate(task_dict)
|
|
1546
|
+
rewritten_tasks.append(updated_task)
|
|
1547
|
+
else:
|
|
1548
|
+
rewritten_tasks.append(task)
|
|
1549
|
+
|
|
1550
|
+
if updated_task is None:
|
|
1551
|
+
raise IntentKitAPIError(
|
|
1552
|
+
404,
|
|
1553
|
+
"TaskNotFound",
|
|
1554
|
+
f"Autonomous task with ID {task_id} not found.",
|
|
1555
|
+
)
|
|
1556
|
+
|
|
1557
|
+
db_agent.autonomous = self._serialize_autonomous(rewritten_tasks)
|
|
1558
|
+
await session.commit()
|
|
1559
|
+
|
|
1560
|
+
self.autonomous = rewritten_tasks
|
|
1561
|
+
return updated_task
|
|
1562
|
+
|
|
1563
|
+
def skill_config(self, category: str) -> dict[str, Any]:
|
|
1274
1564
|
return self.skills.get(category, {}) if self.skills else {}
|
|
1275
1565
|
|
|
1276
1566
|
@staticmethod
|
|
1277
|
-
def _is_agent_owner_only_skill(skill_schema:
|
|
1567
|
+
def _is_agent_owner_only_skill(skill_schema: dict[str, Any]) -> bool:
|
|
1278
1568
|
"""Check if a skill requires agent owner API keys only based on its resolved schema."""
|
|
1279
1569
|
if (
|
|
1280
1570
|
skill_schema
|
|
@@ -1293,8 +1583,7 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1293
1583
|
cls,
|
|
1294
1584
|
db: AsyncSession = None,
|
|
1295
1585
|
filter_owner_api_skills: bool = False,
|
|
1296
|
-
|
|
1297
|
-
) -> Dict:
|
|
1586
|
+
) -> dict:
|
|
1298
1587
|
"""Get the JSON schema for Agent model with all $ref references resolved.
|
|
1299
1588
|
|
|
1300
1589
|
This is the shared function that handles admin configuration filtering
|
|
@@ -1303,7 +1592,6 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1303
1592
|
Args:
|
|
1304
1593
|
db: Database session (optional, will create if not provided)
|
|
1305
1594
|
filter_owner_api_skills: Whether to filter out skills that require agent owner API keys
|
|
1306
|
-
admin_llm_skill_control: Whether to enable admin LLM and skill control features
|
|
1307
1595
|
|
|
1308
1596
|
Returns:
|
|
1309
1597
|
Dict containing the complete JSON schema for the Agent model
|
|
@@ -1311,9 +1599,7 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1311
1599
|
# Get database session if not provided
|
|
1312
1600
|
if db is None:
|
|
1313
1601
|
async with get_session() as session:
|
|
1314
|
-
return await cls.get_json_schema(
|
|
1315
|
-
session, filter_owner_api_skills, admin_llm_skill_control
|
|
1316
|
-
)
|
|
1602
|
+
return await cls.get_json_schema(session, filter_owner_api_skills)
|
|
1317
1603
|
|
|
1318
1604
|
# Get the schema file path relative to this file
|
|
1319
1605
|
current_dir = Path(__file__).parent
|
|
@@ -1326,214 +1612,260 @@ class Agent(AgentCreate, AgentPublicInfo):
|
|
|
1326
1612
|
# Get the model property from the schema
|
|
1327
1613
|
model_property = schema.get("properties", {}).get("model", {})
|
|
1328
1614
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
)
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
for category, price_levels in category_price_levels.items():
|
|
1417
|
-
if price_levels:
|
|
1418
|
-
avg_price_level = int(sum(price_levels) / len(price_levels))
|
|
1419
|
-
category_avg_price_levels[category] = avg_price_level
|
|
1420
|
-
|
|
1421
|
-
# Create a copy of keys to avoid modifying during iteration
|
|
1422
|
-
skill_keys = list(skills_properties.keys())
|
|
1423
|
-
|
|
1424
|
-
# Process each skill in the schema
|
|
1425
|
-
for skill_category in skill_keys:
|
|
1426
|
-
if skill_category not in category_status:
|
|
1427
|
-
# If category not found in database, remove it from schema
|
|
1428
|
-
skills_properties.pop(skill_category, None)
|
|
1429
|
-
elif not category_status[skill_category]:
|
|
1430
|
-
# If category exists but all skills are disabled, remove it
|
|
1431
|
-
skills_properties.pop(skill_category, None)
|
|
1432
|
-
elif filter_owner_api_skills and cls._is_agent_owner_only_skill(
|
|
1433
|
-
skills_properties[skill_category]
|
|
1615
|
+
# Process model property using defaults merged with database overrides
|
|
1616
|
+
if model_property:
|
|
1617
|
+
new_enum = []
|
|
1618
|
+
new_enum_title = []
|
|
1619
|
+
new_enum_category = []
|
|
1620
|
+
new_enum_support_skill = []
|
|
1621
|
+
|
|
1622
|
+
for model_info in await LLMModelInfo.get_all(db):
|
|
1623
|
+
if not model_info.enabled:
|
|
1624
|
+
continue
|
|
1625
|
+
|
|
1626
|
+
provider = (
|
|
1627
|
+
LLMProvider(model_info.provider)
|
|
1628
|
+
if isinstance(model_info.provider, str)
|
|
1629
|
+
else model_info.provider
|
|
1630
|
+
)
|
|
1631
|
+
|
|
1632
|
+
new_enum.append(model_info.id)
|
|
1633
|
+
new_enum_title.append(model_info.name)
|
|
1634
|
+
new_enum_category.append(provider.display_name())
|
|
1635
|
+
new_enum_support_skill.append(model_info.supports_skill_calls)
|
|
1636
|
+
|
|
1637
|
+
model_property["enum"] = new_enum
|
|
1638
|
+
model_property["x-enum-title"] = new_enum_title
|
|
1639
|
+
model_property["x-enum-category"] = new_enum_category
|
|
1640
|
+
model_property["x-support-skill"] = new_enum_support_skill
|
|
1641
|
+
|
|
1642
|
+
if (
|
|
1643
|
+
"default" in model_property
|
|
1644
|
+
and model_property["default"] not in new_enum
|
|
1645
|
+
and new_enum
|
|
1646
|
+
):
|
|
1647
|
+
model_property["default"] = new_enum[0]
|
|
1648
|
+
|
|
1649
|
+
# Process skills property using data from Skill.get_all instead of agent_schema.json
|
|
1650
|
+
skills_property = schema.get("properties", {}).get("skills", {})
|
|
1651
|
+
|
|
1652
|
+
# Build skill_states_map from database
|
|
1653
|
+
skill_states_map: dict[str, dict[str, Skill]] = {}
|
|
1654
|
+
for skill_model in await Skill.get_all(db):
|
|
1655
|
+
if not skill_model.config_name:
|
|
1656
|
+
continue
|
|
1657
|
+
category_states = skill_states_map.setdefault(skill_model.category, {})
|
|
1658
|
+
if skill_model.enabled:
|
|
1659
|
+
category_states[skill_model.config_name] = skill_model
|
|
1660
|
+
else:
|
|
1661
|
+
category_states.pop(skill_model.config_name, None)
|
|
1662
|
+
|
|
1663
|
+
enabled_categories = {
|
|
1664
|
+
category for category, states in skill_states_map.items() if states
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
# Calculate price levels and skills data
|
|
1668
|
+
category_avg_price_levels = {}
|
|
1669
|
+
skills_data = {}
|
|
1670
|
+
for category, states in skill_states_map.items():
|
|
1671
|
+
if not states:
|
|
1672
|
+
continue
|
|
1673
|
+
price_levels = [
|
|
1674
|
+
state.price_level
|
|
1675
|
+
for state in states.values()
|
|
1676
|
+
if state.price_level is not None
|
|
1677
|
+
]
|
|
1678
|
+
if price_levels:
|
|
1679
|
+
category_avg_price_levels[category] = int(
|
|
1680
|
+
sum(price_levels) / len(price_levels)
|
|
1681
|
+
)
|
|
1682
|
+
skills_data[category] = {
|
|
1683
|
+
config_name: state.price_level
|
|
1684
|
+
for config_name, state in states.items()
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
# Dynamically generate skills_properties from Skill.get_all data
|
|
1688
|
+
skills_properties = {}
|
|
1689
|
+
current_dir = Path(__file__).parent
|
|
1690
|
+
|
|
1691
|
+
for category in enabled_categories:
|
|
1692
|
+
# Skip if filtered for auto-generation
|
|
1693
|
+
skill_schema_path = current_dir / f"../skills/{category}/schema.json"
|
|
1694
|
+
if skill_schema_path.exists():
|
|
1695
|
+
try:
|
|
1696
|
+
with open(skill_schema_path) as f:
|
|
1697
|
+
skill_schema = json.load(f)
|
|
1698
|
+
|
|
1699
|
+
# Check if this skill should be filtered for owner API requirements
|
|
1700
|
+
if filter_owner_api_skills and cls._is_agent_owner_only_skill(
|
|
1701
|
+
skill_schema
|
|
1434
1702
|
):
|
|
1435
|
-
# If filtering owner API skills and this skill requires it, remove it
|
|
1436
|
-
skills_properties.pop(skill_category, None)
|
|
1437
1703
|
logger.info(
|
|
1438
|
-
f"Filtered out skill '{
|
|
1704
|
+
f"Filtered out skill '{category}' from auto-generation: requires agent owner API key"
|
|
1439
1705
|
)
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1706
|
+
continue
|
|
1707
|
+
|
|
1708
|
+
# Create skill property with embedded schema instead of reference
|
|
1709
|
+
# Load and embed the full skill schema directly
|
|
1710
|
+
base_uri = f"file://{skill_schema_path}"
|
|
1711
|
+
with open(skill_schema_path) as f:
|
|
1712
|
+
embedded_skill_schema = jsonref.load(
|
|
1713
|
+
f, base_uri=base_uri, proxies=False, lazy_load=False
|
|
1714
|
+
)
|
|
1715
|
+
|
|
1716
|
+
skills_properties[category] = {
|
|
1717
|
+
"title": skill_schema.get("title", category.title()),
|
|
1718
|
+
**embedded_skill_schema, # Embed the full schema instead of using $ref
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
# Add price level information
|
|
1722
|
+
if category in category_avg_price_levels:
|
|
1723
|
+
skills_properties[category]["x-avg-price-level"] = (
|
|
1724
|
+
category_avg_price_levels[category]
|
|
1725
|
+
)
|
|
1726
|
+
|
|
1727
|
+
if category in skills_data:
|
|
1728
|
+
# Add price level to states in the embedded schema
|
|
1729
|
+
skill_states = (
|
|
1730
|
+
skills_properties[category]
|
|
1731
|
+
.get("properties", {})
|
|
1732
|
+
.get("states", {})
|
|
1733
|
+
.get("properties", {})
|
|
1734
|
+
)
|
|
1735
|
+
for state_name, state_config in skill_states.items():
|
|
1736
|
+
if (
|
|
1737
|
+
state_name in skills_data[category]
|
|
1738
|
+
and skills_data[category][state_name] is not None
|
|
1739
|
+
):
|
|
1740
|
+
state_config["x-price-level"] = skills_data[
|
|
1741
|
+
category
|
|
1742
|
+
][state_name]
|
|
1743
|
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
1744
|
+
logger.warning(
|
|
1745
|
+
f"Could not load schema for skill category '{category}': {e}"
|
|
1746
|
+
)
|
|
1747
|
+
continue
|
|
1748
|
+
|
|
1749
|
+
# Update the skills property in the schema
|
|
1750
|
+
if skills_property:
|
|
1751
|
+
skills_property["properties"] = skills_properties
|
|
1464
1752
|
|
|
1465
1753
|
# Log the changes for debugging
|
|
1466
1754
|
logger.debug(
|
|
1467
|
-
|
|
1468
|
-
|
|
1755
|
+
"Schema processed with merged LLM/skill defaults; filtered owner API skills: %s",
|
|
1756
|
+
filter_owner_api_skills,
|
|
1469
1757
|
)
|
|
1470
1758
|
|
|
1471
1759
|
return schema
|
|
1472
1760
|
|
|
1473
1761
|
|
|
1474
1762
|
class AgentResponse(Agent):
|
|
1475
|
-
"""
|
|
1763
|
+
"""Agent response model that excludes sensitive fields from JSON output and schema."""
|
|
1476
1764
|
|
|
1477
1765
|
model_config = ConfigDict(
|
|
1478
|
-
|
|
1766
|
+
title="AgentPublic",
|
|
1479
1767
|
from_attributes=True,
|
|
1480
|
-
json_encoders={
|
|
1481
|
-
|
|
1482
|
-
},
|
|
1768
|
+
# json_encoders={
|
|
1769
|
+
# datetime: lambda v: v.isoformat(timespec="milliseconds"),
|
|
1770
|
+
# },
|
|
1483
1771
|
)
|
|
1484
1772
|
|
|
1485
|
-
#
|
|
1773
|
+
# Override privacy fields to exclude them from JSON schema
|
|
1774
|
+
purpose: SkipJsonSchema[str | None] = None
|
|
1775
|
+
personality: SkipJsonSchema[str | None] = None
|
|
1776
|
+
principles: SkipJsonSchema[str | None] = None
|
|
1777
|
+
prompt: SkipJsonSchema[str | None] = None
|
|
1778
|
+
prompt_append: SkipJsonSchema[str | None] = None
|
|
1779
|
+
temperature: SkipJsonSchema[float | None] = None
|
|
1780
|
+
frequency_penalty: SkipJsonSchema[float | None] = None
|
|
1781
|
+
telegram_entrypoint_prompt: SkipJsonSchema[str | None] = None
|
|
1782
|
+
telegram_config: SkipJsonSchema[dict | None] = None
|
|
1783
|
+
discord_config: SkipJsonSchema[dict | None] = None
|
|
1784
|
+
xmtp_entrypoint_prompt: SkipJsonSchema[str | None] = None
|
|
1785
|
+
|
|
1786
|
+
# Additional fields specific to AgentResponse
|
|
1486
1787
|
cdp_wallet_address: Annotated[
|
|
1487
|
-
|
|
1788
|
+
str | None,
|
|
1789
|
+
PydanticField(
|
|
1790
|
+
default=None,
|
|
1791
|
+
description="CDP wallet address of the agent",
|
|
1792
|
+
),
|
|
1488
1793
|
]
|
|
1489
1794
|
evm_wallet_address: Annotated[
|
|
1490
|
-
|
|
1795
|
+
str | None,
|
|
1796
|
+
PydanticField(
|
|
1797
|
+
default=None,
|
|
1798
|
+
description="EVM wallet address of the agent",
|
|
1799
|
+
),
|
|
1491
1800
|
]
|
|
1492
1801
|
solana_wallet_address: Annotated[
|
|
1493
|
-
|
|
1802
|
+
str | None,
|
|
1803
|
+
PydanticField(
|
|
1804
|
+
default=None,
|
|
1805
|
+
description="Solana wallet address of the agent",
|
|
1806
|
+
),
|
|
1494
1807
|
]
|
|
1495
1808
|
has_twitter_linked: Annotated[
|
|
1496
1809
|
bool,
|
|
1497
|
-
PydanticField(
|
|
1810
|
+
PydanticField(
|
|
1811
|
+
default=False,
|
|
1812
|
+
description="Whether the agent has Twitter linked",
|
|
1813
|
+
),
|
|
1498
1814
|
]
|
|
1499
1815
|
linked_twitter_username: Annotated[
|
|
1500
|
-
|
|
1501
|
-
PydanticField(
|
|
1816
|
+
str | None,
|
|
1817
|
+
PydanticField(
|
|
1818
|
+
default=None,
|
|
1819
|
+
description="Linked Twitter username",
|
|
1820
|
+
),
|
|
1502
1821
|
]
|
|
1503
1822
|
linked_twitter_name: Annotated[
|
|
1504
|
-
|
|
1505
|
-
PydanticField(
|
|
1823
|
+
str | None,
|
|
1824
|
+
PydanticField(
|
|
1825
|
+
default=None,
|
|
1826
|
+
description="Linked Twitter display name",
|
|
1827
|
+
),
|
|
1506
1828
|
]
|
|
1507
1829
|
has_twitter_self_key: Annotated[
|
|
1508
1830
|
bool,
|
|
1509
1831
|
PydanticField(
|
|
1510
|
-
|
|
1832
|
+
default=False,
|
|
1833
|
+
description="Whether the agent has Twitter self key",
|
|
1511
1834
|
),
|
|
1512
1835
|
]
|
|
1513
1836
|
has_telegram_self_key: Annotated[
|
|
1514
1837
|
bool,
|
|
1515
1838
|
PydanticField(
|
|
1516
|
-
|
|
1839
|
+
default=False,
|
|
1840
|
+
description="Whether the agent has Telegram self key",
|
|
1517
1841
|
),
|
|
1518
1842
|
]
|
|
1519
1843
|
linked_telegram_username: Annotated[
|
|
1520
|
-
|
|
1521
|
-
PydanticField(
|
|
1844
|
+
str | None,
|
|
1845
|
+
PydanticField(
|
|
1846
|
+
default=None,
|
|
1847
|
+
description="Linked Telegram username",
|
|
1848
|
+
),
|
|
1522
1849
|
]
|
|
1523
1850
|
linked_telegram_name: Annotated[
|
|
1524
|
-
|
|
1525
|
-
PydanticField(
|
|
1851
|
+
str | None,
|
|
1852
|
+
PydanticField(
|
|
1853
|
+
default=None,
|
|
1854
|
+
description="Linked Telegram display name",
|
|
1855
|
+
),
|
|
1526
1856
|
]
|
|
1527
1857
|
accept_image_input: Annotated[
|
|
1528
1858
|
bool,
|
|
1529
1859
|
PydanticField(
|
|
1530
|
-
|
|
1860
|
+
default=False,
|
|
1861
|
+
description="Whether the agent accepts image input",
|
|
1531
1862
|
),
|
|
1532
1863
|
]
|
|
1533
1864
|
accept_image_input_private: Annotated[
|
|
1534
1865
|
bool,
|
|
1535
1866
|
PydanticField(
|
|
1536
|
-
|
|
1867
|
+
default=False,
|
|
1868
|
+
description="Whether the agent accepts image input in private mode",
|
|
1537
1869
|
),
|
|
1538
1870
|
]
|
|
1539
1871
|
|
|
@@ -1555,7 +1887,7 @@ class AgentResponse(Agent):
|
|
|
1555
1887
|
|
|
1556
1888
|
@classmethod
|
|
1557
1889
|
async def from_agent(
|
|
1558
|
-
cls, agent: Agent, agent_data:
|
|
1890
|
+
cls, agent: Agent, agent_data: AgentData | None = None
|
|
1559
1891
|
) -> "AgentResponse":
|
|
1560
1892
|
"""Create an AgentResponse from an Agent instance.
|
|
1561
1893
|
|
|
@@ -1566,76 +1898,6 @@ class AgentResponse(Agent):
|
|
|
1566
1898
|
Returns:
|
|
1567
1899
|
AgentResponse: Response model with additional processed data
|
|
1568
1900
|
"""
|
|
1569
|
-
# Get base data from agent
|
|
1570
|
-
data = agent.model_dump()
|
|
1571
|
-
|
|
1572
|
-
# Hide the sensitive fields
|
|
1573
|
-
data.pop("purpose", None)
|
|
1574
|
-
data.pop("personality", None)
|
|
1575
|
-
data.pop("principles", None)
|
|
1576
|
-
data.pop("prompt", None)
|
|
1577
|
-
data.pop("prompt_append", None)
|
|
1578
|
-
data.pop("temperature", None)
|
|
1579
|
-
data.pop("frequency_penalty", None)
|
|
1580
|
-
data.pop("telegram_entrypoint_prompt", None)
|
|
1581
|
-
data.pop("telegram_config", None)
|
|
1582
|
-
data.pop("xmtp_entrypoint_prompt", None)
|
|
1583
|
-
|
|
1584
|
-
# Filter sensitive fields from autonomous list
|
|
1585
|
-
if data.get("autonomous"):
|
|
1586
|
-
filtered_autonomous = []
|
|
1587
|
-
for item in data["autonomous"]:
|
|
1588
|
-
if isinstance(item, dict):
|
|
1589
|
-
# Create proper AgentAutonomous instance with only public fields
|
|
1590
|
-
filtered_autonomous.append(
|
|
1591
|
-
AgentAutonomous(
|
|
1592
|
-
id=item.get("id"),
|
|
1593
|
-
name=item.get("name"),
|
|
1594
|
-
enabled=item.get("enabled"),
|
|
1595
|
-
# Set required prompt field to empty string for public view
|
|
1596
|
-
prompt="",
|
|
1597
|
-
)
|
|
1598
|
-
)
|
|
1599
|
-
data["autonomous"] = filtered_autonomous
|
|
1600
|
-
|
|
1601
|
-
# Convert examples dictionaries to AgentExample instances
|
|
1602
|
-
if data.get("examples"):
|
|
1603
|
-
examples_list = []
|
|
1604
|
-
for item in data["examples"]:
|
|
1605
|
-
if isinstance(item, dict):
|
|
1606
|
-
# Create proper AgentExample instance
|
|
1607
|
-
examples_list.append(
|
|
1608
|
-
AgentExample(
|
|
1609
|
-
name=item.get("name", ""),
|
|
1610
|
-
description=item.get("description", ""),
|
|
1611
|
-
prompt=item.get("prompt", ""),
|
|
1612
|
-
)
|
|
1613
|
-
)
|
|
1614
|
-
data["examples"] = examples_list
|
|
1615
|
-
|
|
1616
|
-
# Filter sensitive fields from skills dictionary
|
|
1617
|
-
if data.get("skills"):
|
|
1618
|
-
filtered_skills = {}
|
|
1619
|
-
for skill_name, skill_config in data["skills"].items():
|
|
1620
|
-
if isinstance(skill_config, dict):
|
|
1621
|
-
# Only include skills that are enabled
|
|
1622
|
-
if skill_config.get("enabled") is True:
|
|
1623
|
-
filtered_config = {"enabled": True}
|
|
1624
|
-
# Only keep states with public or private values
|
|
1625
|
-
if "states" in skill_config and isinstance(
|
|
1626
|
-
skill_config["states"], dict
|
|
1627
|
-
):
|
|
1628
|
-
filtered_states = {}
|
|
1629
|
-
for state_key, state_value in skill_config[
|
|
1630
|
-
"states"
|
|
1631
|
-
].items():
|
|
1632
|
-
if state_value in ["public", "private"]:
|
|
1633
|
-
filtered_states[state_key] = state_value
|
|
1634
|
-
if filtered_states:
|
|
1635
|
-
filtered_config["states"] = filtered_states
|
|
1636
|
-
filtered_skills[skill_name] = filtered_config
|
|
1637
|
-
data["skills"] = filtered_skills
|
|
1638
|
-
|
|
1639
1901
|
# Process CDP wallet address
|
|
1640
1902
|
cdp_wallet_address = agent_data.evm_wallet_address if agent_data else None
|
|
1641
1903
|
evm_wallet_address = agent_data.evm_wallet_address if agent_data else None
|
|
@@ -1650,8 +1912,7 @@ class AgentResponse(Agent):
|
|
|
1650
1912
|
linked_twitter_name = agent_data.twitter_name
|
|
1651
1913
|
if agent_data.twitter_access_token_expires_at:
|
|
1652
1914
|
has_twitter_linked = (
|
|
1653
|
-
agent_data.twitter_access_token_expires_at
|
|
1654
|
-
> datetime.now(timezone.utc)
|
|
1915
|
+
agent_data.twitter_access_token_expires_at > datetime.now(UTC)
|
|
1655
1916
|
)
|
|
1656
1917
|
else:
|
|
1657
1918
|
has_twitter_linked = True
|
|
@@ -1661,10 +1922,10 @@ class AgentResponse(Agent):
|
|
|
1661
1922
|
agent_data and agent_data.twitter_self_key_refreshed_at
|
|
1662
1923
|
)
|
|
1663
1924
|
|
|
1664
|
-
# Process Telegram self-key status
|
|
1925
|
+
# Process Telegram self-key status
|
|
1665
1926
|
linked_telegram_username = None
|
|
1666
1927
|
linked_telegram_name = None
|
|
1667
|
-
telegram_config =
|
|
1928
|
+
telegram_config = agent.telegram_config or {}
|
|
1668
1929
|
has_telegram_self_key = bool(
|
|
1669
1930
|
telegram_config and "token" in telegram_config and telegram_config["token"]
|
|
1670
1931
|
)
|
|
@@ -1681,22 +1942,174 @@ class AgentResponse(Agent):
|
|
|
1681
1942
|
or agent.has_image_parser_skill(is_private=True)
|
|
1682
1943
|
)
|
|
1683
1944
|
|
|
1684
|
-
#
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1945
|
+
# Create AgentResponse instance directly from agent with additional fields
|
|
1946
|
+
return cls(
|
|
1947
|
+
# Copy all fields from agent
|
|
1948
|
+
**agent.model_dump(),
|
|
1949
|
+
# Add computed fields
|
|
1950
|
+
cdp_wallet_address=cdp_wallet_address,
|
|
1951
|
+
evm_wallet_address=evm_wallet_address,
|
|
1952
|
+
solana_wallet_address=solana_wallet_address,
|
|
1953
|
+
has_twitter_linked=has_twitter_linked,
|
|
1954
|
+
linked_twitter_username=linked_twitter_username,
|
|
1955
|
+
linked_twitter_name=linked_twitter_name,
|
|
1956
|
+
has_twitter_self_key=has_twitter_self_key,
|
|
1957
|
+
has_telegram_self_key=has_telegram_self_key,
|
|
1958
|
+
linked_telegram_username=linked_telegram_username,
|
|
1959
|
+
linked_telegram_name=linked_telegram_name,
|
|
1960
|
+
accept_image_input=accept_image_input,
|
|
1961
|
+
accept_image_input_private=accept_image_input_private,
|
|
1962
|
+
)
|
|
1963
|
+
|
|
1964
|
+
def model_dump(
|
|
1965
|
+
self,
|
|
1966
|
+
*,
|
|
1967
|
+
mode: str | Literal["json", "python"] = "python",
|
|
1968
|
+
include: IncEx | None = None,
|
|
1969
|
+
exclude: IncEx | None = None,
|
|
1970
|
+
context: Any | None = None,
|
|
1971
|
+
by_alias: bool = False,
|
|
1972
|
+
exclude_unset: bool = False,
|
|
1973
|
+
exclude_defaults: bool = False,
|
|
1974
|
+
exclude_none: bool = False,
|
|
1975
|
+
round_trip: bool = False,
|
|
1976
|
+
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
1977
|
+
serialize_as_any: bool = False,
|
|
1978
|
+
) -> dict[str, Any]:
|
|
1979
|
+
"""Override model_dump to exclude privacy fields and filter data."""
|
|
1980
|
+
# Get the base model dump
|
|
1981
|
+
data = super().model_dump(
|
|
1982
|
+
mode=mode,
|
|
1983
|
+
include=include,
|
|
1984
|
+
exclude=exclude,
|
|
1985
|
+
context=context,
|
|
1986
|
+
by_alias=by_alias,
|
|
1987
|
+
exclude_unset=exclude_unset,
|
|
1988
|
+
exclude_defaults=exclude_defaults,
|
|
1989
|
+
exclude_none=exclude_none,
|
|
1990
|
+
round_trip=round_trip,
|
|
1991
|
+
warnings=warnings,
|
|
1992
|
+
serialize_as_any=serialize_as_any,
|
|
1993
|
+
)
|
|
1994
|
+
|
|
1995
|
+
# Remove privacy fields that might still be present
|
|
1996
|
+
privacy_fields = {
|
|
1997
|
+
"purpose",
|
|
1998
|
+
"personality",
|
|
1999
|
+
"principles",
|
|
2000
|
+
"prompt",
|
|
2001
|
+
"prompt_append",
|
|
2002
|
+
"temperature",
|
|
2003
|
+
"frequency_penalty",
|
|
2004
|
+
"telegram_entrypoint_prompt",
|
|
2005
|
+
"telegram_config",
|
|
2006
|
+
"discord_config",
|
|
2007
|
+
"xmtp_entrypoint_prompt",
|
|
2008
|
+
}
|
|
2009
|
+
for field in privacy_fields:
|
|
2010
|
+
data.pop(field, None)
|
|
2011
|
+
|
|
2012
|
+
# Filter autonomous list to only keep safe fields
|
|
2013
|
+
if "autonomous" in data and data["autonomous"]:
|
|
2014
|
+
filtered_autonomous = []
|
|
2015
|
+
for item in data["autonomous"]:
|
|
2016
|
+
if isinstance(item, dict):
|
|
2017
|
+
# Only keep safe fields: id, name, description, enabled
|
|
2018
|
+
filtered_item = {
|
|
2019
|
+
key: item[key]
|
|
2020
|
+
for key in ["id", "name", "description", "enabled"]
|
|
2021
|
+
if key in item
|
|
2022
|
+
}
|
|
2023
|
+
filtered_autonomous.append(filtered_item)
|
|
2024
|
+
else:
|
|
2025
|
+
# Handle AgentAutonomous objects
|
|
2026
|
+
item_dict = (
|
|
2027
|
+
item.model_dump() if hasattr(item, "model_dump") else dict(item)
|
|
2028
|
+
)
|
|
2029
|
+
# Only keep safe fields: id, name, description, enabled
|
|
2030
|
+
filtered_item = {
|
|
2031
|
+
key: item_dict[key]
|
|
2032
|
+
for key in ["id", "name", "description", "enabled"]
|
|
2033
|
+
if key in item_dict
|
|
2034
|
+
}
|
|
2035
|
+
filtered_autonomous.append(filtered_item)
|
|
2036
|
+
data["autonomous"] = filtered_autonomous
|
|
2037
|
+
|
|
2038
|
+
# Convert examples to AgentExample instances if they're dictionaries
|
|
2039
|
+
if "examples" in data and data["examples"]:
|
|
2040
|
+
converted_examples = []
|
|
2041
|
+
for example in data["examples"]:
|
|
2042
|
+
if isinstance(example, dict):
|
|
2043
|
+
converted_examples.append(AgentExample(**example).model_dump())
|
|
2044
|
+
else:
|
|
2045
|
+
converted_examples.append(
|
|
2046
|
+
example.model_dump()
|
|
2047
|
+
if hasattr(example, "model_dump")
|
|
2048
|
+
else example
|
|
2049
|
+
)
|
|
2050
|
+
data["examples"] = converted_examples
|
|
2051
|
+
|
|
2052
|
+
# Filter skills to only include enabled ones with specific configurations
|
|
2053
|
+
if "skills" in data and data["skills"]:
|
|
2054
|
+
filtered_skills = {}
|
|
2055
|
+
for skill_name, skill_config in data["skills"].items():
|
|
2056
|
+
if (
|
|
2057
|
+
isinstance(skill_config, dict)
|
|
2058
|
+
and skill_config.get("enabled") is True
|
|
2059
|
+
):
|
|
2060
|
+
# Filter out disabled states from the skill configuration
|
|
2061
|
+
original_states = skill_config.get("states", {})
|
|
2062
|
+
filtered_states = {
|
|
2063
|
+
state_name: state_value
|
|
2064
|
+
for state_name, state_value in original_states.items()
|
|
2065
|
+
if state_value != "disabled"
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
# Only include the skill if it has at least one non-disabled state
|
|
2069
|
+
if filtered_states:
|
|
2070
|
+
filtered_config = {
|
|
2071
|
+
"enabled": skill_config["enabled"],
|
|
2072
|
+
"states": filtered_states,
|
|
2073
|
+
}
|
|
2074
|
+
# Add other non-sensitive config fields if needed
|
|
2075
|
+
for key in ["public", "private"]:
|
|
2076
|
+
if key in skill_config:
|
|
2077
|
+
filtered_config[key] = skill_config[key]
|
|
2078
|
+
filtered_skills[skill_name] = filtered_config
|
|
2079
|
+
data["skills"] = filtered_skills
|
|
2080
|
+
|
|
2081
|
+
return data
|
|
2082
|
+
|
|
2083
|
+
def model_dump_json(
|
|
2084
|
+
self,
|
|
2085
|
+
*,
|
|
2086
|
+
indent: int | str | None = None,
|
|
2087
|
+
include: IncEx | None = None,
|
|
2088
|
+
exclude: IncEx | None = None,
|
|
2089
|
+
context: Any | None = None,
|
|
2090
|
+
by_alias: bool = False,
|
|
2091
|
+
exclude_unset: bool = False,
|
|
2092
|
+
exclude_defaults: bool = False,
|
|
2093
|
+
exclude_none: bool = False,
|
|
2094
|
+
round_trip: bool = False,
|
|
2095
|
+
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
2096
|
+
serialize_as_any: bool = False,
|
|
2097
|
+
) -> str:
|
|
2098
|
+
"""Override model_dump_json to exclude privacy fields and filter sensitive data."""
|
|
2099
|
+
# Get the filtered data using the same logic as model_dump
|
|
2100
|
+
data = self.model_dump(
|
|
2101
|
+
mode="json",
|
|
2102
|
+
include=include,
|
|
2103
|
+
exclude=exclude,
|
|
2104
|
+
context=context,
|
|
2105
|
+
by_alias=by_alias,
|
|
2106
|
+
exclude_unset=exclude_unset,
|
|
2107
|
+
exclude_defaults=exclude_defaults,
|
|
2108
|
+
exclude_none=exclude_none,
|
|
2109
|
+
round_trip=round_trip,
|
|
2110
|
+
warnings=warnings,
|
|
2111
|
+
serialize_as_any=serialize_as_any,
|
|
1700
2112
|
)
|
|
1701
2113
|
|
|
1702
|
-
|
|
2114
|
+
# Use json.dumps to serialize the filtered data with proper indentation
|
|
2115
|
+
return json.dumps(data, indent=indent, ensure_ascii=False)
|