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
@@ -0,0 +1,823 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ import traceback
6
+ import types
7
+ from collections.abc import AsyncIterator, Callable, Iterator, Mapping
8
+ from enum import Enum
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from lfx.exceptions.component import ComponentBuildError
12
+ from lfx.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData
13
+ from lfx.graph.utils import UnbuiltObject, UnbuiltResult, log_transaction
14
+ from lfx.graph.vertex.param_handler import ParameterHandler
15
+ from lfx.interface import initialize
16
+ from lfx.interface.listing import lazy_load_dict
17
+ from lfx.log.logger import logger
18
+ from lfx.schema.artifact import ArtifactType
19
+ from lfx.schema.data import Data
20
+ from lfx.schema.message import Message
21
+ from lfx.schema.schema import INPUT_FIELD_NAME, OutputValue, build_output_logs
22
+ from lfx.utils.schemas import ChatOutputResponse
23
+ from lfx.utils.util import sync_to_async
24
+
25
+ if TYPE_CHECKING:
26
+ from uuid import UUID
27
+
28
+ from lfx.custom.custom_component.component import Component
29
+ from lfx.events.event_manager import EventManager
30
+ from lfx.graph.edge.base import CycleEdge, Edge
31
+ from lfx.graph.graph.base import Graph
32
+ from lfx.graph.vertex.schema import NodeData
33
+
34
+ # Simple log type for tracing
35
+ Log = dict
36
+
37
+
38
+ class VertexStates(str, Enum):
39
+ """Vertex are related to it being active, inactive, or in an error state."""
40
+
41
+ ACTIVE = "ACTIVE"
42
+ INACTIVE = "INACTIVE"
43
+ ERROR = "ERROR"
44
+
45
+
46
+ class Vertex:
47
+ def __init__(
48
+ self,
49
+ data: NodeData,
50
+ graph: Graph,
51
+ *,
52
+ base_type: str | None = None,
53
+ is_task: bool = False,
54
+ params: dict | None = None,
55
+ ) -> None:
56
+ # is_external means that the Vertex send or receives data from
57
+ # an external source (e.g the chat)
58
+ self._lock: asyncio.Lock | None = None
59
+ self.will_stream = False
60
+ self.updated_raw_params = False
61
+ self.id: str = data["id"]
62
+ self.base_name = self.id.split("-")[0]
63
+ self.is_state = False
64
+ # TODO: This won't be enough in the long term
65
+ # we need to have a better way to determine if a vertex is an input or an output
66
+ type_strings = [self.id.split("-")[0], data["data"]["type"]]
67
+ self.is_input = any(input_component_name in type_strings for input_component_name in INPUT_COMPONENTS)
68
+ self.is_output = any(output_component_name in type_strings for output_component_name in OUTPUT_COMPONENTS)
69
+ self._is_loop = None
70
+ self.has_session_id = None
71
+ self.custom_component = None
72
+ self.has_external_input = False
73
+ self.has_external_output = False
74
+ self.graph = graph
75
+ self.full_data = data.copy()
76
+ self.base_type: str | None = base_type
77
+ self.outputs: list[dict] = []
78
+ self.parse_data()
79
+ self.built_object: Any = UnbuiltObject()
80
+ self.built_result: Any = None
81
+ self.built = False
82
+ self._successors_ids: list[str] | None = None
83
+ self.artifacts: dict[str, Any] = {}
84
+ self.artifacts_raw: dict[str, Any] | None = {}
85
+ self.artifacts_type: dict[str, str] = {}
86
+ self.steps: list[Callable] = [self._build]
87
+ self.steps_ran: list[Callable] = []
88
+ self.task_id: str | None = None
89
+ self.is_task = is_task
90
+ self.params = params or {}
91
+ self.parent_node_id: str | None = self.full_data.get("parent_node_id")
92
+ self.load_from_db_fields: list[str] = []
93
+ self.parent_is_top_level = False
94
+ self.layer = None
95
+ self.result: ResultData | None = None
96
+ self.results: dict[str, Any] = {}
97
+ self.outputs_logs: dict[str, OutputValue] = {}
98
+ self.logs: dict[str, list[Log]] = {}
99
+ self.has_cycle_edges = False
100
+ try:
101
+ self.is_interface_component = self.vertex_type in InterfaceComponentTypes
102
+ except ValueError:
103
+ self.is_interface_component = False
104
+
105
+ self.use_result = False
106
+ self.build_times: list[float] = []
107
+ self.state = VertexStates.ACTIVE
108
+ self.log_transaction_tasks: set[asyncio.Task] = set()
109
+ self.output_names: list[str] = [
110
+ output["name"] for output in self.outputs if isinstance(output, dict) and "name" in output
111
+ ]
112
+ self._incoming_edges: list[CycleEdge] | None = None
113
+ self._outgoing_edges: list[CycleEdge] | None = None
114
+
115
+ @property
116
+ def lock(self):
117
+ """Lazy initialization of asyncio.Lock."""
118
+ if self._lock is None:
119
+ self._lock = asyncio.Lock()
120
+ return self._lock
121
+
122
+ @property
123
+ def is_loop(self) -> bool:
124
+ """Check if any output allows looping."""
125
+ if self._is_loop is None:
126
+ self._is_loop = any(output.get("allows_loop", False) for output in self.outputs)
127
+ return self._is_loop
128
+
129
+ def set_input_value(self, name: str, value: Any) -> None:
130
+ if self.custom_component is None:
131
+ msg = f"Vertex {self.id} does not have a component instance."
132
+ raise ValueError(msg)
133
+ self.custom_component.set_input_value(name, value)
134
+
135
+ def to_data(self):
136
+ return self.full_data
137
+
138
+ def add_component_instance(self, component_instance: Component) -> None:
139
+ component_instance.set_vertex(self)
140
+ self.custom_component = component_instance
141
+
142
+ def add_result(self, name: str, result: Any) -> None:
143
+ self.results[name] = result
144
+
145
+ def set_state(self, state: str) -> None:
146
+ self.state = VertexStates[state]
147
+ if self.state == VertexStates.INACTIVE and self.graph.in_degree_map[self.id] <= 1:
148
+ # If the vertex is inactive and has only one in degree
149
+ # it means that it is not a merge point in the graph
150
+ self.graph.inactivated_vertices.add(self.id)
151
+ elif self.state == VertexStates.ACTIVE and self.id in self.graph.inactivated_vertices:
152
+ self.graph.inactivated_vertices.remove(self.id)
153
+
154
+ def is_active(self):
155
+ return self.state == VertexStates.ACTIVE
156
+
157
+ @property
158
+ def avg_build_time(self):
159
+ return sum(self.build_times) / len(self.build_times) if self.build_times else 0
160
+
161
+ def add_build_time(self, time) -> None:
162
+ self.build_times.append(time)
163
+
164
+ def set_result(self, result: ResultData) -> None:
165
+ self.result = result
166
+
167
+ def get_built_result(self):
168
+ # If the Vertex.type is a power component
169
+ # then we need to return the built object
170
+ # instead of the result dict
171
+ if self.is_interface_component and not isinstance(self.built_object, UnbuiltObject):
172
+ result = self.built_object
173
+ # if it is not a dict or a string and hasattr model_dump then
174
+ # return the model_dump
175
+ if not isinstance(result, dict | str) and hasattr(result, "content"):
176
+ return result.content
177
+ return result
178
+ if isinstance(self.built_object, str):
179
+ self.built_result = self.built_object
180
+
181
+ if isinstance(self.built_result, UnbuiltResult):
182
+ return {}
183
+ return self.built_result if isinstance(self.built_result, dict) else {"result": self.built_result}
184
+
185
+ def set_artifacts(self) -> None:
186
+ pass
187
+
188
+ @property
189
+ def edges(self) -> list[CycleEdge]:
190
+ return self.graph.get_vertex_edges(self.id)
191
+
192
+ @property
193
+ def outgoing_edges(self) -> list[CycleEdge]:
194
+ if self._outgoing_edges is None:
195
+ self._outgoing_edges = [edge for edge in self.edges if edge.source_id == self.id]
196
+ return self._outgoing_edges
197
+
198
+ @property
199
+ def incoming_edges(self) -> list[CycleEdge]:
200
+ if self._incoming_edges is None:
201
+ self._incoming_edges = [edge for edge in self.edges if edge.target_id == self.id]
202
+ return self._incoming_edges
203
+
204
+ # Get edge connected to an output of a certain name
205
+ def get_incoming_edge_by_target_param(self, target_param: str) -> str | None:
206
+ return next((edge.source_id for edge in self.incoming_edges if edge.target_param == target_param), None)
207
+
208
+ @property
209
+ def edges_source_names(self) -> set[str | None]:
210
+ return {edge.source_handle.name for edge in self.edges}
211
+
212
+ @property
213
+ def predecessors(self) -> list[Vertex]:
214
+ return self.graph.get_predecessors(self)
215
+
216
+ @property
217
+ def successors(self) -> list[Vertex]:
218
+ return self.graph.get_successors(self)
219
+
220
+ @property
221
+ def successors_ids(self) -> list[str]:
222
+ return self.graph.successor_map.get(self.id, [])
223
+
224
+ def __getstate__(self):
225
+ state = self.__dict__.copy()
226
+ state["_lock"] = None # Locks are not serializable
227
+ state["built_object"] = None if isinstance(self.built_object, UnbuiltObject) else self.built_object
228
+ state["built_result"] = None if isinstance(self.built_result, UnbuiltResult) else self.built_result
229
+ return state
230
+
231
+ def __setstate__(self, state):
232
+ self.__dict__.update(state)
233
+ self._lock = asyncio.Lock() # Reinitialize the lock
234
+ self.built_object = state.get("built_object") or UnbuiltObject()
235
+ self.built_result = state.get("built_result") or UnbuiltResult()
236
+
237
+ def set_top_level(self, top_level_vertices: list[str]) -> None:
238
+ self.parent_is_top_level = self.parent_node_id in top_level_vertices
239
+
240
+ def parse_data(self) -> None:
241
+ self.data = self.full_data["data"]
242
+ if self.data["node"]["template"]["_type"] == "Component":
243
+ if "outputs" not in self.data["node"]:
244
+ msg = f"Outputs not found for {self.display_name}"
245
+ raise ValueError(msg)
246
+ self.outputs = self.data["node"]["outputs"]
247
+ else:
248
+ self.outputs = self.data["node"].get("outputs", [])
249
+ self.output = self.data["node"]["base_classes"]
250
+
251
+ self.display_name: str = self.data["node"].get("display_name", self.id.split("-")[0])
252
+ self.icon: str = self.data["node"].get("icon", self.id.split("-")[0])
253
+
254
+ self.description: str = self.data["node"].get("description", "")
255
+ self.frozen: bool = self.data["node"].get("frozen", False)
256
+
257
+ self.is_input = self.data["node"].get("is_input") or self.is_input
258
+ self.is_output = self.data["node"].get("is_output") or self.is_output
259
+ template_dicts = {key: value for key, value in self.data["node"]["template"].items() if isinstance(value, dict)}
260
+
261
+ self.has_session_id = "session_id" in template_dicts
262
+
263
+ self.required_inputs: list[str] = []
264
+ self.optional_inputs: list[str] = []
265
+ for value_dict in template_dicts.values():
266
+ list_to_append = self.required_inputs if value_dict.get("required") else self.optional_inputs
267
+
268
+ if "type" in value_dict:
269
+ list_to_append.append(value_dict["type"])
270
+ if "input_types" in value_dict:
271
+ list_to_append.extend(value_dict["input_types"])
272
+
273
+ template_dict = self.data["node"]["template"]
274
+ self.vertex_type = (
275
+ self.data["type"]
276
+ if "Tool" not in [type_ for out in self.outputs for type_ in out["types"]]
277
+ or template_dict["_type"].islower()
278
+ else template_dict["_type"]
279
+ )
280
+
281
+ if self.base_type is None:
282
+ for base_type, value in lazy_load_dict.all_types_dict.items():
283
+ if self.vertex_type in value:
284
+ self.base_type = base_type
285
+ break
286
+
287
+ def get_value_from_output_names(self, key: str):
288
+ if key in self.output_names:
289
+ return self.graph.get_vertex(key)
290
+ return None
291
+
292
+ def get_value_from_template_dict(self, key: str):
293
+ template_dict = self.data.get("node", {}).get("template", {})
294
+
295
+ if key not in template_dict:
296
+ msg = f"Key {key} not found in template dict"
297
+ raise ValueError(msg)
298
+ return template_dict.get(key, {}).get("value")
299
+
300
+ def _set_params_from_normal_edge(self, params: dict, edge: Edge, template_dict: dict):
301
+ param_key = edge.target_param
302
+
303
+ # If the param_key is in the template_dict and the edge.target_id is the current node
304
+ # We check this to make sure params with the same name but different target_id
305
+ # don't get overwritten
306
+ if param_key in template_dict and edge.target_id == self.id:
307
+ if template_dict[param_key].get("list"):
308
+ if param_key not in params:
309
+ params[param_key] = []
310
+ params[param_key].append(self.graph.get_vertex(edge.source_id))
311
+ elif edge.target_id == self.id:
312
+ if isinstance(template_dict[param_key].get("value"), dict):
313
+ # we don't know the key of the dict but we need to set the value
314
+ # to the vertex that is the source of the edge
315
+ param_dict = template_dict[param_key]["value"]
316
+ if not param_dict or len(param_dict) != 1:
317
+ params[param_key] = self.graph.get_vertex(edge.source_id)
318
+ else:
319
+ params[param_key] = {key: self.graph.get_vertex(edge.source_id) for key in param_dict}
320
+
321
+ else:
322
+ params[param_key] = self.graph.get_vertex(edge.source_id)
323
+ elif param_key in self.output_names:
324
+ # if the loop is run the param_key item will be set over here
325
+ # validate the edge
326
+ params[param_key] = self.graph.get_vertex(edge.source_id)
327
+ return params
328
+
329
+ def build_params(self) -> None:
330
+ """Build parameters for the vertex using the ParameterHandler."""
331
+ if self.graph is None:
332
+ msg = "Graph not found"
333
+ raise ValueError(msg)
334
+
335
+ if self.updated_raw_params:
336
+ self.updated_raw_params = False
337
+ return
338
+
339
+ # Create parameter handler with lazy storage service initialization
340
+ param_handler = ParameterHandler(self, storage_service=None)
341
+
342
+ # Process edge parameters
343
+ edge_params = param_handler.process_edge_parameters(self.edges)
344
+
345
+ # Process field parameters
346
+ field_params, load_from_db_fields = param_handler.process_field_parameters()
347
+
348
+ # Combine parameters, edge_params take precedence
349
+ self.params = {**field_params, **edge_params}
350
+ self.load_from_db_fields = load_from_db_fields
351
+ self.raw_params = self.params.copy()
352
+
353
+ def update_raw_params(self, new_params: Mapping[str, str | list[str]], *, overwrite: bool = False) -> None:
354
+ """Update the raw parameters of the vertex with the given new parameters.
355
+
356
+ Args:
357
+ new_params (Dict[str, Any]): The new parameters to update.
358
+ overwrite (bool, optional): Whether to overwrite the existing parameters.
359
+ Defaults to False.
360
+
361
+ Raises:
362
+ ValueError: If any key in new_params is not found in self.raw_params.
363
+ """
364
+ # First check if the input_value in raw_params is not a vertex
365
+ if not new_params:
366
+ return
367
+ if any(isinstance(self.raw_params.get(key), Vertex) for key in new_params):
368
+ return
369
+ if not overwrite:
370
+ for key in new_params.copy(): # type: ignore[attr-defined]
371
+ if key not in self.raw_params:
372
+ new_params.pop(key) # type: ignore[attr-defined]
373
+ self.raw_params.update(new_params)
374
+ self.params = self.raw_params.copy()
375
+ self.updated_raw_params = True
376
+
377
+ def instantiate_component(self, user_id=None) -> None:
378
+ if not self.custom_component:
379
+ self.custom_component, _ = initialize.loading.instantiate_class(
380
+ user_id=user_id,
381
+ vertex=self,
382
+ )
383
+
384
+ async def _build(
385
+ self,
386
+ fallback_to_env_vars,
387
+ user_id=None,
388
+ event_manager: EventManager | None = None,
389
+ ) -> None:
390
+ """Initiate the build process."""
391
+ await logger.adebug(f"Building {self.display_name}")
392
+ await self._build_each_vertex_in_params_dict()
393
+
394
+ if self.base_type is None:
395
+ msg = f"Base type for vertex {self.display_name} not found"
396
+ raise ValueError(msg)
397
+
398
+ if not self.custom_component:
399
+ custom_component, custom_params = initialize.loading.instantiate_class(
400
+ user_id=user_id, vertex=self, event_manager=event_manager
401
+ )
402
+ else:
403
+ custom_component = self.custom_component
404
+ if hasattr(self.custom_component, "set_event_manager"):
405
+ self.custom_component.set_event_manager(event_manager)
406
+ custom_params = initialize.loading.get_params(self.params)
407
+
408
+ await self._build_results(
409
+ custom_component=custom_component,
410
+ custom_params=custom_params,
411
+ fallback_to_env_vars=fallback_to_env_vars,
412
+ base_type=self.base_type,
413
+ )
414
+
415
+ self._validate_built_object()
416
+
417
+ self.built = True
418
+
419
+ def extract_messages_from_artifacts(self, artifacts: dict[str, Any]) -> list[dict]:
420
+ """Extracts messages from the artifacts.
421
+
422
+ Args:
423
+ artifacts (Dict[str, Any]): The artifacts to extract messages from.
424
+
425
+ Returns:
426
+ List[str]: The extracted messages.
427
+ """
428
+ try:
429
+ text = artifacts["text"]
430
+ sender = artifacts.get("sender")
431
+ sender_name = artifacts.get("sender_name")
432
+ session_id = artifacts.get("session_id")
433
+ stream_url = artifacts.get("stream_url")
434
+ files = [{"path": file} if isinstance(file, str) else file for file in artifacts.get("files", [])]
435
+ component_id = self.id
436
+ type_ = self.artifacts_type
437
+
438
+ if isinstance(sender_name, Data | Message):
439
+ sender_name = sender_name.get_text()
440
+
441
+ messages = [
442
+ ChatOutputResponse(
443
+ message=text,
444
+ sender=sender,
445
+ sender_name=sender_name,
446
+ session_id=session_id,
447
+ stream_url=stream_url,
448
+ files=files,
449
+ component_id=component_id,
450
+ type=type_,
451
+ ).model_dump(exclude_none=True)
452
+ ]
453
+ except KeyError:
454
+ messages = []
455
+
456
+ return messages
457
+
458
+ def finalize_build(self) -> None:
459
+ result_dict = self.get_built_result()
460
+ # We need to set the artifacts to pass information
461
+ # to the frontend
462
+ self.set_artifacts()
463
+ artifacts = self.artifacts_raw
464
+ messages = self.extract_messages_from_artifacts(artifacts) if isinstance(artifacts, dict) else []
465
+ result_dict = ResultData(
466
+ results=result_dict,
467
+ artifacts=artifacts,
468
+ outputs=self.outputs_logs,
469
+ logs=self.logs,
470
+ messages=messages,
471
+ component_display_name=self.display_name,
472
+ component_id=self.id,
473
+ )
474
+ self.set_result(result_dict)
475
+
476
+ async def _build_each_vertex_in_params_dict(self) -> None:
477
+ """Iterates over each vertex in the params dictionary and builds it."""
478
+ for key, value in self.raw_params.items():
479
+ if self._is_vertex(value):
480
+ if value == self:
481
+ del self.params[key]
482
+ continue
483
+ await self._build_vertex_and_update_params(
484
+ key,
485
+ value,
486
+ )
487
+ elif isinstance(value, list) and self._is_list_of_vertices(value):
488
+ await self._build_list_of_vertices_and_update_params(key, value)
489
+ elif isinstance(value, dict):
490
+ await self._build_dict_and_update_params(
491
+ key,
492
+ value,
493
+ )
494
+ elif key not in self.params or self.updated_raw_params:
495
+ self.params[key] = value
496
+
497
+ async def _build_dict_and_update_params(
498
+ self,
499
+ key,
500
+ vertices_dict: dict[str, Vertex],
501
+ ) -> None:
502
+ """Iterates over a dictionary of vertices, builds each and updates the params dictionary."""
503
+ for sub_key, value in vertices_dict.items():
504
+ if not self._is_vertex(value):
505
+ self.params[key][sub_key] = value
506
+ else:
507
+ result = await value.get_result(self, target_handle_name=key)
508
+ self.params[key][sub_key] = result
509
+
510
+ @staticmethod
511
+ def _is_vertex(value):
512
+ """Checks if the provided value is an instance of Vertex."""
513
+ return isinstance(value, Vertex)
514
+
515
+ def _is_list_of_vertices(self, value):
516
+ """Checks if the provided value is a list of Vertex instances."""
517
+ return all(self._is_vertex(vertex) for vertex in value)
518
+
519
+ async def get_result(self, requester: Vertex, target_handle_name: str | None = None) -> Any:
520
+ """Retrieves the result of the vertex.
521
+
522
+ This is a read-only method so it raises an error if the vertex has not been built yet.
523
+
524
+ Returns:
525
+ The result of the vertex.
526
+ """
527
+ async with self.lock:
528
+ return await self._get_result(requester, target_handle_name)
529
+
530
+ async def _log_transaction_async(
531
+ self,
532
+ flow_id: str | UUID,
533
+ source: Vertex,
534
+ status,
535
+ target: Vertex | None = None,
536
+ error=None,
537
+ ) -> None:
538
+ """Log a transaction asynchronously with proper task handling and cancellation.
539
+
540
+ Args:
541
+ flow_id: The ID of the flow
542
+ source: Source vertex
543
+ status: Transaction status
544
+ target: Optional target vertex
545
+ error: Optional error information
546
+ """
547
+ if self.log_transaction_tasks:
548
+ # Safely await and remove completed tasks
549
+ task = self.log_transaction_tasks.pop()
550
+ await task
551
+
552
+ # Create and track new task
553
+ task = asyncio.create_task(log_transaction(flow_id, source, status, target, error))
554
+ self.log_transaction_tasks.add(task)
555
+ task.add_done_callback(self.log_transaction_tasks.discard)
556
+
557
+ async def _get_result(
558
+ self,
559
+ requester: Vertex,
560
+ target_handle_name: str | None = None, # noqa: ARG002
561
+ ) -> Any:
562
+ """Retrieves the result of the built component.
563
+
564
+ If the component has not been built yet, a ValueError is raised.
565
+
566
+ Returns:
567
+ The built result if use_result is True, else the built object.
568
+ """
569
+ flow_id = self.graph.flow_id
570
+ if not self.built:
571
+ if flow_id:
572
+ await self._log_transaction_async(str(flow_id), source=self, target=requester, status="error")
573
+ msg = f"Component {self.display_name} has not been built yet"
574
+ raise ValueError(msg)
575
+
576
+ result = self.built_result if self.use_result else self.built_object
577
+ if flow_id:
578
+ await self._log_transaction_async(str(flow_id), source=self, target=requester, status="success")
579
+ return result
580
+
581
+ async def _build_vertex_and_update_params(self, key, vertex: Vertex) -> None:
582
+ """Builds a given vertex and updates the params dictionary accordingly."""
583
+ result = await vertex.get_result(self, target_handle_name=key)
584
+ self._handle_func(key, result)
585
+ if isinstance(result, list):
586
+ self._extend_params_list_with_result(key, result)
587
+ self.params[key] = result
588
+
589
+ async def _build_list_of_vertices_and_update_params(
590
+ self,
591
+ key,
592
+ vertices: list[Vertex],
593
+ ) -> None:
594
+ """Iterates over a list of vertices, builds each and updates the params dictionary."""
595
+ self.params[key] = []
596
+ for vertex in vertices:
597
+ result = await vertex.get_result(self, target_handle_name=key)
598
+ # Weird check to see if the params[key] is a list
599
+ # because sometimes it is a Data and breaks the code
600
+ if not isinstance(self.params[key], list):
601
+ self.params[key] = [self.params[key]]
602
+
603
+ if isinstance(result, list):
604
+ self.params[key].extend(result)
605
+ else:
606
+ try:
607
+ if self.params[key] == result:
608
+ continue
609
+
610
+ self.params[key].append(result)
611
+ except AttributeError as e:
612
+ await logger.aexception(e)
613
+ msg = (
614
+ f"Params {key} ({self.params[key]}) is not a list and cannot be extended with {result}"
615
+ f"Error building Component {self.display_name}: \n\n{e}"
616
+ )
617
+ raise ValueError(msg) from e
618
+
619
+ def _handle_func(self, key, result) -> None:
620
+ """Handles 'func' key by checking if the result is a function and setting it as coroutine."""
621
+ if key == "func":
622
+ if not isinstance(result, types.FunctionType):
623
+ if hasattr(result, "run"):
624
+ result = result.run
625
+ elif hasattr(result, "get_function"):
626
+ result = result.get_function()
627
+ elif inspect.iscoroutinefunction(result):
628
+ self.params["coroutine"] = result
629
+ else:
630
+ self.params["coroutine"] = sync_to_async(result)
631
+
632
+ def _extend_params_list_with_result(self, key, result) -> None:
633
+ """Extends a list in the params dictionary with the given result if it exists."""
634
+ if isinstance(self.params[key], list):
635
+ self.params[key].extend(result)
636
+
637
+ async def _build_results(
638
+ self,
639
+ custom_component,
640
+ custom_params,
641
+ base_type: str,
642
+ *,
643
+ fallback_to_env_vars=False,
644
+ ) -> None:
645
+ try:
646
+ result = await initialize.loading.get_instance_results(
647
+ custom_component=custom_component,
648
+ custom_params=custom_params,
649
+ vertex=self,
650
+ fallback_to_env_vars=fallback_to_env_vars,
651
+ base_type=base_type,
652
+ )
653
+
654
+ self.outputs_logs = build_output_logs(self, result)
655
+
656
+ self._update_built_object_and_artifacts(result)
657
+ except Exception as exc:
658
+ tb = traceback.format_exc()
659
+ await logger.aexception(exc)
660
+ msg = f"Error building Component {self.display_name}: \n\n{exc}"
661
+ raise ComponentBuildError(msg, tb) from exc
662
+
663
+ def _update_built_object_and_artifacts(self, result: Any | tuple[Any, dict] | tuple[Component, Any, dict]) -> None:
664
+ """Updates the built object and its artifacts."""
665
+ if isinstance(result, tuple):
666
+ if len(result) == 2: # noqa: PLR2004
667
+ self.built_object, self.artifacts = result
668
+ elif len(result) == 3: # noqa: PLR2004
669
+ self.custom_component, self.built_object, self.artifacts = result
670
+ self.logs = self.custom_component.get_output_logs()
671
+ self.artifacts_raw = self.artifacts.get("raw", None)
672
+ self.artifacts_type = {
673
+ self.outputs[0]["name"]: self.artifacts.get("type", None) or ArtifactType.UNKNOWN.value
674
+ }
675
+ self.artifacts = {self.outputs[0]["name"]: self.artifacts}
676
+ else:
677
+ self.built_object = result
678
+
679
+ def _validate_built_object(self) -> None:
680
+ """Checks if the built object is None and raises a ValueError if so."""
681
+ if isinstance(self.built_object, UnbuiltObject):
682
+ msg = f"{self.display_name}: {self.built_object_repr()}"
683
+ raise TypeError(msg)
684
+ if self.built_object is None:
685
+ message = f"{self.display_name} returned None."
686
+ if self.base_type == "custom_components":
687
+ message += " Make sure your build method returns a component."
688
+
689
+ logger.warning(message)
690
+ elif isinstance(self.built_object, Iterator | AsyncIterator):
691
+ if self.display_name == "Text Output":
692
+ msg = f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead."
693
+ raise ValueError(msg)
694
+
695
+ def _reset(self) -> None:
696
+ self.built = False
697
+ self.built_object = UnbuiltObject()
698
+ self.built_result = UnbuiltResult()
699
+ self.artifacts = {}
700
+ self.steps_ran = []
701
+ self.build_params()
702
+
703
+ def _is_chat_input(self) -> bool:
704
+ return False
705
+
706
+ def build_inactive(self) -> None:
707
+ # Just set the results to None
708
+ self.built = True
709
+ self.built_object = None
710
+ self.built_result = None
711
+
712
+ async def build(
713
+ self,
714
+ user_id=None,
715
+ inputs: dict[str, Any] | None = None,
716
+ files: list[str] | None = None,
717
+ requester: Vertex | None = None,
718
+ event_manager: EventManager | None = None,
719
+ **kwargs,
720
+ ) -> Any:
721
+ # Add lazy loading check at the beginning
722
+ # Check if we need to fully load this component first
723
+ from lfx.interface.components import ensure_component_loaded
724
+ from lfx.services.deps import get_settings_service
725
+
726
+ settings_service = get_settings_service()
727
+ if settings_service and settings_service.settings.lazy_load_components:
728
+ component_name = self.id.split("-")[0]
729
+ await ensure_component_loaded(self.vertex_type, component_name, settings_service)
730
+
731
+ # Continue with the original implementation
732
+ async with self.lock:
733
+ if self.state == VertexStates.INACTIVE:
734
+ # If the vertex is inactive, return None
735
+ self.build_inactive()
736
+ return None
737
+
738
+ if self.frozen and self.built:
739
+ return await self.get_requester_result(requester)
740
+ if self.built and requester is not None:
741
+ # This means that the vertex has already been built
742
+ # and we are just getting the result for the requester
743
+ return await self.get_requester_result(requester)
744
+ self._reset()
745
+ # inject session_id if it is not None
746
+ if inputs is not None and "session" in inputs and inputs["session"] is not None and self.has_session_id:
747
+ session_id_value = self.get_value_from_template_dict("session_id")
748
+ if session_id_value == "":
749
+ self.update_raw_params({"session_id": inputs["session"]}, overwrite=True)
750
+ if self._is_chat_input() and (inputs or files):
751
+ chat_input = {}
752
+ if (
753
+ inputs
754
+ and isinstance(inputs, dict)
755
+ and "input_value" in inputs
756
+ and inputs.get("input_value") is not None
757
+ ):
758
+ chat_input.update({"input_value": inputs.get(INPUT_FIELD_NAME, "")})
759
+ if files:
760
+ chat_input.update({"files": files})
761
+
762
+ self.update_raw_params(chat_input, overwrite=True)
763
+
764
+ # Run steps
765
+ for step in self.steps:
766
+ if step not in self.steps_ran:
767
+ await step(user_id=user_id, event_manager=event_manager, **kwargs)
768
+ self.steps_ran.append(step)
769
+
770
+ self.finalize_build()
771
+
772
+ return await self.get_requester_result(requester)
773
+
774
+ async def get_requester_result(self, requester: Vertex | None):
775
+ # If the requester is None, this means that
776
+ # the Vertex is the root of the graph
777
+ if requester is None:
778
+ return self.built_object
779
+
780
+ # Get the requester edge
781
+ requester_edge = next((edge for edge in self.edges if edge.target_id == requester.id), None)
782
+ # Return the result of the requester edge
783
+ return (
784
+ None
785
+ if requester_edge is None
786
+ else await requester_edge.get_result_from_source(source=self, target=requester)
787
+ )
788
+
789
+ def add_edge(self, edge: CycleEdge) -> None:
790
+ if edge not in self.edges:
791
+ self.edges.append(edge)
792
+
793
+ def __repr__(self) -> str:
794
+ return f"Vertex(display_name={self.display_name}, id={self.id}, data={self.data})"
795
+
796
+ def __eq__(self, /, other: object) -> bool:
797
+ try:
798
+ if not isinstance(other, Vertex):
799
+ return False
800
+ # We should create a more robust comparison
801
+ # for the Vertex class
802
+ ids_are_equal = self.id == other.id
803
+ # self.data is a dict and we need to compare them
804
+ # to check if they are equal
805
+ data_are_equal = self.data == other.data
806
+ except AttributeError:
807
+ return False
808
+ else:
809
+ return ids_are_equal and data_are_equal
810
+
811
+ def __hash__(self) -> int:
812
+ return id(self)
813
+
814
+ def built_object_repr(self) -> str:
815
+ # Add a message with an emoji, stars for success,
816
+ return "Built successfully ✨" if self.built_object is not None else "Failed to build 😵‍💫"
817
+
818
+ def apply_on_outputs(self, func: Callable[[Any], Any]) -> None:
819
+ """Applies a function to the outputs of the vertex."""
820
+ if not self.custom_component or not self.custom_component.outputs:
821
+ return
822
+ # Apply the function to each output
823
+ [func(output) for output in self.custom_component.get_outputs_map().values()]