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/models/db.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
from typing import Annotated, AsyncGenerator, Optional
|
|
3
|
+
from urllib.parse import quote_plus
|
|
4
|
+
|
|
5
|
+
from intentkit.models.db_mig import safe_migrate
|
|
6
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
7
|
+
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
|
8
|
+
from langgraph.types import Checkpointer
|
|
9
|
+
from psycopg_pool import AsyncConnectionPool
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
|
|
12
|
+
|
|
13
|
+
engine = None
|
|
14
|
+
_langgraph_checkpointer: Optional[Checkpointer] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def init_db(
|
|
18
|
+
host: Optional[str],
|
|
19
|
+
username: Optional[str],
|
|
20
|
+
password: Optional[str],
|
|
21
|
+
dbname: Optional[str],
|
|
22
|
+
port: Annotated[Optional[str], Field(default="5432", description="Database port")],
|
|
23
|
+
auto_migrate: Annotated[
|
|
24
|
+
bool, Field(default=True, description="Whether to run migrations automatically")
|
|
25
|
+
],
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the database and handle schema updates.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
host: Database host
|
|
31
|
+
username: Database username
|
|
32
|
+
password: Database password
|
|
33
|
+
dbname: Database name
|
|
34
|
+
port: Database port (default: 5432)
|
|
35
|
+
auto_migrate: Whether to run migrations automatically (default: True)
|
|
36
|
+
"""
|
|
37
|
+
global engine, _langgraph_checkpointer
|
|
38
|
+
# Initialize psycopg pool and AsyncPostgresSaver if not already initialized
|
|
39
|
+
if _langgraph_checkpointer is None:
|
|
40
|
+
if host:
|
|
41
|
+
pool = AsyncConnectionPool(
|
|
42
|
+
conninfo=f"postgresql://{username}:{quote_plus(password)}@{host}:{port}/{dbname}",
|
|
43
|
+
min_size=3,
|
|
44
|
+
max_size=20,
|
|
45
|
+
timeout=60,
|
|
46
|
+
max_idle=30 * 60,
|
|
47
|
+
)
|
|
48
|
+
_langgraph_checkpointer = AsyncPostgresSaver(pool)
|
|
49
|
+
if auto_migrate:
|
|
50
|
+
await _langgraph_checkpointer.setup()
|
|
51
|
+
else:
|
|
52
|
+
_langgraph_checkpointer = InMemorySaver()
|
|
53
|
+
# Initialize SQLAlchemy engine with pool settings
|
|
54
|
+
if engine is None:
|
|
55
|
+
if host:
|
|
56
|
+
engine = create_async_engine(
|
|
57
|
+
f"postgresql+asyncpg://{username}:{quote_plus(password)}@{host}:{port}/{dbname}",
|
|
58
|
+
pool_size=20, # Increase pool size
|
|
59
|
+
max_overflow=30, # Increase max overflow
|
|
60
|
+
pool_timeout=60, # Increase timeout
|
|
61
|
+
pool_pre_ping=True, # Enable connection health checks
|
|
62
|
+
pool_recycle=3600, # Recycle connections after 1 hour
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
engine = create_async_engine(
|
|
66
|
+
"sqlite+aiosqlite:///:memory:",
|
|
67
|
+
connect_args={"check_same_thread": False},
|
|
68
|
+
)
|
|
69
|
+
if auto_migrate:
|
|
70
|
+
await safe_migrate(engine)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
74
|
+
async with AsyncSession(engine) as session:
|
|
75
|
+
yield session
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@asynccontextmanager
|
|
79
|
+
async def get_session() -> AsyncSession:
|
|
80
|
+
"""Get a database session using an async context manager.
|
|
81
|
+
|
|
82
|
+
This function is designed to be used with the 'async with' statement,
|
|
83
|
+
ensuring proper session cleanup.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
AsyncSession: A SQLAlchemy async session that will be automatically closed
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```python
|
|
90
|
+
async with get_session() as session:
|
|
91
|
+
# use session here
|
|
92
|
+
session.query(...)
|
|
93
|
+
# session is automatically closed
|
|
94
|
+
```
|
|
95
|
+
"""
|
|
96
|
+
session = AsyncSession(engine)
|
|
97
|
+
try:
|
|
98
|
+
yield session
|
|
99
|
+
finally:
|
|
100
|
+
await session.close()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_engine() -> AsyncEngine:
|
|
104
|
+
"""Get the SQLAlchemy async engine.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
AsyncEngine: The SQLAlchemy async engine
|
|
108
|
+
"""
|
|
109
|
+
return engine
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_langgraph_checkpointer() -> Checkpointer:
|
|
113
|
+
"""Get the AsyncPostgresSaver instance for langgraph.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
AsyncPostgresSaver: The AsyncPostgresSaver instance
|
|
117
|
+
"""
|
|
118
|
+
if _langgraph_checkpointer is None:
|
|
119
|
+
raise RuntimeError("Database pool not initialized. Call init_db first.")
|
|
120
|
+
return _langgraph_checkpointer
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Database migration utilities."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from intentkit.models.base import Base
|
|
7
|
+
from sqlalchemy import Column, MetaData, inspect, text
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def add_column_if_not_exists(
|
|
13
|
+
conn, dialect, table_name: str, column: Column
|
|
14
|
+
) -> None:
|
|
15
|
+
"""Add a column to a table if it doesn't exist.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
conn: SQLAlchemy conn
|
|
19
|
+
table_name: Name of the table
|
|
20
|
+
column: Column to add
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Use run_sync to perform inspection on the connection
|
|
24
|
+
def _get_columns(connection):
|
|
25
|
+
inspector = inspect(connection)
|
|
26
|
+
return [c["name"] for c in inspector.get_columns(table_name)]
|
|
27
|
+
|
|
28
|
+
columns = await conn.run_sync(_get_columns)
|
|
29
|
+
|
|
30
|
+
if column.name not in columns:
|
|
31
|
+
# Build column definition
|
|
32
|
+
column_def = f"{column.name} {column.type.compile(dialect)}"
|
|
33
|
+
|
|
34
|
+
# Add DEFAULT if specified
|
|
35
|
+
if column.default is not None:
|
|
36
|
+
if hasattr(column.default, "arg"):
|
|
37
|
+
default_value = column.default.arg
|
|
38
|
+
if not isinstance(default_value, Callable):
|
|
39
|
+
if isinstance(default_value, bool):
|
|
40
|
+
default_value = str(default_value).lower()
|
|
41
|
+
elif isinstance(default_value, str):
|
|
42
|
+
default_value = f"'{default_value}'"
|
|
43
|
+
elif isinstance(default_value, (list, dict)):
|
|
44
|
+
default_value = "'{}'"
|
|
45
|
+
column_def += f" DEFAULT {default_value}"
|
|
46
|
+
|
|
47
|
+
# Execute ALTER TABLE
|
|
48
|
+
await conn.execute(text(f"ALTER TABLE {table_name} ADD COLUMN {column_def}"))
|
|
49
|
+
logger.info(f"Added column {column.name} to table {table_name}")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def update_table_schema(conn, dialect, model_cls) -> None:
|
|
53
|
+
"""Update table schema by adding missing columns from the model.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
conn: SQLAlchemy conn
|
|
57
|
+
dialect: SQLAlchemy dialect
|
|
58
|
+
model_cls: SQLAlchemy model class to check for new columns
|
|
59
|
+
"""
|
|
60
|
+
if not hasattr(model_cls, "__table__"):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
table_name = model_cls.__tablename__
|
|
64
|
+
for name, column in model_cls.__table__.columns.items():
|
|
65
|
+
if name != "id": # Skip primary key
|
|
66
|
+
await add_column_if_not_exists(conn, dialect, table_name, column)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def safe_migrate(engine) -> None:
|
|
70
|
+
"""Safely migrate all SQLAlchemy models by adding new columns.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
engine: SQLAlchemy engine
|
|
74
|
+
"""
|
|
75
|
+
logger.info("Starting database schema migration")
|
|
76
|
+
dialect = engine.dialect
|
|
77
|
+
|
|
78
|
+
async with engine.begin() as conn:
|
|
79
|
+
try:
|
|
80
|
+
# Create tables if they don't exist
|
|
81
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
82
|
+
|
|
83
|
+
# Get existing table metadata
|
|
84
|
+
metadata = MetaData()
|
|
85
|
+
await conn.run_sync(metadata.reflect)
|
|
86
|
+
|
|
87
|
+
# Update schema for all model classes
|
|
88
|
+
for mapper in Base.registry.mappers:
|
|
89
|
+
model_cls = mapper.class_
|
|
90
|
+
if hasattr(model_cls, "__tablename__"):
|
|
91
|
+
table_name = model_cls.__tablename__
|
|
92
|
+
if table_name in metadata.tables:
|
|
93
|
+
# We need a sync wrapper for the async update_table_schema
|
|
94
|
+
async def update_table_wrapper():
|
|
95
|
+
await update_table_schema(conn, dialect, model_cls)
|
|
96
|
+
|
|
97
|
+
await update_table_wrapper()
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.error(f"Error updating database schema: {str(e)}")
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
logger.info("Database schema updated successfully")
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""Agent Generation Log Model.
|
|
2
|
+
|
|
3
|
+
This module defines the database models for logging agent generation operations,
|
|
4
|
+
including token usage, prompts, AI responses, and generation metadata.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from typing import Annotated, Optional
|
|
9
|
+
|
|
10
|
+
from epyxid import XID
|
|
11
|
+
from intentkit.models.base import Base
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
from sqlalchemy import (
|
|
14
|
+
Boolean,
|
|
15
|
+
Column,
|
|
16
|
+
DateTime,
|
|
17
|
+
Integer,
|
|
18
|
+
String,
|
|
19
|
+
Text,
|
|
20
|
+
func,
|
|
21
|
+
select,
|
|
22
|
+
)
|
|
23
|
+
from sqlalchemy.dialects.postgresql import JSON, JSONB
|
|
24
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentGenerationLogTable(Base):
|
|
28
|
+
"""Agent generation log database table model."""
|
|
29
|
+
|
|
30
|
+
__tablename__ = "agent_generation_logs"
|
|
31
|
+
|
|
32
|
+
id = Column(
|
|
33
|
+
String,
|
|
34
|
+
primary_key=True,
|
|
35
|
+
)
|
|
36
|
+
user_id = Column(
|
|
37
|
+
String,
|
|
38
|
+
nullable=True,
|
|
39
|
+
)
|
|
40
|
+
prompt = Column(
|
|
41
|
+
Text,
|
|
42
|
+
nullable=False,
|
|
43
|
+
)
|
|
44
|
+
existing_agent_id = Column(
|
|
45
|
+
String,
|
|
46
|
+
nullable=True,
|
|
47
|
+
)
|
|
48
|
+
is_update = Column(
|
|
49
|
+
Boolean,
|
|
50
|
+
default=False,
|
|
51
|
+
nullable=False,
|
|
52
|
+
)
|
|
53
|
+
generated_agent_schema = Column(
|
|
54
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
55
|
+
nullable=True,
|
|
56
|
+
)
|
|
57
|
+
identified_skills = Column(
|
|
58
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
59
|
+
nullable=True,
|
|
60
|
+
)
|
|
61
|
+
# LLM API response data
|
|
62
|
+
llm_model = Column(
|
|
63
|
+
String,
|
|
64
|
+
nullable=True,
|
|
65
|
+
)
|
|
66
|
+
total_tokens = Column(
|
|
67
|
+
Integer,
|
|
68
|
+
default=0,
|
|
69
|
+
)
|
|
70
|
+
input_tokens = Column(
|
|
71
|
+
Integer,
|
|
72
|
+
default=0,
|
|
73
|
+
)
|
|
74
|
+
cached_input_tokens = Column(
|
|
75
|
+
Integer,
|
|
76
|
+
default=0,
|
|
77
|
+
)
|
|
78
|
+
output_tokens = Column(
|
|
79
|
+
Integer,
|
|
80
|
+
default=0,
|
|
81
|
+
)
|
|
82
|
+
input_tokens_details = Column(
|
|
83
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
84
|
+
nullable=True,
|
|
85
|
+
)
|
|
86
|
+
completion_tokens_details = Column(
|
|
87
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
88
|
+
nullable=True,
|
|
89
|
+
)
|
|
90
|
+
# Performance metrics
|
|
91
|
+
generation_time_ms = Column(
|
|
92
|
+
Integer,
|
|
93
|
+
nullable=True,
|
|
94
|
+
)
|
|
95
|
+
retry_count = Column(
|
|
96
|
+
Integer,
|
|
97
|
+
default=0,
|
|
98
|
+
)
|
|
99
|
+
validation_errors = Column(
|
|
100
|
+
JSON().with_variant(JSONB(), "postgresql"),
|
|
101
|
+
nullable=True,
|
|
102
|
+
)
|
|
103
|
+
# Status and results
|
|
104
|
+
success = Column(
|
|
105
|
+
Boolean,
|
|
106
|
+
default=False,
|
|
107
|
+
nullable=False,
|
|
108
|
+
)
|
|
109
|
+
error_message = Column(
|
|
110
|
+
Text,
|
|
111
|
+
nullable=True,
|
|
112
|
+
)
|
|
113
|
+
# Timestamps
|
|
114
|
+
created_at = Column(
|
|
115
|
+
DateTime(timezone=True),
|
|
116
|
+
nullable=False,
|
|
117
|
+
server_default=func.now(),
|
|
118
|
+
)
|
|
119
|
+
completed_at = Column(
|
|
120
|
+
DateTime(timezone=True),
|
|
121
|
+
nullable=True,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class AgentGenerationLogCreate(BaseModel):
|
|
126
|
+
"""Model for creating agent generation log entries."""
|
|
127
|
+
|
|
128
|
+
model_config = ConfigDict(
|
|
129
|
+
use_enum_values=True,
|
|
130
|
+
from_attributes=True,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
id: Annotated[
|
|
134
|
+
str,
|
|
135
|
+
Field(
|
|
136
|
+
default_factory=lambda: str(XID()),
|
|
137
|
+
description="Unique identifier for the generation log",
|
|
138
|
+
),
|
|
139
|
+
]
|
|
140
|
+
user_id: Optional[str] = Field(
|
|
141
|
+
None,
|
|
142
|
+
description="User ID who initiated the generation",
|
|
143
|
+
)
|
|
144
|
+
prompt: str = Field(
|
|
145
|
+
...,
|
|
146
|
+
description="The original prompt used for generation",
|
|
147
|
+
)
|
|
148
|
+
existing_agent_id: Optional[str] = Field(
|
|
149
|
+
None,
|
|
150
|
+
description="ID of existing agent if this is an update operation",
|
|
151
|
+
)
|
|
152
|
+
is_update: bool = Field(
|
|
153
|
+
False,
|
|
154
|
+
description="Whether this is an update to existing agent",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AgentGenerationLog(BaseModel):
|
|
159
|
+
"""Agent generation log model."""
|
|
160
|
+
|
|
161
|
+
model_config = ConfigDict(
|
|
162
|
+
use_enum_values=True,
|
|
163
|
+
from_attributes=True,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
id: str
|
|
167
|
+
user_id: Optional[str] = None
|
|
168
|
+
prompt: str
|
|
169
|
+
existing_agent_id: Optional[str] = None
|
|
170
|
+
is_update: bool = False
|
|
171
|
+
generated_agent_schema: Optional[dict] = None
|
|
172
|
+
identified_skills: Optional[dict] = None
|
|
173
|
+
llm_model: Optional[str] = None
|
|
174
|
+
total_tokens: int = 0
|
|
175
|
+
input_tokens: int = 0
|
|
176
|
+
cached_input_tokens: int = 0
|
|
177
|
+
output_tokens: int = 0
|
|
178
|
+
input_tokens_details: Optional[dict] = None
|
|
179
|
+
completion_tokens_details: Optional[dict] = None
|
|
180
|
+
generation_time_ms: Optional[int] = None
|
|
181
|
+
retry_count: int = 0
|
|
182
|
+
validation_errors: Optional[dict] = None
|
|
183
|
+
success: bool = False
|
|
184
|
+
error_message: Optional[str] = None
|
|
185
|
+
created_at: datetime
|
|
186
|
+
completed_at: Optional[datetime] = None
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
async def create(
|
|
190
|
+
cls,
|
|
191
|
+
session: AsyncSession,
|
|
192
|
+
log_data: AgentGenerationLogCreate,
|
|
193
|
+
) -> "AgentGenerationLog":
|
|
194
|
+
"""Create a new agent generation log entry.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
session: Database session
|
|
198
|
+
log_data: Log data to create
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Created log instance
|
|
202
|
+
"""
|
|
203
|
+
# Create database record
|
|
204
|
+
log_record = AgentGenerationLogTable(
|
|
205
|
+
id=log_data.id,
|
|
206
|
+
user_id=log_data.user_id,
|
|
207
|
+
prompt=log_data.prompt,
|
|
208
|
+
existing_agent_id=log_data.existing_agent_id,
|
|
209
|
+
is_update=log_data.is_update,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
session.add(log_record)
|
|
213
|
+
await session.commit()
|
|
214
|
+
await session.refresh(log_record)
|
|
215
|
+
|
|
216
|
+
return cls.model_validate(log_record)
|
|
217
|
+
|
|
218
|
+
async def update_completion(
|
|
219
|
+
self,
|
|
220
|
+
session: AsyncSession,
|
|
221
|
+
generated_agent_schema: Optional[dict] = None,
|
|
222
|
+
identified_skills: Optional[dict] = None,
|
|
223
|
+
llm_model: Optional[str] = None,
|
|
224
|
+
total_tokens: int = 0,
|
|
225
|
+
input_tokens: int = 0,
|
|
226
|
+
cached_input_tokens: int = 0,
|
|
227
|
+
output_tokens: int = 0,
|
|
228
|
+
input_tokens_details: Optional[dict] = None,
|
|
229
|
+
completion_tokens_details: Optional[dict] = None,
|
|
230
|
+
generation_time_ms: Optional[int] = None,
|
|
231
|
+
retry_count: int = 0,
|
|
232
|
+
validation_errors: Optional[dict] = None,
|
|
233
|
+
success: bool = False,
|
|
234
|
+
error_message: Optional[str] = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Update the log entry with completion data.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
session: Database session
|
|
240
|
+
generated_agent_schema: The generated agent schema
|
|
241
|
+
identified_skills: Skills identified during generation
|
|
242
|
+
llm_model: LLM model used
|
|
243
|
+
total_tokens: Total tokens used
|
|
244
|
+
input_tokens: Input tokens used
|
|
245
|
+
cached_input_tokens: Cached input tokens used (for cost calculation)
|
|
246
|
+
output_tokens: Output tokens used
|
|
247
|
+
input_tokens_details: Detailed input token breakdown
|
|
248
|
+
completion_tokens_details: Detailed completion token breakdown
|
|
249
|
+
generation_time_ms: Generation time in milliseconds
|
|
250
|
+
retry_count: Number of retries attempted
|
|
251
|
+
validation_errors: Any validation errors encountered
|
|
252
|
+
success: Whether generation was successful
|
|
253
|
+
error_message: Error message if generation failed
|
|
254
|
+
"""
|
|
255
|
+
# Get the database record
|
|
256
|
+
log_record = await session.get(AgentGenerationLogTable, self.id)
|
|
257
|
+
if not log_record:
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
# Update fields
|
|
261
|
+
log_record.generated_agent_schema = generated_agent_schema
|
|
262
|
+
log_record.identified_skills = identified_skills
|
|
263
|
+
log_record.llm_model = llm_model
|
|
264
|
+
log_record.total_tokens = total_tokens
|
|
265
|
+
log_record.input_tokens = input_tokens
|
|
266
|
+
log_record.cached_input_tokens = cached_input_tokens
|
|
267
|
+
log_record.output_tokens = output_tokens
|
|
268
|
+
log_record.input_tokens_details = input_tokens_details
|
|
269
|
+
log_record.completion_tokens_details = completion_tokens_details
|
|
270
|
+
log_record.generation_time_ms = generation_time_ms
|
|
271
|
+
log_record.retry_count = retry_count
|
|
272
|
+
log_record.validation_errors = validation_errors
|
|
273
|
+
log_record.success = success
|
|
274
|
+
log_record.error_message = error_message
|
|
275
|
+
log_record.completed_at = datetime.now(timezone.utc)
|
|
276
|
+
|
|
277
|
+
session.add(log_record)
|
|
278
|
+
await session.commit()
|
|
279
|
+
await session.refresh(log_record)
|
|
280
|
+
|
|
281
|
+
# Update this instance
|
|
282
|
+
self.generated_agent_schema = log_record.generated_agent_schema
|
|
283
|
+
self.identified_skills = log_record.identified_skills
|
|
284
|
+
self.llm_model = log_record.llm_model
|
|
285
|
+
self.total_tokens = log_record.total_tokens
|
|
286
|
+
self.input_tokens = log_record.input_tokens
|
|
287
|
+
self.cached_input_tokens = log_record.cached_input_tokens
|
|
288
|
+
self.output_tokens = log_record.output_tokens
|
|
289
|
+
self.input_tokens_details = log_record.input_tokens_details
|
|
290
|
+
self.completion_tokens_details = log_record.completion_tokens_details
|
|
291
|
+
self.generation_time_ms = log_record.generation_time_ms
|
|
292
|
+
self.retry_count = log_record.retry_count
|
|
293
|
+
self.validation_errors = log_record.validation_errors
|
|
294
|
+
self.success = log_record.success
|
|
295
|
+
self.error_message = log_record.error_message
|
|
296
|
+
self.completed_at = log_record.completed_at
|
|
297
|
+
|
|
298
|
+
@classmethod
|
|
299
|
+
async def get_by_id(
|
|
300
|
+
cls,
|
|
301
|
+
session: AsyncSession,
|
|
302
|
+
log_id: str,
|
|
303
|
+
) -> Optional["AgentGenerationLog"]:
|
|
304
|
+
"""Get an agent generation log by ID.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
session: Database session
|
|
308
|
+
log_id: Log ID
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Log instance if found, None otherwise
|
|
312
|
+
"""
|
|
313
|
+
result = await session.execute(
|
|
314
|
+
select(AgentGenerationLogTable).where(AgentGenerationLogTable.id == log_id)
|
|
315
|
+
)
|
|
316
|
+
log_record = result.scalar_one_or_none()
|
|
317
|
+
|
|
318
|
+
if log_record:
|
|
319
|
+
return cls.model_validate(log_record)
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
@classmethod
|
|
323
|
+
async def get_by_user(
|
|
324
|
+
cls,
|
|
325
|
+
session: AsyncSession,
|
|
326
|
+
user_id: str,
|
|
327
|
+
limit: int = 50,
|
|
328
|
+
) -> list["AgentGenerationLog"]:
|
|
329
|
+
"""Get agent generation logs for a user.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
session: Database session
|
|
333
|
+
user_id: User ID
|
|
334
|
+
limit: Maximum number of logs to return
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List of log instances
|
|
338
|
+
"""
|
|
339
|
+
result = await session.execute(
|
|
340
|
+
select(AgentGenerationLogTable)
|
|
341
|
+
.where(AgentGenerationLogTable.user_id == user_id)
|
|
342
|
+
.order_by(AgentGenerationLogTable.created_at.desc())
|
|
343
|
+
.limit(limit)
|
|
344
|
+
)
|
|
345
|
+
log_records = result.scalars().all()
|
|
346
|
+
|
|
347
|
+
return [cls.model_validate(record) for record in log_records]
|