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.

Files changed (366) hide show
  1. intentkit/__init__.py +17 -0
  2. intentkit/abstracts/__init__.py +0 -0
  3. intentkit/abstracts/agent.py +60 -0
  4. intentkit/abstracts/api.py +4 -0
  5. intentkit/abstracts/engine.py +38 -0
  6. intentkit/abstracts/exception.py +9 -0
  7. intentkit/abstracts/graph.py +25 -0
  8. intentkit/abstracts/skill.py +129 -0
  9. intentkit/abstracts/twitter.py +54 -0
  10. intentkit/clients/__init__.py +14 -0
  11. intentkit/clients/cdp.py +53 -0
  12. intentkit/clients/twitter.py +445 -0
  13. intentkit/config/__init__.py +0 -0
  14. intentkit/config/config.py +164 -0
  15. intentkit/core/__init__.py +0 -0
  16. intentkit/core/agent.py +191 -0
  17. intentkit/core/api.py +40 -0
  18. intentkit/core/client.py +45 -0
  19. intentkit/core/credit.py +1767 -0
  20. intentkit/core/engine.py +1018 -0
  21. intentkit/core/node.py +223 -0
  22. intentkit/core/prompt.py +58 -0
  23. intentkit/core/skill.py +124 -0
  24. intentkit/models/agent.py +1689 -0
  25. intentkit/models/agent_data.py +810 -0
  26. intentkit/models/agent_schema.json +733 -0
  27. intentkit/models/app_setting.py +156 -0
  28. intentkit/models/base.py +9 -0
  29. intentkit/models/chat.py +581 -0
  30. intentkit/models/conversation.py +286 -0
  31. intentkit/models/credit.py +1406 -0
  32. intentkit/models/db.py +120 -0
  33. intentkit/models/db_mig.py +102 -0
  34. intentkit/models/generator.py +347 -0
  35. intentkit/models/llm.py +746 -0
  36. intentkit/models/redis.py +132 -0
  37. intentkit/models/skill.py +466 -0
  38. intentkit/models/user.py +243 -0
  39. intentkit/skills/__init__.py +12 -0
  40. intentkit/skills/acolyt/__init__.py +83 -0
  41. intentkit/skills/acolyt/acolyt.jpg +0 -0
  42. intentkit/skills/acolyt/ask.py +128 -0
  43. intentkit/skills/acolyt/base.py +28 -0
  44. intentkit/skills/acolyt/schema.json +89 -0
  45. intentkit/skills/aixbt/README.md +71 -0
  46. intentkit/skills/aixbt/__init__.py +73 -0
  47. intentkit/skills/aixbt/aixbt.jpg +0 -0
  48. intentkit/skills/aixbt/base.py +21 -0
  49. intentkit/skills/aixbt/projects.py +153 -0
  50. intentkit/skills/aixbt/schema.json +99 -0
  51. intentkit/skills/allora/__init__.py +83 -0
  52. intentkit/skills/allora/allora.jpeg +0 -0
  53. intentkit/skills/allora/base.py +28 -0
  54. intentkit/skills/allora/price.py +130 -0
  55. intentkit/skills/allora/schema.json +89 -0
  56. intentkit/skills/base.py +174 -0
  57. intentkit/skills/carv/README.md +95 -0
  58. intentkit/skills/carv/__init__.py +121 -0
  59. intentkit/skills/carv/base.py +183 -0
  60. intentkit/skills/carv/carv.webp +0 -0
  61. intentkit/skills/carv/fetch_news.py +92 -0
  62. intentkit/skills/carv/onchain_query.py +164 -0
  63. intentkit/skills/carv/schema.json +137 -0
  64. intentkit/skills/carv/token_info_and_price.py +110 -0
  65. intentkit/skills/cdp/__init__.py +137 -0
  66. intentkit/skills/cdp/base.py +21 -0
  67. intentkit/skills/cdp/cdp.png +0 -0
  68. intentkit/skills/cdp/get_balance.py +81 -0
  69. intentkit/skills/cdp/schema.json +473 -0
  70. intentkit/skills/chainlist/README.md +38 -0
  71. intentkit/skills/chainlist/__init__.py +54 -0
  72. intentkit/skills/chainlist/base.py +21 -0
  73. intentkit/skills/chainlist/chain_lookup.py +208 -0
  74. intentkit/skills/chainlist/chainlist.png +0 -0
  75. intentkit/skills/chainlist/schema.json +47 -0
  76. intentkit/skills/common/__init__.py +82 -0
  77. intentkit/skills/common/base.py +21 -0
  78. intentkit/skills/common/common.jpg +0 -0
  79. intentkit/skills/common/current_time.py +84 -0
  80. intentkit/skills/common/schema.json +57 -0
  81. intentkit/skills/cookiefun/README.md +121 -0
  82. intentkit/skills/cookiefun/__init__.py +78 -0
  83. intentkit/skills/cookiefun/base.py +41 -0
  84. intentkit/skills/cookiefun/constants.py +18 -0
  85. intentkit/skills/cookiefun/cookiefun.png +0 -0
  86. intentkit/skills/cookiefun/get_account_details.py +171 -0
  87. intentkit/skills/cookiefun/get_account_feed.py +282 -0
  88. intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
  89. intentkit/skills/cookiefun/get_sectors.py +128 -0
  90. intentkit/skills/cookiefun/schema.json +155 -0
  91. intentkit/skills/cookiefun/search_accounts.py +225 -0
  92. intentkit/skills/cryptocompare/__init__.py +130 -0
  93. intentkit/skills/cryptocompare/api.py +159 -0
  94. intentkit/skills/cryptocompare/base.py +303 -0
  95. intentkit/skills/cryptocompare/cryptocompare.png +0 -0
  96. intentkit/skills/cryptocompare/fetch_news.py +96 -0
  97. intentkit/skills/cryptocompare/fetch_price.py +99 -0
  98. intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
  99. intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
  100. intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
  101. intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
  102. intentkit/skills/cryptocompare/schema.json +168 -0
  103. intentkit/skills/cryptopanic/__init__.py +108 -0
  104. intentkit/skills/cryptopanic/base.py +51 -0
  105. intentkit/skills/cryptopanic/cryptopanic.png +0 -0
  106. intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
  107. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
  108. intentkit/skills/cryptopanic/schema.json +103 -0
  109. intentkit/skills/dapplooker/README.md +92 -0
  110. intentkit/skills/dapplooker/__init__.py +83 -0
  111. intentkit/skills/dapplooker/base.py +26 -0
  112. intentkit/skills/dapplooker/dapplooker.jpg +0 -0
  113. intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
  114. intentkit/skills/dapplooker/schema.json +91 -0
  115. intentkit/skills/defillama/__init__.py +323 -0
  116. intentkit/skills/defillama/api.py +315 -0
  117. intentkit/skills/defillama/base.py +135 -0
  118. intentkit/skills/defillama/coins/__init__.py +0 -0
  119. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
  120. intentkit/skills/defillama/coins/fetch_block.py +98 -0
  121. intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
  122. intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
  123. intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
  124. intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
  125. intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
  126. intentkit/skills/defillama/config/__init__.py +0 -0
  127. intentkit/skills/defillama/config/chains.py +433 -0
  128. intentkit/skills/defillama/defillama.jpeg +0 -0
  129. intentkit/skills/defillama/fees/__init__.py +0 -0
  130. intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
  131. intentkit/skills/defillama/schema.json +383 -0
  132. intentkit/skills/defillama/stablecoins/__init__.py +0 -0
  133. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
  134. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
  135. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
  136. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
  137. intentkit/skills/defillama/tests/__init__.py +0 -0
  138. intentkit/skills/defillama/tests/api_integration.test.py +192 -0
  139. intentkit/skills/defillama/tests/api_unit.test.py +583 -0
  140. intentkit/skills/defillama/tvl/__init__.py +0 -0
  141. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
  142. intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
  143. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
  144. intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
  145. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
  146. intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
  147. intentkit/skills/defillama/volumes/__init__.py +0 -0
  148. intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
  149. intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
  150. intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
  151. intentkit/skills/defillama/yields/__init__.py +0 -0
  152. intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
  153. intentkit/skills/defillama/yields/fetch_pools.py +126 -0
  154. intentkit/skills/dexscreener/__init__.py +93 -0
  155. intentkit/skills/dexscreener/base.py +133 -0
  156. intentkit/skills/dexscreener/dexscreener.png +0 -0
  157. intentkit/skills/dexscreener/model/__init__.py +0 -0
  158. intentkit/skills/dexscreener/model/search_token_response.py +82 -0
  159. intentkit/skills/dexscreener/schema.json +48 -0
  160. intentkit/skills/dexscreener/search_token.py +321 -0
  161. intentkit/skills/dune_analytics/__init__.py +103 -0
  162. intentkit/skills/dune_analytics/base.py +46 -0
  163. intentkit/skills/dune_analytics/dune.png +0 -0
  164. intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
  165. intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
  166. intentkit/skills/dune_analytics/schema.json +99 -0
  167. intentkit/skills/elfa/README.md +100 -0
  168. intentkit/skills/elfa/__init__.py +123 -0
  169. intentkit/skills/elfa/base.py +28 -0
  170. intentkit/skills/elfa/elfa.jpg +0 -0
  171. intentkit/skills/elfa/mention.py +504 -0
  172. intentkit/skills/elfa/schema.json +153 -0
  173. intentkit/skills/elfa/stats.py +118 -0
  174. intentkit/skills/elfa/tokens.py +126 -0
  175. intentkit/skills/enso/README.md +75 -0
  176. intentkit/skills/enso/__init__.py +114 -0
  177. intentkit/skills/enso/abi/__init__.py +0 -0
  178. intentkit/skills/enso/abi/approval.py +279 -0
  179. intentkit/skills/enso/abi/erc20.py +14 -0
  180. intentkit/skills/enso/abi/route.py +129 -0
  181. intentkit/skills/enso/base.py +44 -0
  182. intentkit/skills/enso/best_yield.py +286 -0
  183. intentkit/skills/enso/enso.jpg +0 -0
  184. intentkit/skills/enso/networks.py +105 -0
  185. intentkit/skills/enso/prices.py +93 -0
  186. intentkit/skills/enso/route.py +300 -0
  187. intentkit/skills/enso/schema.json +212 -0
  188. intentkit/skills/enso/tokens.py +223 -0
  189. intentkit/skills/enso/wallet.py +381 -0
  190. intentkit/skills/github/README.md +63 -0
  191. intentkit/skills/github/__init__.py +54 -0
  192. intentkit/skills/github/base.py +21 -0
  193. intentkit/skills/github/github.jpg +0 -0
  194. intentkit/skills/github/github_search.py +183 -0
  195. intentkit/skills/github/schema.json +59 -0
  196. intentkit/skills/heurist/__init__.py +143 -0
  197. intentkit/skills/heurist/base.py +26 -0
  198. intentkit/skills/heurist/heurist.png +0 -0
  199. intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
  200. intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
  201. intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
  202. intentkit/skills/heurist/image_generation_braindance.py +162 -0
  203. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
  204. intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
  205. intentkit/skills/heurist/image_generation_sdxl.py +161 -0
  206. intentkit/skills/heurist/schema.json +196 -0
  207. intentkit/skills/lifi/README.md +294 -0
  208. intentkit/skills/lifi/__init__.py +141 -0
  209. intentkit/skills/lifi/base.py +21 -0
  210. intentkit/skills/lifi/lifi.png +0 -0
  211. intentkit/skills/lifi/schema.json +89 -0
  212. intentkit/skills/lifi/token_execute.py +472 -0
  213. intentkit/skills/lifi/token_quote.py +190 -0
  214. intentkit/skills/lifi/utils.py +656 -0
  215. intentkit/skills/moralis/README.md +490 -0
  216. intentkit/skills/moralis/__init__.py +110 -0
  217. intentkit/skills/moralis/api.py +281 -0
  218. intentkit/skills/moralis/base.py +55 -0
  219. intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
  220. intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
  221. intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
  222. intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
  223. intentkit/skills/moralis/moralis.png +0 -0
  224. intentkit/skills/moralis/schema.json +156 -0
  225. intentkit/skills/moralis/tests/__init__.py +0 -0
  226. intentkit/skills/moralis/tests/test_wallet.py +511 -0
  227. intentkit/skills/nation/__init__.py +62 -0
  228. intentkit/skills/nation/base.py +31 -0
  229. intentkit/skills/nation/nation.png +0 -0
  230. intentkit/skills/nation/nft_check.py +106 -0
  231. intentkit/skills/nation/schema.json +58 -0
  232. intentkit/skills/openai/__init__.py +107 -0
  233. intentkit/skills/openai/base.py +32 -0
  234. intentkit/skills/openai/dalle_image_generation.py +128 -0
  235. intentkit/skills/openai/gpt_image_generation.py +152 -0
  236. intentkit/skills/openai/gpt_image_to_image.py +186 -0
  237. intentkit/skills/openai/image_to_text.py +126 -0
  238. intentkit/skills/openai/openai.png +0 -0
  239. intentkit/skills/openai/schema.json +139 -0
  240. intentkit/skills/portfolio/README.md +55 -0
  241. intentkit/skills/portfolio/__init__.py +151 -0
  242. intentkit/skills/portfolio/base.py +107 -0
  243. intentkit/skills/portfolio/constants.py +9 -0
  244. intentkit/skills/portfolio/moralis.png +0 -0
  245. intentkit/skills/portfolio/schema.json +237 -0
  246. intentkit/skills/portfolio/token_balances.py +155 -0
  247. intentkit/skills/portfolio/wallet_approvals.py +102 -0
  248. intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
  249. intentkit/skills/portfolio/wallet_history.py +155 -0
  250. intentkit/skills/portfolio/wallet_net_worth.py +112 -0
  251. intentkit/skills/portfolio/wallet_nfts.py +139 -0
  252. intentkit/skills/portfolio/wallet_profitability.py +101 -0
  253. intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
  254. intentkit/skills/portfolio/wallet_stats.py +79 -0
  255. intentkit/skills/portfolio/wallet_swaps.py +147 -0
  256. intentkit/skills/skills.toml +103 -0
  257. intentkit/skills/slack/__init__.py +98 -0
  258. intentkit/skills/slack/base.py +55 -0
  259. intentkit/skills/slack/get_channel.py +109 -0
  260. intentkit/skills/slack/get_message.py +136 -0
  261. intentkit/skills/slack/schedule_message.py +92 -0
  262. intentkit/skills/slack/schema.json +135 -0
  263. intentkit/skills/slack/send_message.py +81 -0
  264. intentkit/skills/slack/slack.jpg +0 -0
  265. intentkit/skills/system/__init__.py +90 -0
  266. intentkit/skills/system/base.py +22 -0
  267. intentkit/skills/system/read_agent_api_key.py +87 -0
  268. intentkit/skills/system/regenerate_agent_api_key.py +77 -0
  269. intentkit/skills/system/schema.json +53 -0
  270. intentkit/skills/system/system.svg +76 -0
  271. intentkit/skills/tavily/README.md +86 -0
  272. intentkit/skills/tavily/__init__.py +91 -0
  273. intentkit/skills/tavily/base.py +27 -0
  274. intentkit/skills/tavily/schema.json +119 -0
  275. intentkit/skills/tavily/tavily.jpg +0 -0
  276. intentkit/skills/tavily/tavily_extract.py +147 -0
  277. intentkit/skills/tavily/tavily_search.py +139 -0
  278. intentkit/skills/token/README.md +89 -0
  279. intentkit/skills/token/__init__.py +107 -0
  280. intentkit/skills/token/base.py +154 -0
  281. intentkit/skills/token/constants.py +9 -0
  282. intentkit/skills/token/erc20_transfers.py +145 -0
  283. intentkit/skills/token/moralis.png +0 -0
  284. intentkit/skills/token/schema.json +141 -0
  285. intentkit/skills/token/token_analytics.py +81 -0
  286. intentkit/skills/token/token_price.py +132 -0
  287. intentkit/skills/token/token_search.py +121 -0
  288. intentkit/skills/twitter/__init__.py +146 -0
  289. intentkit/skills/twitter/base.py +68 -0
  290. intentkit/skills/twitter/follow_user.py +69 -0
  291. intentkit/skills/twitter/get_mentions.py +124 -0
  292. intentkit/skills/twitter/get_timeline.py +111 -0
  293. intentkit/skills/twitter/get_user_by_username.py +84 -0
  294. intentkit/skills/twitter/get_user_tweets.py +123 -0
  295. intentkit/skills/twitter/like_tweet.py +65 -0
  296. intentkit/skills/twitter/post_tweet.py +90 -0
  297. intentkit/skills/twitter/reply_tweet.py +98 -0
  298. intentkit/skills/twitter/retweet.py +76 -0
  299. intentkit/skills/twitter/schema.json +258 -0
  300. intentkit/skills/twitter/search_tweets.py +115 -0
  301. intentkit/skills/twitter/twitter.png +0 -0
  302. intentkit/skills/unrealspeech/__init__.py +55 -0
  303. intentkit/skills/unrealspeech/base.py +21 -0
  304. intentkit/skills/unrealspeech/schema.json +100 -0
  305. intentkit/skills/unrealspeech/text_to_speech.py +177 -0
  306. intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
  307. intentkit/skills/venice_audio/__init__.py +106 -0
  308. intentkit/skills/venice_audio/base.py +119 -0
  309. intentkit/skills/venice_audio/input.py +41 -0
  310. intentkit/skills/venice_audio/schema.json +152 -0
  311. intentkit/skills/venice_audio/venice_audio.py +240 -0
  312. intentkit/skills/venice_audio/venice_logo.jpg +0 -0
  313. intentkit/skills/venice_image/README.md +119 -0
  314. intentkit/skills/venice_image/__init__.py +154 -0
  315. intentkit/skills/venice_image/api.py +138 -0
  316. intentkit/skills/venice_image/base.py +188 -0
  317. intentkit/skills/venice_image/config.py +35 -0
  318. intentkit/skills/venice_image/image_enhance/README.md +119 -0
  319. intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
  320. intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
  321. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
  322. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
  323. intentkit/skills/venice_image/image_generation/README.md +144 -0
  324. intentkit/skills/venice_image/image_generation/__init__.py +0 -0
  325. intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
  326. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
  327. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
  328. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
  329. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
  330. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
  331. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
  332. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
  333. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
  334. intentkit/skills/venice_image/image_upscale/README.md +111 -0
  335. intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
  336. intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
  337. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
  338. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
  339. intentkit/skills/venice_image/image_vision/README.md +112 -0
  340. intentkit/skills/venice_image/image_vision/__init__.py +0 -0
  341. intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
  342. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
  343. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
  344. intentkit/skills/venice_image/schema.json +267 -0
  345. intentkit/skills/venice_image/utils.py +78 -0
  346. intentkit/skills/venice_image/venice_image.jpg +0 -0
  347. intentkit/skills/web_scraper/README.md +82 -0
  348. intentkit/skills/web_scraper/__init__.py +92 -0
  349. intentkit/skills/web_scraper/base.py +21 -0
  350. intentkit/skills/web_scraper/langchain.png +0 -0
  351. intentkit/skills/web_scraper/schema.json +115 -0
  352. intentkit/skills/web_scraper/scrape_and_index.py +327 -0
  353. intentkit/utils/__init__.py +1 -0
  354. intentkit/utils/chain.py +436 -0
  355. intentkit/utils/error.py +134 -0
  356. intentkit/utils/logging.py +70 -0
  357. intentkit/utils/middleware.py +61 -0
  358. intentkit/utils/random.py +16 -0
  359. intentkit/utils/s3.py +267 -0
  360. intentkit/utils/slack_alert.py +79 -0
  361. intentkit/utils/tx.py +37 -0
  362. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
  363. intentkit-0.5.2.dist-info/RECORD +365 -0
  364. intentkit-0.5.0.dist-info/RECORD +0 -4
  365. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
  366. {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
+ }
@@ -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}