intentkit 0.6.13.dev2__py3-none-any.whl → 0.8.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +1 -1
- intentkit/abstracts/agent.py +4 -5
- intentkit/abstracts/engine.py +5 -5
- intentkit/abstracts/graph.py +14 -7
- intentkit/abstracts/skill.py +6 -144
- intentkit/abstracts/twitter.py +4 -5
- intentkit/clients/__init__.py +5 -2
- intentkit/clients/cdp.py +101 -141
- intentkit/clients/twitter.py +83 -62
- intentkit/clients/web3.py +29 -0
- intentkit/config/config.py +8 -5
- intentkit/core/agent.py +472 -195
- intentkit/core/asset.py +253 -0
- intentkit/core/chat.py +51 -0
- intentkit/core/client.py +1 -1
- intentkit/core/credit.py +460 -130
- intentkit/core/engine.py +262 -233
- intentkit/core/node.py +15 -16
- intentkit/core/prompt.py +62 -28
- intentkit/core/scheduler.py +92 -0
- intentkit/core/statistics.py +168 -0
- intentkit/models/agent.py +1096 -949
- intentkit/models/agent_data.py +68 -38
- intentkit/models/agent_public.json +98 -0
- intentkit/models/agent_schema.json +54 -439
- intentkit/models/app_setting.py +96 -33
- intentkit/models/chat.py +74 -27
- intentkit/models/conversation.py +8 -8
- intentkit/models/credit.py +362 -74
- intentkit/models/db.py +26 -8
- intentkit/models/db_mig.py +2 -2
- intentkit/models/llm.csv +28 -0
- intentkit/models/llm.py +185 -350
- intentkit/models/redis.py +6 -4
- intentkit/models/skill.py +186 -72
- intentkit/models/skills.csv +174 -0
- intentkit/models/user.py +82 -24
- 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 +248 -85
- 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 +5 -5
- intentkit/skills/casino/README.md +254 -0
- intentkit/skills/casino/__init__.py +86 -0
- intentkit/skills/casino/base.py +17 -0
- intentkit/skills/casino/casino.png +0 -0
- intentkit/skills/casino/deck_draw.py +127 -0
- intentkit/skills/casino/deck_shuffle.py +118 -0
- intentkit/skills/casino/dice_roll.py +100 -0
- intentkit/skills/casino/schema.json +77 -0
- intentkit/skills/casino/utils.py +107 -0
- 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 +11 -25
- 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 +11 -21
- 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/README.md +154 -0
- intentkit/skills/dexscreener/__init__.py +97 -93
- intentkit/skills/dexscreener/base.py +125 -133
- intentkit/skills/dexscreener/get_pair_info.py +158 -0
- intentkit/skills/dexscreener/get_token_pairs.py +165 -0
- intentkit/skills/dexscreener/get_tokens_info.py +212 -0
- intentkit/skills/dexscreener/model/search_token_response.py +80 -82
- intentkit/skills/dexscreener/schema.json +91 -48
- intentkit/skills/dexscreener/search_token.py +182 -321
- intentkit/skills/dexscreener/utils.py +420 -0
- 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 +50 -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/README.md +11 -5
- intentkit/skills/firecrawl/__init__.py +5 -18
- intentkit/skills/firecrawl/base.py +4 -11
- 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 +6 -8
- intentkit/skills/firecrawl/scrape.py +150 -40
- 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 +12 -13
- intentkit/skills/heurist/image_generation_arthemy_comics.py +12 -13
- intentkit/skills/heurist/image_generation_arthemy_real.py +12 -13
- intentkit/skills/heurist/image_generation_braindance.py +12 -13
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +12 -13
- intentkit/skills/heurist/image_generation_flux_1_dev.py +12 -13
- intentkit/skills/heurist/image_generation_sdxl.py +12 -13
- 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 +1 -7
- intentkit/skills/lifi/schema.json +17 -8
- intentkit/skills/lifi/token_execute.py +36 -30
- 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 +23 -0
- intentkit/skills/openai/__init__.py +17 -18
- intentkit/skills/openai/base.py +10 -14
- intentkit/skills/openai/dalle_image_generation.py +3 -8
- intentkit/skills/openai/gpt_avatar_generator.py +102 -0
- intentkit/skills/openai/gpt_image_generation.py +4 -8
- intentkit/skills/openai/gpt_image_mini_generator.py +91 -0
- intentkit/skills/openai/gpt_image_to_image.py +4 -8
- 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 +40 -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 +9 -13
- intentkit/skills/supabase/delete_data.py +5 -6
- intentkit/skills/supabase/fetch_data.py +13 -14
- intentkit/skills/supabase/insert_data.py +5 -6
- intentkit/skills/supabase/invoke_function.py +7 -8
- intentkit/skills/supabase/schema.json +2 -3
- intentkit/skills/supabase/update_data.py +7 -8
- intentkit/skills/supabase/upsert_data.py +5 -6
- 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 +23 -35
- intentkit/skills/twitter/follow_user.py +3 -7
- intentkit/skills/twitter/get_mentions.py +6 -13
- intentkit/skills/twitter/get_timeline.py +5 -13
- intentkit/skills/twitter/get_user_by_username.py +3 -7
- intentkit/skills/twitter/get_user_tweets.py +6 -14
- intentkit/skills/twitter/like_tweet.py +3 -7
- intentkit/skills/twitter/post_tweet.py +23 -12
- intentkit/skills/twitter/reply_tweet.py +21 -12
- intentkit/skills/twitter/retweet.py +3 -7
- intentkit/skills/twitter/schema.json +1 -0
- intentkit/skills/twitter/search_tweets.py +5 -13
- 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 +9 -9
- 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 +61 -0
- intentkit/skills/x402/ask_agent.py +98 -0
- intentkit/skills/x402/base.py +99 -0
- intentkit/skills/x402/http_request.py +117 -0
- intentkit/skills/x402/schema.json +45 -0
- intentkit/skills/x402/x402.webp +0 -0
- intentkit/skills/xmtp/__init__.py +4 -15
- intentkit/skills/xmtp/base.py +61 -2
- intentkit/skills/xmtp/price.py +18 -13
- intentkit/skills/xmtp/schema.json +69 -71
- intentkit/skills/xmtp/swap.py +22 -25
- intentkit/skills/xmtp/transfer.py +71 -32
- intentkit/utils/chain.py +3 -3
- intentkit/utils/error.py +14 -1
- intentkit/utils/logging.py +2 -4
- intentkit/utils/s3.py +59 -7
- intentkit/utils/schema.py +100 -0
- intentkit/utils/slack_alert.py +7 -8
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/METADATA +14 -16
- intentkit-0.8.17.dist-info/RECORD +466 -0
- intentkit/abstracts/exception.py +0 -9
- intentkit/core/skill.py +0 -200
- 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.6.13.dev2.dist-info/RECORD +0 -409
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/WHEEL +0 -0
- {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/licenses/LICENSE +0 -0
intentkit/models/agent.py
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
1
4
|
import json
|
|
2
5
|
import logging
|
|
3
6
|
import re
|
|
4
7
|
import textwrap
|
|
5
|
-
from datetime import
|
|
8
|
+
from datetime import UTC, datetime
|
|
6
9
|
from decimal import Decimal
|
|
7
10
|
from pathlib import Path
|
|
8
|
-
from typing import Annotated, Any,
|
|
11
|
+
from typing import Annotated, Any, Literal
|
|
9
12
|
|
|
10
13
|
import jsonref
|
|
11
14
|
import yaml
|
|
12
15
|
from cron_validator import CronValidator
|
|
13
16
|
from epyxid import XID
|
|
14
|
-
from fastapi import HTTPException
|
|
15
17
|
from intentkit.models.agent_data import AgentData
|
|
16
18
|
from intentkit.models.base import Base
|
|
19
|
+
from intentkit.models.credit import CreditAccount
|
|
17
20
|
from intentkit.models.db import get_session
|
|
18
|
-
from intentkit.models.llm import LLMModelInfo,
|
|
19
|
-
from intentkit.models.skill import
|
|
20
|
-
from
|
|
21
|
+
from intentkit.models.llm import LLMModelInfo, LLMProvider
|
|
22
|
+
from intentkit.models.skill import Skill
|
|
23
|
+
from intentkit.utils.error import IntentKitAPIError
|
|
24
|
+
from pydantic import BaseModel, ConfigDict, field_validator
|
|
21
25
|
from pydantic import Field as PydanticField
|
|
22
26
|
from pydantic.json_schema import SkipJsonSchema
|
|
27
|
+
from pydantic.main import IncEx
|
|
23
28
|
from sqlalchemy import (
|
|
24
29
|
Boolean,
|
|
25
30
|
Column,
|
|
@@ -31,6 +36,7 @@ from sqlalchemy import (
|
|
|
31
36
|
select,
|
|
32
37
|
)
|
|
33
38
|
from sqlalchemy.dialects.postgresql import JSON, JSONB
|
|
39
|
+
from sqlalchemy.exc import IntegrityError
|
|
34
40
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
35
41
|
|
|
36
42
|
logger = logging.getLogger(__name__)
|
|
@@ -53,7 +59,7 @@ class AgentAutonomous(BaseModel):
|
|
|
53
59
|
),
|
|
54
60
|
]
|
|
55
61
|
name: Annotated[
|
|
56
|
-
|
|
62
|
+
str | None,
|
|
57
63
|
PydanticField(
|
|
58
64
|
default=None,
|
|
59
65
|
description="Display name of the autonomous configuration",
|
|
@@ -64,7 +70,7 @@ class AgentAutonomous(BaseModel):
|
|
|
64
70
|
),
|
|
65
71
|
]
|
|
66
72
|
description: Annotated[
|
|
67
|
-
|
|
73
|
+
str | None,
|
|
68
74
|
PydanticField(
|
|
69
75
|
default=None,
|
|
70
76
|
description="Description of the autonomous configuration",
|
|
@@ -75,7 +81,7 @@ class AgentAutonomous(BaseModel):
|
|
|
75
81
|
),
|
|
76
82
|
]
|
|
77
83
|
minutes: Annotated[
|
|
78
|
-
|
|
84
|
+
int | None,
|
|
79
85
|
PydanticField(
|
|
80
86
|
default=None,
|
|
81
87
|
description="Interval in minutes between operations, mutually exclusive with cron",
|
|
@@ -85,7 +91,7 @@ class AgentAutonomous(BaseModel):
|
|
|
85
91
|
),
|
|
86
92
|
]
|
|
87
93
|
cron: Annotated[
|
|
88
|
-
|
|
94
|
+
str | None,
|
|
89
95
|
PydanticField(
|
|
90
96
|
default=None,
|
|
91
97
|
description="Cron expression for scheduling operations, mutually exclusive with minutes",
|
|
@@ -105,7 +111,7 @@ class AgentAutonomous(BaseModel):
|
|
|
105
111
|
),
|
|
106
112
|
]
|
|
107
113
|
enabled: Annotated[
|
|
108
|
-
|
|
114
|
+
bool | None,
|
|
109
115
|
PydanticField(
|
|
110
116
|
default=False,
|
|
111
117
|
description="Whether the autonomous configuration is enabled",
|
|
@@ -128,33 +134,6 @@ class AgentAutonomous(BaseModel):
|
|
|
128
134
|
)
|
|
129
135
|
return v
|
|
130
136
|
|
|
131
|
-
@field_validator("name")
|
|
132
|
-
@classmethod
|
|
133
|
-
def validate_name(cls, v: Optional[str]) -> Optional[str]:
|
|
134
|
-
if v is not None and len(v.encode()) > 50:
|
|
135
|
-
raise ValueError("name must be at most 50 bytes")
|
|
136
|
-
return v
|
|
137
|
-
|
|
138
|
-
@field_validator("description")
|
|
139
|
-
@classmethod
|
|
140
|
-
def validate_description(cls, v: Optional[str]) -> Optional[str]:
|
|
141
|
-
if v is not None and len(v.encode()) > 200:
|
|
142
|
-
raise ValueError("description must be at most 200 bytes")
|
|
143
|
-
return v
|
|
144
|
-
|
|
145
|
-
@field_validator("prompt")
|
|
146
|
-
@classmethod
|
|
147
|
-
def validate_prompt(cls, v: Optional[str]) -> Optional[str]:
|
|
148
|
-
if v is not None and len(v.encode()) > 20000:
|
|
149
|
-
raise ValueError("prompt must be at most 20000 bytes")
|
|
150
|
-
return v
|
|
151
|
-
|
|
152
|
-
@model_validator(mode="after")
|
|
153
|
-
def validate_schedule(self) -> "AgentAutonomous":
|
|
154
|
-
# This validator is kept for backward compatibility
|
|
155
|
-
# The actual validation now happens in AgentUpdate.validate_autonomous_schedule
|
|
156
|
-
return self
|
|
157
|
-
|
|
158
137
|
|
|
159
138
|
class AgentExample(BaseModel):
|
|
160
139
|
"""Agent example configuration."""
|
|
@@ -165,7 +144,7 @@ class AgentExample(BaseModel):
|
|
|
165
144
|
description="Name of the example",
|
|
166
145
|
max_length=50,
|
|
167
146
|
json_schema_extra={
|
|
168
|
-
"x-
|
|
147
|
+
"x-placeholder": "Add a name for the example",
|
|
169
148
|
},
|
|
170
149
|
),
|
|
171
150
|
]
|
|
@@ -175,7 +154,7 @@ class AgentExample(BaseModel):
|
|
|
175
154
|
description="Description of the example",
|
|
176
155
|
max_length=200,
|
|
177
156
|
json_schema_extra={
|
|
178
|
-
"x-
|
|
157
|
+
"x-placeholder": "Add a short description for the example",
|
|
179
158
|
},
|
|
180
159
|
),
|
|
181
160
|
]
|
|
@@ -185,72 +164,28 @@ class AgentExample(BaseModel):
|
|
|
185
164
|
description="Example prompt",
|
|
186
165
|
max_length=2000,
|
|
187
166
|
json_schema_extra={
|
|
188
|
-
"x-
|
|
167
|
+
"x-placeholder": "The prompt will be sent to the agent",
|
|
189
168
|
},
|
|
190
169
|
),
|
|
191
170
|
]
|
|
192
171
|
|
|
193
172
|
|
|
194
|
-
class
|
|
195
|
-
"""
|
|
173
|
+
class AgentUserInputColumns:
|
|
174
|
+
"""Abstract base class containing columns that are common to AgentTable and other tables."""
|
|
196
175
|
|
|
197
|
-
|
|
176
|
+
__abstract__ = True
|
|
198
177
|
|
|
199
|
-
|
|
200
|
-
String,
|
|
201
|
-
primary_key=True,
|
|
202
|
-
comment="Unique identifier for the agent. Must be URL-safe, containing only lowercase letters, numbers, and hyphens",
|
|
203
|
-
)
|
|
178
|
+
# Basic information fields from AgentCore
|
|
204
179
|
name = Column(
|
|
205
180
|
String,
|
|
206
181
|
nullable=True,
|
|
207
182
|
comment="Display name of the agent",
|
|
208
183
|
)
|
|
209
|
-
slug = Column(
|
|
210
|
-
String,
|
|
211
|
-
nullable=True,
|
|
212
|
-
comment="Slug of the agent, used for URL generation",
|
|
213
|
-
)
|
|
214
|
-
description = Column(
|
|
215
|
-
String,
|
|
216
|
-
nullable=True,
|
|
217
|
-
comment="Description of the agent, for public view, not contained in prompt",
|
|
218
|
-
)
|
|
219
|
-
external_website = Column(
|
|
220
|
-
String,
|
|
221
|
-
nullable=True,
|
|
222
|
-
comment="Link of external website of the agent, if you have one",
|
|
223
|
-
)
|
|
224
184
|
picture = Column(
|
|
225
185
|
String,
|
|
226
186
|
nullable=True,
|
|
227
187
|
comment="Picture of the agent",
|
|
228
188
|
)
|
|
229
|
-
ticker = Column(
|
|
230
|
-
String,
|
|
231
|
-
nullable=True,
|
|
232
|
-
comment="Ticker symbol of the agent",
|
|
233
|
-
)
|
|
234
|
-
token_address = Column(
|
|
235
|
-
String,
|
|
236
|
-
nullable=True,
|
|
237
|
-
comment="Token address of the agent",
|
|
238
|
-
)
|
|
239
|
-
token_pool = Column(
|
|
240
|
-
String,
|
|
241
|
-
nullable=True,
|
|
242
|
-
comment="Pool of the agent token",
|
|
243
|
-
)
|
|
244
|
-
mode = Column(
|
|
245
|
-
String,
|
|
246
|
-
nullable=True,
|
|
247
|
-
comment="Mode of the agent, public or private",
|
|
248
|
-
)
|
|
249
|
-
fee_percentage = Column(
|
|
250
|
-
Numeric(22, 4),
|
|
251
|
-
nullable=True,
|
|
252
|
-
comment="Fee percentage of the agent",
|
|
253
|
-
)
|
|
254
189
|
purpose = Column(
|
|
255
190
|
String,
|
|
256
191
|
nullable=True,
|
|
@@ -266,34 +201,8 @@ class AgentTable(Base):
|
|
|
266
201
|
nullable=True,
|
|
267
202
|
comment="Principles or values of the agent",
|
|
268
203
|
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
nullable=True,
|
|
272
|
-
comment="Owner identifier of the agent, used for access control",
|
|
273
|
-
)
|
|
274
|
-
upstream_id = Column(
|
|
275
|
-
String,
|
|
276
|
-
index=True,
|
|
277
|
-
nullable=True,
|
|
278
|
-
comment="Upstream reference ID for idempotent operations",
|
|
279
|
-
)
|
|
280
|
-
upstream_extra = Column(
|
|
281
|
-
JSON().with_variant(JSONB(), "postgresql"),
|
|
282
|
-
nullable=True,
|
|
283
|
-
comment="Additional data store for upstream use",
|
|
284
|
-
)
|
|
285
|
-
wallet_provider = Column(
|
|
286
|
-
String,
|
|
287
|
-
nullable=True,
|
|
288
|
-
comment="Provider of the agent's wallet",
|
|
289
|
-
)
|
|
290
|
-
network_id = Column(
|
|
291
|
-
String,
|
|
292
|
-
nullable=True,
|
|
293
|
-
default="base-mainnet",
|
|
294
|
-
comment="Network identifier",
|
|
295
|
-
)
|
|
296
|
-
# AI part
|
|
204
|
+
|
|
205
|
+
# AI model configuration fields from AgentCore
|
|
297
206
|
model = Column(
|
|
298
207
|
String,
|
|
299
208
|
nullable=True,
|
|
@@ -328,81 +237,192 @@ class AgentTable(Base):
|
|
|
328
237
|
default=0.0,
|
|
329
238
|
comment="Controls topic adherence (-2.0~2.0). Higher values allow more topic deviation, lower values enforce stricter topic adherence.",
|
|
330
239
|
)
|
|
240
|
+
|
|
241
|
+
# Wallet and network configuration fields from AgentCore
|
|
242
|
+
wallet_provider = Column(
|
|
243
|
+
String,
|
|
244
|
+
nullable=True,
|
|
245
|
+
comment="Provider of the agent's wallet",
|
|
246
|
+
)
|
|
247
|
+
readonly_wallet_address = Column(
|
|
248
|
+
String,
|
|
249
|
+
nullable=True,
|
|
250
|
+
comment="Readonly wallet address of the agent",
|
|
251
|
+
)
|
|
252
|
+
network_id = Column(
|
|
253
|
+
String,
|
|
254
|
+
nullable=True,
|
|
255
|
+
default="base-mainnet",
|
|
256
|
+
comment="Network identifier",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Skills configuration from AgentCore
|
|
260
|
+
skills = Column(
|
|
261
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
262
|
+
nullable=True,
|
|
263
|
+
comment="Dict of skills and their corresponding configurations",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Additional fields from AgentUserInput
|
|
331
267
|
short_term_memory_strategy = Column(
|
|
332
268
|
String,
|
|
333
269
|
nullable=True,
|
|
334
270
|
default="trim",
|
|
335
271
|
comment="Strategy for managing short-term memory when context limit is reached. 'trim' removes oldest messages, 'summarize' creates summaries.",
|
|
336
272
|
)
|
|
337
|
-
# autonomous mode
|
|
338
273
|
autonomous = Column(
|
|
339
274
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
340
275
|
nullable=True,
|
|
341
276
|
comment="Autonomous agent configurations",
|
|
342
277
|
)
|
|
343
|
-
|
|
344
|
-
|
|
278
|
+
telegram_entrypoint_enabled = Column(
|
|
279
|
+
Boolean,
|
|
280
|
+
nullable=True,
|
|
281
|
+
default=False,
|
|
282
|
+
comment="Whether the agent can receive events from Telegram",
|
|
283
|
+
)
|
|
284
|
+
telegram_entrypoint_prompt = Column(
|
|
345
285
|
String,
|
|
346
286
|
nullable=True,
|
|
347
|
-
comment="
|
|
287
|
+
comment="Extra prompt for telegram entrypoint",
|
|
348
288
|
)
|
|
349
|
-
|
|
289
|
+
telegram_config = Column(
|
|
350
290
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
351
291
|
nullable=True,
|
|
352
|
-
comment="
|
|
292
|
+
comment="Telegram integration configuration settings",
|
|
353
293
|
)
|
|
354
|
-
|
|
355
|
-
|
|
294
|
+
discord_entrypoint_enabled = Column(
|
|
295
|
+
Boolean,
|
|
296
|
+
nullable=True,
|
|
297
|
+
default=False,
|
|
298
|
+
comment="Whether the agent can receive events from Discord",
|
|
299
|
+
)
|
|
300
|
+
discord_config = Column(
|
|
356
301
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
357
302
|
nullable=True,
|
|
358
|
-
comment="
|
|
303
|
+
comment="Discord integration configuration settings",
|
|
359
304
|
)
|
|
305
|
+
xmtp_entrypoint_prompt = Column(
|
|
306
|
+
String,
|
|
307
|
+
nullable=True,
|
|
308
|
+
comment="Extra prompt for xmtp entrypoint",
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class AgentTable(Base, AgentUserInputColumns):
|
|
313
|
+
"""Agent table db model."""
|
|
360
314
|
|
|
361
|
-
|
|
315
|
+
__tablename__ = "agents"
|
|
316
|
+
|
|
317
|
+
id = Column(
|
|
318
|
+
String,
|
|
319
|
+
primary_key=True,
|
|
320
|
+
comment="Unique identifier for the agent. Must be URL-safe, containing only lowercase letters, numbers, and hyphens",
|
|
321
|
+
)
|
|
322
|
+
slug = Column(
|
|
362
323
|
String,
|
|
363
324
|
nullable=True,
|
|
364
|
-
|
|
365
|
-
comment="Network identifier for CDP integration",
|
|
325
|
+
comment="Slug of the agent, used for URL generation",
|
|
366
326
|
)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
Boolean,
|
|
327
|
+
owner = Column(
|
|
328
|
+
String,
|
|
370
329
|
nullable=True,
|
|
371
|
-
|
|
372
|
-
comment="Dangerous, reply all mentions from x.com",
|
|
330
|
+
comment="Owner identifier of the agent, used for access control",
|
|
373
331
|
)
|
|
374
|
-
|
|
332
|
+
upstream_id = Column(
|
|
375
333
|
String,
|
|
334
|
+
index=True,
|
|
376
335
|
nullable=True,
|
|
377
|
-
comment="
|
|
336
|
+
comment="Upstream reference ID for idempotent operations",
|
|
378
337
|
)
|
|
379
|
-
|
|
338
|
+
upstream_extra = Column(
|
|
380
339
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
381
340
|
nullable=True,
|
|
382
|
-
comment="
|
|
341
|
+
comment="Additional data store for upstream use",
|
|
383
342
|
)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
Boolean,
|
|
343
|
+
version = Column(
|
|
344
|
+
String,
|
|
387
345
|
nullable=True,
|
|
388
|
-
|
|
389
|
-
comment="Whether the agent can receive events from Telegram",
|
|
346
|
+
comment="Version hash of the agent",
|
|
390
347
|
)
|
|
391
|
-
|
|
392
|
-
|
|
348
|
+
statistics = Column(
|
|
349
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
393
350
|
nullable=True,
|
|
394
|
-
comment="
|
|
351
|
+
comment="Statistics of the agent, update every 1 hour for query",
|
|
395
352
|
)
|
|
396
|
-
|
|
353
|
+
assets = Column(
|
|
397
354
|
JSON().with_variant(JSONB(), "postgresql"),
|
|
398
355
|
nullable=True,
|
|
399
|
-
comment="
|
|
356
|
+
comment="Assets of the agent, update every 1 hour for query",
|
|
400
357
|
)
|
|
401
|
-
|
|
358
|
+
account_snapshot = Column(
|
|
359
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
360
|
+
nullable=True,
|
|
361
|
+
comment="Account snapshot of the agent, update every 1 hour for query",
|
|
362
|
+
)
|
|
363
|
+
extra = Column(
|
|
364
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
365
|
+
nullable=True,
|
|
366
|
+
comment="Other helper data fields for query, come from agent and agent data",
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# Fields moved from AgentUserInputColumns that are no longer in AgentUserInput
|
|
370
|
+
description = Column(
|
|
402
371
|
String,
|
|
403
372
|
nullable=True,
|
|
404
|
-
comment="
|
|
373
|
+
comment="Description of the agent, for public view, not contained in prompt",
|
|
374
|
+
)
|
|
375
|
+
external_website = Column(
|
|
376
|
+
String,
|
|
377
|
+
nullable=True,
|
|
378
|
+
comment="Link of external website of the agent, if you have one",
|
|
379
|
+
)
|
|
380
|
+
ticker = Column(
|
|
381
|
+
String,
|
|
382
|
+
nullable=True,
|
|
383
|
+
comment="Ticker symbol of the agent",
|
|
384
|
+
)
|
|
385
|
+
token_address = Column(
|
|
386
|
+
String,
|
|
387
|
+
nullable=True,
|
|
388
|
+
comment="Token address of the agent",
|
|
389
|
+
)
|
|
390
|
+
token_pool = Column(
|
|
391
|
+
String,
|
|
392
|
+
nullable=True,
|
|
393
|
+
comment="Pool of the agent token",
|
|
394
|
+
)
|
|
395
|
+
fee_percentage = Column(
|
|
396
|
+
Numeric(22, 4),
|
|
397
|
+
nullable=True,
|
|
398
|
+
comment="Fee percentage of the agent",
|
|
399
|
+
)
|
|
400
|
+
example_intro = Column(
|
|
401
|
+
String,
|
|
402
|
+
nullable=True,
|
|
403
|
+
comment="Introduction for example interactions",
|
|
404
|
+
)
|
|
405
|
+
examples = Column(
|
|
406
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
407
|
+
nullable=True,
|
|
408
|
+
comment="List of example interactions for the agent",
|
|
409
|
+
)
|
|
410
|
+
public_extra = Column(
|
|
411
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
412
|
+
nullable=True,
|
|
413
|
+
comment="Public extra data of the agent",
|
|
414
|
+
)
|
|
415
|
+
deployed_at = Column(
|
|
416
|
+
DateTime(timezone=True),
|
|
417
|
+
nullable=True,
|
|
418
|
+
comment="Timestamp when the agent was deployed",
|
|
405
419
|
)
|
|
420
|
+
public_info_updated_at = Column(
|
|
421
|
+
DateTime(timezone=True),
|
|
422
|
+
nullable=True,
|
|
423
|
+
comment="Timestamp when the agent public info was last updated",
|
|
424
|
+
)
|
|
425
|
+
|
|
406
426
|
# auto timestamp
|
|
407
427
|
created_at = Column(
|
|
408
428
|
DateTime(timezone=True),
|
|
@@ -414,218 +434,52 @@ class AgentTable(Base):
|
|
|
414
434
|
DateTime(timezone=True),
|
|
415
435
|
nullable=False,
|
|
416
436
|
server_default=func.now(),
|
|
417
|
-
onupdate=lambda: datetime.now(
|
|
437
|
+
onupdate=lambda: datetime.now(UTC),
|
|
418
438
|
comment="Timestamp when the agent was last updated",
|
|
419
439
|
)
|
|
420
440
|
|
|
421
441
|
|
|
422
|
-
class
|
|
423
|
-
"""Agent
|
|
424
|
-
|
|
425
|
-
model_config = ConfigDict(
|
|
426
|
-
title="Agent",
|
|
427
|
-
from_attributes=True,
|
|
428
|
-
json_schema_extra={
|
|
429
|
-
"required": ["name", "purpose", "personality", "principles"],
|
|
430
|
-
},
|
|
431
|
-
)
|
|
442
|
+
class AgentCore(BaseModel):
|
|
443
|
+
"""Agent core model."""
|
|
432
444
|
|
|
433
445
|
name: Annotated[
|
|
434
|
-
|
|
446
|
+
str | None,
|
|
435
447
|
PydanticField(
|
|
436
448
|
default=None,
|
|
437
449
|
title="Name",
|
|
438
450
|
description="Display name of the agent",
|
|
439
451
|
max_length=50,
|
|
440
|
-
json_schema_extra={
|
|
441
|
-
"x-group": "basic",
|
|
442
|
-
"x-placeholder": "Name your agent",
|
|
443
|
-
},
|
|
444
452
|
),
|
|
445
453
|
]
|
|
446
|
-
|
|
447
|
-
|
|
454
|
+
picture: Annotated[
|
|
455
|
+
str | None,
|
|
448
456
|
PydanticField(
|
|
449
457
|
default=None,
|
|
450
|
-
description="
|
|
451
|
-
max_length=30,
|
|
452
|
-
min_length=2,
|
|
453
|
-
json_schema_extra={
|
|
454
|
-
"x-group": "internal",
|
|
455
|
-
"readOnly": True,
|
|
456
|
-
},
|
|
458
|
+
description="Avatar of the agent",
|
|
457
459
|
),
|
|
458
460
|
]
|
|
459
|
-
|
|
460
|
-
|
|
461
|
+
purpose: Annotated[
|
|
462
|
+
str | None,
|
|
461
463
|
PydanticField(
|
|
462
464
|
default=None,
|
|
463
|
-
description="
|
|
464
|
-
|
|
465
|
-
"x-group": "basic",
|
|
466
|
-
"x-placeholder": "Introduce your agent",
|
|
467
|
-
},
|
|
465
|
+
description="Purpose or role of the agent",
|
|
466
|
+
max_length=20000,
|
|
468
467
|
),
|
|
469
468
|
]
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
PydanticField(
|
|
473
|
-
default=None,
|
|
474
|
-
description="Link of external website of the agent, if you have one",
|
|
475
|
-
json_schema_extra={
|
|
476
|
-
"x-group": "basic",
|
|
477
|
-
"x-placeholder": "Enter agent external website url",
|
|
478
|
-
"format": "uri",
|
|
479
|
-
},
|
|
480
|
-
),
|
|
481
|
-
]
|
|
482
|
-
picture: Annotated[
|
|
483
|
-
Optional[str],
|
|
484
|
-
PydanticField(
|
|
485
|
-
default=None,
|
|
486
|
-
description="Picture of the agent",
|
|
487
|
-
json_schema_extra={
|
|
488
|
-
"x-group": "experimental",
|
|
489
|
-
"x-placeholder": "Upload a picture of your agent",
|
|
490
|
-
},
|
|
491
|
-
),
|
|
492
|
-
]
|
|
493
|
-
ticker: Annotated[
|
|
494
|
-
Optional[str],
|
|
495
|
-
PydanticField(
|
|
496
|
-
default=None,
|
|
497
|
-
description="Ticker symbol of the agent",
|
|
498
|
-
max_length=10,
|
|
499
|
-
min_length=1,
|
|
500
|
-
json_schema_extra={
|
|
501
|
-
"x-group": "basic",
|
|
502
|
-
"x-placeholder": "If one day, your agent has it's own token, what will it be?",
|
|
503
|
-
},
|
|
504
|
-
),
|
|
505
|
-
]
|
|
506
|
-
token_address: Annotated[
|
|
507
|
-
Optional[str],
|
|
508
|
-
PydanticField(
|
|
509
|
-
default=None,
|
|
510
|
-
description="Token address of the agent",
|
|
511
|
-
max_length=42,
|
|
512
|
-
json_schema_extra={
|
|
513
|
-
"x-group": "internal",
|
|
514
|
-
"readOnly": True,
|
|
515
|
-
},
|
|
516
|
-
),
|
|
517
|
-
]
|
|
518
|
-
token_pool: Annotated[
|
|
519
|
-
Optional[str],
|
|
520
|
-
PydanticField(
|
|
521
|
-
default=None,
|
|
522
|
-
description="Pool of the agent token",
|
|
523
|
-
max_length=42,
|
|
524
|
-
json_schema_extra={
|
|
525
|
-
"x-group": "internal",
|
|
526
|
-
"readOnly": True,
|
|
527
|
-
},
|
|
528
|
-
),
|
|
529
|
-
]
|
|
530
|
-
mode: Annotated[
|
|
531
|
-
Optional[Literal["public", "private"]],
|
|
532
|
-
PydanticField(
|
|
533
|
-
default=None,
|
|
534
|
-
description="Mode of the agent, public or private",
|
|
535
|
-
json_schema_extra={
|
|
536
|
-
"x-group": "basic",
|
|
537
|
-
},
|
|
538
|
-
),
|
|
539
|
-
]
|
|
540
|
-
fee_percentage: Annotated[
|
|
541
|
-
Optional[Decimal],
|
|
542
|
-
PydanticField(
|
|
543
|
-
default=None,
|
|
544
|
-
description="Fee percentage of the agent",
|
|
545
|
-
ge=Decimal("0.0"),
|
|
546
|
-
json_schema_extra={
|
|
547
|
-
"x-group": "basic",
|
|
548
|
-
},
|
|
549
|
-
),
|
|
550
|
-
]
|
|
551
|
-
purpose: Annotated[
|
|
552
|
-
Optional[str],
|
|
553
|
-
PydanticField(
|
|
554
|
-
default=None,
|
|
555
|
-
description="Purpose or role of the agent",
|
|
556
|
-
max_length=20000,
|
|
557
|
-
json_schema_extra={
|
|
558
|
-
"x-group": "basic",
|
|
559
|
-
"x-placeholder": "Enter agent purpose, it will be a part of the system prompt",
|
|
560
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
561
|
-
"errorMessage": {
|
|
562
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
563
|
-
},
|
|
564
|
-
},
|
|
565
|
-
),
|
|
566
|
-
]
|
|
567
|
-
personality: Annotated[
|
|
568
|
-
Optional[str],
|
|
469
|
+
personality: Annotated[
|
|
470
|
+
str | None,
|
|
569
471
|
PydanticField(
|
|
570
472
|
default=None,
|
|
571
473
|
description="Personality traits of the agent",
|
|
572
474
|
max_length=20000,
|
|
573
|
-
json_schema_extra={
|
|
574
|
-
"x-group": "basic",
|
|
575
|
-
"x-placeholder": "Enter agent personality, it will be a part of the system prompt",
|
|
576
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
577
|
-
"errorMessage": {
|
|
578
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
579
|
-
},
|
|
580
|
-
},
|
|
581
475
|
),
|
|
582
476
|
]
|
|
583
477
|
principles: Annotated[
|
|
584
|
-
|
|
478
|
+
str | None,
|
|
585
479
|
PydanticField(
|
|
586
480
|
default=None,
|
|
587
481
|
description="Principles or values of the agent",
|
|
588
482
|
max_length=20000,
|
|
589
|
-
json_schema_extra={
|
|
590
|
-
"x-group": "basic",
|
|
591
|
-
"x-placeholder": "Enter agent principles, it will be a part of the system prompt",
|
|
592
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
593
|
-
"errorMessage": {
|
|
594
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
595
|
-
},
|
|
596
|
-
},
|
|
597
|
-
),
|
|
598
|
-
]
|
|
599
|
-
owner: Annotated[
|
|
600
|
-
Optional[str],
|
|
601
|
-
PydanticField(
|
|
602
|
-
default=None,
|
|
603
|
-
description="Owner identifier of the agent, used for access control",
|
|
604
|
-
max_length=50,
|
|
605
|
-
json_schema_extra={
|
|
606
|
-
"x-group": "internal",
|
|
607
|
-
},
|
|
608
|
-
),
|
|
609
|
-
]
|
|
610
|
-
upstream_id: Annotated[
|
|
611
|
-
Optional[str],
|
|
612
|
-
PydanticField(
|
|
613
|
-
default=None,
|
|
614
|
-
description="External reference ID for idempotent operations",
|
|
615
|
-
max_length=100,
|
|
616
|
-
json_schema_extra={
|
|
617
|
-
"x-group": "internal",
|
|
618
|
-
},
|
|
619
|
-
),
|
|
620
|
-
]
|
|
621
|
-
upstream_extra: Annotated[
|
|
622
|
-
Optional[Dict[str, Any]],
|
|
623
|
-
PydanticField(
|
|
624
|
-
default=None,
|
|
625
|
-
description="Additional data store for upstream use",
|
|
626
|
-
json_schema_extra={
|
|
627
|
-
"x-group": "internal",
|
|
628
|
-
},
|
|
629
483
|
),
|
|
630
484
|
]
|
|
631
485
|
# AI part
|
|
@@ -633,91 +487,137 @@ class AgentUpdate(BaseModel):
|
|
|
633
487
|
str,
|
|
634
488
|
PydanticField(
|
|
635
489
|
default="gpt-5-mini",
|
|
636
|
-
description="
|
|
637
|
-
json_schema_extra={
|
|
638
|
-
"x-group": "ai",
|
|
639
|
-
},
|
|
490
|
+
description="LLM of the agent",
|
|
640
491
|
),
|
|
641
492
|
]
|
|
642
493
|
prompt: Annotated[
|
|
643
|
-
|
|
494
|
+
str | None,
|
|
644
495
|
PydanticField(
|
|
645
496
|
default=None,
|
|
646
497
|
description="Base system prompt that defines the agent's behavior and capabilities",
|
|
647
498
|
max_length=20000,
|
|
648
|
-
json_schema_extra={
|
|
649
|
-
"x-group": "ai",
|
|
650
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
651
|
-
"errorMessage": {
|
|
652
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
499
|
),
|
|
656
500
|
]
|
|
657
501
|
prompt_append: Annotated[
|
|
658
|
-
|
|
502
|
+
str | None,
|
|
659
503
|
PydanticField(
|
|
660
504
|
default=None,
|
|
661
505
|
description="Additional system prompt that has higher priority than the base prompt",
|
|
662
506
|
max_length=20000,
|
|
663
|
-
json_schema_extra={
|
|
664
|
-
"x-group": "ai",
|
|
665
|
-
"pattern": "^(([^#].*)|#[^# ].*|#{3,}[ ].*|$)(\n(([^#].*)|#[^# ].*|#{3,}[ ].*|$))*$",
|
|
666
|
-
"errorMessage": {
|
|
667
|
-
"pattern": "Level 1 and 2 headings (# and ##) are not allowed. Please use level 3+ headings (###, ####, etc.) instead."
|
|
668
|
-
},
|
|
669
|
-
},
|
|
670
507
|
),
|
|
671
508
|
]
|
|
672
509
|
temperature: Annotated[
|
|
673
|
-
|
|
510
|
+
float | None,
|
|
674
511
|
PydanticField(
|
|
675
512
|
default=0.7,
|
|
676
513
|
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)",
|
|
677
514
|
ge=0.0,
|
|
678
515
|
le=2.0,
|
|
679
|
-
json_schema_extra={
|
|
680
|
-
"x-group": "ai",
|
|
681
|
-
},
|
|
682
516
|
),
|
|
683
517
|
]
|
|
684
518
|
frequency_penalty: Annotated[
|
|
685
|
-
|
|
519
|
+
float | None,
|
|
686
520
|
PydanticField(
|
|
687
521
|
default=0.0,
|
|
688
522
|
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)",
|
|
689
523
|
ge=-2.0,
|
|
690
524
|
le=2.0,
|
|
691
|
-
json_schema_extra={
|
|
692
|
-
"x-group": "ai",
|
|
693
|
-
},
|
|
694
525
|
),
|
|
695
526
|
]
|
|
696
527
|
presence_penalty: Annotated[
|
|
697
|
-
|
|
528
|
+
float | None,
|
|
698
529
|
PydanticField(
|
|
699
530
|
default=0.0,
|
|
700
531
|
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)",
|
|
701
532
|
ge=-2.0,
|
|
702
533
|
le=2.0,
|
|
703
|
-
json_schema_extra={
|
|
704
|
-
"x-group": "ai",
|
|
705
|
-
},
|
|
706
534
|
),
|
|
707
535
|
]
|
|
536
|
+
wallet_provider: Annotated[
|
|
537
|
+
Literal["cdp", "readonly", "none"] | None,
|
|
538
|
+
PydanticField(
|
|
539
|
+
default=None,
|
|
540
|
+
description="Provider of the agent's wallet",
|
|
541
|
+
),
|
|
542
|
+
]
|
|
543
|
+
readonly_wallet_address: Annotated[
|
|
544
|
+
str | None,
|
|
545
|
+
PydanticField(
|
|
546
|
+
default=None,
|
|
547
|
+
description="Address of the agent's wallet, only used when wallet_provider is readonly. Agent will not be able to sign transactions.",
|
|
548
|
+
),
|
|
549
|
+
]
|
|
550
|
+
network_id: Annotated[
|
|
551
|
+
Literal[
|
|
552
|
+
"base-mainnet",
|
|
553
|
+
"ethereum-mainnet",
|
|
554
|
+
"polygon-mainnet",
|
|
555
|
+
"arbitrum-mainnet",
|
|
556
|
+
"optimism-mainnet",
|
|
557
|
+
"solana",
|
|
558
|
+
]
|
|
559
|
+
| None,
|
|
560
|
+
PydanticField(
|
|
561
|
+
default="base-mainnet",
|
|
562
|
+
description="Network identifier",
|
|
563
|
+
),
|
|
564
|
+
]
|
|
565
|
+
skills: Annotated[
|
|
566
|
+
dict[str, Any] | None,
|
|
567
|
+
PydanticField(
|
|
568
|
+
default=None,
|
|
569
|
+
description="Dict of skills and their corresponding configurations",
|
|
570
|
+
),
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
def hash(self) -> str:
|
|
574
|
+
"""
|
|
575
|
+
Generate a fixed-length hash based on the agent's content.
|
|
576
|
+
|
|
577
|
+
The hash remains unchanged if the content is the same and changes if the content changes.
|
|
578
|
+
This method serializes only AgentCore fields to JSON and generates a SHA-256 hash.
|
|
579
|
+
When called from subclasses, it will only use AgentCore fields, not subclass fields.
|
|
580
|
+
|
|
581
|
+
Returns:
|
|
582
|
+
str: A 64-character hexadecimal hash string
|
|
583
|
+
"""
|
|
584
|
+
# Create a dictionary with only AgentCore fields for hashing
|
|
585
|
+
hash_data = {}
|
|
586
|
+
|
|
587
|
+
# Get only AgentCore field values, excluding None values for consistency
|
|
588
|
+
for field_name in AgentCore.model_fields:
|
|
589
|
+
value = getattr(self, field_name)
|
|
590
|
+
if value is not None:
|
|
591
|
+
hash_data[field_name] = value
|
|
592
|
+
|
|
593
|
+
# Convert to JSON string with sorted keys for consistent ordering
|
|
594
|
+
json_str = json.dumps(hash_data, sort_keys=True, default=str, ensure_ascii=True)
|
|
595
|
+
|
|
596
|
+
# Generate SHA-256 hash
|
|
597
|
+
return hashlib.sha256(json_str.encode("utf-8")).hexdigest()
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
class AgentUserInput(AgentCore):
|
|
601
|
+
"""Agent update model."""
|
|
602
|
+
|
|
603
|
+
model_config = ConfigDict(
|
|
604
|
+
title="AgentUserInput",
|
|
605
|
+
from_attributes=True,
|
|
606
|
+
json_schema_extra={
|
|
607
|
+
"required": ["name"],
|
|
608
|
+
},
|
|
609
|
+
)
|
|
610
|
+
|
|
708
611
|
short_term_memory_strategy: Annotated[
|
|
709
|
-
|
|
612
|
+
Literal["trim", "summarize"] | None,
|
|
710
613
|
PydanticField(
|
|
711
614
|
default="trim",
|
|
712
615
|
description="Strategy for managing short-term memory when context limit is reached. 'trim' removes oldest messages, 'summarize' creates summaries.",
|
|
713
|
-
json_schema_extra={
|
|
714
|
-
"x-group": "ai",
|
|
715
|
-
},
|
|
716
616
|
),
|
|
717
617
|
]
|
|
718
618
|
# autonomous mode
|
|
719
619
|
autonomous: Annotated[
|
|
720
|
-
|
|
620
|
+
list[AgentAutonomous] | None,
|
|
721
621
|
PydanticField(
|
|
722
622
|
default=None,
|
|
723
623
|
description=(
|
|
@@ -734,158 +634,94 @@ class AgentUpdate(BaseModel):
|
|
|
734
634
|
" prompt: |-\n"
|
|
735
635
|
" Say hi [sequence], use number for sequence.\n"
|
|
736
636
|
),
|
|
737
|
-
json_schema_extra={
|
|
738
|
-
"x-group": "autonomous",
|
|
739
|
-
"x-inline": True,
|
|
740
|
-
},
|
|
741
637
|
),
|
|
742
638
|
]
|
|
743
|
-
|
|
744
|
-
|
|
639
|
+
# if telegram_entrypoint_enabled, the telegram_entrypoint_enabled will be enabled, telegram_config will be checked
|
|
640
|
+
telegram_entrypoint_enabled: Annotated[
|
|
641
|
+
bool | None,
|
|
745
642
|
PydanticField(
|
|
746
|
-
default=
|
|
747
|
-
description="
|
|
748
|
-
max_length=2000,
|
|
749
|
-
json_schema_extra={
|
|
750
|
-
"x-group": "examples",
|
|
751
|
-
},
|
|
643
|
+
default=False,
|
|
644
|
+
description="Whether the agent can play telegram bot",
|
|
752
645
|
),
|
|
753
646
|
]
|
|
754
|
-
|
|
755
|
-
|
|
647
|
+
telegram_entrypoint_prompt: Annotated[
|
|
648
|
+
str | None,
|
|
756
649
|
PydanticField(
|
|
757
650
|
default=None,
|
|
758
|
-
description="
|
|
759
|
-
max_length=
|
|
760
|
-
json_schema_extra={
|
|
761
|
-
"x-group": "examples",
|
|
762
|
-
"x-inline": True,
|
|
763
|
-
},
|
|
651
|
+
description="Extra prompt for telegram entrypoint",
|
|
652
|
+
max_length=10000,
|
|
764
653
|
),
|
|
765
654
|
]
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
Optional[Dict[str, Any]],
|
|
655
|
+
telegram_config: Annotated[
|
|
656
|
+
dict | None,
|
|
769
657
|
PydanticField(
|
|
770
658
|
default=None,
|
|
771
|
-
description="
|
|
772
|
-
json_schema_extra={
|
|
773
|
-
"x-group": "skills",
|
|
774
|
-
"x-inline": True,
|
|
775
|
-
},
|
|
659
|
+
description="Telegram integration configuration settings",
|
|
776
660
|
),
|
|
777
661
|
]
|
|
778
|
-
|
|
779
|
-
|
|
662
|
+
discord_entrypoint_enabled: Annotated[
|
|
663
|
+
bool | None,
|
|
780
664
|
PydanticField(
|
|
781
|
-
default=
|
|
782
|
-
description="
|
|
665
|
+
default=False,
|
|
666
|
+
description="Whether the agent can play discord bot",
|
|
783
667
|
json_schema_extra={
|
|
784
|
-
"x-group": "
|
|
668
|
+
"x-group": "entrypoint",
|
|
785
669
|
},
|
|
786
670
|
),
|
|
787
671
|
]
|
|
788
|
-
|
|
789
|
-
|
|
672
|
+
discord_config: Annotated[
|
|
673
|
+
dict | None,
|
|
790
674
|
PydanticField(
|
|
791
675
|
default=None,
|
|
792
|
-
description="
|
|
793
|
-
),
|
|
794
|
-
]
|
|
795
|
-
network_id: Annotated[
|
|
796
|
-
Optional[
|
|
797
|
-
Literal[
|
|
798
|
-
"ethereum-mainnet",
|
|
799
|
-
"ethereum-sepolia",
|
|
800
|
-
"polygon-mainnet",
|
|
801
|
-
"polygon-mumbai",
|
|
802
|
-
"base-mainnet",
|
|
803
|
-
"base-sepolia",
|
|
804
|
-
"arbitrum-mainnet",
|
|
805
|
-
"arbitrum-sepolia",
|
|
806
|
-
"optimism-mainnet",
|
|
807
|
-
"optimism-sepolia",
|
|
808
|
-
"solana",
|
|
809
|
-
]
|
|
810
|
-
],
|
|
811
|
-
PydanticField(
|
|
812
|
-
default="base-mainnet",
|
|
813
|
-
description="Network identifier",
|
|
814
|
-
json_schema_extra={
|
|
815
|
-
"x-group": "onchain",
|
|
816
|
-
},
|
|
817
|
-
),
|
|
818
|
-
]
|
|
819
|
-
cdp_network_id: Annotated[
|
|
820
|
-
Optional[
|
|
821
|
-
Literal[
|
|
822
|
-
"ethereum-mainnet",
|
|
823
|
-
"ethereum-sepolia",
|
|
824
|
-
"polygon-mainnet",
|
|
825
|
-
"polygon-mumbai",
|
|
826
|
-
"base-mainnet",
|
|
827
|
-
"base-sepolia",
|
|
828
|
-
"arbitrum-mainnet",
|
|
829
|
-
"arbitrum-sepolia",
|
|
830
|
-
"optimism-mainnet",
|
|
831
|
-
"optimism-sepolia",
|
|
832
|
-
]
|
|
833
|
-
],
|
|
834
|
-
PydanticField(
|
|
835
|
-
default="base-mainnet",
|
|
836
|
-
description="Network identifier for CDP integration",
|
|
837
|
-
json_schema_extra={
|
|
838
|
-
"x-group": "deprecated",
|
|
839
|
-
},
|
|
840
|
-
),
|
|
841
|
-
]
|
|
842
|
-
# if telegram_entrypoint_enabled, the telegram_entrypoint_enabled will be enabled, telegram_config will be checked
|
|
843
|
-
telegram_entrypoint_enabled: Annotated[
|
|
844
|
-
Optional[bool],
|
|
845
|
-
PydanticField(
|
|
846
|
-
default=False,
|
|
847
|
-
description="Whether the agent can play telegram bot",
|
|
676
|
+
description="Discord integration configuration settings including token, whitelists, and behavior settings",
|
|
848
677
|
json_schema_extra={
|
|
849
678
|
"x-group": "entrypoint",
|
|
850
679
|
},
|
|
851
680
|
),
|
|
852
681
|
]
|
|
853
|
-
|
|
854
|
-
|
|
682
|
+
xmtp_entrypoint_prompt: Annotated[
|
|
683
|
+
str | None,
|
|
855
684
|
PydanticField(
|
|
856
685
|
default=None,
|
|
857
|
-
description="Extra prompt for
|
|
686
|
+
description="Extra prompt for xmtp entrypoint, xmtp support is in beta",
|
|
858
687
|
max_length=10000,
|
|
859
|
-
json_schema_extra={
|
|
860
|
-
"x-group": "entrypoint",
|
|
861
|
-
},
|
|
862
688
|
),
|
|
863
689
|
]
|
|
864
|
-
|
|
865
|
-
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
class AgentUpdate(AgentUserInput):
|
|
693
|
+
"""Agent update model."""
|
|
694
|
+
|
|
695
|
+
model_config = ConfigDict(
|
|
696
|
+
title="Agent",
|
|
697
|
+
from_attributes=True,
|
|
698
|
+
json_schema_extra={
|
|
699
|
+
"required": ["name"],
|
|
700
|
+
},
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
upstream_id: Annotated[
|
|
704
|
+
str | None,
|
|
866
705
|
PydanticField(
|
|
867
706
|
default=None,
|
|
868
|
-
description="
|
|
869
|
-
|
|
870
|
-
"x-group": "entrypoint",
|
|
871
|
-
},
|
|
707
|
+
description="External reference ID for idempotent operations",
|
|
708
|
+
max_length=100,
|
|
872
709
|
),
|
|
873
710
|
]
|
|
874
|
-
|
|
875
|
-
|
|
711
|
+
upstream_extra: Annotated[
|
|
712
|
+
dict[str, Any] | None,
|
|
876
713
|
PydanticField(
|
|
877
714
|
default=None,
|
|
878
|
-
description="
|
|
879
|
-
max_length=10000,
|
|
715
|
+
description="Additional data store for upstream use",
|
|
880
716
|
json_schema_extra={
|
|
881
|
-
"x-group": "
|
|
717
|
+
"x-group": "internal",
|
|
882
718
|
},
|
|
883
719
|
),
|
|
884
720
|
]
|
|
885
721
|
|
|
886
722
|
@field_validator("purpose", "personality", "principles", "prompt", "prompt_append")
|
|
887
723
|
@classmethod
|
|
888
|
-
def validate_no_level1_level2_headings(cls, v:
|
|
724
|
+
def validate_no_level1_level2_headings(cls, v: str | None) -> str | None:
|
|
889
725
|
"""Validate that the text doesn't contain level 1 or level 2 headings."""
|
|
890
726
|
if v is None:
|
|
891
727
|
return v
|
|
@@ -912,20 +748,25 @@ class AgentUpdate(BaseModel):
|
|
|
912
748
|
for autonomous_config in self.autonomous:
|
|
913
749
|
# Check that exactly one scheduling method is provided
|
|
914
750
|
if not autonomous_config.minutes and not autonomous_config.cron:
|
|
915
|
-
raise
|
|
916
|
-
status_code=400,
|
|
751
|
+
raise IntentKitAPIError(
|
|
752
|
+
status_code=400,
|
|
753
|
+
key="InvalidAutonomousConfig",
|
|
754
|
+
message="either minutes or cron must have a value",
|
|
917
755
|
)
|
|
918
756
|
|
|
919
757
|
if autonomous_config.minutes and autonomous_config.cron:
|
|
920
|
-
raise
|
|
921
|
-
status_code=400,
|
|
758
|
+
raise IntentKitAPIError(
|
|
759
|
+
status_code=400,
|
|
760
|
+
key="InvalidAutonomousConfig",
|
|
761
|
+
message="only one of minutes or cron can be set",
|
|
922
762
|
)
|
|
923
763
|
|
|
924
764
|
# Validate minimum interval of 5 minutes
|
|
925
765
|
if autonomous_config.minutes and autonomous_config.minutes < 5:
|
|
926
|
-
raise
|
|
766
|
+
raise IntentKitAPIError(
|
|
927
767
|
status_code=400,
|
|
928
|
-
|
|
768
|
+
key="InvalidAutonomousInterval",
|
|
769
|
+
message="The shortest execution interval is 5 minutes",
|
|
929
770
|
)
|
|
930
771
|
|
|
931
772
|
# Validate cron expression to ensure interval is at least 5 minutes
|
|
@@ -935,15 +776,18 @@ class AgentUpdate(BaseModel):
|
|
|
935
776
|
try:
|
|
936
777
|
CronValidator.parse(autonomous_config.cron)
|
|
937
778
|
except ValueError:
|
|
938
|
-
raise
|
|
779
|
+
raise IntentKitAPIError(
|
|
939
780
|
status_code=400,
|
|
940
|
-
|
|
781
|
+
key="InvalidCronExpression",
|
|
782
|
+
message=f"Invalid cron expression format: {autonomous_config.cron}",
|
|
941
783
|
)
|
|
942
784
|
|
|
943
785
|
parts = autonomous_config.cron.split()
|
|
944
786
|
if len(parts) < 5:
|
|
945
|
-
raise
|
|
946
|
-
status_code=400,
|
|
787
|
+
raise IntentKitAPIError(
|
|
788
|
+
status_code=400,
|
|
789
|
+
key="InvalidCronExpression",
|
|
790
|
+
message="Invalid cron expression format",
|
|
947
791
|
)
|
|
948
792
|
|
|
949
793
|
minute, hour, day_of_month, month, day_of_week = parts[:5]
|
|
@@ -951,27 +795,31 @@ class AgentUpdate(BaseModel):
|
|
|
951
795
|
# Check if minutes or hours have too frequent intervals
|
|
952
796
|
if "*" in minute and "*" in hour:
|
|
953
797
|
# If both minute and hour are wildcards, it would run every minute
|
|
954
|
-
raise
|
|
798
|
+
raise IntentKitAPIError(
|
|
955
799
|
status_code=400,
|
|
956
|
-
|
|
800
|
+
key="InvalidAutonomousInterval",
|
|
801
|
+
message="The shortest execution interval is 5 minutes",
|
|
957
802
|
)
|
|
958
803
|
|
|
959
804
|
if "/" in minute:
|
|
960
805
|
# Check step value in minute field (e.g., */15)
|
|
961
806
|
step = int(minute.split("/")[1])
|
|
962
807
|
if step < 5 and hour == "*":
|
|
963
|
-
raise
|
|
808
|
+
raise IntentKitAPIError(
|
|
964
809
|
status_code=400,
|
|
965
|
-
|
|
810
|
+
key="InvalidAutonomousInterval",
|
|
811
|
+
message="The shortest execution interval is 5 minutes",
|
|
966
812
|
)
|
|
967
813
|
|
|
968
814
|
# Check for comma-separated values or ranges that might result in multiple executions per hour
|
|
969
815
|
if ("," in minute or "-" in minute) and hour == "*":
|
|
970
|
-
raise
|
|
816
|
+
raise IntentKitAPIError(
|
|
971
817
|
status_code=400,
|
|
972
|
-
|
|
818
|
+
key="InvalidAutonomousInterval",
|
|
819
|
+
message="The shortest execution interval is 5 minutes",
|
|
973
820
|
)
|
|
974
821
|
|
|
822
|
+
# deprecated, use override instead
|
|
975
823
|
async def update(self, id: str) -> "Agent":
|
|
976
824
|
# Validate autonomous schedule settings if present
|
|
977
825
|
if "autonomous" in self.model_dump(exclude_unset=True):
|
|
@@ -980,16 +828,16 @@ class AgentUpdate(BaseModel):
|
|
|
980
828
|
async with get_session() as db:
|
|
981
829
|
db_agent = await db.get(AgentTable, id)
|
|
982
830
|
if not db_agent:
|
|
983
|
-
raise
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
status_code=403,
|
|
988
|
-
detail="You do not have permission to update this agent",
|
|
831
|
+
raise IntentKitAPIError(
|
|
832
|
+
status_code=404,
|
|
833
|
+
key="AgentNotFound",
|
|
834
|
+
message="Agent not found",
|
|
989
835
|
)
|
|
990
836
|
# update
|
|
991
837
|
for key, value in self.model_dump(exclude_unset=True).items():
|
|
992
838
|
setattr(db_agent, key, value)
|
|
839
|
+
db_agent.version = self.hash()
|
|
840
|
+
db_agent.deployed_at = func.now()
|
|
993
841
|
await db.commit()
|
|
994
842
|
await db.refresh(db_agent)
|
|
995
843
|
return Agent.model_validate(db_agent)
|
|
@@ -1002,16 +850,17 @@ class AgentUpdate(BaseModel):
|
|
|
1002
850
|
async with get_session() as db:
|
|
1003
851
|
db_agent = await db.get(AgentTable, id)
|
|
1004
852
|
if not db_agent:
|
|
1005
|
-
raise
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
status_code=403,
|
|
1010
|
-
detail="You do not have permission to update this agent",
|
|
853
|
+
raise IntentKitAPIError(
|
|
854
|
+
status_code=404,
|
|
855
|
+
key="AgentNotFound",
|
|
856
|
+
message="Agent not found",
|
|
1011
857
|
)
|
|
1012
858
|
# update
|
|
1013
859
|
for key, value in self.model_dump().items():
|
|
1014
860
|
setattr(db_agent, key, value)
|
|
861
|
+
# version
|
|
862
|
+
db_agent.version = self.hash()
|
|
863
|
+
db_agent.deployed_at = func.now()
|
|
1015
864
|
await db.commit()
|
|
1016
865
|
await db.refresh(db_agent)
|
|
1017
866
|
return Agent.model_validate(db_agent)
|
|
@@ -1030,6 +879,14 @@ class AgentCreate(AgentUpdate):
|
|
|
1030
879
|
max_length=67,
|
|
1031
880
|
),
|
|
1032
881
|
]
|
|
882
|
+
owner: Annotated[
|
|
883
|
+
str | None,
|
|
884
|
+
PydanticField(
|
|
885
|
+
default=None,
|
|
886
|
+
description="Owner identifier of the agent, used for access control",
|
|
887
|
+
max_length=50,
|
|
888
|
+
),
|
|
889
|
+
]
|
|
1033
890
|
|
|
1034
891
|
async def check_upstream_id(self) -> None:
|
|
1035
892
|
if not self.upstream_id:
|
|
@@ -1039,12 +896,13 @@ class AgentCreate(AgentUpdate):
|
|
|
1039
896
|
select(AgentTable).where(AgentTable.upstream_id == self.upstream_id)
|
|
1040
897
|
)
|
|
1041
898
|
if existing:
|
|
1042
|
-
raise
|
|
899
|
+
raise IntentKitAPIError(
|
|
1043
900
|
status_code=400,
|
|
1044
|
-
|
|
901
|
+
key="UpstreamIdConflict",
|
|
902
|
+
message="Upstream id already in use",
|
|
1045
903
|
)
|
|
1046
904
|
|
|
1047
|
-
async def get_by_upstream_id(self) ->
|
|
905
|
+
async def get_by_upstream_id(self) -> Agent | None:
|
|
1048
906
|
if not self.upstream_id:
|
|
1049
907
|
return None
|
|
1050
908
|
async with get_session() as db:
|
|
@@ -1061,46 +919,226 @@ class AgentCreate(AgentUpdate):
|
|
|
1061
919
|
self.validate_autonomous_schedule()
|
|
1062
920
|
|
|
1063
921
|
async with get_session() as db:
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
922
|
+
try:
|
|
923
|
+
db_agent = AgentTable(**self.model_dump())
|
|
924
|
+
db_agent.version = self.hash()
|
|
925
|
+
db_agent.deployed_at = func.now()
|
|
926
|
+
db.add(db_agent)
|
|
927
|
+
await db.commit()
|
|
928
|
+
await db.refresh(db_agent)
|
|
929
|
+
return Agent.model_validate(db_agent)
|
|
930
|
+
except IntegrityError:
|
|
931
|
+
await db.rollback()
|
|
932
|
+
raise IntentKitAPIError(
|
|
933
|
+
status_code=400,
|
|
934
|
+
key="AgentExists",
|
|
935
|
+
message=f"Agent with ID '{self.id}' already exists",
|
|
936
|
+
)
|
|
1069
937
|
|
|
1070
|
-
async def create_or_update(self) -> ("Agent", bool):
|
|
1071
|
-
# Validation is now handled by field validators
|
|
1072
|
-
await self.check_upstream_id()
|
|
1073
938
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
939
|
+
class AgentPublicInfo(BaseModel):
|
|
940
|
+
"""Public information of the agent."""
|
|
941
|
+
|
|
942
|
+
model_config = ConfigDict(
|
|
943
|
+
title="AgentPublicInfo",
|
|
944
|
+
from_attributes=True,
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
description: Annotated[
|
|
948
|
+
str | None,
|
|
949
|
+
PydanticField(
|
|
950
|
+
default=None,
|
|
951
|
+
description="Description of the agent, for public view, not contained in prompt",
|
|
952
|
+
json_schema_extra={
|
|
953
|
+
"x-placeholder": "Introduce your agent",
|
|
954
|
+
},
|
|
955
|
+
),
|
|
956
|
+
]
|
|
957
|
+
external_website: Annotated[
|
|
958
|
+
str | None,
|
|
959
|
+
PydanticField(
|
|
960
|
+
default=None,
|
|
961
|
+
description="Link of external website of the agent, if you have one",
|
|
962
|
+
json_schema_extra={
|
|
963
|
+
"x-placeholder": "Enter agent external website url",
|
|
964
|
+
"format": "uri",
|
|
965
|
+
},
|
|
966
|
+
),
|
|
967
|
+
]
|
|
968
|
+
ticker: Annotated[
|
|
969
|
+
str | None,
|
|
970
|
+
PydanticField(
|
|
971
|
+
default=None,
|
|
972
|
+
description="Ticker symbol of the agent",
|
|
973
|
+
max_length=10,
|
|
974
|
+
min_length=1,
|
|
975
|
+
json_schema_extra={
|
|
976
|
+
"x-placeholder": "If one day, your agent has it's own token, what will it be?",
|
|
977
|
+
},
|
|
978
|
+
),
|
|
979
|
+
]
|
|
980
|
+
token_address: Annotated[
|
|
981
|
+
str | None,
|
|
982
|
+
PydanticField(
|
|
983
|
+
default=None,
|
|
984
|
+
description="Token address of the agent",
|
|
985
|
+
max_length=66,
|
|
986
|
+
json_schema_extra={
|
|
987
|
+
"x-placeholder": "The contract address of the agent token",
|
|
988
|
+
},
|
|
989
|
+
),
|
|
990
|
+
]
|
|
991
|
+
token_pool: Annotated[
|
|
992
|
+
str | None,
|
|
993
|
+
PydanticField(
|
|
994
|
+
default=None,
|
|
995
|
+
description="Pool of the agent token",
|
|
996
|
+
max_length=66,
|
|
997
|
+
json_schema_extra={
|
|
998
|
+
"x-placeholder": "The contract address of the agent token pool",
|
|
999
|
+
},
|
|
1000
|
+
),
|
|
1001
|
+
]
|
|
1002
|
+
fee_percentage: Annotated[
|
|
1003
|
+
Decimal | None,
|
|
1004
|
+
PydanticField(
|
|
1005
|
+
default=None,
|
|
1006
|
+
description="Fee percentage of the agent",
|
|
1007
|
+
ge=Decimal("0.0"),
|
|
1008
|
+
json_schema_extra={
|
|
1009
|
+
"x-placeholder": "Agent will charge service fee according to this ratio.",
|
|
1010
|
+
},
|
|
1011
|
+
),
|
|
1012
|
+
]
|
|
1013
|
+
example_intro: Annotated[
|
|
1014
|
+
str | None,
|
|
1015
|
+
PydanticField(
|
|
1016
|
+
default=None,
|
|
1017
|
+
description="Introduction of the example",
|
|
1018
|
+
max_length=2000,
|
|
1019
|
+
json_schema_extra={
|
|
1020
|
+
"x-placeholder": "Add a short introduction in new chat",
|
|
1021
|
+
},
|
|
1022
|
+
),
|
|
1023
|
+
]
|
|
1024
|
+
examples: Annotated[
|
|
1025
|
+
list[AgentExample] | None,
|
|
1026
|
+
PydanticField(
|
|
1027
|
+
default=None,
|
|
1028
|
+
description="List of example prompts for the agent",
|
|
1029
|
+
max_length=6,
|
|
1030
|
+
json_schema_extra={
|
|
1031
|
+
"x-inline": True,
|
|
1032
|
+
},
|
|
1033
|
+
),
|
|
1034
|
+
]
|
|
1035
|
+
public_extra: Annotated[
|
|
1036
|
+
dict[str, Any] | None,
|
|
1037
|
+
PydanticField(
|
|
1038
|
+
default=None,
|
|
1039
|
+
description="Public extra data of the agent",
|
|
1040
|
+
),
|
|
1041
|
+
]
|
|
1042
|
+
|
|
1043
|
+
async def override(self, agent_id: str) -> "Agent":
|
|
1044
|
+
"""Override agent public info with all fields from this instance.
|
|
1045
|
+
|
|
1046
|
+
Args:
|
|
1047
|
+
agent_id: The ID of the agent to override
|
|
1048
|
+
|
|
1049
|
+
Returns:
|
|
1050
|
+
The updated Agent instance
|
|
1051
|
+
"""
|
|
1052
|
+
async with get_session() as session:
|
|
1053
|
+
# Get the agent from database
|
|
1054
|
+
result = await session.execute(
|
|
1055
|
+
select(AgentTable).where(AgentTable.id == agent_id)
|
|
1056
|
+
)
|
|
1057
|
+
db_agent = result.scalar_one_or_none()
|
|
1077
1058
|
|
|
1078
|
-
is_new = False
|
|
1079
|
-
async with get_session() as db:
|
|
1080
|
-
db_agent = await db.get(AgentTable, self.id)
|
|
1081
1059
|
if not db_agent:
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
if
|
|
1088
|
-
raise HTTPException(
|
|
1089
|
-
status_code=403,
|
|
1090
|
-
detail="You do not have permission to update this agent",
|
|
1091
|
-
)
|
|
1092
|
-
for key, value in self.model_dump(exclude_unset=True).items():
|
|
1060
|
+
raise IntentKitAPIError(404, "NotFound", f"Agent {agent_id} not found")
|
|
1061
|
+
|
|
1062
|
+
# Update public info fields
|
|
1063
|
+
update_data = self.model_dump()
|
|
1064
|
+
for key, value in update_data.items():
|
|
1065
|
+
if hasattr(db_agent, key):
|
|
1093
1066
|
setattr(db_agent, key, value)
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1067
|
+
|
|
1068
|
+
# Update public_info_updated_at timestamp
|
|
1069
|
+
db_agent.public_info_updated_at = func.now()
|
|
1070
|
+
|
|
1071
|
+
# Commit changes
|
|
1072
|
+
await session.commit()
|
|
1073
|
+
await session.refresh(db_agent)
|
|
1074
|
+
|
|
1075
|
+
return Agent.model_validate(db_agent)
|
|
1097
1076
|
|
|
1098
1077
|
|
|
1099
|
-
class Agent(AgentCreate):
|
|
1078
|
+
class Agent(AgentCreate, AgentPublicInfo):
|
|
1100
1079
|
"""Agent model."""
|
|
1101
1080
|
|
|
1102
1081
|
model_config = ConfigDict(from_attributes=True)
|
|
1103
1082
|
|
|
1083
|
+
slug: Annotated[
|
|
1084
|
+
str | None,
|
|
1085
|
+
PydanticField(
|
|
1086
|
+
default=None,
|
|
1087
|
+
description="Slug of the agent, used for URL generation",
|
|
1088
|
+
max_length=100,
|
|
1089
|
+
min_length=2,
|
|
1090
|
+
),
|
|
1091
|
+
]
|
|
1092
|
+
version: Annotated[
|
|
1093
|
+
str | None,
|
|
1094
|
+
PydanticField(
|
|
1095
|
+
default=None,
|
|
1096
|
+
description="Version hash of the agent",
|
|
1097
|
+
),
|
|
1098
|
+
]
|
|
1099
|
+
statistics: Annotated[
|
|
1100
|
+
dict[str, Any] | None,
|
|
1101
|
+
PydanticField(
|
|
1102
|
+
default=None,
|
|
1103
|
+
description="Statistics of the agent, update every 1 hour for query",
|
|
1104
|
+
),
|
|
1105
|
+
]
|
|
1106
|
+
assets: Annotated[
|
|
1107
|
+
dict[str, Any] | None,
|
|
1108
|
+
PydanticField(
|
|
1109
|
+
default=None,
|
|
1110
|
+
description="Assets of the agent, update every 1 hour for query",
|
|
1111
|
+
),
|
|
1112
|
+
]
|
|
1113
|
+
account_snapshot: Annotated[
|
|
1114
|
+
CreditAccount | None,
|
|
1115
|
+
PydanticField(
|
|
1116
|
+
default=None,
|
|
1117
|
+
description="Account snapshot of the agent, update every 1 hour for query",
|
|
1118
|
+
),
|
|
1119
|
+
]
|
|
1120
|
+
extra: Annotated[
|
|
1121
|
+
dict[str, Any] | None,
|
|
1122
|
+
PydanticField(
|
|
1123
|
+
default=None,
|
|
1124
|
+
description="Other helper data fields for query, come from agent and agent data",
|
|
1125
|
+
),
|
|
1126
|
+
]
|
|
1127
|
+
deployed_at: Annotated[
|
|
1128
|
+
datetime | None,
|
|
1129
|
+
PydanticField(
|
|
1130
|
+
default=None,
|
|
1131
|
+
description="Timestamp when the agent was deployed",
|
|
1132
|
+
),
|
|
1133
|
+
]
|
|
1134
|
+
public_info_updated_at: Annotated[
|
|
1135
|
+
datetime | None,
|
|
1136
|
+
PydanticField(
|
|
1137
|
+
default=None,
|
|
1138
|
+
description="Timestamp when the agent public info was last updated",
|
|
1139
|
+
),
|
|
1140
|
+
]
|
|
1141
|
+
|
|
1104
1142
|
# auto timestamp
|
|
1105
1143
|
created_at: Annotated[
|
|
1106
1144
|
datetime,
|
|
@@ -1135,8 +1173,11 @@ class Agent(AgentCreate):
|
|
|
1135
1173
|
return False
|
|
1136
1174
|
|
|
1137
1175
|
async def is_model_support_image(self) -> bool:
|
|
1138
|
-
|
|
1139
|
-
|
|
1176
|
+
try:
|
|
1177
|
+
model = await LLMModelInfo.get(self.model)
|
|
1178
|
+
return model.supports_image_input
|
|
1179
|
+
except Exception:
|
|
1180
|
+
return False
|
|
1140
1181
|
|
|
1141
1182
|
def to_yaml(self) -> str:
|
|
1142
1183
|
"""
|
|
@@ -1229,7 +1270,7 @@ class Agent(AgentCreate):
|
|
|
1229
1270
|
)
|
|
1230
1271
|
yaml_lines.append(yaml_value.rstrip())
|
|
1231
1272
|
elif isinstance(value, list) and value and hasattr(value[0], "model_dump"):
|
|
1232
|
-
# Handle list of Pydantic models (e.g.,
|
|
1273
|
+
# Handle list of Pydantic models (e.g., list[AgentAutonomous])
|
|
1233
1274
|
yaml_lines.append(f"{field_name}:")
|
|
1234
1275
|
# Convert each Pydantic model to dict
|
|
1235
1276
|
model_dicts = [item.model_dump(exclude_none=True) for item in value]
|
|
@@ -1244,20 +1285,22 @@ class Agent(AgentCreate):
|
|
|
1244
1285
|
yaml_lines.append(indented_yaml.rstrip())
|
|
1245
1286
|
elif hasattr(value, "model_dump"):
|
|
1246
1287
|
# Handle individual Pydantic model
|
|
1247
|
-
|
|
1288
|
+
yaml_lines.append(f"{field_name}:")
|
|
1248
1289
|
yaml_value = yaml.dump(
|
|
1249
|
-
|
|
1290
|
+
value.model_dump(exclude_none=True),
|
|
1250
1291
|
default_flow_style=False,
|
|
1251
1292
|
allow_unicode=True,
|
|
1252
1293
|
)
|
|
1253
|
-
yaml_lines
|
|
1294
|
+
# Indent all lines and append to yaml_lines
|
|
1295
|
+
indented_yaml = "\n".join(
|
|
1296
|
+
f" {line}" for line in yaml_value.split("\n") if line.strip()
|
|
1297
|
+
)
|
|
1298
|
+
yaml_lines.append(indented_yaml)
|
|
1254
1299
|
else:
|
|
1255
|
-
# Handle Decimal
|
|
1300
|
+
# Handle Decimal and other types
|
|
1256
1301
|
if isinstance(value, Decimal):
|
|
1257
|
-
|
|
1258
|
-
yaml_lines.append(f"{field_name}: {value}")
|
|
1302
|
+
yaml_lines.append(f"{field_name}: {str(value)}")
|
|
1259
1303
|
else:
|
|
1260
|
-
# Handle other non-string values
|
|
1261
1304
|
yaml_value = yaml.dump(
|
|
1262
1305
|
{field_name: value},
|
|
1263
1306
|
default_flow_style=False,
|
|
@@ -1273,18 +1316,154 @@ class Agent(AgentCreate):
|
|
|
1273
1316
|
return await db.scalar(select(func.count(AgentTable.id)))
|
|
1274
1317
|
|
|
1275
1318
|
@classmethod
|
|
1276
|
-
async def get(cls, agent_id: str) ->
|
|
1319
|
+
async def get(cls, agent_id: str) -> "Agent" | None:
|
|
1277
1320
|
async with get_session() as db:
|
|
1278
1321
|
item = await db.scalar(select(AgentTable).where(AgentTable.id == agent_id))
|
|
1279
1322
|
if item is None:
|
|
1280
1323
|
return None
|
|
1281
1324
|
return cls.model_validate(item)
|
|
1282
1325
|
|
|
1283
|
-
|
|
1326
|
+
@classmethod
|
|
1327
|
+
async def get_by_id_or_slug(cls, agent_id: str) -> "Agent" | None:
|
|
1328
|
+
"""Get agent by ID or slug.
|
|
1329
|
+
|
|
1330
|
+
First tries to get by ID if agent_id length <= 20,
|
|
1331
|
+
then falls back to searching by slug if not found.
|
|
1332
|
+
|
|
1333
|
+
Args:
|
|
1334
|
+
agent_id: Agent ID or slug to search for
|
|
1335
|
+
|
|
1336
|
+
Returns:
|
|
1337
|
+
Agent if found, None otherwise
|
|
1338
|
+
"""
|
|
1339
|
+
async with get_session() as db:
|
|
1340
|
+
agent = None
|
|
1341
|
+
|
|
1342
|
+
# Try to get by ID if length <= 20
|
|
1343
|
+
if len(agent_id) <= 20:
|
|
1344
|
+
agent = await Agent.get(agent_id)
|
|
1345
|
+
|
|
1346
|
+
# If not found, try to get by slug
|
|
1347
|
+
if agent is None:
|
|
1348
|
+
slug_stmt = select(AgentTable).where(AgentTable.slug == agent_id)
|
|
1349
|
+
agent_row = await db.scalar(slug_stmt)
|
|
1350
|
+
if agent_row is not None:
|
|
1351
|
+
agent = Agent.model_validate(agent_row)
|
|
1352
|
+
|
|
1353
|
+
return agent
|
|
1354
|
+
|
|
1355
|
+
@staticmethod
|
|
1356
|
+
def _deserialize_autonomous(
|
|
1357
|
+
autonomous_data: list[Any] | None,
|
|
1358
|
+
) -> list[AgentAutonomous]:
|
|
1359
|
+
if not autonomous_data:
|
|
1360
|
+
return []
|
|
1361
|
+
|
|
1362
|
+
deserialized: list[AgentAutonomous] = []
|
|
1363
|
+
for entry in autonomous_data:
|
|
1364
|
+
if isinstance(entry, AgentAutonomous):
|
|
1365
|
+
deserialized.append(entry)
|
|
1366
|
+
else:
|
|
1367
|
+
deserialized.append(AgentAutonomous.model_validate(entry))
|
|
1368
|
+
return deserialized
|
|
1369
|
+
|
|
1370
|
+
@staticmethod
|
|
1371
|
+
def _serialize_autonomous(tasks: list[AgentAutonomous]) -> list[dict[str, Any]]:
|
|
1372
|
+
return [task.model_dump() for task in tasks]
|
|
1373
|
+
|
|
1374
|
+
@staticmethod
|
|
1375
|
+
def _autonomous_not_allowed_error() -> IntentKitAPIError:
|
|
1376
|
+
return IntentKitAPIError(
|
|
1377
|
+
400,
|
|
1378
|
+
"AgentNotDeployed",
|
|
1379
|
+
"Only deployed agents can call this feature.",
|
|
1380
|
+
)
|
|
1381
|
+
|
|
1382
|
+
async def list_autonomous_tasks(self) -> list[AgentAutonomous]:
|
|
1383
|
+
persisted = await Agent.get(self.id)
|
|
1384
|
+
if persisted is None:
|
|
1385
|
+
raise self._autonomous_not_allowed_error()
|
|
1386
|
+
|
|
1387
|
+
tasks = persisted.autonomous or []
|
|
1388
|
+
# Keep local state in sync with persisted data
|
|
1389
|
+
self.autonomous = tasks
|
|
1390
|
+
return tasks
|
|
1391
|
+
|
|
1392
|
+
async def add_autonomous_task(self, task: AgentAutonomous) -> AgentAutonomous:
|
|
1393
|
+
async with get_session() as session:
|
|
1394
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1395
|
+
if db_agent is None:
|
|
1396
|
+
raise self._autonomous_not_allowed_error()
|
|
1397
|
+
|
|
1398
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1399
|
+
current_tasks.append(task)
|
|
1400
|
+
|
|
1401
|
+
db_agent.autonomous = self._serialize_autonomous(current_tasks)
|
|
1402
|
+
await session.commit()
|
|
1403
|
+
|
|
1404
|
+
self.autonomous = current_tasks
|
|
1405
|
+
return task
|
|
1406
|
+
|
|
1407
|
+
async def delete_autonomous_task(self, task_id: str) -> None:
|
|
1408
|
+
async with get_session() as session:
|
|
1409
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1410
|
+
if db_agent is None:
|
|
1411
|
+
raise self._autonomous_not_allowed_error()
|
|
1412
|
+
|
|
1413
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1414
|
+
|
|
1415
|
+
updated_tasks = [task for task in current_tasks if task.id != task_id]
|
|
1416
|
+
if len(updated_tasks) == len(current_tasks):
|
|
1417
|
+
raise IntentKitAPIError(
|
|
1418
|
+
404,
|
|
1419
|
+
"TaskNotFound",
|
|
1420
|
+
f"Autonomous task with ID {task_id} not found.",
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
db_agent.autonomous = self._serialize_autonomous(updated_tasks)
|
|
1424
|
+
await session.commit()
|
|
1425
|
+
|
|
1426
|
+
self.autonomous = updated_tasks
|
|
1427
|
+
|
|
1428
|
+
async def update_autonomous_task(
|
|
1429
|
+
self, task_id: str, task_updates: dict
|
|
1430
|
+
) -> AgentAutonomous:
|
|
1431
|
+
async with get_session() as session:
|
|
1432
|
+
db_agent = await session.get(AgentTable, self.id)
|
|
1433
|
+
if db_agent is None:
|
|
1434
|
+
raise self._autonomous_not_allowed_error()
|
|
1435
|
+
|
|
1436
|
+
current_tasks = self._deserialize_autonomous(db_agent.autonomous)
|
|
1437
|
+
|
|
1438
|
+
updated_task: AgentAutonomous | None = None
|
|
1439
|
+
rewritten_tasks: list[AgentAutonomous] = []
|
|
1440
|
+
for task in current_tasks:
|
|
1441
|
+
if task.id == task_id:
|
|
1442
|
+
task_dict = task.model_dump()
|
|
1443
|
+
task_dict.update(task_updates)
|
|
1444
|
+
updated_task = AgentAutonomous.model_validate(task_dict)
|
|
1445
|
+
rewritten_tasks.append(updated_task)
|
|
1446
|
+
else:
|
|
1447
|
+
rewritten_tasks.append(task)
|
|
1448
|
+
|
|
1449
|
+
if updated_task is None:
|
|
1450
|
+
raise IntentKitAPIError(
|
|
1451
|
+
404,
|
|
1452
|
+
"TaskNotFound",
|
|
1453
|
+
f"Autonomous task with ID {task_id} not found.",
|
|
1454
|
+
)
|
|
1455
|
+
|
|
1456
|
+
db_agent.autonomous = self._serialize_autonomous(rewritten_tasks)
|
|
1457
|
+
await session.commit()
|
|
1458
|
+
|
|
1459
|
+
self.autonomous = rewritten_tasks
|
|
1460
|
+
return updated_task
|
|
1461
|
+
|
|
1462
|
+
def skill_config(self, category: str) -> dict[str, Any]:
|
|
1284
1463
|
return self.skills.get(category, {}) if self.skills else {}
|
|
1285
1464
|
|
|
1286
1465
|
@staticmethod
|
|
1287
|
-
def _is_agent_owner_only_skill(skill_schema:
|
|
1466
|
+
def _is_agent_owner_only_skill(skill_schema: dict[str, Any]) -> bool:
|
|
1288
1467
|
"""Check if a skill requires agent owner API keys only based on its resolved schema."""
|
|
1289
1468
|
if (
|
|
1290
1469
|
skill_schema
|
|
@@ -1303,8 +1482,7 @@ class Agent(AgentCreate):
|
|
|
1303
1482
|
cls,
|
|
1304
1483
|
db: AsyncSession = None,
|
|
1305
1484
|
filter_owner_api_skills: bool = False,
|
|
1306
|
-
|
|
1307
|
-
) -> Dict:
|
|
1485
|
+
) -> dict:
|
|
1308
1486
|
"""Get the JSON schema for Agent model with all $ref references resolved.
|
|
1309
1487
|
|
|
1310
1488
|
This is the shared function that handles admin configuration filtering
|
|
@@ -1313,7 +1491,6 @@ class Agent(AgentCreate):
|
|
|
1313
1491
|
Args:
|
|
1314
1492
|
db: Database session (optional, will create if not provided)
|
|
1315
1493
|
filter_owner_api_skills: Whether to filter out skills that require agent owner API keys
|
|
1316
|
-
admin_llm_skill_control: Whether to enable admin LLM and skill control features
|
|
1317
1494
|
|
|
1318
1495
|
Returns:
|
|
1319
1496
|
Dict containing the complete JSON schema for the Agent model
|
|
@@ -1321,9 +1498,7 @@ class Agent(AgentCreate):
|
|
|
1321
1498
|
# Get database session if not provided
|
|
1322
1499
|
if db is None:
|
|
1323
1500
|
async with get_session() as session:
|
|
1324
|
-
return await cls.get_json_schema(
|
|
1325
|
-
session, filter_owner_api_skills, admin_llm_skill_control
|
|
1326
|
-
)
|
|
1501
|
+
return await cls.get_json_schema(session, filter_owner_api_skills)
|
|
1327
1502
|
|
|
1328
1503
|
# Get the schema file path relative to this file
|
|
1329
1504
|
current_dir = Path(__file__).parent
|
|
@@ -1336,400 +1511,260 @@ class Agent(AgentCreate):
|
|
|
1336
1511
|
# Get the model property from the schema
|
|
1337
1512
|
model_property = schema.get("properties", {}).get("model", {})
|
|
1338
1513
|
|
|
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
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
for category, price_levels in category_price_levels.items():
|
|
1427
|
-
if price_levels:
|
|
1428
|
-
avg_price_level = int(sum(price_levels) / len(price_levels))
|
|
1429
|
-
category_avg_price_levels[category] = avg_price_level
|
|
1430
|
-
|
|
1431
|
-
# Create a copy of keys to avoid modifying during iteration
|
|
1432
|
-
skill_keys = list(skills_properties.keys())
|
|
1433
|
-
|
|
1434
|
-
# Process each skill in the schema
|
|
1435
|
-
for skill_category in skill_keys:
|
|
1436
|
-
if skill_category not in category_status:
|
|
1437
|
-
# If category not found in database, remove it from schema
|
|
1438
|
-
skills_properties.pop(skill_category, None)
|
|
1439
|
-
elif not category_status[skill_category]:
|
|
1440
|
-
# If category exists but all skills are disabled, remove it
|
|
1441
|
-
skills_properties.pop(skill_category, None)
|
|
1442
|
-
elif filter_owner_api_skills and cls._is_agent_owner_only_skill(
|
|
1443
|
-
skills_properties[skill_category]
|
|
1514
|
+
# Process model property using defaults merged with database overrides
|
|
1515
|
+
if model_property:
|
|
1516
|
+
new_enum = []
|
|
1517
|
+
new_enum_title = []
|
|
1518
|
+
new_enum_category = []
|
|
1519
|
+
new_enum_support_skill = []
|
|
1520
|
+
|
|
1521
|
+
for model_info in await LLMModelInfo.get_all(db):
|
|
1522
|
+
if not model_info.enabled:
|
|
1523
|
+
continue
|
|
1524
|
+
|
|
1525
|
+
provider = (
|
|
1526
|
+
LLMProvider(model_info.provider)
|
|
1527
|
+
if isinstance(model_info.provider, str)
|
|
1528
|
+
else model_info.provider
|
|
1529
|
+
)
|
|
1530
|
+
|
|
1531
|
+
new_enum.append(model_info.id)
|
|
1532
|
+
new_enum_title.append(model_info.name)
|
|
1533
|
+
new_enum_category.append(provider.display_name())
|
|
1534
|
+
new_enum_support_skill.append(model_info.supports_skill_calls)
|
|
1535
|
+
|
|
1536
|
+
model_property["enum"] = new_enum
|
|
1537
|
+
model_property["x-enum-title"] = new_enum_title
|
|
1538
|
+
model_property["x-enum-category"] = new_enum_category
|
|
1539
|
+
model_property["x-support-skill"] = new_enum_support_skill
|
|
1540
|
+
|
|
1541
|
+
if (
|
|
1542
|
+
"default" in model_property
|
|
1543
|
+
and model_property["default"] not in new_enum
|
|
1544
|
+
and new_enum
|
|
1545
|
+
):
|
|
1546
|
+
model_property["default"] = new_enum[0]
|
|
1547
|
+
|
|
1548
|
+
# Process skills property using data from Skill.get_all instead of agent_schema.json
|
|
1549
|
+
skills_property = schema.get("properties", {}).get("skills", {})
|
|
1550
|
+
|
|
1551
|
+
# Build skill_states_map from database
|
|
1552
|
+
skill_states_map: dict[str, dict[str, Skill]] = {}
|
|
1553
|
+
for skill_model in await Skill.get_all(db):
|
|
1554
|
+
if not skill_model.config_name:
|
|
1555
|
+
continue
|
|
1556
|
+
category_states = skill_states_map.setdefault(skill_model.category, {})
|
|
1557
|
+
if skill_model.enabled:
|
|
1558
|
+
category_states[skill_model.config_name] = skill_model
|
|
1559
|
+
else:
|
|
1560
|
+
category_states.pop(skill_model.config_name, None)
|
|
1561
|
+
|
|
1562
|
+
enabled_categories = {
|
|
1563
|
+
category for category, states in skill_states_map.items() if states
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
# Calculate price levels and skills data
|
|
1567
|
+
category_avg_price_levels = {}
|
|
1568
|
+
skills_data = {}
|
|
1569
|
+
for category, states in skill_states_map.items():
|
|
1570
|
+
if not states:
|
|
1571
|
+
continue
|
|
1572
|
+
price_levels = [
|
|
1573
|
+
state.price_level
|
|
1574
|
+
for state in states.values()
|
|
1575
|
+
if state.price_level is not None
|
|
1576
|
+
]
|
|
1577
|
+
if price_levels:
|
|
1578
|
+
category_avg_price_levels[category] = int(
|
|
1579
|
+
sum(price_levels) / len(price_levels)
|
|
1580
|
+
)
|
|
1581
|
+
skills_data[category] = {
|
|
1582
|
+
config_name: state.price_level
|
|
1583
|
+
for config_name, state in states.items()
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
# Dynamically generate skills_properties from Skill.get_all data
|
|
1587
|
+
skills_properties = {}
|
|
1588
|
+
current_dir = Path(__file__).parent
|
|
1589
|
+
|
|
1590
|
+
for category in enabled_categories:
|
|
1591
|
+
# Skip if filtered for auto-generation
|
|
1592
|
+
skill_schema_path = current_dir / f"../skills/{category}/schema.json"
|
|
1593
|
+
if skill_schema_path.exists():
|
|
1594
|
+
try:
|
|
1595
|
+
with open(skill_schema_path) as f:
|
|
1596
|
+
skill_schema = json.load(f)
|
|
1597
|
+
|
|
1598
|
+
# Check if this skill should be filtered for owner API requirements
|
|
1599
|
+
if filter_owner_api_skills and cls._is_agent_owner_only_skill(
|
|
1600
|
+
skill_schema
|
|
1444
1601
|
):
|
|
1445
|
-
# If filtering owner API skills and this skill requires it, remove it
|
|
1446
|
-
skills_properties.pop(skill_category, None)
|
|
1447
1602
|
logger.info(
|
|
1448
|
-
f"Filtered out skill '{
|
|
1603
|
+
f"Filtered out skill '{category}' from auto-generation: requires agent owner API key"
|
|
1604
|
+
)
|
|
1605
|
+
continue
|
|
1606
|
+
|
|
1607
|
+
# Create skill property with embedded schema instead of reference
|
|
1608
|
+
# Load and embed the full skill schema directly
|
|
1609
|
+
base_uri = f"file://{skill_schema_path}"
|
|
1610
|
+
with open(skill_schema_path) as f:
|
|
1611
|
+
embedded_skill_schema = jsonref.load(
|
|
1612
|
+
f, base_uri=base_uri, proxies=False, lazy_load=False
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
skills_properties[category] = {
|
|
1616
|
+
"title": skill_schema.get("title", category.title()),
|
|
1617
|
+
**embedded_skill_schema, # Embed the full schema instead of using $ref
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
# Add price level information
|
|
1621
|
+
if category in category_avg_price_levels:
|
|
1622
|
+
skills_properties[category]["x-avg-price-level"] = (
|
|
1623
|
+
category_avg_price_levels[category]
|
|
1624
|
+
)
|
|
1625
|
+
|
|
1626
|
+
if category in skills_data:
|
|
1627
|
+
# Add price level to states in the embedded schema
|
|
1628
|
+
skill_states = (
|
|
1629
|
+
skills_properties[category]
|
|
1630
|
+
.get("properties", {})
|
|
1631
|
+
.get("states", {})
|
|
1632
|
+
.get("properties", {})
|
|
1449
1633
|
)
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
state_name in skills_data[skill_category]
|
|
1468
|
-
and skills_data[skill_category][state_name]
|
|
1469
|
-
is not None
|
|
1470
|
-
):
|
|
1471
|
-
state_config["x-price-level"] = skills_data[
|
|
1472
|
-
skill_category
|
|
1473
|
-
][state_name]
|
|
1634
|
+
for state_name, state_config in skill_states.items():
|
|
1635
|
+
if (
|
|
1636
|
+
state_name in skills_data[category]
|
|
1637
|
+
and skills_data[category][state_name] is not None
|
|
1638
|
+
):
|
|
1639
|
+
state_config["x-price-level"] = skills_data[
|
|
1640
|
+
category
|
|
1641
|
+
][state_name]
|
|
1642
|
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
1643
|
+
logger.warning(
|
|
1644
|
+
f"Could not load schema for skill category '{category}': {e}"
|
|
1645
|
+
)
|
|
1646
|
+
continue
|
|
1647
|
+
|
|
1648
|
+
# Update the skills property in the schema
|
|
1649
|
+
if skills_property:
|
|
1650
|
+
skills_property["properties"] = skills_properties
|
|
1474
1651
|
|
|
1475
1652
|
# Log the changes for debugging
|
|
1476
1653
|
logger.debug(
|
|
1477
|
-
|
|
1478
|
-
|
|
1654
|
+
"Schema processed with merged LLM/skill defaults; filtered owner API skills: %s",
|
|
1655
|
+
filter_owner_api_skills,
|
|
1479
1656
|
)
|
|
1480
1657
|
|
|
1481
1658
|
return schema
|
|
1482
1659
|
|
|
1483
1660
|
|
|
1484
|
-
class AgentResponse(
|
|
1485
|
-
"""
|
|
1661
|
+
class AgentResponse(Agent):
|
|
1662
|
+
"""Agent response model that excludes sensitive fields from JSON output and schema."""
|
|
1486
1663
|
|
|
1487
1664
|
model_config = ConfigDict(
|
|
1665
|
+
title="AgentPublic",
|
|
1488
1666
|
from_attributes=True,
|
|
1489
|
-
json_encoders={
|
|
1490
|
-
|
|
1491
|
-
},
|
|
1667
|
+
# json_encoders={
|
|
1668
|
+
# datetime: lambda v: v.isoformat(timespec="milliseconds"),
|
|
1669
|
+
# },
|
|
1492
1670
|
)
|
|
1493
1671
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
]
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
description="Timestamp when the agent was last updated, will ignore when importing"
|
|
1511
|
-
),
|
|
1512
|
-
]
|
|
1513
|
-
# Agent part
|
|
1514
|
-
name: Annotated[
|
|
1515
|
-
Optional[str],
|
|
1516
|
-
PydanticField(
|
|
1517
|
-
default=None,
|
|
1518
|
-
description="Display name of the agent",
|
|
1519
|
-
),
|
|
1520
|
-
]
|
|
1521
|
-
slug: Annotated[
|
|
1522
|
-
Optional[str],
|
|
1523
|
-
PydanticField(
|
|
1524
|
-
default=None,
|
|
1525
|
-
description="Slug of the agent, used for URL generation",
|
|
1526
|
-
),
|
|
1527
|
-
]
|
|
1528
|
-
description: Annotated[
|
|
1529
|
-
Optional[str],
|
|
1530
|
-
PydanticField(
|
|
1531
|
-
default=None,
|
|
1532
|
-
description="Description of the agent, for public view, not contained in prompt",
|
|
1533
|
-
),
|
|
1534
|
-
]
|
|
1535
|
-
external_website: Annotated[
|
|
1536
|
-
Optional[str],
|
|
1537
|
-
PydanticField(
|
|
1538
|
-
default=None,
|
|
1539
|
-
description="Link of external website of the agent, if you have one",
|
|
1540
|
-
),
|
|
1541
|
-
]
|
|
1542
|
-
picture: Annotated[
|
|
1543
|
-
Optional[str],
|
|
1544
|
-
PydanticField(
|
|
1545
|
-
default=None,
|
|
1546
|
-
description="Picture of the agent",
|
|
1547
|
-
),
|
|
1548
|
-
]
|
|
1549
|
-
ticker: Annotated[
|
|
1550
|
-
Optional[str],
|
|
1551
|
-
PydanticField(
|
|
1552
|
-
default=None,
|
|
1553
|
-
description="Ticker symbol of the agent",
|
|
1554
|
-
),
|
|
1555
|
-
]
|
|
1556
|
-
token_address: Annotated[
|
|
1557
|
-
Optional[str],
|
|
1558
|
-
PydanticField(
|
|
1559
|
-
default=None,
|
|
1560
|
-
description="Token address of the agent",
|
|
1561
|
-
),
|
|
1562
|
-
]
|
|
1563
|
-
token_pool: Annotated[
|
|
1564
|
-
Optional[str],
|
|
1565
|
-
PydanticField(
|
|
1566
|
-
default=None,
|
|
1567
|
-
description="Pool of the agent token",
|
|
1568
|
-
),
|
|
1569
|
-
]
|
|
1570
|
-
mode: Annotated[
|
|
1571
|
-
Optional[Literal["public", "private"]],
|
|
1572
|
-
PydanticField(
|
|
1573
|
-
default=None,
|
|
1574
|
-
description="Mode of the agent, public or private",
|
|
1575
|
-
),
|
|
1576
|
-
]
|
|
1577
|
-
fee_percentage: Annotated[
|
|
1578
|
-
Optional[Decimal],
|
|
1579
|
-
PydanticField(
|
|
1580
|
-
default=None,
|
|
1581
|
-
description="Fee percentage of the agent",
|
|
1582
|
-
),
|
|
1583
|
-
]
|
|
1584
|
-
owner: Annotated[
|
|
1585
|
-
Optional[str],
|
|
1586
|
-
PydanticField(
|
|
1587
|
-
default=None,
|
|
1588
|
-
description="Owner identifier of the agent, used for access control",
|
|
1589
|
-
max_length=50,
|
|
1590
|
-
json_schema_extra={
|
|
1591
|
-
"x-group": "internal",
|
|
1592
|
-
},
|
|
1593
|
-
),
|
|
1594
|
-
]
|
|
1595
|
-
upstream_id: Annotated[
|
|
1596
|
-
Optional[str],
|
|
1672
|
+
# Override privacy fields to exclude them from JSON schema
|
|
1673
|
+
purpose: SkipJsonSchema[str | None] = None
|
|
1674
|
+
personality: SkipJsonSchema[str | None] = None
|
|
1675
|
+
principles: SkipJsonSchema[str | None] = None
|
|
1676
|
+
prompt: SkipJsonSchema[str | None] = None
|
|
1677
|
+
prompt_append: SkipJsonSchema[str | None] = None
|
|
1678
|
+
temperature: SkipJsonSchema[float | None] = None
|
|
1679
|
+
frequency_penalty: SkipJsonSchema[float | None] = None
|
|
1680
|
+
telegram_entrypoint_prompt: SkipJsonSchema[str | None] = None
|
|
1681
|
+
telegram_config: SkipJsonSchema[dict | None] = None
|
|
1682
|
+
discord_config: SkipJsonSchema[dict | None] = None
|
|
1683
|
+
xmtp_entrypoint_prompt: SkipJsonSchema[str | None] = None
|
|
1684
|
+
|
|
1685
|
+
# Additional fields specific to AgentResponse
|
|
1686
|
+
cdp_wallet_address: Annotated[
|
|
1687
|
+
str | None,
|
|
1597
1688
|
PydanticField(
|
|
1598
1689
|
default=None,
|
|
1599
|
-
description="
|
|
1600
|
-
max_length=100,
|
|
1601
|
-
json_schema_extra={
|
|
1602
|
-
"x-group": "internal",
|
|
1603
|
-
},
|
|
1690
|
+
description="CDP wallet address of the agent",
|
|
1604
1691
|
),
|
|
1605
1692
|
]
|
|
1606
|
-
|
|
1607
|
-
|
|
1693
|
+
evm_wallet_address: Annotated[
|
|
1694
|
+
str | None,
|
|
1608
1695
|
PydanticField(
|
|
1609
1696
|
default=None,
|
|
1610
|
-
description="
|
|
1611
|
-
),
|
|
1612
|
-
]
|
|
1613
|
-
# AI part
|
|
1614
|
-
model: Annotated[
|
|
1615
|
-
str,
|
|
1616
|
-
PydanticField(
|
|
1617
|
-
description="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, reigent",
|
|
1697
|
+
description="EVM wallet address of the agent",
|
|
1618
1698
|
),
|
|
1619
1699
|
]
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
Optional[List[Dict[str, Any]]],
|
|
1700
|
+
solana_wallet_address: Annotated[
|
|
1701
|
+
str | None,
|
|
1623
1702
|
PydanticField(
|
|
1624
1703
|
default=None,
|
|
1625
|
-
description=
|
|
1704
|
+
description="Solana wallet address of the agent",
|
|
1626
1705
|
),
|
|
1627
1706
|
]
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
Optional[str],
|
|
1707
|
+
has_twitter_linked: Annotated[
|
|
1708
|
+
bool,
|
|
1631
1709
|
PydanticField(
|
|
1632
|
-
default=
|
|
1633
|
-
description="
|
|
1710
|
+
default=False,
|
|
1711
|
+
description="Whether the agent has Twitter linked",
|
|
1634
1712
|
),
|
|
1635
1713
|
]
|
|
1636
|
-
|
|
1637
|
-
|
|
1714
|
+
linked_twitter_username: Annotated[
|
|
1715
|
+
str | None,
|
|
1638
1716
|
PydanticField(
|
|
1639
1717
|
default=None,
|
|
1640
|
-
description="
|
|
1718
|
+
description="Linked Twitter username",
|
|
1641
1719
|
),
|
|
1642
1720
|
]
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
Optional[Dict[str, Any]],
|
|
1721
|
+
linked_twitter_name: Annotated[
|
|
1722
|
+
str | None,
|
|
1646
1723
|
PydanticField(
|
|
1647
1724
|
default=None,
|
|
1648
|
-
description="
|
|
1649
|
-
),
|
|
1650
|
-
]
|
|
1651
|
-
wallet_provider: Annotated[
|
|
1652
|
-
Optional[Literal["cdp", "readonly"]],
|
|
1653
|
-
PydanticField(
|
|
1654
|
-
default="cdp",
|
|
1655
|
-
description="Provider of the agent's wallet",
|
|
1656
|
-
),
|
|
1657
|
-
]
|
|
1658
|
-
network_id: Annotated[
|
|
1659
|
-
Optional[str],
|
|
1660
|
-
PydanticField(
|
|
1661
|
-
default="base-mainnet",
|
|
1662
|
-
description="Network identifier",
|
|
1663
|
-
),
|
|
1664
|
-
]
|
|
1665
|
-
cdp_network_id: Annotated[
|
|
1666
|
-
Optional[str],
|
|
1667
|
-
PydanticField(
|
|
1668
|
-
default="base-mainnet",
|
|
1669
|
-
description="Network identifier for CDP integration",
|
|
1670
|
-
),
|
|
1671
|
-
]
|
|
1672
|
-
# telegram entrypoint
|
|
1673
|
-
telegram_entrypoint_enabled: Annotated[
|
|
1674
|
-
Optional[bool],
|
|
1675
|
-
PydanticField(
|
|
1676
|
-
default=False,
|
|
1677
|
-
description="Whether the agent can play telegram bot",
|
|
1725
|
+
description="Linked Twitter display name",
|
|
1678
1726
|
),
|
|
1679
1727
|
]
|
|
1680
|
-
|
|
1681
|
-
# data part
|
|
1682
|
-
cdp_wallet_address: Annotated[
|
|
1683
|
-
Optional[str], PydanticField(description="CDP wallet address for the agent")
|
|
1684
|
-
]
|
|
1685
|
-
evm_wallet_address: Annotated[
|
|
1686
|
-
Optional[str], PydanticField(description="EVM wallet address for the agent")
|
|
1687
|
-
]
|
|
1688
|
-
solana_wallet_address: Annotated[
|
|
1689
|
-
Optional[str], PydanticField(description="Solana wallet address for the agent")
|
|
1690
|
-
]
|
|
1691
|
-
has_twitter_linked: Annotated[
|
|
1692
|
-
bool,
|
|
1693
|
-
PydanticField(description="Whether the agent has linked their Twitter account"),
|
|
1694
|
-
]
|
|
1695
|
-
linked_twitter_username: Annotated[
|
|
1696
|
-
Optional[str],
|
|
1697
|
-
PydanticField(description="The username of the linked Twitter account"),
|
|
1698
|
-
]
|
|
1699
|
-
linked_twitter_name: Annotated[
|
|
1700
|
-
Optional[str],
|
|
1701
|
-
PydanticField(description="The name of the linked Twitter account"),
|
|
1702
|
-
]
|
|
1703
1728
|
has_twitter_self_key: Annotated[
|
|
1704
1729
|
bool,
|
|
1705
1730
|
PydanticField(
|
|
1706
|
-
|
|
1731
|
+
default=False,
|
|
1732
|
+
description="Whether the agent has Twitter self key",
|
|
1707
1733
|
),
|
|
1708
1734
|
]
|
|
1709
1735
|
has_telegram_self_key: Annotated[
|
|
1710
1736
|
bool,
|
|
1711
1737
|
PydanticField(
|
|
1712
|
-
|
|
1738
|
+
default=False,
|
|
1739
|
+
description="Whether the agent has Telegram self key",
|
|
1713
1740
|
),
|
|
1714
1741
|
]
|
|
1715
1742
|
linked_telegram_username: Annotated[
|
|
1716
|
-
|
|
1717
|
-
PydanticField(
|
|
1743
|
+
str | None,
|
|
1744
|
+
PydanticField(
|
|
1745
|
+
default=None,
|
|
1746
|
+
description="Linked Telegram username",
|
|
1747
|
+
),
|
|
1718
1748
|
]
|
|
1719
1749
|
linked_telegram_name: Annotated[
|
|
1720
|
-
|
|
1721
|
-
PydanticField(
|
|
1750
|
+
str | None,
|
|
1751
|
+
PydanticField(
|
|
1752
|
+
default=None,
|
|
1753
|
+
description="Linked Telegram display name",
|
|
1754
|
+
),
|
|
1722
1755
|
]
|
|
1723
1756
|
accept_image_input: Annotated[
|
|
1724
1757
|
bool,
|
|
1725
1758
|
PydanticField(
|
|
1726
|
-
|
|
1759
|
+
default=False,
|
|
1760
|
+
description="Whether the agent accepts image input",
|
|
1727
1761
|
),
|
|
1728
1762
|
]
|
|
1729
1763
|
accept_image_input_private: Annotated[
|
|
1730
1764
|
bool,
|
|
1731
1765
|
PydanticField(
|
|
1732
|
-
|
|
1766
|
+
default=False,
|
|
1767
|
+
description="Whether the agent accepts image input in private mode",
|
|
1733
1768
|
),
|
|
1734
1769
|
]
|
|
1735
1770
|
|
|
@@ -1751,7 +1786,7 @@ class AgentResponse(BaseModel):
|
|
|
1751
1786
|
|
|
1752
1787
|
@classmethod
|
|
1753
1788
|
async def from_agent(
|
|
1754
|
-
cls, agent: Agent, agent_data:
|
|
1789
|
+
cls, agent: Agent, agent_data: AgentData | None = None
|
|
1755
1790
|
) -> "AgentResponse":
|
|
1756
1791
|
"""Create an AgentResponse from an Agent instance.
|
|
1757
1792
|
|
|
@@ -1762,45 +1797,6 @@ class AgentResponse(BaseModel):
|
|
|
1762
1797
|
Returns:
|
|
1763
1798
|
AgentResponse: Response model with additional processed data
|
|
1764
1799
|
"""
|
|
1765
|
-
# Get base data from agent
|
|
1766
|
-
data = agent.model_dump()
|
|
1767
|
-
|
|
1768
|
-
# Filter sensitive fields from autonomous list
|
|
1769
|
-
if data.get("autonomous"):
|
|
1770
|
-
filtered_autonomous = []
|
|
1771
|
-
for item in data["autonomous"]:
|
|
1772
|
-
if isinstance(item, dict):
|
|
1773
|
-
filtered_item = {
|
|
1774
|
-
"id": item.get("id"),
|
|
1775
|
-
"name": item.get("name"),
|
|
1776
|
-
"enabled": item.get("enabled"),
|
|
1777
|
-
}
|
|
1778
|
-
filtered_autonomous.append(filtered_item)
|
|
1779
|
-
data["autonomous"] = filtered_autonomous
|
|
1780
|
-
|
|
1781
|
-
# Filter sensitive fields from skills dictionary
|
|
1782
|
-
if data.get("skills"):
|
|
1783
|
-
filtered_skills = {}
|
|
1784
|
-
for skill_name, skill_config in data["skills"].items():
|
|
1785
|
-
if isinstance(skill_config, dict):
|
|
1786
|
-
# Only include skills that are enabled
|
|
1787
|
-
if skill_config.get("enabled") is True:
|
|
1788
|
-
filtered_config = {"enabled": True}
|
|
1789
|
-
# Only keep states with public or private values
|
|
1790
|
-
if "states" in skill_config and isinstance(
|
|
1791
|
-
skill_config["states"], dict
|
|
1792
|
-
):
|
|
1793
|
-
filtered_states = {}
|
|
1794
|
-
for state_key, state_value in skill_config[
|
|
1795
|
-
"states"
|
|
1796
|
-
].items():
|
|
1797
|
-
if state_value in ["public", "private"]:
|
|
1798
|
-
filtered_states[state_key] = state_value
|
|
1799
|
-
if filtered_states:
|
|
1800
|
-
filtered_config["states"] = filtered_states
|
|
1801
|
-
filtered_skills[skill_name] = filtered_config
|
|
1802
|
-
data["skills"] = filtered_skills
|
|
1803
|
-
|
|
1804
1800
|
# Process CDP wallet address
|
|
1805
1801
|
cdp_wallet_address = agent_data.evm_wallet_address if agent_data else None
|
|
1806
1802
|
evm_wallet_address = agent_data.evm_wallet_address if agent_data else None
|
|
@@ -1815,8 +1811,7 @@ class AgentResponse(BaseModel):
|
|
|
1815
1811
|
linked_twitter_name = agent_data.twitter_name
|
|
1816
1812
|
if agent_data.twitter_access_token_expires_at:
|
|
1817
1813
|
has_twitter_linked = (
|
|
1818
|
-
agent_data.twitter_access_token_expires_at
|
|
1819
|
-
> datetime.now(timezone.utc)
|
|
1814
|
+
agent_data.twitter_access_token_expires_at > datetime.now(UTC)
|
|
1820
1815
|
)
|
|
1821
1816
|
else:
|
|
1822
1817
|
has_twitter_linked = True
|
|
@@ -1826,10 +1821,10 @@ class AgentResponse(BaseModel):
|
|
|
1826
1821
|
agent_data and agent_data.twitter_self_key_refreshed_at
|
|
1827
1822
|
)
|
|
1828
1823
|
|
|
1829
|
-
# Process Telegram self-key status
|
|
1824
|
+
# Process Telegram self-key status
|
|
1830
1825
|
linked_telegram_username = None
|
|
1831
1826
|
linked_telegram_name = None
|
|
1832
|
-
telegram_config =
|
|
1827
|
+
telegram_config = agent.telegram_config or {}
|
|
1833
1828
|
has_telegram_self_key = bool(
|
|
1834
1829
|
telegram_config and "token" in telegram_config and telegram_config["token"]
|
|
1835
1830
|
)
|
|
@@ -1846,22 +1841,174 @@ class AgentResponse(BaseModel):
|
|
|
1846
1841
|
or agent.has_image_parser_skill(is_private=True)
|
|
1847
1842
|
)
|
|
1848
1843
|
|
|
1849
|
-
#
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1844
|
+
# Create AgentResponse instance directly from agent with additional fields
|
|
1845
|
+
return cls(
|
|
1846
|
+
# Copy all fields from agent
|
|
1847
|
+
**agent.model_dump(),
|
|
1848
|
+
# Add computed fields
|
|
1849
|
+
cdp_wallet_address=cdp_wallet_address,
|
|
1850
|
+
evm_wallet_address=evm_wallet_address,
|
|
1851
|
+
solana_wallet_address=solana_wallet_address,
|
|
1852
|
+
has_twitter_linked=has_twitter_linked,
|
|
1853
|
+
linked_twitter_username=linked_twitter_username,
|
|
1854
|
+
linked_twitter_name=linked_twitter_name,
|
|
1855
|
+
has_twitter_self_key=has_twitter_self_key,
|
|
1856
|
+
has_telegram_self_key=has_telegram_self_key,
|
|
1857
|
+
linked_telegram_username=linked_telegram_username,
|
|
1858
|
+
linked_telegram_name=linked_telegram_name,
|
|
1859
|
+
accept_image_input=accept_image_input,
|
|
1860
|
+
accept_image_input_private=accept_image_input_private,
|
|
1861
|
+
)
|
|
1862
|
+
|
|
1863
|
+
def model_dump(
|
|
1864
|
+
self,
|
|
1865
|
+
*,
|
|
1866
|
+
mode: str | Literal["json", "python"] = "python",
|
|
1867
|
+
include: IncEx | None = None,
|
|
1868
|
+
exclude: IncEx | None = None,
|
|
1869
|
+
context: Any | None = None,
|
|
1870
|
+
by_alias: bool = False,
|
|
1871
|
+
exclude_unset: bool = False,
|
|
1872
|
+
exclude_defaults: bool = False,
|
|
1873
|
+
exclude_none: bool = False,
|
|
1874
|
+
round_trip: bool = False,
|
|
1875
|
+
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
1876
|
+
serialize_as_any: bool = False,
|
|
1877
|
+
) -> dict[str, Any]:
|
|
1878
|
+
"""Override model_dump to exclude privacy fields and filter data."""
|
|
1879
|
+
# Get the base model dump
|
|
1880
|
+
data = super().model_dump(
|
|
1881
|
+
mode=mode,
|
|
1882
|
+
include=include,
|
|
1883
|
+
exclude=exclude,
|
|
1884
|
+
context=context,
|
|
1885
|
+
by_alias=by_alias,
|
|
1886
|
+
exclude_unset=exclude_unset,
|
|
1887
|
+
exclude_defaults=exclude_defaults,
|
|
1888
|
+
exclude_none=exclude_none,
|
|
1889
|
+
round_trip=round_trip,
|
|
1890
|
+
warnings=warnings,
|
|
1891
|
+
serialize_as_any=serialize_as_any,
|
|
1892
|
+
)
|
|
1893
|
+
|
|
1894
|
+
# Remove privacy fields that might still be present
|
|
1895
|
+
privacy_fields = {
|
|
1896
|
+
"purpose",
|
|
1897
|
+
"personality",
|
|
1898
|
+
"principles",
|
|
1899
|
+
"prompt",
|
|
1900
|
+
"prompt_append",
|
|
1901
|
+
"temperature",
|
|
1902
|
+
"frequency_penalty",
|
|
1903
|
+
"telegram_entrypoint_prompt",
|
|
1904
|
+
"telegram_config",
|
|
1905
|
+
"discord_config",
|
|
1906
|
+
"xmtp_entrypoint_prompt",
|
|
1907
|
+
}
|
|
1908
|
+
for field in privacy_fields:
|
|
1909
|
+
data.pop(field, None)
|
|
1910
|
+
|
|
1911
|
+
# Filter autonomous list to only keep safe fields
|
|
1912
|
+
if "autonomous" in data and data["autonomous"]:
|
|
1913
|
+
filtered_autonomous = []
|
|
1914
|
+
for item in data["autonomous"]:
|
|
1915
|
+
if isinstance(item, dict):
|
|
1916
|
+
# Only keep safe fields: id, name, description, enabled
|
|
1917
|
+
filtered_item = {
|
|
1918
|
+
key: item[key]
|
|
1919
|
+
for key in ["id", "name", "description", "enabled"]
|
|
1920
|
+
if key in item
|
|
1921
|
+
}
|
|
1922
|
+
filtered_autonomous.append(filtered_item)
|
|
1923
|
+
else:
|
|
1924
|
+
# Handle AgentAutonomous objects
|
|
1925
|
+
item_dict = (
|
|
1926
|
+
item.model_dump() if hasattr(item, "model_dump") else dict(item)
|
|
1927
|
+
)
|
|
1928
|
+
# Only keep safe fields: id, name, description, enabled
|
|
1929
|
+
filtered_item = {
|
|
1930
|
+
key: item_dict[key]
|
|
1931
|
+
for key in ["id", "name", "description", "enabled"]
|
|
1932
|
+
if key in item_dict
|
|
1933
|
+
}
|
|
1934
|
+
filtered_autonomous.append(filtered_item)
|
|
1935
|
+
data["autonomous"] = filtered_autonomous
|
|
1936
|
+
|
|
1937
|
+
# Convert examples to AgentExample instances if they're dictionaries
|
|
1938
|
+
if "examples" in data and data["examples"]:
|
|
1939
|
+
converted_examples = []
|
|
1940
|
+
for example in data["examples"]:
|
|
1941
|
+
if isinstance(example, dict):
|
|
1942
|
+
converted_examples.append(AgentExample(**example).model_dump())
|
|
1943
|
+
else:
|
|
1944
|
+
converted_examples.append(
|
|
1945
|
+
example.model_dump()
|
|
1946
|
+
if hasattr(example, "model_dump")
|
|
1947
|
+
else example
|
|
1948
|
+
)
|
|
1949
|
+
data["examples"] = converted_examples
|
|
1950
|
+
|
|
1951
|
+
# Filter skills to only include enabled ones with specific configurations
|
|
1952
|
+
if "skills" in data and data["skills"]:
|
|
1953
|
+
filtered_skills = {}
|
|
1954
|
+
for skill_name, skill_config in data["skills"].items():
|
|
1955
|
+
if (
|
|
1956
|
+
isinstance(skill_config, dict)
|
|
1957
|
+
and skill_config.get("enabled") is True
|
|
1958
|
+
):
|
|
1959
|
+
# Filter out disabled states from the skill configuration
|
|
1960
|
+
original_states = skill_config.get("states", {})
|
|
1961
|
+
filtered_states = {
|
|
1962
|
+
state_name: state_value
|
|
1963
|
+
for state_name, state_value in original_states.items()
|
|
1964
|
+
if state_value != "disabled"
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
# Only include the skill if it has at least one non-disabled state
|
|
1968
|
+
if filtered_states:
|
|
1969
|
+
filtered_config = {
|
|
1970
|
+
"enabled": skill_config["enabled"],
|
|
1971
|
+
"states": filtered_states,
|
|
1972
|
+
}
|
|
1973
|
+
# Add other non-sensitive config fields if needed
|
|
1974
|
+
for key in ["public", "private"]:
|
|
1975
|
+
if key in skill_config:
|
|
1976
|
+
filtered_config[key] = skill_config[key]
|
|
1977
|
+
filtered_skills[skill_name] = filtered_config
|
|
1978
|
+
data["skills"] = filtered_skills
|
|
1979
|
+
|
|
1980
|
+
return data
|
|
1981
|
+
|
|
1982
|
+
def model_dump_json(
|
|
1983
|
+
self,
|
|
1984
|
+
*,
|
|
1985
|
+
indent: int | str | None = None,
|
|
1986
|
+
include: IncEx | None = None,
|
|
1987
|
+
exclude: IncEx | None = None,
|
|
1988
|
+
context: Any | None = None,
|
|
1989
|
+
by_alias: bool = False,
|
|
1990
|
+
exclude_unset: bool = False,
|
|
1991
|
+
exclude_defaults: bool = False,
|
|
1992
|
+
exclude_none: bool = False,
|
|
1993
|
+
round_trip: bool = False,
|
|
1994
|
+
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
1995
|
+
serialize_as_any: bool = False,
|
|
1996
|
+
) -> str:
|
|
1997
|
+
"""Override model_dump_json to exclude privacy fields and filter sensitive data."""
|
|
1998
|
+
# Get the filtered data using the same logic as model_dump
|
|
1999
|
+
data = self.model_dump(
|
|
2000
|
+
mode="json",
|
|
2001
|
+
include=include,
|
|
2002
|
+
exclude=exclude,
|
|
2003
|
+
context=context,
|
|
2004
|
+
by_alias=by_alias,
|
|
2005
|
+
exclude_unset=exclude_unset,
|
|
2006
|
+
exclude_defaults=exclude_defaults,
|
|
2007
|
+
exclude_none=exclude_none,
|
|
2008
|
+
round_trip=round_trip,
|
|
2009
|
+
warnings=warnings,
|
|
2010
|
+
serialize_as_any=serialize_as_any,
|
|
1865
2011
|
)
|
|
1866
2012
|
|
|
1867
|
-
|
|
2013
|
+
# Use json.dumps to serialize the filtered data with proper indentation
|
|
2014
|
+
return json.dumps(data, indent=indent, ensure_ascii=False)
|