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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, Optional, Type
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
from langchain_core.runnables import RunnableConfig
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
11
|
+
from intentkit.skills.venice_audio.base import VeniceAudioBaseTool
|
|
12
|
+
from intentkit.skills.venice_audio.input import AllowedAudioFormat, VeniceAudioInput
|
|
13
|
+
from intentkit.utils.s3 import FileType, store_file_bytes
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
base_url = "https://api.venice.ai"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class VeniceAudioTool(VeniceAudioBaseTool):
|
|
21
|
+
"""
|
|
22
|
+
Tool for generating audio using the Venice AI Text-to-Speech API (/audio/speech).
|
|
23
|
+
It requires a specific 'voice_model' to be configured for the instance.
|
|
24
|
+
Handles API calls, rate limiting, storage, and returns results or API errors as dictionaries.
|
|
25
|
+
|
|
26
|
+
On successful audio generation, returns a dictionary with audio details.
|
|
27
|
+
On Venice API error (non-200 status), returns a dictionary containing
|
|
28
|
+
the error details from the API response instead of raising an exception.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
name: str = "venice_audio_text_to_speech"
|
|
32
|
+
description: str = (
|
|
33
|
+
"Converts text to speech using a configured Venice AI voice model. "
|
|
34
|
+
"Requires input text. Optional parameters include speed (0.25-4.0, default 1.0) "
|
|
35
|
+
"and audio format (mp3, opus, aac, flac, wav, pcm, default mp3)."
|
|
36
|
+
)
|
|
37
|
+
args_schema: Type[BaseModel] = VeniceAudioInput
|
|
38
|
+
skill_store: SkillStoreABC = Field(
|
|
39
|
+
description="The skill store instance for accessing system/agent configurations and persisting data."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
async def _arun(
|
|
43
|
+
self,
|
|
44
|
+
input: str,
|
|
45
|
+
voice_model: str,
|
|
46
|
+
config: RunnableConfig,
|
|
47
|
+
speed: Optional[float] = 1.0,
|
|
48
|
+
response_format: Optional[AllowedAudioFormat] = "mp3",
|
|
49
|
+
**kwargs, # type: ignore
|
|
50
|
+
) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Generates audio using the configured voice model via Venice AI TTS /audio/speech endpoint.
|
|
53
|
+
Stores the resulting audio using store_file_bytes.
|
|
54
|
+
Returns a dictionary containing audio details on success, or API error details on failure.
|
|
55
|
+
"""
|
|
56
|
+
context = self.context_from_config(config)
|
|
57
|
+
final_response_format = response_format if response_format else "mp3"
|
|
58
|
+
tts_model_id = "tts-kokoro" # API model used
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
# --- Setup Checks ---
|
|
62
|
+
api_key = self.get_api_key(context)
|
|
63
|
+
|
|
64
|
+
_, error_info = self.validate_voice_model(context, voice_model)
|
|
65
|
+
if error_info:
|
|
66
|
+
return error_info
|
|
67
|
+
|
|
68
|
+
if not api_key:
|
|
69
|
+
message = (
|
|
70
|
+
f"Venice AI API key configuration missing for skill '{self.name}'."
|
|
71
|
+
)
|
|
72
|
+
details = f"API key not found for category '{self.category}'. Please configure it."
|
|
73
|
+
logger.error(message)
|
|
74
|
+
return {
|
|
75
|
+
"error": True,
|
|
76
|
+
"error_type": "ConfigurationError",
|
|
77
|
+
"message": message,
|
|
78
|
+
"details": details,
|
|
79
|
+
"voice_model": voice_model,
|
|
80
|
+
"requested_format": final_response_format,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if not voice_model:
|
|
84
|
+
message = (
|
|
85
|
+
f"Instance of {self.name} was created without a 'voice_model'."
|
|
86
|
+
)
|
|
87
|
+
details = "Voice model must be specified for this tool instance."
|
|
88
|
+
logger.error(message)
|
|
89
|
+
return {
|
|
90
|
+
"error": True,
|
|
91
|
+
"error_type": "ConfigurationError",
|
|
92
|
+
"message": message,
|
|
93
|
+
"details": details,
|
|
94
|
+
"voice_model": voice_model,
|
|
95
|
+
"requested_format": final_response_format,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await self.apply_rate_limit(context)
|
|
99
|
+
|
|
100
|
+
# --- Prepare API Call ---
|
|
101
|
+
payload: Dict[str, Any] = {
|
|
102
|
+
"model": tts_model_id,
|
|
103
|
+
"input": input,
|
|
104
|
+
"voice": voice_model,
|
|
105
|
+
"response_format": final_response_format,
|
|
106
|
+
"speed": speed if speed is not None else 1.0,
|
|
107
|
+
"streaming": False,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
payload = {k: v for k, v in payload.items() if v is not None}
|
|
111
|
+
|
|
112
|
+
logger.debug(
|
|
113
|
+
f"Venice Audio API Call: Voice='{voice_model}', Format='{final_response_format}', Payload='{payload}'"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
headers = {
|
|
117
|
+
"Authorization": f"Bearer {api_key}",
|
|
118
|
+
"Content-Type": "application/json",
|
|
119
|
+
}
|
|
120
|
+
api_url = f"{base_url}/api/v1/audio/speech"
|
|
121
|
+
|
|
122
|
+
# --- Execute API Call ---
|
|
123
|
+
async with httpx.AsyncClient(timeout=120.0) as client:
|
|
124
|
+
response = await client.post(api_url, json=payload, headers=headers)
|
|
125
|
+
logger.debug(
|
|
126
|
+
f"Venice Audio API Response: Voice='{voice_model}', Format='{final_response_format}', Status={response.status_code}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
content_type_header = str(
|
|
130
|
+
response.headers.get("content-type", "")
|
|
131
|
+
).lower()
|
|
132
|
+
|
|
133
|
+
# --- Handle API Success or Error from Response Body ---
|
|
134
|
+
if response.status_code == 200 and content_type_header.startswith(
|
|
135
|
+
"audio/"
|
|
136
|
+
):
|
|
137
|
+
audio_bytes = response.content
|
|
138
|
+
if not audio_bytes:
|
|
139
|
+
message = (
|
|
140
|
+
"API returned success status but response body was empty."
|
|
141
|
+
)
|
|
142
|
+
logger.warning(
|
|
143
|
+
f"Venice Audio API (Voice: {voice_model}) returned 200 OK but empty audio content."
|
|
144
|
+
)
|
|
145
|
+
return {
|
|
146
|
+
"error": True,
|
|
147
|
+
"error_type": "NoContentError",
|
|
148
|
+
"message": message,
|
|
149
|
+
"status_code": response.status_code,
|
|
150
|
+
"voice_model": voice_model,
|
|
151
|
+
"requested_format": final_response_format,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# --- Store Audio ---
|
|
155
|
+
file_extension = final_response_format
|
|
156
|
+
audio_hash = hashlib.sha256(audio_bytes).hexdigest()
|
|
157
|
+
key = f"{self.category}/{voice_model}/{audio_hash}.{file_extension}"
|
|
158
|
+
|
|
159
|
+
size_limit = 1024 * 20 # 20Mb Size limit
|
|
160
|
+
stored_url = await store_file_bytes(
|
|
161
|
+
file_bytes=audio_bytes,
|
|
162
|
+
key=key,
|
|
163
|
+
file_type=FileType.AUDIO,
|
|
164
|
+
size_limit_bytes=size_limit,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if not stored_url:
|
|
168
|
+
message = "Failed to store audio: S3 storage is not configured."
|
|
169
|
+
logger.error(
|
|
170
|
+
f"Failed to store audio (Voice: {voice_model}): S3 storage is not configured."
|
|
171
|
+
)
|
|
172
|
+
return {
|
|
173
|
+
"error": True,
|
|
174
|
+
"error_type": "StorageConfigurationError",
|
|
175
|
+
"message": message,
|
|
176
|
+
"voice_model": voice_model,
|
|
177
|
+
"requested_format": final_response_format,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
logger.info(
|
|
181
|
+
f"Venice TTS success: Voice='{voice_model}', Format='{final_response_format}', Stored='{stored_url}'"
|
|
182
|
+
)
|
|
183
|
+
# --- Return Success Dictionary ---
|
|
184
|
+
return {
|
|
185
|
+
"audio_url": stored_url,
|
|
186
|
+
"audio_bytes_sha256": audio_hash,
|
|
187
|
+
"content_type": content_type_header,
|
|
188
|
+
"voice_model": voice_model,
|
|
189
|
+
"tts_engine": tts_model_id,
|
|
190
|
+
"speed": speed if speed is not None else 1.0,
|
|
191
|
+
"response_format": final_response_format,
|
|
192
|
+
"input_text_length": len(input),
|
|
193
|
+
"error": False,
|
|
194
|
+
"status_code": response.status_code,
|
|
195
|
+
}
|
|
196
|
+
else:
|
|
197
|
+
# Non-200 API response or non-audio content
|
|
198
|
+
error_details: Any = f"Raw error response text: {response.text}"
|
|
199
|
+
try:
|
|
200
|
+
parsed_details = response.json()
|
|
201
|
+
error_details = parsed_details
|
|
202
|
+
except json.JSONDecodeError:
|
|
203
|
+
pass # Keep raw text if JSON parsing fails
|
|
204
|
+
|
|
205
|
+
message = "Venice Audio API returned a non-success status or unexpected content type."
|
|
206
|
+
logger.error(
|
|
207
|
+
f"Venice Audio API Error: Voice='{voice_model}', Format='{final_response_format}', Status={response.status_code}, Details: {error_details}"
|
|
208
|
+
)
|
|
209
|
+
return {
|
|
210
|
+
"error": True,
|
|
211
|
+
"error_type": "APIError",
|
|
212
|
+
"message": message,
|
|
213
|
+
"status_code": response.status_code,
|
|
214
|
+
"details": error_details,
|
|
215
|
+
"voice_model": voice_model,
|
|
216
|
+
"requested_format": final_response_format,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
# Global exception handling for any uncaught error
|
|
221
|
+
error_type = type(
|
|
222
|
+
e
|
|
223
|
+
).__name__ # Gets the class name of the exception (e.g., 'TimeoutException', 'ToolException')
|
|
224
|
+
message = f"An unexpected error occurred during audio generation for voice {voice_model}."
|
|
225
|
+
details = str(e) # The string representation of the exception
|
|
226
|
+
|
|
227
|
+
# Log the error with full traceback for debugging
|
|
228
|
+
logger.error(
|
|
229
|
+
f"Venice Audio Tool Global Error ({error_type}): {message} | Details: {details}",
|
|
230
|
+
exc_info=True,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
"error": True,
|
|
235
|
+
"error_type": error_type, # e.g., "TimeoutException", "ToolException", "ClientError", "ValueError"
|
|
236
|
+
"message": message,
|
|
237
|
+
"details": details,
|
|
238
|
+
"voice_model": voice_model,
|
|
239
|
+
"requested_format": final_response_format,
|
|
240
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Venice Image Skill Suite
|
|
2
|
+
|
|
3
|
+
Venice Image is a comprehensive skill suite for intelligent agents, enabling state-of-the-art AI image generation, enhancement, upscaling, and vision analysis using the [Venice AI API](https://venice.ai/). This suite offers a modular interface: each sub-tool covers a focused aspect of visual intelligence, while sharing unified configuration and error handling.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### 1. **Image Generation**
|
|
10
|
+
Prompt-based creation of new artworks or photorealistic images, with support for multiple leading AI models, extensive style presets, and negative prompting. Models include:
|
|
11
|
+
- **Fluently XL** (realism, professional art)
|
|
12
|
+
- **Flux Dev** (innovative research, art workflows)
|
|
13
|
+
- **Lustify SDXL** (photorealistic, NSFW/SFW)
|
|
14
|
+
- **Pony Realism** (anime/character detail, Danbooru tags)
|
|
15
|
+
- **Venice SD35 / Stable Diffusion 3.5** (Stability AI, creative design)
|
|
16
|
+
|
|
17
|
+
### 2. **Image Enhancement**
|
|
18
|
+
Stylize or refine *existing* images without changing their resolution—ideal for artistic edits, restoration, or visual polishing.
|
|
19
|
+
|
|
20
|
+
### 3. **Image Upscaling**
|
|
21
|
+
Increase resolution by 2x or 4x while preserving essential details (with optional noise/replication settings). Great for preparing web images for print or HD use.
|
|
22
|
+
|
|
23
|
+
### 4. **Image Vision**
|
|
24
|
+
Obtain highly detailed, context-rich textual descriptions of images—useful for content understanding, accessibility, indexing, or cognitive agents.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## How It Works
|
|
29
|
+
|
|
30
|
+
- Tools call the Venice API via secure network requests, automatically handling authentication, rate limiting, and error management.
|
|
31
|
+
- Any generated or processed images are transparently stored in an object store (S3 or compatible), with returned URLs ready for user consumption.
|
|
32
|
+
- Unified logging and troubleshooting: every tool shares a robust diagnostic backbone for consistent developer experience.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Setup and Configuration
|
|
37
|
+
|
|
38
|
+
All skills require a **Venice API key** for operation.
|
|
39
|
+
|
|
40
|
+
### Required Configuration
|
|
41
|
+
- `enabled` *(bool)*: Enable or disable the overall skill suite.
|
|
42
|
+
- `api_key` *(string, sensitive)*: Your [Venice AI API key](https://venice.ai/).
|
|
43
|
+
- `states`: Enable/disable and set visibility for each sub-tool (public/private/disabled).
|
|
44
|
+
|
|
45
|
+
### Advanced Options
|
|
46
|
+
- `safe_mode` *(bool, default: true)*: If true, blurs images classified as adult/NSFW.
|
|
47
|
+
- `hide_watermark` *(bool, default: true)*: Request images without a Venice watermark (subject to Venice policy).
|
|
48
|
+
- `embed_exif_metadata` *(bool, default: false)*: Whether to embed prompt/config info in EXIF metadata.
|
|
49
|
+
- `negative_prompt` *(string)*: Default negative prompt, e.g. `(worst quality: 1.4), bad quality, nsfw`.
|
|
50
|
+
- `rate_limit_number` / `rate_limit_minutes`: (optional) Set a max request rate per agent.
|
|
51
|
+
|
|
52
|
+
For per-tool configuration, refer to the `states` section in [schema.json](schema.json):
|
|
53
|
+
- Each tool (e.g. `image_generation_flux_dev`, `image_enhance`, etc.) can be set to `"public"` (all users), `"private"` (agent owner only), or `"disabled"` (hidden).
|
|
54
|
+
|
|
55
|
+
#### Example (YAML/JSON-like)
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"enabled": true,
|
|
59
|
+
"api_key": "<YOUR_VENICE_API_KEY>",
|
|
60
|
+
"safe_mode": true,
|
|
61
|
+
"states": {
|
|
62
|
+
"image_vision": "public",
|
|
63
|
+
"image_enhance": "private",
|
|
64
|
+
"image_upscale": "disabled",
|
|
65
|
+
"image_generation_flux_dev": "public"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Usage Patterns
|
|
73
|
+
|
|
74
|
+
Each sub-tool has its own standardized input:
|
|
75
|
+
- URL-based tools (`image_enhance`, `image_upscale`, `image_vision`) require a web-accessible image URL.
|
|
76
|
+
- Generation tools require a *prompt* and offer flexible parameters (size, style, negative prompt, etc).
|
|
77
|
+
|
|
78
|
+
Errors and troubleshooting info are always returned in a structured dictionary, with clear separation of success and error fields.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Output and Storage
|
|
83
|
+
|
|
84
|
+
- All generated/processed images are written to S3-compatible storage using a SHA256-based unique key.
|
|
85
|
+
- Returned URLs are agent-accessible and stable.
|
|
86
|
+
- For Vision and non-binary results, the output is returned inline as a dictionary.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Security, License & Compliance
|
|
91
|
+
|
|
92
|
+
- Your Venice API key is required and kept confidential per config practices.
|
|
93
|
+
- Generated images and tool usage are subject to [Venice AI Terms of Service](https://venice.ai/) and the terms of the respective models (e.g. Stability AI, Black Forest Labs).
|
|
94
|
+
- Agents should implement their own access and moderation layers; Safe Mode and watermarking are best-effort.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Included Sub-Tools
|
|
99
|
+
|
|
100
|
+
_(For detailed docs, see the respective sub-tool README entries.)_
|
|
101
|
+
|
|
102
|
+
- image_generation_fluently_xl
|
|
103
|
+
- image_generation_flux_dev
|
|
104
|
+
- image_generation_flux_dev_uncensored
|
|
105
|
+
- image_generation_lustify_sdxl
|
|
106
|
+
- image_generation_pony_realism
|
|
107
|
+
- image_generation_venice_sd35
|
|
108
|
+
- image_generation_stable_diffusion_3_5
|
|
109
|
+
- image_enhance
|
|
110
|
+
- image_upscale
|
|
111
|
+
- image_vision
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Contributing & Support
|
|
116
|
+
|
|
117
|
+
For issues, bugfixes, or requests, please open a GitHub issue or contact the maintainers. This suite is regularly updated as Venice AI evolves.
|
|
118
|
+
|
|
119
|
+
---
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import NotRequired, Optional, TypedDict
|
|
3
|
+
|
|
4
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
5
|
+
from intentkit.skills.base import (
|
|
6
|
+
SkillConfig,
|
|
7
|
+
SkillState,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
# Import the base tool and all specific model skill classes
|
|
11
|
+
from intentkit.skills.venice_image.base import VeniceImageBaseTool
|
|
12
|
+
from intentkit.skills.venice_image.image_enhance.image_enhance import ImageEnhance
|
|
13
|
+
from intentkit.skills.venice_image.image_generation.image_generation_fluently_xl import (
|
|
14
|
+
ImageGenerationFluentlyXL,
|
|
15
|
+
)
|
|
16
|
+
from intentkit.skills.venice_image.image_generation.image_generation_flux_dev import (
|
|
17
|
+
ImageGenerationFluxDev,
|
|
18
|
+
)
|
|
19
|
+
from intentkit.skills.venice_image.image_generation.image_generation_flux_dev_uncensored import (
|
|
20
|
+
ImageGenerationFluxDevUncensored,
|
|
21
|
+
)
|
|
22
|
+
from intentkit.skills.venice_image.image_generation.image_generation_lustify_sdxl import (
|
|
23
|
+
ImageGenerationLustifySDXL,
|
|
24
|
+
)
|
|
25
|
+
from intentkit.skills.venice_image.image_generation.image_generation_pony_realism import (
|
|
26
|
+
ImageGenerationPonyRealism,
|
|
27
|
+
)
|
|
28
|
+
from intentkit.skills.venice_image.image_generation.image_generation_stable_diffusion_3_5 import (
|
|
29
|
+
ImageGenerationStableDiffusion35,
|
|
30
|
+
)
|
|
31
|
+
from intentkit.skills.venice_image.image_generation.image_generation_venice_sd35 import (
|
|
32
|
+
ImageGenerationVeniceSD35,
|
|
33
|
+
)
|
|
34
|
+
from intentkit.skills.venice_image.image_upscale.image_upscale import ImageUpscale
|
|
35
|
+
from intentkit.skills.venice_image.image_vision.image_vision import ImageVision
|
|
36
|
+
|
|
37
|
+
# Cache skills at the system level, because they are stateless and only depend on the store
|
|
38
|
+
_cache: dict[str, VeniceImageBaseTool] = {}
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Define the expected structure for the 'states' dictionary in the config
|
|
44
|
+
class SkillStates(TypedDict):
|
|
45
|
+
image_upscale: SkillState
|
|
46
|
+
image_enhance: SkillState
|
|
47
|
+
image_vision: SkillState
|
|
48
|
+
image_generation_flux_dev: SkillState
|
|
49
|
+
image_generation_flux_dev_uncensored: SkillState
|
|
50
|
+
image_generation_venice_sd35: SkillState
|
|
51
|
+
image_generation_fluently_xl: SkillState
|
|
52
|
+
image_generation_lustify_sdxl: SkillState
|
|
53
|
+
image_generation_pony_realism: SkillState
|
|
54
|
+
image_generation_stable_diffusion_3_5: SkillState
|
|
55
|
+
# Add new skill names here if more models are added
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Define the overall configuration structure for the venice_image category
|
|
59
|
+
class Config(SkillConfig):
|
|
60
|
+
"""Configuration for Venice Image skills."""
|
|
61
|
+
|
|
62
|
+
enabled: bool # Keep standard enabled flag
|
|
63
|
+
states: SkillStates
|
|
64
|
+
api_key_provider: str = "agent_owner"
|
|
65
|
+
api_key: NotRequired[Optional[str]] # Explicitly Optional
|
|
66
|
+
safe_mode: NotRequired[bool] # Defaults handled in base or usage
|
|
67
|
+
hide_watermark: NotRequired[bool] # Defaults handled in base or usage
|
|
68
|
+
negative_prompt: NotRequired[str] # Defaults handled in base or usage
|
|
69
|
+
rate_limit_number: NotRequired[Optional[int]] # Explicitly Optional
|
|
70
|
+
rate_limit_minutes: NotRequired[Optional[int]] # Explicitly Optional
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_SKILL_NAME_TO_CLASS_MAP: dict[str, type[VeniceImageBaseTool]] = {
|
|
74
|
+
"image_upscale": ImageUpscale,
|
|
75
|
+
"image_enhance": ImageEnhance,
|
|
76
|
+
"image_vision": ImageVision,
|
|
77
|
+
"image_generation_flux_dev": ImageGenerationFluxDev,
|
|
78
|
+
"image_generation_flux_dev_uncensored": ImageGenerationFluxDevUncensored,
|
|
79
|
+
"image_generation_venice_sd35": ImageGenerationVeniceSD35,
|
|
80
|
+
"image_generation_fluently_xl": ImageGenerationFluentlyXL,
|
|
81
|
+
"image_generation_lustify_sdxl": ImageGenerationLustifySDXL,
|
|
82
|
+
"image_generation_pony_realism": ImageGenerationPonyRealism,
|
|
83
|
+
"image_generation_stable_diffusion_3_5": ImageGenerationStableDiffusion35,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def get_skills(
|
|
88
|
+
config: "Config",
|
|
89
|
+
is_private: bool,
|
|
90
|
+
store: SkillStoreABC,
|
|
91
|
+
**_, # Allow for extra arguments if the loader passes them
|
|
92
|
+
) -> list[VeniceImageBaseTool]:
|
|
93
|
+
"""Get all enabled Venice Image skills based on configuration and privacy level.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
config: The configuration for Venice Image skills.
|
|
97
|
+
is_private: Whether the context is private (e.g., agent owner).
|
|
98
|
+
store: The skill store for persisting data and accessing system config.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
A list of instantiated and enabled Venice Image skill objects.
|
|
102
|
+
"""
|
|
103
|
+
# Check if the entire category is disabled first
|
|
104
|
+
if not config.get("enabled", False):
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
available_skills: list[VeniceImageBaseTool] = []
|
|
108
|
+
|
|
109
|
+
# Include skills based on their state
|
|
110
|
+
for skill_name, state in config["states"].items():
|
|
111
|
+
if state == "disabled":
|
|
112
|
+
continue
|
|
113
|
+
elif state == "public" or (state == "private" and is_private):
|
|
114
|
+
available_skills.append(skill_name)
|
|
115
|
+
|
|
116
|
+
# Get each skill using the cached getter
|
|
117
|
+
result = []
|
|
118
|
+
for name in available_skills:
|
|
119
|
+
skill = get_venice_image_skill(name, store, config)
|
|
120
|
+
if skill:
|
|
121
|
+
result.append(skill)
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_venice_image_skill(
|
|
126
|
+
name: str,
|
|
127
|
+
store: SkillStoreABC,
|
|
128
|
+
config: "Config",
|
|
129
|
+
) -> Optional[VeniceImageBaseTool]:
|
|
130
|
+
"""
|
|
131
|
+
Factory function to get a cached Venice Image skill instance by name.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
name: The name of the skill to get (must match keys in _SKILL_NAME_TO_CLASS_MAP).
|
|
135
|
+
store: The skill store, passed to the skill constructor.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
The requested Venice Image skill instance, or None if the name is unknown.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
# Return from cache immediately if already exists
|
|
142
|
+
if name in _cache:
|
|
143
|
+
return _cache[name]
|
|
144
|
+
|
|
145
|
+
skill_class = _SKILL_NAME_TO_CLASS_MAP.get(name)
|
|
146
|
+
if not skill_class:
|
|
147
|
+
logger.warning(f"Unknown Venice skill: {name}")
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
# Cache and return the newly created instance
|
|
151
|
+
_cache[name] = skill_class(
|
|
152
|
+
skill_store=store,
|
|
153
|
+
)
|
|
154
|
+
return _cache[name]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module encapsulates all interactions with the Venice AI API.
|
|
3
|
+
It provides a function, make_venice_api_request, to make POST requests
|
|
4
|
+
to the API and handles the responses, including error handling,
|
|
5
|
+
content type checking, and image storage via S3. This separation
|
|
6
|
+
of concerns keeps the core skill logic cleaner and easier to maintain.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
from intentkit.utils.s3 import store_image_bytes
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def make_venice_api_request(
|
|
21
|
+
api_key: str,
|
|
22
|
+
path: str,
|
|
23
|
+
payload: Dict[str, Any],
|
|
24
|
+
category: str,
|
|
25
|
+
tool_name: str,
|
|
26
|
+
) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
|
|
27
|
+
"""
|
|
28
|
+
Makes a POST request to the Venice AI API, handling all aspects
|
|
29
|
+
of the API interaction. This includes:
|
|
30
|
+
|
|
31
|
+
1. Constructing the API URL using a base URL and the provided path.
|
|
32
|
+
2. Adding the required authorization header with the provided API key.
|
|
33
|
+
3. Sending the POST request with the given payload.
|
|
34
|
+
4. Handling potential connection and HTTP errors.
|
|
35
|
+
5. Calling the internal _handle_response function to process the API's
|
|
36
|
+
response, which might be JSON or an image.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
api_key: The Venice AI API key for authentication.
|
|
40
|
+
path: The API endpoint path (e.g., "/api/v1/image/generate"). Should *not* start with the base URL.
|
|
41
|
+
payload: The data to send in the request body (as JSON).
|
|
42
|
+
category: The category of the skill making the request (e.g., "venice_image"). Used for S3 storage and logging purpose.
|
|
43
|
+
tool_name: The name of the tool or skill making the request (e.g., "image_generation"). Used for S3 storage and logging purpose.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
A tuple: (success_data, error_data).
|
|
47
|
+
- success_data: A dictionary containing the parsed JSON response from the API if the request was successful
|
|
48
|
+
(or a dictionary containing the S3 URL if the response is an image).
|
|
49
|
+
- error_data: A dictionary containing information about any errors that occurred,
|
|
50
|
+
or None if the request was successful. The dictionary includes an 'error' key.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
venice_base_url = "https://api.venice.ai" # Venice AI API base URL
|
|
54
|
+
|
|
55
|
+
if not path.startswith("/"):
|
|
56
|
+
path = "/" + path
|
|
57
|
+
|
|
58
|
+
api_url = f"{venice_base_url}{path}"
|
|
59
|
+
|
|
60
|
+
headers = {
|
|
61
|
+
"Authorization": f"Bearer {api_key}",
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
"Accept": "image/*, application/json",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
logger.info(
|
|
67
|
+
f"[{category}/{tool_name}] Sending request to {api_url} with payload: {payload}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
async with httpx.AsyncClient(timeout=180.0) as client:
|
|
72
|
+
response = await client.post(api_url, json=payload, headers=headers)
|
|
73
|
+
return await _handle_response(response, category, tool_name)
|
|
74
|
+
|
|
75
|
+
except httpx.RequestError as e:
|
|
76
|
+
error_msg = f"Connection error: {e}"
|
|
77
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}")
|
|
78
|
+
return {}, {"success": False, "error": error_msg}
|
|
79
|
+
except Exception as e:
|
|
80
|
+
error_msg = f"Unexpected error: {e}"
|
|
81
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}", exc_info=True)
|
|
82
|
+
return {}, {"success": False, "error": error_msg}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def _handle_response(
|
|
86
|
+
response: httpx.Response, category: str, tool_name: str
|
|
87
|
+
) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
|
|
88
|
+
"""
|
|
89
|
+
Handles the API response, differentiating between JSON and image responses.
|
|
90
|
+
|
|
91
|
+
If the response is an image (based on the 'content-type' header),
|
|
92
|
+
it stores the image in S3 and returns the S3 URL.
|
|
93
|
+
If the response is JSON, it parses the JSON and returns it.
|
|
94
|
+
If any errors occur, it returns an error dictionary.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
content_type = str(response.headers.get("content-type", "")).lower()
|
|
98
|
+
|
|
99
|
+
if response.status_code == 200 and content_type.startswith("image/"):
|
|
100
|
+
try:
|
|
101
|
+
upscaled_image_bytes = response.content
|
|
102
|
+
image_hash = hashlib.sha256(upscaled_image_bytes).hexdigest()
|
|
103
|
+
file_extension = content_type.split("/")[-1].split("+")[0] or "png"
|
|
104
|
+
|
|
105
|
+
key = f"{category}/{tool_name}/{image_hash}.{file_extension}"
|
|
106
|
+
|
|
107
|
+
logger.info(f"[{category}/{tool_name}] Storing image with key: {key}")
|
|
108
|
+
|
|
109
|
+
stored_url = await store_image_bytes(
|
|
110
|
+
upscaled_image_bytes, key, content_type=content_type
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return {"success": True, "result": stored_url}, None
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
error_msg = f"Error processing image response: {e}"
|
|
117
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}", exc_info=True)
|
|
118
|
+
return {}, {"success": False, "error": error_msg}
|
|
119
|
+
|
|
120
|
+
elif response.status_code == 200:
|
|
121
|
+
try:
|
|
122
|
+
logger.info(f"[{category}/{tool_name}] Received successful JSON response.")
|
|
123
|
+
return response.json(), None
|
|
124
|
+
except Exception as json_err:
|
|
125
|
+
error_msg = f"Failed to parse JSON response: {json_err} - {response.text}"
|
|
126
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}")
|
|
127
|
+
return {}, {"success": False, "error": error_msg}
|
|
128
|
+
|
|
129
|
+
else:
|
|
130
|
+
try:
|
|
131
|
+
error_data = response.json()
|
|
132
|
+
error_msg = f"API returned error: {error_data.get('message', error_data.get('detail', response.text))}"
|
|
133
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}")
|
|
134
|
+
return {}, {"success": False, "error": error_msg}
|
|
135
|
+
except Exception:
|
|
136
|
+
error_msg = f"API returned status code {response.status_code} with text: {response.text}"
|
|
137
|
+
logger.error(f"[{category}/{tool_name}] {error_msg}")
|
|
138
|
+
return {}, {"success": False, "error": error_msg}
|