intentkit 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +17 -0
- intentkit/abstracts/__init__.py +0 -0
- intentkit/abstracts/agent.py +60 -0
- intentkit/abstracts/api.py +4 -0
- intentkit/abstracts/engine.py +38 -0
- intentkit/abstracts/exception.py +9 -0
- intentkit/abstracts/graph.py +25 -0
- intentkit/abstracts/skill.py +129 -0
- intentkit/abstracts/twitter.py +54 -0
- intentkit/clients/__init__.py +14 -0
- intentkit/clients/cdp.py +53 -0
- intentkit/clients/twitter.py +445 -0
- intentkit/config/__init__.py +0 -0
- intentkit/config/config.py +164 -0
- intentkit/core/__init__.py +0 -0
- intentkit/core/agent.py +191 -0
- intentkit/core/api.py +40 -0
- intentkit/core/client.py +45 -0
- intentkit/core/credit.py +1767 -0
- intentkit/core/engine.py +1018 -0
- intentkit/core/node.py +223 -0
- intentkit/core/prompt.py +58 -0
- intentkit/core/skill.py +124 -0
- intentkit/models/agent.py +1689 -0
- intentkit/models/agent_data.py +810 -0
- intentkit/models/agent_schema.json +733 -0
- intentkit/models/app_setting.py +156 -0
- intentkit/models/base.py +9 -0
- intentkit/models/chat.py +581 -0
- intentkit/models/conversation.py +286 -0
- intentkit/models/credit.py +1406 -0
- intentkit/models/db.py +120 -0
- intentkit/models/db_mig.py +102 -0
- intentkit/models/generator.py +347 -0
- intentkit/models/llm.py +746 -0
- intentkit/models/redis.py +132 -0
- intentkit/models/skill.py +466 -0
- intentkit/models/user.py +243 -0
- intentkit/skills/__init__.py +12 -0
- intentkit/skills/acolyt/__init__.py +83 -0
- intentkit/skills/acolyt/acolyt.jpg +0 -0
- intentkit/skills/acolyt/ask.py +128 -0
- intentkit/skills/acolyt/base.py +28 -0
- intentkit/skills/acolyt/schema.json +89 -0
- intentkit/skills/aixbt/README.md +71 -0
- intentkit/skills/aixbt/__init__.py +73 -0
- intentkit/skills/aixbt/aixbt.jpg +0 -0
- intentkit/skills/aixbt/base.py +21 -0
- intentkit/skills/aixbt/projects.py +153 -0
- intentkit/skills/aixbt/schema.json +99 -0
- intentkit/skills/allora/__init__.py +83 -0
- intentkit/skills/allora/allora.jpeg +0 -0
- intentkit/skills/allora/base.py +28 -0
- intentkit/skills/allora/price.py +130 -0
- intentkit/skills/allora/schema.json +89 -0
- intentkit/skills/base.py +174 -0
- intentkit/skills/carv/README.md +95 -0
- intentkit/skills/carv/__init__.py +121 -0
- intentkit/skills/carv/base.py +183 -0
- intentkit/skills/carv/carv.webp +0 -0
- intentkit/skills/carv/fetch_news.py +92 -0
- intentkit/skills/carv/onchain_query.py +164 -0
- intentkit/skills/carv/schema.json +137 -0
- intentkit/skills/carv/token_info_and_price.py +110 -0
- intentkit/skills/cdp/__init__.py +137 -0
- intentkit/skills/cdp/base.py +21 -0
- intentkit/skills/cdp/cdp.png +0 -0
- intentkit/skills/cdp/get_balance.py +81 -0
- intentkit/skills/cdp/schema.json +473 -0
- intentkit/skills/chainlist/README.md +38 -0
- intentkit/skills/chainlist/__init__.py +54 -0
- intentkit/skills/chainlist/base.py +21 -0
- intentkit/skills/chainlist/chain_lookup.py +208 -0
- intentkit/skills/chainlist/chainlist.png +0 -0
- intentkit/skills/chainlist/schema.json +47 -0
- intentkit/skills/common/__init__.py +82 -0
- intentkit/skills/common/base.py +21 -0
- intentkit/skills/common/common.jpg +0 -0
- intentkit/skills/common/current_time.py +84 -0
- intentkit/skills/common/schema.json +57 -0
- intentkit/skills/cookiefun/README.md +121 -0
- intentkit/skills/cookiefun/__init__.py +78 -0
- intentkit/skills/cookiefun/base.py +41 -0
- intentkit/skills/cookiefun/constants.py +18 -0
- intentkit/skills/cookiefun/cookiefun.png +0 -0
- intentkit/skills/cookiefun/get_account_details.py +171 -0
- intentkit/skills/cookiefun/get_account_feed.py +282 -0
- intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
- intentkit/skills/cookiefun/get_sectors.py +128 -0
- intentkit/skills/cookiefun/schema.json +155 -0
- intentkit/skills/cookiefun/search_accounts.py +225 -0
- intentkit/skills/cryptocompare/__init__.py +130 -0
- intentkit/skills/cryptocompare/api.py +159 -0
- intentkit/skills/cryptocompare/base.py +303 -0
- intentkit/skills/cryptocompare/cryptocompare.png +0 -0
- intentkit/skills/cryptocompare/fetch_news.py +96 -0
- intentkit/skills/cryptocompare/fetch_price.py +99 -0
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
- intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
- intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
- intentkit/skills/cryptocompare/schema.json +168 -0
- intentkit/skills/cryptopanic/__init__.py +108 -0
- intentkit/skills/cryptopanic/base.py +51 -0
- intentkit/skills/cryptopanic/cryptopanic.png +0 -0
- intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
- intentkit/skills/cryptopanic/schema.json +103 -0
- intentkit/skills/dapplooker/README.md +92 -0
- intentkit/skills/dapplooker/__init__.py +83 -0
- intentkit/skills/dapplooker/base.py +26 -0
- intentkit/skills/dapplooker/dapplooker.jpg +0 -0
- intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
- intentkit/skills/dapplooker/schema.json +91 -0
- intentkit/skills/defillama/__init__.py +323 -0
- intentkit/skills/defillama/api.py +315 -0
- intentkit/skills/defillama/base.py +135 -0
- intentkit/skills/defillama/coins/__init__.py +0 -0
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
- intentkit/skills/defillama/coins/fetch_block.py +98 -0
- intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
- intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
- intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
- intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
- intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
- intentkit/skills/defillama/config/__init__.py +0 -0
- intentkit/skills/defillama/config/chains.py +433 -0
- intentkit/skills/defillama/defillama.jpeg +0 -0
- intentkit/skills/defillama/fees/__init__.py +0 -0
- intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
- intentkit/skills/defillama/schema.json +383 -0
- intentkit/skills/defillama/stablecoins/__init__.py +0 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
- intentkit/skills/defillama/tests/__init__.py +0 -0
- intentkit/skills/defillama/tests/api_integration.test.py +192 -0
- intentkit/skills/defillama/tests/api_unit.test.py +583 -0
- intentkit/skills/defillama/tvl/__init__.py +0 -0
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
- intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
- intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
- intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
- intentkit/skills/defillama/volumes/__init__.py +0 -0
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
- intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
- intentkit/skills/defillama/yields/__init__.py +0 -0
- intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
- intentkit/skills/defillama/yields/fetch_pools.py +126 -0
- intentkit/skills/dexscreener/__init__.py +93 -0
- intentkit/skills/dexscreener/base.py +133 -0
- intentkit/skills/dexscreener/dexscreener.png +0 -0
- intentkit/skills/dexscreener/model/__init__.py +0 -0
- intentkit/skills/dexscreener/model/search_token_response.py +82 -0
- intentkit/skills/dexscreener/schema.json +48 -0
- intentkit/skills/dexscreener/search_token.py +321 -0
- intentkit/skills/dune_analytics/__init__.py +103 -0
- intentkit/skills/dune_analytics/base.py +46 -0
- intentkit/skills/dune_analytics/dune.png +0 -0
- intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
- intentkit/skills/dune_analytics/schema.json +99 -0
- intentkit/skills/elfa/README.md +100 -0
- intentkit/skills/elfa/__init__.py +123 -0
- intentkit/skills/elfa/base.py +28 -0
- intentkit/skills/elfa/elfa.jpg +0 -0
- intentkit/skills/elfa/mention.py +504 -0
- intentkit/skills/elfa/schema.json +153 -0
- intentkit/skills/elfa/stats.py +118 -0
- intentkit/skills/elfa/tokens.py +126 -0
- intentkit/skills/enso/README.md +75 -0
- intentkit/skills/enso/__init__.py +114 -0
- intentkit/skills/enso/abi/__init__.py +0 -0
- intentkit/skills/enso/abi/approval.py +279 -0
- intentkit/skills/enso/abi/erc20.py +14 -0
- intentkit/skills/enso/abi/route.py +129 -0
- intentkit/skills/enso/base.py +44 -0
- intentkit/skills/enso/best_yield.py +286 -0
- intentkit/skills/enso/enso.jpg +0 -0
- intentkit/skills/enso/networks.py +105 -0
- intentkit/skills/enso/prices.py +93 -0
- intentkit/skills/enso/route.py +300 -0
- intentkit/skills/enso/schema.json +212 -0
- intentkit/skills/enso/tokens.py +223 -0
- intentkit/skills/enso/wallet.py +381 -0
- intentkit/skills/github/README.md +63 -0
- intentkit/skills/github/__init__.py +54 -0
- intentkit/skills/github/base.py +21 -0
- intentkit/skills/github/github.jpg +0 -0
- intentkit/skills/github/github_search.py +183 -0
- intentkit/skills/github/schema.json +59 -0
- intentkit/skills/heurist/__init__.py +143 -0
- intentkit/skills/heurist/base.py +26 -0
- intentkit/skills/heurist/heurist.png +0 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
- intentkit/skills/heurist/image_generation_braindance.py +162 -0
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
- intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
- intentkit/skills/heurist/image_generation_sdxl.py +161 -0
- intentkit/skills/heurist/schema.json +196 -0
- intentkit/skills/lifi/README.md +294 -0
- intentkit/skills/lifi/__init__.py +141 -0
- intentkit/skills/lifi/base.py +21 -0
- intentkit/skills/lifi/lifi.png +0 -0
- intentkit/skills/lifi/schema.json +89 -0
- intentkit/skills/lifi/token_execute.py +472 -0
- intentkit/skills/lifi/token_quote.py +190 -0
- intentkit/skills/lifi/utils.py +656 -0
- intentkit/skills/moralis/README.md +490 -0
- intentkit/skills/moralis/__init__.py +110 -0
- intentkit/skills/moralis/api.py +281 -0
- intentkit/skills/moralis/base.py +55 -0
- intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
- intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
- intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
- intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
- intentkit/skills/moralis/moralis.png +0 -0
- intentkit/skills/moralis/schema.json +156 -0
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +511 -0
- intentkit/skills/nation/__init__.py +62 -0
- intentkit/skills/nation/base.py +31 -0
- intentkit/skills/nation/nation.png +0 -0
- intentkit/skills/nation/nft_check.py +106 -0
- intentkit/skills/nation/schema.json +58 -0
- intentkit/skills/openai/__init__.py +107 -0
- intentkit/skills/openai/base.py +32 -0
- intentkit/skills/openai/dalle_image_generation.py +128 -0
- intentkit/skills/openai/gpt_image_generation.py +152 -0
- intentkit/skills/openai/gpt_image_to_image.py +186 -0
- intentkit/skills/openai/image_to_text.py +126 -0
- intentkit/skills/openai/openai.png +0 -0
- intentkit/skills/openai/schema.json +139 -0
- intentkit/skills/portfolio/README.md +55 -0
- intentkit/skills/portfolio/__init__.py +151 -0
- intentkit/skills/portfolio/base.py +107 -0
- intentkit/skills/portfolio/constants.py +9 -0
- intentkit/skills/portfolio/moralis.png +0 -0
- intentkit/skills/portfolio/schema.json +237 -0
- intentkit/skills/portfolio/token_balances.py +155 -0
- intentkit/skills/portfolio/wallet_approvals.py +102 -0
- intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
- intentkit/skills/portfolio/wallet_history.py +155 -0
- intentkit/skills/portfolio/wallet_net_worth.py +112 -0
- intentkit/skills/portfolio/wallet_nfts.py +139 -0
- intentkit/skills/portfolio/wallet_profitability.py +101 -0
- intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
- intentkit/skills/portfolio/wallet_stats.py +79 -0
- intentkit/skills/portfolio/wallet_swaps.py +147 -0
- intentkit/skills/skills.toml +103 -0
- intentkit/skills/slack/__init__.py +98 -0
- intentkit/skills/slack/base.py +55 -0
- intentkit/skills/slack/get_channel.py +109 -0
- intentkit/skills/slack/get_message.py +136 -0
- intentkit/skills/slack/schedule_message.py +92 -0
- intentkit/skills/slack/schema.json +135 -0
- intentkit/skills/slack/send_message.py +81 -0
- intentkit/skills/slack/slack.jpg +0 -0
- intentkit/skills/system/__init__.py +90 -0
- intentkit/skills/system/base.py +22 -0
- intentkit/skills/system/read_agent_api_key.py +87 -0
- intentkit/skills/system/regenerate_agent_api_key.py +77 -0
- intentkit/skills/system/schema.json +53 -0
- intentkit/skills/system/system.svg +76 -0
- intentkit/skills/tavily/README.md +86 -0
- intentkit/skills/tavily/__init__.py +91 -0
- intentkit/skills/tavily/base.py +27 -0
- intentkit/skills/tavily/schema.json +119 -0
- intentkit/skills/tavily/tavily.jpg +0 -0
- intentkit/skills/tavily/tavily_extract.py +147 -0
- intentkit/skills/tavily/tavily_search.py +139 -0
- intentkit/skills/token/README.md +89 -0
- intentkit/skills/token/__init__.py +107 -0
- intentkit/skills/token/base.py +154 -0
- intentkit/skills/token/constants.py +9 -0
- intentkit/skills/token/erc20_transfers.py +145 -0
- intentkit/skills/token/moralis.png +0 -0
- intentkit/skills/token/schema.json +141 -0
- intentkit/skills/token/token_analytics.py +81 -0
- intentkit/skills/token/token_price.py +132 -0
- intentkit/skills/token/token_search.py +121 -0
- intentkit/skills/twitter/__init__.py +146 -0
- intentkit/skills/twitter/base.py +68 -0
- intentkit/skills/twitter/follow_user.py +69 -0
- intentkit/skills/twitter/get_mentions.py +124 -0
- intentkit/skills/twitter/get_timeline.py +111 -0
- intentkit/skills/twitter/get_user_by_username.py +84 -0
- intentkit/skills/twitter/get_user_tweets.py +123 -0
- intentkit/skills/twitter/like_tweet.py +65 -0
- intentkit/skills/twitter/post_tweet.py +90 -0
- intentkit/skills/twitter/reply_tweet.py +98 -0
- intentkit/skills/twitter/retweet.py +76 -0
- intentkit/skills/twitter/schema.json +258 -0
- intentkit/skills/twitter/search_tweets.py +115 -0
- intentkit/skills/twitter/twitter.png +0 -0
- intentkit/skills/unrealspeech/__init__.py +55 -0
- intentkit/skills/unrealspeech/base.py +21 -0
- intentkit/skills/unrealspeech/schema.json +100 -0
- intentkit/skills/unrealspeech/text_to_speech.py +177 -0
- intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
- intentkit/skills/venice_audio/__init__.py +106 -0
- intentkit/skills/venice_audio/base.py +119 -0
- intentkit/skills/venice_audio/input.py +41 -0
- intentkit/skills/venice_audio/schema.json +152 -0
- intentkit/skills/venice_audio/venice_audio.py +240 -0
- intentkit/skills/venice_audio/venice_logo.jpg +0 -0
- intentkit/skills/venice_image/README.md +119 -0
- intentkit/skills/venice_image/__init__.py +154 -0
- intentkit/skills/venice_image/api.py +138 -0
- intentkit/skills/venice_image/base.py +188 -0
- intentkit/skills/venice_image/config.py +35 -0
- intentkit/skills/venice_image/image_enhance/README.md +119 -0
- intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
- intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
- intentkit/skills/venice_image/image_generation/README.md +144 -0
- intentkit/skills/venice_image/image_generation/__init__.py +0 -0
- intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
- intentkit/skills/venice_image/image_upscale/README.md +111 -0
- intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
- intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
- intentkit/skills/venice_image/image_vision/README.md +112 -0
- intentkit/skills/venice_image/image_vision/__init__.py +0 -0
- intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
- intentkit/skills/venice_image/schema.json +267 -0
- intentkit/skills/venice_image/utils.py +78 -0
- intentkit/skills/venice_image/venice_image.jpg +0 -0
- intentkit/skills/web_scraper/README.md +82 -0
- intentkit/skills/web_scraper/__init__.py +92 -0
- intentkit/skills/web_scraper/base.py +21 -0
- intentkit/skills/web_scraper/langchain.png +0 -0
- intentkit/skills/web_scraper/schema.json +115 -0
- intentkit/skills/web_scraper/scrape_and_index.py +327 -0
- intentkit/utils/__init__.py +1 -0
- intentkit/utils/chain.py +436 -0
- intentkit/utils/error.py +134 -0
- intentkit/utils/logging.py +70 -0
- intentkit/utils/middleware.py +61 -0
- intentkit/utils/random.py +16 -0
- intentkit/utils/s3.py +267 -0
- intentkit/utils/slack_alert.py +79 -0
- intentkit/utils/tx.py +37 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
- intentkit-0.5.2.dist-info/RECORD +365 -0
- intentkit-0.5.0.dist-info/RECORD +0 -4
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
intentkit/core/engine.py
ADDED
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
"""AI Agent Management Module.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for initializing and executing AI agents. It handles:
|
|
4
|
+
- Agent initialization with LangChain
|
|
5
|
+
- Tool and skill management
|
|
6
|
+
- Agent execution and response handling
|
|
7
|
+
- Memory management with PostgreSQL
|
|
8
|
+
- Integration with CDP and Twitter
|
|
9
|
+
|
|
10
|
+
The module uses a global cache to store initialized agents for better performance.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import importlib
|
|
14
|
+
import logging
|
|
15
|
+
import re
|
|
16
|
+
import textwrap
|
|
17
|
+
import time
|
|
18
|
+
import traceback
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
import sqlalchemy
|
|
23
|
+
from epyxid import XID
|
|
24
|
+
from fastapi import HTTPException
|
|
25
|
+
from langchain_core.messages import (
|
|
26
|
+
BaseMessage,
|
|
27
|
+
HumanMessage,
|
|
28
|
+
)
|
|
29
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
30
|
+
from langchain_core.runnables import RunnableConfig
|
|
31
|
+
from langchain_core.tools import BaseTool
|
|
32
|
+
from langgraph.errors import GraphRecursionError
|
|
33
|
+
from langgraph.graph.state import CompiledStateGraph
|
|
34
|
+
from langgraph.prebuilt import create_react_agent
|
|
35
|
+
from sqlalchemy import func, update
|
|
36
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
37
|
+
|
|
38
|
+
from intentkit.abstracts.graph import AgentError, AgentState
|
|
39
|
+
from intentkit.config.config import config
|
|
40
|
+
from intentkit.core.credit import expense_message, expense_skill
|
|
41
|
+
from intentkit.core.node import PreModelNode, post_model_node
|
|
42
|
+
from intentkit.core.prompt import agent_prompt
|
|
43
|
+
from intentkit.core.skill import skill_store
|
|
44
|
+
from intentkit.models.agent import Agent, AgentTable
|
|
45
|
+
from intentkit.models.agent_data import AgentData, AgentQuota
|
|
46
|
+
from intentkit.models.app_setting import AppSetting
|
|
47
|
+
from intentkit.models.chat import (
|
|
48
|
+
AuthorType,
|
|
49
|
+
ChatMessage,
|
|
50
|
+
ChatMessageCreate,
|
|
51
|
+
ChatMessageSkillCall,
|
|
52
|
+
)
|
|
53
|
+
from intentkit.models.credit import CreditAccount, OwnerType
|
|
54
|
+
from intentkit.models.db import get_langgraph_checkpointer, get_session
|
|
55
|
+
from intentkit.models.llm import LLMModelInfo, LLMProvider
|
|
56
|
+
from intentkit.models.skill import AgentSkillData, Skill, ThreadSkillData
|
|
57
|
+
from intentkit.models.user import User
|
|
58
|
+
from intentkit.utils.error import IntentKitAPIError
|
|
59
|
+
|
|
60
|
+
logger = logging.getLogger(__name__)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def explain_prompt(message: str) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Process message to replace @skill:*:* patterns with (call skill xxxxx) format.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
message (str): The input message to process
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: The processed message with @skill patterns replaced
|
|
72
|
+
"""
|
|
73
|
+
# Pattern to match @skill:category:config_name with word boundaries
|
|
74
|
+
pattern = r"\b@skill:([^:]+):([^\s]+)\b"
|
|
75
|
+
|
|
76
|
+
async def replace_skill_pattern(match):
|
|
77
|
+
category = match.group(1)
|
|
78
|
+
config_name = match.group(2)
|
|
79
|
+
|
|
80
|
+
# Get skill by category and config_name
|
|
81
|
+
skill = await Skill.get_by_config_name(category, config_name)
|
|
82
|
+
|
|
83
|
+
if skill:
|
|
84
|
+
return f"(call skill {skill.name})"
|
|
85
|
+
else:
|
|
86
|
+
# If skill not found, keep original pattern
|
|
87
|
+
return match.group(0)
|
|
88
|
+
|
|
89
|
+
# Find all matches
|
|
90
|
+
matches = list(re.finditer(pattern, message))
|
|
91
|
+
|
|
92
|
+
# Process matches in reverse order to maintain string positions
|
|
93
|
+
result = message
|
|
94
|
+
for match in reversed(matches):
|
|
95
|
+
replacement = await replace_skill_pattern(match)
|
|
96
|
+
result = result[: match.start()] + replacement + result[match.end() :]
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Global variable to cache all agent executors
|
|
102
|
+
_agents: dict[str, CompiledStateGraph] = {}
|
|
103
|
+
_private_agents: dict[str, CompiledStateGraph] = {}
|
|
104
|
+
|
|
105
|
+
# Global dictionaries to cache agent update times
|
|
106
|
+
_agents_updated: dict[str, datetime] = {}
|
|
107
|
+
_private_agents_updated: dict[str, datetime] = {}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def create_agent(
|
|
111
|
+
agent: Agent, is_private: bool = False, has_search: bool = False
|
|
112
|
+
) -> CompiledStateGraph:
|
|
113
|
+
"""Create an AI agent with specified configuration and tools.
|
|
114
|
+
|
|
115
|
+
This function:
|
|
116
|
+
1. Initializes LLM with specified model
|
|
117
|
+
2. Loads and configures requested tools
|
|
118
|
+
3. Sets up PostgreSQL-based memory
|
|
119
|
+
4. Creates and returns the agent
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
agent (Agent): Agent configuration object
|
|
123
|
+
is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
|
|
124
|
+
has_search (bool, optional): Flag indicating whether to include search tools. Defaults to False.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
CompiledStateGraph: Initialized LangChain agent
|
|
128
|
+
"""
|
|
129
|
+
agent_data: Optional[AgentData] = await AgentData.get(agent.id)
|
|
130
|
+
|
|
131
|
+
# ==== Initialize LLM using the LLM abstraction.
|
|
132
|
+
from intentkit.models.llm import create_llm_model
|
|
133
|
+
|
|
134
|
+
# Create the LLM model instance
|
|
135
|
+
llm_model = await create_llm_model(
|
|
136
|
+
model_name=agent.model,
|
|
137
|
+
temperature=agent.temperature,
|
|
138
|
+
frequency_penalty=agent.frequency_penalty,
|
|
139
|
+
presence_penalty=agent.presence_penalty,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Get the LLM instance
|
|
143
|
+
llm = await llm_model.create_instance(config)
|
|
144
|
+
|
|
145
|
+
# Get the token limit from the model info
|
|
146
|
+
input_token_limit = min(config.input_token_limit, llm_model.info.context_length)
|
|
147
|
+
|
|
148
|
+
# ==== Store buffered conversation history in memory.
|
|
149
|
+
memory = get_langgraph_checkpointer()
|
|
150
|
+
|
|
151
|
+
# ==== Load skills
|
|
152
|
+
tools: list[BaseTool | dict] = []
|
|
153
|
+
|
|
154
|
+
if agent.skills:
|
|
155
|
+
for k, v in agent.skills.items():
|
|
156
|
+
if not v.get("enabled", False):
|
|
157
|
+
continue
|
|
158
|
+
try:
|
|
159
|
+
skill_module = importlib.import_module(f"intentkit.skills.{k}")
|
|
160
|
+
if hasattr(skill_module, "get_skills"):
|
|
161
|
+
skill_tools = await skill_module.get_skills(
|
|
162
|
+
v, is_private, skill_store, agent_id=agent.id
|
|
163
|
+
)
|
|
164
|
+
if skill_tools and len(skill_tools) > 0:
|
|
165
|
+
tools.extend(skill_tools)
|
|
166
|
+
else:
|
|
167
|
+
logger.error(f"Skill {k} does not have get_skills function")
|
|
168
|
+
except ImportError as e:
|
|
169
|
+
logger.error(f"Could not import skill module: {k} ({e})")
|
|
170
|
+
|
|
171
|
+
# filter the duplicate tools
|
|
172
|
+
tools = list({tool.name: tool for tool in tools}.values())
|
|
173
|
+
|
|
174
|
+
# Add search tools if requested
|
|
175
|
+
if (
|
|
176
|
+
has_search
|
|
177
|
+
and llm_model.info.provider == LLMProvider.OPENAI
|
|
178
|
+
and llm_model.info.supports_search
|
|
179
|
+
):
|
|
180
|
+
tools.append({"type": "web_search_preview"})
|
|
181
|
+
|
|
182
|
+
# finally, set up the system prompt
|
|
183
|
+
prompt = agent_prompt(agent, agent_data)
|
|
184
|
+
# Escape curly braces in the prompt
|
|
185
|
+
escaped_prompt = prompt.replace("{", "{{").replace("}", "}}")
|
|
186
|
+
# Process message to handle @skill patterns
|
|
187
|
+
if config.admin_llm_skill_control:
|
|
188
|
+
escaped_prompt = await explain_prompt(escaped_prompt)
|
|
189
|
+
prompt_array = [
|
|
190
|
+
("system", escaped_prompt),
|
|
191
|
+
("placeholder", "{entrypoint_prompt}"),
|
|
192
|
+
("placeholder", "{messages}"),
|
|
193
|
+
]
|
|
194
|
+
if agent.prompt_append:
|
|
195
|
+
# Escape any curly braces in prompt_append
|
|
196
|
+
escaped_append = agent.prompt_append.replace("{", "{{").replace("}", "}}")
|
|
197
|
+
# Process message to handle @skill patterns
|
|
198
|
+
if config.admin_llm_skill_control:
|
|
199
|
+
escaped_append = await explain_prompt(escaped_append)
|
|
200
|
+
prompt_array.append(("system", escaped_append))
|
|
201
|
+
|
|
202
|
+
prompt_temp = ChatPromptTemplate.from_messages(prompt_array)
|
|
203
|
+
|
|
204
|
+
def formatted_prompt(
|
|
205
|
+
state: AgentState, config: RunnableConfig
|
|
206
|
+
) -> list[BaseMessage]:
|
|
207
|
+
entrypoint_prompt = []
|
|
208
|
+
if config.get("configurable") and config["configurable"].get(
|
|
209
|
+
"entrypoint_prompt"
|
|
210
|
+
):
|
|
211
|
+
entrypoint_prompt = [
|
|
212
|
+
("system", config["configurable"]["entrypoint_prompt"])
|
|
213
|
+
]
|
|
214
|
+
return prompt_temp.invoke(
|
|
215
|
+
{"messages": state["messages"], "entrypoint_prompt": entrypoint_prompt},
|
|
216
|
+
config,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# log final prompt and all skills
|
|
220
|
+
logger.debug(
|
|
221
|
+
f"[{agent.id}{'-private' if is_private else ''}] init prompt: {escaped_prompt}"
|
|
222
|
+
)
|
|
223
|
+
for tool in tools:
|
|
224
|
+
logger.info(
|
|
225
|
+
f"[{agent.id}{'-private' if is_private else ''}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Pre model hook
|
|
229
|
+
pre_model_hook = PreModelNode(
|
|
230
|
+
model=llm,
|
|
231
|
+
short_term_memory_strategy=agent.short_term_memory_strategy,
|
|
232
|
+
max_tokens=input_token_limit // 2,
|
|
233
|
+
max_summary_tokens=2048, # later we can let agent to set this
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Create ReAct Agent using the LLM and CDP Agentkit tools.
|
|
237
|
+
executor = create_react_agent(
|
|
238
|
+
model=llm,
|
|
239
|
+
tools=tools,
|
|
240
|
+
prompt=formatted_prompt,
|
|
241
|
+
pre_model_hook=pre_model_hook,
|
|
242
|
+
post_model_hook=post_model_node if config.payment_enabled else None,
|
|
243
|
+
state_schema=AgentState,
|
|
244
|
+
checkpointer=memory,
|
|
245
|
+
debug=config.debug_checkpoint,
|
|
246
|
+
name=agent.id,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return executor
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
async def initialize_agent(aid, is_private=False):
|
|
253
|
+
"""Initialize an AI agent with specified configuration and tools.
|
|
254
|
+
|
|
255
|
+
This function:
|
|
256
|
+
1. Loads agent configuration from database
|
|
257
|
+
2. Uses create_agent to build the agent
|
|
258
|
+
3. Caches the agent
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
aid (str): Agent ID to initialize
|
|
262
|
+
is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
Agent: Initialized LangChain agent
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
HTTPException: If agent not found (404) or database error (500)
|
|
269
|
+
"""
|
|
270
|
+
# get the agent from the database
|
|
271
|
+
agent: Optional[Agent] = await Agent.get(aid)
|
|
272
|
+
if not agent:
|
|
273
|
+
raise HTTPException(status_code=404, detail="Agent not found")
|
|
274
|
+
|
|
275
|
+
# Determine if search should be enabled based on model capabilities
|
|
276
|
+
from intentkit.models.llm import create_llm_model
|
|
277
|
+
|
|
278
|
+
llm_model = await create_llm_model(
|
|
279
|
+
model_name=agent.model,
|
|
280
|
+
temperature=agent.temperature,
|
|
281
|
+
frequency_penalty=agent.frequency_penalty,
|
|
282
|
+
presence_penalty=agent.presence_penalty,
|
|
283
|
+
)
|
|
284
|
+
has_search = (
|
|
285
|
+
llm_model.info.provider == LLMProvider.OPENAI and llm_model.info.supports_search
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Create the agent using the new create_agent function
|
|
289
|
+
executor = await create_agent(agent, is_private, has_search)
|
|
290
|
+
|
|
291
|
+
# Cache the agent executor
|
|
292
|
+
if is_private:
|
|
293
|
+
_private_agents[aid] = executor
|
|
294
|
+
_private_agents_updated[aid] = agent.updated_at
|
|
295
|
+
else:
|
|
296
|
+
_agents[aid] = executor
|
|
297
|
+
_agents_updated[aid] = agent.updated_at
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
async def agent_executor(
|
|
301
|
+
agent_id: str, is_private: bool
|
|
302
|
+
) -> (CompiledStateGraph, float):
|
|
303
|
+
start = time.perf_counter()
|
|
304
|
+
agent = await Agent.get(agent_id)
|
|
305
|
+
if not agent:
|
|
306
|
+
raise HTTPException(status_code=404, detail="Agent not found")
|
|
307
|
+
agents = _private_agents if is_private else _agents
|
|
308
|
+
agents_updated = _private_agents_updated if is_private else _agents_updated
|
|
309
|
+
|
|
310
|
+
# Check if agent needs reinitialization due to updates
|
|
311
|
+
needs_reinit = False
|
|
312
|
+
if agent_id in agents:
|
|
313
|
+
if (
|
|
314
|
+
agent_id not in agents_updated
|
|
315
|
+
or agent.updated_at != agents_updated[agent_id]
|
|
316
|
+
):
|
|
317
|
+
needs_reinit = True
|
|
318
|
+
logger.info(
|
|
319
|
+
f"Reinitializing agent {agent_id} due to updates, private mode: {is_private}"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# cold start or needs reinitialization
|
|
323
|
+
cold_start_cost = 0.0
|
|
324
|
+
if (agent_id not in agents) or needs_reinit:
|
|
325
|
+
await initialize_agent(agent_id, is_private)
|
|
326
|
+
cold_start_cost = time.perf_counter() - start
|
|
327
|
+
return agents[agent_id], cold_start_cost
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
async def stream_agent(message: ChatMessageCreate):
|
|
331
|
+
"""
|
|
332
|
+
Stream agent execution results as an async generator.
|
|
333
|
+
|
|
334
|
+
This function:
|
|
335
|
+
1. Configures execution context with thread ID
|
|
336
|
+
2. Initializes agent if not in cache
|
|
337
|
+
3. Streams agent execution results
|
|
338
|
+
4. Formats and times the execution steps
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
message (ChatMessageCreate): The chat message containing agent_id, chat_id, and message content
|
|
342
|
+
|
|
343
|
+
Yields:
|
|
344
|
+
ChatMessage: Individual response messages including timing information
|
|
345
|
+
"""
|
|
346
|
+
start = time.perf_counter()
|
|
347
|
+
# make sure reply_to is set
|
|
348
|
+
message.reply_to = message.id
|
|
349
|
+
|
|
350
|
+
# save input message first
|
|
351
|
+
input = await message.save()
|
|
352
|
+
|
|
353
|
+
# agent
|
|
354
|
+
agent = await Agent.get(input.agent_id)
|
|
355
|
+
|
|
356
|
+
# model
|
|
357
|
+
model = await LLMModelInfo.get(agent.model)
|
|
358
|
+
|
|
359
|
+
payment_enabled = config.payment_enabled
|
|
360
|
+
|
|
361
|
+
# check user balance
|
|
362
|
+
if payment_enabled:
|
|
363
|
+
if not input.user_id or not agent.owner:
|
|
364
|
+
raise IntentKitAPIError(
|
|
365
|
+
500,
|
|
366
|
+
"PaymentError",
|
|
367
|
+
"Payment is enabled but user_id or agent owner is not set",
|
|
368
|
+
)
|
|
369
|
+
if agent.fee_percentage and agent.fee_percentage > 100:
|
|
370
|
+
owner = await User.get(agent.owner)
|
|
371
|
+
if owner and agent.fee_percentage > 100 + owner.nft_count * 10:
|
|
372
|
+
error_message_create = ChatMessageCreate(
|
|
373
|
+
id=str(XID()),
|
|
374
|
+
agent_id=input.agent_id,
|
|
375
|
+
chat_id=input.chat_id,
|
|
376
|
+
user_id=input.user_id,
|
|
377
|
+
author_id=input.agent_id,
|
|
378
|
+
author_type=AuthorType.SYSTEM,
|
|
379
|
+
thread_type=input.author_type,
|
|
380
|
+
reply_to=input.id,
|
|
381
|
+
message="If you are the owner of this agent, please Update the Service Fee % to be in compliance with the Nation guidelines (Max 100% + 10% per Nation Pass NFT held)",
|
|
382
|
+
time_cost=time.perf_counter() - start,
|
|
383
|
+
)
|
|
384
|
+
error_message = await error_message_create.save()
|
|
385
|
+
yield error_message
|
|
386
|
+
return
|
|
387
|
+
# payer
|
|
388
|
+
payer = input.user_id
|
|
389
|
+
if input.author_type in [
|
|
390
|
+
AuthorType.TELEGRAM,
|
|
391
|
+
AuthorType.TWITTER,
|
|
392
|
+
AuthorType.API,
|
|
393
|
+
]:
|
|
394
|
+
payer = agent.owner
|
|
395
|
+
# user account
|
|
396
|
+
user_account = await CreditAccount.get_or_create(OwnerType.USER, payer)
|
|
397
|
+
# quota
|
|
398
|
+
quota = await AgentQuota.get(message.agent_id)
|
|
399
|
+
# payment settings
|
|
400
|
+
payment_settings = await AppSetting.payment()
|
|
401
|
+
# agent abuse check
|
|
402
|
+
abuse_check = True
|
|
403
|
+
if (
|
|
404
|
+
payment_settings.agent_whitelist_enabled
|
|
405
|
+
and agent.id in payment_settings.agent_whitelist
|
|
406
|
+
):
|
|
407
|
+
abuse_check = False
|
|
408
|
+
if abuse_check and payer != agent.owner and user_account.free_credits > 0:
|
|
409
|
+
if quota and quota.free_income_daily > 24000:
|
|
410
|
+
error_message_create = ChatMessageCreate(
|
|
411
|
+
id=str(XID()),
|
|
412
|
+
agent_id=input.agent_id,
|
|
413
|
+
chat_id=input.chat_id,
|
|
414
|
+
user_id=input.user_id,
|
|
415
|
+
author_id=input.agent_id,
|
|
416
|
+
author_type=AuthorType.SYSTEM,
|
|
417
|
+
thread_type=input.author_type,
|
|
418
|
+
reply_to=input.id,
|
|
419
|
+
message="This Agent has reached its free CAP income limit for today! Start using paid CAPs or wait until this limit expires in less than 24 hours.",
|
|
420
|
+
time_cost=time.perf_counter() - start,
|
|
421
|
+
)
|
|
422
|
+
error_message = await error_message_create.save()
|
|
423
|
+
yield error_message
|
|
424
|
+
return
|
|
425
|
+
# avg cost
|
|
426
|
+
avg_count = 1
|
|
427
|
+
if quota and quota.avg_action_cost > 0:
|
|
428
|
+
avg_count = quota.avg_action_cost
|
|
429
|
+
if not user_account.has_sufficient_credits(avg_count):
|
|
430
|
+
error_message_create = ChatMessageCreate(
|
|
431
|
+
id=str(XID()),
|
|
432
|
+
agent_id=input.agent_id,
|
|
433
|
+
chat_id=input.chat_id,
|
|
434
|
+
user_id=input.user_id,
|
|
435
|
+
author_id=input.agent_id,
|
|
436
|
+
author_type=AuthorType.SYSTEM,
|
|
437
|
+
thread_type=input.author_type,
|
|
438
|
+
reply_to=input.id,
|
|
439
|
+
message="Insufficient balance.",
|
|
440
|
+
time_cost=time.perf_counter() - start,
|
|
441
|
+
)
|
|
442
|
+
error_message = await error_message_create.save()
|
|
443
|
+
yield error_message
|
|
444
|
+
return
|
|
445
|
+
|
|
446
|
+
is_private = False
|
|
447
|
+
if input.user_id == agent.owner:
|
|
448
|
+
is_private = True
|
|
449
|
+
|
|
450
|
+
executor, cold_start_cost = await agent_executor(input.agent_id, is_private)
|
|
451
|
+
last = start + cold_start_cost
|
|
452
|
+
|
|
453
|
+
# Extract images from attachments
|
|
454
|
+
image_urls = []
|
|
455
|
+
if input.attachments:
|
|
456
|
+
image_urls = [
|
|
457
|
+
att["url"]
|
|
458
|
+
for att in input.attachments
|
|
459
|
+
if "type" in att and att["type"] == "image" and "url" in att
|
|
460
|
+
]
|
|
461
|
+
|
|
462
|
+
# Process input message to handle @skill patterns
|
|
463
|
+
if config.admin_llm_skill_control:
|
|
464
|
+
input_message = await explain_prompt(input.message)
|
|
465
|
+
else:
|
|
466
|
+
input_message = input.message
|
|
467
|
+
|
|
468
|
+
# super mode
|
|
469
|
+
recursion_limit = 30
|
|
470
|
+
if re.search(r"\b@super\b", input_message):
|
|
471
|
+
recursion_limit = 300
|
|
472
|
+
# Remove @super from the message
|
|
473
|
+
input_message = re.sub(r"\b@super\b", "", input_message).strip()
|
|
474
|
+
|
|
475
|
+
# llm native search
|
|
476
|
+
if re.search(r"\b@search\b", input_message) or re.search(
|
|
477
|
+
r"\b@web\b", input_message
|
|
478
|
+
):
|
|
479
|
+
if model.supports_search:
|
|
480
|
+
input_message = re.sub(
|
|
481
|
+
r"\b@search\b",
|
|
482
|
+
"(You have native search tool, you can use it to get more recent information)",
|
|
483
|
+
input_message,
|
|
484
|
+
).strip()
|
|
485
|
+
input_message = re.sub(
|
|
486
|
+
r"\b@web\b",
|
|
487
|
+
"(You have native search tool, you can use it to get more recent information)",
|
|
488
|
+
input_message,
|
|
489
|
+
).strip()
|
|
490
|
+
else:
|
|
491
|
+
input_message = re.sub(r"\b@search\b", "", input_message).strip()
|
|
492
|
+
input_message = re.sub(r"\b@web\b", "", input_message).strip()
|
|
493
|
+
|
|
494
|
+
# content to llm
|
|
495
|
+
content = [
|
|
496
|
+
{"type": "text", "text": input_message},
|
|
497
|
+
]
|
|
498
|
+
# if the model doesn't natively support image parsing, add the image URLs to the message
|
|
499
|
+
if image_urls:
|
|
500
|
+
if (
|
|
501
|
+
agent.has_image_parser_skill(is_private=is_private)
|
|
502
|
+
and not model.supports_image_input
|
|
503
|
+
):
|
|
504
|
+
input_message += f"\n\nImages:\n{'\n'.join(image_urls)}"
|
|
505
|
+
content = [
|
|
506
|
+
{"type": "text", "text": input_message},
|
|
507
|
+
]
|
|
508
|
+
else:
|
|
509
|
+
# anyway, pass it directly to LLM
|
|
510
|
+
content.extend(
|
|
511
|
+
[
|
|
512
|
+
{"type": "image_url", "image_url": {"url": image_url}}
|
|
513
|
+
for image_url in image_urls
|
|
514
|
+
]
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
messages = [
|
|
518
|
+
HumanMessage(content=content),
|
|
519
|
+
]
|
|
520
|
+
|
|
521
|
+
entrypoint_prompt = None
|
|
522
|
+
if (
|
|
523
|
+
agent.twitter_entrypoint_enabled
|
|
524
|
+
and agent.twitter_entrypoint_prompt
|
|
525
|
+
and input.author_type == AuthorType.TWITTER
|
|
526
|
+
):
|
|
527
|
+
entrypoint_prompt = agent.twitter_entrypoint_prompt
|
|
528
|
+
logger.debug("twitter entrypoint prompt added")
|
|
529
|
+
elif (
|
|
530
|
+
agent.telegram_entrypoint_enabled
|
|
531
|
+
and agent.telegram_entrypoint_prompt
|
|
532
|
+
and input.author_type == AuthorType.TELEGRAM
|
|
533
|
+
):
|
|
534
|
+
entrypoint_prompt = agent.telegram_entrypoint_prompt
|
|
535
|
+
logger.debug("telegram entrypoint prompt added")
|
|
536
|
+
if entrypoint_prompt and config.admin_llm_skill_control:
|
|
537
|
+
entrypoint_prompt = await explain_prompt(entrypoint_prompt)
|
|
538
|
+
|
|
539
|
+
# stream config
|
|
540
|
+
thread_id = f"{input.agent_id}-{input.chat_id}"
|
|
541
|
+
stream_config = {
|
|
542
|
+
"configurable": {
|
|
543
|
+
"agent": agent,
|
|
544
|
+
"thread_id": thread_id,
|
|
545
|
+
"user_id": input.user_id,
|
|
546
|
+
"entrypoint": input.author_type,
|
|
547
|
+
"entrypoint_prompt": entrypoint_prompt,
|
|
548
|
+
"payer": payer if payment_enabled else None,
|
|
549
|
+
},
|
|
550
|
+
"recursion_limit": recursion_limit,
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
# run
|
|
554
|
+
cached_tool_step = None
|
|
555
|
+
try:
|
|
556
|
+
async for chunk in executor.astream({"messages": messages}, stream_config):
|
|
557
|
+
this_time = time.perf_counter()
|
|
558
|
+
logger.debug(f"stream chunk: {chunk}", extra={"thread_id": thread_id})
|
|
559
|
+
if "agent" in chunk and "messages" in chunk["agent"]:
|
|
560
|
+
if len(chunk["agent"]["messages"]) != 1:
|
|
561
|
+
logger.error(
|
|
562
|
+
"unexpected agent message: " + str(chunk["agent"]["messages"]),
|
|
563
|
+
extra={"thread_id": thread_id},
|
|
564
|
+
)
|
|
565
|
+
msg = chunk["agent"]["messages"][0]
|
|
566
|
+
if hasattr(msg, "tool_calls") and msg.tool_calls:
|
|
567
|
+
# tool calls, save for later use, if it is deleted by post_model_hook, will not be used.
|
|
568
|
+
cached_tool_step = msg
|
|
569
|
+
if hasattr(msg, "content") and msg.content:
|
|
570
|
+
content = msg.content
|
|
571
|
+
if isinstance(msg.content, list):
|
|
572
|
+
# in new version, content item maybe a list
|
|
573
|
+
content = msg.content[0]
|
|
574
|
+
if isinstance(content, dict):
|
|
575
|
+
if "text" in content:
|
|
576
|
+
content = content["text"]
|
|
577
|
+
else:
|
|
578
|
+
content = str(content)
|
|
579
|
+
logger.error(f"unexpected content type: {content}")
|
|
580
|
+
# agent message
|
|
581
|
+
chat_message_create = ChatMessageCreate(
|
|
582
|
+
id=str(XID()),
|
|
583
|
+
agent_id=input.agent_id,
|
|
584
|
+
chat_id=input.chat_id,
|
|
585
|
+
user_id=input.user_id,
|
|
586
|
+
author_id=input.agent_id,
|
|
587
|
+
author_type=AuthorType.AGENT,
|
|
588
|
+
model=agent.model,
|
|
589
|
+
thread_type=input.author_type,
|
|
590
|
+
reply_to=input.id,
|
|
591
|
+
message=content,
|
|
592
|
+
input_tokens=(
|
|
593
|
+
msg.usage_metadata.get("input_tokens", 0)
|
|
594
|
+
if hasattr(msg, "usage_metadata") and msg.usage_metadata
|
|
595
|
+
else 0
|
|
596
|
+
),
|
|
597
|
+
output_tokens=(
|
|
598
|
+
msg.usage_metadata.get("output_tokens", 0)
|
|
599
|
+
if hasattr(msg, "usage_metadata") and msg.usage_metadata
|
|
600
|
+
else 0
|
|
601
|
+
),
|
|
602
|
+
time_cost=this_time - last,
|
|
603
|
+
)
|
|
604
|
+
last = this_time
|
|
605
|
+
if cold_start_cost > 0:
|
|
606
|
+
chat_message_create.cold_start_cost = cold_start_cost
|
|
607
|
+
cold_start_cost = 0
|
|
608
|
+
# handle message and payment in one transaction
|
|
609
|
+
async with get_session() as session:
|
|
610
|
+
# payment
|
|
611
|
+
if payment_enabled:
|
|
612
|
+
amount = await model.calculate_cost(
|
|
613
|
+
chat_message_create.input_tokens,
|
|
614
|
+
chat_message_create.output_tokens,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
# Check for web_search_call in additional_kwargs
|
|
618
|
+
if (
|
|
619
|
+
hasattr(msg, "additional_kwargs")
|
|
620
|
+
and msg.additional_kwargs
|
|
621
|
+
):
|
|
622
|
+
tool_outputs = msg.additional_kwargs.get(
|
|
623
|
+
"tool_outputs", []
|
|
624
|
+
)
|
|
625
|
+
for tool_output in tool_outputs:
|
|
626
|
+
if tool_output.get("type") == "web_search_call":
|
|
627
|
+
logger.info(
|
|
628
|
+
f"[{input.agent_id}] Found web_search_call in additional_kwargs"
|
|
629
|
+
)
|
|
630
|
+
amount += 35
|
|
631
|
+
break
|
|
632
|
+
credit_event = await expense_message(
|
|
633
|
+
session,
|
|
634
|
+
payer,
|
|
635
|
+
chat_message_create.id,
|
|
636
|
+
input.id,
|
|
637
|
+
amount,
|
|
638
|
+
agent,
|
|
639
|
+
)
|
|
640
|
+
logger.info(f"[{input.agent_id}] expense message: {amount}")
|
|
641
|
+
chat_message_create.credit_event_id = credit_event.id
|
|
642
|
+
chat_message_create.credit_cost = credit_event.total_amount
|
|
643
|
+
chat_message = await chat_message_create.save_in_session(
|
|
644
|
+
session
|
|
645
|
+
)
|
|
646
|
+
await session.commit()
|
|
647
|
+
yield chat_message
|
|
648
|
+
elif "tools" in chunk and "messages" in chunk["tools"]:
|
|
649
|
+
if not cached_tool_step:
|
|
650
|
+
logger.error(
|
|
651
|
+
"unexpected tools message: " + str(chunk["tools"]),
|
|
652
|
+
extra={"thread_id": thread_id},
|
|
653
|
+
)
|
|
654
|
+
continue
|
|
655
|
+
skill_calls = []
|
|
656
|
+
have_first_call_in_cache = False # tool node emit every tool call
|
|
657
|
+
for msg in chunk["tools"]["messages"]:
|
|
658
|
+
if not hasattr(msg, "tool_call_id"):
|
|
659
|
+
logger.error(
|
|
660
|
+
"unexpected tools message: " + str(chunk["tools"]),
|
|
661
|
+
extra={"thread_id": thread_id},
|
|
662
|
+
)
|
|
663
|
+
continue
|
|
664
|
+
for call_index, call in enumerate(cached_tool_step.tool_calls):
|
|
665
|
+
if call["id"] == msg.tool_call_id:
|
|
666
|
+
if call_index == 0:
|
|
667
|
+
have_first_call_in_cache = True
|
|
668
|
+
skill_call: ChatMessageSkillCall = {
|
|
669
|
+
"id": msg.tool_call_id,
|
|
670
|
+
"name": call["name"],
|
|
671
|
+
"parameters": call["args"],
|
|
672
|
+
"success": True,
|
|
673
|
+
}
|
|
674
|
+
if msg.status == "error":
|
|
675
|
+
skill_call["success"] = False
|
|
676
|
+
skill_call["error_message"] = str(msg.content)
|
|
677
|
+
else:
|
|
678
|
+
if config.debug:
|
|
679
|
+
skill_call["response"] = str(msg.content)
|
|
680
|
+
else:
|
|
681
|
+
skill_call["response"] = textwrap.shorten(
|
|
682
|
+
str(msg.content), width=1000, placeholder="..."
|
|
683
|
+
)
|
|
684
|
+
skill_calls.append(skill_call)
|
|
685
|
+
break
|
|
686
|
+
skill_message_create = ChatMessageCreate(
|
|
687
|
+
id=str(XID()),
|
|
688
|
+
agent_id=input.agent_id,
|
|
689
|
+
chat_id=input.chat_id,
|
|
690
|
+
user_id=input.user_id,
|
|
691
|
+
author_id=input.agent_id,
|
|
692
|
+
author_type=AuthorType.SKILL,
|
|
693
|
+
model=agent.model,
|
|
694
|
+
thread_type=input.author_type,
|
|
695
|
+
reply_to=input.id,
|
|
696
|
+
message="",
|
|
697
|
+
skill_calls=skill_calls,
|
|
698
|
+
input_tokens=(
|
|
699
|
+
cached_tool_step.usage_metadata.get("input_tokens", 0)
|
|
700
|
+
if hasattr(cached_tool_step, "usage_metadata")
|
|
701
|
+
and cached_tool_step.usage_metadata
|
|
702
|
+
and have_first_call_in_cache
|
|
703
|
+
else 0
|
|
704
|
+
),
|
|
705
|
+
output_tokens=(
|
|
706
|
+
cached_tool_step.usage_metadata.get("output_tokens", 0)
|
|
707
|
+
if hasattr(cached_tool_step, "usage_metadata")
|
|
708
|
+
and cached_tool_step.usage_metadata
|
|
709
|
+
and have_first_call_in_cache
|
|
710
|
+
else 0
|
|
711
|
+
),
|
|
712
|
+
time_cost=this_time - last,
|
|
713
|
+
)
|
|
714
|
+
last = this_time
|
|
715
|
+
if cold_start_cost > 0:
|
|
716
|
+
skill_message_create.cold_start_cost = cold_start_cost
|
|
717
|
+
cold_start_cost = 0
|
|
718
|
+
# save message and credit in one transaction
|
|
719
|
+
async with get_session() as session:
|
|
720
|
+
if payment_enabled:
|
|
721
|
+
# message payment, only first call in a group has message bill
|
|
722
|
+
if have_first_call_in_cache:
|
|
723
|
+
message_amount = await model.calculate_cost(
|
|
724
|
+
skill_message_create.input_tokens,
|
|
725
|
+
skill_message_create.output_tokens,
|
|
726
|
+
)
|
|
727
|
+
message_payment_event = await expense_message(
|
|
728
|
+
session,
|
|
729
|
+
payer,
|
|
730
|
+
skill_message_create.id,
|
|
731
|
+
input.id,
|
|
732
|
+
message_amount,
|
|
733
|
+
agent,
|
|
734
|
+
)
|
|
735
|
+
skill_message_create.credit_event_id = (
|
|
736
|
+
message_payment_event.id
|
|
737
|
+
)
|
|
738
|
+
skill_message_create.credit_cost = (
|
|
739
|
+
message_payment_event.total_amount
|
|
740
|
+
)
|
|
741
|
+
# skill payment
|
|
742
|
+
for skill_call in skill_calls:
|
|
743
|
+
if not skill_call["success"]:
|
|
744
|
+
continue
|
|
745
|
+
payment_event = await expense_skill(
|
|
746
|
+
session,
|
|
747
|
+
payer,
|
|
748
|
+
skill_message_create.id,
|
|
749
|
+
input.id,
|
|
750
|
+
skill_call["id"],
|
|
751
|
+
skill_call["name"],
|
|
752
|
+
agent,
|
|
753
|
+
)
|
|
754
|
+
skill_call["credit_event_id"] = payment_event.id
|
|
755
|
+
skill_call["credit_cost"] = payment_event.total_amount
|
|
756
|
+
logger.info(
|
|
757
|
+
f"[{input.agent_id}] skill payment: {skill_call}"
|
|
758
|
+
)
|
|
759
|
+
skill_message_create.skill_calls = skill_calls
|
|
760
|
+
skill_message = await skill_message_create.save_in_session(session)
|
|
761
|
+
await session.commit()
|
|
762
|
+
yield skill_message
|
|
763
|
+
elif "pre_model_hook" in chunk:
|
|
764
|
+
pass
|
|
765
|
+
elif "post_model_hook" in chunk:
|
|
766
|
+
logger.debug(
|
|
767
|
+
f"post_model_hook: {chunk}",
|
|
768
|
+
extra={"thread_id": thread_id},
|
|
769
|
+
)
|
|
770
|
+
if chunk["post_model_hook"] and "error" in chunk["post_model_hook"]:
|
|
771
|
+
if (
|
|
772
|
+
chunk["post_model_hook"]["error"]
|
|
773
|
+
== AgentError.INSUFFICIENT_CREDITS
|
|
774
|
+
):
|
|
775
|
+
if "messages" in chunk["post_model_hook"]:
|
|
776
|
+
msg = chunk["post_model_hook"]["messages"][-1]
|
|
777
|
+
content = msg.content
|
|
778
|
+
if isinstance(msg.content, list):
|
|
779
|
+
# in new version, content item maybe a list
|
|
780
|
+
content = msg.content[0]
|
|
781
|
+
post_model_message_create = ChatMessageCreate(
|
|
782
|
+
id=str(XID()),
|
|
783
|
+
agent_id=input.agent_id,
|
|
784
|
+
chat_id=input.chat_id,
|
|
785
|
+
user_id=input.user_id,
|
|
786
|
+
author_id=input.agent_id,
|
|
787
|
+
author_type=AuthorType.AGENT,
|
|
788
|
+
model=agent.model,
|
|
789
|
+
thread_type=input.author_type,
|
|
790
|
+
reply_to=input.id,
|
|
791
|
+
message=content,
|
|
792
|
+
input_tokens=0,
|
|
793
|
+
output_tokens=0,
|
|
794
|
+
time_cost=this_time - last,
|
|
795
|
+
)
|
|
796
|
+
last = this_time
|
|
797
|
+
if cold_start_cost > 0:
|
|
798
|
+
post_model_message_create.cold_start_cost = (
|
|
799
|
+
cold_start_cost
|
|
800
|
+
)
|
|
801
|
+
cold_start_cost = 0
|
|
802
|
+
post_model_message = await post_model_message_create.save()
|
|
803
|
+
yield post_model_message
|
|
804
|
+
error_message_create = ChatMessageCreate(
|
|
805
|
+
id=str(XID()),
|
|
806
|
+
agent_id=input.agent_id,
|
|
807
|
+
chat_id=input.chat_id,
|
|
808
|
+
user_id=input.user_id,
|
|
809
|
+
author_id=input.agent_id,
|
|
810
|
+
author_type=AuthorType.SYSTEM,
|
|
811
|
+
thread_type=input.author_type,
|
|
812
|
+
reply_to=input.id,
|
|
813
|
+
message="Insufficient balance.",
|
|
814
|
+
time_cost=0,
|
|
815
|
+
)
|
|
816
|
+
error_message = await error_message_create.save()
|
|
817
|
+
yield error_message
|
|
818
|
+
else:
|
|
819
|
+
error_traceback = traceback.format_exc()
|
|
820
|
+
logger.error(
|
|
821
|
+
f"unexpected message type: {str(chunk)}\n{error_traceback}",
|
|
822
|
+
extra={"thread_id": thread_id},
|
|
823
|
+
)
|
|
824
|
+
except SQLAlchemyError as e:
|
|
825
|
+
error_traceback = traceback.format_exc()
|
|
826
|
+
logger.error(
|
|
827
|
+
f"failed to execute agent: {str(e)}\n{error_traceback}",
|
|
828
|
+
extra={"thread_id": thread_id},
|
|
829
|
+
)
|
|
830
|
+
error_message_create = ChatMessageCreate(
|
|
831
|
+
id=str(XID()),
|
|
832
|
+
agent_id=input.agent_id,
|
|
833
|
+
chat_id=input.chat_id,
|
|
834
|
+
user_id=input.user_id,
|
|
835
|
+
author_id=input.agent_id,
|
|
836
|
+
author_type=AuthorType.SYSTEM,
|
|
837
|
+
thread_type=input.author_type,
|
|
838
|
+
reply_to=input.id,
|
|
839
|
+
message="IntentKit Internal Error",
|
|
840
|
+
time_cost=time.perf_counter() - start,
|
|
841
|
+
)
|
|
842
|
+
error_message = await error_message_create.save()
|
|
843
|
+
yield error_message
|
|
844
|
+
return
|
|
845
|
+
except GraphRecursionError as e:
|
|
846
|
+
error_traceback = traceback.format_exc()
|
|
847
|
+
logger.error(
|
|
848
|
+
f"reached recursion limit: {str(e)}\n{error_traceback}",
|
|
849
|
+
extra={"thread_id": thread_id, "agent_id": input.agent_id},
|
|
850
|
+
)
|
|
851
|
+
error_message_create = ChatMessageCreate(
|
|
852
|
+
id=str(XID()),
|
|
853
|
+
agent_id=input.agent_id,
|
|
854
|
+
chat_id=input.chat_id,
|
|
855
|
+
user_id=input.user_id,
|
|
856
|
+
author_id=input.agent_id,
|
|
857
|
+
author_type=AuthorType.SYSTEM,
|
|
858
|
+
thread_type=input.author_type,
|
|
859
|
+
reply_to=input.id,
|
|
860
|
+
message="Step Limit Error",
|
|
861
|
+
time_cost=time.perf_counter() - start,
|
|
862
|
+
)
|
|
863
|
+
error_message = await error_message_create.save()
|
|
864
|
+
yield error_message
|
|
865
|
+
return
|
|
866
|
+
except Exception as e:
|
|
867
|
+
error_traceback = traceback.format_exc()
|
|
868
|
+
logger.error(
|
|
869
|
+
f"failed to execute agent: {str(e)}\n{error_traceback}",
|
|
870
|
+
extra={"thread_id": thread_id, "agent_id": input.agent_id},
|
|
871
|
+
)
|
|
872
|
+
error_message_create = ChatMessageCreate(
|
|
873
|
+
id=str(XID()),
|
|
874
|
+
agent_id=input.agent_id,
|
|
875
|
+
chat_id=input.chat_id,
|
|
876
|
+
user_id=input.user_id,
|
|
877
|
+
author_id=input.agent_id,
|
|
878
|
+
author_type=AuthorType.SYSTEM,
|
|
879
|
+
thread_type=input.author_type,
|
|
880
|
+
reply_to=input.id,
|
|
881
|
+
message="Internal Agent Error",
|
|
882
|
+
time_cost=time.perf_counter() - start,
|
|
883
|
+
)
|
|
884
|
+
error_message = await error_message_create.save()
|
|
885
|
+
yield error_message
|
|
886
|
+
return
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
async def execute_agent(message: ChatMessageCreate) -> list[ChatMessage]:
|
|
890
|
+
"""
|
|
891
|
+
Execute an agent with the given prompt and return response lines.
|
|
892
|
+
|
|
893
|
+
This function:
|
|
894
|
+
1. Configures execution context with thread ID
|
|
895
|
+
2. Initializes agent if not in cache
|
|
896
|
+
3. Streams agent execution results
|
|
897
|
+
4. Formats and times the execution steps
|
|
898
|
+
|
|
899
|
+
Args:
|
|
900
|
+
message (ChatMessageCreate): The chat message containing agent_id, chat_id, and message content
|
|
901
|
+
debug (bool): Enable debug mode, will save the skill results
|
|
902
|
+
|
|
903
|
+
Returns:
|
|
904
|
+
list[ChatMessage]: Formatted response lines including timing information
|
|
905
|
+
"""
|
|
906
|
+
resp = []
|
|
907
|
+
async for chat_message in stream_agent(message):
|
|
908
|
+
resp.append(chat_message)
|
|
909
|
+
return resp
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
async def clean_agent_memory(
|
|
913
|
+
agent_id: str,
|
|
914
|
+
chat_id: str = "",
|
|
915
|
+
clean_agent: bool = False,
|
|
916
|
+
clean_skill: bool = False,
|
|
917
|
+
) -> str:
|
|
918
|
+
"""
|
|
919
|
+
Clean an agent's memory with the given prompt and return response.
|
|
920
|
+
|
|
921
|
+
This function:
|
|
922
|
+
1. Cleans the agents skills data.
|
|
923
|
+
2. Cleans the thread skills data.
|
|
924
|
+
3. Cleans the graph checkpoint data.
|
|
925
|
+
4. Cleans the graph checkpoint_writes data.
|
|
926
|
+
5. Cleans the graph checkpoint_blobs data.
|
|
927
|
+
|
|
928
|
+
Args:
|
|
929
|
+
agent_id (str): Agent ID
|
|
930
|
+
chat_id (str): Thread ID for the agent memory cleanup
|
|
931
|
+
clean_agent (bool): Whether to clean agent's memory data
|
|
932
|
+
clean_skill (bool): Whether to clean skills memory data
|
|
933
|
+
|
|
934
|
+
Returns:
|
|
935
|
+
str: Successful response message.
|
|
936
|
+
"""
|
|
937
|
+
# get the agent from the database
|
|
938
|
+
try:
|
|
939
|
+
if not clean_skill and not clean_agent:
|
|
940
|
+
raise HTTPException(
|
|
941
|
+
status_code=400,
|
|
942
|
+
detail="at least one of skills data or agent memory should be true.",
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
if clean_skill:
|
|
946
|
+
await AgentSkillData.clean_data(agent_id)
|
|
947
|
+
await ThreadSkillData.clean_data(agent_id, chat_id)
|
|
948
|
+
|
|
949
|
+
async with get_session() as db:
|
|
950
|
+
if clean_agent:
|
|
951
|
+
chat_id = chat_id.strip()
|
|
952
|
+
q_suffix = "%"
|
|
953
|
+
if chat_id and chat_id != "":
|
|
954
|
+
q_suffix = chat_id
|
|
955
|
+
|
|
956
|
+
deletion_param = {"value": agent_id + "-" + q_suffix}
|
|
957
|
+
await db.execute(
|
|
958
|
+
sqlalchemy.text(
|
|
959
|
+
"DELETE FROM checkpoints WHERE thread_id like :value",
|
|
960
|
+
),
|
|
961
|
+
deletion_param,
|
|
962
|
+
)
|
|
963
|
+
await db.execute(
|
|
964
|
+
sqlalchemy.text(
|
|
965
|
+
"DELETE FROM checkpoint_writes WHERE thread_id like :value",
|
|
966
|
+
),
|
|
967
|
+
deletion_param,
|
|
968
|
+
)
|
|
969
|
+
await db.execute(
|
|
970
|
+
sqlalchemy.text(
|
|
971
|
+
"DELETE FROM checkpoint_blobs WHERE thread_id like :value",
|
|
972
|
+
),
|
|
973
|
+
deletion_param,
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
# update the updated_at field so that the agent instance will all reload
|
|
977
|
+
await db.execute(
|
|
978
|
+
update(AgentTable)
|
|
979
|
+
.where(AgentTable.id == agent_id)
|
|
980
|
+
.values(updated_at=func.now())
|
|
981
|
+
)
|
|
982
|
+
await db.commit()
|
|
983
|
+
|
|
984
|
+
logger.info(f"Agent [{agent_id}] data cleaned up successfully.")
|
|
985
|
+
return "Agent data cleaned up successfully."
|
|
986
|
+
except SQLAlchemyError as e:
|
|
987
|
+
# Handle other SQLAlchemy-related errors
|
|
988
|
+
logger.error(e)
|
|
989
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
990
|
+
except Exception as e:
|
|
991
|
+
logger.error("failed to cleanup the agent memory: " + str(e))
|
|
992
|
+
raise e
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
async def thread_stats(agent_id: str, chat_id: str) -> list[BaseMessage]:
|
|
996
|
+
thread_id = f"{agent_id}-{chat_id}"
|
|
997
|
+
stream_config = {"configurable": {"thread_id": thread_id}}
|
|
998
|
+
is_private = False
|
|
999
|
+
if chat_id.startswith("owner") or chat_id.startswith("autonomous"):
|
|
1000
|
+
is_private = True
|
|
1001
|
+
executor, _ = await agent_executor(agent_id, is_private)
|
|
1002
|
+
snap = await executor.aget_state(stream_config)
|
|
1003
|
+
if snap.values and "messages" in snap.values:
|
|
1004
|
+
return snap.values["messages"]
|
|
1005
|
+
else:
|
|
1006
|
+
return []
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
async def is_payment_required(input: ChatMessageCreate, agent: Agent) -> bool:
|
|
1010
|
+
if not config.payment_enabled:
|
|
1011
|
+
return False
|
|
1012
|
+
payment_settings = await AppSetting.payment()
|
|
1013
|
+
if payment_settings.agent_whitelist_enabled:
|
|
1014
|
+
if agent.id not in payment_settings.agent_whitelist:
|
|
1015
|
+
return False
|
|
1016
|
+
if input.user_id and agent.owner:
|
|
1017
|
+
return True
|
|
1018
|
+
return False
|