lfx-nightly 0.1.11.dev0__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.
Files changed (699) hide show
  1. lfx/__init__.py +0 -0
  2. lfx/__main__.py +25 -0
  3. lfx/base/__init__.py +0 -0
  4. lfx/base/agents/__init__.py +0 -0
  5. lfx/base/agents/agent.py +268 -0
  6. lfx/base/agents/callback.py +130 -0
  7. lfx/base/agents/context.py +109 -0
  8. lfx/base/agents/crewai/__init__.py +0 -0
  9. lfx/base/agents/crewai/crew.py +231 -0
  10. lfx/base/agents/crewai/tasks.py +12 -0
  11. lfx/base/agents/default_prompts.py +23 -0
  12. lfx/base/agents/errors.py +15 -0
  13. lfx/base/agents/events.py +346 -0
  14. lfx/base/agents/utils.py +205 -0
  15. lfx/base/astra_assistants/__init__.py +0 -0
  16. lfx/base/astra_assistants/util.py +171 -0
  17. lfx/base/chains/__init__.py +0 -0
  18. lfx/base/chains/model.py +19 -0
  19. lfx/base/composio/__init__.py +0 -0
  20. lfx/base/composio/composio_base.py +1291 -0
  21. lfx/base/compressors/__init__.py +0 -0
  22. lfx/base/compressors/model.py +60 -0
  23. lfx/base/constants.py +46 -0
  24. lfx/base/curl/__init__.py +0 -0
  25. lfx/base/curl/parse.py +188 -0
  26. lfx/base/data/__init__.py +5 -0
  27. lfx/base/data/base_file.py +685 -0
  28. lfx/base/data/docling_utils.py +245 -0
  29. lfx/base/data/utils.py +198 -0
  30. lfx/base/document_transformers/__init__.py +0 -0
  31. lfx/base/document_transformers/model.py +43 -0
  32. lfx/base/embeddings/__init__.py +0 -0
  33. lfx/base/embeddings/aiml_embeddings.py +62 -0
  34. lfx/base/embeddings/model.py +26 -0
  35. lfx/base/flow_processing/__init__.py +0 -0
  36. lfx/base/flow_processing/utils.py +86 -0
  37. lfx/base/huggingface/__init__.py +0 -0
  38. lfx/base/huggingface/model_bridge.py +133 -0
  39. lfx/base/io/__init__.py +0 -0
  40. lfx/base/io/chat.py +20 -0
  41. lfx/base/io/text.py +22 -0
  42. lfx/base/langchain_utilities/__init__.py +0 -0
  43. lfx/base/langchain_utilities/model.py +35 -0
  44. lfx/base/langchain_utilities/spider_constants.py +1 -0
  45. lfx/base/langwatch/__init__.py +0 -0
  46. lfx/base/langwatch/utils.py +18 -0
  47. lfx/base/mcp/__init__.py +0 -0
  48. lfx/base/mcp/constants.py +2 -0
  49. lfx/base/mcp/util.py +1398 -0
  50. lfx/base/memory/__init__.py +0 -0
  51. lfx/base/memory/memory.py +49 -0
  52. lfx/base/memory/model.py +38 -0
  53. lfx/base/models/__init__.py +3 -0
  54. lfx/base/models/aiml_constants.py +51 -0
  55. lfx/base/models/anthropic_constants.py +47 -0
  56. lfx/base/models/aws_constants.py +151 -0
  57. lfx/base/models/chat_result.py +76 -0
  58. lfx/base/models/google_generative_ai_constants.py +70 -0
  59. lfx/base/models/groq_constants.py +134 -0
  60. lfx/base/models/model.py +375 -0
  61. lfx/base/models/model_input_constants.py +307 -0
  62. lfx/base/models/model_metadata.py +41 -0
  63. lfx/base/models/model_utils.py +8 -0
  64. lfx/base/models/novita_constants.py +35 -0
  65. lfx/base/models/ollama_constants.py +49 -0
  66. lfx/base/models/openai_constants.py +122 -0
  67. lfx/base/models/sambanova_constants.py +18 -0
  68. lfx/base/processing/__init__.py +0 -0
  69. lfx/base/prompts/__init__.py +0 -0
  70. lfx/base/prompts/api_utils.py +224 -0
  71. lfx/base/prompts/utils.py +61 -0
  72. lfx/base/textsplitters/__init__.py +0 -0
  73. lfx/base/textsplitters/model.py +28 -0
  74. lfx/base/tools/__init__.py +0 -0
  75. lfx/base/tools/base.py +26 -0
  76. lfx/base/tools/component_tool.py +325 -0
  77. lfx/base/tools/constants.py +49 -0
  78. lfx/base/tools/flow_tool.py +132 -0
  79. lfx/base/tools/run_flow.py +224 -0
  80. lfx/base/vectorstores/__init__.py +0 -0
  81. lfx/base/vectorstores/model.py +193 -0
  82. lfx/base/vectorstores/utils.py +22 -0
  83. lfx/base/vectorstores/vector_store_connection_decorator.py +52 -0
  84. lfx/cli/__init__.py +5 -0
  85. lfx/cli/commands.py +319 -0
  86. lfx/cli/common.py +650 -0
  87. lfx/cli/run.py +441 -0
  88. lfx/cli/script_loader.py +247 -0
  89. lfx/cli/serve_app.py +546 -0
  90. lfx/cli/validation.py +69 -0
  91. lfx/components/FAISS/__init__.py +34 -0
  92. lfx/components/FAISS/faiss.py +111 -0
  93. lfx/components/Notion/__init__.py +19 -0
  94. lfx/components/Notion/add_content_to_page.py +269 -0
  95. lfx/components/Notion/create_page.py +94 -0
  96. lfx/components/Notion/list_database_properties.py +68 -0
  97. lfx/components/Notion/list_pages.py +122 -0
  98. lfx/components/Notion/list_users.py +77 -0
  99. lfx/components/Notion/page_content_viewer.py +93 -0
  100. lfx/components/Notion/search.py +111 -0
  101. lfx/components/Notion/update_page_property.py +114 -0
  102. lfx/components/__init__.py +411 -0
  103. lfx/components/_importing.py +42 -0
  104. lfx/components/agentql/__init__.py +3 -0
  105. lfx/components/agentql/agentql_api.py +151 -0
  106. lfx/components/agents/__init__.py +34 -0
  107. lfx/components/agents/agent.py +558 -0
  108. lfx/components/agents/mcp_component.py +501 -0
  109. lfx/components/aiml/__init__.py +37 -0
  110. lfx/components/aiml/aiml.py +112 -0
  111. lfx/components/aiml/aiml_embeddings.py +37 -0
  112. lfx/components/amazon/__init__.py +36 -0
  113. lfx/components/amazon/amazon_bedrock_embedding.py +109 -0
  114. lfx/components/amazon/amazon_bedrock_model.py +124 -0
  115. lfx/components/amazon/s3_bucket_uploader.py +211 -0
  116. lfx/components/anthropic/__init__.py +34 -0
  117. lfx/components/anthropic/anthropic.py +187 -0
  118. lfx/components/apify/__init__.py +5 -0
  119. lfx/components/apify/apify_actor.py +325 -0
  120. lfx/components/arxiv/__init__.py +3 -0
  121. lfx/components/arxiv/arxiv.py +163 -0
  122. lfx/components/assemblyai/__init__.py +46 -0
  123. lfx/components/assemblyai/assemblyai_get_subtitles.py +83 -0
  124. lfx/components/assemblyai/assemblyai_lemur.py +183 -0
  125. lfx/components/assemblyai/assemblyai_list_transcripts.py +95 -0
  126. lfx/components/assemblyai/assemblyai_poll_transcript.py +72 -0
  127. lfx/components/assemblyai/assemblyai_start_transcript.py +188 -0
  128. lfx/components/azure/__init__.py +37 -0
  129. lfx/components/azure/azure_openai.py +95 -0
  130. lfx/components/azure/azure_openai_embeddings.py +83 -0
  131. lfx/components/baidu/__init__.py +32 -0
  132. lfx/components/baidu/baidu_qianfan_chat.py +113 -0
  133. lfx/components/bing/__init__.py +3 -0
  134. lfx/components/bing/bing_search_api.py +61 -0
  135. lfx/components/cassandra/__init__.py +40 -0
  136. lfx/components/cassandra/cassandra.py +264 -0
  137. lfx/components/cassandra/cassandra_chat.py +92 -0
  138. lfx/components/cassandra/cassandra_graph.py +238 -0
  139. lfx/components/chains/__init__.py +3 -0
  140. lfx/components/chroma/__init__.py +34 -0
  141. lfx/components/chroma/chroma.py +167 -0
  142. lfx/components/cleanlab/__init__.py +40 -0
  143. lfx/components/cleanlab/cleanlab_evaluator.py +155 -0
  144. lfx/components/cleanlab/cleanlab_rag_evaluator.py +254 -0
  145. lfx/components/cleanlab/cleanlab_remediator.py +131 -0
  146. lfx/components/clickhouse/__init__.py +34 -0
  147. lfx/components/clickhouse/clickhouse.py +135 -0
  148. lfx/components/cloudflare/__init__.py +32 -0
  149. lfx/components/cloudflare/cloudflare.py +81 -0
  150. lfx/components/cohere/__init__.py +40 -0
  151. lfx/components/cohere/cohere_embeddings.py +81 -0
  152. lfx/components/cohere/cohere_models.py +46 -0
  153. lfx/components/cohere/cohere_rerank.py +51 -0
  154. lfx/components/composio/__init__.py +74 -0
  155. lfx/components/composio/composio_api.py +268 -0
  156. lfx/components/composio/dropbox_compnent.py +11 -0
  157. lfx/components/composio/github_composio.py +11 -0
  158. lfx/components/composio/gmail_composio.py +38 -0
  159. lfx/components/composio/googlecalendar_composio.py +11 -0
  160. lfx/components/composio/googlemeet_composio.py +11 -0
  161. lfx/components/composio/googletasks_composio.py +8 -0
  162. lfx/components/composio/linear_composio.py +11 -0
  163. lfx/components/composio/outlook_composio.py +11 -0
  164. lfx/components/composio/reddit_composio.py +11 -0
  165. lfx/components/composio/slack_composio.py +582 -0
  166. lfx/components/composio/slackbot_composio.py +11 -0
  167. lfx/components/composio/supabase_composio.py +11 -0
  168. lfx/components/composio/todoist_composio.py +11 -0
  169. lfx/components/composio/youtube_composio.py +11 -0
  170. lfx/components/confluence/__init__.py +3 -0
  171. lfx/components/confluence/confluence.py +84 -0
  172. lfx/components/couchbase/__init__.py +34 -0
  173. lfx/components/couchbase/couchbase.py +102 -0
  174. lfx/components/crewai/__init__.py +49 -0
  175. lfx/components/crewai/crewai.py +107 -0
  176. lfx/components/crewai/hierarchical_crew.py +46 -0
  177. lfx/components/crewai/hierarchical_task.py +44 -0
  178. lfx/components/crewai/sequential_crew.py +52 -0
  179. lfx/components/crewai/sequential_task.py +73 -0
  180. lfx/components/crewai/sequential_task_agent.py +143 -0
  181. lfx/components/custom_component/__init__.py +34 -0
  182. lfx/components/custom_component/custom_component.py +31 -0
  183. lfx/components/data/__init__.py +64 -0
  184. lfx/components/data/api_request.py +544 -0
  185. lfx/components/data/csv_to_data.py +95 -0
  186. lfx/components/data/directory.py +113 -0
  187. lfx/components/data/file.py +577 -0
  188. lfx/components/data/json_to_data.py +98 -0
  189. lfx/components/data/news_search.py +164 -0
  190. lfx/components/data/rss.py +69 -0
  191. lfx/components/data/sql_executor.py +101 -0
  192. lfx/components/data/url.py +311 -0
  193. lfx/components/data/web_search.py +112 -0
  194. lfx/components/data/webhook.py +56 -0
  195. lfx/components/datastax/__init__.py +70 -0
  196. lfx/components/datastax/astra_assistant_manager.py +306 -0
  197. lfx/components/datastax/astra_db.py +75 -0
  198. lfx/components/datastax/astra_vectorize.py +124 -0
  199. lfx/components/datastax/astradb.py +1285 -0
  200. lfx/components/datastax/astradb_cql.py +314 -0
  201. lfx/components/datastax/astradb_graph.py +330 -0
  202. lfx/components/datastax/astradb_tool.py +414 -0
  203. lfx/components/datastax/astradb_vectorstore.py +1285 -0
  204. lfx/components/datastax/cassandra.py +92 -0
  205. lfx/components/datastax/create_assistant.py +58 -0
  206. lfx/components/datastax/create_thread.py +32 -0
  207. lfx/components/datastax/dotenv.py +35 -0
  208. lfx/components/datastax/get_assistant.py +37 -0
  209. lfx/components/datastax/getenvvar.py +30 -0
  210. lfx/components/datastax/graph_rag.py +141 -0
  211. lfx/components/datastax/hcd.py +314 -0
  212. lfx/components/datastax/list_assistants.py +25 -0
  213. lfx/components/datastax/run.py +89 -0
  214. lfx/components/deactivated/__init__.py +15 -0
  215. lfx/components/deactivated/amazon_kendra.py +66 -0
  216. lfx/components/deactivated/chat_litellm_model.py +158 -0
  217. lfx/components/deactivated/code_block_extractor.py +26 -0
  218. lfx/components/deactivated/documents_to_data.py +22 -0
  219. lfx/components/deactivated/embed.py +16 -0
  220. lfx/components/deactivated/extract_key_from_data.py +46 -0
  221. lfx/components/deactivated/json_document_builder.py +57 -0
  222. lfx/components/deactivated/list_flows.py +20 -0
  223. lfx/components/deactivated/mcp_sse.py +61 -0
  224. lfx/components/deactivated/mcp_stdio.py +62 -0
  225. lfx/components/deactivated/merge_data.py +93 -0
  226. lfx/components/deactivated/message.py +37 -0
  227. lfx/components/deactivated/metal.py +54 -0
  228. lfx/components/deactivated/multi_query.py +59 -0
  229. lfx/components/deactivated/retriever.py +43 -0
  230. lfx/components/deactivated/selective_passthrough.py +77 -0
  231. lfx/components/deactivated/should_run_next.py +40 -0
  232. lfx/components/deactivated/split_text.py +63 -0
  233. lfx/components/deactivated/store_message.py +24 -0
  234. lfx/components/deactivated/sub_flow.py +124 -0
  235. lfx/components/deactivated/vectara_self_query.py +76 -0
  236. lfx/components/deactivated/vector_store.py +24 -0
  237. lfx/components/deepseek/__init__.py +34 -0
  238. lfx/components/deepseek/deepseek.py +136 -0
  239. lfx/components/docling/__init__.py +43 -0
  240. lfx/components/docling/chunk_docling_document.py +186 -0
  241. lfx/components/docling/docling_inline.py +231 -0
  242. lfx/components/docling/docling_remote.py +193 -0
  243. lfx/components/docling/export_docling_document.py +117 -0
  244. lfx/components/documentloaders/__init__.py +3 -0
  245. lfx/components/duckduckgo/__init__.py +3 -0
  246. lfx/components/duckduckgo/duck_duck_go_search_run.py +92 -0
  247. lfx/components/elastic/__init__.py +37 -0
  248. lfx/components/elastic/elasticsearch.py +267 -0
  249. lfx/components/elastic/opensearch.py +243 -0
  250. lfx/components/embeddings/__init__.py +37 -0
  251. lfx/components/embeddings/similarity.py +76 -0
  252. lfx/components/embeddings/text_embedder.py +64 -0
  253. lfx/components/exa/__init__.py +3 -0
  254. lfx/components/exa/exa_search.py +68 -0
  255. lfx/components/firecrawl/__init__.py +43 -0
  256. lfx/components/firecrawl/firecrawl_crawl_api.py +88 -0
  257. lfx/components/firecrawl/firecrawl_extract_api.py +136 -0
  258. lfx/components/firecrawl/firecrawl_map_api.py +89 -0
  259. lfx/components/firecrawl/firecrawl_scrape_api.py +73 -0
  260. lfx/components/git/__init__.py +4 -0
  261. lfx/components/git/git.py +262 -0
  262. lfx/components/git/gitextractor.py +196 -0
  263. lfx/components/glean/__init__.py +3 -0
  264. lfx/components/glean/glean_search_api.py +173 -0
  265. lfx/components/google/__init__.py +17 -0
  266. lfx/components/google/gmail.py +192 -0
  267. lfx/components/google/google_bq_sql_executor.py +157 -0
  268. lfx/components/google/google_drive.py +92 -0
  269. lfx/components/google/google_drive_search.py +152 -0
  270. lfx/components/google/google_generative_ai.py +147 -0
  271. lfx/components/google/google_generative_ai_embeddings.py +141 -0
  272. lfx/components/google/google_oauth_token.py +89 -0
  273. lfx/components/google/google_search_api_core.py +68 -0
  274. lfx/components/google/google_serper_api_core.py +74 -0
  275. lfx/components/groq/__init__.py +34 -0
  276. lfx/components/groq/groq.py +136 -0
  277. lfx/components/helpers/__init__.py +52 -0
  278. lfx/components/helpers/calculator_core.py +89 -0
  279. lfx/components/helpers/create_list.py +40 -0
  280. lfx/components/helpers/current_date.py +42 -0
  281. lfx/components/helpers/id_generator.py +42 -0
  282. lfx/components/helpers/memory.py +251 -0
  283. lfx/components/helpers/output_parser.py +45 -0
  284. lfx/components/helpers/store_message.py +90 -0
  285. lfx/components/homeassistant/__init__.py +7 -0
  286. lfx/components/homeassistant/home_assistant_control.py +152 -0
  287. lfx/components/homeassistant/list_home_assistant_states.py +137 -0
  288. lfx/components/huggingface/__init__.py +37 -0
  289. lfx/components/huggingface/huggingface.py +197 -0
  290. lfx/components/huggingface/huggingface_inference_api.py +106 -0
  291. lfx/components/ibm/__init__.py +34 -0
  292. lfx/components/ibm/watsonx.py +203 -0
  293. lfx/components/ibm/watsonx_embeddings.py +135 -0
  294. lfx/components/icosacomputing/__init__.py +5 -0
  295. lfx/components/icosacomputing/combinatorial_reasoner.py +84 -0
  296. lfx/components/input_output/__init__.py +38 -0
  297. lfx/components/input_output/chat.py +120 -0
  298. lfx/components/input_output/chat_output.py +200 -0
  299. lfx/components/input_output/text.py +27 -0
  300. lfx/components/input_output/text_output.py +29 -0
  301. lfx/components/jigsawstack/__init__.py +23 -0
  302. lfx/components/jigsawstack/ai_scrape.py +126 -0
  303. lfx/components/jigsawstack/ai_web_search.py +136 -0
  304. lfx/components/jigsawstack/file_read.py +115 -0
  305. lfx/components/jigsawstack/file_upload.py +94 -0
  306. lfx/components/jigsawstack/image_generation.py +205 -0
  307. lfx/components/jigsawstack/nsfw.py +60 -0
  308. lfx/components/jigsawstack/object_detection.py +124 -0
  309. lfx/components/jigsawstack/sentiment.py +112 -0
  310. lfx/components/jigsawstack/text_to_sql.py +90 -0
  311. lfx/components/jigsawstack/text_translate.py +77 -0
  312. lfx/components/jigsawstack/vocr.py +107 -0
  313. lfx/components/langchain_utilities/__init__.py +109 -0
  314. lfx/components/langchain_utilities/character.py +53 -0
  315. lfx/components/langchain_utilities/conversation.py +59 -0
  316. lfx/components/langchain_utilities/csv_agent.py +107 -0
  317. lfx/components/langchain_utilities/fake_embeddings.py +26 -0
  318. lfx/components/langchain_utilities/html_link_extractor.py +35 -0
  319. lfx/components/langchain_utilities/json_agent.py +45 -0
  320. lfx/components/langchain_utilities/langchain_hub.py +126 -0
  321. lfx/components/langchain_utilities/language_recursive.py +49 -0
  322. lfx/components/langchain_utilities/language_semantic.py +138 -0
  323. lfx/components/langchain_utilities/llm_checker.py +39 -0
  324. lfx/components/langchain_utilities/llm_math.py +42 -0
  325. lfx/components/langchain_utilities/natural_language.py +61 -0
  326. lfx/components/langchain_utilities/openai_tools.py +53 -0
  327. lfx/components/langchain_utilities/openapi.py +48 -0
  328. lfx/components/langchain_utilities/recursive_character.py +60 -0
  329. lfx/components/langchain_utilities/retrieval_qa.py +83 -0
  330. lfx/components/langchain_utilities/runnable_executor.py +137 -0
  331. lfx/components/langchain_utilities/self_query.py +80 -0
  332. lfx/components/langchain_utilities/spider.py +142 -0
  333. lfx/components/langchain_utilities/sql.py +40 -0
  334. lfx/components/langchain_utilities/sql_database.py +35 -0
  335. lfx/components/langchain_utilities/sql_generator.py +78 -0
  336. lfx/components/langchain_utilities/tool_calling.py +59 -0
  337. lfx/components/langchain_utilities/vector_store_info.py +49 -0
  338. lfx/components/langchain_utilities/vector_store_router.py +33 -0
  339. lfx/components/langchain_utilities/xml_agent.py +71 -0
  340. lfx/components/langwatch/__init__.py +3 -0
  341. lfx/components/langwatch/langwatch.py +278 -0
  342. lfx/components/link_extractors/__init__.py +3 -0
  343. lfx/components/lmstudio/__init__.py +34 -0
  344. lfx/components/lmstudio/lmstudioembeddings.py +89 -0
  345. lfx/components/lmstudio/lmstudiomodel.py +129 -0
  346. lfx/components/logic/__init__.py +52 -0
  347. lfx/components/logic/conditional_router.py +171 -0
  348. lfx/components/logic/data_conditional_router.py +125 -0
  349. lfx/components/logic/flow_tool.py +110 -0
  350. lfx/components/logic/listen.py +29 -0
  351. lfx/components/logic/loop.py +125 -0
  352. lfx/components/logic/notify.py +88 -0
  353. lfx/components/logic/pass_message.py +35 -0
  354. lfx/components/logic/run_flow.py +71 -0
  355. lfx/components/logic/sub_flow.py +114 -0
  356. lfx/components/maritalk/__init__.py +32 -0
  357. lfx/components/maritalk/maritalk.py +52 -0
  358. lfx/components/mem0/__init__.py +3 -0
  359. lfx/components/mem0/mem0_chat_memory.py +136 -0
  360. lfx/components/milvus/__init__.py +34 -0
  361. lfx/components/milvus/milvus.py +115 -0
  362. lfx/components/mistral/__init__.py +37 -0
  363. lfx/components/mistral/mistral.py +114 -0
  364. lfx/components/mistral/mistral_embeddings.py +58 -0
  365. lfx/components/models/__init__.py +34 -0
  366. lfx/components/models/embedding_model.py +114 -0
  367. lfx/components/models/language_model.py +144 -0
  368. lfx/components/mongodb/__init__.py +34 -0
  369. lfx/components/mongodb/mongodb_atlas.py +213 -0
  370. lfx/components/needle/__init__.py +3 -0
  371. lfx/components/needle/needle.py +104 -0
  372. lfx/components/notdiamond/__init__.py +34 -0
  373. lfx/components/notdiamond/notdiamond.py +228 -0
  374. lfx/components/novita/__init__.py +32 -0
  375. lfx/components/novita/novita.py +130 -0
  376. lfx/components/nvidia/__init__.py +57 -0
  377. lfx/components/nvidia/nvidia.py +157 -0
  378. lfx/components/nvidia/nvidia_embedding.py +77 -0
  379. lfx/components/nvidia/nvidia_ingest.py +317 -0
  380. lfx/components/nvidia/nvidia_rerank.py +63 -0
  381. lfx/components/nvidia/system_assist.py +65 -0
  382. lfx/components/olivya/__init__.py +3 -0
  383. lfx/components/olivya/olivya.py +116 -0
  384. lfx/components/ollama/__init__.py +37 -0
  385. lfx/components/ollama/ollama.py +330 -0
  386. lfx/components/ollama/ollama_embeddings.py +106 -0
  387. lfx/components/openai/__init__.py +37 -0
  388. lfx/components/openai/openai.py +100 -0
  389. lfx/components/openai/openai_chat_model.py +176 -0
  390. lfx/components/openrouter/__init__.py +32 -0
  391. lfx/components/openrouter/openrouter.py +202 -0
  392. lfx/components/output_parsers/__init__.py +3 -0
  393. lfx/components/perplexity/__init__.py +34 -0
  394. lfx/components/perplexity/perplexity.py +75 -0
  395. lfx/components/pgvector/__init__.py +34 -0
  396. lfx/components/pgvector/pgvector.py +72 -0
  397. lfx/components/pinecone/__init__.py +34 -0
  398. lfx/components/pinecone/pinecone.py +134 -0
  399. lfx/components/processing/__init__.py +117 -0
  400. lfx/components/processing/alter_metadata.py +108 -0
  401. lfx/components/processing/batch_run.py +205 -0
  402. lfx/components/processing/combine_text.py +39 -0
  403. lfx/components/processing/converter.py +159 -0
  404. lfx/components/processing/create_data.py +110 -0
  405. lfx/components/processing/data_operations.py +438 -0
  406. lfx/components/processing/data_to_dataframe.py +70 -0
  407. lfx/components/processing/dataframe_operations.py +313 -0
  408. lfx/components/processing/extract_key.py +53 -0
  409. lfx/components/processing/filter_data.py +42 -0
  410. lfx/components/processing/filter_data_values.py +88 -0
  411. lfx/components/processing/json_cleaner.py +103 -0
  412. lfx/components/processing/lambda_filter.py +154 -0
  413. lfx/components/processing/llm_router.py +499 -0
  414. lfx/components/processing/merge_data.py +90 -0
  415. lfx/components/processing/message_to_data.py +36 -0
  416. lfx/components/processing/parse_data.py +70 -0
  417. lfx/components/processing/parse_dataframe.py +68 -0
  418. lfx/components/processing/parse_json_data.py +90 -0
  419. lfx/components/processing/parser.py +143 -0
  420. lfx/components/processing/prompt.py +67 -0
  421. lfx/components/processing/python_repl_core.py +98 -0
  422. lfx/components/processing/regex.py +82 -0
  423. lfx/components/processing/save_file.py +225 -0
  424. lfx/components/processing/select_data.py +48 -0
  425. lfx/components/processing/split_text.py +141 -0
  426. lfx/components/processing/structured_output.py +202 -0
  427. lfx/components/processing/update_data.py +160 -0
  428. lfx/components/prototypes/__init__.py +34 -0
  429. lfx/components/prototypes/python_function.py +73 -0
  430. lfx/components/qdrant/__init__.py +34 -0
  431. lfx/components/qdrant/qdrant.py +109 -0
  432. lfx/components/redis/__init__.py +37 -0
  433. lfx/components/redis/redis.py +89 -0
  434. lfx/components/redis/redis_chat.py +43 -0
  435. lfx/components/sambanova/__init__.py +32 -0
  436. lfx/components/sambanova/sambanova.py +84 -0
  437. lfx/components/scrapegraph/__init__.py +40 -0
  438. lfx/components/scrapegraph/scrapegraph_markdownify_api.py +64 -0
  439. lfx/components/scrapegraph/scrapegraph_search_api.py +64 -0
  440. lfx/components/scrapegraph/scrapegraph_smart_scraper_api.py +71 -0
  441. lfx/components/searchapi/__init__.py +34 -0
  442. lfx/components/searchapi/search.py +79 -0
  443. lfx/components/serpapi/__init__.py +3 -0
  444. lfx/components/serpapi/serp.py +115 -0
  445. lfx/components/supabase/__init__.py +34 -0
  446. lfx/components/supabase/supabase.py +76 -0
  447. lfx/components/tavily/__init__.py +4 -0
  448. lfx/components/tavily/tavily_extract.py +117 -0
  449. lfx/components/tavily/tavily_search.py +212 -0
  450. lfx/components/textsplitters/__init__.py +3 -0
  451. lfx/components/toolkits/__init__.py +3 -0
  452. lfx/components/tools/__init__.py +72 -0
  453. lfx/components/tools/calculator.py +108 -0
  454. lfx/components/tools/google_search_api.py +45 -0
  455. lfx/components/tools/google_serper_api.py +115 -0
  456. lfx/components/tools/python_code_structured_tool.py +327 -0
  457. lfx/components/tools/python_repl.py +97 -0
  458. lfx/components/tools/search_api.py +87 -0
  459. lfx/components/tools/searxng.py +145 -0
  460. lfx/components/tools/serp_api.py +119 -0
  461. lfx/components/tools/tavily_search_tool.py +344 -0
  462. lfx/components/tools/wikidata_api.py +102 -0
  463. lfx/components/tools/wikipedia_api.py +49 -0
  464. lfx/components/tools/yahoo_finance.py +129 -0
  465. lfx/components/twelvelabs/__init__.py +52 -0
  466. lfx/components/twelvelabs/convert_astra_results.py +84 -0
  467. lfx/components/twelvelabs/pegasus_index.py +311 -0
  468. lfx/components/twelvelabs/split_video.py +291 -0
  469. lfx/components/twelvelabs/text_embeddings.py +57 -0
  470. lfx/components/twelvelabs/twelvelabs_pegasus.py +408 -0
  471. lfx/components/twelvelabs/video_embeddings.py +100 -0
  472. lfx/components/twelvelabs/video_file.py +179 -0
  473. lfx/components/unstructured/__init__.py +3 -0
  474. lfx/components/unstructured/unstructured.py +121 -0
  475. lfx/components/upstash/__init__.py +34 -0
  476. lfx/components/upstash/upstash.py +124 -0
  477. lfx/components/vectara/__init__.py +37 -0
  478. lfx/components/vectara/vectara.py +97 -0
  479. lfx/components/vectara/vectara_rag.py +164 -0
  480. lfx/components/vectorstores/__init__.py +40 -0
  481. lfx/components/vectorstores/astradb.py +1285 -0
  482. lfx/components/vectorstores/astradb_graph.py +319 -0
  483. lfx/components/vectorstores/cassandra.py +264 -0
  484. lfx/components/vectorstores/cassandra_graph.py +238 -0
  485. lfx/components/vectorstores/chroma.py +167 -0
  486. lfx/components/vectorstores/clickhouse.py +135 -0
  487. lfx/components/vectorstores/couchbase.py +102 -0
  488. lfx/components/vectorstores/elasticsearch.py +267 -0
  489. lfx/components/vectorstores/faiss.py +111 -0
  490. lfx/components/vectorstores/graph_rag.py +141 -0
  491. lfx/components/vectorstores/hcd.py +314 -0
  492. lfx/components/vectorstores/local_db.py +261 -0
  493. lfx/components/vectorstores/milvus.py +115 -0
  494. lfx/components/vectorstores/mongodb_atlas.py +213 -0
  495. lfx/components/vectorstores/opensearch.py +243 -0
  496. lfx/components/vectorstores/pgvector.py +72 -0
  497. lfx/components/vectorstores/pinecone.py +134 -0
  498. lfx/components/vectorstores/qdrant.py +109 -0
  499. lfx/components/vectorstores/supabase.py +76 -0
  500. lfx/components/vectorstores/upstash.py +124 -0
  501. lfx/components/vectorstores/vectara.py +97 -0
  502. lfx/components/vectorstores/vectara_rag.py +164 -0
  503. lfx/components/vectorstores/weaviate.py +89 -0
  504. lfx/components/vertexai/__init__.py +37 -0
  505. lfx/components/vertexai/vertexai.py +71 -0
  506. lfx/components/vertexai/vertexai_embeddings.py +67 -0
  507. lfx/components/weaviate/__init__.py +34 -0
  508. lfx/components/weaviate/weaviate.py +89 -0
  509. lfx/components/wikipedia/__init__.py +4 -0
  510. lfx/components/wikipedia/wikidata.py +86 -0
  511. lfx/components/wikipedia/wikipedia.py +53 -0
  512. lfx/components/wolframalpha/__init__.py +3 -0
  513. lfx/components/wolframalpha/wolfram_alpha_api.py +54 -0
  514. lfx/components/xai/__init__.py +32 -0
  515. lfx/components/xai/xai.py +167 -0
  516. lfx/components/yahoosearch/__init__.py +3 -0
  517. lfx/components/yahoosearch/yahoo.py +137 -0
  518. lfx/components/youtube/__init__.py +52 -0
  519. lfx/components/youtube/channel.py +227 -0
  520. lfx/components/youtube/comments.py +231 -0
  521. lfx/components/youtube/playlist.py +33 -0
  522. lfx/components/youtube/search.py +120 -0
  523. lfx/components/youtube/trending.py +285 -0
  524. lfx/components/youtube/video_details.py +263 -0
  525. lfx/components/youtube/youtube_transcripts.py +118 -0
  526. lfx/components/zep/__init__.py +3 -0
  527. lfx/components/zep/zep.py +44 -0
  528. lfx/constants.py +6 -0
  529. lfx/custom/__init__.py +7 -0
  530. lfx/custom/attributes.py +86 -0
  531. lfx/custom/code_parser/__init__.py +3 -0
  532. lfx/custom/code_parser/code_parser.py +361 -0
  533. lfx/custom/custom_component/__init__.py +0 -0
  534. lfx/custom/custom_component/base_component.py +128 -0
  535. lfx/custom/custom_component/component.py +1808 -0
  536. lfx/custom/custom_component/component_with_cache.py +8 -0
  537. lfx/custom/custom_component/custom_component.py +588 -0
  538. lfx/custom/dependency_analyzer.py +165 -0
  539. lfx/custom/directory_reader/__init__.py +3 -0
  540. lfx/custom/directory_reader/directory_reader.py +359 -0
  541. lfx/custom/directory_reader/utils.py +171 -0
  542. lfx/custom/eval.py +12 -0
  543. lfx/custom/schema.py +32 -0
  544. lfx/custom/tree_visitor.py +21 -0
  545. lfx/custom/utils.py +877 -0
  546. lfx/custom/validate.py +488 -0
  547. lfx/events/__init__.py +1 -0
  548. lfx/events/event_manager.py +110 -0
  549. lfx/exceptions/__init__.py +0 -0
  550. lfx/exceptions/component.py +15 -0
  551. lfx/field_typing/__init__.py +91 -0
  552. lfx/field_typing/constants.py +215 -0
  553. lfx/field_typing/range_spec.py +35 -0
  554. lfx/graph/__init__.py +6 -0
  555. lfx/graph/edge/__init__.py +0 -0
  556. lfx/graph/edge/base.py +277 -0
  557. lfx/graph/edge/schema.py +119 -0
  558. lfx/graph/edge/utils.py +0 -0
  559. lfx/graph/graph/__init__.py +0 -0
  560. lfx/graph/graph/ascii.py +202 -0
  561. lfx/graph/graph/base.py +2238 -0
  562. lfx/graph/graph/constants.py +63 -0
  563. lfx/graph/graph/runnable_vertices_manager.py +133 -0
  564. lfx/graph/graph/schema.py +52 -0
  565. lfx/graph/graph/state_model.py +66 -0
  566. lfx/graph/graph/utils.py +1024 -0
  567. lfx/graph/schema.py +75 -0
  568. lfx/graph/state/__init__.py +0 -0
  569. lfx/graph/state/model.py +237 -0
  570. lfx/graph/utils.py +200 -0
  571. lfx/graph/vertex/__init__.py +0 -0
  572. lfx/graph/vertex/base.py +823 -0
  573. lfx/graph/vertex/constants.py +0 -0
  574. lfx/graph/vertex/exceptions.py +4 -0
  575. lfx/graph/vertex/param_handler.py +264 -0
  576. lfx/graph/vertex/schema.py +26 -0
  577. lfx/graph/vertex/utils.py +19 -0
  578. lfx/graph/vertex/vertex_types.py +489 -0
  579. lfx/helpers/__init__.py +1 -0
  580. lfx/helpers/base_model.py +71 -0
  581. lfx/helpers/custom.py +13 -0
  582. lfx/helpers/data.py +167 -0
  583. lfx/helpers/flow.py +194 -0
  584. lfx/inputs/__init__.py +68 -0
  585. lfx/inputs/constants.py +2 -0
  586. lfx/inputs/input_mixin.py +328 -0
  587. lfx/inputs/inputs.py +714 -0
  588. lfx/inputs/validators.py +19 -0
  589. lfx/interface/__init__.py +6 -0
  590. lfx/interface/components.py +489 -0
  591. lfx/interface/importing/__init__.py +5 -0
  592. lfx/interface/importing/utils.py +39 -0
  593. lfx/interface/initialize/__init__.py +3 -0
  594. lfx/interface/initialize/loading.py +224 -0
  595. lfx/interface/listing.py +26 -0
  596. lfx/interface/run.py +16 -0
  597. lfx/interface/utils.py +111 -0
  598. lfx/io/__init__.py +63 -0
  599. lfx/io/schema.py +289 -0
  600. lfx/load/__init__.py +8 -0
  601. lfx/load/load.py +256 -0
  602. lfx/load/utils.py +99 -0
  603. lfx/log/__init__.py +5 -0
  604. lfx/log/logger.py +385 -0
  605. lfx/memory/__init__.py +90 -0
  606. lfx/memory/stubs.py +283 -0
  607. lfx/processing/__init__.py +1 -0
  608. lfx/processing/process.py +238 -0
  609. lfx/processing/utils.py +25 -0
  610. lfx/py.typed +0 -0
  611. lfx/schema/__init__.py +66 -0
  612. lfx/schema/artifact.py +83 -0
  613. lfx/schema/content_block.py +62 -0
  614. lfx/schema/content_types.py +91 -0
  615. lfx/schema/data.py +308 -0
  616. lfx/schema/dataframe.py +210 -0
  617. lfx/schema/dotdict.py +74 -0
  618. lfx/schema/encoders.py +13 -0
  619. lfx/schema/graph.py +47 -0
  620. lfx/schema/image.py +131 -0
  621. lfx/schema/json_schema.py +141 -0
  622. lfx/schema/log.py +61 -0
  623. lfx/schema/message.py +473 -0
  624. lfx/schema/openai_responses_schemas.py +74 -0
  625. lfx/schema/properties.py +41 -0
  626. lfx/schema/schema.py +171 -0
  627. lfx/schema/serialize.py +13 -0
  628. lfx/schema/table.py +140 -0
  629. lfx/schema/validators.py +114 -0
  630. lfx/serialization/__init__.py +5 -0
  631. lfx/serialization/constants.py +2 -0
  632. lfx/serialization/serialization.py +314 -0
  633. lfx/services/__init__.py +23 -0
  634. lfx/services/base.py +28 -0
  635. lfx/services/cache/__init__.py +6 -0
  636. lfx/services/cache/base.py +183 -0
  637. lfx/services/cache/service.py +166 -0
  638. lfx/services/cache/utils.py +169 -0
  639. lfx/services/chat/__init__.py +1 -0
  640. lfx/services/chat/config.py +2 -0
  641. lfx/services/chat/schema.py +10 -0
  642. lfx/services/deps.py +129 -0
  643. lfx/services/factory.py +19 -0
  644. lfx/services/initialize.py +19 -0
  645. lfx/services/interfaces.py +103 -0
  646. lfx/services/manager.py +172 -0
  647. lfx/services/schema.py +20 -0
  648. lfx/services/session.py +82 -0
  649. lfx/services/settings/__init__.py +3 -0
  650. lfx/services/settings/auth.py +130 -0
  651. lfx/services/settings/base.py +539 -0
  652. lfx/services/settings/constants.py +31 -0
  653. lfx/services/settings/factory.py +23 -0
  654. lfx/services/settings/feature_flags.py +12 -0
  655. lfx/services/settings/service.py +35 -0
  656. lfx/services/settings/utils.py +40 -0
  657. lfx/services/shared_component_cache/__init__.py +1 -0
  658. lfx/services/shared_component_cache/factory.py +30 -0
  659. lfx/services/shared_component_cache/service.py +9 -0
  660. lfx/services/storage/__init__.py +5 -0
  661. lfx/services/storage/local.py +155 -0
  662. lfx/services/storage/service.py +54 -0
  663. lfx/services/tracing/__init__.py +1 -0
  664. lfx/services/tracing/service.py +21 -0
  665. lfx/settings.py +6 -0
  666. lfx/template/__init__.py +6 -0
  667. lfx/template/field/__init__.py +0 -0
  668. lfx/template/field/base.py +257 -0
  669. lfx/template/field/prompt.py +15 -0
  670. lfx/template/frontend_node/__init__.py +6 -0
  671. lfx/template/frontend_node/base.py +212 -0
  672. lfx/template/frontend_node/constants.py +65 -0
  673. lfx/template/frontend_node/custom_components.py +79 -0
  674. lfx/template/template/__init__.py +0 -0
  675. lfx/template/template/base.py +100 -0
  676. lfx/template/utils.py +217 -0
  677. lfx/type_extraction/__init__.py +19 -0
  678. lfx/type_extraction/type_extraction.py +75 -0
  679. lfx/type_extraction.py +80 -0
  680. lfx/utils/__init__.py +1 -0
  681. lfx/utils/async_helpers.py +42 -0
  682. lfx/utils/component_utils.py +154 -0
  683. lfx/utils/concurrency.py +60 -0
  684. lfx/utils/connection_string_parser.py +11 -0
  685. lfx/utils/constants.py +205 -0
  686. lfx/utils/data_structure.py +212 -0
  687. lfx/utils/exceptions.py +22 -0
  688. lfx/utils/helpers.py +28 -0
  689. lfx/utils/image.py +73 -0
  690. lfx/utils/lazy_load.py +15 -0
  691. lfx/utils/request_utils.py +18 -0
  692. lfx/utils/schemas.py +139 -0
  693. lfx/utils/util.py +481 -0
  694. lfx/utils/util_strings.py +56 -0
  695. lfx/utils/version.py +24 -0
  696. lfx_nightly-0.1.11.dev0.dist-info/METADATA +293 -0
  697. lfx_nightly-0.1.11.dev0.dist-info/RECORD +699 -0
  698. lfx_nightly-0.1.11.dev0.dist-info/WHEEL +4 -0
  699. lfx_nightly-0.1.11.dev0.dist-info/entry_points.txt +2 -0
