intentkit 0.6.9.dev2__py3-none-any.whl → 0.6.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +1 -1
- intentkit/abstracts/graph.py +17 -2
- intentkit/core/engine.py +49 -30
- intentkit/core/node.py +10 -20
- intentkit/models/agent.py +215 -11
- intentkit/models/agent_schema.json +4 -0
- intentkit/models/chat.py +9 -1
- intentkit/models/llm.py +53 -0
- intentkit/skills/acolyt/ask.py +2 -5
- intentkit/skills/acolyt/base.py +16 -6
- intentkit/skills/aixbt/__init__.py +3 -7
- intentkit/skills/aixbt/projects.py +12 -36
- intentkit/skills/allora/base.py +16 -6
- intentkit/skills/allora/price.py +2 -4
- intentkit/skills/base.py +8 -1
- intentkit/skills/carv/base.py +12 -10
- intentkit/skills/carv/fetch_news.py +90 -92
- intentkit/skills/carv/onchain_query.py +162 -164
- intentkit/skills/carv/token_info_and_price.py +108 -110
- intentkit/skills/chainlist/chain_lookup.py +1 -2
- intentkit/skills/common/current_time.py +1 -2
- intentkit/skills/cookiefun/base.py +20 -12
- intentkit/skills/cookiefun/get_account_details.py +1 -3
- intentkit/skills/cookiefun/get_account_feed.py +1 -3
- intentkit/skills/cookiefun/get_account_smart_followers.py +1 -3
- intentkit/skills/cookiefun/get_sectors.py +2 -3
- intentkit/skills/cookiefun/search_accounts.py +1 -3
- intentkit/skills/cryptocompare/fetch_news.py +3 -4
- intentkit/skills/cryptocompare/fetch_price.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +3 -4
- intentkit/skills/cryptocompare/fetch_top_volume.py +3 -4
- intentkit/skills/cryptocompare/fetch_trading_signals.py +3 -4
- intentkit/skills/cryptopanic/base.py +13 -9
- intentkit/skills/cryptopanic/fetch_crypto_news.py +150 -153
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +133 -136
- intentkit/skills/dapplooker/base.py +16 -6
- intentkit/skills/dapplooker/dapplooker_token_data.py +2 -4
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +2 -3
- intentkit/skills/defillama/coins/fetch_block.py +2 -3
- intentkit/skills/defillama/coins/fetch_current_prices.py +2 -5
- intentkit/skills/defillama/coins/fetch_first_price.py +2 -5
- intentkit/skills/defillama/coins/fetch_historical_prices.py +2 -3
- intentkit/skills/defillama/coins/fetch_price_chart.py +2 -5
- intentkit/skills/defillama/coins/fetch_price_percentage.py +2 -5
- intentkit/skills/defillama/fees/fetch_fees_overview.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +2 -3
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +2 -3
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +2 -5
- intentkit/skills/defillama/tvl/fetch_chains.py +2 -3
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +2 -3
- intentkit/skills/defillama/tvl/fetch_protocol.py +2 -5
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +2 -5
- intentkit/skills/defillama/tvl/fetch_protocols.py +2 -3
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +2 -3
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +2 -5
- intentkit/skills/defillama/volumes/fetch_options_overview.py +2 -3
- intentkit/skills/defillama/yields/fetch_pool_chart.py +2 -5
- intentkit/skills/defillama/yields/fetch_pools.py +2 -3
- intentkit/skills/dune_analytics/base.py +15 -9
- intentkit/skills/dune_analytics/fetch_kol_buys.py +125 -128
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +234 -237
- intentkit/skills/elfa/base.py +16 -6
- intentkit/skills/elfa/mention.py +2 -7
- intentkit/skills/elfa/stats.py +2 -6
- intentkit/skills/elfa/tokens.py +1 -4
- intentkit/skills/enso/base.py +25 -13
- intentkit/skills/enso/best_yield.py +1 -4
- intentkit/skills/enso/networks.py +2 -5
- intentkit/skills/enso/prices.py +1 -5
- intentkit/skills/enso/route.py +2 -5
- intentkit/skills/enso/tokens.py +1 -4
- intentkit/skills/enso/wallet.py +3 -9
- intentkit/skills/firecrawl/base.py +16 -6
- intentkit/skills/firecrawl/clear.py +1 -3
- intentkit/skills/firecrawl/crawl.py +7 -8
- intentkit/skills/firecrawl/query.py +7 -9
- intentkit/skills/firecrawl/scrape.py +7 -8
- intentkit/skills/github/github_search.py +1 -3
- intentkit/skills/heurist/base.py +15 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +3 -4
- intentkit/skills/heurist/image_generation_arthemy_comics.py +3 -4
- intentkit/skills/heurist/image_generation_arthemy_real.py +3 -4
- intentkit/skills/heurist/image_generation_braindance.py +3 -4
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +3 -4
- intentkit/skills/heurist/image_generation_flux_1_dev.py +3 -4
- intentkit/skills/heurist/image_generation_sdxl.py +3 -4
- intentkit/skills/http/get.py +0 -2
- intentkit/skills/http/post.py +0 -2
- intentkit/skills/http/put.py +0 -2
- intentkit/skills/lifi/token_execute.py +1 -3
- intentkit/skills/lifi/token_quote.py +0 -2
- intentkit/skills/moralis/base.py +15 -1
- intentkit/skills/nation/nft_check.py +2 -5
- intentkit/skills/openai/base.py +14 -5
- intentkit/skills/openai/dalle_image_generation.py +6 -5
- intentkit/skills/openai/gpt_image_generation.py +6 -5
- intentkit/skills/openai/gpt_image_to_image.py +6 -5
- intentkit/skills/openai/image_to_text.py +6 -6
- intentkit/skills/portfolio/base.py +4 -3
- intentkit/skills/portfolio/token_balances.py +2 -4
- intentkit/skills/portfolio/wallet_approvals.py +2 -4
- intentkit/skills/portfolio/wallet_defi_positions.py +3 -4
- intentkit/skills/portfolio/wallet_history.py +2 -4
- intentkit/skills/portfolio/wallet_net_worth.py +2 -4
- intentkit/skills/portfolio/wallet_nfts.py +2 -4
- intentkit/skills/portfolio/wallet_profitability.py +2 -4
- intentkit/skills/portfolio/wallet_profitability_summary.py +2 -4
- intentkit/skills/portfolio/wallet_stats.py +2 -4
- intentkit/skills/portfolio/wallet_swaps.py +2 -4
- intentkit/skills/slack/base.py +18 -0
- intentkit/skills/slack/get_channel.py +3 -4
- intentkit/skills/slack/get_message.py +3 -4
- intentkit/skills/slack/schedule_message.py +3 -4
- intentkit/skills/slack/send_message.py +3 -4
- intentkit/skills/supabase/delete_data.py +3 -6
- intentkit/skills/supabase/fetch_data.py +3 -6
- intentkit/skills/supabase/insert_data.py +3 -6
- intentkit/skills/supabase/invoke_function.py +3 -6
- intentkit/skills/supabase/update_data.py +3 -6
- intentkit/skills/supabase/upsert_data.py +3 -6
- intentkit/skills/system/add_autonomous_task.py +1 -3
- intentkit/skills/system/delete_autonomous_task.py +1 -3
- intentkit/skills/system/edit_autonomous_task.py +1 -3
- intentkit/skills/system/list_autonomous_tasks.py +1 -3
- intentkit/skills/system/read_agent_api_key.py +2 -3
- intentkit/skills/system/regenerate_agent_api_key.py +2 -5
- intentkit/skills/tavily/base.py +14 -5
- intentkit/skills/tavily/tavily_extract.py +7 -8
- intentkit/skills/tavily/tavily_search.py +11 -9
- intentkit/skills/token/base.py +4 -6
- intentkit/skills/token/erc20_transfers.py +2 -4
- intentkit/skills/token/token_analytics.py +2 -4
- intentkit/skills/token/token_price.py +2 -4
- intentkit/skills/token/token_search.py +2 -4
- intentkit/skills/twitter/base.py +41 -0
- intentkit/skills/twitter/follow_user.py +4 -4
- intentkit/skills/twitter/get_mentions.py +4 -4
- intentkit/skills/twitter/get_timeline.py +4 -4
- intentkit/skills/twitter/get_user_by_username.py +4 -4
- intentkit/skills/twitter/get_user_tweets.py +4 -4
- intentkit/skills/twitter/like_tweet.py +4 -4
- intentkit/skills/twitter/post_tweet.py +3 -4
- intentkit/skills/twitter/reply_tweet.py +3 -4
- intentkit/skills/twitter/retweet.py +4 -4
- intentkit/skills/twitter/search_tweets.py +4 -4
- intentkit/skills/unrealspeech/base.py +16 -0
- intentkit/skills/unrealspeech/text_to_speech.py +4 -4
- intentkit/skills/venice_audio/base.py +11 -9
- intentkit/skills/venice_audio/venice_audio.py +238 -240
- intentkit/skills/venice_image/base.py +23 -19
- intentkit/skills/venice_image/image_enhance/image_enhance.py +78 -80
- intentkit/skills/venice_image/image_generation/image_generation_base.py +115 -117
- intentkit/skills/venice_image/image_upscale/image_upscale.py +88 -90
- intentkit/skills/venice_image/image_vision/image_vision.py +98 -100
- intentkit/skills/web_scraper/document_indexer.py +3 -5
- intentkit/skills/web_scraper/scrape_and_index.py +14 -17
- intentkit/skills/web_scraper/website_indexer.py +8 -10
- intentkit/skills/xmtp/README.md +110 -0
- intentkit/skills/xmtp/__init__.py +82 -0
- intentkit/skills/xmtp/base.py +15 -0
- intentkit/skills/xmtp/schema.json +43 -0
- intentkit/skills/xmtp/transfer.py +155 -0
- intentkit/skills/xmtp/xmtp.png +0 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/METADATA +4 -3
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/RECORD +170 -164
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/WHEEL +0 -0
- {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dist-info}/licenses/LICENSE +0 -0
intentkit/__init__.py
CHANGED
intentkit/abstracts/graph.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from enum import Enum
|
|
2
|
-
from typing import Any,
|
|
3
|
+
from typing import Any, Dict, Literal, NotRequired, Optional
|
|
3
4
|
|
|
4
5
|
from langgraph.prebuilt.chat_agent_executor import AgentState as BaseAgentState
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from intentkit.models.agent import Agent
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
class AgentError(str, Enum):
|
|
@@ -22,4 +26,15 @@ class AgentState(BaseAgentState):
|
|
|
22
26
|
__extra__: NotRequired[Dict[str, Any]]
|
|
23
27
|
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
class AgentContext(BaseModel):
|
|
30
|
+
agent_id: str
|
|
31
|
+
chat_id: str
|
|
32
|
+
user_id: Optional[str] = None
|
|
33
|
+
app_id: Optional[str] = None
|
|
34
|
+
entrypoint: Literal["web", "twitter", "telegram", "trigger", "api"]
|
|
35
|
+
is_private: bool
|
|
36
|
+
payer: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def agent(self) -> Agent:
|
|
40
|
+
return asyncio.run(Agent.get(self.agent_id))
|
intentkit/core/engine.py
CHANGED
|
@@ -27,15 +27,15 @@ from langchain_core.messages import (
|
|
|
27
27
|
HumanMessage,
|
|
28
28
|
)
|
|
29
29
|
from langchain_core.prompts import ChatPromptTemplate
|
|
30
|
-
from langchain_core.runnables import RunnableConfig
|
|
31
30
|
from langchain_core.tools import BaseTool
|
|
32
31
|
from langgraph.errors import GraphRecursionError
|
|
33
32
|
from langgraph.graph.state import CompiledStateGraph
|
|
34
33
|
from langgraph.prebuilt import create_react_agent
|
|
34
|
+
from langgraph.runtime import Runtime
|
|
35
35
|
from sqlalchemy import func, update
|
|
36
36
|
from sqlalchemy.exc import SQLAlchemyError
|
|
37
37
|
|
|
38
|
-
from intentkit.abstracts.graph import AgentError, AgentState
|
|
38
|
+
from intentkit.abstracts.graph import AgentContext, AgentError, AgentState
|
|
39
39
|
from intentkit.config.config import config
|
|
40
40
|
from intentkit.core.credit import expense_message, expense_skill
|
|
41
41
|
from intentkit.core.node import PreModelNode, post_model_node
|
|
@@ -201,11 +201,13 @@ async def create_agent(
|
|
|
201
201
|
prompt_temp = ChatPromptTemplate.from_messages(prompt_array)
|
|
202
202
|
|
|
203
203
|
async def formatted_prompt(
|
|
204
|
-
state: AgentState,
|
|
204
|
+
state: AgentState, runtime: Runtime[AgentContext]
|
|
205
205
|
) -> list[BaseMessage]:
|
|
206
206
|
final_system_prompt = escaped_prompt
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
context = runtime.context
|
|
208
|
+
logger.debug(f"formatted_prompt, context: {context}")
|
|
209
|
+
if context.entrypoint:
|
|
210
|
+
entrypoint = context.entrypoint
|
|
209
211
|
entrypoint_prompt = None
|
|
210
212
|
if (
|
|
211
213
|
agent.twitter_entrypoint_enabled
|
|
@@ -222,11 +224,7 @@ async def create_agent(
|
|
|
222
224
|
entrypoint_prompt = agent.telegram_entrypoint_prompt
|
|
223
225
|
logger.debug("telegram entrypoint prompt added")
|
|
224
226
|
elif entrypoint == AuthorType.TRIGGER.value:
|
|
225
|
-
task_id = (
|
|
226
|
-
config["configurable"]
|
|
227
|
-
.get("chat_id", "")
|
|
228
|
-
.removeprefix("autonomous-")
|
|
229
|
-
)
|
|
227
|
+
task_id = context.chat_id.removeprefix("autonomous-")
|
|
230
228
|
# Find the autonomous task by task_id
|
|
231
229
|
autonomous_task = None
|
|
232
230
|
if agent.autonomous:
|
|
@@ -263,20 +261,15 @@ async def create_agent(
|
|
|
263
261
|
if entrypoint_prompt:
|
|
264
262
|
entrypoint_prompt = await explain_prompt(entrypoint_prompt)
|
|
265
263
|
final_system_prompt = f"{final_system_prompt}## Entrypoint rules\n\n{entrypoint_prompt}\n\n"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
final_system_prompt = f"{final_system_prompt}chat_id: {chat_id}\n\n"
|
|
273
|
-
user_id = config["configurable"].get("user_id")
|
|
274
|
-
if user_id:
|
|
275
|
-
final_system_prompt = f"{final_system_prompt}user_id: {user_id}\n\n"
|
|
264
|
+
final_system_prompt = f"{final_system_prompt}## Internal Info\n\n"
|
|
265
|
+
"These are for your internal use. You can use them when querying or storing data, "
|
|
266
|
+
"but please do not directly share this information with users.\n\n"
|
|
267
|
+
final_system_prompt = f"{final_system_prompt}chat_id: {context.chat_id}\n\n"
|
|
268
|
+
if context.user_id:
|
|
269
|
+
final_system_prompt = f"{final_system_prompt}user_id: {context.user_id}\n\n"
|
|
276
270
|
system_prompt = [("system", final_system_prompt)]
|
|
277
271
|
return prompt_temp.invoke(
|
|
278
|
-
{"messages": state["messages"], "system_prompt": system_prompt}
|
|
279
|
-
config,
|
|
272
|
+
{"messages": state["messages"], "system_prompt": system_prompt}
|
|
280
273
|
)
|
|
281
274
|
|
|
282
275
|
# log final prompt and all skills
|
|
@@ -304,6 +297,7 @@ async def create_agent(
|
|
|
304
297
|
pre_model_hook=pre_model_hook,
|
|
305
298
|
post_model_hook=post_model_node if config.payment_enabled else None,
|
|
306
299
|
state_schema=AgentState,
|
|
300
|
+
context_schema=AgentContext,
|
|
307
301
|
checkpointer=memory,
|
|
308
302
|
debug=config.debug_checkpoint,
|
|
309
303
|
name=agent.id,
|
|
@@ -585,22 +579,27 @@ async def stream_agent(message: ChatMessageCreate):
|
|
|
585
579
|
thread_id = f"{input.agent_id}-{input.chat_id}"
|
|
586
580
|
stream_config = {
|
|
587
581
|
"configurable": {
|
|
588
|
-
"agent_id": agent.id,
|
|
589
582
|
"thread_id": thread_id,
|
|
590
|
-
"chat_id": input.chat_id,
|
|
591
|
-
"user_id": input.user_id,
|
|
592
|
-
"app_id": input.app_id,
|
|
593
|
-
"entrypoint": input.author_type,
|
|
594
|
-
"is_private": is_private,
|
|
595
|
-
"payer": payer if payment_enabled else None,
|
|
596
583
|
},
|
|
597
584
|
"recursion_limit": recursion_limit,
|
|
598
585
|
}
|
|
599
586
|
|
|
587
|
+
context = AgentContext(
|
|
588
|
+
agent_id=input.agent_id,
|
|
589
|
+
chat_id=input.chat_id,
|
|
590
|
+
user_id=input.user_id,
|
|
591
|
+
app_id=input.app_id,
|
|
592
|
+
entrypoint=input.author_type,
|
|
593
|
+
is_private=is_private,
|
|
594
|
+
payer=payer if payment_enabled else None,
|
|
595
|
+
)
|
|
596
|
+
|
|
600
597
|
# run
|
|
601
598
|
cached_tool_step = None
|
|
602
599
|
try:
|
|
603
|
-
async for chunk in executor.astream(
|
|
600
|
+
async for chunk in executor.astream(
|
|
601
|
+
{"messages": messages}, context=context, config=stream_config
|
|
602
|
+
):
|
|
604
603
|
this_time = time.perf_counter()
|
|
605
604
|
logger.debug(f"stream chunk: {chunk}", extra={"thread_id": thread_id})
|
|
606
605
|
if "agent" in chunk and "messages" in chunk["agent"]:
|
|
@@ -728,6 +727,26 @@ async def stream_agent(message: ChatMessageCreate):
|
|
|
728
727
|
skill_call["response"] = textwrap.shorten(
|
|
729
728
|
str(msg.content), width=1000, placeholder="..."
|
|
730
729
|
)
|
|
730
|
+
if msg.artifact:
|
|
731
|
+
artifact_message_create = ChatMessageCreate(
|
|
732
|
+
id=str(XID()),
|
|
733
|
+
agent_id=input.agent_id,
|
|
734
|
+
chat_id=input.chat_id,
|
|
735
|
+
user_id=input.user_id,
|
|
736
|
+
author_id=input.agent_id,
|
|
737
|
+
author_type=AuthorType.SKILL,
|
|
738
|
+
model=agent.model,
|
|
739
|
+
thread_type=input.author_type,
|
|
740
|
+
reply_to=input.id,
|
|
741
|
+
message="",
|
|
742
|
+
attachments=msg.artifact,
|
|
743
|
+
time_cost=this_time - last,
|
|
744
|
+
)
|
|
745
|
+
artifact_message = (
|
|
746
|
+
await artifact_message_create.save()
|
|
747
|
+
)
|
|
748
|
+
yield artifact_message
|
|
749
|
+
last = this_time
|
|
731
750
|
skill_calls.append(skill_call)
|
|
732
751
|
break
|
|
733
752
|
skill_message_create = ChatMessageCreate(
|
intentkit/core/node.py
CHANGED
|
@@ -11,8 +11,8 @@ from langchain_core.messages import (
|
|
|
11
11
|
ToolMessage,
|
|
12
12
|
)
|
|
13
13
|
from langchain_core.messages.utils import count_tokens_approximately, trim_messages
|
|
14
|
-
from langchain_core.runnables import RunnableConfig
|
|
15
14
|
from langgraph.graph.message import REMOVE_ALL_MESSAGES
|
|
15
|
+
from langgraph.runtime import get_runtime
|
|
16
16
|
from langgraph.utils.runnable import RunnableCallable
|
|
17
17
|
from langmem.short_term.summarization import (
|
|
18
18
|
DEFAULT_EXISTING_SUMMARY_PROMPT,
|
|
@@ -21,9 +21,8 @@ from langmem.short_term.summarization import (
|
|
|
21
21
|
SummarizationResult,
|
|
22
22
|
asummarize_messages,
|
|
23
23
|
)
|
|
24
|
-
from pydantic import BaseModel
|
|
25
24
|
|
|
26
|
-
from intentkit.abstracts.graph import AgentError, AgentState
|
|
25
|
+
from intentkit.abstracts.graph import AgentContext, AgentError, AgentState
|
|
27
26
|
from intentkit.core.credit import skill_cost
|
|
28
27
|
from intentkit.models.agent import Agent
|
|
29
28
|
from intentkit.models.credit import CreditAccount, OwnerType
|
|
@@ -108,11 +107,7 @@ class PreModelNode(RunnableCallable):
|
|
|
108
107
|
def _func(self, AgentState) -> dict[str, Any]:
|
|
109
108
|
raise NotImplementedError("Not implemented yet")
|
|
110
109
|
|
|
111
|
-
async def _afunc(
|
|
112
|
-
self,
|
|
113
|
-
input: AgentState,
|
|
114
|
-
config: RunnableConfig,
|
|
115
|
-
) -> dict[str, Any]:
|
|
110
|
+
async def _afunc(self, input: AgentState) -> dict[str, Any]:
|
|
116
111
|
messages, context = self._parse_input(input)
|
|
117
112
|
try:
|
|
118
113
|
_validate_chat_history(messages)
|
|
@@ -180,28 +175,23 @@ class PostModelNode(RunnableCallable):
|
|
|
180
175
|
super().__init__(self._func, self._afunc, name="post_model_node", trace=False)
|
|
181
176
|
self.func_accepts_config = True
|
|
182
177
|
|
|
183
|
-
def _func(self, input:
|
|
178
|
+
def _func(self, input: AgentState) -> dict[str, Any]:
|
|
184
179
|
raise NotImplementedError("Not implemented yet")
|
|
185
180
|
|
|
186
|
-
async def _afunc(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
) -> dict[str, Any]:
|
|
191
|
-
logger.debug(f"Running PostModelNode, input: {input}, config: {config}")
|
|
181
|
+
async def _afunc(self, input: AgentState) -> dict[str, Any]:
|
|
182
|
+
runtime = get_runtime(AgentContext)
|
|
183
|
+
context = runtime.context
|
|
184
|
+
logger.debug(f"Running PostModelNode, input: {input}, context: {context}")
|
|
192
185
|
state_update = {}
|
|
193
186
|
messages = input.get("messages")
|
|
194
187
|
if messages is None or not isinstance(messages, list) or len(messages) == 0:
|
|
195
188
|
raise ValueError("Missing required field `messages` in the input.")
|
|
196
|
-
|
|
197
|
-
if not config:
|
|
198
|
-
raise ValueError("Missing required field `configurable` in the config.")
|
|
199
|
-
payer = cfg.get("payer")
|
|
189
|
+
payer = context.payer
|
|
200
190
|
if not payer:
|
|
201
191
|
return state_update
|
|
202
192
|
logger.debug(f"last: {messages[-1]}")
|
|
203
193
|
msg = messages[-1]
|
|
204
|
-
agent_id =
|
|
194
|
+
agent_id = context.agent_id
|
|
205
195
|
agent = await Agent.get(agent_id)
|
|
206
196
|
account = await CreditAccount.get_or_create(OwnerType.USER, payer)
|
|
207
197
|
if hasattr(msg, "tool_calls") and msg.tool_calls:
|
intentkit/models/agent.py
CHANGED
|
@@ -4,8 +4,10 @@ import re
|
|
|
4
4
|
import textwrap
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
6
|
from decimal import Decimal
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
from typing import Annotated, Any, Dict, List, Literal, Optional
|
|
8
9
|
|
|
10
|
+
import jsonref
|
|
9
11
|
import yaml
|
|
10
12
|
from cron_validator import CronValidator
|
|
11
13
|
from epyxid import XID
|
|
@@ -13,7 +15,8 @@ from fastapi import HTTPException
|
|
|
13
15
|
from intentkit.models.agent_data import AgentData
|
|
14
16
|
from intentkit.models.base import Base
|
|
15
17
|
from intentkit.models.db import get_session
|
|
16
|
-
from intentkit.models.llm import LLMModelInfo
|
|
18
|
+
from intentkit.models.llm import LLMModelInfo, LLMModelInfoTable, LLMProvider
|
|
19
|
+
from intentkit.models.skill import SkillTable
|
|
17
20
|
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
|
18
21
|
from pydantic import Field as PydanticField
|
|
19
22
|
from pydantic.json_schema import SkipJsonSchema
|
|
@@ -28,6 +31,7 @@ from sqlalchemy import (
|
|
|
28
31
|
select,
|
|
29
32
|
)
|
|
30
33
|
from sqlalchemy.dialects.postgresql import JSON, JSONB
|
|
34
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
31
35
|
|
|
32
36
|
logger = logging.getLogger(__name__)
|
|
33
37
|
|
|
@@ -293,7 +297,7 @@ class AgentTable(Base):
|
|
|
293
297
|
model = Column(
|
|
294
298
|
String,
|
|
295
299
|
nullable=True,
|
|
296
|
-
default="gpt-
|
|
300
|
+
default="gpt-5-mini",
|
|
297
301
|
comment="AI model identifier to be used by this agent for processing requests. Available models: gpt-4o, gpt-4o-mini, deepseek-chat, deepseek-reasoner, grok-2, eternalai",
|
|
298
302
|
)
|
|
299
303
|
prompt = Column(
|
|
@@ -623,7 +627,7 @@ class AgentUpdate(BaseModel):
|
|
|
623
627
|
model: Annotated[
|
|
624
628
|
str,
|
|
625
629
|
PydanticField(
|
|
626
|
-
default="gpt-
|
|
630
|
+
default="gpt-5-mini",
|
|
627
631
|
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, venice-uncensored",
|
|
628
632
|
json_schema_extra={
|
|
629
633
|
"x-group": "ai",
|
|
@@ -914,38 +918,38 @@ class AgentUpdate(BaseModel):
|
|
|
914
918
|
if not self.autonomous:
|
|
915
919
|
return
|
|
916
920
|
|
|
917
|
-
for
|
|
921
|
+
for autonomous_config in self.autonomous:
|
|
918
922
|
# Check that exactly one scheduling method is provided
|
|
919
|
-
if not
|
|
923
|
+
if not autonomous_config.minutes and not autonomous_config.cron:
|
|
920
924
|
raise HTTPException(
|
|
921
925
|
status_code=400, detail="either minutes or cron must have a value"
|
|
922
926
|
)
|
|
923
927
|
|
|
924
|
-
if
|
|
928
|
+
if autonomous_config.minutes and autonomous_config.cron:
|
|
925
929
|
raise HTTPException(
|
|
926
930
|
status_code=400, detail="only one of minutes or cron can be set"
|
|
927
931
|
)
|
|
928
932
|
|
|
929
933
|
# Validate minimum interval of 5 minutes
|
|
930
|
-
if
|
|
934
|
+
if autonomous_config.minutes and autonomous_config.minutes < 5:
|
|
931
935
|
raise HTTPException(
|
|
932
936
|
status_code=400,
|
|
933
937
|
detail="The shortest execution interval is 5 minutes",
|
|
934
938
|
)
|
|
935
939
|
|
|
936
940
|
# Validate cron expression to ensure interval is at least 5 minutes
|
|
937
|
-
if
|
|
941
|
+
if autonomous_config.cron:
|
|
938
942
|
# First validate the cron expression format using cron-validator
|
|
939
943
|
|
|
940
944
|
try:
|
|
941
|
-
CronValidator.parse(
|
|
945
|
+
CronValidator.parse(autonomous_config.cron)
|
|
942
946
|
except ValueError:
|
|
943
947
|
raise HTTPException(
|
|
944
948
|
status_code=400,
|
|
945
|
-
detail=f"Invalid cron expression format: {
|
|
949
|
+
detail=f"Invalid cron expression format: {autonomous_config.cron}",
|
|
946
950
|
)
|
|
947
951
|
|
|
948
|
-
parts =
|
|
952
|
+
parts = autonomous_config.cron.split()
|
|
949
953
|
if len(parts) < 5:
|
|
950
954
|
raise HTTPException(
|
|
951
955
|
status_code=400, detail="Invalid cron expression format"
|
|
@@ -1285,6 +1289,206 @@ class Agent(AgentCreate):
|
|
|
1285
1289
|
return None
|
|
1286
1290
|
return cls.model_validate(item)
|
|
1287
1291
|
|
|
1292
|
+
def skill_config(self, category: str) -> Dict[str, Any]:
|
|
1293
|
+
return self.skills.get(category, {}) if self.skills else {}
|
|
1294
|
+
|
|
1295
|
+
@staticmethod
|
|
1296
|
+
def _is_agent_owner_only_skill(skill_schema: Dict[str, Any]) -> bool:
|
|
1297
|
+
"""Check if a skill requires agent owner API keys only based on its resolved schema."""
|
|
1298
|
+
if (
|
|
1299
|
+
skill_schema
|
|
1300
|
+
and "properties" in skill_schema
|
|
1301
|
+
and "api_key_provider" in skill_schema["properties"]
|
|
1302
|
+
):
|
|
1303
|
+
api_key_provider = skill_schema["properties"]["api_key_provider"]
|
|
1304
|
+
if "enum" in api_key_provider and api_key_provider["enum"] == [
|
|
1305
|
+
"agent_owner"
|
|
1306
|
+
]:
|
|
1307
|
+
return True
|
|
1308
|
+
return False
|
|
1309
|
+
|
|
1310
|
+
@classmethod
|
|
1311
|
+
async def get_json_schema(
|
|
1312
|
+
cls,
|
|
1313
|
+
db: AsyncSession = None,
|
|
1314
|
+
filter_owner_api_skills: bool = False,
|
|
1315
|
+
admin_llm_skill_control: bool = True,
|
|
1316
|
+
) -> Dict:
|
|
1317
|
+
"""Get the JSON schema for Agent model with all $ref references resolved.
|
|
1318
|
+
|
|
1319
|
+
This is the shared function that handles admin configuration filtering
|
|
1320
|
+
for both the API endpoint and agent generation.
|
|
1321
|
+
|
|
1322
|
+
Args:
|
|
1323
|
+
db: Database session (optional, will create if not provided)
|
|
1324
|
+
filter_owner_api_skills: Whether to filter out skills that require agent owner API keys
|
|
1325
|
+
admin_llm_skill_control: Whether to enable admin LLM and skill control features
|
|
1326
|
+
|
|
1327
|
+
Returns:
|
|
1328
|
+
Dict containing the complete JSON schema for the Agent model
|
|
1329
|
+
"""
|
|
1330
|
+
# Get database session if not provided
|
|
1331
|
+
if db is None:
|
|
1332
|
+
async with get_session() as session:
|
|
1333
|
+
return await cls.get_json_schema(
|
|
1334
|
+
session, filter_owner_api_skills, admin_llm_skill_control
|
|
1335
|
+
)
|
|
1336
|
+
|
|
1337
|
+
# Get the schema file path relative to this file
|
|
1338
|
+
current_dir = Path(__file__).parent
|
|
1339
|
+
agent_schema_path = current_dir / "agent_schema.json"
|
|
1340
|
+
|
|
1341
|
+
base_uri = f"file://{agent_schema_path}"
|
|
1342
|
+
with open(agent_schema_path) as f:
|
|
1343
|
+
schema = jsonref.load(f, base_uri=base_uri, proxies=False, lazy_load=False)
|
|
1344
|
+
|
|
1345
|
+
# Get the model property from the schema
|
|
1346
|
+
model_property = schema.get("properties", {}).get("model", {})
|
|
1347
|
+
|
|
1348
|
+
if admin_llm_skill_control:
|
|
1349
|
+
# Process model property - use LLMModelInfo as primary source
|
|
1350
|
+
if model_property:
|
|
1351
|
+
# Query all LLM models from the database
|
|
1352
|
+
stmt = select(LLMModelInfoTable).where(LLMModelInfoTable.enabled)
|
|
1353
|
+
result = await db.execute(stmt)
|
|
1354
|
+
models = result.scalars().all()
|
|
1355
|
+
|
|
1356
|
+
# Create new lists based on LLMModelInfo
|
|
1357
|
+
new_enum = []
|
|
1358
|
+
new_enum_title = []
|
|
1359
|
+
new_enum_category = []
|
|
1360
|
+
new_enum_support_skill = []
|
|
1361
|
+
|
|
1362
|
+
# Process each model from database
|
|
1363
|
+
for model in models:
|
|
1364
|
+
model_info = LLMModelInfo.model_validate(model)
|
|
1365
|
+
|
|
1366
|
+
# Add model ID to enum
|
|
1367
|
+
new_enum.append(model_info.id)
|
|
1368
|
+
|
|
1369
|
+
# Add model name as title
|
|
1370
|
+
new_enum_title.append(model_info.name)
|
|
1371
|
+
|
|
1372
|
+
# Add provider display name as category
|
|
1373
|
+
provider = (
|
|
1374
|
+
LLMProvider(model_info.provider)
|
|
1375
|
+
if isinstance(model_info.provider, str)
|
|
1376
|
+
else model_info.provider
|
|
1377
|
+
)
|
|
1378
|
+
new_enum_category.append(provider.display_name())
|
|
1379
|
+
|
|
1380
|
+
# Add skill support information
|
|
1381
|
+
new_enum_support_skill.append(model_info.supports_skill_calls)
|
|
1382
|
+
|
|
1383
|
+
# Update the schema with the new lists constructed from LLMModelInfo
|
|
1384
|
+
model_property["enum"] = new_enum
|
|
1385
|
+
model_property["x-enum-title"] = new_enum_title
|
|
1386
|
+
model_property["x-enum-category"] = new_enum_category
|
|
1387
|
+
model_property["x-support-skill"] = new_enum_support_skill
|
|
1388
|
+
|
|
1389
|
+
# If the default model is not in the new enum, update it if possible
|
|
1390
|
+
if (
|
|
1391
|
+
"default" in model_property
|
|
1392
|
+
and model_property["default"] not in new_enum
|
|
1393
|
+
and new_enum
|
|
1394
|
+
):
|
|
1395
|
+
model_property["default"] = new_enum[0]
|
|
1396
|
+
|
|
1397
|
+
# Process skills property
|
|
1398
|
+
skills_property = schema.get("properties", {}).get("skills", {})
|
|
1399
|
+
skills_properties = skills_property.get("properties", {})
|
|
1400
|
+
|
|
1401
|
+
if skills_properties:
|
|
1402
|
+
# Load all skills from the database
|
|
1403
|
+
# Query all skills grouped by category with enabled status
|
|
1404
|
+
stmt = select(
|
|
1405
|
+
SkillTable.category,
|
|
1406
|
+
func.bool_or(SkillTable.enabled).label("any_enabled"),
|
|
1407
|
+
).group_by(SkillTable.category)
|
|
1408
|
+
result = await db.execute(stmt)
|
|
1409
|
+
category_status = {row.category: row.any_enabled for row in result}
|
|
1410
|
+
|
|
1411
|
+
# Query all skills with their price levels for adding x-price-level fields
|
|
1412
|
+
skills_stmt = select(
|
|
1413
|
+
SkillTable.category,
|
|
1414
|
+
SkillTable.config_name,
|
|
1415
|
+
SkillTable.price_level,
|
|
1416
|
+
SkillTable.enabled,
|
|
1417
|
+
).where(SkillTable.enabled)
|
|
1418
|
+
skills_result = await db.execute(skills_stmt)
|
|
1419
|
+
skills_data = {}
|
|
1420
|
+
category_price_levels = {}
|
|
1421
|
+
|
|
1422
|
+
for row in skills_result:
|
|
1423
|
+
if row.category not in skills_data:
|
|
1424
|
+
skills_data[row.category] = {}
|
|
1425
|
+
category_price_levels[row.category] = []
|
|
1426
|
+
|
|
1427
|
+
if row.config_name:
|
|
1428
|
+
skills_data[row.category][row.config_name] = row.price_level
|
|
1429
|
+
|
|
1430
|
+
if row.price_level is not None:
|
|
1431
|
+
category_price_levels[row.category].append(row.price_level)
|
|
1432
|
+
|
|
1433
|
+
# Calculate average price levels for categories
|
|
1434
|
+
category_avg_price_levels = {}
|
|
1435
|
+
for category, price_levels in category_price_levels.items():
|
|
1436
|
+
if price_levels:
|
|
1437
|
+
avg_price_level = int(sum(price_levels) / len(price_levels))
|
|
1438
|
+
category_avg_price_levels[category] = avg_price_level
|
|
1439
|
+
|
|
1440
|
+
# Create a copy of keys to avoid modifying during iteration
|
|
1441
|
+
skill_keys = list(skills_properties.keys())
|
|
1442
|
+
|
|
1443
|
+
# Process each skill in the schema
|
|
1444
|
+
for skill_category in skill_keys:
|
|
1445
|
+
if skill_category not in category_status:
|
|
1446
|
+
# If category not found in database, remove it from schema
|
|
1447
|
+
skills_properties.pop(skill_category, None)
|
|
1448
|
+
elif not category_status[skill_category]:
|
|
1449
|
+
# If category exists but all skills are disabled, remove it
|
|
1450
|
+
skills_properties.pop(skill_category, None)
|
|
1451
|
+
elif filter_owner_api_skills and cls._is_agent_owner_only_skill(
|
|
1452
|
+
skills_properties[skill_category]
|
|
1453
|
+
):
|
|
1454
|
+
# If filtering owner API skills and this skill requires it, remove it
|
|
1455
|
+
skills_properties.pop(skill_category, None)
|
|
1456
|
+
logger.info(
|
|
1457
|
+
f"Filtered out skill '{skill_category}' from auto-generation: requires agent owner API key"
|
|
1458
|
+
)
|
|
1459
|
+
else:
|
|
1460
|
+
# Add x-avg-price-level to category level
|
|
1461
|
+
if skill_category in category_avg_price_levels:
|
|
1462
|
+
skills_properties[skill_category][
|
|
1463
|
+
"x-avg-price-level"
|
|
1464
|
+
] = category_avg_price_levels[skill_category]
|
|
1465
|
+
|
|
1466
|
+
# Add x-price-level to individual skill states
|
|
1467
|
+
if skill_category in skills_data:
|
|
1468
|
+
skill_states = (
|
|
1469
|
+
skills_properties[skill_category]
|
|
1470
|
+
.get("properties", {})
|
|
1471
|
+
.get("states", {})
|
|
1472
|
+
.get("properties", {})
|
|
1473
|
+
)
|
|
1474
|
+
for state_name, state_config in skill_states.items():
|
|
1475
|
+
if (
|
|
1476
|
+
state_name in skills_data[skill_category]
|
|
1477
|
+
and skills_data[skill_category][state_name]
|
|
1478
|
+
is not None
|
|
1479
|
+
):
|
|
1480
|
+
state_config["x-price-level"] = skills_data[
|
|
1481
|
+
skill_category
|
|
1482
|
+
][state_name]
|
|
1483
|
+
|
|
1484
|
+
# Log the changes for debugging
|
|
1485
|
+
logger.debug(
|
|
1486
|
+
f"Schema processed with LLM and skill controls enabled: {admin_llm_skill_control}, "
|
|
1487
|
+
f"filtered owner API skills: {filter_owner_api_skills}"
|
|
1488
|
+
)
|
|
1489
|
+
|
|
1490
|
+
return schema
|
|
1491
|
+
|
|
1288
1492
|
|
|
1289
1493
|
class AgentResponse(BaseModel):
|
|
1290
1494
|
"""Response model for Agent API."""
|
|
@@ -491,6 +491,10 @@
|
|
|
491
491
|
"title": "X",
|
|
492
492
|
"$ref": "../skills/twitter/schema.json"
|
|
493
493
|
},
|
|
494
|
+
"xmtp": {
|
|
495
|
+
"title": "XMTP",
|
|
496
|
+
"$ref": "../skills/xmtp/schema.json"
|
|
497
|
+
},
|
|
494
498
|
"chainlist": {
|
|
495
499
|
"title": "Chainlist RPC Endpoints",
|
|
496
500
|
"$ref": "../skills/chainlist/schema.json"
|
intentkit/models/chat.py
CHANGED
|
@@ -31,6 +31,7 @@ class ChatMessageAttachmentType(str, Enum):
|
|
|
31
31
|
LINK = "link"
|
|
32
32
|
IMAGE = "image"
|
|
33
33
|
FILE = "file"
|
|
34
|
+
XMTP = "xmtp"
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class AuthorType(str, Enum):
|
|
@@ -61,13 +62,20 @@ class ChatMessageAttachment(TypedDict):
|
|
|
61
62
|
),
|
|
62
63
|
]
|
|
63
64
|
url: Annotated[
|
|
64
|
-
str,
|
|
65
|
+
Optional[str],
|
|
65
66
|
Field(
|
|
66
67
|
...,
|
|
67
68
|
description="URL of the attachment",
|
|
68
69
|
examples=["https://example.com/image.jpg"],
|
|
69
70
|
),
|
|
70
71
|
]
|
|
72
|
+
json: Annotated[
|
|
73
|
+
Optional[dict],
|
|
74
|
+
Field(
|
|
75
|
+
None,
|
|
76
|
+
description="JSON data of the attachment",
|
|
77
|
+
),
|
|
78
|
+
]
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
class ChatMessageSkillCall(TypedDict):
|
intentkit/models/llm.py
CHANGED
|
@@ -266,6 +266,59 @@ AVAILABLE_MODELS = {
|
|
|
266
266
|
supports_frequency_penalty=False,
|
|
267
267
|
supports_presence_penalty=False,
|
|
268
268
|
),
|
|
269
|
+
"gpt-5-nano": LLMModelInfo(
|
|
270
|
+
id="gpt-5-nano",
|
|
271
|
+
name="GPT-5 Nano",
|
|
272
|
+
provider=LLMProvider.OPENAI,
|
|
273
|
+
input_price=Decimal("0.05"), # per 1M input tokens
|
|
274
|
+
output_price=Decimal("0.4"), # per 1M output tokens
|
|
275
|
+
context_length=400000,
|
|
276
|
+
output_length=128000,
|
|
277
|
+
intelligence=3,
|
|
278
|
+
speed=5,
|
|
279
|
+
supports_image_input=True,
|
|
280
|
+
supports_skill_calls=True,
|
|
281
|
+
supports_structured_output=True,
|
|
282
|
+
supports_temperature=False,
|
|
283
|
+
supports_frequency_penalty=False,
|
|
284
|
+
supports_presence_penalty=False,
|
|
285
|
+
),
|
|
286
|
+
"gpt-5-mini": LLMModelInfo(
|
|
287
|
+
id="gpt-5-mini",
|
|
288
|
+
name="GPT-5 Mini",
|
|
289
|
+
provider=LLMProvider.OPENAI,
|
|
290
|
+
input_price=Decimal("0.25"), # per 1M input tokens
|
|
291
|
+
output_price=Decimal("2"), # per 1M output tokens
|
|
292
|
+
context_length=400000,
|
|
293
|
+
output_length=128000,
|
|
294
|
+
intelligence=4,
|
|
295
|
+
speed=4,
|
|
296
|
+
supports_image_input=True,
|
|
297
|
+
supports_skill_calls=True,
|
|
298
|
+
supports_structured_output=True,
|
|
299
|
+
supports_search=True,
|
|
300
|
+
supports_temperature=False,
|
|
301
|
+
supports_frequency_penalty=False,
|
|
302
|
+
supports_presence_penalty=False,
|
|
303
|
+
),
|
|
304
|
+
"gpt-5": LLMModelInfo(
|
|
305
|
+
id="gpt-5",
|
|
306
|
+
name="GPT-5",
|
|
307
|
+
provider=LLMProvider.OPENAI,
|
|
308
|
+
input_price=Decimal("1.25"), # per 1M input tokens
|
|
309
|
+
output_price=Decimal("10.00"), # per 1M output tokens
|
|
310
|
+
context_length=400000,
|
|
311
|
+
output_length=128000,
|
|
312
|
+
intelligence=5,
|
|
313
|
+
speed=3,
|
|
314
|
+
supports_image_input=True,
|
|
315
|
+
supports_skill_calls=True,
|
|
316
|
+
supports_structured_output=True,
|
|
317
|
+
supports_search=True,
|
|
318
|
+
supports_temperature=False,
|
|
319
|
+
supports_frequency_penalty=False,
|
|
320
|
+
supports_presence_penalty=False,
|
|
321
|
+
),
|
|
269
322
|
"gpt-4.1-nano": LLMModelInfo(
|
|
270
323
|
id="gpt-4.1-nano",
|
|
271
324
|
name="GPT-4.1 Nano",
|