lfx/custom/utils.py ADDED
@@ -0,0 +1,877 @@
1
+ # mypy: ignore-errors
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ import asyncio
6
+ import contextlib
7
+ import hashlib
8
+ import inspect
9
+ import re
10
+ import traceback
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ from fastapi import HTTPException
15
+ from pydantic import BaseModel
16
+
17
+ from lfx.custom import validate
18
+ from lfx.custom.custom_component.component import Component
19
+ from lfx.custom.custom_component.custom_component import CustomComponent
20
+ from lfx.custom.dependency_analyzer import analyze_component_dependencies
21
+ from lfx.custom.directory_reader.utils import (
22
+ abuild_custom_component_list_from_path,
23
+ build_custom_component_list_from_path,
24
+ merge_nested_dicts_with_renaming,
25
+ )
26
+ from lfx.custom.eval import eval_custom_component_code
27
+ from lfx.custom.schema import MissingDefault
28
+ from lfx.field_typing.range_spec import RangeSpec
29
+ from lfx.helpers.custom import format_type
30
+ from lfx.log.logger import logger
31
+ from lfx.schema.dotdict import dotdict
32
+ from lfx.template.field.base import Input
33
+ from lfx.template.frontend_node.custom_components import ComponentFrontendNode, CustomComponentFrontendNode
34
+ from lfx.type_extraction.type_extraction import extract_inner_type
35
+ from lfx.utils.util import get_base_classes
36
+
37
+ if TYPE_CHECKING:
38
+ from uuid import UUID
39
+
40
+ from lfx.custom.custom_component.custom_component import CustomComponent
41
+
42
+
43
+ def _generate_code_hash(source_code: str, modname: str) -> str:
44
+ """Generate a hash of the component source code.
45
+
46
+ Args:
47
+ source_code: The source code string
48
+ modname: The module name for context
49
+
50
+ Returns:
51
+ SHA256 hash of the source code
52
+
53
+ Raises:
54
+ ValueError: If source_code is empty or None
55
+ UnicodeEncodeError: If source_code cannot be encoded
56
+ TypeError: If source_code is not a string
57
+ """
58
+ if not isinstance(source_code, str):
59
+ msg = "Source code must be a string"
60
+ raise TypeError(msg)
61
+
62
+ if not source_code:
63
+ msg = f"Empty source code for {modname}"
64
+ raise ValueError(msg)
65
+
66
+ # Generate SHA256 hash of the source code
67
+ return hashlib.sha256(source_code.encode("utf-8")).hexdigest()[:12] # First 12 chars for brevity
68
+
69
+
70
+ class UpdateBuildConfigError(Exception):
71
+ pass
72
+
73
+
74
+ def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: list[str]) -> None:
75
+ """Add output types to the frontend node."""
76
+ for return_type in return_types:
77
+ if return_type is None:
78
+ raise HTTPException(
79
+ status_code=400,
80
+ detail={
81
+ "error": ("Invalid return type. Please check your code and try again."),
82
+ "traceback": traceback.format_exc(),
83
+ },
84
+ )
85
+ if return_type is str:
86
+ return_type_ = "Text"
87
+ elif hasattr(return_type, "__name__"):
88
+ return_type_ = return_type.__name__
89
+ elif hasattr(return_type, "__class__"):
90
+ return_type_ = return_type.__class__.__name__
91
+ else:
92
+ return_type_ = str(return_type)
93
+
94
+ frontend_node.add_output_type(return_type_)
95
+
96
+
97
+ def reorder_fields(frontend_node: CustomComponentFrontendNode, field_order: list[str]) -> None:
98
+ """Reorder fields in the frontend node based on the specified field_order."""
99
+ if not field_order:
100
+ return
101
+
102
+ # Create a dictionary for O(1) lookup time.
103
+ field_dict = {field.name: field for field in frontend_node.template.fields}
104
+ reordered_fields = [field_dict[name] for name in field_order if name in field_dict]
105
+ # Add any fields that are not in the field_order list
106
+ reordered_fields.extend(field for field in frontend_node.template.fields if field.name not in field_order)
107
+ frontend_node.template.fields = reordered_fields
108
+ frontend_node.field_order = field_order
109
+
110
+
111
+ def add_base_classes(frontend_node: CustomComponentFrontendNode, return_types: list[str]) -> None:
112
+ """Add base classes to the frontend node."""
113
+ for return_type_instance in return_types:
114
+ if return_type_instance is None:
115
+ raise HTTPException(
116
+ status_code=400,
117
+ detail={
118
+ "error": ("Invalid return type. Please check your code and try again."),
119
+ "traceback": traceback.format_exc(),
120
+ },
121
+ )
122
+
123
+ base_classes = get_base_classes(return_type_instance)
124
+ if return_type_instance is str:
125
+ base_classes.append("Text")
126
+
127
+ for base_class in base_classes:
128
+ frontend_node.add_base_class(base_class)
129
+
130
+
131
+ def extract_type_from_optional(field_type):
132
+ """Extract the type from a string formatted as "Optional[<type>]".
133
+
134
+ Parameters:
135
+ field_type (str): The string from which to extract the type.
136
+
137
+ Returns:
138
+ str: The extracted type, or an empty string if no type was found.
139
+ """
140
+ if "optional" not in field_type.lower():
141
+ return field_type
142
+ match = re.search(r"\[(.*?)\]$", field_type)
143
+ return match[1] if match else field_type
144
+
145
+
146
+ def get_field_properties(extra_field):
147
+ """Get the properties of an extra field."""
148
+ field_name = extra_field["name"]
149
+ field_type = extra_field.get("type", "str")
150
+ field_value = extra_field.get("default", "")
151
+ # a required field is a field that does not contain
152
+ # optional in field_type
153
+ # and a field that does not have a default value
154
+ field_required = "optional" not in field_type.lower() and isinstance(field_value, MissingDefault)
155
+ field_value = field_value if not isinstance(field_value, MissingDefault) else None
156
+
157
+ if not field_required:
158
+ field_type = extract_type_from_optional(field_type)
159
+ if field_value is not None:
160
+ with contextlib.suppress(Exception):
161
+ field_value = ast.literal_eval(field_value)
162
+ return field_name, field_type, field_value, field_required
163
+
164
+
165
+ def process_type(field_type: str):
166
+ if field_type.startswith(("list", "List")):
167
+ return extract_inner_type(field_type)
168
+
169
+ # field_type is a string can be Prompt or Code too
170
+ # so we just need to lower if it is the case
171
+ lowercase_type = field_type.lower()
172
+ if lowercase_type in {"prompt", "code"}:
173
+ return lowercase_type
174
+ return field_type
175
+
176
+
177
+ def add_new_custom_field(
178
+ *,
179
+ frontend_node: CustomComponentFrontendNode,
180
+ field_name: str,
181
+ field_type: str,
182
+ field_value: Any,
183
+ field_required: bool,
184
+ field_config: dict,
185
+ ):
186
+ # Check field_config if any of the keys are in it
187
+ # if it is, update the value
188
+ display_name = field_config.pop("display_name", None)
189
+ if not field_type:
190
+ if "type" in field_config and field_config["type"] is not None:
191
+ field_type = field_config.pop("type")
192
+ elif "field_type" in field_config and field_config["field_type"] is not None:
193
+ field_type = field_config.pop("field_type")
194
+ field_contains_list = "list" in field_type.lower()
195
+ field_type = process_type(field_type)
196
+ field_value = field_config.pop("value", field_value)
197
+ field_advanced = field_config.pop("advanced", False)
198
+
199
+ if field_type == "Dict":
200
+ field_type = "dict"
201
+
202
+ if field_type == "bool" and field_value is None:
203
+ field_value = False
204
+
205
+ if field_type == "SecretStr":
206
+ field_config["password"] = True
207
+ field_config["load_from_db"] = True
208
+ field_config["input_types"] = ["Text"]
209
+
210
+ # If options is a list, then it's a dropdown or multiselect
211
+ # If options is None, then it's a list of strings
212
+ is_list = isinstance(field_config.get("options"), list)
213
+ field_config["is_list"] = is_list or field_config.get("list", False) or field_contains_list
214
+
215
+ if "name" in field_config:
216
+ logger.warning("The 'name' key in field_config is used to build the object and can't be changed.")
217
+ required = field_config.pop("required", field_required)
218
+ placeholder = field_config.pop("placeholder", "")
219
+
220
+ new_field = Input(
221
+ name=field_name,
222
+ field_type=field_type,
223
+ value=field_value,
224
+ show=True,
225
+ required=required,
226
+ advanced=field_advanced,
227
+ placeholder=placeholder,
228
+ display_name=display_name,
229
+ **sanitize_field_config(field_config),
230
+ )
231
+ frontend_node.template.upsert_field(field_name, new_field)
232
+ if isinstance(frontend_node.custom_fields, dict):
233
+ frontend_node.custom_fields[field_name] = None
234
+
235
+ return frontend_node
236
+
237
+
238
+ def add_extra_fields(frontend_node, field_config, function_args) -> None:
239
+ """Add extra fields to the frontend node."""
240
+ if not function_args:
241
+ return
242
+ field_config_ = field_config.copy()
243
+ function_args_names = [arg["name"] for arg in function_args]
244
+ # If kwargs is in the function_args and not all field_config keys are in function_args
245
+ # then we need to add the extra fields
246
+
247
+ for extra_field in function_args:
248
+ if "name" not in extra_field or extra_field["name"] in {
249
+ "self",
250
+ "kwargs",
251
+ "args",
252
+ }:
253
+ continue
254
+
255
+ field_name, field_type, field_value, field_required = get_field_properties(extra_field)
256
+ config = field_config_.pop(field_name, {})
257
+ frontend_node = add_new_custom_field(
258
+ frontend_node=frontend_node,
259
+ field_name=field_name,
260
+ field_type=field_type,
261
+ field_value=field_value,
262
+ field_required=field_required,
263
+ field_config=config,
264
+ )
265
+ if "kwargs" in function_args_names and not all(key in function_args_names for key in field_config):
266
+ for field_name, config in field_config_.items():
267
+ if "name" not in config or field_name == "code":
268
+ continue
269
+ config_ = config.model_dump() if isinstance(config, BaseModel) else config
270
+ field_name_, field_type, field_value, field_required = get_field_properties(extra_field=config_)
271
+ frontend_node = add_new_custom_field(
272
+ frontend_node=frontend_node,
273
+ field_name=field_name_,
274
+ field_type=field_type,
275
+ field_value=field_value,
276
+ field_required=field_required,
277
+ field_config=config_,
278
+ )
279
+
280
+
281
+ def get_field_dict(field: Input | dict):
282
+ """Get the field dictionary from a Input or a dict."""
283
+ if isinstance(field, Input):
284
+ return dotdict(field.model_dump(by_alias=True, exclude_none=True))
285
+ return field
286
+
287
+
288
+ def run_build_inputs(
289
+ custom_component: Component,
290
+ ):
291
+ """Run the build inputs of a custom component."""
292
+ try:
293
+ return custom_component.build_inputs()
294
+ # add_extra_fields(frontend_node, field_config, field_config.values())
295
+ except Exception as exc:
296
+ logger.exception("Error running build inputs")
297
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
298
+
299
+
300
+ def get_component_instance(custom_component: CustomComponent | Component, user_id: str | UUID | None = None):
301
+ """Returns an instance of a custom component, evaluating its code if necessary.
302
+
303
+ If the input is already an instance of `Component` or `CustomComponent`, it is returned directly.
304
+ Otherwise, the function evaluates the component's code to create and return an instance. Raises an
305
+ HTTP 400 error if the code is missing, invalid, or instantiation fails.
306
+ """
307
+ # Fast path: avoid repeated str comparisons
308
+
309
+ code = custom_component._code
310
+ if not isinstance(code, str):
311
+ # Only two failure cases: None, or other non-str
312
+ error = "Code is None" if code is None else "Invalid code type"
313
+ msg = f"Invalid type conversion: {error}. Please check your code and try again."
314
+ logger.error(msg)
315
+ raise HTTPException(status_code=400, detail={"error": msg})
316
+
317
+ # Only now, try to process expensive exception/log traceback only *if needed*
318
+ try:
319
+ custom_class = eval_custom_component_code(code)
320
+ except Exception as exc:
321
+ # Only generate traceback if an error occurs (save time on success)
322
+ tb = traceback.format_exc()
323
+ logger.error("Error while evaluating custom component code\n%s", tb)
324
+ raise HTTPException(
325
+ status_code=400,
326
+ detail={
327
+ "error": "Invalid type conversion. Please check your code and try again.",
328
+ "traceback": tb,
329
+ },
330
+ ) from exc
331
+
332
+ try:
333
+ return custom_class(_user_id=user_id, _code=code)
334
+ except Exception as exc:
335
+ tb = traceback.format_exc()
336
+ logger.error("Error while instantiating custom component\n%s", tb)
337
+ # Only log inner traceback if present in 'detail'
338
+ detail_tb = getattr(exc, "detail", {}).get("traceback", None)
339
+ if detail_tb is not None:
340
+ logger.error(detail_tb)
341
+ raise
342
+
343
+
344
+ def is_a_preimported_component(custom_component: CustomComponent):
345
+ """Check if the component is a preimported component."""
346
+ klass = type(custom_component)
347
+ # This avoids double type lookups, and may speed up the common-case short-circuit
348
+ return issubclass(klass, Component) and klass is not Component
349
+
350
+
351
+ def run_build_config(
352
+ custom_component: CustomComponent,
353
+ user_id: str | UUID | None = None,
354
+ ) -> tuple[dict, CustomComponent]:
355
+ """Builds the field configuration dictionary for a custom component.
356
+
357
+ If the input is an instance of a subclass of Component (excluding Component itself), returns its
358
+ build configuration and the instance. Otherwise, evaluates the component's code to create an instance,
359
+ calls its build_config method, and processes any RangeSpec objects in the configuration. Raises an
360
+ HTTP 400 error if the code is missing or invalid, or if instantiation or configuration building fails.
361
+
362
+ Returns:
363
+ A tuple containing the field configuration dictionary and the component instance.
364
+ """
365
+ # Check if the instance's class is a subclass of Component (but not Component itself)
366
+ # If we have a Component that is a subclass of Component, that means
367
+ # we have imported it
368
+ # If not, it means the component was loaded through LANGFLOW_COMPONENTS_PATH
369
+ # and loaded from a file
370
+ if is_a_preimported_component(custom_component):
371
+ return custom_component.build_config(), custom_component
372
+
373
+ if custom_component._code is None:
374
+ error = "Code is None"
375
+ elif not isinstance(custom_component._code, str):
376
+ error = "Invalid code type"
377
+ else:
378
+ try:
379
+ custom_class = eval_custom_component_code(custom_component._code)
380
+ except Exception as exc:
381
+ logger.exception("Error while evaluating custom component code")
382
+ raise HTTPException(
383
+ status_code=400,
384
+ detail={
385
+ "error": ("Invalid type conversion. Please check your code and try again."),
386
+ "traceback": traceback.format_exc(),
387
+ },
388
+ ) from exc
389
+
390
+ try:
391
+ custom_instance = custom_class(_user_id=user_id)
392
+ build_config: dict = custom_instance.build_config()
393
+
394
+ for field_name, field in build_config.copy().items():
395
+ # Allow user to build Input as well
396
+ # as a dict with the same keys as Input
397
+ field_dict = get_field_dict(field)
398
+ # Let's check if "rangeSpec" is a RangeSpec object
399
+ if "rangeSpec" in field_dict and isinstance(field_dict["rangeSpec"], RangeSpec):
400
+ field_dict["rangeSpec"] = field_dict["rangeSpec"].model_dump()
401
+ build_config[field_name] = field_dict
402
+
403
+ except Exception as exc:
404
+ logger.exception("Error while building field config")
405
+ if hasattr(exc, "detail") and "traceback" in exc.detail:
406
+ logger.error(exc.detail["traceback"])
407
+ raise
408
+ return build_config, custom_instance
409
+
410
+ msg = f"Invalid type conversion: {error}. Please check your code and try again."
411
+ logger.error(msg)
412
+ raise HTTPException(
413
+ status_code=400,
414
+ detail={"error": msg},
415
+ )
416
+
417
+
418
+ def add_code_field(frontend_node: CustomComponentFrontendNode, raw_code):
419
+ code_field = Input(
420
+ dynamic=True,
421
+ required=True,
422
+ placeholder="",
423
+ multiline=True,
424
+ value=raw_code,
425
+ password=False,
426
+ name="code",
427
+ advanced=True,
428
+ field_type="code",
429
+ is_list=False,
430
+ )
431
+ frontend_node.template.add_field(code_field)
432
+
433
+ return frontend_node
434
+
435
+
436
+ def add_code_field_to_build_config(build_config: dict, raw_code: str):
437
+ build_config["code"] = Input(
438
+ dynamic=True,
439
+ required=True,
440
+ placeholder="",
441
+ multiline=True,
442
+ value=raw_code,
443
+ password=False,
444
+ name="code",
445
+ advanced=True,
446
+ field_type="code",
447
+ is_list=False,
448
+ ).model_dump()
449
+ return build_config
450
+
451
+
452
+ def get_module_name_from_display_name(display_name: str):
453
+ """Get the module name from the display name."""
454
+ # Convert display name to snake_case for Python module name
455
+ # e.g., "Custom Component" -> "custom_component"
456
+ # Remove extra spaces and convert to lowercase
457
+ cleaned_name = re.sub(r"\s+", " ", display_name.strip())
458
+ # Replace spaces with underscores and convert to lowercase
459
+ module_name = cleaned_name.replace(" ", "_").lower()
460
+ # Remove any non-alphanumeric characters except underscores
461
+ return re.sub(r"[^a-z0-9_]", "", module_name)
462
+
463
+
464
+ def build_custom_component_template_from_inputs(
465
+ custom_component: Component | CustomComponent, user_id: str | UUID | None = None, module_name: str | None = None
466
+ ):
467
+ # The List of Inputs fills the role of the build_config and the entrypoint_args
468
+ """Builds a frontend node template from a custom component using its input-based configuration.
469
+
470
+ This function generates a frontend node template by extracting input fields from the component,
471
+ adding the code field, determining output types from method return types, validating the component,
472
+ setting base classes, and reordering fields. Returns the frontend node as a dictionary along with
473
+ the component instance.
474
+
475
+ Returns:
476
+ A tuple containing the frontend node dictionary and the component instance.
477
+ """
478
+ ctype_name = custom_component.__class__.__name__
479
+ if ctype_name in _COMPONENT_TYPE_NAMES:
480
+ cc_instance = get_component_instance(custom_component, user_id=user_id)
481
+
482
+ field_config = cc_instance.get_template_config(cc_instance)
483
+ frontend_node = ComponentFrontendNode.from_inputs(**field_config)
484
+
485
+ else:
486
+ frontend_node = ComponentFrontendNode.from_inputs(**custom_component.template_config)
487
+ cc_instance = custom_component
488
+ frontend_node = add_code_field(frontend_node, custom_component._code)
489
+ # But we now need to calculate the return_type of the methods in the outputs
490
+ for output in frontend_node.outputs:
491
+ if output.types:
492
+ continue
493
+ return_types = cc_instance.get_method_return_type(output.method)
494
+ return_types = [format_type(return_type) for return_type in return_types]
495
+ output.add_types(return_types)
496
+
497
+ # Validate that there is not name overlap between inputs and outputs
498
+ frontend_node.validate_component()
499
+ # ! This should be removed when we have a better way to handle this
500
+ frontend_node.set_base_classes_from_outputs()
501
+ reorder_fields(frontend_node, cc_instance._get_field_order())
502
+ frontend_node = build_component_metadata(frontend_node, cc_instance, module_name, ctype_name)
503
+
504
+ return frontend_node.to_dict(keep_name=False), cc_instance
505
+
506
+
507
+ def build_component_metadata(
508
+ frontend_node: CustomComponentFrontendNode, custom_component: CustomComponent, module_name: str, ctype_name: str
509
+ ):
510
+ """Build the metadata for a custom component."""
511
+ if module_name:
512
+ frontend_node.metadata["module"] = module_name
513
+ else:
514
+ module_name = get_module_name_from_display_name(frontend_node.display_name)
515
+ frontend_node.metadata["module"] = f"custom_components.{module_name}"
516
+
517
+ # Generate code hash for cache invalidation and debugging
518
+ try:
519
+ code_hash = _generate_code_hash(custom_component._code, module_name)
520
+ if code_hash:
521
+ frontend_node.metadata["code_hash"] = code_hash
522
+ except Exception as exc: # noqa: BLE001
523
+ logger.debug(f"Error generating code hash for {custom_component.__class__.__name__}", exc_info=exc)
524
+
525
+ # Analyze component dependencies
526
+ try:
527
+ dependency_info = analyze_component_dependencies(custom_component._code)
528
+ frontend_node.metadata["dependencies"] = dependency_info
529
+ except (SyntaxError, TypeError, ValueError, ImportError) as exc:
530
+ logger.warning(f"Failed to analyze dependencies for component {ctype_name}: {exc}")
531
+ # Set minimal dependency info on failure
532
+ frontend_node.metadata["dependencies"] = {
533
+ "total_dependencies": 0,
534
+ "dependencies": [],
535
+ }
536
+
537
+ return frontend_node
538
+
539
+
540
+ def build_custom_component_template(
541
+ custom_component: CustomComponent,
542
+ user_id: str | UUID | None = None,
543
+ module_name: str | None = None,
544
+ ) -> tuple[dict[str, Any], CustomComponent | Component]:
545
+ """Builds a frontend node template and instance for a custom component.
546
+
547
+ If the component uses input-based configuration, delegates to the appropriate builder. Otherwise,
548
+ constructs a frontend node from the component's template configuration, adds extra fields, code,
549
+ base classes, and output types, reorders fields, and returns the resulting template dictionary
550
+ along with the component instance.
551
+
552
+ Raises:
553
+ HTTPException: If the component is missing required attributes or if any error occurs during
554
+ template construction.
555
+ """
556
+ try:
557
+ has_template_config = hasattr(custom_component, "template_config")
558
+ except Exception as exc:
559
+ raise HTTPException(
560
+ status_code=400,
561
+ detail={
562
+ "error": (f"Error building Component: {exc}"),
563
+ "traceback": traceback.format_exc(),
564
+ },
565
+ ) from exc
566
+ if not has_template_config:
567
+ raise HTTPException(
568
+ status_code=400,
569
+ detail={
570
+ "error": ("Error building Component. Please check if you are importing Component correctly."),
571
+ },
572
+ )
573
+ try:
574
+ if "inputs" in custom_component.template_config:
575
+ return build_custom_component_template_from_inputs(
576
+ custom_component, user_id=user_id, module_name=module_name
577
+ )
578
+ frontend_node = CustomComponentFrontendNode(**custom_component.template_config)
579
+
580
+ field_config, custom_instance = run_build_config(
581
+ custom_component,
582
+ user_id=user_id,
583
+ )
584
+
585
+ entrypoint_args = custom_component.get_function_entrypoint_args
586
+
587
+ add_extra_fields(frontend_node, field_config, entrypoint_args)
588
+
589
+ frontend_node = add_code_field(frontend_node, custom_component._code)
590
+
591
+ add_base_classes(frontend_node, custom_component._get_function_entrypoint_return_type)
592
+ add_output_types(frontend_node, custom_component._get_function_entrypoint_return_type)
593
+
594
+ reorder_fields(frontend_node, custom_instance._get_field_order())
595
+
596
+ if module_name:
597
+ frontend_node = build_component_metadata(
598
+ frontend_node, custom_component, module_name, custom_component.__class__.__name__
599
+ )
600
+
601
+ return frontend_node.to_dict(keep_name=False), custom_instance
602
+ except Exception as exc:
603
+ if isinstance(exc, HTTPException):
604
+ raise
605
+ raise HTTPException(
606
+ status_code=400,
607
+ detail={
608
+ "error": (f"Error building Component: {exc}"),
609
+ "traceback": traceback.format_exc(),
610
+ },
611
+ ) from exc
612
+
613
+
614
+ def create_component_template(
615
+ component: dict | None = None,
616
+ component_extractor: Component | CustomComponent | None = None,
617
+ module_name: str | None = None,
618
+ ):
619
+ """Creates a component template and instance from either a component dictionary or an existing component extractor.
620
+
621
+ If a component dictionary is provided, a new Component instance is created from its code. If a component
622
+ extractor is provided, it is used directly. The function returns the generated template and the component
623
+ instance. Output types are set on the template if missing.
624
+ """
625
+ component_output_types = []
626
+ if component_extractor is None and component is not None:
627
+ component_code = component["code"]
628
+ component_output_types = component["output_types"]
629
+
630
+ component_extractor = Component(_code=component_code)
631
+
632
+ component_template, component_instance = build_custom_component_template(
633
+ component_extractor, module_name=module_name
634
+ )
635
+ if not component_template["output_types"] and component_output_types:
636
+ component_template["output_types"] = component_output_types
637
+
638
+ return component_template, component_instance
639
+
640
+
641
+ def build_custom_components(components_paths: list[str]):
642
+ """Build custom components from the specified paths."""
643
+ if not components_paths:
644
+ return {}
645
+
646
+ logger.info(f"Building custom components from {components_paths}")
647
+
648
+ custom_components_from_file: dict = {}
649
+ processed_paths = set()
650
+ for path in components_paths:
651
+ path_str = str(path)
652
+ if path_str in processed_paths:
653
+ continue
654
+
655
+ custom_component_dict = build_custom_component_list_from_path(path_str)
656
+ if custom_component_dict:
657
+ category = next(iter(custom_component_dict))
658
+ logger.debug(f"Loading {len(custom_component_dict[category])} component(s) from category {category}")
659
+ custom_components_from_file = merge_nested_dicts_with_renaming(
660
+ custom_components_from_file, custom_component_dict
661
+ )
662
+ processed_paths.add(path_str)
663
+
664
+ return custom_components_from_file
665
+
666
+
667
+ async def abuild_custom_components(components_paths: list[str]):
668
+ """Build custom components from the specified paths."""
669
+ if not components_paths:
670
+ return {}
671
+
672
+ await logger.adebug(f"Building custom components from {components_paths}")
673
+ custom_components_from_file: dict = {}
674
+ processed_paths = set()
675
+ for path in components_paths:
676
+ path_str = str(path)
677
+ if path_str in processed_paths:
678
+ continue
679
+
680
+ custom_component_dict = await abuild_custom_component_list_from_path(path_str)
681
+ if custom_component_dict:
682
+ category = next(iter(custom_component_dict))
683
+ await logger.adebug(f"Loading {len(custom_component_dict[category])} component(s) from category {category}")
684
+ custom_components_from_file = merge_nested_dicts_with_renaming(
685
+ custom_components_from_file, custom_component_dict
686
+ )
687
+ processed_paths.add(path_str)
688
+
689
+ return custom_components_from_file
690
+
691
+
692
+ def sanitize_field_config(field_config: dict | Input):
693
+ # If any of the already existing keys are in field_config, remove them
694
+ field_dict = field_config.to_dict() if isinstance(field_config, Input) else field_config
695
+ for key in [
696
+ "name",
697
+ "field_type",
698
+ "value",
699
+ "required",
700
+ "placeholder",
701
+ "display_name",
702
+ "advanced",
703
+ "show",
704
+ ]:
705
+ field_dict.pop(key, None)
706
+
707
+ # Remove field_type and type because they were extracted already
708
+ field_dict.pop("field_type", None)
709
+ field_dict.pop("type", None)
710
+
711
+ return field_dict
712
+
713
+
714
+ def build_component(component):
715
+ """Build a single component."""
716
+ component_template, component_instance = create_component_template(component)
717
+ component_name = get_instance_name(component_instance)
718
+ return component_name, component_template
719
+
720
+
721
+ def get_function(code):
722
+ """Get the function."""
723
+ function_name = validate.extract_function_name(code)
724
+
725
+ return validate.create_function(code, function_name)
726
+
727
+
728
+ def get_instance_name(instance):
729
+ name = instance.__class__.__name__
730
+ if hasattr(instance, "name") and instance.name:
731
+ name = instance.name
732
+ return name
733
+
734
+
735
+ async def update_component_build_config(
736
+ component: CustomComponent,
737
+ build_config: dotdict,
738
+ field_value: Any,
739
+ field_name: str | None = None,
740
+ ):
741
+ if inspect.iscoroutinefunction(component.update_build_config):
742
+ return await component.update_build_config(build_config, field_value, field_name)
743
+ return await asyncio.to_thread(component.update_build_config, build_config, field_value, field_name)
744
+
745
+
746
+ async def get_all_types_dict(components_paths: list[str]):
747
+ """Get all types dictionary with full component loading."""
748
+ # This is the async version of the existing function
749
+ return await abuild_custom_components(components_paths=components_paths)
750
+
751
+
752
+ async def get_single_component_dict(component_type: str, component_name: str, components_paths: list[str]):
753
+ """Get a single component dictionary."""
754
+ # For example, if components are loaded by importing Python modules:
755
+ for base_path in components_paths:
756
+ module_path = Path(base_path) / component_type / f"{component_name}.py"
757
+ if module_path.exists():
758
+ # Try to import the module
759
+ module_name = f"lfx.components.{component_type}.{component_name}"
760
+ try:
761
+ # This is a simplified example - actual implementation may vary
762
+ import importlib.util
763
+
764
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
765
+ if spec and spec.loader:
766
+ module = importlib.util.module_from_spec(spec)
767
+ spec.loader.exec_module(module)
768
+ if hasattr(module, "template"):
769
+ return module.template
770
+ except ImportError as e:
771
+ await logger.aerror(f"Import error loading component {module_path}: {e!s}")
772
+ except AttributeError as e:
773
+ await logger.aerror(f"Attribute error loading component {module_path}: {e!s}")
774
+ except ValueError as e:
775
+ await logger.aerror(f"Value error loading component {module_path}: {e!s}")
776
+ except (KeyError, IndexError) as e:
777
+ await logger.aerror(f"Data structure error loading component {module_path}: {e!s}")
778
+ except RuntimeError as e:
779
+ await logger.aerror(f"Runtime error loading component {module_path}: {e!s}")
780
+ await logger.adebug("Full traceback for runtime error", exc_info=True)
781
+ except OSError as e:
782
+ await logger.aerror(f"OS error loading component {module_path}: {e!s}")
783
+
784
+ # If we get here, the component wasn't found or couldn't be loaded
785
+ return None
786
+
787
+
788
+ async def load_custom_component(component_name: str, components_paths: list[str]):
789
+ """Load a custom component by name.
790
+
791
+ Args:
792
+ component_name: Name of the component to load
793
+ components_paths: List of paths to search for components
794
+ """
795
+ from lfx.interface.custom_component import get_custom_component_from_name
796
+
797
+ try:
798
+ # First try to get the component from the registered components
799
+ component_class = get_custom_component_from_name(component_name)
800
+ if component_class:
801
+ # Define the function locally if it's not imported
802
+ def get_custom_component_template(component_cls):
803
+ """Get template for a custom component class."""
804
+ # This is a simplified implementation - adjust as needed
805
+ if hasattr(component_cls, "get_template"):
806
+ return component_cls.get_template()
807
+ if hasattr(component_cls, "template"):
808
+ return component_cls.template
809
+ return None
810
+
811
+ return get_custom_component_template(component_class)
812
+
813
+ # If not found in registered components, search in the provided paths
814
+ for path in components_paths:
815
+ # Try to find the component in different category directories
816
+ base_path = Path(path)
817
+ if base_path.exists() and base_path.is_dir():
818
+ # Search for the component in all subdirectories
819
+ for category_dir in base_path.iterdir():
820
+ if category_dir.is_dir():
821
+ component_file = category_dir / f"{component_name}.py"
822
+ if component_file.exists():
823
+ # Try to import the module
824
+ module_name = f"lfx.components.{category_dir.name}.{component_name}"
825
+ try:
826
+ import importlib.util
827
+
828
+ spec = importlib.util.spec_from_file_location(module_name, component_file)
829
+ if spec and spec.loader:
830
+ module = importlib.util.module_from_spec(spec)
831
+ spec.loader.exec_module(module)
832
+ if hasattr(module, "template"):
833
+ return module.template
834
+ if hasattr(module, "get_template"):
835
+ return module.get_template()
836
+ except ImportError as e:
837
+ await logger.aerror(f"Import error loading component {component_file}: {e!s}")
838
+ await logger.adebug("Import error traceback", exc_info=True)
839
+ except AttributeError as e:
840
+ await logger.aerror(f"Attribute error loading component {component_file}: {e!s}")
841
+ await logger.adebug("Attribute error traceback", exc_info=True)
842
+ except (ValueError, TypeError) as e:
843
+ await logger.aerror(f"Value/Type error loading component {component_file}: {e!s}")
844
+ await logger.adebug("Value/Type error traceback", exc_info=True)
845
+ except (KeyError, IndexError) as e:
846
+ await logger.aerror(f"Data structure error loading component {component_file}: {e!s}")
847
+ await logger.adebug("Data structure error traceback", exc_info=True)
848
+ except RuntimeError as e:
849
+ await logger.aerror(f"Runtime error loading component {component_file}: {e!s}")
850
+ await logger.adebug("Runtime error traceback", exc_info=True)
851
+ except OSError as e:
852
+ await logger.aerror(f"OS error loading component {component_file}: {e!s}")
853
+ await logger.adebug("OS error traceback", exc_info=True)
854
+
855
+ except ImportError as e:
856
+ await logger.aerror(f"Import error loading custom component {component_name}: {e!s}")
857
+ return None
858
+ except AttributeError as e:
859
+ await logger.aerror(f"Attribute error loading custom component {component_name}: {e!s}")
860
+ return None
861
+ except ValueError as e:
862
+ await logger.aerror(f"Value error loading custom component {component_name}: {e!s}")
863
+ return None
864
+ except (KeyError, IndexError) as e:
865
+ await logger.aerror(f"Data structure error loading custom component {component_name}: {e!s}")
866
+ return None
867
+ except RuntimeError as e:
868
+ await logger.aerror(f"Runtime error loading custom component {component_name}: {e!s}")
869
+ logger.debug("Full traceback for runtime error", exc_info=True)
870
+ return None
871
+
872
+ # If we get here, the component wasn't found in any of the paths
873
+ await logger.awarning(f"Component {component_name} not found in any of the provided paths")
874
+ return None
875
+
876
+
877
+ _COMPONENT_TYPE_NAMES = {"Component", "CustomComponent"}